Home ==> Micro beginner ==> 3. LED Blinking

Lecture 3: A LED is blinking

With this lecture animation starts: the LED is going on and off. First with a high frequency, then slowly in one second.

3.0 Overview

  1. Introduction
  2. Hardware, components, mounting
  3. Fast blinking
  4. Second blinking

3.1 Introduction

To blink a LED this has to be driven off and on. The basics of switching an output port has already been described in lecture 2: cbi PORTB,PORTB0 switches the LED on, sbi PORTB,PORTB0 off. That is it.

Unfortunately a controller with a clock frequency of 1.2 Mcs/s, on which the ATtiny13 is working by default, requires for those two operations only 4 / 1.200.000 seconds = 0.000,003,33 seconds. This is much much too fast for the human eye to realize. The solution, to engage the controller with something else in between, is demonstrated here.

3.1.1 Exceution of instructions by the controller

Instruction processing

That is the way an AVR executes instruction words:
  1. The next instruction word is read from the flash storage.
  2. The instruction word is decoded into stages to be executed.
  3. The instruction word is executed. During execution the next instruction word is read and decoded ("Pre-Fetch").
Actually the execution of an instruction word requires two clock cycles. But this is halved by pre-fetching the next instruction already during execution of the previous, so effective execution requires only one cycle. This works in general but not in cases when the instruction word is changing execution address (in case of jumps to somewhere else in the code). In this case the pre-fetched instruction is useless. Therefore all jump instructions that change the address require two clock cycles.

Because of the pre-fetch AVR execute double as fast than without. If one compares different controller types and their clock cycles this has to be taken into account.

SBI instruction

Nearly all instructions of the AVR controllers are executed in a single clock cycle. But a few ones require two (or even more). The ones that we used in the previous lecture and which we use to blink the LED, SBI and CBI belong to this rare species. This results from reading in the whole port first, then setting or clearing a single bit in that byte and rewriting the result back to the port. The execution with pre-fetch requires two clock cycles.

3.1.2 Execution times of instructions

Instruction Set The exceution times of all instructions of an AVR are listed in the device databook in the table "Instruction Set Summary". In the column "Clocks" the number of clock cycles is listed.

Whenever it comes to execution times and exact timing, like in the case of the second blinking, this is the relevant information source.
Home Top Introduction Hardware Fast blinking Second blinking

3.2 Hardware, components and mounting

For blinking the LED we use the same hardware as before in lecture 2. Nothing to be added or changed here.
Home Top Introduction Hardware Fast blinking Second blinking

3.3 Fast blinking

3.1.1 The simple fast blinker

As described at the beginning we can just add "sbi PORTB,PORTB0" to the source code and we are done:

	sbi DDRB,DDB0 ; PB0 output driver enable
	cbi PORTB,PORTB0 ; LED on
	sbi PORTB,PORTB0 ; LED off

The result would be disappointing: a nearly dark LED. We would not see much of the LED because it is ON just for a too short period.

A weak LED The reason for this is that the diode is just for two clock cycles active, followed by 511 inactive clock cycles where the controller reads the empty flash storage, finds 0xFFFF there and does nothing. Until he reaches the end of the flash and starts new at address 0x0000.

Two things can be learned here: first, a controller cannot do nothing, he is executing on and on (the SLEEP instruction where the controller is send to bed is later introduced and used), and second that we need a mechanism to restart the CBI/SBI sequence on and on.

The mechanism to start back with CBI after SBI has been executed is a jump instruction, that redirects the execution address to the CBI instruction. As the address space of the flash is small we use the relative jump instruction RJMP, which can jump forward and backward by more than 2000 instructions. The code would look like this:

	sbi DDRB,DDB0 ; PB0 as output, driver stage on, 2 clock cycles
	cbi PORTB,PORTB0 ; LED on, 2 clock cycles
	sbi PORTB,PORTB0 ; LED off, 2 clock cycles
	rjmp Loop ; Jump relative back to label Loop, 2 clock cycles

The label "Loop:" is the jump adress. Labels always end with ":". The instruction RJMP calculates a relative displacement between the current address and the label's address and inserts this automatically into the RJMP's binary representation, so we do not have to care about this.

. The formulation above has a disadvantage: the LED is on for two cycles and off for four cycles. To correct this we insert two NOP between CBI and SBI, this delays the LED-Off and gives a 50% duty cycle rectangle of four ON and four OFF cycles.

	sbi DDRB,DDB0 ; PB0 as output, driver stage on, 2 clock cycles
	cbi PORTB,PORTB0 ; LED on, 2 clock cycles
	nop ; do nothing, 1 clock cycle
	nop ; do nothing, 1 clock cycle
	sbi PORTB,PORTB0 ; LED off, 2 clock cycles
	rjmp Loop ; Jump relative back to label Loop, 2 clock cycles

The instruction NOP in fact is simply delaying execution for one clock cycle and it does nothing else.

The source code is here for download).

Flow diagram Here we see the so-called flow diagram of the source code. It starts with the reset of the controller and shows I/O operations as trapezoid. The number of clock cycles is added, and they show that the operation of the LED is symetrical.

Every larger project should have such a flow-diagram, it allows us to systematically analyze the tasks to be performed and the decisions to be made during the different flow stages. We will later in this lecture see how helpful such diagrams can be to plan flows and to document those.

For the purists that learned not to use JUMPING in their theoretical studies: jumps are an absolutely necessary element in programming. If you are trained to avoid those you are just blind for the fact that you are using them over and over. But they do not look like jumps. In your language those look like "end" or "}", but they are factually jumps (back to something else). In assembler you have the freedom to use jumps whenever they are useful, so forget those useless theories.

In the simulator avr_sim (available at the avr_sim website) the program execution looks like that.

Start fast blinking Port at start of fast blinking At the start the operating frequency is 1.2 MHz, all portpins are inactive.

First step fast blinking Port at first step of fast blinking After the first step, executiong the instruction SBI DDRB,DDB0, the time elapsed is 1.67 µs (two clock cycles) and the respective direction bit is one now, driving the output pin low because respective PORT register bit still is zero.

Cycle fast blinking Here the instructions of a complete cycle were executed. The stop watch which was cleared when entering the cycle, shows 6.67 µs time, which corresponds to 150 kHz cycle frequency.

Unfortunately the blinking LED is going on and off with a frequency of 1,200,000 / 8 = 150,000 cs/s. In the next section we try to reduce this high speed further.

3.1.2 Delayed fast blinking, 8-Bit

The two NOP instructions that we used to delay execution are not able to delay by more than 500 clock cycles, due to flash limitations. So, more effective solutions are necessary to delay further.

A delay would be to count a counter down until he reaches zero. The following sequence is such a typical delay loop:

.equ cCounter = 250 ; define the number of downcounts
	ldi R16, cCounter ; load a register with that constant
	dec R16 ; decrease counter by one
	brne Loop ; branch if zero flag was not set in last instruction

Here a register (R16) is used to count down. Each AVR has 32 such registers, R0 to R31, each with 8 bits length. So they can store binary values between 0 and decimal 255. LDI means "load immediate". Only the upper half of the registers can be loaded using the LDI instruction.

The constant cCounter defines how often the loop is repeated. DEC decreases the content of the register by one. The Z (zero) flag in the status register of the controller is set if the register reaches zero after DEC, if not it is cleared. The status register is eight bit wide. If and which bit is changed in the status register during execution of an instruction is listed in the Instruction Set Summary.

If the DEC instruction leads to zero in the above example, the execution of the loop ends, if not the loop is repeated. That is reached by the instruction BRNE.That means "Branch if not equal", a so-called conditional jump. Those conditional jumps move 63 instructions backward or 64 instructions forward. If this extension is exceeded a different jump mode has to be used. In our case above the branch is only one instruction back, so BRNE is valid.

The Instruction Set Summary yields the following clock cycles for the loop:

.equ cCounter = 250 ; (no clock cycle, assembler internal operation)
	ldi R16, cCounter ; 1 clock cycle
Loop: ; (no clock cycle, assembler internal operation)
	dec R16 ; 1 clock cycle
	brne Loop ; 2 cycles when branching, 1 cycle without branching

The Instruction Set Summary says that BRNE requires one or two clock cycles. As we know the pre-fetch mechanism requires two clock cycles if the jump back is executed. If no jump back occurs the number of clock cycles is one. The total loop execution requires the following clock cycles:

.equ cCounter = 250 ; (no code)
	ldi R16, cCounter ; 1 clock once executed
	dec R16 ; 1 clock 250 times executed
	brne Loop ; 2 clock 249 times executed, 1 clock once executed

The complete execution takes (1 + 250 + 2 * 249 + 1) = 750 clock cycles. At a system clock frequency of 1.2 Mcs/s 750 clock cycles are equivalent to 625 µs, still too short for the human eye.

Delayed fast blinking, 16 Bit

Now try a 16 bit counter. This requires a 16 bit register, of which the AVR has four: the register pairs R25:R24, R27:R26, R29:R28 and R31:R30. Those can be accessed like single registers (e.g. with LDI instructions), but a few instructions work on the whole pair.

The 16 bit counting loop works like this:

.equ cCounter16 = 50000 ; 1 to 65535 (does not generate code)
	ldi R25,HIGH(cCounter16) ; 1 clock cycle, executed once
	ldi R24,LOW(cCounter16) ; 1 clock cycle, executed once
	sbiw R24,1 ; count down 16 bit, 2 clock cycles, executed 50000 times
	brne Loop16 ; 2 clock cycles when branching 49999 times, 1 clock cycle once

The two mathematical formulations "HIGH" and "LOW" split the 16 bit constant in their upper and lower eight bits and place them 16 bit wise to the register pair R25:R24.

SBIW counts the register pair R25:R24 down by one in 16 bit mode. If the pair reaches zero, the Z flag is set. The instruction "BRNE" again branches conditionally as long as Z is clear.

Now the loop requires (1 + 1 + 2*50000 + 2*49999 + 1) = 200.001 clock cycles or 0.167 seconds. That is already rather near to a second, but does not match it.

Home Top Introduction Hardware Fast blinking Second blinking

3.4 Exact second blinking

The second blinking requires a combined execution of an 8 bit and a 16 bit loop. The source code is here for download.

; **************************************************
; * Blinking a LED in one second with an ATtiny13  *
; * (C)2017 by http://www.avr-asm-tutorial.net     *
; **************************************************
.NOLIST ; Output to list file off
.INCLUDE "tn13def.inc" ; Port definitions 
.LIST ; Output to list file on
; Define Registers
.def rCounterA = R16 ; Outer 8 bit counter
.def rCounterIL = R24 ; Inner 16 bit counter, LSB
.def rCounterIH = R25 ; Inner 16 bit counter, MSB
; Define constants
.equ cInner = 2458 ; Counter inner loop
.equ cOuter = 61 ; Counter outer loop
; Program start
	sbi DDRB,DDB0 ; Port pin PB0 as output
; Program loop
	cbi PORTB,PORTB0 ; Port pin PB0 low, Led on, 2 clock cycles
	; Outer delay loop, Led on
	ldi rCounterA,cOuter ; Outer 8 bit counter, 1 clock cycle
	ldi rCounterIH,HIGH(cInner) ; Inner 16 bit counter, 1 clock cycle
	ldi rCounterIL,LOW(cInner) ; 1 clock cycle
	sbiw rCounterIL,1 ; Inner 16 bit counter downwards, 2 clock cycles
	brne Loop1i ; if not zero: jump to loop1i 2 cycles, if zero 1 cycle
	dec rCounterA ; Outer 8 bit counter downwards, 1 clock cycle
	brne Loop1 ; if not zero: jump to loop1 2 cycles, zero: 1 cycle
	nop ; Delay, 1 clock cycle
	nop ; Delay, 1 clock cycle
	sbi PORTB,PORTB0 ; Port pin PB0 high, Led off, 2 clock cycles
	; Outer delay loop, Led off
	ldi rCounterA,cOuter ; Outer 8 bit counter, 1 clock cycle
	ldi rCounterIH,HIGH(cInner) ; Inner 16 bit counter, 1 clock cycle
	ldi rCounterIL,LOW(cInner) ; 1 clock cycle
	sbiw rCounterIL,1 ; Inner 16 bit counter downwards, 2 clock cycles
	brne Loop2i ; if not zero: jump to Loop2i 2 cycles, if zero 1 cycle
	dec rCounterA ; Outer 8 bit counter downwards, 1 clock cycle
	brne Loop2 ; if not zero: jump to Loop2 2 cycles, if zero 1 cycle
	; Cycle end
	rjmp Loop ; start from the beginning, 2 clock cycles
; End of source code

The inner 16 bit and the outer 8 bit counting loops are available two times identically to yield a 0.5 s off period of the Led and a 0.5 s on period.

This is the flow diagram and the cycle calculation:

Cycle calculation

The resulting formula for the number of cycles is relatively simple. To calculate the optimal constants cOuter and cInner is less simple. The two given constants in the source code are an optimal solution.

Simulating the source code with avr_sim yields the following results:

Simulating 0.5 sec Port B after 0.5 sec The first half of the loop execution lasts rather exactly 0.5 seconds. The portbit 0 in port B has been set to one, driving the output high and switching the LED off. Note that more than 300,000 instructions have been executed in that half second.

Simulating 1 sec Port B after 1 sec The same after the full second: execution time is exact and the portbit has been cleared, by that switching the LED on.

That example demonstrates the clear advantage of assembler over any other programming language: exact planning and control of execution times via counting loops is a simple and straight-forward task. No uncertainties exist, e.g. on what a compiler thinks how long a second would be and what instructions he inserts to resolve the given delay task. Exact planning instead of guessing around what the compiler does.

Home Top Introduction Hardware Fast blinking Second blinking

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