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 Interrupt Not Working

Status
Not open for further replies.

isaac12345

Member level 2
Member level 2
Joined
Mar 24, 2010
Messages
47
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,286
Activity points
1,634
Hi guys,

I'm trying to use the PIC's timer0 to time a certain period of time. I'm using the CCS compiler, below's my code -

#include "16F877A.h"
int x=0;

#int_rtcc // Timer0 overflow interrupt
void isrtcc() // Interrupt service routine
{
++x;
switch(x)
{
case 0:eek:utput_high(PIN_D0);
break;
case 1:eek:utput_high(PIN_D1);
break;
case 2:eek:utput_high(PIN_D2);
break;
case 3:eek:utput_high(PIN_D3);
break;
case 4:eek:utput_high(PIN_D4);
break;
case 5:eek:utput_high(PIN_D5);
break;
case 6:eek:utput_high(PIN_D6);
break;
case 7:eek:utput_high(PIN_D7);
break;
}
}
void main() //********************************************
{

enable_interrupts(int_rtcc); // Enable named interrupt
enable_interrupts(global); // Enable all interrupts
ext_int_edge(H_TO_L); // Interrupt signal polarity
while(x<9) // Foreground loop
{
output_high(PIN_B1);
}
output_low(PIN_B1);
}



Problem is that the PIC never goes into the interrupt. It stays inside the while loop forever. My hunch is that the timer0 has not been enabled even though its interrupt is. If so, what's the command/function call to enable it?
Thanks!
 

which compiler you are using ????

I suggest to use MPLAB it has debugger tool which has ability handle timer interrupts.
 

I'm not familiar with CCS syntax but I would guess you are not calling the interrupt at all. Maybe I'm wrong but I can see a routine that sequences pins over 8 of the 256 counts but nothing that actually calls that routine. Do you need to create an ISR routine then call isrtcc() from within it ?

The other thing that isn't clear is what is TMR0 actually counting. You specify the edge to clock it with as though you are using an external source but you don't make the pin an input.

Brian.
 

I am using an external 4MHz crystal as the clock source as the pic16f877a does not have an internal clock source. Do I have to connect it to the crystal via RA4? Is it also possible to use timer1 as a timer to time 8 hours?
 

To count the internal clock you have to make T0CS = 0. What puzzled me was that you set the clock to trigger on the falling edge of a signal on RA4. When using the internal clock, RA4 is a general purpose I/O pin, it isn't connected to TMR0 so it makes no difference which edge it is to detect. Note that the internal clock is ¼ of the crystal frequency so it counts at either 1MHz or slower if the prescaler is used.

Timer 1 is a better choice for creating very long delays because it is 16 bits wide and can therefore be set to roll over far slower. You can't get 8 hours directly though, even setting it to its slowest values will still cause an interrupt in a little over half a second. You have to count interrupts and return, only calling your function when enough have occurred that 8 hours have elapsed.

I suggest this:
Set TMR1 prescaler to divide by 8,
Load TMR1 with 3035 (decimal) or 0x0bdb (hex) giving exactly 2 interrupts per second,
At each interrupt, reload that value back into TMR1 again,
In the interrupt routine, count up to 57600, this is reached after exactly 8 hours have passed.
Reset the interrupt counter to zero to start the next 8 hour period then call your function.

Brian.
 

    isaac12345

    Points: 2
    Helpful Answer Positive Rating
Thanks Brian. Could you please explain -

1. How loading TMR1 with 3035 will give exactly 2 interrupts?

2. How did you come up with the value of 57600?

Sorry for the newbie questions, I'm quite new to microcontrollers.
 

2^16 = 65536, so 65535 is the maximum value that can be stored in 2 byte register. When loaded with 3035, 65535 - 3035 = 62500 timer ticks are required before overflow, which requires 8 * 62500 = 500000 clock cycles with 1:8 prescaler. It results 0.5 seconds, as clock cycles take 1 us with a 4 MHz XTAL.

(8 hours) * (3600 seconds per hour) * (2 overflows per second) = 57600
 
  • Like
Reactions: iczey

    isaac12345

    Points: 2
    Helpful Answer Positive Rating

    iczey

    Points: 2
    Helpful Answer Positive Rating
I'm glad math in Turkey agrees with math in Wales :D

isaac12345, remember that the interrupt occurs when the timer rolls over from FFFF to 0000 (overflows) so pre-loading a number into the timer gives it a head start. The larger the number you load, the less counts it takes to reach FFFF so the interrupt occurs sooner.

Brian.
 

    isaac12345

    Points: 2
    Helpful Answer Positive Rating
It worked!! Thanks a lot betwixt and Tagli!
 

2^16 = 65536, so 65535 is the maximum value that can be stored in 2 byte register. When loaded with 3035, 65535 - 3035 = 62500 timer ticks are required before overflow, which requires 8 * 62500 = 500000 clock cycles with 1:8 prescaler. It results 0.5 seconds, as clock cycles take 1 us with a 4 MHz XTAL.

(8 hours) * (3600 seconds per hour) * (2 overflows per second) = 57600

i just have a few querries about your computation, i have am making a program that continuously reads an adc level, and then sends the average adc level thru uart every hour. I am having problems using delays, and i'm a newb in timers and interrupts. after reading your comments can i also use the same equation i just change the "8 hours" to just "1 hour". And could you please explain to me what does a prescaler do and how it affects the timers and such.
 

At each interrupt, reload that value back into TMR1 again,

because of the latency as well as overhead surrounding interrupt, when you "reload" the timer offset, you want to "increment" or "decrement" the timer. the wrong way, and also the most widely used way, is to assign the offset to the timer/counter. that will cause timing errors over a (long) period of time.
 

That's easy, the 8 hour delay was made by counting half seconds. The figure 57600 is the number of half seconds in 8 hours. To change it to one hour, use the number of half seconds in that period instead:
1hr = 60 mins = 3600 seconds, so the number of half seconds in 7200. Simply change 57600 to 7200 and the period becomes one hour instead of 8.

A pre-scaler is just another counter placed ahead of the main counter, it's purpose is to slow the count down if a longer period is needed. The counter alone would be 1:1 pre-scale, changing it to 2:1 would make it count at half the speed, 1:4 at one quarter speed and so on. You just use the most appropriate setting for your application, as you needed fairly long delays, a high prescaler value was chosen.

Brian.
 

    V

    Points: 2
    Helpful Answer Positive Rating
That's easy, the 8 hour delay was made by counting half seconds. The figure 57600 is the number of half seconds in 8 hours. To change it to one hour, use the number of half seconds in that period instead:
1hr = 60 mins = 3600 seconds, so the number of half seconds in 7200. Simply change 57600 to 7200 and the period becomes one hour instead of 8.

A pre-scaler is just another counter placed ahead of the main counter, it's purpose is to slow the count down if a longer period is needed. The counter alone would be 1:1 pre-scale, changing it to 2:1 would make it count at half the speed, 1:4 at one quarter speed and so on. You just use the most appropriate setting for your application, as you needed fairly long delays, a high prescaler value was chosen.

Brian.

Hi Brian, thanks for replying.

i'm quite lost as to how will i be initializing the option_reg and the intcon. how will i able to count up to an hour and then execute a certain line of code and this goes on repeatedly every hour? and where should i be using the 7200?

best regards.
 

I don't have the CCS compiler but you should be able to work with this idea. I've written it in 'pseudo code' because I have no idea what the actual CCS commands are.

You need two routines, one does the timing and counting, the other performs the actions you want.

interrupt routine:
{
check it really is a Timer 1 interrupt, if it isn't either exit or use another routine if appropriate.
clear the Timer 1 interrupt bit
load Timer 1 with the starting value again, if you have to load the high and low bytes in separate instructions, load the high byte first.
subtract one from the delay counter
if the delay counter is zero, set a flag (lets call it 'OneHour' for this example) and reload the delay counter with 7200 again
}

Note that 'OneHour' is only set when the interrupt routine has executed 7200 times, in other words, every hour.

main function
{
if OneHour is set, reset it and do the hourly work
The rest of your program goes here
}

So you use the flag 'OneHour' to communicate between the interrupt routine and your program loop. Only the interrupt routine can set it and only the program loop can reset it. The periodic interrupts set it once per hour, when the count of 7200 has reached zero then immediately reloads it ready for the next hours event.

INTCON should have the Timer 1 interrupt enabled and the Global Interrupt enabled. Note that you don't need to touch the Global interrupt enable again, the PIC turns it off by itself during the interrupt routine and turns it back on again afterward. None of the bits in OPTION_REG change the way Timer 1 works. You do need to set T1CON so it counts the internal clock, prescales by 1:8 and enables the timer. You do not need to enable the Timer 1 oscillator, that's a special circuit for using a low frequency crystal between pins 15 and 16.

Brian.
 

    V

    Points: 2
    Helpful Answer Positive Rating
Thanks again Brian.

Here is my code

unsigned int ctr;
void interrupt()
{
// Timer1 Interrupt - Freq = 30Hz - Period = 0.03333333333 seconds
if (PIR1.TMR1IF && PIR1.TMR1IE) // timer 1 interrupt flag
{
PIR1.TMR1IF = 0; // clear timer1 interupt bit TMR1IF
TMR1H = 0x1C; // preset for timer1 MSB register
TMR1L = 0x20; // preset for timer1 LSB register

PORTB.F1 = ~PORTB.F1; // Toggle PORTB bit1 LED
ctr++;

}

}

void main()

{

// setup portb to show the interrupts by blibking LEDs
TRISB = 0x00; // PORT is all output...to show the interrupts
PORTB=0x00; // start with all outputs low
TRISD=0x00;

//Timer1 Registers Prescaler= 1:8 - Freq = 30.52 Hz - Period = 0.032766 seconds
T1CON.T1CKPS1 = 1; // bits 5-4 Prescaler Rate Select bits
T1CON.T1CKPS0 = 1; // bit 4
T1CON.T1OSCEN = 1; // bit 3 Timer1 Oscillator Enable Control bit 1 = on
T1CON.T1SYNC = 1; // bit 2 Timer1 External Clock Input Synchronization Control bit...1 = Do not synchronize external clock input
T1CON.TMR1CS = 0; // bit 1 Timer1 Clock Source Select bit...0 = Internal clock (FOSC/4)
T1CON.TMR1ON = 1; // bit 0 enables timer


// Interrupt Registers
INTCON = 0; // clear the interrpt control register
INTCON.TMR0IE = 0; // bit5 TMR0 Overflow Interrupt Enable bit...0 = Disables the TMR0 interrupt

PIE1.TMR1IE = 1; // enable Timer1 interrupts
INTCON.TMR0IF = 0; // bit2 clear timer 0 interrupt flag
INTCON.GIE = 1; // bit7 global interrupt enable
INTCON.PEIE = 1; // bit6 Peripheral Interrupt Enable bit...1 = Enables all unmasked peripheral interrupts

if (ctr==306)
{
PORTD=0x0F;
}
else
PORTD=0xF0;


while(1) //endless loop
{
}
}

Do I set the preset within the main function or within the interrupt? so the preset is the 7200 and its in decimal so i have to convert it to hex and set it as the preset of timer1 is that right?hehe.. And about the counter i'm quite lost on how often my ctr increments and up to what value should it reach before i reset it.
 
Last edited:

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top