PDA

View Full Version : Phase Angle Dimming with a PIC 16F



awhaley
02-28-2009, 01:35 AM
I think this is the closest to the right place to put this...

I just posted a file in the file library that is a stripped down one channel dimming algorithm for a PIC 16F727 controller.

http://www.doityourselfchristmas.com/forums/dynamics/showentry.php?e=11

It's a cleaned up version of an early test file from my dimmer development project... it is just the code for checking ZC, counting through the AC half Cycle, and turning on an SSR at the right point in the cycle. I hope the comments make it clear. It should be very easy to add more channels, and to add any sort of communication code, or standalone level generating code you want inside the MainLoop.

I included what I think you need to know to adapt it to other PICs...

Any suggestions will be appreciated if I'm doing something that could be easier!

I'm posting it here because it sort of becomes a how-to on phase angle dimming with a PIC!

Art

djulien
02-28-2009, 02:13 AM
Any suggestions will be appreciated if I'm doing something that could be easier!

Just a minor point, but the first retfie isn't restoring state:



call CountLoader ; run the sub to load values from V (value)
; variables to C (counter) variables
retfie ; If wer're in ZC, we don't need to run the rest of the interrupt routine


It should restore state like down further:



;restore state and return
movf _status, w
movwf STATUS
movf _w, w
retfie


Also, I'm not sure if the '727 is the same as the '688 and others, but you probably want to use a couple of "swapf" instead of "movf" at the end of the ISR, otherwise STATUS.Z will get changed.

I realize this is preliminary demo code, but if somebody takes it and adds code without looking carefully at the ISR entry/exit, then they'll spend a lot of time hunting for hard-to-find bugs.

Also, it's better to add/subtract from Timers than to just set them to a value. If you add/subtract, you automatically correct for ISR latency, whereas if you set to a constant value, you'll get timing jitter, and possibly drift over a longer period of time.

don

awhaley
02-28-2009, 06:51 AM
Thanks Don!

I'll correct the early exit from the ISR today and upload the code...

And you're right, I have been lazy with just movwf ing the status around, and should correct that too. I see so many people do it the lazy way, but you're right, if the ISR got called in the middle of something that needed the Z bit, I'd mess with it. I'll fix this too later today.

I hadn't heard about the timer drift thing? Any recommended reading on it? I guess I don't imagine it causing damage in this application, since the timer just needs to throw roughly regular interrupts, and the exact timing of that isn't overly critical? there's no reason NOT to use and addwf instead of a movwf I suppose, so after I test the changes to my code I'll update that too.

I really appreciate the advice!

Art

P. Short
02-28-2009, 11:55 AM
That code relies on the foreground code avoiding any memory bank-switching. Any foreground bank-switching would cause the saves to _w and _status to possibly corrupt other registers or to write to non-existent memory. The Renard code only uses code in the first bank, and the Ren-C places the _w and _status variables in locations that can be accessed from all banks.

djulien
02-28-2009, 02:57 PM
I hadn't heard about the timer drift thing? Any recommended reading on it? I guess I don't imagine it causing damage in this application, since the timer just needs to throw roughly regular interrupts, and the exact timing of that isn't overly critical? there's no reason NOT to use and addwf instead of a movwf I suppose, so after I test the changes to my code I'll update that too.

Most of the code examples I've seen use add/sub. If you know *exactly* what the delay time is from when timer0 overflows until you arrive at the instruction that updates timer0, you can just set a value. But that can be a very complex calculation, or not precise if there are conditional instructions in the mix. It's actually a lot easier to use the add/sub - you just make the timer skip ahead the number of cycles needed to shorten its time until overflow, without the need to meticulously account for the instructions leading up to that point.

Here is one example that sort of explains it: http://www.ccsinfo.com/forum/viewtopic.php?t=22467...



The reason why I directly add 6 to whatever timer 0's count is at is because 6 is the number of ticks I need to skip in order for timer 0 to count 250 times before it rolls over. If used:

Code:
Code:
set_timer0(6);



instead, the accuracy of the 1 ms tick would be very poor. That is because it takes quite a long time for the processor to stop what it is doing and service the interrupt. With a 4 MHz clock, this may be 60 - 80 us or more. By the time that the processor gets around to servicing the interrupt, timer 0 will have counted up to 15 - 20 already; by setting it back to 6, what we think is our 1 ms tick will be way too slow.


You're right, for approx time intervals it's not important. I just envisioned someone trying to use the code as a basis for something more complex that needed exact times (like servos or a clock, for example).

don

omzig
02-28-2009, 05:22 PM
Most of the code examples I've seen use add/sub. If you know *exactly* what the delay time is from when timer0 overflows until you arrive at the instruction that updates timer0, you can just set a value. But that can be a very complex calculation, or not precise if there are conditional instructions in the mix. It's actually a lot easier to use the add/sub - you just make the timer skip ahead the number of cycles needed to shorten its time until overflow, without the need to meticulously account for the instructions leading up to that point. Just to clarify this a bit...The reason this works, is that when you reach the code that resets the timer, the timer contains the value that is equal to the number of instructions that have been executed since the beginning of the ISR. So if you add your timer value to this, you have compensated for those instruction cycles. This will also compensate for the jitter caused when the interrupt fires when the foreground code is on a 2-cycle instruction, because when this happens, the timer value will be 1 (instead of 0) when the first instruction of the ISR is executed.

P. Short
02-28-2009, 05:47 PM
Yes, all of this. Also, when determining the value to add/subtract you do need to compensate for the timer increment(s) that are lost when updating the counter register.