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.

:; Struggling With PWM ::

Status
Not open for further replies.

suby786

New Member
Hey all..

ive ben recommended that this site is ace... and im hoping its true lol...

im interfacing triacs with a PIC 16F877A... and i need a PWM period of 20ms (50Hz) with a 10% or so duty cycle...

now ive read a few posts on here regarding getting this and its pretty hard to get 20ms period on pwm module but i still dont get it..some people HAVE got 20ms period by choosing correct register values??? but they havent put up their register values... im using a 20MHz xtal... which i believe is a 200ns cycle time...i preferebaly wouldnt like to reduce the crytsal but if its the ONLY way then i will do.

im using the timer2 for the PWM module as instructed by datasheet, and CCP1 as the PWM output pin...

at the moment ive set PR2 at its max...256 and 16 postscaler and 16 prescaler, and i should get 13.1ms

but i dont i get a freq of 1.2kHz on my scope...which doesnt give a period of 13.1ms... HELPPPPPP im a n00b and im gonna admit it lol

Code:
	MOVLW	h'ff'		;Setup PR2 for PWM
	MOVWF	PR2
...............
..............
	MOVLW	d'50'              ;Random number for testing purposes...i get 20% duty with this
	MOVWF	CCPR1L			;Set duty cycle
	MOVLW	b'00001100'
	MOVWF	CCP1CON			;Mode Select PWM
	MOVLW	b'01111111'		;post scaler 16/prescaler 16
	MOVWF	T2CON
 
You can't do what you want with the PWM module but you can do it with the CCP module.

Setup the CCP/PWM for "Special events trigger". This in effect makes CCPR1H:CCPR1L a 16 bit value to reset timer1. With the prescaler set to 2 and a 20MHz clock, timer1 will increment every 400nS. To get a 20mS time frame your total time needs to be 50,000. So, to get a 10% duty cycle you would load CCPRH:L with 45,000 and then 5000.

Timer 1 will now generate an interrupt after 18mS and another one 2mS later. In your interrupt service routine (ISR) you set/clr your output pin and load the new value into CCPRH:L.

The only limitation to this method is the ISR time. The shortest period you can generate is 400nS * the time it takes from the ISR being generated and loading the CCPRH:L registers.

The above will also work with a servo and will give you 2500 unique positions.

HTH

Mike.

I've been wanting to try this for some time and finally got around to it.

This should give you a PWM output on bit 0 of portB. On for 18mS - off for 2mS. Vary the values in ontime and offtime as needed. The shortest time you can have is 8uS - 20 clock cycles.

You should be able to copy this straight into MPLAB and run it with the simulator. Set break points where indicated and check the stop watch:D :D

Code:
;*******************************************************************
;                           16F876 PWM Program
;*******************************************************************

		include	"p16f876A.inc"

		errorlevel	-302
		radix	dec

	__CONFIG _CP_OFF & _DEBUG_ON & _WDT_OFF & _BODEN_OFF & _PWRTE_ON & _HS_OSC & _WRT_OFF & _LVP_OFF & _CPD_OFF

		cblock	20h
OnTime:2
OffTime:2
		endc

		cblock	71h
int_work
int_status 
int_pclath
		endc

		org     0h
		nop
		goto    start
		nop
		nop

interupt	movwf	int_work
		swapf	STATUS,W
		movwf	int_status
		bcf	STATUS,RP0
		bcf	STATUS,RP1
		movfw	PCLATH
		movwf	int_pclath
		clrf	PCLATH

		btfsc	PORTB,0
		goto	TurnOff
; turn on the output and write the on time
		nop;		delay to make both path identical
		bsf	PORTB,0;<<<<<<<<<< Break Point
		movfw	OnTime+1
		movwf	CCPR1H
		movfw	OnTime
		movwf	CCPR1L
		goto	DonePWM

TurnOff		bcf	PORTB,0;<<<<<<<<<< Break Point
		movfw	OffTime+1
		movwf	CCPR1H
		movfw	OffTime
		movwf	CCPR1L
DonePWM
		bcf	PIR1,CCP1IF;	reset special event trigger interupt

		movfw	int_pclath
		movwf	PCLATH
		swapf	int_status,W
		movwf	STATUS
		swapf	int_work,F;	swap to file
		swapf	int_work,W;	swap to work
		retfie


start		bsf	STATUS,RP0
		bcf	STATUS,RP1
		bsf	STATUS,IRP;	all indirest access is to 100h - 1ffh
		movlw	(0<<NOT_RBPU|0<<INTEDG|0<<T0CS|0<<T0SE|0<<PSA|B'000'<<PS0)
		movwf	OPTION_REG
		movlw	b'11111110'
		movwf	TRISB
		bcf	STATUS,RP0
		movlw	(b'01'<<T1CKPS0|0<<T1OSCEN|0<<NOT_T1SYNC|0<<TMR1CS|1<<TMR1ON)
		movwf	T1CON;		enable timer 1
		movlw	low(45000)
		movwf	CCPR1L
		movwf	OnTime
		movlw	high(45000)
		movwf	CCPR1H
		movwf	OnTime+1
		movlw	low(5000)
		movwf	OffTime
		movlw	high(5000)
		movwf	OffTime+1
		movlw	(0<<CCP1X|0<<CCP1Y|b'1011'<<CCP1M0);	enable special event trigger on CCP1
		movwf	CCP1CON;	
		bsf	STATUS,RP0
		bsf	PIE1,CCP1IE;	enable CCP1 interupt
		bcf	STATUS,RP0

		movlw	(1<<GIE|1<<PEIE|0<<T0IE|0<<INTE|0<<RBIE|0<<T0IF|0<<INTF|0<<RBIF)
		movwf	INTCON;		enable Peripheral interupts

Loop		
		goto	Loop


		END
 
Last edited:
Thinking about the above code, it could very easily be modified to give 14 servo outputs on one 18 pin 16F88 within 20mS :eek: . If each servo pulse is started 0.8 mS before the previous one had finished and all were set to 2mS then total time would be 2+13*1.2 = 16.6mS. If all are set to 1mS then it's 2+13*0.2 = 4.6mS. This could be evened out by starting the next one 0.8mS before if the current one is >1.5mS and 0.2mS before if current one is <1.5mS. With this arrangment the shortest period between IRQs would be 0.2mS - easily managable. 0.2mS=400 instructions with the 8meg internal clock.

I might have to try that.

Edit, on further thought this won't work as outlined here. I think it can be made to work with a little more thought.

Mike.
 
Last edited:
hey mate

ur code works a treat... thank you... ive got 1 problem though... im already using TIMER1 to vary a delay between 1ms and 9ms and i dont think timer0 or timer2 can count up to 9ms (max) can they? if they can then let me know and ill implement eitehr of those to do the 1-9ms delay and leave timer1 for the 20ms delay...

basically its nothing to do with servos... i have a zero cross detector, when the AC signal passes zero, i wait X amount of ms before i turn ON my 20ms PWM pulse... once its on i let it running until the delay for PWM on changes..

are there pics that i can do this TWICE at the same time...but the delay to turn on will be different... u see i have 4 lights that need to be controlled INDIVIDUALLY... meaning i need 4 x 16bit timers? plus 4 x timers (that count up to 9ms - does that need 16bit?)...so 8 timers...i dont see that happening

HELPPPP
 
I'd go about this differently. Use the zero crossing as an interrupt and use any timer you want to wait the desired period.

You don't need 4 or 8 timers. just 1. compute the different trigger times and then the difference between those times. then chain the interrupts for each difference.

An example: you want to turn light 1 on at 1 mS after the ZC, light 2 at 2mS, light 3 at 5 mS and light 4 at 6 mS. That works out to be 1 mS, 1 mS, 3 mS and 1 mS.

So, you take the zero crossing interrupt, turn on the timer interrupt and program the timer for 1 mS
Take the timer interrupt (at 1 mS), do the light 1 thing and program the timer for 1 mS.
Take the next timer interrupt (now 2 mS past ZC), do the light 2 thing and program the timer for 3 mS.
Take that interrupt (now 5 mS), do the light 3 thing and program the timer for 1 mS.
Take the last interrupt, do the light 4 thing and turn off the timer.

you will need to handle the case where multiple lights are set for the same time.

edit: you can use timer 1 for this by starting out with your pre-trigger time as the first timer interrupt.
 
Last edited:
your post has been really helpful...can i extract some more information from you regarding this or am i askin for too much..

when u say "do the light 2 thing", do u mean take pommies code and start 1 of the pwm channels running? this means i need 4 x PWM modules...on the 1 chip.. baring in mind all 4 PWM signals are IDENTICALLY SETUP... its just when they are triggered to start in relation to ZC...

oh and i forgot i dont need 20ms pwm pulse...i need it @ 10ms... i.e. 100Hz PWM...

also what if light 4 needs to be on @ 2ms...and light 3,2 and 1 are AFTER THIS.. i have my intterupt after 2ms... then how would i tell the pic that its 4 that needs2be on and not 1?
 
oh 1 more thing... as i believe i can only use timer1 for the PWM/compare special trigger thing... which timer can i use for the intterupt delays...i.e. ur 1/1/3/1.. which could be 1/3/5/9 for instance... or worst case could be 1/1/1/9 so id need a timer that could reach 8 or 9ms... can either timer0 or timer2 do that with ease?
 
by "do the light 1 thing" I mean do what ever you would do if you had 8 timers. If it's phase control of AC, then I would assume turn on the TRIAC.

what I am saying if forget pwm/cap/comp. Use timer 1 to do the initial delay after you see the ZC interrupt (use RB.0, RB.4, 5, 6 or 7). Then do the laddered timer ints I suggested with timer 1.

You will need to figure out how to determine which triac to trigger. It's not that hard. Just think of it as a list of triacs ordered by trigger time.

By the way, depending on your zero crossing interrupt timing, you may need to turn off the TRIACs before then. The will stay on until the zero crossing, of course. If the ZC int is late, you may inadvertently turn the TRIACs on for the next cycle. I don't know how narrow a trigger pulse your TRIACs need.
 
Can I suggest setting up a 1mS interrupt by setting CCP1H:L to 2500, then, in you interrupt simply increment a variable and in your main code wait for the desired time.

To do your 4 outputs, setup an array of 20 bytes and set the bit that needs turning on in the relevant bytes.
eg,
Code:
; in a cblock do,
OnArray:20
; and in your main code do
		movfw	Out1OffTime
		addlw	OnArray
		movwf	FSR
		bsf	INDF,0
		movfw	Out2OffTime
		addlw	OnArray
		movwf	FSR
		bsf	INDF,1
;etc

Then in your timing loop do,
Code:
		call	Delay1mS
		movfw	OnArray
		movwf	PORTB
		call	Delay1mS
		movfw	OnArray+1
		movwf	PORTB
;etc

HTH

Mike.
 
the triacs do stay on till it passes zero, BUT its important that the trigger pulse is REMOVED before this event happens...

SOOO....if light 1 delay has elapsed...i turn on the output... but i need to turn it off too...say after 1ms... (acual trigger time i cant find in datasheets) - apparently its very small time...

so how would i turn it off???

i dont understand ur code that well mate... i dont really know what ur gettin from it...

also my ZC detection is a bit weird... when the sin wave starts... the pulse goes high...after 10ms, the sine wave passes ZERO AGAIN (going down)... the ZC pulse stays high for a further 2ms... then goes low... then when the sin wave rises again, it goes high... so the freq is 50hz, but duty cycle is about 60% when it should be 50%...why this happens i dont know...

IDEALLY id like a ZC pulse whenever the wave CROSSES zero...but mine doesnt so i cant use ZC interrupt...as it would only happen every cycle..i need a pulse TWICE a cycle...

this is how i was planning on doin it...

i have 4 registers with timer values that reflects the actual delay time...

when the first ZC interrupt happens,
i increment a counter, from 1 to 2
i load timer1 with reg1's value...
when timer1 interrupts, it turns output1 on... and after 1ms turn it off (turn off in 1ms in either software or use either timer2 or 0 - which 1 is best way?)
i then wait...

here i have the problem with the ZC intterupt occuring once a cycle.. i need it twice...

after the 2nd ZC intterupt, cycle 2, i then load timer1 with reg2's delay time... and do the same thing...

so over 4 cycles i carry out my triggering... i then reset my counter and on the5th cycle load my 1st 1 again... (this value can be changed thru my pc-wireless comms)

triggering over this 80ms period will not be noticable and is more in my depth...and i could do it...if i can get the ZC interrupt to work... OR have 4 x PWM modules doing the TWICE a cycle thing...

HELP ME STILL PLZZZ
 
I would look into fixing the ZC detection - it should not be that hard. Getting a square wave should be pretty trivial. With a square wave at 50 hz, you can still get interrupts every 10 mS. there are at least 2 ways and probably more. RB.0 (aka INT) can be programmed as to which edge it sees. You simply flip the edge sense with each interrupt. Or you can use the Int on Change feature of RB.4..7 which will interrupt when ever there is a change on any input pin of the upper nibble of PORTB.

I think you don't see the power and flexibility of the timers. You can create a timer chain to create an arbitrary sequence of timed events.

why don't you post your ZC schematic?
 
here there are 2 ZC circuits...both are quite identical, and to be honest ive mixed and matched them...

ZC1... i have wired it WITHOUT THE 4 DIODE ARRANGMENT...everything else is the same...inc the 1n4148 diode,,,

ZC1... this arrangement WITHOUT THE DIODES is what i did, but i didnt use the 1N4005 for the diode across the inputs... the signal looked better when i used the 4148 that ZC1 used..

will i need this 4 diode arrangement to full wave rectifiy? should i get a different looking signal????

i didnt know about that int on state change... thats a good idea...
 

Attachments

  • ZC1.JPG
    ZC1.JPG
    26.6 KB · Views: 297
  • ZC2.JPG
    ZC2.JPG
    16.4 KB · Views: 259
I would have used an AC opto, like the H11AA - much simpler. https://www.fairchildsemi.com/ds/H1/H11AA1.pdf

why did you remove the diodes? try putting them back in.

Now I understand why you are screwing around with the first delay. Why not build that into the timer delay for the earliest TRIAC firing? As long as the ZC interrupt timing is consistent, you can set your timer to compensate for how ever early it is. And you can do it differently for leading or trailing edge if that's an issue. This can all be done with timer1 and the ZC interrupt.
 
hey mate ur last paragraph...can you rephrase i didnt understand a word of that... when u sed the screwing around with the 1st delay...what are you talking about?????

i would really like ur input

that chip...it does exactly what my circuit does but in a package... excluding pull up and the 2 input resistors...

so i DO need the full wave rectifier,,,???
 
You mentioned needing an inital delay (2 mS, iirc). I think that was because of your ZC was off.

yes, you need the full wave. as I said in my last post, "try putting them back".
 
suby786 said:
i dont understand ur code that well mate... i dont really know what ur gettin from it...

The code is an example of using an array to turn the right bit on at the right time. You have a byte for each mS period and set the bit that needs turning on in that mS. So, if output 2 has to come on after 9mS you would set bit 2 of [array+9]. Then, after you ZC happens you write [array+0] to the port, wait 1mS, write [array+1] to the port, wait 1mS and so on until you have done all 20 bytes. Have a look at how FSR and INDF work in the datasheet.

It is an example of what Philba described as "Laddered timer ints"

Mike.
 
Pommie u are a LEGEND...

i undersdtand what u want me to do now...and it makes alot of sense...im gonna try and implement it tomorrow or this week...

i believe i can implement what ur asking.... i hope...if i run into any problems can i ask any further questions? i hop u dont mind... uve just taken my PWM problem off my head...and i guess to overcome turning each output off... i can wait till 1ms before the end of the cycle and turn off all outputs at teh same time...as there cant be a trigger AT ZC...as ill be false triggering the next cycle...

u r a legend mate
 
hi im shoghi!!

im able to use pic16f873 and set it to pwm mode, but it can controll only one led,the problem is i wan to controll 3led using that one available pwm output, i wan to do RGB color changing project for my car dashboard.i very new to pic..i noe assembly only abit..any idea how to use that one pwm to controll rgb leds and get the color i want myb by pressing a switch
 
You have 2 PWM modules so you can control 2 of the 3 colors. You might want to do sw PWM to get all 3. Not that hard to do.
 
Last edited:
so if u have a pic with 3 PWM outputs (and some with 4 PWM outputs...) does that mean it has 4 timers associated with it...? to do the period timer..
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top