Path:
Home ==>
AVR-EN ==>
Micro beginner ==> 3. LED Blinking
Diese Seite in Deutsch (extern):
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
- Introduction
- Hardware, components, mounting
- Fast blinking
- Second blinking
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
That is the way an AVR executes instruction words:
- The next instruction word is read from the flash storage.
- The instruction word is decoded into stages to be executed.
- 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.
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
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.
For blinking the LED we use the same hardware as before in lecture 2. Nothing to be added
or changed here.
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.
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
Loop:
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
Loop:
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).
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.
At the start the operating frequency is 1.2 MHz, all portpins are inactive.
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.
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
Loop:
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
Loop:
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
Loop16:
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.
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
;
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
Loop1:
ldi rCounterIH,HIGH(cInner) ; Inner 16 bit counter, 1 clock cycle
ldi rCounterIL,LOW(cInner) ; 1 clock cycle
Loop1i:
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
Loop2:
ldi rCounterIH,HIGH(cInner) ; Inner 16 bit counter, 1 clock cycle
ldi rCounterIL,LOW(cInner) ; 1 clock cycle
Loop2i:
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:
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:
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.
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.
©2017 by http://www.avr-asm-tutorial.net