Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

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

Status
Not open for further replies.

Garyl

Full Member level 5
Joined
Mar 28, 2017
Messages
253
Helped
6
Reputation
12
Reaction score
7
Trophy points
18
Activity points
2,633
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
}
 

Wrong sequence in reading TMR0L/TMR0H, respectively you get inconsistent values. Review the datasheet.
 
  • Like
Reactions: Garyl

    Garyl

    Points: 2
    Helpful Answer Positive Rating
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
 

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....
 

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.
 
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?
 

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.
 

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.
 

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.
 

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:
timer0messages.png
how is this possible that UART1_Write_Text is executed...?
 

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.
 

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;
  }
}
 

Zip and post your complete project. I will test it.
 
  • Like
Reactions: Garyl

    Garyl

    Points: 2
    Helpful Answer Positive Rating
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
 

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?
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top