AGH University of Science and Technology Cracow Department of Electronics Microprocessors laboratory Tutorial 7 Interrupts Author: Paweł Russek http://www.fpga.agh.edu.pl/upt ver. 01/07/14 1/12
1. Introduction 1.1. Objectives The goal of this tutorial is to present the concept of interrupts and interrupts programming in AVR family of microcontrollers. The major advantages of interrupts will be highlighted at the beginning. Then possible interrupt sources in ATMega32 will be given. Interrupt subsystem and registers will be introduce later. Special focus on timer interrupts and external interrupts will be done. Some hands on exercises will be proposed for students. 1.2. Prerequisites The AVR programming tutorials 1, 2, 3, 4, 5 and 6 should be completed before reading this tutorial. 1.3. Preparing ZL2AVR board 2. Interrupts basics 2.1. Interrupts vs. polling So far we had to test for certain events ourselves. For example to determine wether a button is pressed we tested the appropriate bit in PinX register. This method is called polling. In polling, the microcontroler continuously monitors the status of a given device (e.g. button). The polling is not an efficient way to use a microcontroller because the polling procedure wastes the most of processors time and systems electrical energy. The alternative approach is an interrupt procedure. In interrupt method the devices that requests microcontroller service notifies the microcontroller by driving an interrupt signal. Upon receiving an interrupt signal, the microcontroler stops whatever it is doing and starts to execute a program associated with the interrupt. The program is called interrupt service routine (ISR) or interrupt handler. In oppose to polling, interrupts allow to introduce priorities. So when more the one event occurs at a time, the one with the highest priority is serviced first. 2.2. Steps in executing an interrupt Upon activation of an interrupt, the microcontroller: 1. finishes the instruction it is currently executing and saves the address of the next instruction on the stack, 2. jumps to the interrupt vector table which directs the microcontroller to the address of ISR, 3. executes ISR until it reaches the last instruction of the routine, which is return from interrupt (reti) instruction. 4. Upon executing reti instruction, the microcontroller returns to the place where it was interrupted. It restores the program counter from the stack and executes from that address. 2/12
Note From the critical role of the stack in servicing the interrupts (and other routines), we must be careful in manipulating the stack content in the ISR. The number of push and pop instruction throughout ISR must be equal. Similarly, every register used in ISR should be pushed at the beginning of the ISR and then poped at its end in order that the ISR does not interfere with the standard procedures. 3. AVR interrupts sources There are a number of events which can automaticly alert us when they occur. Every interupt source must have an interrupt service routine. In AVRs for every interrupt there is a fixed location in memory that holds the address of its ISR. The group of memory locations set aside to hold the addresses of ISRs is called the interrupt vector table. Excerpt of the interrupt vector table for ATMega32 is given below. Interrupt Reset $0000 EEPROM location.equ label External Interrupt request 0 $0002 INT0addr External Interrupt request 1 $0004 INT1addr External Interrupt request 2 $0006 INT2addr Timer/Counter2 Compare Match $0008 OC2addr Timer/Counter2 Overflow $000A OVF2addr Timer/Counter1 Capture Event $000C ICP1addr Timer/Counter1 Compare Match A $000E OC1Aaddr Timer/Counter1 Compare Match B $0010 OC1Baddr Timer/Counter1 Overflow $0012 OVF1addr Timer/Counter0 Compare Match $0014 OC0addr Timer/Counter0 Overflow $0016 OVF0addr The most typical and general program setup for the Reset and selected Interrupt Vector Addresses in ATmega32 can look like the code below. It handles External Interrupt request 0 interrupt, Timer/Counter0 Compare Match interrupt, and Timer/Counter0 Overflow interrupt. Other interrupts handlers can be added when required according the above table. Example..include "m32def.inc".org 0 jmp reset.org INT0addr ; External Interrupt Request 0.org OC0addr jmp external_interrupt_0 ; Timer/Counter0 Compare Match 3/12
jmp timer0_compare_match.org OVF0addr jmp timer0_overflow reset: ldi r16,high(ramend) out SPH,r16 ldi r16,low(ramend) out SPL,r16 ;... some code external_interrupt_0: ;... some code reti timer0_compare_match ;... some code reti timer0_overflow: ;... some code reti ; Timer/Counter0 Overflow ; Main program start ; Set Stack Pointer to top of RAM 4. Enabling and disabling interrupts globally Each interrupt can be disabled (masked). That means that it will not be responded by the microcontroller. The interrupts must be enabled (unmasked) by software in order for microcontroller to respond to them. The bit D7 of the status register (sreg) is responsible for enabling and disabling the interrupts globally. The bit is called interrupt bit i. See figure for bits of the status register. Bit 7 6 5 4 3 2 1 0 sreg i 4.1. Set interrupts (sei) instruction Sets the Global Interrupt Flag (i) in sreg (Status Register). The instruction following sei will be executed before any pending interrupts. sei ; set global interrupt enable 4.2. Clear interrupts (cli) instruction Clears the Global Interrupt Flag (i) in sreg (Status Register). The interrupts will be immediately disabled. No interrupt will be executed after the cli instruction, even if it occurs 4/12
simultaneously with the cli instruction. With single cli, we can make i-0 during operation of critical task. cli ; disable all interrupts 4.3. Sleep instruction This instruction sets the circuit in sleep mode sleep ; put MCU in sleep mode Example: sei ; set global interrupt enable sleep ; enter sleep, waiting for interrupt 5. Timer interrupts In tutorial 6 we learn how to use timer 0 and timer 1 with polling method. For example we could examine tov0 bit with the sbrs instruction to monitor timer 0 overflow. Similarly we could examine ocf0 bit to monitor timer 0 compare match occurrence. These methods tied down microcontroller. With a timer interrupt we can avoid tying down the controller. To enable timer overflow interrupt, timer overflow interrupt enable (toien) bit of timer interrupt mask (timsk) register, must be set. To enable timer compare match interrupt, output compare match interrupt enable (ocien) bit of timsk register, must be set. Example ldi r20, (1<<toie0) out timsk, r20 ;enable Timer0 overflow interrupt 5.1. Timer interrupt mask (timsk) register Bit 7 6 5 4 3 2 1 0 timsk ocie2 toie2 ocie1a ocie1b toie1 ocie0 toie0 ocie2 D7 Timer/Counter2 Output Compare Match Interrupt Enable toie2 D6 Timer/Counter2 Overflow Interrupt Enable ocie1a D4 Timer/Counter1, Output Compare A Match Interrupt Enable ocie1b D3 Timer/Counter1, Output Compare BMatch Interrupt Enable toie1 D2 Timer/Counter1, Overflow Interrupt Enable ocie0 D1 Timer/Counter0 Output Compare Match Interrupt Enable toie0 D0 Timer/Counter0 Overflow Interrupt Enable 5/12
Exercise 7.1 Taking that the system clock of your ATMega processor is 1MHz and using Timer1 write and run a program that toggle the LED every one second. Utilize compare match interrupt capability of tier 1. Tip: Use timer1_1sec.asm program from tutorial 6 as a starting point to build a solution. Save the program as timer1_1sec_int.asm when you finish. Exercise 7.2 Write a program that counts how many times a push button is pressed. In this exercise you must propose button debouncing solution. Hints. 1. Use timer 0 interrupt to read buttons state every 5ms. 2. In the interrupt procedure copy pin state to general purpose register. Use the general purpose register in the main program loop to analyse the current pin state. 3. To account a single button event, detect when the button is pressed but wait until the button is released to complete the cycle. 6. External interrupts There are three external hardware interrupts in ATMega32: Int0, Int1, and Int2. They are located on selected ports' pins. These interrupts must be enabled before they can take effect. This is done using the intn bit located in the gicr register. Ext. interrupt Pin location Int. vector location Enable bit location Int0 Port D.2 $0002 gicr.6 Int1 Port D.3 $0004 gicr.7 Int2 Port B.2 $0006 gicr.5 Example ldi r20, (1<<int0) ;enable external interrupt 0 out gicr, r20 6.1. General Interrupt Control (gicr) register Individual external interrupts can be enabled using bits in General Interrupt Control Register (gicr). Bit 7 6 5 4 3 2 1 0 6/12
gicr int1 Int0 int2 int1 D7 =0 disabled external interrupt 1 =1 enables external interrupt 1 int0 D6 =0 disabled external interrupt 0 =1 enables external interrupt 0 int2 D5 =0 disabled external interrupt 2 =1 enables external interrupt 2 These bits, along with bit I, must be set high for interrupt to be responded to. 6.2. Edge-triggered vs. level-triggered interrupts Int2 is only edge triggered, while Int0 and Int1 can be programmed to be edge or level triggered. In the case of level triggered interrupts, if we want the ISR to be executed only once, the interrupt pin must be brought back to inactive state before reti is executed. Upon reset Int0 and Int1 are low-level-triggered interrupts. If we want to change trigger mode we should program associated Interrupt Sense Control (isc) bits in mcucr register. Bit 7 6 5 4 3 2 1 0 mcucr isc11 isc10 isc01 isc00 Isc01 and isc00 bits are responsible for Int0 triggering mode. isc01 isc00 Description 0 0 Low-level of Int0 pin 0 1 Any logical change on Int0 pin 1 0 Falling edge of Int0 pin 1 1 Rising edge of Int0 pin Isc11 and isc10 bits are responsible for Int1 triggering mode. isc11 isc10 Description 0 0 Low-level of Int1 pin 0 1 Any logical change on Int1 pin 1 0 Falling edge of Int1 pin 1 1 Rising edge of Int1 pin Isc2 bit of mcucsr register defines whenever the Int2 interrupts activate on falling or rising edge. 7/12
Bit 7 6 5 4 3 2 1 0 mcucsr isc2 isc2 Description 0 Falling edge of Int2 pin 1 Rising edge of Int2 pin Example ;make Int0 rising-edge active ldi r20, (1<<isc01) (1<<isc00) out mcucr, r20 6.3. General Interrupt Flag (gifr) register The gifr register contains the interrupt flags for all the ATMega external interrupts. Each of these bit are set individually to logic 1 when the interrupt event for the specified interrupt occurs. Note that the flag bit of an interrupt is set, whether or not the interrupt is enabled, once the interrupt event occurs. In order to cancel any previous external events, the flags should be cleared when an external interrupt is enabled. Bit 7 6 5 4 3 2 1 0 gifr intf1 intf0 intf2 The intf0, intf1, and intf2 flags are set when respective interrupt is pending. This flags are clear automatically after the interrupt service routine is called. The flags can also be cleared by a bit set operation in the gifr register. Example ;clear intf1 flag ldi r20, (1<<intf1) out gifr, r20 7. ZL3AVR keypad revisited So far we used small keypad mode of the ZL2AVR's keypad in our tutorial (JP3 jumper was closed). Now it is time to start using the full functionality of the keypad. We want to be able to read all 16 buttons. We must develop a special procedure to do so, as buttons of the keypad are arranged in 4 4 matrix. To decode which button is pressed we must determine both active column and active row of 8/12
the keypad. To read which columns is active we must set row lines (W1..W4) as outputs and column line (K1..K4) as inputs. We set low state on row lines and read column lines. The column line which state is low is active. To read which row is active we must set column lines (K1..K4) as outputs and row lines (W1..W4) as inputs. We set low state on column lines and read row lines. The row line which state is low is active. Now we must combine the row and the column to deliver the keypad button code. Additionally the key pad is able to generate logic signal when some button is pressed. The signal can be used to generate external interrupt. If row lines (W1..W4) are set as output and they are at low state, the signal on JP13 becomes low when any button is pressed. Exercise 7.3 Write a program which reads a pressed keypad button. Use external interrupt to trigger reading procedure. Output a keypad button code on LED controls. To simplify your program you can assume that button code consist of a row code on high nibble and column code on lower nibble. The row and column code can be 'one cold' encoding (inversion of one hot encoding) 7.. 4 3.. 0 Column code (one cold) Row code (one cold) Row no. Code Column no. Code 1 '0111' 1 '0111' 2 '1011' 2 '1011' 3 '1101' 3 '1101' 4 '1110' 4 '1110' 9/12
Connect LEDs control to Port A, and the keypad to port D. Port A.0=D0 Port A.1=D1 Port A.2=D2 Port A.3=D3 Port A.4=D4 Port A.5=D5 Port A.6=D6 Port A.7=D7 Port D.0=K4 Port D.1=K3 Port D.2=K2 Port D.3=K1 Port D.4=W4 Port D.5=W3 Port D.6=W2 Port D.7=W1 Use external interrupt Int2 to start keypad reading routine. Ensure, that the clock source of ATMega is set to the default 1MHz internal oscillator. The proposal of the assembly program template for the exercise is given below..include"m32def.inc".cseg.org 0 jmp start.org INT2addr jmp keypad_isr ;Keyoad External Interrupt Request 2.def button_code=r20 ; Main program start start: ; Set Stack Pointer to top of RAM ;Set up port A as output for LED controls ; Clear intf2 flag ldi r16, out gifr, r16 ; Enable Int2 ldi r16, out gicr, r16 ; Set Int2 active on falling edge ldi r16, 10/12
out mcucr, r16 ;Set rows as inputs and columns as outputs ;Set rows to high (pull ups) and columns to low to activate JP13 ;Global Enable Interrupt ;Set up infinite loop loop: ;read button code from r20 and send to port A rjmp loop ;Keypad Interrupt Service Routine keypad_isr: ;Disable interrupts ;Store registers that are to be used in ISR on stack ;Set rows as outputs and columns as inputs on Port D ;Set rows to low and columns to high (pull up) on Port D ;Read Port D. Column code in lower nibble nop ;!!!! ;Store column code to r20 on lower nibble ;Set rows as inputs and columns as outputs on Port D ;Set rows to high (pull up) and columns to low on Port D ;Read Port D. Row code in higher nibble 11/12
nop ;!!!!! in r16, ;Store row code to r20 on higher nibble ;Set rows as inputs and columns as outputs ;Set rows to high and columns to low to activate JP13 Clear intf2 flag ldi r16, out gifr, r16 ;Restore registers from stack (in opposite order) pop pop ;Enable interrupts globally reti 12/12