# 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

#### Pommie

##### Well-Known Member
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:
;###############################################################################
;    	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){
;        ServoOut=1;         //Turn on output
;        Delay8(250);        //delay 1mS
;            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
bcf	STATUS,RP0;	back to bank 0
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
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
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

movwf	ADCON0;		select pin A3 and 16 TOSC
bsf	STATUS,RP0;	bank 1
bcf	STATUS,RP0;	back to bank 0
movlw	5;		wait 5*8 = 40 cycles
call	DelayW;		for ADC capacitor to charge
goto	WaitADC;	No, so keep waiting
movfw	ADRESH;		return with result in W
return

END

Last edited:

#### trvo

##### New Member
Thanks Mike, I'll have a look into it tonight and see if I can understand it all

Many Thanks - Trev

#### trvo

##### New Member
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 - K8LH

##### Well-Known Member
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)
;  {
;    ServoOut = 1;                    //
;    DelayUS(1500-(1024/2)+Pot);      // 988..2011 usecs
;    ServoOut = 0;                    //
;  }
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

#### Pommie

##### Well-Known Member
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.

#### ashwal

##### New Member
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.
[email protected]

Status
Not open for further replies.