LED-Lauflicht

© Eberhard Haug 2003-2018

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

Besucher seit 6.1.2004:
 

WEBCounter by GOWEB

 

Hinweis:
Bei Ansicht auf Smartphones werden Zeilen umgebrochen, so dass die Strukturierung der s’AVR-Programme teilweise verloren geht.

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 dank Visual Assist blau markiert und Kommentare sind grün):

.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 strukturierte 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 (Unterschiede 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.

Das "flache" AVR-Assembler-Programm (21.1.2018)

Falls jemand das Lauflicht selbst in purem AVR-Assembler nachprogrammieren will, folgt hier noch das von s’AVR 2.x im effizienten Mode erzeugte "flache" AVR-Assembler-Programm (Achtung: s’AVR-Befehle einschließlich dort stehender Kommentare sind hier unterdrückt):

.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 (/SS, default)

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

 

_L1:

_L4:

 ldi LEDport,0b0011_1111 ; set lower 6 bits

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

 ldi bitmask,0b00_0001   ; set LSB of 6 bits

 LDI LEDcnt,12

_L7:

                         ; 6 cycles OFF one after the other

 in LEDport,PORTC

 eor LEDport,bitmask     ; toggle masked LED position

 out PORTC,LEDport

 CPI LEDcnt,7

 BRNE _L10

 ldi bitmask,0b00_0001   ; reload bitmask

 RJMP _L12

_L11:

_L10:

 lsl bitmask             ; next LED position

_L12:

 rcall delay100ms        ; defines the walking speed

 SBIS PINB,2

 RJMP _L9

 DEC LEDcnt

 BRNE _L7

_L9:

 SBIC PINB,2

 RJMP _L4

 

 ; pushing the button continuously will flash the MSB and LSB

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

 

_L14:

 ldi LEDport,0b0011_1111 ; set lower 6 bits

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

 ldi bitmask,0b10_0000   ; set MSB of 6 bits

 LDI LEDcnt,12

_L17:

                         ; 6 cycles OFF one after the other

 in LEDport,PORTC

 eor LEDport,bitmask     ; toggle masked LED position

 out PORTC,LEDport

 CPI LEDcnt,7

 BRNE _L20

 ldi bitmask,0b0010_0000 ; reload bitmask

 RJMP _L22

_L21:

_L20:

 lsr bitmask             ; next LED position

_L22:

 rcall delay100ms        ; defines the walking speed

 SBIS PINB,2

 RJMP _L19

 DEC LEDcnt

 BRNE _L17

_L19:

 SBIC PINB,2

 RJMP _L14

 RJMP _L1
 

Einzig die Interrupt-Sprungtabelle, die Initialisierung des Stack-Pointers und das aufgerufene Unterprogramm delay100ms (eine 100ms-Warteschleife, ebenfalls mittels s’AVR erstellt) sind weder im s’AVR-Programm noch im "flachen" AVR-Assembler-Progremm 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 18 s’AVR-Anweisungen und 22 reine Assembler-Befehle enthalten. Im übersetzten Assembler-Programm werden daraus 47 reine Assembler-Befehle im "schmerzfreien" Mode und 43 reine Assembler-Befehle im "effizienten" Mode (s’AVR 2.x).