This post originated as a response to an inquiry about how to get exact 1 second timing from a 4 MHz PIC. I replied to the inquiry, but it struck me that some list members might not read the post because the heading did not make it look like it had anything to do with achieving precise timing. So, I have re-posted under the current subject heading in the hope that more people will see it and perhaps get some ideas as to where they can use such techniques in their own PIC programs. ************************************************************* *** How can I get precise 1 second timing on a 4 Mhz PIC? *** ************************************************************* One way I have tackled this problem is to set the prescaler to 4. Now, normally this would cause an interrupt every 1024 microseconds when using a 4 MHz crystal. The sneaky trick is to *add* 8 to TMR0. We actually want to increase the current contents of TMR0 by 6, but we add the extra 2 because TMR0 is not incremented for 2 instruction cycles when it is changed by our add routine. (Effectively the counter will now roll over after 250 counts instead of 256 counts). We add instead of just stuffing 6 (actually stuffing 6+2 to be exact), since the actual count in TMR0 can vary slightly depending on whether the interrupt occured during a 1 or 2 cycle instruction. Also, some time has elapsed since the interrupt routine was entered and the time it took to get to the point in the isr where you are modifying TMR0. By ADDING a correction value to TMR0 we get around the problem of trying to figure out the current value of TMR0. Now the time it takes between interrupts is no longer 1024 microseconds, but 1000 microseconds. This is a much nicer number to be working with! The time between interrupts is corrected such that it is now 1 ms. A 16 bit counter inside the isr is counted up from 0 until it gets to 1000. When it gets to 1000 it is reset to 0000 and then any counters that are based on the 1 second interval are updated. It is also very useful to have a software bit flag called one_sec that is SET each time the isr's 16 bit counter is reset. Programs in main can clear this flag and wait until it goes high. When they see it go high they can clear a counter that is usually incremented in the isr. Thus the bit flag allows you to synchronize to the beginning of a new 1 second period. You don't HAVE to synchronize, though. If you skip the synchronization and just reset the appropriate counter immediately, the counter will reach a count of 1000 with a timing error of plus or minus 1 millisecond. Not too shabby. If you use the synchronization method your error will only be a few microseconds instead of plus or minus a millisecond. Let's say you wanted a ten second delay: after resetting the appropriate isr counter, you would monitor the state of the counter in a loop. When it changes to 10 you know that 10 seconds has elapsed. If you have another function that needs to go off at 12 seconds, then just let the counter continue to run until it reaches 12. Such cumulative uses of a counter will give you a timing accuracy that is just as good at 1000 seconds as it is at 1 second. Relative timing accuracy from second to second will actually be a dozen or so microseconds, and the error is non-cumulative! *************** I use a variation on this scheme where I have my interrupt period set for 1/2 the baud rate period time. Transmission requires only that I get my data set up and then synchronize to a bit flag called half_baud. From there on, start, data, and stop bits are timed by watching the half_baud flag which is SET every half baud period. The program in main is responsible for resetting the flag once it goes high. Any baud rate error that exists is non-cumulative even across hundreds of bit times. The reason I use a half-baud time period is that for uart reception we read in the center of the bit time. Since reception cannot be synchronized in the way transmission can, I use a different sync method for reception. In the isr I maintain a counter that is cleared to 0000 each time we reach the end of a half baud period. All other times this counter is incremented. When we detect a start bit on the uart receive line, we immediately clear the half baud counter that is usually incremented in the isr. We then wait until the counter is non-zero. Then we wait until the counter returns to zero. It will return after a period equal to the half-baud period. This puts us right in the middle of the start bit. Every second time the counter returns to zero represents one full baud bit period. That puts us right in the middle of data bit, which we then read and use to build the 8 bit byte value. The beauty of this method is that you don't have any instruction counting to deal with to get the baud rate right on. Error is non-cumulative. I even have a switch driven i/o line dedicated to informing the isr which of two baud rates I want selected. This determines how the isr will handle the specifics of updating. With absolutely NO changes in the uart receive code in the main program I can switch baud rates on the fly. Fr. Thomas McGahee -- http://www.piclist.com hint: PICList Posts must start with ONE topic: "[PIC]:","[SX]:","[AVR]:" =uP ONLY! "[EE]:","[OT]:" =Other "[BUY]:","[AD]:" =Ads