PIC TIMER0 problems/questions

Discussion in 'Embedded Systems and Microcontrollers' started by Shreder001, Jun 22, 2009.

  1. Shreder001

    Thread Starter Member

    Mar 7, 2009
    22
    0
    Hey everyone, I just have a few questions regarding TIMER0 in the PIC16F684. I am using this MCU in an application where if a certain condition is detected for 2 seconds, it will enable reverse mode. I was wondering if I could use TIMER0 to do this and if anyone could tell me how to make it count up to 2 seconds. Another Basic question I have is TIMER0 interrupts upon it's rollover. When an interrupt happens, how do you know? Does it jump to a certain address? By the way I am already using TIMER2 and TIMER1 for PWM. THANKS!
     
  2. eblc1388

    Senior Member

    Nov 28, 2008
    1,542
    102
    You certainly can.

    However, Timer0 is a 8-bit timer and usually counts from 0 to 255 too quickly even with a high prescale factor. However, this doesn't preclude the use of Timer0 for timing long events of seconds or minutes.

    For that, one needs a "long" 16, 24 or 32 bits software counter, clocked via Timer0 overflow.

    Let's say Timer0 rollovers(i.e. goes from zero to 255 and back to zero) every few milliseconds. One can detect this condition by polling the overflow flag with interval shorter than one complete rollover period. This will detect the rollover event and user can then increment/decrement the above software counter. Thus the value of this software counter will give whatever timing needed.

    One then needs to clear the "overflow" bit to enable next overflow to be registered.

    Another better way is to use Interrupt in this case. Your PIC 16F684, as different to AVR, do not have vectored interrupts. This means that interrupts from difference sources will ALL cause a jump to address 0x0004. In the interrupt code, one then check to see what causes the interrupt to happen using software. However, you don't need to do that as Timer0 is the only source that can cause an interrupt.

    Inside the interrupt code, one just increment/decrement the same software counter mentioned above. If a certain value is reached, set another flag to signal the end of the timing. One can then exit the interrupt. For some purists, they only change the software counter inside the interrupt and leave all the checking in the main code.

    Of course you have to tell the PIC you want interrupt jump to happen using the GIE bit in INTCON register. Any registers, including W, which value might change would need to be stored first on interrupt entry and restored at the very end of interrupt exit. The PIC data sheet will tell you how to do exactly that.

    In your main code, just check if the flag has been set, if so perform the required action.
     
  3. Vaughanabe13

    Active Member

    May 4, 2009
    102
    5
    To answer a few of your questions:

    When any interrupt occurs the program counter will be loaded with the address of the interrupt vector. Depending on the chip, there may only be one generic interrupt vector, or there may be two different interrupt vectors - one for low priority and one for high priority. I don't know which setup your PIC16F has but I am guessing it has one interrupt vector, which you can confirm by searching the manual. You MUST provide code that will tell the PIC where to go when an interrupt occurs, and there are many examples of this online. Typically you will have a call statement to go to an interrupt service routine.

    Now you must write your interrupt service routine. You can have multiple interrupts direct to the same routine so it is important that you check to see which interrupt flag was set. Each different kind of interrupt (TMR0 rollover, pin change on port B, ADC conversion, etc. etc.) will set its own interrupt flag. The TMR0 interrupt flag is TMR0IF, so you simply check that to see if it is set, and if it is you know TMR0 has rolled over, so you can write your code to do something. Another thing you might want to consider is disabling all interrupts once you get into the service routine, so that nothing else can interfere with your interrupt code.

    Now, to get TMR0 to roll over after 2 seconds, you need to do some calculations. Since you are working with an 8 bit timer, you have 255 timer increments before it will roll over, so you can take several steps here.
    1. Use the TMR0 prescaler. Set it to a high number if your FOSC is fast. Say you have a 10MHz crystal in your circuit and you set the TMR0 prescaler to 1:256. Then, (10000000/4)/256 = 9765.625 Hz, so TMR0 will take 1/9765.625 = 0.0001024 seconds per instruction cycle. If you wanted to delay 2 seconds from that you would need to increment a counter 19,531 times, since that is 2/.0001024.

    Now you have to do more work, since TMR0 is only 8 bits and it can only count to 255 before it rolls over. Using one of the 16 bit timers is one way to go, or you can use a two-byte method. The binary equivalent of 19531 is 100110001001011, so you could set the bytes as follows:
    CountHigh = 01001100
    CountLow = 01001011

    But the interrupt will occur every 256 instructions, so a better way would be to divide your total instructions by 256, so your interrupt service routine should occur 19531/256 =~ 76 times before you do your code. You lose a little bit of timing accuracy by doing it that way, because our combination of clock frequency and TMR0 prescaler don't work very well together. You could experiment with different clock/prescaler combinations to find something that works.

    Or you could do it a different way, without using interrupts at all, depending on how the rest of your code works. The algorithm works like this:
    -Reset TMR0 so it starts counting from 0 and clear the TMR0IF flag
    -Write your code that happens before the 2 seconds are up
    -After that code is done you need to wait for the 2 seconds to finish, so call a routine (example: call wait)
    -Poll the TMR0 flag until it is set. Example:
    wait
    btfss INTCON,TMR0IF
    goto wait
    ;Do code here, meaning TMR0IF has been set

    The code will keep looping over and over and nothing else will happen until TMR0 has reached 2 seconds of delay. The disadvantage here is that all of your instructions have to fit inside the 2 second period, otherwise if it takes longer than 2 seconds to execute the other instructions you won't have good timing.

    So basically, this is why 8-bit architecture sucks. Long delay times are not fun. If you can give up one of your 16-bit timers to do this and then use an 8 bit timer for PWM or whatever you're doing that would be ideal. It's still possible without doing that but it's harder.
     
Loading...