Path: Home ==> AVR-EN ==> Micro beginner ==> 7. LED with key interrupt     Diese Seite in Deutsch (extern):

# Lecture 7: A LED blinks with a key interrupt

With this lecture we learn how external level changes on a pin can be used to generate an interrupt. We use the interrupt INT0 to detect such level changes without having to poll pins in lengthy loops.

## 7.1 Introduction to key operation and INT0 programming

### 7.1.1 Keys on input ports

If a port pins output driver is switched of, by clearing its DDR/port bit, the logical state of the pin can be read in and, depending from this state, decisions can be made such as conditional branching etc.

This is such a simple wiring to follow the external switch:

To light a LED on port pin PB0 as long as the switch on PB1 is high (connected to the positive operating voltage), the following program would be necessary:
``````
sbi DDRB,PB0 ; enable LED output driver PB0
Loop:
sbis PINB,PINB1 ; jump over next instruction if input pin is high
sbi PORTB,PORTB0 ; switch LED off
sbic PINB,PINB1 ; jump over next instruction if input pin is low
cbi PORTB,PORTB0 ; switch LED on
rjmp Loop
```
```

Now, what happens if the switch is defect or the switch is not attached to the pin? The input is open and, due to the extremely high input resistance on any changes in its proximity, e.g. electrical fields from the 50/60 cs/s electricity supply net, static voltages of fingers in close distance or level changes on neighboring pins. The result is an uncontrolled flickering of the LED.

To end the flickering the input resistance of the port pin has to be reduced and a definitive level has to be applied. This is done with a resistor of e.g. 47 k. Now the open input has a defined high level, if the resistor is attached to the operating voltage. If the switch is closed the additional current is not very large (0.1 mA), even with a mouse piano attached (eight switches in a row). Note that the default level with such a pull-up resistor is high if no switch or key are attached.

Because pull-up resistors on open input pins are necessary very often, those are built-in in AVRs. They are switched on by clearing the data direction bit and setting the output bit. E.g. like this:
``````
cbi DDRB,DDB1 ; direction pin PB1 = input
sbi PORTB,PORTB1 ; switch on pull-up resistor
```
```
Why pull-up and not pull-down? This is due to historic reasons: the input of TTL devices were by default high, so why not have CMOS inputs on the same default level. The same applies if a key or push-button is attached: it is open by default.

### 7.1.2 Keys and switches are bouncing

This is an example of a switch. If the switch changes polarity, this comes not into effect immediately. On a µs or ms level the switch is bouncing before reaching a steady state. In the example case 14 level changes occur within approximately 2 ms. Such a signal swarm can cause immense difficulties if as an AVR reacts very much faster than in ms time. We have to consider bouncing in such cases. Bouncing plays a role in the next lecture.

### 7.1.3 The INT0 interrupt

The INT0 interrupt detects levels and level changes on the INT0 input, which is pin 6 in an ATtiny13. The interrupt branches to the INT0 vector, if the respective interrupt is enabled. The bits ISC01 and ISC00 allow to select which states or changes trigger this interrupt:
ISC01ISC00Interrupt triggered
00Low level triggers INT0 interrupt
01Any change of input level triggers INT0 interrupt
10Falling level triggers INT0 interrupt
11Rising level triggers INT0 interrupt
The default, ISC01 = ISC00 = 0, is rather fatal because a low level on the INT0 pin triggers never ending interrupts, with leaving no time for any other activities. As the INT0 interrupt has the highest priority, no other interrupt comes through as long the low level on the INT0 input pin continues.

The ISC bits are located in the same port, MCUCR, as the SE bits for selecting sleep mode and sleep enable.

The INT0 enable bit is located in the General Interrupt MaSK register GIMSK:

A typical instruction sequence to enable INT0 is as follows:
``````
ldi R16,(1<<ISC01)|(1<<ISC00)|(1<<SE) ; rising level triggers INT0, sleep mode idle
out MCUCR,R16 ; to the universal control port
ldi R16,1<<INT0 ; set INT0 enable bit
out GIMSK,R16 ; to the General Interrupt Mask
sei ; enable interrupt processing
```
```
In the vector table INT0 is the first interrupt following the reset vector, with the highest priority of all interrupts. In the ATtiny13:
``````
.CSEG ; Assemble to program flash (Code Segment)
.ORG 0 ; Addresse to zero (Reset- and Interrupt vectors start at zero)
rjmp Int0_Isr ; INT0 int, active
reti ; PCINT-Int, inactive
reti ; TIM0_OVF, inactive
reti ; EE_RDY-Int, inactive
reti ; ANA_COMP-Int, inactive
reti ; TIM0_COMPA-Int, inactive
reti ; TIM0_COMPB-Int, inactive
reti ; WDT-Int, inactive
;
; Interrupt service routine
;
Int0_Isr: ; INT0 ISR
in R15,Sreg ; save SREG
[...] ; further actions
out SREG,R15 ; restore SREG
reti ; return from int, set I flag
;
; Program init at reset
;
Start:
; [Here the program starts]
```
```
That is it and we can enter practical usage.

Home Top Introduction Task Hardware Program

## 7.2 The task to be programmed

This here is the complex task:

A key signal starts the sequence of the duoled, that consists of two short and a third longer signal with the displayed timing. The program should be insensible against key bouncing.

Home Top Introduction Task Hardware Program

## 7.3 Hardware, Bauteile und Aufbau

### 7.3.1 Hardware

The duo LED is attached to port pins PB2 (anode red) and PB0 (anode green) via a current limiting resistor. The port bit combinations and the resulting colors are listed in the table.

The key is attached to the INT0 input on PB1, the second key pin is on low (minus).

### 7.3.2 Components

The Duo-LED
The duo-LED consists of two LEDs, a red and a green one, connected to two pins. Note that there are other types available with three pins. The longer connecting wire is the anode of the red LED and the cathode of the green LED.

Key
This is a key. With this type two of the pins are internally connected, pushing the key connects the four pins.

Home Top Introduction Task Hardware Program

## 7.4 Program

### 7.4.1 Processes and flows

To perform the task a clear flow is necessary. In this case this flow is obviously like this:

This is a counting scheme. The counter counts down from six to zero, at the different values the on and off periods of the LED and its colors can be derived for the six phases. The count step duration is 0.25 seconds, which has to be derived from a timer overflow or CTC/compare match A interrupt. The counter starts with six and a red LED. Also, in phases 4 and 2 the LED has to be switched to red, in phases 5 and 3 to green.

If the counter reaches zero, the cycle ends and can be restarted by the key. To ensure that the process is only restarted if the cycle has ended we need a flag that signals an active cycle. We use the T flag for that purpose. This also ensures that bouncing has no effect. The T flag is bit 6 of the status register, it can be used freely as it is not affected by any other instruction. To set this flag the instruction SET can be used, to clear it CLT is available. The instructions BRTC and BRTS can be used to branch if the T bit is clear or set. The instruction BST Register, Bit can be used to copy a bit in the register to the T flag, BLD Register, Bit to copy the T flag to the bit in the register.

### 7.4.2 Flow diagrams

The INT0 ISR is simple: it simply sets the T flag. To achieve that the usual saving and restoring of the status register here would be adverse: the restore would override the changed T flag! Fortunately no automatic save/restore mechanism has been implemented, so we can skip this here.

This is the CTC interrupt. To achieve cycles of 0.25 s duration we select a prescaler of 8, a compare match A divider of 150 for the CTC and an 8 bit software counter with 250 counting stages. This yields exactly 0.25 seconds (8 * 150 * 250 / 1200000 = 0.25). The ISR of the CTC decreases the cycle counter. If the counter reaches zero, a flag (bTo = Time-out) is set (to be handled outside the ISR) and the counter is restarted.

The main flow diagram shows what has to done outside the two ISRs, after init and after waking up from the sleep phase. The first task is to detect if the counter is at zero. If yes then the T flag is checked. If set, a new cycle starts and the counter is set to six. If the cycle counter is not zero, the bTo flag is checked (which is set by the CTC ISR when a time-out occurs). If this is set, the next stage is handled. In any case the flow returns to the sleep instruction.

The start sequence does the following with the timer:
• the CTC compare match A value of 150 is written,
• the CTC mode is written to control register A,
• with a prescaler of 8 in control register B the timer is started, and
• the compare match A interrupt is enabled.
The LED is switched to red and the cycle counter is started with 6.

### 7.4.3 The program

This is the program, the source code is here:
``````
;
; ***************************************
; * Key with INT0 interrupt             *
; * (C)2017 by www.avr-asm-tutorial.net *
; ***************************************
;
.NOLIST
.INCLUDE "tn13def.inc"
.LIST
;
; --------- Registers -----------------
; free R0 .. R14
.def rSreg = R15 ; save and restore status register
.def rmp = R16 ; multi purpose register
.def rimp = R17 ; multi purpose inside interrupts
.def rFlag = R18 ; Flag register
.equ bTo = 0 ; Timeout flag timer
.def rCntDwn = R19 ; Cycle count down register
.def rCtcCnt = R20 ; CTC count down
; free R21 .. R31
;
; --------- Ports ---------------------
.equ pOut = PORTB ; Output port
.equ pDir = DDRB  ; Direction port
.equ pIn  = PINB  ; Input port
.equ bARO = PORTB2 ; Anode LED red output pin
.equ bCRO = PORTB0 ; Cathode LED red output pin
.equ bPuO = PORTB1 ; Pull-Up key output pin
.equ bARD = DDB2 ; Anode LED red direction pin
.equ bCRD = DDB0 ; Cathode LED red direction
;
; --------- Timing --------------------
; Clock       = 1200000 cs/s
; Prescaler   = 8
; CTC divider = 150
; Counter     = 250
; ----------------------------
; Signaldauer = 0,250 Sekunden
;
; --------- Konstanten ----------------
.equ cCtcCmp = 149 ; CTC divider - 1
.equ cCtcInt = 250 ; CTC Int counter
;
; --------- Reset- and Int vectors ----
.CSEG ; Assemble to program flash (Code Segment)
.ORG 0 ; Address to zero (Reset- and int vectors start at zero)
rjmp Int0_Isr ; INT0-Int, active
reti ; PCINT-Int, inactive
reti ; TIM0_OVF, inactive
reti ; EE_RDY-Int, inactive
reti ; ANA_COMP-Int, inactive
rjmp Tc0CmpA ; TIM0_COMPA-Int, active
reti ; TIM0_COMPB-Int, inactive
reti ; WDT-Int, inactive
;
; Interrupt Service Routines
;
Int0_Isr: ; INT0-ISR
set ; set T flag
reti ; return, set I flag
;
Tc0CmpA: ; TC0 Compare A ISR
in rSreg,SREG ; save SREG
dec rCtcCnt ; decrease CTC counter
brne Tc0CmpA1 ; not yet zero
sbr rFlag,1<<bTo ; set time out flag
ldi rCtcCnt,cCtcInt ; restart counter
Tc0CmpA1:
out SREG,rSreg ; restore SREG
reti ; return, set I flag
;
; Program start at reset
;
Start:
; Stack Init
ldi rmp,LOW(RAMEND)
out SPL,rmp;
; LED outputs init
ldi rmp,(1<<bARD)|(1<<bCRD) ; outputs
out pDir,rmp ; to direction port
; Pull-Up resistor on
sbi pOut,bPuO ; Pull-up on
; Start conditions counter
clr rCntDwn ; Countdown counter off
clt ; Busy flag off
; Sleep enable, external Int0
ldi rmp,(1<<SE)|(1<<ISC01) ; Sleep, INT0 falling edge
out MCUCR,rmp ; to universal control port
; INT0 enable
ldi rmp,1<<INT0 ; INT0 interrupts on
out GIMSK,rmp ; to General Interrupt Mask
; Enable Interrupts
sei ; set interrupt flag
Loop: ; main program loop
sleep ; go to sleep
nop ; after wakeup
tst rCntDwn ; Countdown count = zero?
brne Loop1 ; no, do not start
brtc Loop1 ; T flag=0, do not start
rcall StartSeq ; start sequence
Loop1:
sbrc rFlag,bTo ; Springe, wenn CTC-Flagge aus
rcall Countdown ; abwaerts bearbeiten
rjmp Loop
;
; Start sequence
;
StartSeq:
ldi rCntDwn,6 ; start value first cycle
ldi rmp,(1<<bPuO)|(1<<bARO) ; red LED and pullup
out pOut,rmp ; to output port
ldi rCtcCnt,cCtcInt ; Restart CTC Int counter
cbr rFlag,1<<bTo ; clear time out flag
ldi rmp,cCtcCmp ; CTC compare match value
out OCR0A,rmp ; to compare port A
ldi rmp,1<<WGM01 ; TC0 as CTC with compare A
out TCCR0A,rmp ; to control port A
ldi rmp,1<<CS01 ; Prescaler = 8
out TCCR0B,rmp ; to control port B
ldi rmp,1<<OCIE0A ; Compare-A-Interrupt
out TIMSK0,rmp ; to TC0 Int Mask
;
; 250 ms over, next phase
;
Countdown:
cbr rFlag,1<<bTo ; clear bTo flag
dec rCntDwn ; next phase down
breq CountDownOff ; reached zero, end of cycle
cpi rCntDwn,5 ; cycle = 5?
breq CountDownGreen ; yes, LED to green
cpi rCntDwn,3 ; cycle = 3?
breq CountDownGreen ; yes, LED to green
; Switch LED to red
ldi rmp,(1<<bPuO)|(1<<bARO) ; Red and Pullup
out pOut,rmp ; to output port
ret
CountDownGreen: ; LED to green
ldi rmp,(1<<bPuO)|(1<<bCRO) ; Green and Pullup
out pOut,rmp ; to output port
ret ; return
CountDownOff: ; End of cycle, switch all off
clr rmp ; timer off
out TCCR0B,rmp ; to control register B
out TIMSK0,rmp ; timer int off
ldi rmp,1<<bPuO ; all clear but Pullup
out pOut,rmp ; LED off
clt ; T flag off
ret ; return
;
; End of source code
;
```
```
Two instructions are new:
• CLR Register: clears register and sets the Z flag,
• TST Register: tests if register is clear (equivalent to OR Register,Register.

### 7.4.4 Simulating the processes

To simulate all these mechanisms we feed the source code to avr_sim and step through the sequences.

That is the status of port B after init:
• The two pin outputs PB0 and PB2 are defined as outputs, their direction bits are set and their driver is switched on. As they both are low, the Duo-LED between PB0 and PB2 is off.
• On PB1, where the key is attached to, the direction bit is zero and the PORT bit is one, which switches the internal pullup resistor on. If we would read the PIN1 bit, it would be high.
• On PB1 the INT0 input pin is located. The software has enabled the respective interrupt enable bit on falling edges on this input, which will happen if the key will be closed.
Now the controller goes to sleep mode idle and port B waits for signals on PB1.

Either by clicking onto the PINB bit 1 with the active pull-up or by clicking onto the INT0 we initiate an INT0 interrupt request. If no other interrupts are executed, the INT0 interrupt will be processed either after four clock cycles or with the next instruction. This would be the case even if another interrupt would be pending because INT0 is the interrupt with the highest priority (highest position in the interrupt vector list.

The controller woke up from sleep mode idle, stored the current execution address to the stack and jumped to the INT0 vector address. From there he jumps to the interrupt service routine.

The only task in the INT0 interrupt service routine is to set the T flag in SREG - to start a new sequence.

After waking up, the controller realizes that the T flag has been set and branches with an RCALL to the StartSeq: section. By doing that, the stackpointer is decreased by two to store the calling address on the stack.

The StartSeq: has set the downcounter in R19 to 6 to start a downcounting sequence. The CTC counter in R20 has been initiated to 250 to execute 250 timer CTC cycles. Note that the T flag in SREG is still set: it will only be cleared after the whole sequence of six phases has been absolved. Further INT0 interrupts therefore do nothing.

The StartSeq: has brought the timer TC0 to count in CTC mode. The TOP value of the counter is 149, so the counter restarts after 150 pulses. The controller clock is divided by 8 in the prescaler, so that the whole CTC cycle lasts 8 * 150 / 1,200,000 = 1.00 ms.

After TC0 reached 150 (and restarted on compare match A) he initiates a compare match interrupt and the controller jumps to the compare match A interrupt vector.

As calculated the first compare match interrupt happens after 1.0025 ms have elapsed. The "overtime" of 0.0025 ms is due to the fact that the compare match had to store the execution address to the stack, to clear the I flag in SREG and to jump to the vector address. If you need more accuracy in your application consider these time delays. In our application that makes no sense as the delay is always the same and as the human eye is not that fast and accurate.

250 CTC cycles have been absolved here. The bTO flag will be set now to signal the timeout. Execution time since timer start is rather accurate.

Now that the bTO flag is set, it is time to change the color of the duo LED according to the phase diagram from red to green. The portbits PB0 and PB2 change here.

This is repeated five times, with one color change omitted.

After 1.5 seconds the whole cycle is over, the T flag is cleared and a restart can be initiated by the key.

Home Top Introduction Task Hardware Program