Home ==> Micro beginner ==> 7. LED with key interrupt

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.0 Overview

  1. Introduction to key operation and INT0 programming
  2. Tasks to be performed
  3. Hardware, components, mounting
  4. Program

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:

Switch on port pin

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
	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.

Pull-Up 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. Bouncing switch 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

ISC bits in port MCUCR 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 Start ; Reset vector, jump to init routine
	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
	reti ; ADC-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
	; [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:

Signal sequence and timing

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

Schematic duoled The duo LED is attached to port pins PB2 (anode red) and PB0 (node 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
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 from below Key from above 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: Um die Aufgabe zu lösen ist ein klarer Ablauf aufzuzeichnen. In diesem Fall ist er offenkundig so:

Counter flow 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.

T flag 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

INT0 sets T flag 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.

CTC ISR 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.

Main flow 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 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 *
; ***************************************
.INCLUDE "tn13def.inc"
; --------- 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 Start ; Reset vector, jump to init
	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
	reti ; ADC-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
	out SREG,rSreg ; restore SREG
	reti ; return, set I flag
; Program start at reset
	; 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
	sbrc rFlag,bTo ; Springe, wenn CTC-Flagge aus
	rcall Countdown ; abwaerts bearbeiten
	rjmp Loop
; Start sequence
	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
	ret ; Ready, return
; 250 ms over, next phase
	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
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:

Home Top Introduction Task Hardware Program

©2017 by http://www.avr-asm-tutorial.net