LED-Lauflicht

© Eberhard Haug 2003-2017

Optimale Darstellung dieser Website
bei aktiviertem
"Active Scripting" (LED-Menü-Buttons).

Bildschirm-Auflösung 800x600 wird unterstützt

Besucher seit 6.1.2004:
 

WEBCounter by GOWEB

 

LED-Lauflicht für AVR-Mikrocontroller (7.4.2016)

Wie versprochen, folgt nun eine µC-basierende LED-Applikation, die sauber strukturiert mittels s’AVR programmiert wurde (jeweils nur eine Stelle eingerückt, damit mehr Text in eine Zeile passt).

Es handelt sich um ein Lauflicht mit 6 LEDs, die zunächst nacheinander angehen und dann in derselben Laufrichtung  nacheinander wieder aus und wieder an u.s.w. (wie ein kriechender Wurm) und zwar solange, bis man kurz eine Taste drückt, wodurch die Laufrichtung umgekehrt wird.

Drückt man die Taste nochmals kurz, erhält man wieder die ursprüngliche Laufrichtung, u.s.w.

Drückt man die Taste länger, so blinken die beiden äußeren LEDs solange, bis man die Taste wieder los lässt, womit erneut eine der beiden Laufrichtungen gestartet wird (je nachdem, zu welchem Zeitpunkt man die Taste losgelassen hat).

Und so einfach schaut das s’AVR-Programm aus (eine Kopie aus der Atmel-Studio-Quelldatei, Assembler-Befehle sind blau markiert):

.def LEDport = r17  ; register for LED port data (bits 0...5)

.def bitmask = r18  ; bit mask 00_0001 through 10_0000

.def LEDcnt  = r19  ; loop counter addressing the individual LEDs

 

ldi LEDport,0b0011_1111   ; set lower 6 bits ...

out DDRC,LEDport          ; ... as outputs

cbi DDRB,2                ; Port B bit 2 = input

sbi PORTB,2               ; enable pull-up (push button to GND)

 

LOOP                      ; main loop

 

 REPEAT                   ; walking LEDs from LSB to MSB

  ldi LEDport,0b0011_1111 ; set lower 6 bits ...

  out PORTC,LEDport       ; ... and all LEDs off

  ldi bitmask,0b00_0001   ; set LSB of 6 bits

  FOR LEDcnt := #12       ; 6 cycles ON one after the other

                          ; 6 cycles OFF one after the other

   in  LEDport,PORTC

   eor LEDport,bitmask    ; toggle masked LED position

   out PORTC,LEDport

   IF  LEDcnt == #7

    ldi bitmask,0b00_0001 ; reload bitmask

   ELSE

    lsl bitmask           ; next LED position

   ENDI

   rcall delay100ms       ; defines the walking speed

   EXITIF !%PINB,2        ; push button LOW?

  ENDF

 UNTIL !%PINB,2           ; push button still LOW?

 

 ; pushing the button continuously will flash the MSB and LSB

 ; LEDs and continue one of the modes after releasing the button

 

 REPEAT                   ; walking LEDs from MSB (reverse order)

  ldi LEDport,0b0011_1111 ; set lower 6 bits ...

  out PORTC,LEDport       ; ... and all LEDs off

  ldi bitmask,0b10_0000   ; set MSB of 6 bits

  FOR LEDcnt := #12       ; 6 cycles ON one after the other

                          ; 6 cycles OFF one after the other

   in  LEDport,PORTC

   eor LEDport,bitmask    ; toggle masked LED position

   out PORTC,LEDport

   IF  LEDcnt == #7

    ldi bitmask,0b10_0000 ; reload bitmask

   ELSE

    lsr bitmask           ; next LED position

   ENDI

   rcall delay100ms       ; defines the walking speed

   EXITIF !%PINB,2        ; push button LOW?

  ENDF

 UNTIL !%PINB,2           ; push button still LOW?

 

ENDL                      ; main loop (forever)


So einfach kann AVR-Assembler-Programmierung ausschauen!

Genau genommen wurde sogar nur die erste REPEAT/UNTIL-Struktur getippt, denn die zweite ist bis auf die geänderte Bitmaske und die Schieberichtung (rot markiert) exakt dieselbe.

Natürlich ist das unstrukturierte "flache" Assembler-Programm nach der Compilierung durch s’AVR einiges länger[1] und alles andere als übersichtlich.

Einzig die Interrupt-Sprungtabelle, die Initialisierung des Stack-Pointers und das aufgerufene Unterprogramm delay100ms (eine 100ms-Warteschleife, ebenfalls mittels s’AVR erstellt) sind hier nicht aufgelistet, auch nicht die Include-Zeile für den jeweiligen AVR-Mikrocontroller.

Im vorliegenden Fall ist es ein ATmega328P (include "m328pdef.inc"), mit dem eine Schaltung aufgebaut und das Programm auch gestestet wurde.

Die LEDs sind quasi mit gemeinsamer Anode an +5V angeschlossen und gehen per LED-Vorwiderstand direkt an die AVR-Port-Ausgänge.

Dieses LED-Lauflicht-Beispiel wurde gewählt, da die Funktion bereits etwas komplexer ist und das s’AVR-Programm bis auf WHILE/ENDW beinahe alle s’AVR-Anweisungen enthält.

Trickreich - und sauber strukturiert - ist die Abfrage der Taste mittels EXITIF, wodurch zunächst die FOR-Schleife verlassen und über die UNTIL-Abfrage auf denselben Pin-Zustand an die REPEAT/UNTIL-Struktur für die jeweils andere Laufrichtung weitergereicht wird.
Das Entprellen der Taste geschieht automatisch über die 100ms-Verzögerungsschleifen.

Natürlich kann man dieses Programmbeispiel einfach an seine eigenen Wünsche anpassen, z.B. für mehr LEDs und ein anderes zeitliches Verhalten.

Per Hardware

Wollte man dieselbe Lauflicht-Funktion ausschließlich per Hardware (also ohne µC) realisieren, würde man etwa zwei bis fünf ICs benötigen (Schieberegister, Taktgeber und ein paar Gatter). Das wäre also schon etwas mehr Platz- und Lötaufwand und auch deutlich weniger flexibel.

Allerdings benötigt man dafür keine µC-Entwicklungsumgebung und man muss auch kein Programm schreiben.

nach oben


[1] Im obigen s’AVR-Programm sind 17 s’AVR-Anweisungen und 22 reine Assembler-Befehle enthalten. Im übersetzten Assembler-Programm werden daraus 47 reine Assembler-Befehle.