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.

12F683 PWM Duty Cycle

Status
Not open for further replies.

jimg

Member
Edit**** The following is my trials and tribulations of getting PWM to work properly on a 12F683. All of the problems were caused by one incorrect instruction in my program, finally found by Pommie, - I typed movf rather than movwf. I am correcting this first post so that anyone reading this thread can get the information they need up front. I am also revising my method for figuring out the values to set up PWM slightly for clarity. My original post was correct, but misleading and incomplete. So....

My understanding of PWM period and duty cycle are:

PERIOD is the number of instructions cycles you want it to take for one cycle of the output, on then off.

PULSE is the number of instructions cycles for the on portion of the cycle.

You put the (PERIOD - 1) into PR2.
You put the PULSE into CCPR1L.

You may also add 1, 2, or 3 quarters of a pulse to the pulse length by putting 1, 2, or 3 into CCP1CON bits 4-5.

This assumes the Timer 2 prescaler (T2CON bits 0-1) are set to zero.
You may multiply both the period and pulse by 4 if you put 01 in T2CON bits 0-1
You may multiply both the period and pulse by 16 if you put 2 or 3 in T2CON bits 0-1


For example, I have a 32768 hz crystal clock. This gives 8192 instruction cycles/second. I want approximately 1Khz output so the best I can do is 8 instruction cycles per period. I want 25% duty cycle, so that would be 2 for the pulse width.

Code:
;** note, case sensitivity turned off ( /c option), sorry for any inconvenience.
 include p12F683.inc
 __config _BOD_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_OFF & _FCMEN_OFF & _LP_OSC
    org 0
    bsf     status,rp0  ; bank 1
    bsf     trisio,2    ; make gp2/ccp1 an input, disabling pwm while we are setting up the registers
    bcf     status,rp0  ; back to bank 0
    clrf    gpio
    clrf    tmr2        ; clear timer2
    bsf     status,rp0  ; bank 1
    movlw   .7          ; set up for approx. 1 khz pwm with 25% duty cycle (32768 crystal)
    movwf   pr2         ; put period=8 instruction cycles into timer2 (set pr2 = period-1)
    bcf     status,rp0  ; bank 0
    movlw   2           ; want pulse width = 25% of PWM period. = 2 instruction cycles.
    movwf   ccpr1L      ; 
    movlw   b'001100'   ; add 0,1,2,or 3 quarters of an instruction cycle in bits 4-5 of ccp1con
    movwf   ccp1con     ;   last 4 bits (1100) set pwm mode to active-high
    ;movlw   0          ;  multiplier, multiplies both period and pulse by 1,4, or 16
    ;movwf   t2con      ; 00=no multiply, 01=multiply by 4, 10 or 11 = multiply by 16
    bsf     T2Con,Tmr2on ; start timer2
    bsf     status,rp0  ; bank 1
    bsf     trisio,2    ; make gp2/ccp1 an output, enabling pwm
    bcf     status,rp0  ; back to bank 0

d2  goto d2
    end

That's it. No muss, No fuss, No ugly multiplications by log2 or other yucky stuff like in the data sheets!

Edit-- changed program to enable/disable pwm using trisio, and to set ansel.
Edit again-- removed the set ansel, doesn't seem to be necessary.
Edit once again. It is not necessary to turn off the comparator so removed that code.
 
Last edited:
here's an excel that I made to calculate the different values

https://projects.dimension-x.net/files/pwm_calc.xls

man you assembler guys got it rough if all that code is needed to initialize the CCP into pwm mode?

other than setup the various ccp and timer2 registers, this is all I need to program the duty cycle in Proton

Code:
	  TempWord = pwmDuty ' 0 to 1023 = 0 to 100%
		
		TempWord=TempWord<<6
		CCPR1L=TempWord.highbyte
		CCP1CON.5=TempWord.7
		CCP1CON.4=TempWord.6
 
Ok, the results so far:
Code:
  for oscillator frequency = 32768hz, wanted pwm frequency = approx. 1Khz

  for timer2 prescaler = 1  T2CON bits 0-1 = 00

                (period-1)  Duty Cycle value to put into 
                       PR2   CCPR1L(bits 0-7):CCP1CON(bits 4-5)
spreadsheet            7             00000000  10
PWM calculator site    7             00000001  01

My calculations above agree with the spreadsheet. Tried both, nothing works.

Edit: I now see that duty needs to be based on oscillator freq, not instruction cycle, so should be 4 time bigger, so I don't agree with either now, the duty should be 4*2 or 1000, 2 in ccpr1l, 0 in ccp1con.

Is there something wrong with my code? I mostly copied it right out of the manual. I should be able to see a rectangle wave on GP2 with a scope, right?

Is there some restriction on using PWM when running from a 32768 crystal in LP mode? Can somebody with a 12F683 and a 32768 crystal try this? I'm sure there is just some bit somewhere I'm not setting properly, but I just can't find it!
 
Last edited:
You've all ignored the fact that GP2 is a multipuroose pin. I do not think that the default pin function after RESET is to be a digital output. IIRC it is an Analog Input. Check to make sure that the ANSEL and CMCON0 registers correctly disable the Analog Input.

RTFDS!
 
Last edited:
Papabravo said:
You've all ignored the fact that GP2 is a multipuroose pin. I do not think that the default pin function after RESET is to be a digital output. IIRC it is an Analog Input. Check to make sure that the ANSEL and CMCON0 registers correctly disable the Analog Input.

RTFDS!

isn't that what the instruction

clrf trisio ; make all output

does?
I also have a clfr ansel in my code that wasn't in my earilier posting, but still no good.

I've been "RTFDS" for days. Perhaps you could be a little more helpful with some working code?
 
Last edited:
Your code looks correct to me. Assuming that you have now changed it to have PR2 = 32 and CCPR = 8.

I'm assuming that the endif is a typo of some kind. Otherwise, it could be missing the last bit of code.

Mike.
 
Pommie said:
Your code looks correct to me. Assuming that you have now changed it to have PR2 = 32 and CCPR = 8.

I'm assuming that the endif is a typo of some kind. Otherwise, it could be missing the last bit of code.

Mike.
PR2 = 32 ??? Now I'm really confused. I thought I had that right all along.
Yes, the endif was a leftover. Here's my latest code exactly as it stands.

EDIT**** I removed code, it had one error as pointed out by Pommie later in this thread. See the first post for the corrected program****


I tried several 12F683's, still no results. I'm beginning to think it just won't work at this frequency.
 
Last edited:
Sorry, my mistake, I read your post regarding "should be 4 time bigger" and hadn't realised you changed your initial post. I just wanted to point out the endif.

I haven't time right now, but could try this later with a 16F88 and a 38k crystal (all I have at the moment). If someone has a firefly, they could try this now with the 32k int osc.

Mike.
 
My mistake again. I should read the datasheet before posting.

Anyway, this code gives me a 25% duty cycle on a 16F88 using the internal RC clock at 32kHz.

Code:
		include	"p16f88.inc"
		errorlevel	-302	; stop the annoying " Register in operand not in bank 0." error.
	__config  _CONFIG1, _CP_OFF & _CCP1_RB3 & _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

		org     0h
		nop	;		required for ICD2
		bsf	STATUS,RP0;	select bank 1
		bcf	STATUS,RP1
		movlw	(0<<NOT_RBPU|0<<INTEDG|0<<T0CS|0<<T0SE|0<<PSA|B'000'<<PS0)
		movwf	OPTION_REG
		bcf	TRISB,3	;	make b3 output
		clrf	OSCCON
		clrf	ANSEL
		movlw	7
		movwf	PR2
		bcf	STATUS,RP0
		movlw	1
		movwf	CCPR1L
		movlw	(0<<CCP1X|0<<CCP1Y|b'1111'<<CCP1M0);	enable PWM on CCP1
		movwf	CCP1CON;	
		clrf	T2CON
		bsf	T2CON,TMR2ON

loop		goto	loop

		END

As you can see the code is identical to yours except I use 4 as the duty cycle.

HTH

Mike.
 
Spotted it,
Code:
    movlw   b'00001100' ; 2 least significant bits in bits 4-5,
    movf    ccp1con     ;  , last 4 bits set pwm mode to active-high

The movf instruction moves CCP1CON to itself. It should of course be movwf.

Mike.
 
Outstanding! :D

I can't believe I didn't spot that after two days of looking.

Pommie is the BEST. :p

(and yes, I'm embarrassed:eek: )
 
Last edited:
All is well and working. See the first post for the corrected minimal program and an explanation of how to get the required values and where to put them:)
 
jimg said:
isn't that what the instruction

clrf trisio ; make all output

does?
No that is not enough. Focus on the diagram of pin GP2 in the datasheet, (figure 4-3, p. 35) and notice all the different places that an output signal can come from or an input signal can go to.

I've not used a 12F683 for a PWM output, but numerous other posters have run into the identical problem. They have not covered all the bases when it comes to configuring a pin with five or six different functions.
 
Okay, From the PIC12F683 data sheet:
Code:
11.3.7 SETUP FOR PWM OPERATION
The following steps should be taken when configuring the CCP module for PWM operation:

1. Disable the PWM pin (CCP1) output drivers by setting the associated TRIS bit.
2. Set the PWM period by loading the PR2 register.
3. Configure the CCP module for the PWM mode by loading the CCP1CON register with the appropriate values.
4. Set the PWM duty cycle by loading the CCPR1L register and DC1B bits of the CCP1CON register.
5. Configure and start Timer2:
     Clear the TMR2IF interrupt flag bit of the PIR1 register.
     Set the Timer2 prescale value by loading the T2CKPS bits of the T2CON register.
     Enable Timer2 by setting the TMR2ON bit of the T2CON register.
6. Enable PWM output after a new PWM cycle has started:
     Wait until Timer2 overflows (TMR2IF bit of the PIR1 register is set).
     Enable the CCP1 pin output driver by clearing the associated TRIS bit.

So that means you should disable the pwm output by making gp2/ccp1 an input before setting all the stuff-

bsf status,rp0
bsf trisio,2
bcf status,rp0

setup all your registers
;;;(be sure to do a "bcf ansel,ans2" to get the pin away from the a/d converter)
edit -- this ansel step no longer seems to be necessary.

start timer 2

when you want the PWM to start do -

bsf status,rp0
bcf trisio,2
bcf status,rp0

when you want it to stop do another -

bsf status,rp0
bsf trisio,2
bcf status,rp0

leaving timer 2 running all the time.

If you want to ensure that your first pulse is a full sized one, wait for PIR1,TMR2IF to become true

xx
btfss pir1,tmr2if
goto xx

then clear trisio,2


------

Anything else you can think of?


Edit---

The easier way to start and stop the PWM is a Mike says below----
 
Last edited:
I thought that the correct way to stop the PWM was to write a period of zero.

PapaBravo, looking at the diagram, it looks like leaving the pin as analogue and leaving the comparator on would allow the pin to still work as a digital output. BTW, it's page 37 in my DS - is yours out of date?

Jimg, can you try leaving the pin as analogue AND leaving the comparator turned on and see if you still get a PWM output.

Mike.
 
Pommie-

If I don't turn off the comparator, then I can't get the PWM output no matter what I do.

It seems to work if I leave the pin as analogue or not, makes no difference.
 
Last edited:
Why can't you turn it off by doing,
Code:
    clrf   ccpr1L

And to turn it back on,
Code:
    movlw   2
    movwf   ccpr1L

Does this not work or is there something I'm missing?

Mike.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top