12F683 timer 1 question

Discussion in 'Embedded Systems and Microcontrollers' started by dayv3, Jul 26, 2014.

  1. dayv3

    Thread Starter Member

    May 22, 2014
    31
    0
    Hi,

    I have been working on some 12F683 code that uses timer 1 and
    know that you have to treat the two halves of the 16 bit register
    as a high byte and a low byte. But, is there a way to do a compare
    on the whole register as in

    #define VALUE 2400
    if(TMR1 < VALUE){

    I am using Mikro C in case some compilers act differentaly.

    Thanks,
    Dave
     
  2. takao21203

    Distinguished Member

    Apr 28, 2012
    3,577
    463
    how? it's an 8bit controller.
     
  3. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    5,435
    1,305
    Because that 12F PIC does not have the ability to grab a 16bit timer value in one cycle, you need to do a "16bit timer read on midrange PIC".

    The Microchip "midrange reference manual" shows techniques for doing a 16bit timer read. There are probably some appnotes on the matter too.

    Some of it depends on the use of the timer you have chosen and the timer prescaler etc, and since you have not described that the best thing is for you to have a google for the key terms above. :)
     
  4. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,388
    1,605
    C doesn't care what the data size is. On an 8 bit platform it can do 8, 16, 24, 32, and greater, bit operations no sweat.

    So of course you can do exactly what you wish.


    It's a two step process: define a union that can hold both 8 bit quantities, or access all 16 bits at once. Then you need a small function (a macro would also suffice) to read the registers, stuff into the union, and return the value.

    Code ( (Unknown Language)):
    1.  
    2. union Timers
    3. {
    4.   unsigned int lt;
    5.   char bt[2];
    6. };
    7.  
    8.  
    9.  unsigned int ReadTimer1(void)
    10.  {
    11.    union Timers timer;
    12.  
    13.    timer.bt[0] = TMR1L;    // Read Lower byte
    14.    timer.bt[1] = TMR1H;    // Read upper byte
    15.  
    16.    return (timer.lt);      // Return the 16-bit value
    17.  }
    18.  
    19.  
    20. void main(void)
    21. {
    22.     #define VALUE 2400
    23.     if( ReadTimer1() < VALUE )
    24.     {
    25.         ...
    26.  
    27.  
    I'm sure there are many other ways to do this.
     
  5. JohnInTX

    Moderator

    Jun 26, 2012
    2,347
    1,029
    The 12F683 has no facility to read the two 8 bit halves of the timer as a 16 bit value. You can't directly compare the timer to a 16bit value since the compiler has to read the timer in 2 operations; rollover can occur and throw off your readings. Depending on what you are trying to do consider:

    Turning off the timer, reading TMR1H and L into temporary unsigned chars, start the timer then combine the two halves into an int for compare like this:
    Code ( (Unknown Language)):
    1. unsigned int T1image;
    2. unsigned char T1imageL,T1imageH;
    3.  
    4. void main() {
    5.   T1CON.TMR1ON=0;
    6.   T1imageH = TMR1H;
    7.   T1imageL = TMR1L;
    8.   T1CON.TMR1ON=1;
    9.   T1image = (T1imageH << 8) + T1imageL;
    10.   if(T1image > 2400){
    11.     // do something
    12.   }
    13. }
    The problem of course is that you lose some time while the timer is off. To avoid this problem, you can read the timer bytes twice and inspect for rollover before comparing.

    If you just want to know if the timer has reached the count, you can use the CAPTURE/COMPARE facility -OR- load the timer with the complement (65536-count) and inspect TMR1IF to see if its counted up to 0000h.

    You might consider using TMR0 or TMR2 with a prescaler to keep your elapsed time within 8 bits. Reading that would be no problem but the timing would be a bit more coarse.

    Good luck!
     
  6. jpanhalt

    AAC Fanatic!

    Jan 18, 2008
    5,694
    904
    I am C illiterate, but in Assembly, one can capture TMR1H and TMR1L bytes without stopping the counter. Just use the CCP module in capture mode:

    Capture.PNG

    In my application, I use the rising or falling edge of a signal to mark an "event" to capture the two TMR1 bytes without any lost counts. TMR1 just runs continuously, and the math takes care of calculation the periods.

    Wish I could post my example in C, but I cannot.

    John
     
    Last edited: Jul 26, 2014
  7. dayv3

    Thread Starter Member

    May 22, 2014
    31
    0
    Wow!

    I only got one E-Mail that there was a reply.
    I spent a few days with my face in the data sheet and wondered back to
    the forum on there are many replies. Thank you everyone.

    What I am trying to do is to accurately measure the duration of an event
    and if the even takes longer then a given time X. (I really just need the duration of the event but the longer then time X part is a nice touch being that I am trying to learn.)

    At some point, I would like to add the ability to display the running count on a led display but, that is
    not my aim right now.

    I am using a 12F683, but I am open to using a different PIC as long as
    my c compiler supports it.

    My concern is the best approach to reading the running timer.
    I read an app note on reading the running timer 1, but would it be better to use timer 2 instead?

    My other question is about the switch, or debouncing it to be more exact.
    What is the best approach here? If I use a debounce ISR then the timers count be off by the amount that it takes the ISR to kick in. Can I compensate for this? ... Did I miss anything?

    I look forward to your suggestions

    Thanks,
    Dave
     
    Last edited: Jul 28, 2014
  8. jpanhalt

    AAC Fanatic!

    Jan 18, 2008
    5,694
    904
    Timer 1 is 16-bit; timer 2 is 8-bit. What is the period you need to time and how accurately do you need to time it? Why do you think timer 2 might be a better choice?

    As for notifications, I believe AAC only sends one notice of a response. It is then up to the recipient to view and clear the notice to get another one. This message should have been appended to your notice:


    John
     
  9. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    5,435
    1,305
    The simplest and easiest way to do that task is this;

    1. stop the 16bit timer1, and reset its 2 registers TMR1H and TMR1L to 0 and 0.
    2. when the event starts (/ edge) start timer1
    3. when the event stops (\ edge) stop timer1
    4. now timer1 is stopped, read the TMR1H and TMR1L registers
     
  10. JohnInTX

    Moderator

    Jun 26, 2012
    2,347
    1,029
    RB's approach will work but with some lost precision due to overhead. I've done this:
    Use the CAPTURE function with TMR1. Set it to capture on the first edge of your event.
    Start TIMER1. Let it free run.
    When you get a capture (CCPxIF=1) save the value of CCPREG as your first point - the timer keeps running. At this point, you might have to reconfigure the capture for the other edge, depending on your hardware. That's OK as long as you can get it done before the end of the event.
    Clear CCPxIF.
    Wait for CCPxIF indicating the end of the event (as determined by the programmed input edge).
    Read CCPREG and subtract the first captured value from it.
    If the value is positive, that's the duration - expressed in TMR1 cycles.
    If the value is negative (MSB==1) negate it (take the 2s comp) for the duration. (TMR1 has rolled over during the event - no biggie). You could also inspect TMR1IF (clear it at the first edge) and if set (timer rolled over during the event) do the subtraction the other way.

    This assumes that the duration fits into 16 bits of TIMER1.

    I do this kind of things with interrupts on CCPxIF and it works well. Leaving TMR1 free running eliminates errors due latency in detecting the event, turning on the timer etc.

    Only because its 8 bits and can be read on the fly without turning it off/on. Not a perfect solution to be sure.

    The double capture described will compensate for this by giving you some time to respond to the various events.

    I would use interrupts for everything. Use TMR1/CCP as described. Use another timer w/interrupt (doesn't have to be real accurate) to maintain a switch debouncer - on each interrupt sample the switch and increment a counter if the switch is closed, decrement when open - within limits. When the switch is closed for a number of interrupts (an easy way to test is look at one bit e.g. bit 5 in the counter is 2^32 tiks) and issue a flag for switch closed when it reaches that value. If the switch is open, decrement to 0 (but test for 0 and don't decrement past it) and clear the flag for switch open. This way the switch is debounced both ways. Add just a little logic to set a flag when the count first reaches the debounce time, I call this the switch REQUEST. Then the main (non-interrupt) code just samples the REQUEST flag. If set, the switch has closed. Main clears the flag and it will not set again until the switch counts down to 0 and back up to the count. Now you don't have to worry about any switch things - one push, one REQUEST.

    If you take the time to make the captures and switch interrupt-handled, all the main routine has to do is loop around for the switch REQUESTs and capture results. Using interrupts and peripherals unloads much of the timing problems encountered with a straight line approach.

    Just my .03 (inflation)

    EDIT: Re-reading, I didn't explain that very well. If it sounds like a potential solution, we can expand on the concepts.
     
    Last edited: Jul 28, 2014
Loading...