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.
Code:
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.
Bookmarks