ECE 2325UMDFall 2002

Lab6: Interrupts and Interrupt Service Routines

Reaction Time Calculator

Objective:

This lab demonstrates the initiation and resolution of interrupts on the 68HC11 and explores some possible applications for Interrupt Driven I/O.

Discussion:

Interrupts on the 68HC11 are rather straightforward. They are usually maskable (can be disabled by setting a mask bit). They automatically push all registers to the stack at the initiation of the interrupt and restore them upon return from interrupt. For all interrupts maskable by the I-bit (bit 4 of the CCR), the 68HC11 automatically disables interrupts for the duration of the interrupt service routine.

It is desired to construct a Reaction Timer to test the eye-hand reaction time of a human being. The timer will be constructed using the 68HC11 EVB lab station and a pushbutton switch that shorts the IRQ pin to ground. The EVB will be driven by a program which will do the following:

  1. Prompt the test subject to declare their readiness for the test: (“Ready (y or n)?”
  2. Upon an “n” response to the above query, the program will end via the SWI (Software Interrupt).
  3. Upon a “y” response, the program will enter a pseudo random delay of 1 to 2 seconds.
  4. Upon any other response, the program will re-prompt for readiness.
  5. After a “y” response and the random delay, the 8 digit LED display will start to flash and a counter will be started by setting the RTII bit (bit 6 in register 1024).
  6. The counter will be incremented by the Real Time Interrupt Service Routine.
  7. As soon as the subject sees the flashing LEDs, the subject will press the spacebar.
  8. The press of the spacebar will initiate an SWI interrupt, whose service routine will stop the flashing LEDs and disable RTI interrupts.
  9. The press of the spacebar will also cause the reaction time to be displayed on the monitor in milliseconds.
  10. The program must keep track of the four most recent reaction times.
  11. At any time, if the pushbutton switch is pressed, the ensuing IRQ Interrupt Service Routine will average the four most recent reaction times and display that average on the monitor, in milliseconds.

The skeleton of the program is shown at the end of this document. In order to complete it, 4 routines must be coded:

  1. A Delay Subroutine, DelVar, which is of variable length between 1 and 2 seconds
  2. An Interrupt Service routine, IRQSvc, initiated by the IRQ interrupt
  3. An Interrupt Service Routine, RTISvc, initiated by the RTI interrupt
  4. An Interrupt Service Routine, SWISvc, initiated by the SWI interrupt

NOTE: The three service routines also need to have their jump vectors initialized at the top of the main program.

Each of these routines is described in some detail below:

Delay Subroutine, DelVar:

When called, this subroutine will execute a delay loop of variable length between 1 and 2 seconds. Therefore, the down-counter loop must be initialized by a variable number. The variable number is obtained by performing the 16-bit addition of two values. Value 1 is the number for the fixed delay value, (4000)16. Value 2 is the variable delay value. To obtain a pseudo-random value for the variable delay, use the current value of TCNT. TCNT is a free running 16-bit counter located in registers 100E and 100F. TCNT must be scaled from its current range, 0000 – FFFF, down to 0000 – 3FFF. This can be accomplished by dividing TCNT by 4 (Do not use IDIV for this).

The pseudo-code for this subroutine should look something like this:

DelVar: Save old register values on the stack

Load TCNT in a 16 bit accumulator

Divide TCNT by 4

Add 4000 to result

Make that sum a counter value

Loop:NOP

Dec Counter

Branch not equal zero to Loop

Restore old register values

Return from subroutine

The value 4000 Hex, should, in a one NOP loop, provide about 1 second of delay. TCNT/4 should provide between zero and one second of additional delay.

IRQ Interrupt Service Routine:

As previously stated, the IRQ pin on the 68HC11 causes an interrupt on a transition from one to zero. To make the IRQ pin edge sensitive, set the IRQE flag, which is bit 5 of register 1039. Use the bset instruction to do this during the jump vector initialization portion of the main program.

On the EVB stations in Lab, the IRQ pin is wired out to a 16-pin test socket, as is ground. IRQ will be wired to ground through a spring-contact, push-button switch as shown below:

This interrupt will be used to initiate an IRQ Interrupt Service Routine that calculates the arithmetic average of the four most recent reaction time tests. Each test result will be an 8-bit, unsigned number, located in memory at addresses C000 – C003. The summing of four 8-bit, unsigned numbers can create a 10-bit result. If we divide this result by 4 (back to 8-bits), we would have the average count. However, we want the average time in milliseconds which would be that average count multiplied by 4. Thus, if we just sum the four numbers in a 16 bit register, push it on the stack and call a custom subroutine, Reslt (provided in the code at the end of this document), it will send the information to the monitor for display as milliseconds. End this Routine with the rti (Return from Interrupt) instruction.

Note: The main program will initially clear the 4 memory locations. This subroutine will not accurately compute an average until after the first 4 test results have been performed.

Also Note: Since an IRQ interrupt can happen at any time, it would make the screen look nice if a carriage return and a line feed were sent to screen prior to calling the Reslt subroutine. Use the following code or something similar at the TOP of your IRQ service routine:

ldx #0a0d

pshx

jsr PutC

jsr PutC

Your calculations here

push 2 bytes of data

jsr Reslt

pull off 2 bytes of data

IMPORTANT: Reslt does not corrupt registers, BUT it does leave data on the stack. When called during an interrupt service routine it will alter the stack frame, unless data pushed to Reslt is pulled off afterward.

RTI (Real-Time Interrupt) Service Routine:

It is intended to use the RTI counter to initiate interrupts at regular intervals of 4.1 milliseconds during the test sequence. The monitor program for the EVB initializes RTI to interrupt at that rate already. The main program will enable the RTII flag at the start of the test (After the variable delay and just after the LED’s start to flash). At that point, RTI interrupts will occur every 4.1 ms until the spacebar is pushed. Each time an RTI interrupt occurs, a counter (an 8-bit memory location) must be incremented. Thus, the number of counts multiplied by 4.1 milliseconds will result in the reaction time in milliseconds. In order to accomplish this the RTI Interrupt Service Routine will need to do the following things:

  1. Clear the RTIF flag. RTIF is set every time the RTI counter overflows. In order to clear the flag, a logic One must be written to that bit. This may sound odd, but is standard practice to avoid the accidental clearing of important flags. Do this with the bclr instruction. RTIF is bit 6 of register 1025, so use bclr with a mask byte that is all ones, but has a zero in bit six..
  2. Increment the memory location that currently counts the number of RTI interrupts. Memory addresses C000 – C003 are used for this. The main program will keep track of which memory location is currently being used. It will store this memory address at C006, C007. Use that address as the vector for incrementing memory in indexed mode.
  3. Important: Check if memory is making the transition from FF to 1(00). Since the interrupt interval is 4.1 ms and the number of interrupts counted can only reach 25510, the reaction timer can only measure about 1 Second of reaction time. To prevent the count from “rolling over” to zero and starting over, “freeze” the counter at FF if it occurs.
  4. Return from Interrupt

VALID ASSUMPTION:

You may assume that your service routine won’t need to overwrite the data in C000-C003. In other words, you may write RTISvc in a manner that doesn’t clear the memory address it’s going to use, the second time it uses it. The main program will take care of initializing and maintaining the data locations.

SWI Interrupt Service Routine:

Since swi is a useful instruction for ending the main program, it is desired to use it both as normally intended and also as a means of ending the reaction time measurement. The SWI interrupt will be used normally unless the spacebar is pressed. Therefore, the custom service routine must check to see if the spacebar was pressed. If not, the custom routine will jump to the normal jump vector for swi and execute the normal interrupt sequence. If the spacebar was indeed pressed, the custom routine must do the following:

  1. Disable the RTI interrupt by clearing the RTII bit. RTII is bit 6 of register 1024. Use the bclr instruction with a 1 in bit six, zeros elsewhere.
  2. Load the value of the current count. Remember that the address of the memory location for the current count resides in C006,7. Use an indexed load.
  3. Multiply the value by 4 (4 is close enough to 4.1 ms for this application), realizing once again that the result will be a 10-bit number (2 bytes). This will scale the test result as milliseconds.
  4. Push both bytes on the stack, low byte first, and call the custom subroutine, Reslt. Remember that to use Reslt:
  5. push 2 bytes of data to stack, lo byte first
  6. jsr Reslt
  7. pull 2 bytes of data off stack

Do this twice.

  1. Return from Interrupt.

As mentioned above, the custom service routine must check to see if spacebar was pressed. The main program will use GetC to detect a keypress. If the spacebar is pressed, the main program will push it on the stack and execute the swi instruction, causing an interrupt. At that point, all registers are pushed on the stack ON TOP of the spacebar push. Therefore, upon entering the custom routine, the stack appears as follows:

CCR

AccB
AccA
InX hi
InX lo
InY hi
InY lo
PC hi
PC lo
SpaceBar = $20

Load the value of the stack where the spacebar push should be. Compare it to the hex value 20. If true, your custom routine should do 1 through 4 above. If false, jump to AA73, the normal jump vector for SWI.

Memory Map of Data Locations:

AddressContents

C000 / Test Result 1
C001 / Test Result 2
C002 / Test Result 3
C003 / Test Result 4
C004
C005
C006 / Current Test Hi-byte
C007 / Current Test Lo-byte

INTERRUPT DATA: Jump table data on the EVB after reset:

Interrupt / Base Address / Jump Table Address / Jump Vector
IRQ / BFF2,3 / 00EE /
FF FF FF
RTI / BFF0,1 / 00EB / FF FF FF
SWI / BFF6,7 / 00F4 / 7E AA 73

Example Main Program:

; L6NEW.asm - new Reslt Subr, better

; management of data locations

; Code for Lab 6 on the EVB

; Must be modified to test on THRSim11

; Program prints a message to screen,

; accepts a keypress, and calculates

; reaction time in SWI ISR.

;

PutC = bfd3 ;assign labels to the custom

GetC = bfd0 ; instruction address locations

;;;;;;;;;;;;;;;;;;;;;;;;

IRQ = ef

SWI = f5; You may use these

RTI = ec; or make your own

TCNT = 100e

;;;;;;;;;;;;;;;;;;;;;;;;

org c00a

db 0a,0d

db 72,65,61,64; Message data

db 79,28,79,20

db 6f,72,20,6e

db 29,3f,00

org c200

Main: lds #dfff ; initialize the stack at bottom of

; memory

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Set jump table for

; Interrupt Svc. Routines

; here

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

clr c000; initialize test data locations

clr c001

clr c002

clr c003

First: ldy #c000; initialize data pointer

Load: sty c006

cli ; enable interrupts

ldx #c00a

Send: ldaa 0,x ; load A with ASCII for Message

beq Wait

psha ; push the contents of A onto the stack

inx

jsr PutC ; Send message to screen

bra Send

Wait: jsr GetC ; use GetC to detect a key press

bcc Wait

pula ; when a key is pressed load the ASCII vale into A

psha ; put it back on the stack

jsr PutC ; dump the ASCII character for the key to the Monitor

cmpa #79 ; check to see if the key press was "yes"

beq Next

cmpa #6e

beq End

bra Load

Next: clr 0,y

clr 1004

com 1004; blank the LEDs

jsr DelVar; Wait for a variable time

ldx #1000

bset 24,x,40; Enable RTI interrupt

Port: com 1004; Shine LEDs

Spc: jsr GetC; Wait for spacebar press

bcc Spc

pula

cmpa #20

bne Spc

psha

ldx #0a0d

pshx

jsr PutC

jsr PutC

swi; If Spacebar press, Interrupt

ldy c006

iny

cpy #c004

bne Load

jmp First

End: ldaa #65

psha

ldx #6279

pshx

ldx #0a0d

pshx

jsr PutC

jsr PutC

jsr PutC

jsr PutC

jsr PutC

jsr DelVar

swi

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DelVar:

; make your delay subroutine here

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

IRQSvc:

; make your IRQ ISR here

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

RTISvc:

; make your RTI ISR here

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SWISvc:

;make your SWI ISR here

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

HtoA:

; Convert 8-bit ASCII to 4-bit Hex

; input registers: none - 8-bit

; local variable

; passed to stack

; output registers: none - 8-bit

; local variable

; passed to stack

; 4-bits of leading

; zeros, 4-bits data

;

pshx; store old values of X and A

psha

tsx ; transfer SP contents to X

ldaa 5,x ; load ASCII char from stack

adda #30 ; stubtract 30 from ASCII

cmpa #3a ; check if number was less than A (10)

blt Done ; if so, done, restore registers

adda #27 ; if not, convert to "a" thru "f" value

Done: staa 5,x ; put HEX number on stack

pula ; restore contents of A and X

pulx

rts

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Reslt:

;Input: 16 bit variable passed to stack

;Output: Puts 2 ASCII Char to Montr

;!!Data NOT removed from stack!!

;Does not corrupt registers

;Calls Subroutine Parse

pshy

pshx

psha

tsx

ldaa 7,x

psha

psha

jsr Parse

jsr HtoA

jsr PutC

jsr HtoA

jsr PutC

ldaa 8,x

psha

psha

jsr Parse

jsr HtoA

jsr PutC

jsr HtoA

jsr PutC

ldx #6d53

pshx

jsr PutC

jsr PutC

pula

pulx

puly

rts

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Parse:

;Subr to turn 8-bit Hex into

;2 4-bit Hex

;INPUT: 2 pushes of one 8-bit Hex

;passed to the stack

;OUTPUT: 2 nibbles of 4-bit Hex

;passed to the stack,

;4-bits of leading zeros

pshx

psha

pshb

tsx

ldaa 6,x

tab

anda #0f

lsrb

lsrb

lsrb

lsrb

staa 7,x

stab 6,x

pulb

pula

pulx

rts

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

S.Norr- 1 -10/17/02