Home ==> Mikrobeginner ==> 3. LED-Blinker
ATtiny13

Lektion 3: Eine LED blinkt


Mit dieser Lektion kommt Leben in die Bude: die LED blinkt. Zuerst hektisch, dann gemütlich.

3.0 Übersicht

  1. Einführung
  2. Hardware, Bauteile, Aufbau
  3. Schnellblinker
  4. Sekundenblinker

3.1 Einführung

Zum Blinken einer LED muss diese rhythmisch ein- und ausgeschaltet werden. Die Grundlagen zum Ein- und Ausschalten hatten wir schon in der vorherigen Lektion: cbi PORTB,PORTB0 schaltet sie ein, sbi PORTB,PORTB0 aus. Das war es.

Dummerweise braucht ein mit 1,2 MHz betriebener Prozessor dafür nur 4 / 1.200.000 Sekunden = 0,000.003.33 Sekunden, was für das menschliche Auge etwas zu schnell ist. Die Möglichkeiten, den Prozessor gezielt mit etwas anderem zu beschäftigen, werden hier aufgezeigt.

3.1.1 Ausführung von Instruktionen durch den Prozessor

Instruktionsablauf

So bearbeitet ein AVR programmierte Befehlsworte:
  1. Das nächste Befehlswort wird aus dem Flashspeicher gelesen.
  2. Das Befehlswort wird dekodiert, das heißt in Schrittfolgen zerlegt.
  3. Das Befehlswort wird ausgeführt. Währenddessen wird bereits das nächste Befehswort gelesen und dekodiert ("Pre-Fetch").
Eigentlich dauert die Bearbeitung eines Befehlswortes daher zwei Taktzyklen. Da aber während der Ausführung schon das nächste Befehlswort geholt und dekodiert wird, braucht die Instruktion effektiv nur einen Taktzyklus. Das funktioniert meistens, aber dann nicht, wenn die Instruktion mit einem Sprung zu einer anderen Adresse im Flashspeicher verbunden ist. In diesem Fall ist das schon gelesene und dekodierte Wort wertlos, da es nicht zur Ausführung kommt. Daher dauern alle Instruktionen, bei denen gesprungen wird, mindestens zwei Taktzyklen.

Wegen des Pre-Fetch-Mechanismus sind AVR fast doppelt so schnell wie sie das ohne diesen wären. Vergleicht man verschiedene Prozessortypen bezüglich ihrer Ausführungsgeschwindigkeit, reicht es nicht aus, die MHz Takt zu vergleichen. Zu vergleichen ist, wieviel Taktzyklen je Instruktion anfallen und ob die Ausführung durch Pre-Fetch beschleunigt wird. So kann aus den gleichen MHz Takt das bis zu vierfache an Geschwindigkeit herauskommen.

SBI-Instruktion

Fast alle Instruktionen des AVR-Prozessors benötigen einen Taktzyklus. Es gibt jedoch auch Instruktionen, die derer zwei benötigen. Die beiden, die wir in der vorherigen Lektion verwendet haben, gehören zu dieser eher seltenen Spezies. Das kommt in diesem Fall daher, dass für das Setzen eines Bits erst der gesamte Port eingelesen, dann das erste Bit gesetzt und der gesamte Port dann wieder geschrieben wird. Die Ausführungszeit beträgt mit Pre-Fetch zwei Taktzyklen.

3.1.2 Ausführungszeiten von Instruktionen

Instruction Set Die Ausführungszeiten aller Instruktionen stehen im Device-Handbuch in der Tabelle "Instruction Set Summary" in der Spalte "Clocks".

Wann immer es auf Ausführungszeiten und Timing ankommt, wie im Falle des Sekundenblinkers, ist das die erste Anlaufstelle.
Home Top Einführung Hardware Schnellblinker Sekundenblinker

3.2 Hardware, Bauteile und Aufbau

Für die Blinker kommt die gleiche Hardware zum Einsatz, wie wir sie schon in Lektion 2 aufgebaut haben.
Home Top Einführung Hardware Schnellblinker Sekundenblinker

3.3 Schnellblinker

3.1.1 Der einfachste Schnellblinker

Wie eingangs beschrieben, bräuchten wir nur die Codezeilen


	sbi DDRB,DDB0 ; PB0 ist Ausgang, Treiber einschalten
	cbi PORTB,PORTB0 ; LED einschalten
	sbi PORTB,PORTB0 ; LED ausschalten

zum Ein- und Ausschalten der LED verwenden. Das Ergebnis wäre eine ganz schwach leuchtende LED.

Schwache LED Die Ursache für diese Schwäche ist, dass die Diode nur für zwei Taktzyklen eingeschaltet ist, dann aber 511 Takte bei ausgeschalteter LED folgen. Der Prozessor arbeitet nämlich seinen ganzen Flashspeicher mit NOPs ab, bis er wieder von vorne beginnt.

Abstellen können wir dies, indem wir eine Sprunginstruktion einfügen, die nach dem Abschalten direkt wieder zum Anschalten verzweigt. Das geht so ( Quellcode hier):

	sbi DDRB,DDB0 ; PB0 ist Ausgang, Treiber einschalten
Schleife:
	cbi PORTB,PORTB0 ; LED einschalten
	sbi PORTB,PORTB0 ; LED ausschalten
	rjmp Schleife ; zum Einschalten springen

Schleife: nennt man ein Label, es ist die Sprungsadresse für den Rücksprung. Der Rücksprung ist die Instruktion "rjmp", in Deutsch "Relativer Sprung".

Die Instruction Set Summary bringt folgende Ausführungszeiten zutage:

	sbi DDRB,DDB0 ; PB0 ist Ausgang, Treiber einschalten
Schleife:
	cbi PORTB,PORTB0 ; LED einschalten, 2 Takte
	sbi PORTB,PORTB0 ; LED ausschalten, 2 Takte
	rjmp Schleife ; zum Einschalten springen, 2 Takte

Das bedeutet, die LED ist für zwei Takte angeschaltet und für vier Takte ausgeschaltet. Das bedeutet 33% Helligkeit 2 / (2 + 4) oder 1 / 3.

Für 50% Helligkeit wäre folgendes nötig:

	sbi DDRB,DDB0 ; PB0 ist Ausgang, Treiber einschalten
Schleife:
	cbi PORTB,PORTB0 ; LED einschalten, 2 Takte
	nop ; Nichts tun, 1 Takt
	nop ; Nichts tin, 1 Takt
	sbi PORTB,PORTB0 ; LED ausschalten, 2 Takte
	rjmp Schleife ; zum Einschalten springen, 2 Takte

Die beiden Instruktionen NOP tun nix, verzögern aber der Programmablauf. Jetzt ist die LED für vier Takte an und für vier Takte aus. Macht genau 50% duty cycle. Allerdings beträgt die Blinkfrequenz nunmehr immer noch 1.200.000 / 8 = 150.000 Hz. Also etwas zu viel für das menschliche Auge.

3.1.2 Verzögerter Schnellblinker, 8-Bit

Um die Blinkfrequenz zu senken, muss noch mehr Verzögerung in den Code. Die folgende Sequenz ist eine typische Verzögerungsschleife:

.equ cZaehler = 250 ; definiere eine Konstante
	ldi R16, cZaehler ; lade Register mit Konstante
Schleife:
	dec R16 ; zaehle Register abwaerts
	brne Schleife ; verzweige, wenn Null-Flagge nicht gesetzt ist

Hier wird ein Register (R16) verwendet, um abwärts zu zählen. Jeder AVR hat 32 solcher Register, von denen sich aber nur R16 bis R31 in einer einzigen Instruktion mit einem Festwert beladen lassen. Register sind frei verfügbare Speicherstellen mit je 8 Bits. Sie können daher Werte zwischen 0 und 255 aufnehmen. Die Konstante cZaehler legt fest, wie oft die Schleife durchlaufen wird. "LDI" bedeutet Load Immediate, "DEC" Decrease oder vermindern, "BRNE" Branch if Not Equal (verzweige wenn nicht Null).

Wiederum fördert das Instruction Set Summary folgende Taktzyklen zutage:

.equ cZaehler = 250 ; (kein Takt)
	ldi R16, cZaehler ; 1 Takt
Schleife:
	dec R16 ; 1 Takt
	brne Schleife ; 2 Takte beim Verzweigen, 1 Takt ohne Verzweigung

Die einzelnen Instruktionen nehmen folgende Takte in Anspruch:

.equ cZaehler = 250 ; (erzeugt keinen Code)
	ldi R16, cZaehler ; 1 Takt
Schleife:
	dec R16 ; cZaehler Takte
	brne Schleife ; 2 * (cZaehler - 1) Takte plus 1 Takt

Der gesamte Durchlauf nimmt also 1 + cZaehler + 2 * cZaehler - 2 + 1 Takte in Anspruch. Das ergibt 3 * cZaehler oder 750 Takte. Zeitlich dauert die Schleife 750 / 1.200.000 oder 625 µs.

Verzögerter Schnellblinker, 16 Bit

Das ist immer noch wesentlich zu kurz. Versuchen wir es also mit 16 Bit.

Wir benötigen dafür 16-Bit-Register, der AVR hat davon vier Stück. Das sind die Registerpaare R25:R24, R27:R26, R29:R28 und R31:R30. Sie lassen sich wie Einzelregister behandeln, manche Instruktionen wirken sich aber jeweils auf das ganze Paar aus.

Die 16-Bit-Schleife sieht so aus:

.equ cZaehler = 50000 ; 1 bis 65535 (erzeugt keinen Code)
	ldi R25, HIGH(cZaehler) ; 1 Takt
	ldi R24,LOW(cZaehler) ; 1 Takt
Schleife:
	sbiw R24,1 ; abwaerts, 2 Takte, 2 * cZaehler
	brne Schleife ; 2 * (cZaehler - 1) Takte plus 1 Takt

Das "SBIW" bewirkt, dass vom Registerpaar R25:R24 immer eins abgezogen wird, und zwar wortweise (16-Bit). Kommt beim Abziehen Null heraus, wird die Z-Flagge im Statusregister gesetzt, ansonsten ist sie Null. Die Instruktion "BRNE" wertet diese Flagge aus, um zur Schleife zurückzukehren, solange diese noch nicht Null ist.

Nun nimmt der Durchlauf 1 + 1 + 2 * cZaehler + 2 * (cZaehler - 1) + 1 = 4 * cZaehler + 1 Takte ein. Bei 50.000 Durchläufen sind das 200.001 Takte oder 0,167 Sekunden. Das kommt der Sekunde schon näher, aber noch nicht ganz.
Home Top Einführung Hardware Schnellblinker Sekundenblinker

3.4 Programm Sekundenblinker

Die Sekunde kriegen wir daher nur mit einer Kombination einer 8-Bit- mit einer 16-Bit-Verzögerungsschleife hin. Und das geht so (Quellcode hier ):

;
; ***********************************************
; * Eine LED blinken lassen mit einem ATtiny13  *
; * (C)2016 by http://www.gsc-elektronic.net    *
; ***********************************************
;
.NOLIST ; Ausgabe im Listing unterdruecken
.INCLUDE "tn13def.inc" ; Port-Definitionen lesen 
.LIST ; Ausgabe im Listing einschalten
;
; Register definieren
; 
.def ZaehlerA = R16 ; Aeusserer 8-Bit-Zaehler
.def ZaehlerIL = R24 ; Innerer 16-Bit-Zaehler, LSB
.def ZaehlerIH = R25 ; Innerer 16-Bit-Zaehler, MSB
;
; Konstanten definieren
;
.equ cInnen = 2458 ; Zaehler Innere Schleife
.equ cAussen = 61 ; Zaehler Aeussere Schleife
;
; Programmstart
;
	sbi DDRB,DDB0 ; Portpin PB0 auf Ausgang
;
; Programmschleife
;
Schleife:
	cbi PORTB,PORTB0 ; Portpin PB0 auf Null, Led an, 2 Takte
	; Verzoegerungsschleifen
	ldi ZaehlerA,cAussen ; Aeusserer 8-Bit-Zaehler setzen, 1 Takt
Schleife1:
	ldi ZaehlerIH,HIGH(cInnen) ; Innerer 16-Bit-Zaehler setzen, 1 Takt
	ldi ZaehlerIL,LOW(cInnen) ; 1 Takt
Schleife1i:
	sbiw ZaehlerIL,1 ; Inneren 16-Bit-Zaehler abwaerts zaehlen, 2 Takte
	brne Schleife1i ; wenn noch nicht Null: zurueck, 2 Takte bei Sprung, 1 Takt ohne Sprung
	dec ZaehlerA ; aeusseren 8-Bit-Zaehler abwaerts, 1 Takt
	brne Schleife1 ; aeussere 8-Bit-Schleife wiederholen, 2 Takte bei Sprung, 1 Takt ohne Sprung 
	nop ; Verzoegerung, 1 Takt
	nop ; Verzoegerung, 1 Takt
;
	sbi PORTB,PORTB0 ; Portpin PB0 auf Eins, Led aus, 2 Takte
    ; Verzoegerungsschleifen
	ldi ZaehlerA,cAussen ; aeusserer 8-Bit-Zaehler setzen, 1 Takt
Schleife2:
	ldi ZaehlerIH,HIGH(cInnen) ; Innerer 16-Bit-Zaehler setzen, 1 Takt
	ldi ZaehlerIL,LOW(cInnen) ; 1 Takt
Schleife2i:
	sbiw ZaehlerIL,1 ; Inneren 16-Bit-Zaehler abwaerts zaehlen, 2 Takte
	brne Schleife2i ; wenn noch nicht Null: zurueck, 2 Takte bei Sprung, 1 Takt ohne Sprung
	dec ZaehlerA ; aeusseren 8-Bit-Zaehler abwaerts, 1 Takt
	brne Schleife2 ; aeussere 8-Bit-Schleife wiederholen, 2 Takte bei Sprung, 1 Takt ohne Sprung
	; Zyklusende
	rjmp Schleife ; von vorne beginnen, 2 Takte
;
; Ende Quellcode
;

Innere 16-Bit- und äussere 8-Bit-Schleife sind zweifach identisch vorhanden.

Taktberechnung

Die sich ergebende Formel für die Anzahl Takte ist relativ einfach, die Ermittlung der beiden Konstanten cAussen und cInnen für ein optimales Ergebnis ist weniger trivial. Die im Quellcode verwendeten Konstanten sind einzigartige Lösungen für diese Aufgabe.
Home Top Einführung Hardware Schnellblinker Sekundenblinker
©2016 by http://www.gsc-elektronic.net