PDA

View Full Version : Count down timer with interrupt/jump ?



AndrewC
- 23rd August 2007, 16:35
Hi,

I've been searching for an answer to this with no success so apologies if I've just been looking in the wrong place or I'm stupid :)

I'm building a control circuit / program for a photo trap which needs to do the following:

1) Count down from 30secs to 0sec while displaying the remaining seconds on a serial LCD. No problems with that bit !
2) If at any time during the 30sec "window" an input changes state, I want to immediately jump to a subroutine with the shortest possible delay.

As I understand it, interrupts only work at the end of a command so if I pause 1sec the interrupt would only execute at the end of the pause ? Same as if I paused 1sec then just read an input pin.

I could go round and around an input read loop with an if...then...goto for enough times to add up to 1sec and then decrease the second counter, refresh the lcd, then round'n'round for another sec. In that case how long do PBP instructions take to execute ? I know it's 4clock cycles per command in assembly language but how many commands per PBP instruction so I can calculate times ?

I can't help but feel that there might be a really simple/elegant way of doing this.

Thanks in advance for any tips,

Andrew

Johan
- 23rd August 2007, 17:56
Andrew,

have you looked Darrel's instant interrupt : elapsed timer ?
I think you can modify the sample code to make it work as you suggest

I have tried this instant interrupt : elapsed timer for similar application and it works fine

Johan

Bruce
- 24th August 2007, 01:15
As I understand it, interrupts only work at the end of a command so if I pause 1sec the interrupt would only execute at the end of the pause ? Same as if I paused 1sec then just read an input pin.
Yes, but only if you're using ON INTERRUPT, which is really not the way to go for something
like this application. Assembler interrupts would work, but I think this can be done with a lot
less hassle.

Let's get creative on this one, and not use interrupts. We'll just monitor the Timer1 overflow
flag. With Timer1 running in the background you have a multi-tasking environment that can
remove a ton of firmware overhead.


DEFINE LOADER_USED 1
DEFINE OSC 4

' Define LAB-X1 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
DEFINE LCD_COMMANDUS 2000
DEFINE LCD_DATAUS 50

SYMBOL PROBE = PORTA.0 ' O-scope probe for timing tests
SEC_COUNT VAR BYTE ' seconds counter
OLD_COUNT VAR BYTE ' copy of count
PORT_VAL VAR BYTE ' holds value read from input port
O_FLOWS VAR BYTE ' holds the number of timer1 overflows

T1LO CON $B0 ' timer1 reload values
T1HI CON $3C

PORTB = 0 ' RB3 to RB0 = 0
TRISB = %11110000 ' RB7 to RB4 = inputs, RB3 to RB0 = outputs
INTCON = 0 ' interrupts disabled (we're just watching tmr1 int flag)
OPTION_REG = 0 ' portb pull-ups on for RB7 to RB4
T1CON = %00100000 ' 1:4 prescale (4uS per tick), timer1 off
TMR1H = T1HI ' $3CB0=15,536. 65,536-15,536 = 50,000
TMR1L = T1LO ' 50,000 ticks * 4uS = 0.2 seconds. 1 second / 0.2 = 5
' every 5 timer1 overflows = 1 second
ADCON1 = 7 ' Set PORTA and PORTE to digital
LOW PORTE.2 ' LCD R/W line low (W)
PAUSE 1000 ' Wait for LCD to start up
LCDOUT $FE,1 ' Clear LCD

Main:
SEC_COUNT=30 ' load for 30 to 0 countdown
OLD_COUNT=1 ' mismatch for display (see below)
O_FLOWS=0 ' clear overflow count
PIR1.0=0 ' clear timer1 overflow flag
T1CON.0=1 ' start timer1

Test:
TOGGLE PROBE ' 77uS from start to return

' Note: Timer1 overflows every 0.2 seconds, so we have time to execute
' a whopping 200,000 single-cycle instructions before this section is
' executed
IF PIR1.0 THEN ' if timer1 overflow then
O_FLOWS=O_FLOWS+1 ' inc oveflow counter
IF O_FLOWS=5 THEN ' if 1 second elapsed then
SEC_COUNT=SEC_COUNT-1 ' decrement seconds count
O_FLOWS=0 ' reset overflow counter
ENDIF
PIR1.0=0 ' clear timer1 overflow flag
T1CON.0 = 0 ' stop timer1
TMR1L = TMR1L + T1LO ' add in low byte
IF TMR1L < T1LO THEN TMR1H = TMR1H + 1 ' if low byte overflow, increment high byte
TMR1H = TMR1H + T1HI ' add in high byte
T1CON.0=1 ' re-start timer1
IF SEC_COUNT=255 THEN
SEC_COUNT=30 ' reset to 30 after 0-1 underflow
OLD_COUNT=1 ' make sure new value is displayed
ENDIF
ENDIF

' display is updated only when count has changed, so LCDOUT gets executed
' only once each second
IF SEC_COUNT != OLD_COUNT THEN
LCDOUT $FE,1,"Count = ",DEC SEC_COUNT
OLD_COUNT=SEC_COUNT ' match last displayed count for next pass
ENDIF

' portb gets read ~every 77uS
PORT_VAL = (PORTB >> 4) ' read portb
IF PORT_VAL !=15 THEN ProcessInput ' do something if switch pressed
GOTO Test

ProcessInput:
' print switch inputs on 2nd line of LCD when RB7, 6, 5 or 4 are at ground
LCDOUT $FE,$C0,IBIN4 PORT_VAL ' display input values
GOTO Test

END
This is pretty heavily commented, but if you have questions, let me know. I didn't have a
serial LCD handy, but this should be pretty easy to modify for your own hardware.

Making entry into the time-consuming routines conditional, leaves a ton of time to monitor
your switch inputs, and it's easy to keep everything up to date without using interrupts.

So you have a boat-load of time to goof-off doing other stuff between the count,
display updates, and timer reloads.

Instead of wasting valuable time with a PAUSE 1000, you're running around the entire loop
from start to return in around 77uS, and have plenty of time to burn for other stuff.

If you have an O-scope, try placing the TOGGLE PROBE at different locations to see how fast
everything happens.

AndrewC
- 24th August 2007, 06:47
Thanks for the answers, much appreciated. Now, if I can just get my company to stop sending me on ridiculous business trips I'll make the leap from virtual programming in Seat 21A of flight xyz and get home and test them !!

Andrew