Help me get a grip on TMR0

Discussion in 'Programmer's Corner' started by ke5nnt, Dec 13, 2011.

  1. ke5nnt

    Thread Starter Active Member

    Mar 1, 2009
    384
    15
    I'm new to the timer 0 function of microcontrollers, and I'm having a bit of difficulty figuring something out.

    The basic jist of what I want to do is:

    Program is running in a loop - A button is pressed
    Button press causes the MCU to execute a different loop of code, at the same time, TMR0 is started. The loop continues to execute while the TMR0 basically counts 6 seconds. At the end of the 6 seconds, a value is written to the EEPROM and then the loop continues and waits for another button press, which would send the MCU to yet another loop to start the process over.

    I can try to explain a bit better if I need to.

    My thoughts are:
    The time it takes TMR0 to overflow is quite short, even with a prescaler set to 128. I imagine that I would have to have a variable defined and set a value to it. Each time TMR0 overflows, increment this variable and clear the TMR0 interrupt flag until the variable reaches the set value. I just don't have any idea how to figure out what value this should be, not sure about the math. With an oscillator of 4MHz and a 128 prescaler: 1/[oscillator/4] x prescaler x 256 = overflow time? Using numbers: 1/[4MHz/4] x 128 x 256 = ~32.8mS?

    If the above is correct, set a variable to increment each time the timer overflows. For 6 seconds divided by 32.8mS would be roughly 183 counts, so TMR0 interrupt flag set, increment count, if count ==183 write EEPROM and clear IF, if not, Clear IF and keep counting.

    Let me know if I need to clarify anything, and thanks for the help. Sorry about the length of the post.

    Regards
     
  2. MrChips

    Moderator

    Oct 2, 2009
    12,418
    3,355
    Firstly, you must state which MCU you are using or plan to use. Every MCU is different in the way timers are implemented.

    Secondly, you need to state clearly what are your final objectives. The solution can change depending on what you are attempting to do.

    Thirdly, you need to determine what is the accuracy you desire. How much of a deviation from 6 sec can you tolerate? If you are doing time-of-day measurements where you need the time to be as accurate as possible then what you have described is not the way to do it. For time-of-day measurements you keep the timer running and you never stop it. Even though MCUs have a limited word size, 8 or 16 bits, this is not a problem and there are ways around this basic limitation.
     
    Last edited: Dec 13, 2011
  3. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    Those calculations and method are fine for a delay of roughly 6 seconds. You have to make sure the IF gets checked more often than every 23.8ms.
     
  4. ke5nnt

    Thread Starter Active Member

    Mar 1, 2009
    384
    15
    Alright fair enough, and thanks for the response.

    The target MCU is the 12F629, it has all the features I need, and I only need 3 I/O Ports. I am using the Internal Oscillator, so the 4MHz value can be changed, and was used here as an example only.

    To answer the question of accuracy right off, it's not that important. Target is around 6 seconds, but that figure is ballpark. The intent of the delay is simply to give the user time to determine whether the currently operating loop is what they want before it writes that loop to the EEPROM.

    The operation is basically summarized like this:

    The program starts and initializes all the important MCU stuff

    The program reads the EEPROM to see if there is a value stored there, which directs the MCU to perform a specific loop. If no value is found, it goes to the default loop.

    Once in a loop, the MCU sits there. Each loop generates a frequency which is output by cycling an I/O port on/off at a specific rate. It does this continuously until a button is pressed, which interrupts the program.

    The interrupt debounces the button, and increments a variable named "loop" to the next number, which sends the MCU to the next loop in line after clearing the button IF.

    The new loop starts TMR0 and then proceeds into the loop, which operates a new frequency. After TMR0 elapses ~6 seconds, the value of the variable "loop" is written to EEPROM so that the next time the device is turned off and then back on, it reads this number and automatically starts in the loop number that was written to EEPROM. After the EEPROM write cycle is complete and the EEPROM IF is cleared, it goes back into the loop and stays there unless the button is pressed again.

    This whole cycle is pretty much repeated for about 10 or so different loops to give the user a choice of which frequency to use, but in all likelihood, once a frequency is selected, it would be rare for a user to select a new one. But the option to change it is always there.
     
  5. ke5nnt

    Thread Starter Active Member

    Mar 1, 2009
    384
    15
    Writing a response to your post has just brought up more questions for me. After my previous post, I'm going to see what kind of responses I get before asking anything more with the exception of simply asking you to explain the reason behind checking the IF more often than every 23.8ms.

    Thanks
     
  6. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    Try to prescale the timer to be an even mS number.

    Declare a long int (Assuming you are using C)
    Each time you are in the interrupt loop, add the mS to the long int, if total >= 6 seconds, set 6 second flag and reset timer to 0mS
    When interrupt is entered again, first check for timer0 triggering, do the mS add, reset tmrif. Check for pin interrupt, if so do set a flag for button pressed, (or set ms6second timer to 0 in here) reset interrupt.

    Main loop simply runs, waits for button status to change (from inside the interrupt), reset the mS timer, wait for 6 second flag to be reached, take action.

    Does that make sense? I kinda put two ways to do it in the description above in a think out loud how I would do it way, rather than a pseudocode way.

    --ETA: Do EEPROM write in main loop from flag set after button pressed and time is up, it's a slow process.
    Also, do not check for == on the mS counter, check for >=, in the event the timer misses a beat due to processing the button trigger or a glitch that jumps the timer.
     
  7. atferrari

    AAC Fanatic!

    Jan 6, 2004
    2,645
    759
    The first thing to do is to calculate how many Tcy (duration of a machine cycle period) you need to reach 6 sec. Call it "HM".

    Now, make Count = HM / (256*prescaler) and that are the times that TMR0 has to wrap around to reach 6 seconds.

    Keep in mind that Tcy is 4*Tclk (clock driving your micro).It take 4 of these to get one of that.

    Sorry to say, but instead of posting here I would simulate it with MPSIM using the Stopwatch to see immediately how far/close I am from 6 sec.
     
  8. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    To elaborate, there are at least couple of ways to use TMR0.
    1) Enable the interrupt for TMR0 and in the interrupt routine increment your counter and check for completion.
    2) Disable the interrupt but check the interrupt flag in a loop.
    I got the impression that you were using method 2.
    Method 2 probably uses a slightly higher percentage of total CPU cycles, but has the advantage that you know exactly where in the program it will happen, so there is no chance of an interrupt messing up precise timing in the main program.
    The reason for checking more often than every 32.8ms in method 2 (sorry my earlier 23.8ms was a typo) is that otherwise you will miss some of the timer rollovers and the 6 second delay could become much longer.
     
  9. ke5nnt

    Thread Starter Active Member

    Mar 1, 2009
    384
    15
    Ok, good info, and I appreciate it. I'm sitting at my desk with pencil and paper and trying to wrap my head around exactly how to work TMR0, and how TMR0 performs. Let me ask...

    Let's say I setup timer0 to increment on falling edge, every time the count reaches 255, TMR0 overflows, which generates an overflow interrupt and will send the program to an interrupt service routine (ISR), which is a separate ISR from the button press interrupt?

    How do you actually start tmr0 counting, and how do you stop it once you don't need it to count and interrupt anymore? As in, once the count reaches the 6 second mark and writes data to eeprom, the program goes back into the loop and TMR0 is no longer needed until the button is pressed again. This could potentially be indefinitely.

    If I wanted to keep the EEPROM write command inside of the main loop, how would I do that without the EEPROM being written to each time a loop cycles?

    Here are some thoughts of mine on program execution:

    Loop is running and a button is pressed>
    Button ISR> debounce switch> Clear tmr0 and set it to start counting, and clear the count variable> clear the button interrupt flag>increment loop number> go to the next loop as indicated by the recently incremented loop number

    Timer overflows sending program to TMR0 ISR
    if count >= 6 seconds return to pattern
    else count++ > clear tmr0 interrupt flag > return

    I'm thinking that in the loop:
    while count>= 6 seconds
    stop tmr0 completely
    loopnum==eeprom_write(address)
    clear count
    clear interrupt flags (if any)
    while count<6 seconds
    just do the loop

    Does any of that make sense? And just for clarification, yes, this program is being written in the C language using MPLAB IDE and Hi-Tech C-Lite

    Really appreciate the help guys
     
  10. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    Close, but you have one function for interrupt:

    interrupt()
    {
    if (Timer0 overflow)
    Keep track of time stuff
    reset timer0if
    }

    If (button interrupt)
    {
    debounce
    set flag to do whatever needs to be done on button press
    reset externIF (Forgot that register right now!)
    }
    } // end of interrupt function
    main()
    {
    setup

    while(1)
    {
    sit around checking variables changed by interrupts
    act on those variables
    }
    } //end of main function
     
  11. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    I don't know C, so can't help with that.
    TMR0 always runs, it is either clocked by the internal instruction clock or by changes on pin GP2. I don't think there are any disadvantages to leaving it on the internal instruction clock when it is not needed, you can either disable the TMR0 interrupt or just not check the TMR0IF if you are just polling the flag.
    I'd just set a bit of a register for while the 6 seconds is running, then when the time is up, write the EEPROM and clear the bit.
    The rising/falling edge is only meaningful if TMR0 is set to changes on GP2.
     
  12. ke5nnt

    Thread Starter Active Member

    Mar 1, 2009
    384
    15
    Ok cool, thanks guys. I'll see what I can do and report back in a few days. Thanks again for all your help!
     
Loading...