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.

PIC16F877A Timer 1 setting initial value

Status
Not open for further replies.

Chris_WMS

Newbie level 4
Newbie level 4
Joined
Oct 2, 2020
Messages
7
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
67
Hi guys, I would like some help.
I am using the Timer 1 to use as a delay using the initial value method for delaying 1 sec.
The FOSC used is 20MHz.
Here are the ways I use to calculate the initial value.
PIC Input frequency = 20MHz/4=5MHz
Prescaler = 1:1
Tick counter frequency = 1/5MHz = 0.2u sec
Full scale time = 0.2u sec * 65536 = 13.11 m sec
10m sec is chosen as a time to count for every cycle and repeated 100 times to obtain 1 sec.
Timer count = 10m sec/0.2 u sec = 50000
Register value = 65536-50000 = 15535 = 3CAF (hex)

Below is the attachment of the code I am using and the I am using MPLAB IDE v8.92 and the figure shows what I obtain from the logic analyzer.
As the Figure shows, the delay time is only 0.5m sec which is not as what I expected to obtain. Therefore, I would like to ask that which is the parts that I done wrong that causes this error?

Code:
#include<htc.h>
#define _XTAL_FREQ 20000000
__CONFIG(FOSC_HS & WDTE_OFF & PWRTE_ON & BOREN_OFF & LVP_OFF);
int Count = 0;
 
void main () 
{     
    TRISD=0x00;                    //configurate PORTD as output
    T1CON=0b00000000;            //disable Timer 1
    TMR1H=0x3C;                    //setting initial value as 15535
    TMR1L=0xAF;
    T1CON=0b00000001;            //starting Timer 1
    while (1)
    {
       while(TMR1IF=1) 
           {    
            TMR1IF = 0;         //clear interrupt flag
               Count ++;             //increase count bit by 1
               if (Count ==100)
            { 
                   Count = 0;        //clear count bit
                PORTD=~PORTD;    //toggle PORT D
            }
            TMR1H=0x3C;
            TMR1L=0xAF;
        }
    }
}
 

Attachments

  • 1.jpg
    1.jpg
    69.7 KB · Views: 180

You do not set the prescaler or postscaler in your code. I have an example for a lower processor, but you should check your OPTION register and the datasheet for PIC16F877A in order to see the necessary bits:

Code:
    OPTION = 0b11010100;            // configure Timer0:
             //--0-----                 timer mode (T0CS = 0)
             //----0---                 prescaler assigned to Timer0 (PSA = 0)
             //-----100                 prescale = 32 (PS = 100)
             //                         -> increment every 32 us

Your calculations seem correct, I am not certain, but I think the watchdog should be disabled, because sometimes it shares its timer with timer0.[/code]

The whole working code:
Code:
*                                                                       *
*   Architecture:  Baseline PIC                                         *
*   Processor:     12F508/509                                           *
*   Compiler:      MPLAB XC8 v1.01 (Free mode)                          *
*                                                                       *
*************************************************************************
*                                                                       *
*   Files required: none                                                *
*                                                                       *
*************************************************************************
*                                                                       *
*   Description:                                   *
*                                                                       *
*   Demonstrates use of Timer0 to maintain timing of background actions *
*   while performing other actions in response to changing inputs       *
*                                                                       *
*   One LED simply flashes at 1 Hz (50% duty cycle).                    *
*   The other LED is only lit when the pushbutton is pressed            *
*                                                                       *
*   Uses symbols to represent port bit positions                        *
*                                                                       *
*************************************************************************
*                                                                       *
*   Pin assignments:                                                    *
*       GP1 = "button pressed" indicator LED                            *
*       GP2 = flashing LED                                              *
*       GP3 = pushbutton switch (active low)                            *
*                                                                       *
************************************************************************/

#include <xc.h>
#include <stdint.h>


/***** CONFIGURATION *****/
// int reset, no code protect, no watchdog, int RC clock 
__CONFIG(MCLRE_OFF & CP_OFF & WDT_OFF & OSC_IntRC);

// Pin assignments
#define nFLASH  2               // flashing LED on GP2
#define nPRESS  1               // "button pressed" indicator LED on GP1

#define BUTTON  GPIObits.GP3    // pushbutton


/***** GLOBAL VARIABLES *****/
uint8_t     sGPIO;                  // shadow copy of GPIO


/***** MAIN PROGRAM *****/
void main()
{
    uint8_t   dc;                   // delay counter

    //*** Initialisation  
    
    // configure port
    GPIO = 0;                       // start with all LEDs off
    sGPIO = 0;                      //   update shadow
    TRIS = ~(1<<nFLASH|1<<nPRESS);  // configure LEDs (only) as outputs
    
    // configure Timer0    
    OPTION = 0b11010100;            // configure Timer0:
             //--0-----                 timer mode (T0CS = 0)
             //----0---                 prescaler assigned to Timer0 (PSA = 0)
             //-----100                 prescale = 32 (PS = 100)
             //                         -> increment every 32 us

    
    //*** Main loop  
    for (;;) 
    {
        // delay 500 ms while responding to button press
        for (dc = 0; dc < 125; dc++)     // repeat 125 times (125 x 4 ms = 500 ms)
        {  
            TMR0 = 0;                    //   clear timer0
            while (TMR0 < 125)           //   repeat for 4 ms (125 x 32 us)
            {                            //     check and respond to button press
                sGPIO &= ~(1<<nPRESS);   //       assume button up -> indicator LED off
                if (BUTTON == 0)         //       if button pressed (low)
                    sGPIO |= 1<<nPRESS;  //         turn on indicator LED
                GPIO = sGPIO;            //     update port (copy shadow to GPIO)
            }
        }
        // toggle flashing LED       
        sGPIO ^= 1<<nFLASH;              // toggle flashing LED, using shadow register
    } 
}
 
Thanks for replying, Carry for cents bazar. I thought I also set for the prescaler in the T1CON. Thanks for the code that you shared 😁
Code:
T1CON=0b00000000;            //disable Timer 1(bit 0),Prescaler (bit4-5)
You do not set the prescaler or postscaler in your code. I have an example for a lower processor, but you should check your OPTION register and the datasheet for PIC16F877A in order to see the necessary bits:

Thanks for replying, Brian. and it solves the problem. 😁
It should be 'while(TMR1IF == 1)'
or better still 'if(TMR1IF)'
--- Updated ---

I would also like add on some question.
First, after declaring the initial value/register value in the main program. Should I reassign the initial value/register value after clearing the overflow flag/interrupt flag? or is it not necessary to reassign the initial value/register value?
e.g.
Code:
#include<htc.h>
#define _XTAL_FREQ 20000000
__CONFIG(FOSC_HS & WDTE_OFF & PWRTE_ON & BOREN_OFF & LVP_OFF);
int Count = 0;
 
void main ()
{     
    TRISD=0x00;                    //configurate PORTD as output
    T1CON=0b00000000;            //disable Timer 1(bit 0),Prescaler (bit4-5)
    TMR1H=0x3C;                    //setting initial value as 15535
    TMR1L=0xAF;
    T1CON=0b00000001;            //starting Timer 1
    while (1)
    {
       while(TMR1IF==1)
           {   
            TMR1IF = 0;         //clear interrupt flag
               Count ++;             //increase count bit by 1
               if (Count ==100)
            {
                   Count = 0;        //clear count bit
                PORTD=~PORTD;    //toggle PORT D
            }
            TMR1H=0x3C;
               TMR1L=0xAF;
        }
    }
}

Second, if I'm using a interrupt sub function. Should I reassign the initial value/register value in the interrupt sub function?
If according to the datasheet of PIC 16F87XA 6.7 which is in the attachment, the statement "TMR1H and TMR1L registers are not reset to 00h on a POR" means the initial value inside the main program or including the interrupt function?
Code:
#include<htc.h>
#define _XTAL_FREQ 20000000
__CONFIG(FOSC_HS&WDTE_OFF&PWRTE_ON&BOREN_OFF&LVP_OFF);
int Count = 0;

void interrupt isr()
{
       if (TMR1IF==1)
       {   
            TMR1IF = 0;         //clear interrupt flag
               Count ++;             //increase count bit by 1

               if (Count == 100)   
            {
                   Count = 0;        //clear count bit
                PORTD=~PORTD;    //toggle PORT D
            }
            TMR1H=0x3C;                    //setting initial value as 15535
            TMR1L=0xAF;                    
        }
}
 
void main ()
{     
    T1CON=0x00;                    //initialise Timer 1, prescare=1:1, unable timer1
    TMR1H=0x3C;                    //setting initial value as 15535
    TMR1L=0xAF;                    
    T1CON = 0b00000001;         //starting TImer 1
    TRISD=0x00;                    //configurate PORTD as output
    INTCONbits.GIE=1;            //enable all global interrupts
    INTCONbits.PEIE=1;            //enable periferal int
    PIE1bits.TMR1IE=1;            //enable timer 1 overflow interrupt
    PIR1bits.TMR1IF=0;            //timer  register 1 is not overflow
    while (1)
    {
    }
}
Third, can I assign the initial value/register value as global value? If can, how should I define ?
--- Updated ---

Once again, thanks for the help 😁
 

Attachments

  • 2.jpg
    2.jpg
    35 KB · Views: 257
Last edited:

For that timer on that chip, yes you do need to reset the TMRx registers. The data sheet explains how the timer works by counting UP until it rolls over from 0xffff to0x0000 which sets the xxxIF bit (which triggers the ISR if it is enabled). Therefore without resetting the TMRx registers it will always count up from 0x0000 each time.
This applies whether you are polling or using an interrupt.
Also, in both cases you need to take into account the latency between the rollover and your code resetting the TMRx registers. An 'easy' way of doing this (assuming that the latency is not so large as opposed to the tick frequency that TMRH is also incremented) is to set TMRH to your calculated value but ADD your calculated value for TMRL to that register. That way you automatically account for the increments that have occurred between the rollover and your resetting the registers.
On that chip, if you are wanting to have the hardware do all of the hard work for accurate timing, then use TIMER2 as it has the PRx registers and the hardware does the comparison and reset. Also the counting if always from 0x0000 t the PRx value so latency of you polling the xxxIF bit is not a problem.
Susan
 
So does it means that the register value (not in the while loop) that I declare in the main program will run only once. After the counting Up to 0xFFFF to 0x0000, it will not return to the initial value that I declare but will only count from 0x0000 to 0xFFFF?
Can latency also be defined as the instruction set time?
Thanks for the opinion.
Chris
 

Yes!
The interrupt flag will be set at the end of the first count above 0xFFFF but that leaves TMR1 with 0x0000 in it. You have to re-load the counter with it's start value again to shorten the remaining period. Obviously, as the timer is still running, it might have gone above 0x0000 by the time you have re-loaded it so you may have to adjust the value you use slightly to compensate. I would advise loading TMR1H before TMR1L to reduce the chance of a second roll-over before you have loaded all the value.

You can stop the timer while you reload it then start it again but there is no advantage to doing that in your application.

Brian.
 

    Chris_WMS

    Points: 2
    Helpful Answer Positive Rating
Oh... I understand now. So if the delay time desired is 1 sec sharp, the initial value that set will not be 0x3CAF but will be larger than 0x3CAF depending the instruction set time multiply with the line of code in the program for the PIC16F877A?

Yes!
The interrupt flag will be set at the end of the first count above 0xFFFF but that leaves TMR1 with 0x0000 in it. You have to re-load the counter with it's start value again to shorten the remaining period. Obviously, as the timer is still running, it might have gone above 0x0000 by the time you have re-loaded it so you may have to adjust the value you use slightly to compensate. I would advise loading TMR1H before TMR1L to reduce the chance of a second roll-over before you have loaded all the value.

You can stop the timer while you reload it then start it again but there is no advantage to doing that in your application.

Brian.
 

Hi,

a workaround is to read the current counter value and add it to the 0x3CAF. This should be done in ISR or at least while the ISRs are disabled temprarily.

so if the counter value is already at (let´s say) 0x11 then you set the new value to 0x3CAF + 0x0011 = 0x3CC0.

I´m not familiar with the PICs, but I expect they have a better (hardware) solution.
* Either by setting a fixed "TOP" or "compare" value and automatically restart the counter with 0 (by hardware) => counting from 0 to TOP value
* or there is an option that the counter "presets" it´s value to 0x3CAF on each overflow.

Klaus
 

    Chris_WMS

    Points: 2
    Helpful Answer Positive Rating
Hi Klaus, Thanks for explaining in detail to me. So the way I'm doing (calculate and adding the counter value to the initial value) is much more complex compare to CCP?
Can I know is the fixed "TOP" or ”compare“ value you mentioned is using the CCP method?

Hi,

a workaround is to read the current counter value and add it to the 0x3CAF. This should be done in ISR or at least while the ISRs are disabled temprarily.

so if the counter value is already at (let´s say) 0x11 then you set the new value to 0x3CAF + 0x0011 = 0x3CC0.

I´m not familiar with the PICs, but I expect they have a better (hardware) solution.
* Either by setting a fixed "TOP" or "compare" value and automatically restart the counter with 0 (by hardware) => counting from 0 to TOP value
* or there is an option that the counter "presets" it´s value to 0x3CAF on each overflow.

Klaus
 

The method Klaus suggests is quite valid but it would be easier to simply use the interrupt method and reload the counters immediately you enter the ISR. As the interrupt will be serviced within a few machine cycles the count will not have time to increment much. You probably only need to add one or two to the timer value when you reload it. Bear in mind that if you calculate the amount to be added rather than just adding a fixed number, the counter will still be running while you do the math. You can reset the interrupt flag after reloading the timer because the global interrupt flag will prevent another interrupt occurring until you have left the ISR.

Brian
 

    Chris_WMS

    Points: 2
    Helpful Answer Positive Rating
Hi,

as already said, I´m not familiar with PIC. There should be experts for PIC.

I´m more familiar with AVRs, they have the ability to automatically preset the counter value without software processing time.
Then you are 100% sure to get precise timing.

You talk about complex:
* reading a coutner value is no complex task
* adding a constant value is no complex task
* writing the counter value is no complex task
... and doing these three simple commands is still no complex task. :)

Klaus
--- Updated ---

I just did a quick view into the datasheet.

There is a timer1 prescaler. Why don´t you use it?
The benefit is:
When it´s set to max value of 8 you have 4 x 8 = 32 Fosc cycles of time to update the counter value
.. since the counter frequency is much smaller.

next benefit is that you now can go to 100ms interrupt timeout.
Thus the update rate is just 1/10 and the error caused by the updates is only 1/10, too.
***
Prescaler combined with the "CCP COMPARE" mode will generate an interrupt (every 100ms) while automaticallly resetting the counter values.
Compare registers need - only once - be loaded with the correct values.

***
Another option is to use a 32768 Hz crystal for the timer1.

***
Another option is to use timer0 with 1/64 prescaler.
5MHz / 64 = 78125Hz
625 x 125 counter ticks = 625 x 125 = 1s

With a 1/64 prescaler there will be no problem in updating the counter wihtin one counting cycle.

***

so there are plenty of options for precise timing. :)

Klaus
 
Last edited:

    Chris_WMS

    Points: 2
    Helpful Answer Positive Rating
Please re-read my previous post where all of the above options are outlined - perhaps not in the level of detail provided since but it is there, especialy for 'precise' timing (for a specific meaning of 'precise')
Susan
 
Thanks Brian, Klaus and Susan. Thank you for your opinion and I will implement it in my program. I also need to study more about the options that are listed by you guys. 😁

Chris
 

May I know how do I search for the 'Instruction Set' time for the PIC, because I would like to try for a shorter time delay but I heard that instruction set time would be critical for a shorter time delay for example 5m sec delay.
 

Nonsense - you can set time delays in that device down to one instruction cycle - at 20MHz clock that is 200nS.

If you look at the data sheet in the 'instruction set' section it will show how many machine cycles each instruction takes. Most are one or two cycles and each cycle is at one quarter of the master clock frequency. For one cycle instructions it can execute 5 million per second!

Note that high level languages add some overhead and may include instructions that are never executed but in general PIC is very language efficient. You can mix assembly language in 'C' code where timing is very critical.

I would strongly advise you to use timer interrupts when delays have to be accurate. The reason is simple - they are hardware triggered. If you look at a software loop in which you poll for the TMRxIF bit being set, depending on where you are in the loop it could take some time before the instruction to read the bit is executed. This is particularly a problem if you use delays inside the loop as they could significantly impact on the exact time you read the interrupt bit. If you use interrupts, they are acted upon within a machine cycle regardless of what the rest of your program is doing so they are very predictable.

I have written video signal generating programs on PICs, ones that need precise timing to keep a picture stable and had no problems.

Brian.
 

Hi,

we gave you several solutions without the "instruction time" problem.
The timing is determined by hardware only, no software = no instruction is invoved in the timing.

BTW:
you already should have the instruction set documentation! .. but in case you don't have.. just do an internet search on "PIC instruction set".
Best is to use the original MICROCHIP one. It is the most reliable.
There you will find the timing information for each instruction.

Doesn´t the datasheet contain the complete instruction set .. with timing information..?

Klaus
 

I am not aware of any Microchip data sheet that does NOT have the instruction set listed along with the timing information. (There again, I've only been using the more recent MCUS of theirs - say within the last 15 years.)
Susan
 

Even the original "General Instruments" PIC1650 has the instruction timing in the data sheet. It is one paragraph saying "all instructions take one machine cycle except branches which take two", not quite as detailed as later sheets but still just as accurate. Interestingly, all the address map is in octal notation - that dates it (and me) as I worked on them when they were first released!

Brian.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top