Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Software PWM in ASM (12f675)

Status
Not open for further replies.

trvo

New Member
Hey Guys,

As you probably know, I'm VERY new to all this. I'm playing about with some digital R/C servos now which operate at 3mS wavelength (1 - 2ms high for full movement). I've managed to come up with some VERY crude and pretty much useless code that outputs the correct signal for 1.5mS high (followed by 1.5ms low). This 'should' centre a servo. However, I want to make something a bit more useful than this. Can anyone suggest a better way of going about generating a software PWM output that is actually useable in real life applications?

Please see my current messy code below:
list p=12f675 ; list directive to define processor
#include <p12f675.inc> ; processor specific variable definitions

errorlevel -302 ; suppress message 302 from list file

__CONFIG _CP_OFF & _CPD_OFF & _BODEN_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT

;***** VARIABLE DEFINITIONS
INT_VAR UDATA_SHR 0x20
timer RES 1
counter RES 1

;**********************************************************************
RESET_VECTOR CODE 0x000 ; processor reset vector
goto main ; go to beginning of program

main:
call 0x3FF ; retrieve factory calibration value
bsf STATUS,RP0 ; set file register bank to 1
movwf OSCCAL ; update register with factory cal value
clrf TRISIO ; clear the tri state register
movlw b'00000001' ; set the tri state reg to AN0 as input, remainder output
movwf TRISIO
bcf STATUS,RP0 ; set file register bank to 0
clrf GPIO ; clear the i/o ports (turn all off)
movlw .50
movwf counter

;************************************************************************************************
; Centre servo position
;************************************************************************************************

centre:
bsf GPIO,02 ; set bit 2 of gpio to high
call delay1_5ms ; call delay to keep the pulse high
nop
nop ; need these to make 150uS
nop
bcf GPIO,02 ; set bit 2 of gpio to low
call delay1_5ms ; call delay to keep the pulse low
decfsz counter,1
goto centre ; repeat
goto null_loop
delay1_5ms:
movlw .24 ; move 24 into w
movwf timer ; move w into the timer variable
loopb:
decfsz timer,1 ; decrease timer by 1 and put result in timer
goto temploop ; repeat if timer is not zero
retlw 0 ; return
temploop:
nop ; added to make each loop worth 6uS
goto loopb ; we've taken up the time, go back

null_loop:
goto null_loop ; just don't do anything else

END ; directive 'end of program'

Many Thanks - Trev
 
I posted this code a while ago, it reads a pot on RA3 and moves a servo on RB3 to match the pot. Servos normally require a pulse between 1 and 2mS every 20mS to operate.

Mike.

Code:
;###############################################################################
;	Title:			ADC, Servo Demo
;    	Author:			Mike Webb
;	Date:			21 Nov 2006
;################################################################################

		include	"p16f88.inc"

		errorlevel	-302	; stop the annoying " Register in operand not in bank 0." error.

	__config  _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_ON & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _INTRC_IO
	__config  _CONFIG2, _IESO_OFF & _FCMEN_OFF

;Just for Mike McLaren, here's some psuedo C code.
;main{
;    while(1){
;        W=ReadADC()>>2;     //get 8 most significant bits of ADC
;        ServoOut=1;         //Turn on output
;        Delay8(250);        //delay 1mS
;        If(W!=0)            //skip if ADC=0
;            Delay8(W);      //delay W*8 clock cycles = 0 to 1,03mS
;        ServoOut=0;         //Turn off output
;        W=256-W;            //calculate off time to make total 2mS
;        if(W!=0)            //Skip if zero
;            Delay8(W);      //delay to make 2mS
;        For(x=0;x<18;x++){  //Repeat 18 times
;            Delay8(250);    //Delay 8*250 clock cycles = 1mS
;        }
;    }
;}


; Setup Variables
		cblock	0x20
Count
OnTime
		endc

		org     0h
		nop	;		required for ICD2
		bcf	STATUS,RP1;	bank 0 or 2
		bsf	STATUS,RP0;	select bank 1
		movlw	(0<<NOT_RBPU|0<<INTEDG|0<<T0CS|0<<T0SE|0<<PSA|B'000'<<PS0)
		movwf	OPTION_REG;	setup option reg
		bcf	TRISB,3	;	make b3 output
		movlw	B'111'<<IRCF0;	=b'01110000'
		movwf	OSCCON;		select 8MHz clock
		movlw	b'00001000';	Select bit 3	
		movwf	ANSEL;		ADC on RA3
		bcf	STATUS,RP0;	back to bank 0
Loop		Call	ReadADC;	read A3
		movwf	OnTime;		Store it for later
		movlw	d'250';		250*8 = 2000 cycles = 1mS
		bsf	PORTB,3;	Turn output on
		call	DelayW;		delay the 1mS
		movfw	OnTime;		Get value from ADC
		btfss	STATUS,Z;	if it's zero don't do any delay
		call	DelayW;		delay W*8 = 0 to 255 = 0 to 1.03 mS  -- near enough
		bcf	PORTB,3;	Turn output off - pulse is complete
		comf	OnTime,W;	Complement ADC value
		addlw	1;		com and inc = negate.
		btfsc	STATUS,Z;	Don't do a delay of zero
		call	DelayW;		delay (256-ADC value)*8 = time required to get to 2mS 
		movlw	d'18';		We need to delay another 18mS
		movwf	Count;		so setup a counter
TimeLoop	movlw	d'250';		250*8 = 2000 clock cycles = 1mS
		call	DelayW;		do the delay
		decfsz	Count,F;	repeat it?
		goto	TimeLoop;	yes, go around again
		goto	Loop;		Go back and do it all again

DelayW
; will delay W*8 cycles including the call/return
		addlw	0xff;		add -1 to W			1
		btfsc	STATUS,Z;	=zero?				1/2
		goto	DoneDelay;	yes, better get out of here	2
		nop;			delay a bit			1
		goto	$+1;		delay a bit more		2
		goto	DelayW;		go around again			2
DoneDelay	return	;						2

ReadADC		movlw	b'01'<<ADCS0|b'011'<<CHS0|b'0'<<GO|b'1'<<ADON
		movwf	ADCON0;		select pin A3 and 16 TOSC
		bsf	STATUS,RP0;	bank 1
		movlw	b'0'<<ADFM|b'1'<<ADCS2|b'00'<<VCFG0
		movwf	ADCON1;		Left justify
		bcf	STATUS,RP0;	back to bank 0
		movlw	5;		wait 5*8 = 40 cycles
		call	DelayW;		for ADC capacitor to charge
		bsf	ADCON0,GO;	Start the conversion
WaitADC		btfsc	ADCON0,GO;	Conversion complete?
		goto	WaitADC;	No, so keep waiting
		movfw	ADRESH;		return with result in W
		return

		END
 
Last edited:
Thanks Mike, I'll have a look into it tonight and see if I can understand it all :)

Many Thanks - Trev
 
Hi Guys,

Had a closer look at my code, I've been timing 150us instead of 1500us, that's where my problem is ;). After very crudely altering the timing to 1500us high and 1500us low, the servo correctly centres.

With most digital servos you can update them at 333fps. Which means that they can be sent a 'high' signal every 3ms whilst still using the 1ms - 2ms high pulse to control the positioning. Internally the gearing / motor / pot of the digital servos are the same, the only real difference is that they use a microprocessor in the digitals instead of the old custom logic chip.

I have a 50MHz oscilloscope en route so that I can look into this a bit closer :).

Thanks - Trev
 
Mike (Pommie);

Thanks for adding the pseudo C code just for me (grin). I had already figured out that you were using 256 steps of 4 usecs.

I suspect it wouldn't be too much more difficult to rewrite it for 1 usec resolution, would it?

Code:
;
;
;  int Pot = 0;                      // 0..1023
;  
;  While(1)
;  {
;    Pot = ReadADC();                 // 0..1023
;    ServoOut = 1;                    //
;    DelayUS(1500-(1024/2)+Pot);      // 988..2011 usecs
;    ServoOut = 0;                    //
;    DelayUS(20000-tADC-988-Pot);     //
;  }
Trev;

Thanks for the info' on digital servos. I had no idea you could drive them with a 3 msec Servo period. That's pretty cool...

Regards, Mike
 
Mike said:
I suspect it wouldn't be too much more difficult to rewrite it for 1 usec resolution, would it?
Regards, Mike

No, it wouldn't be difficult to improve it, however, that was not the purpose of writing it. It was written to demonstrate the use of the ADC and software delays. I purposely avoided the use of any variable greater than 8 bit and any hardware except the ADC module. Hopefully, this makes it easier to understand for someone less experienced.

This has reminded me, I must have another go at that multi servo problem from last year.

Mike.
 
PWM control with push buttons

I am new to microcontrollers. I am going to build my first project "Push button controlled PWM" using PIC12F675.

I want to control the duty cycle of 100Hz pulse from 0-100%.
Two pushbuttons, one for increment and another for decrement of duty cycle.
Resolution required is in steps of 2%.

Kindly help me with code.

Don't have knowledge and experience of programming.

Thx. :)
ashwal.patel@gmail.com
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top