+ Post New Thread
Results 1 to 16 of 16
  1. #1
    Full Member level 5
    Points: 1,316, Level: 8

    Join Date
    Mar 2017
    Posts
    253
    Helped
    5 / 5
    Points
    1,316
    Level
    8

    Please explain my mistake related to 16 bit timer...

    Hey,
    There is something I don't understand in behaviour in my code. Can someone explain me whats wrong?

    Basically, I have a 16 bit timer0 for PIC18F4550.
    I set it to count impulses.
    At the start I define:
    Code:
    // 65536-50000=15536
    // dec 15536 is hex  3CB0
    #define START_VAL_TMR0H 0x3C
    #define START_VAL_TMR0L 0xB0
    
    (...)
    And I start timer with those values, so it counts up to 65536.
    Code:
            TMR0H = START_VAL_TMR0H;         // Initial count
            TMR0L = START_VAL_TMR0L;
    And in the interrupt I reset it back to 15536:
    Code:
    void interrupt(void) {
     // UART1_Write_Text("int\n");
      // http://www.enmcu.com/software/timer0calculatorandcodegeneration
    
      // Reset the timer count
      TMR0H=START_VAL_TMR0H;
      TMR0L=START_VAL_TMR0L;
        myCounter++;
      // Reset interrupt flag
      INTCON.TMR0IF = 0;
    
      // Do some work here
    }
    So, basically... timer starts at 15536, goes up to 65536, and resets back to 15536 - right?

    Actually, something else happens... sometimes (after stopping timer) the:
    Code:
      nowval =         ((TMR0H)<< 8 )         + (TMR0L);
          sprintf(buff,"nowval %ld (should be over 15536)\r\n",nowval);
    prints number LOWER than 15536.
    I have tested it with Proteus, in most cases it works, but sometimes it prints things like:

    "nowval 15377 (should be over 15536)".

    My question is HOW - how could value of TMR0H/TMR0L go lower than 15536?
    Where is the mistake in my understading?



    PS: My whole code in case that's needed:
    Code:
      void init_timers();
    
    // dec 10 000 is hex 0x2710
    // dec 1 000 is hex 0x03E8
    // 65536-10000=55536
    // dec 55 536 is hex D8F0
    // 65536-1000=64536
    // dec 64536 is hex  FC18
    // 65536-100=65436
    // dec 65436 is hex  FF9C
    // 65536-50000=15536
    // dec 15536 is hex  3CB0
    #define START_VAL_TMR0H 0x3C
    #define START_VAL_TMR0L 0xB0
     int myCounter;
     
    void main() {
    char res[64];
    int p10000, p1000, p100, p10, p1;
    int k, m;
    long nowval;
    int ph, pl
    ;
    long b, before;
     long a,  c, d;
        union {
              struct {
                  unsigned char l ;
                  unsigned char h;
              };
              unsigned int i;
        };
        char buff[64];
      UART1_Init(9600);
      init_timers();
    
      for (;;) {
            TMR0H = START_VAL_TMR0H;         // Initial count of 0xF830
            TMR0L = START_VAL_TMR0L;
            myCounter = 0;
            T0CON.TMR0ON = 1;     // Turn Timer0 on.
            Delay_ms(1000);
            T0CON.TMR0ON = 0;     // Turn Timer0 off.
            h = TMR0H;
            l = TMR0L;
            // myCounter is increased once per 50k changes
            k = myCounter * 50;
             sprintf(buff,"Freq (integer): %ikHz\r\n",k);
          UART1_Write_Text(buff);      // Interrupt rutine
            // eh. 3500
            p1000 = (k - (k % 1000)) / 1000;
            k -= p1000 * 1000;
            p100 = (k - (k % 100)) / 100;
            k -= p100 * 100;
            p10 = (k - (k % 10)) / 10;
            k -= p10* 10;
            p1 = k;
          //  p1000 = 1;
         //   p100 = 2;
         //   p10 = 3;
        //    p1 = 4;
            res[0] = '0'+p1000;
            res[1] = '0'+p100;
            res[2] = '0'+p10;
            res[3] = '0'+p1;
            res[4] = 0;
             sprintf(buff,"Freq (string): %s kHz\r\n",res);
            // 15
          //   sprintf(buff,"Freq: %ikHz\r\n",k);
          UART1_Write_Text(buff);      // Interrupt rutine
      //  a = i;
      nowval =         ((TMR0H)<< 8 )         + (TMR0L);
          sprintf(buff,"nowval %ld (should be over 15536)\r\n",nowval);
        d = c - 15536;
        b = d;
          UART1_Write_Text(buff);      // Interrupt rutine
      a = ((TMR0H)<< 8 );
     d = (TMR0L);
      c = d + a;
      before = c;
          sprintf(buff,"before %ld (we sub 15536)\r\n",before);
        d = c - 15536;
        b = d;
          UART1_Write_Text(buff);      // Interrupt rutine
             sprintf(buff,"b %ld\r\n",d);
          UART1_Write_Text(buff);      // Interrupt rutine
            p10000 = (b - (b % 10000)) / 10000;
            b -= p10000 * 10000;
            p1000 = (b - (b % 1000)) / 1000;
            b -= p1000 * 1000;
            p100 = (b - (b % 100)) / 100;
            b -= p100 * 100;
            p10 = (b - (b % 10)) / 10;
            b -= p10* 10;
            p1 = b;
            res[0] = '0'+p10000;
            res[1] = '0'+p1000;
            res[2] = '0'+p100;
            res[3] = '0'+p10;
            res[4] = '0'+p1;
            res[5] = 0;
             sprintf(buff,"Freq (string): %s Hz\r\n",res);
          UART1_Write_Text(buff);      // Interrupt rutine
       // i = i - 55536;
     //   ph =   TMR0H -         START_VAL_TMR0H;
    //    pl =   TMR0L -         START_VAL_TMR0L;
      ///   i = b;
      ///       sprintf(buff,"ph: %i\r\n",ph);
      ///    UART1_Write_Text(buff);      // Interrupt rutine
       //      sprintf(buff,"pl: %i\r\n",pl);
        ///  UART1_Write_Text(buff);      // Interrupt rutine
           sprintf(buff,"Rem: %ld\r\n",b);
          UART1_Write_Text(buff);      // Interrupt rutine
            Delay_ms(100);
      }
    }
    void init_timers() {
      // Initialise Timer0 for a 1Khz interrupt
      // 8Mhz clock & want 1Khz interrupt
      // See http://www.enmcu.com/software/timer0calculatorandcodegeneration
      // for values.
      //
      INTCON.GIE=1;         //globle interrupt enable
      INTCON.PEIE=1;        //peripharel interrupt enable
      INTCON.TMR0IF = 0x0;  //Clear timer0 overflow interrupt flag
      INTCON.TMR0IE = 1;    //enable the timer0 by setting TRM0IE flag
    
      T0CON.T08BIT = 0;     // 16 Bit timer
      T0CON.T0CS = 1;     //  1 = Transition on T0CKI pin
      T0CON.PSA = 0b1111;        // Set scaler
    }
    // interrupt handler for the timer0 overflow
    void interrupt(void) {
     // UART1_Write_Text("int\n");
      // http://www.enmcu.com/software/timer0calculatorandcodegeneration
    
      // Reset the timer count
      TMR0H=START_VAL_TMR0H;
      TMR0L=START_VAL_TMR0L;
        myCounter++;
      // Reset interrupt flag
      INTCON.TMR0IF = 0;
    
      // Do some work here
    }

  2. #2
    Super Moderator
    Points: 260,279, Level: 100
    Awards:
    1st Helpful Member

    Join Date
    Jan 2008
    Location
    Bochum, Germany
    Posts
    45,450
    Helped
    13829 / 13829
    Points
    260,279
    Level
    100

    Re: Please explain my mistake related to 16 bit timer...

    Wrong sequence in reading TMR0L/TMR0H, respectively you get inconsistent values. Review the datasheet.


    1 members found this post helpful.

    •   AltAdvertisement

        
       

  3. #3
    Full Member level 5
    Points: 1,316, Level: 8

    Join Date
    Mar 2017
    Posts
    253
    Helped
    5 / 5
    Points
    1,316
    Level
    8

    Re: Please explain my mistake related to 16 bit timer...

    Quote Originally Posted by FvM View Post
    Wrong sequence in reading TMR0L/TMR0H, respectively you get inconsistent values. Review the datasheet.
    TMR0H is not the actual high byte of Timer0 in 16-bit
    mode. It is actually a buffered version of the real high
    byte of Timer0 which is not directly readable nor
    writable (refer to Figure 11-2). TMR0H is updated with
    the contents of the high byte of Timer0 during a read of
    TMR0L. This provides the ability to read all 16 bits of
    Timer0 without having to verify that the read of the high
    and low byte were valid, due to a rollover between
    successive reads of the high and low byte.
    Ah, you are right... how lucky I am - I had 50% chance to get it working without knowing why.

    Anyway...
    it seems that there is something more wrong.

    Code:
            T0CON.TMR0ON = 1;     // Turn Timer0 on.
            Delay_ms(1000);
            T0CON.TMR0ON = 0;     // Turn Timer0 off.
    the T0CON.TMR0ON = 0 does not stop the TMR0H/TMR0L.

    I've added this code snippet several times:
    Code:
               {
            l = TMR0L;
            h = TMR0H;
             sprintf(buff,"TMR0L %i TMR0H %i\r\n",l,h);
             UART1_Write_Text(buff);      // Interrupt rutine
          }
    And it prints differenet values, even after:
    Code:
        T0CON.TMR0ON = 0;     // Turn Timer0 off.
      INTCON.PEIE=0;        //peripharel interrupt enable
      INTCON.TMR0IE = 0;    //disable the timer0 by setting TRM0IE flag
    Why? The datasheet reads that "TMR0ON" enables/disables timer0.... how can TMR0L /TMR0H change after that?

    - - - Updated - - -

    Update: only setting " T0CON.T0CS = 0" to stop it worked so far... strange, I expected TMR0ON to stop counting as well



  4. #4
    Full Member level 5
    Points: 1,316, Level: 8

    Join Date
    Mar 2017
    Posts
    253
    Helped
    5 / 5
    Points
    1,316
    Level
    8

    Re: Please explain my mistake related to 16 bit timer...

    There is one more thing that I'm unsure about now.

    Can I write to INTCON or T1CON inside the interrupt handling routine?

    I wanted to stop TIMER0 when TIMER1 overflows, but it does not work - timer0 is not stopping.
    The same code snippet is stopping timer0 when used inside main loop.
    And I haven't found anything about that in the datasheet....



  5. #5
    Super Moderator
    Points: 260,279, Level: 100
    Awards:
    1st Helpful Member

    Join Date
    Jan 2008
    Location
    Bochum, Germany
    Posts
    45,450
    Helped
    13829 / 13829
    Points
    260,279
    Level
    100

    Re: Please explain my mistake related to 16 bit timer...

    Can I write to INTCON or T1CON inside the interrupt handling routine?
    Yes, there's no restriction in this regard.

    I don't know what's happening in your code. The most likely explanation is that the respective interrupt function isn't executed when you think it should, or that the timer is reenabled at a different place.


    1 members found this post helpful.

  6. #6
    Full Member level 5
    Points: 1,316, Level: 8

    Join Date
    Mar 2017
    Posts
    253
    Helped
    5 / 5
    Points
    1,316
    Level
    8

    Re: Please explain my mistake related to 16 bit timer...

    Ok... I'm on it.

    One more question, cause I can't seem to find it in the datasheet....

    TMR0ON: Timer0 On/Off Control bit
    1 = Enables Timer0
    0 = Stops Timer0

    does it refer to the Timer0 as the "TIMER", or to the Timer0 as timer module?

    I mean, I am using Timer0 as rising edge counter (I set T0CS mode) - do I need to set this TMR0ON bit as well?



    •   AltAdvertisement

        
       

  7. #7
    Advanced Member level 2
    Points: 3,130, Level: 13
    baileychic's Avatar
    Join Date
    Aug 2017
    Posts
    622
    Helped
    49 / 49
    Points
    3,130
    Level
    13

    Re: Please explain my mistake related to 16 bit timer...

    Whether you are using TimerX as Timer or Counter you have to enable the TMRxON_bit otherwise timer will not run. The TMRxIE_bit and TMRxIF_bit are to enable the timer interrupt so that you can use interrupts method for timers.



    •   AltAdvertisement

        
       

  8. #8
    Full Member level 5
    Points: 1,316, Level: 8

    Join Date
    Mar 2017
    Posts
    253
    Helped
    5 / 5
    Points
    1,316
    Level
    8

    Re: Please explain my mistake related to 16 bit timer...

    Quote Originally Posted by baileychic View Post
    Whether you are using TimerX as Timer or Counter you have to enable the TMRxON_bit otherwise timer will not run.
    So please explain that:
    Code:
    void main() {
    
      UART1_Init(9600);
    
    
      INTCON.GIE=1;         //globle interrupt enable
      INTCON.PEIE=1;        //peripharel interrupt enable
      INTCON.TMR0IF = 0x0;  //Clear timer0 overflow interrupt flag
      INTCON.TMR0IE = 1;    //enable the timer0 by setting TRM0IE fla
    
      T0CON.T08BIT = 0;     // 16 Bit timer
      T0CON.T0CS = 1;     //  1 = Transition on T0CKI pin
      T0CON.PSA = 0b111;        // Set scaler
      T0CON.TMR0ON = 0;     // ????
    
      for (;;) {
    
      }
    }
    void interrupt(void) {
      if(INTCON.TMR0IF==1)
      {
          TMR0H=0;
          TMR0L=0;
           UART1_Write_Text("Timer0!\r\n");
          INTCON.TMR0IF = 0;
      }
    }
    I do T0CON.TMR0ON = 0; before infinite for loop, and according to the datasheet its:

    TMR0ON: Timer0 On/Off Control bit
    1 = Enables Timer0
    0 = Stops Timer0


    but I still get the Timer0 spam in the uart.

    NOTE: I am feeding a 4100kHz signal to the T0CKI.
    NOTE: I am testing everything in Proteus on virtual PIC18F4550, not the real life micro.



  9. #9
    Advanced Member level 2
    Points: 3,130, Level: 13
    baileychic's Avatar
    Join Date
    Aug 2017
    Posts
    622
    Helped
    49 / 49
    Points
    3,130
    Level
    13

    Re: Please explain my mistake related to 16 bit timer...

    Timer has nothing to do with spam or garbage characters received on UART in your case as you are using hardware UART and not software UART which uses timers. Maybe you have not configured the Oscillator correctly or maybe you have not set the Oscillator frequency correctly in Proteus. If Oscillator frequency is not correctly set then you get wrong baudrate and hence garbage characters.

    In your case Timer is used to send data to UART on interrupt but Timer IF flag is cleared after UART data is sent. So, Timer is not causing the problem. If you were clearing TMR0IF_bit before sending UART data in ISR then there would be a chance of another timer interrupt occuring and affecting the UART transmission but that is not the case in your code.

    Zip and post your mikroC PRO PIC project.



  10. #10
    Full Member level 5
    Points: 1,316, Level: 8

    Join Date
    Mar 2017
    Posts
    253
    Helped
    5 / 5
    Points
    1,316
    Level
    8

    Re: Please explain my mistake related to 16 bit timer...

    but the TIMER0 interrupt handling is the only place where I do UART1_WRite_Text and it executes it:

    Code:
    void interrupt(void) {
      if(INTCON.TMR0IF==1)
      {
          TMR0H=0;
          TMR0L=0;
           UART1_Write_Text("Timer0!\r\n");
          INTCON.TMR0IF = 0;
      }
    }
    just take a look:
    Click image for larger version. 

Name:	timer0messages.png 
Views:	3 
Size:	116.8 KB 
ID:	142636
    how is this possible that UART1_Write_Text is executed...?



  11. #11
    Advanced Member level 2
    Points: 3,130, Level: 13
    baileychic's Avatar
    Join Date
    Aug 2017
    Posts
    622
    Helped
    49 / 49
    Points
    3,130
    Level
    13

    Re: Please explain my mistake related to 16 bit timer...

    You should not call UART or other functions from inside timer interrupt. It is very bad. You just have to set a flag in ISR when interrupt occurs and test if this flag is set in the while(1) loop using an if() condition and then if flag set then you have to write data to UART and clear the flag. All processing should be done in the main loop. Try it and see if UART prints correct timer value.

    If you try to send UART data in the main loop when timer is running then when timer interrupt occurs UART data sending stops and timer ISR executes and this will cause UART bot to function properly and so you will get garbage on UART.



  12. #12
    Full Member level 5
    Points: 1,316, Level: 8

    Join Date
    Mar 2017
    Posts
    253
    Helped
    5 / 5
    Points
    1,316
    Level
    8

    Re: Please explain my mistake related to 16 bit timer...

    I moved the print to main loop as you said, but it still works.

    It still seems lik the TIMER 0 is running, despite having T0CON.TMR0ON = 0

    Code:
    int timer0overflow = 0;
    
    void main() {
    
      UART1_Init(9600);
    
    
      INTCON.GIE=1;         //globle interrupt enable
      INTCON.PEIE=1;        //peripharel interrupt enable
      INTCON.TMR0IF = 0x0;  //Clear timer0 overflow interrupt flag
      INTCON.TMR0IE = 1;    //enable the timer0 by setting TRM0IE fla
    
      T0CON.T08BIT = 0;     // 16 Bit timer
      T0CON.T0CS = 1;     //  1 = Transition on T0CKI pin
      T0CON.PSA = 0b111;        // Set scaler
      T0CON.TMR0ON = 0;     // ????
    
      for (;;) {
              if(timer0overflow==1)
              {
               timer0overflow = 0;
           UART1_Write_Text("Timer0 overflow happened!\r\n");
              }
      }
    }
    void interrupt(void) {
      if(INTCON.TMR0IF==1)
      {
         timer0overflow = 1;
          TMR0H=0;
          TMR0L=0;
          INTCON.TMR0IF = 0;
      }
    }



  13. #13
    Advanced Member level 2
    Points: 3,130, Level: 13
    baileychic's Avatar
    Join Date
    Aug 2017
    Posts
    622
    Helped
    49 / 49
    Points
    3,130
    Level
    13

    Re: Please explain my mistake related to 16 bit timer...

    Zip and post your complete project. I will test it.


    1 members found this post helpful.

  14. #14
    Full Member level 5
    Points: 1,316, Level: 8

    Join Date
    Mar 2017
    Posts
    253
    Helped
    5 / 5
    Points
    1,316
    Level
    8

    Re: Please explain my mistake related to 16 bit timer...

    Okay, here it is:

    proteus isis sim:
    Proteus_18F_Freq3.zip

    mikroC PRO for PIC project, code, hex:
    TIMER0TEST_4550_Blink_12MHz.zip

    Click image for larger version. 

Name:	20171119bits.png 
Views:	2 
Size:	34.6 KB 
ID:	142639

    NOTE: this is PIC18F4550 with 12MHz crystal. Do NOT confuse it with 2550, because the project name is not updated (project is called 2550).


    the hex is also included.



    •   AltAdvertisement

        
       

  15. #15
    Advanced Member level 1
    Points: 5,352, Level: 17

    Join Date
    Jul 2004
    Location
    Hungary
    Posts
    480
    Helped
    189 / 189
    Points
    5,352
    Level
    17

    Re: Please explain my mistake related to 16 bit timer...

    Hi Garyl;

    I advise you to try it in reality. I've readed this somewhere:
    In Proteus the T0CON.TMR0ON bit works incorrectly in counter mode (ie if T0CON.T0CS = 1),
    but it's working in timer mode (T0CON.T0CS = 0).
    Tried also by me, it's true.

    My rearranged (and repaired) initialization:
    Code C - [expand]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    // first set TMR0 prescaler to 1:256
      T0CON = 0b111; // this clears the other T0CON bits !!
      // so the following are unnecessary:
         // T0CON.T08BIT = 0; // 16 Bit timer
         // T0CON.TMR0ON = 0; // stop TMR0 (only if T0CON.T0CS = 0 !!!)
         // T0CON.PSA = 0;    // enable prescaler
      // NOTE: the following line was wrong because it sets the PSA bit:
      //   T0CON.PSA = 0b111 is equal to T0CON.PSA = 1 (disable prescaler)
     
      // now set the counter mode (ext. input: T0CKI pin)
      T0CON.T0CS = 1; // 0;
      // ****** NOTE: in this case TMR0ON = 0 DOES NOT WORK IN SIMULATOR !!
      //        but TMR0ON = 0 stop TMR0 if T0CS = 0 (ie in timer mode)
      
      // finally set the interrupt control bits:
      INTCON.TMR0IF = 0;  // Clear timer0 overflow interrupt flag
      INTCON.TMR0IE = 1;  // enable TMR0 interrupt
      INTCON.PEIE = 1;    // enable peripharel interrupts
      INTCON.GIE=1;       // enable global interrupt
     
      // then
      T0CON.TMR0ON = 0; // 0: stop, 1: start TMR0 if T0CON.T0CS = 0

    Hope this helps you.
    zuisti



  16. #16
    Full Member level 5
    Points: 1,316, Level: 8

    Join Date
    Mar 2017
    Posts
    253
    Helped
    5 / 5
    Points
    1,316
    Level
    8

    Re: Please explain my mistake related to 16 bit timer...

    Quote Originally Posted by zuisti View Post
    I advise you to try it in reality. I've readed this somewhere:
    In Proteus the T0CON.TMR0ON bit works incorrectly in counter mode (ie if T0CON.T0CS = 1),
    but it's working in timer mode (T0CON.T0CS = 0).
    Tried also by me, it's true.
    which Proteus version are you using?

    Anyway, that TMR0ON issue explains everything, even mine troubles from post #4 and #3, and maybe even #1.
    FvM was obviouslr right in post #2 but that was not the main issue...

    okay so I will get PIC18F4550 soon, check it myself, and report back here.
    Or maybe anyone has PIC18F4550 at hand can check?



--[[ ]]--