PDA

View Full Version : Timer 0 not accurate



mfsparky
- 13th March 2024, 19:30
Hello, I am new to to this forum and am looking for help regarding timer issue. I am using a 18F4523 with a 4Mhz clock. Timer 0 is configured for 8 bit 1/64 prescaler (16.384ms/tick) which I am displaying hour:min:sec to LCD display. The problem is that the time is 52 seconds faster every minute. I have gone over the code umpteen times and do not no why it is acting as it is. The object of the program is to condition a device at a certain temperature for 3 days then test the device and continue for the next 5 days testing every day. See code for possible error.



'Define LCD registers and bits
DEFINE LCD_DREG PORTD
DEFINE LCD_DBIT 4
DEFINE LCD_RSREG PORTE
DEFINE LCD_RSBIT 0
DEFINE LCD_EREG PORTE
DEFINE LCD_EBIT 1


low PORTE.2
pause 100


TRISB.0 = 1 ' Set PORTB bit 0 as input external pullups
TRISB.1 = 1 ' Set PORTB bit 1 as input external pullups
TRISB.2 = 1 ' Set PORTB bit 2 as input external pullups
TRISB.3 = 0 ' Set PORTB bit 3 as output
TRISB.4 = 0 ' Set PORTB bit 4 as output
TRISB.5 = 0 ' Set PORTB bit 5 as output


' Set TMR0 to interrupt every 16.384 milliseconds
INTCON = $E0 ' Enable TMR0 interrupts
TMR0IF var INTCON.2 ' Timer0 interrupt flag
T0CON = $45 ' Set timer0 register - timer off,
' internal instruction clock, 1:64 prescale
TMR0ON var T0CON.7 ' Timer0 on bit




' Timer 1 used to blink LEDs
' Set TMR1 to interrupt every 500 milliseconds
T1CON = $31 ' Set prescaler to 1:8 and timer1 on
TMR1IF var PIR1.0 ' Timer1 interrupt flag
TMR1ON var T1CON.0 ' Timer1 on bit
PIE1.0 = 1 ' Timer1 enabled
TMR1H = $0B ' preset for timer1 MSB register
TMR1L = $DC ' preset for timer1 LSB register
TMR1IF = 0 ' Clear timer1 interrupt flag
ADCON1 = $0F ' Set channels all digital






'---------------------------Declare Variables-----------------------------------


reset_pb var PORTB.0 ' 4.7K external pullup resistor Reset button
cycle_pb var PORTB.1 ' 4.7K external pullup resistor Inc Cycle button
alarm_in var PORTB.2 ' 4.7K external pullup resistor Alarm indication
done_LED var PORTB.3 ' Done indication LED
cond_LED var PORTB.4 ' Condition LED
alrm_LED var PORTB.5 ' Alarm from temperature controller LED
hour Var byte ' Define hour variable
minute Var byte ' Define minute variable
second Var byte ' Define second variable
ticks Var byte ' Define pieces of seconds variable for timer0
ticks1 var byte ' Define pieces of seconds variable for timer1
update Var bit ' Define variable to indicate update of LCD
i Var byte ' Debounce loop variable
cond_days var byte ' Days of conditioning = 60
cycles var byte ' Test cycles = 10
do_once var bit ' Only do one time flag
do_once1 var bit ' Only do one time flag
ready var bit ' Flag for cycles push button ready
counts var byte ' Counter for alarms
hr var bit ' Flag to indicate 24hrs has passed
timer1 var bit ' Flag to blink LED's




hour = 0 ' Reset hour
minute = 0 ' Reset minute
second = 0 ' Reset second
low done_LED ' Turn off LED
low cond_LED ' Turn off LED
low alrm_LED ' Turn off LED
update = 1 ' Set to update LCD display
do_once = 0 ' Reset flag
do_once1 = 0 ' Reset flag
counts = 0 ' Reset flag


read 100, cond_days ' Load condition days from memory
read 101, cycles ' Load cycles from memory


On Interrupt Goto ISR ' Jump to interrupt service routine


main:
if reset_pb = 0 then ' Reset button pressed
cond_days = 0
cycles = 0
second = 0
minute = 0
hour = 0
low done_LED
low cond_LED
low alrm_LED
do_once = 0 ' Flag for temp in range
do_once1 = 0 ' Flag for temp out of range
update = 1
endif

If update = 1 Then ' Check for time to update screen
INTCON = $00 ' No interrupts during lcdout write
lcdout,$FE,1
lcdout,$FE,$C0," ",dec2 hour, ":",dec2 minute,":",dec2 second
lcdout,$FE,$94," CONDITION DAYS: ",dec cond_days
lcdout,$FE,$D4," TEST CYCLES: ", dec cycles
update = 0 ' Screen updated
INTCON = $E0 ' Enable interrupts
Endif



if cycles = 5 and cond_days = 8 then ' Condition for 3 days and
TMR0ON = 0 ' cycle for 5 = 8 total days
TMR1ON = 0
INTCON = $00
low cond_led
high done_led
goto main
endif

if alarm_in = 0 then
if do_once1 = 0 then ' Temperature out of range
TMR0ON = 0 ' Turn off timer0
TMR1ON = 0 ' Turn off timer1
high alrm_LED
low done_LED
low cond_LED
do_once = 0
do_once1 = 1
endif
endif

if alarm_in = 1 then ' Temperature in range
if do_once = 0 then
T0CON = $C5 ' Turn on timer 0, 8 bit, 1/64 prescaler
TMR1ON = 1 ' Turn on timer 0
TMR1IF = 0 ' Clear timer 0 interrupt flag
low alrm_LED
do_once = 1
do_once1 = 0
endif
endif

if hr = 1 then
if do_once = 1 then
T0CON = $25
cond_days = cond_days + 1
write 100,cond_days
second = 0
minute = 0
hour = 0
do_once = 0
if cond_days > 3 then ready = 1 ' change according to test requirement
endif
hr = 0
endif

if cond_days > 3 and cycles < 6 then ' Change according to test requirements
if ready = 1 then ' 24 hours passed ready for cycle
T0CON = $25 ' Set timer0 register - timer off, internal instruction clock, 1/64 prescale
if cycles < 6 then ' Change according to test requirements
if cycle_pb = 0 then
gosub Debounce
cycles = cycles + 1
write 101, cycles
second = 0
minute = 0
hour = 0
low done_LED
do_once = 0
ready = 0
update = 1
endif
endif
endif
endif


if timer1 = 1 then
if cond_days < 9 then ' Change according to test requirements
if ready = 1 then
low cond_led
else
toggle cond_LED
endif
endif
if cond_days > 3 and cycles < 6 then ' Change according to test requirements
if ready = 1 then
toggle done_LED
endif
endif
timer1 = 0
endif

goto main




' Interrupt routine to handle each timer tick
Disable ' Disable interrupts during interrupt handler
ISR:


if TMR0IF = 1 then
ticks = ticks + 1 ' Count pieces of seconds
If ticks < 61 Then tiexit ' 61 ticks per second (16.384ms per tick)

ticks = 0


' One second elasped - update time
second = second + 1
If second > 59 Then
minute = minute + 1
second = 0
If minute > 59 Then
minute = 0
hour = hour + 1
if hour >= 24 then hr = 1 ' Set flag 24hrs passed
endif
endif
update = 1 ' Set to update LCD
endif






if TMR1IF = 1 then
timer1 = 1
TMR1H = $0B ' preset for timer1 MSB register
TMR1L = $DC ' preset for timer1 LSB register
TMR1IF = 0
endif
resume


tiexit:
TMR0IF = 0 ' Reset timer interrupt flag
TMR0H = $00
TMR0L = $00
resume


debounce:
For i = 1 to 25
Pause 10 ' 10ms at a time so no interrupts are lost
Next i
Resume


end


Thanks mfsparky

richard
- 13th March 2024, 19:53
how have you set FOSC for the pic.
there is no OSCCON REGISTER setting in your code , the CONFIG settings are not shown, unless you have a 4mz Xtal setup
how sure are you that FOSC is the 4mhz you think it is

Ioannis
- 13th March 2024, 20:03
One second in the ISR is counted as 16.384*62=1.015808

Every minute is 60*1.015808=60.94848 so it is indeed about 50-56 seconds faster.

Why not use the TMR0 as 16 bit timer, no prescaler. After 10000 counts you will have 1 second.

And if you need it to be more accurate you can either use a XTAL 4MHz with a trimmer to adjust it on spot or use the Timer 1 with external xtal at 32.768KHz, divide by 32768 and get 1Hz pulse.

Ioannis

mfsparky
- 13th March 2024, 22:26
I'm sorry I meant that it was 52sec faster every hour not minute. @Richard No I have not defined oscillator but thought it defaulted to 4Mhz. Thanks to you both for your speedy reply. I will define osc and give it ago.

richard
- 13th March 2024, 22:37
I will define osc and give it ago.

that won't help at all, you need to ensure that the config is correct and that osccon reg is set appropriately
define OSC just tells compiler what freq you think you are running at, it does nothing to configure the chip in any way.
and note the OSC bit is case sensitive

Acetronics2
- 16th April 2024, 13:57
you also can play with the OSCTUNE register ... ( datasheet DS39631E-page 27 )

most of time it's enough to get usable precision without tuning the OSC Crystal ...

Alain