ECE 2325 UMD Fall 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. seconds 2. S.Norr A Delay Subroutine, DelVar, which is of variable length between 1 and 2 An Interrupt Service routine, IRQSvc, initiated by the IRQ interrupt -1- 10/17/02 ECE 2325 3. 4. UMD Fall 2002 An Interrupt Service Routine, RTISvc, initiated by the RTI interrupt 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: Loop: 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 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: S.Norr -2- 10/17/02 ECE 2325 UMD Fall 2002 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 S.Norr -3- 10/17/02 ECE 2325 UMD Fall 2002 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 C000C003. 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 S.Norr -4- 10/17/02 ECE 2325 UMD Fall 2002 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: A. push 2 bytes of data to stack, lo byte first B. jsr Reslt C. pull 2 bytes of data off stack Do this twice. 5. 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: Address C000 C001 C002 C003 S.Norr Contents Test Result 1 Test Result 2 Test Result 3 Test Result 4 -5- 10/17/02 ECE 2325 UMD C004 C005 C006 C007 Fall 2002 Current Test Hi-byte Current Test Lo-byte INTERRUPT DATA: Jump table data on the EVB after reset: Interrupt Base Address IRQ RTI SWI BFF2,3 BFF0,1 BFF6,7 Jump Table Address 00EE 00EB 00F4 Jump Vector FF FF FF FF FF FF 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 db db db db 0a,0d 72,65,61,64 79,28,79,20 6f,72,20,6e 29,3f,00 ; Message data org c200 Main: lds #dfff ; initialize the stack at bottom of ; memory ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Set jump table for ; Interrupt Svc. Routines ; here ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; clr c000 clr c001 S.Norr ; initialize test data locations -6- 10/17/02 ECE 2325 UMD Fall 2002 clr c002 clr c003 First: ldy #c000 Load: sty c006 cli ldx #c00a Send: ldaa 0,x beq Wait psha inx jsr PutC bra Send Wait: Next: Port: Spc: End: S.Norr ; initialize data pointer ; enable interrupts ; load A with ASCII for Message ; push the contents of A onto the stack ; Send message to screen 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 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 com 1004 ; Shine LEDs 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 ldaa #65 psha ldx #6279 pshx ldx #0a0d pshx jsr PutC jsr PutC jsr PutC -7- 10/17/02 ECE 2325 UMD Fall 2002 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 S.Norr -8- 10/17/02 ECE 2325 UMD Fall 2002 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 -9- 10/17/02