How to read TMR0L and TMR0H registers together to create a delay?

Thread Starter

z_iron

Joined May 24, 2020
17
Using PIC16F18446 TMR0 module to create a 1 second delay WITHOUT the use of the interrupts. Here is the program so far:

Code:
void main(void) {
    
    // Oscillator Settings:
    // HFINTOSC 8 MHz
    OSCCON1 = 0b01110100;
    OSCFRQ = 0b00000011;
    
    // Data Direction Settings
    TRISA = 0x00;
    
    // Data Port Settings
    
    // TMR0 Settings
    T0CON0 = 0b10010000;  // TMR0 Enabled / 16-bit mode
    T0CON1 = 0b01001000;  // Fosc/4 Source / Synchronized / 1:256 Pre-scalar
    
    while(1){
        
        if (  "TIMER0 < 7182" ){
            //BLINK LED CODE
        }
            
    }
    
    return;
}
I'm new to embedded programming. Essentially I've selected internal 8 MHz oscillator and the TMR0 clock source is Fosc/4 so 2 MHz. I've also enabled 1:256 pre-scalar and my math to create 1 second delay is as follows:

2MHz / 256
= 7182.5
TMR0 has to count to 7182 in order to last 1 second. My problem is how do I read the two registers TMR0H and TMR0L together, in an if statement to check whether it has exceeded this number, as efficiently as I can. There are a lot of peripherals in this PIC chip that utilities two registers to hold one value and the datasheet isn't clear to me on how to read the values in both registers as one total value. So how do I combine the registers in a single read and while I'm asking how can I do the same for writing?
 

jpanhalt

Joined Jan 18, 2008
10,219
Just read/poll the interrupt flag (TMR1IF). No need to enable TMR1 interrupt (TMR1IE).

Edit: Apparently, the registers for TMR0 on that chip mimic TMR1 on earlier chips. Just treat them the same.
 
Last edited:

sagor

Joined Mar 10, 2019
248
PIC timer 0 counts "upwards" to overflow (by default). That overflow sets the TMR0IF flag. You just have to check that flag.
So, to count 7182 counts, you set the TMR0 registers to 65535-7128=58407 . (High register = E4, low register = 27) and start timer. When TMR0IF is set, reset the timer registers to the same start value (high byte first).
No need to read registers if using the TMR0IF. Sometimes, due to code overhead, one has to adjust the register values slightly to get more accurate timing, if necessary.
 

Thread Starter

z_iron

Joined May 24, 2020
17
PIC timer 0 counts "upwards" to overflow (by default). That overflow sets the TMR0IF flag. You just have to check that flag.
So, to count 7182 counts, you set the TMR0 registers to 65535-7128=58407 . (High register = E4, low register = 27) and start timer. When TMR0IF is set, reset the timer registers to the same start value (high byte first).
No need to read registers if using the TMR0IF. Sometimes, due to code overhead, one has to adjust the register values slightly to get more accurate timing, if necessary.
I've changed the code to your suggestion. I used the interrupt flag and it worked and It also works with the TMR0's output bit (the one im using in the code). The issue is for some reason whichever I use doesn't matter because when the LED on RA1 comes on after a second in does not go off again (simple blink program).

Code:
    TMR0H = 0xE4;
    TMR0L = 0x24;
   
    while(1){
       
        if ( T0OUT == 1 ){
            if(PORTAbits.RA1 == 1){
              PORTAbits.RA1 = 0;
            }else if ((PORTAbits.RA1 == 0)){
              PORTAbits.RA1 = 1;
            }
           
            TMR0H = 0xE4;
            TMR0L = 0x24;
            T0CON0bits.T0EN = 1;
            T0OUT = 0;
        }
    }
 
Change the PORTA.... to LATA.
Only use PORTx for reading the pin / port. Use LATx to set the output of the pin / port. Some older 16f, 12f, and 10f parts don't have the LATx registers and only have PORTx and the suggested way is to set up a shadow register to keep track of the current output states. There is a read / modify / write issue to work around.

Also...

Code:
else if ((PORTAbits.RA1 ==)) {
could just be
Code:
else {
since there is only two possible values it will always either go through one or the other.

When you read TMR0L it stores the current TMR0H in TMR0H. The value in TMR0H does not change as the timer counts up... it's kind of done internally and only updates the actual TMR0H register when you read from TMR0L. The opposite is true for writing... write to TMR0H first then TMR0L. (Section 18.1.2 of the datasheet)
 

jpanhalt

Joined Jan 18, 2008
10,219
That chip is different than earlier enhance midrange chips. TMR0IF is now in PIR0. Does that flag need to be cleared in firmware? For the earlier chips (e.g., 16F1xxx), it needs to be cleared in software.
 
Something else I didn't catch...

Code:
T0OUT = 0;
won't do anything. It is specified as a read only bit in the datasheet. After the first second and T0OUT gets set high you end up just resetting the timer over and over since T0OUT can't be changed. Your led may actually be toggling, but you'll never see it.
 
@jpanhalt made a good comment also. You'll have to clear TMR0IF in your program if you make use of the TMR0IF for anything.

Another thought is you can use timer 1, 3, and 5 as the clock source for timers 1, 3, and 5 also to create a 32 bit timer.
 
A little something to get the ball rolling...

Code:
while(1){
        if (PIR0bits.TMR0IF == 1 ) {
            TMR0H = 0xE4;
            TMR0L = 0x24;
            PIR0bits.TMR0IF = 0;

           if ( T0OUT == 1 ) {
              LATAbits.RA1 = 1;
           }
           else {
              LATAbits.RA1 = 0;
         }         
    }
}
Something else to keep in mind... eventually you will get to a point where the time between the timer rolled over and the loop works it's way around to check the status it will start to slow the timer down a tad. If you need precision you will have to set up an interrupt service routine and enable global and peripheral interrupts so that it is dealt with as soon as possible after the rollover happens.

I normally work in assembly so there may be a couple small errors with bit names. I apologize, but the idea should be clear enough.
 
Last edited:
Top