Interrupts on the
68HC12
I have recently built a sensor/datalogger system that has
to run on a single battery that originally only powered the
sensor. In order to reduce the power consumption somewhat
-- to get at least one week of power from the combined
systems -- I decided to use the WAIT mode on each
processor. In both cases, WAIT mode roughly halved the
power requirement (to ca. 20 mA for each device). This
looks to be adequate for one week's operation on a battery.
(LATER: I had hoped that proper attention to port setups
and such would improve this value but, alas, nothing seems
to help. This is as good as it gets.)
For the datalogger, I decided to use the serial input
interrupt. The CPU idles in WAIT mode until a character
appears on the serial port. At this point, the character is
read and appropriate action taken. In my system, there are
commands for Start Of Data, End Of Data, and some
housekeeping functions (like dumping and erasing data). The
sensor sends data (bracketed by SOD and EOD) as ASCII-HEX
on the serial input.
For the sensor, I decided to use the Real Time Interrupt
because the sensor takes data at timed intervals of 10-20
minutes. I set up this internal clock to interrupt the CPU
at 65.536 mSec intervals (the maximum available) and wrote
a routine to stay in WAIT mode for 48 cycles or about 3
seconds. At this point, the CPU can read the clock and see
if it is time to take data. If not, back into the WAIT
routine for another 3 seconds.
In either case, the key element to handling interrupts is
to SERVICE THE SOURCE OF THE INTERRUPT. In most cases, a
bit is set by the interrupting device. This bit must be
cleared before you exit the interrupt handler or ... guess
what? Yep, the interrupt is serviced again. And again, and
again and ... Sometimes the bit is cleared by reading it
(see the Datalogger example) and sometimes by writing it
(see the Sensor example).
DATALOGGER:
This code puts the CPU into WAIT mode until a character is
received on the serial port. When the character is
received, the calling program must then decide what to do
with it. When processing is complete, the code can re-enter
WAIT mode.
The SIO interrupt vector at $F7D6 was changed to $0F00, an
address in EEPROM. At this address, the following interrupt
handler was installed
4D C3 20 ( bclr siocr2, #$20 ; clear the interrupt
enable bit
96 C4 ( ldaa siostr ; clear the interrupt bit
14 10 ( sei ; disable interrupts
0B ( rti
Remember, if using MaxForth to install this code, use EE!
for the interrupt handler and FL! to install the vector to
the handler routine.
This is the code fragment that calls WAIT mode:
:
dokey ( - )
key dup sod = if datalog save-data then
dup D = if dump-data then
dup E = if erase-ram then
dup S = if disp-status then
KM = if true killme ! then
;
hex
code-sub wait
4cc3 , 20 c, ( bset siocr2,$20 ; set RCV INT bit
10ef , ( cli ; enable interrupts
3e c, ( wai ; WAIT
3d c, ( rts ; return to FORTH
end-code
decimal
( Initialize the datalogger, then wait for an input
character
( Expect a command first: SOD to start datalogging or
( DUMP to dump data or STATUS to show # of data sectors or
( ERASE to erase CF-RAM by resetting # sectors to zero or
( KILL to exit from program.
: main ( - )
init ( initialize the system
begin
wait ( low-power mode until input
dokey ( process input
killme @
until
." Overwrite $D00 with $FF to stop autorun" cr
;
The routine WAIT enables received data interrupts on the
serial I/O port. MAIN is the routine that calls WAIT and
DOKEY processes the character. If it is a valid command,
then the appropriate routine is called to execute it. SOD
and EOD are the commands that bracket data to be stored.
The routine that saves this data to RAM is called DATALOG:
(
---------------------------------------------------------
( Program datalog.asm
( 18 June 2009: Datalogger routine for SandScan unit
( 68HC12 subroutine:
( Called after receiving a Start Of Data word
( Reads data from SIO, saves to sequential loc'ns
( in array DATA.
( Ends and returns when receives End of Data word
(
( NOTE: all data must be ASCII-HEX or TEXT, no CTRL chars
(
( ---------------------------------------------------------
code-sub datalog
3b c, ( pshd ; save D accumulator
34 c, ( pshx ; save X register
35 c, ( pshy ; save Y register
ce c, data , ( ldx #data ; point to start of DATA array
08 c, ( inx ; save two bytes at start
08 c, ( inx ; for # bytes
cd c, 0000 , ( ldy #00 ; Y = byte counter
( inchr:
96c4 , ( ldaa scxsr1 ; check status
8420 , ( anda #$20 ; data ready?
27fa , ( beq inchr
96c7 , ( ldaa scxdrl ; get character
8105 , ( cmpa #eod ; is it EOD?
2706 , ( beq done ; if yes, exit
6a00 , ( staa 0,x ; save to data array
08 c, ( inx ; increment address
02 c, ( iny ; increment byte counter
20ee , ( bra inchr ; do again
( done:
7d c, data , ( sty data ; put byte count at DATA
31 c, ( puly
30 c, ( pulx ; recover registers
3a c, ( puld ; and accumulator
3d c, ( rts ; return to FORTH
end-code
This routine saves all the characters after the SOD to RAM
(at the array I call DATA), keeping track of the number of
bytes in a counter. When EOD is received, the byte count is
saved at the beginning of the DATA array. In my system,
this RAM buffer is then saved to a CF-RAM card for
semi-permanent storage.
SENSOR:
This code puts the CPU into WAIT mode for approximately 3
seconds. Each time the RTI interrupt fires, the counter
(held in the Y register) is decremented; when it reaches
zero, the subroutine returns to the calling program. Note
that you need to install an interrupt handler somewhere (I
chose $0F00 in EEPROM) and point the RTI interrupt vector
at $F7F0 to this handler. If you are using MaxForth to
install these routines, remember to use EE! to put code in
EEPROM and FL! to put the handler vector in Flash.
(
Forth/asm fragment to test RTI interrupts on 68HC12
( This routine enters WAIT for approximately 3 seconds
( and exits with RTI interrupts disabled
(
( 2 July 2009: cfg
hex
code-sub wait3seconds
3b c, ( pshd
35 c, ( pshy
cd00 , 30 c, ( ldy #48 ; pre-load Y with count
( Enable RTI interrupt at 65.536 mSec intevals
4c14 , 87 c, ( bset rtictl,#$87 ; enable RTI ints @ 65.536
mSec
4c15 , 80 c, ( bset rtiflg,#$80 ; clear RTI interrupt flag
( sleep:
10ef , ( cli ; enable interrupts
3e c, ( wai ; wait for RTI interrupt
( Interrupt handler clears RTIFLG high bit, disables ints
( and RTI's to here
03 c, ( dey ; decrement tic counter
26fa , ( bne sleep
1410 , ( sei
31 c, ( puly
3a c, ( puld
3d c, ( rts
end-code
( The RTI interrupt vector at $f7f0 has been set to $0f00
( Code at $0f00 looks like this:
(
( 14 10 sei
( 4c 15 80 bset rtiflg,#$80
( 0b rti