|
|
View previous topic :: View next topic |
Author |
Message |
hemnath
Joined: 03 Oct 2012 Posts: 242 Location: chennai
|
help with timer |
Posted: Wed Feb 10, 2016 11:36 pm |
|
|
hi all,
Code: | #include "18F2520.h"
#include "f2520_regs.h"
#fuses HS
#use delay(clock=12000000)
#use fixed_io(b_outputs= PIN_B4,PIN_B5,PIN_B6,PIN_B7)
#include "defines.h"
#include "lcd.c"
int1 DOWN, ENTER, UP = 0;
unsigned int16 delay_ms_flag = 0;
unsigned int16 milliseconds;
unsigned int SECONDS1, SECONDS2, SECONDS3, SECONDS4 = 0;
void timer0_init();
#INT_EXT
void interrupt()
{
DOWN = 1;
clear_interrupt(INT_EXT); // clear flag
}
#INT_EXT1
void interrupt_1()
{
ENTER = 1;
clear_interrupt(INT_EXT1); // clear flag
}
#INT_EXT2
void interrupt_2()
{
UP = 1;
clear_interrupt(INT_EXT2); // clear flag
}
#INT_TIMER0
void timer0_isr()
{
if(TMR0IF == 1)
{
delay_ms_flag++; // increment variable
milliseconds++;
if(milliseconds >= 1000) //
{
milliseconds = 0;
SECONDS1++;
SECONDS2++;
SECONDS3++;
SECONDS4++;
}
TMR0IF = 0; // clear flag
TMR0IE = 1; // enable interrupt
TMR0H = 0xF4; // 1ms for 12Mhz
TMR0L = 0x47;
}
}
void main()
{
lcd_init(); // LCD initialization
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF|ADC_TAD_MUL_0);
setup_comparator (NC_NC_NC_NC); // Disable comparator
ext_int_edge(H_TO_L); // External Interupt High to LOW transistion
enable_interrupts(INT_EXT); // Enable External Interrupt
ext_int_edge(1, H_TO_L); // External Interupt High to LOW transistion
enable_interrupts(INT_EXT1); // Enable External Interrupt1
ext_int_edge(2, H_TO_L); // External Interupt High to LOW transistion
enable_interrupts(INT_EXT2); // Enable External Interrupt2
timer0_init();
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL); // Enable Global Interrupt
while(1)
{
if(ENTER == 1)
{
disp_cmd(0x80);
printf(disp_data, "ENTER");
ENTER = 0;
delay_ms_flag = 0;
while(delay_ms_flag <= 200);
SECONDS1 = 0;
while(SECONDS1 <= 30)
{
disp_cmd(0x80);
printf(disp_data, "%u", SECONDS1);
ENTER = 0;
}
}
delay_ms_flag = 0;
while(delay_ms_flag <= 1000);
disp_cmd(0x01);
delay_ms_flag = 0;
while(delay_ms_flag <= 1000);
}
}
void timer0_init()
{
T0CON = 0b00001000; // 16 bit, prescaler not assigned
TMR0H = 0xF4; // 1ms for 12Mhz
TMR0L = 0x47;
TMR0ON = 1; // Timer1 ON
TMR0IF = 0; // set Timer1 Interrupt Flag as zero
TMR0IE = 1; // Set Timer1 Interrupt Enable = 1
} |
I have a problem with the above code. displayed and compared the variable SECONDS1 with the actual clock, there is a lag of 8 seconds. Seconds1 reaches 30 secs and in my stopwatch it displays 38 seconds.
Can anyone help me? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19569
|
|
Posted: Thu Feb 11, 2016 2:55 am |
|
|
A lot of comments first:
Use the compiler.....
You are trying to use what is effectively partially ported 'assembler' in the compiler. A lot of wasted instructions. For instance:
Code: |
#INT_EXT
void interrupt()
{
DOWN = 1;
}
|
Is all that is needed for this interrupt handler. If you read the manual for interrupts, there is the line:
"The compiler will generate code to jump to the function when the interrupt is detected. It will generate code to save and restore the machine state, and will clear the interrupt flag".
Note the 'and will clear the interrupt flag'.
Then for the timer setup, use the setup_timer function.
It is the old comment about 'keeping a dog, and barking yourself'. You have the compiler to save you from having to do a lot of basic tasks, yet your code does not reflect this.
Then on the timer interrupt handler, you test for the interrupt flag being set. Pointless again. The interrupt handler will only be called if the flag is set.
There is a generic problem with the code. Setting a timer 'to' a value in it's ISR, does not really work (there are exceptions, for instance you can set bit 15 of a timer, or using a timer with a very large prescaler). The problem is that the timer _will_ have counted quite a few cycles once you get to the ISR (and even worse, where you set it at the end of the ISR...), so when you set it to a value, these counts are lost. Hence the timer will always run slow...
This can be improved by _adding_ the required offset to the timer, but there will still be a small loss.
This will be worse because your count is wrong. The timer interrupts when it wraps from 65535 to 0. So to count to 3000, requires it to be loaded with 65536-3000 = 62536 = 0xF448. Yet you load with 0xF447....
Much better, especially on a timer running as fast as this, to use the timer that can automatically reload itself for you. Timer2.
So your interrupt routine to use addition becomes:
Code: |
#INT_TIMER0
void timer0_isr()
{
set_timer0(get_timer0()+62536);
delay_ms_flag++; // increment variable
milliseconds++;
if(milliseconds >= 1000) //
{
milliseconds = 0;
SECONDS1++;
SECONDS2++;
SECONDS3++;
SECONDS4++;
}
}
|
However Timer2 will be far superior.
setup_timer_2(T2_DIV_BY_4, 249, 3);
This gives 3000 counts. (4 * 250 * 3).
The timer2 interrupt will then occur every mSec, without having to set or change anything. |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Thu Feb 11, 2016 3:28 am |
|
|
Have a look at your interrupt routine.
How long does it take to get into the routine?
How long does it take to perform all actions within the routine?
Why do you need to enable interrupts within the routine?
What is the Timer0 value just before you reset it to -3000?
Mike |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Thu Feb 11, 2016 6:38 am |
|
|
do you NEED actual millisecond resolution?
the way to design complex time keeping in a PIC program
is to determine the required granularity of your clock and
then back into your clock frequency.
for precise binary seconds or binary-divisible fractions
8.388608 and 16.77716 mhz are ideal oscillators /crystals
for whole seconds ( time 0-16bit prescaler div_32 or div64)
and binary fractions 500msec 250msec 125msec etc
if you really need 1 millisec granularity
a good choice of master oscillator is to use
a clock of 8,192mhz , or 16.384mhz prescalar of 8 or 16 and
then be sure to set timer0 to 8 bit mode.
the natural rollover then is 1msec with no jiggering of the
timer registers inn the ISR and perfect time keeping.
i say "do you need" because 1000 ints/sec is gonna suck some life out of any program running in the foreground.
i plan my code to know in advance what my tightest timing requirement is in ANY unit of time and make multiples of it as my master clock.
you are trying to do it in reverse with an arbitrary clock and that will always cause you to waste cycles in your ISR |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|