Assembler mit dem Arduino programmieren
https://bildung.social/@oerinformatik/
https://oer-informatik.de/assembler-mit-arduino-ide-programmieren
tl/dr; (ca. 5 min Lesezeit): Wir haben uns entschlossen, die Arduino-Welt auf den Kopf zu stellen und mit dieser vereinfachten IDE alles möglichst kompliziert zu machen: Assembler mit Arduino-Boardmitteln zu programmieren. Nach den Vorbereitungen geht es hier in Teil 2 ins Eingemachte: die eigentliche Programmierung (zu Teil 1, den Vorbereitungen, geht es hier). (Zuletzt geändert am 22.02.2024)
Die Arduino-IDE funktioniert, die einzelnen Tools zur Programmierung wurden in den Tiefen der Arduino-IDE identifiziert und idealerweise in den PATH übernommen, um nicht immer die ganzen Pfade eingeben zu müssen? Der Arduino wurde angeschlossen und der genutzte Port des Arduinos ist bekannt? Fein, dann können wir loslegen! (Andernfalls bitte zu Teil 1, den Vorbereitungen, springen).
Eine Assembler-Datei editieren und speichern
Jetzt geht es ans Eingemachte: Wir wollen eine Assembler-Datei auf dem Arduino ausführen.
Dazu ist zunächst wichtig, die einzelnen erforderlichen Schritte zu verstehen:
Das Flussdiagramm zeigt den Weg vom Assembler- oder C++-Quellcode zum ausführbaren Programm, dass der Programmer auf den Microcontroller überspielt:
Wir müssen diese Schritte der Reihe nach manuell durchführen (die nötigen Befehle folgen weiter unten):
Wir starten mit dem Assembler-Quellcode (Dateiendung
*.asm).Dieser wird von irrelevanten Zeilen bereinigt und daraus ein Object-File erstellt (Dateiendung
*.o).Der Linker fügt weitere Abhängigkeiten an und erstellt daraus eine Datei im Executable and Linking Format (Dateiendung
*.elf).Dieses wandeln wir mit ObjectCopy in eine Datei mit Operationen in hexadezimaler Schreibweise um, die der Microcontroller interpretieren kann (Dateiendung
*.hex)Die
*.hex-Datei wird schließlich vom Programmer auf den Microcontroller geladen.
O.k., das war zu schnell. Wir schauen uns Sinn, Kommandos und Ergebnisse für jeden Schritt noch einmal detaillierter an:

0. Ausgangspunkt: Der Assembler-Quelltext
Wir benötigen eine gültige Assembler-Datei für den ATMega328. In unserem Beispiel nutzen wir blink.asm:
(Der Quelltext ist unten auch abgedruckt)
Außerdem benötigen wir die AVR-Dude-Konfigurationsdatei avrdude.conf aus dem Arduino-Programmpfad (in meinem Fall findet sie sich unter C:\Program Files (x86)\Arduino\hardware\tools\avr\etc\avrdude.conf).
Am einfachsten ist es, beides in einen neuen Ordner zu kopieren und danach in diesen Ordner zu wechseln.
In meinem Beispiel:
(Das > vor jeder Zeile muss nicht abgetippt werden und markiert nur, dass es sich um einen Befehl handelt, der in der Windows PowerShell abgesetzt werden soll.)
Der Befehl dir gibt die jeweiligen Dateien im Ordner aus. Im aktuellen Verzeichnis sollte der *.asm-Quelltext sein:
> dir
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 04.11.2020 10:05 903 blink.asm1. Entschlacken des Assembler-Codes mit dem Object-File (Programm avr-as)
Im ersten Verarbeitungsschritt wird ein Object-File (*.o) aus dem Assembler-Code (*.asm) erzeugt. Dabei wird der Quellcode um Zeilen gekürzt, die für die Ausführung irrelevant sind wie beispielsweise Kommentare. Für diesen Schritt wird das “GNU Assembler”-Programm avr-as mit folgenden Optionen für die Beispieldatei blink.asm genutzt:
Im Ordner sollte sich nun - wenn alles geklappt hat - die Datei blink.o befinden.
> dir
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 04.11.2020 10:05 903 blink.asm
-a---- 04.11.2020 10:09 1752 blink.o2. Einbinden weiterer Bibliotheken mit dem Linker (Programm avr-ld)
Im nächsten Schritt wird das kompilierte Programm mit weiteren benötigten Programmteilen verknüpft, die im Assembler-Quelltext referenziert wurden. Solche Programmteile werden in Bibliotheken zusammengefasst (Libraries wie z.B. die AVR LibC) und können z.B. Konstanten oder andere häufig benötigte Sequenzen enthalten. Aus dem Object-File (*.o) wird dadurch eine Datei im Executable and Linking Format (*.elf). Für diesen Schritt nutzen wir den “GNU Linker” avr-ld mit dem Befehl:
Im Ordner sollte sich nun die Datei blink.elf befinden:
> dir
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 04.11.2020 10:05 903 blink.asm
-a---- 04.11.2020 10:16 1924 blink.elf
-a---- 04.11.2020 10:09 1752 blink.o
3. Datei umwandeln in Hexadezimal-Format mit avr-objcopy
Für den Microcontroller muss die Datei in hexadezimaler Form vorliegen. Nur in diesem Format kann sie direkt auf den Arduino übertragen werden. Das Programm ObjectCopy wandelt die *elf Datei in eine *.hex-Datei um:
Somit sollte sich im Ordner auch die Datei blink.hex befinden. dir bringt Gewissheit:
> dir
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 04.11.2020 10:05 903 blink.asm
-a---- 04.11.2020 10:16 1924 blink.elf
-a---- 04.11.2020 10:19 95 blink.hex
-a---- 04.11.2020 10:09 1752 blink.o
4. Laden der *.hex-Datei auf den Microcontroller
Im letzten Schritt wird die *.hex-Datei auf den Microcontroller überspielt. Diese Aufgabe übernimmt das Programm avrdude. Dazu muss allerdings bekannt sein, über welchen seriellen Anschluss (COMxy) der Arduino erreichbar ist.
An welchem COM-Port lauscht der Arduino?
Falls nicht klar ist, an welchem seriellen COM-Port der Arduino hängt kann dies unter Windows im Geräte-Manager nachgeschlagen werden (Windows-Taste + “Geräte-” tippen). Unter Anschlüsse (COM&LPT) findet sich ein Eintrag wie “Arduino Uno (COM_)”, “Serielles USB-Gerät (COM_)” oder “USB SERIAL CH340 (COM_)”, was die Anschlussnummer verrät. Taucht kein USB-Anschluss auf, ist entweder der USB-Treiber nicht installiert (siehe Info zu CH340 weiter oben) oder es liegen andere Probleme vor.
In der Langform ist dazu der folgende Befehl erforderlich:
Auch über die PowerShell kann der Anschluss mit dem Befehl Get-PnpDevice | Select-String -Pattern 'COM' herausgefunden werden. Neben allerlei anderen USB-Geräten müssten sich auch Einträge wie die folgenden finden, die alle zu unterschiedlichen Arduinos gehören:
> Get-PnpDevice | Select-String -Pattern 'COM'
Win32_PnPEntity: Serielles USB-Gerät (COM7) (DeviceID = "USB\VID_239A&PID_0010&MI_00\6&212F6B11&...)
Win32_PnPEntity: Genuino Micro (COM5) (DeviceID = "USB\VID_2341&PID_8237&MI_00\6&363D022B&...)
Win32_PnPEntity: Arduino Uno (COM10) (DeviceID = "USB\VID_2341&PID_0043\75439323635351412...)
Win32_PnPEntity: USB-SERIAL CH340 (COM9) (DeviceID = "USB\VID_1A86&PID_7523\5&2C705BFE&0&1")
Den AVRDude-Befehl zusammenstellen
Als Einzeiler sieht in meinem Fall der Befehl zum Übertragen wie folgt aus - jedoch muss dieser individuell angepasst werden: also noch nicht direkt kopieren!
Es müssen für diesen Befehl eine Reihe von Optionen geändert werden. In der folgenden Darstellung sind alle Optionen übersichtlich in einzelnen Zeilen dargestellt - und wir gehen sie Stück für Stück durch::
avrdude
-C "C:\Program Files (x86)\Arduino\hardware\tools\avr\etc\avrdude.conf"
-p atmega328p
-c arduino
-P com3
-b 115200
-D
-U flash:w:blink.hex:iFolgende Optionen müssen ggf. angepasst werden:
-C ...: Wir benötigen hier den Pfad zur avrdude.conf-Datei. Diese befindet sich in meinem Fall unter:C:\Program Files (x86)\Arduino\hardware\tools\avr\etc. Wenn man diese Datei direkt in das aktuelle Verzeichnis kopiert kann man sich ggf. den langen Pfad sparen.-p atmega328p -c arduinokann unverändert übernommen werden, wenn es sich um einen Arduino Uno Rev3 handelt.Bei der nächsten Option
-P com3muss die Nummer des seriellen Anschlusses angegeben werden, an dem der Arduino hängt (siehe oben).In der folgenden Option
-U flash:w:blink.hex:iversteckt sich der Dateiname, der ggf. angepasst werden muss (hier:blink.hex)
Die Kurzform (nach kopieren der avrdude.conf) würde etwa so aussehen:
Wird der avrdude...-Befehl abgesendet gibt das Programm den aktuellen Status zurück, und zeigt den Fortschritt an:
avrdude.exe: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.05s
avrdude.exe: Device signature = 0x1e950f (probably m328p)
avrdude.exe: reading input file "blink.hex"
avrdude.exe: writing flash (28 bytes):
Writing | ################################################## | 100% 0.03s
avrdude.exe: 28 bytes of flash written
avrdude.exe: verifying flash memory against blink.hex:
avrdude.exe: load data flash data from input file blink.hex:
avrdude.exe: input file blink.hex contains 28 bytes
avrdude.exe: reading on-chip flash data:
Reading | ################################################## | 100% 0.02s
avrdude.exe: verifying ...
avrdude.exe: 28 bytes of flash verified
avrdude.exe: safemode: Fuses OK (E:00, H:00, L:00)
avrdude.exe done. Thank you.Sofern keine Fehlermeldungen aufgetreten sind, sollte das Programm jetzt laufen - und die LED blinken.
Der Chef-Move am Ende: alles in einem Rutsch mit einer Batch-Datei
Natürlich sind diese vier Schritte recht nervig, wenn man am Programmieren und ausprobieren ist. Jetzt, wo wir die Einzelschritte verstanden haben kann eine Batch-Datei, die alles nacheinander durchführt, alles enorm beschleunigen. Ich habe folgende Datei als “loadasm.bat” gespeichert und rufe sie dann z.B. für blink.asm an COM 7 folgendermaßen auf (wichtig ist in der Powershell Punkt-Backslash vorneweg!):
Der Inhalt der loadasm.bat (Pfad der avrdude.conf unten muss angepasst werden):
@echo off
echo Assemblieren
avr-as -g -mmcu=atmega328p -o %1.o %1.asm
echo Linken
avr-ld -o %1.elf %1.o
echo Intel-Hex erstellen
avr-objcopy -O ihex -R .eeprom %1.elf %1.hex
echo Upload
avrdude -C "C:\Program Files (x86)\Arduino\hardware\tools\avr\etc\avrdude.conf" -p atmega328p -c arduino -P com%2 -b 115200 -D -U flash:w:%1.hex:i
echo Fertig
pause
Anhang: der Assembler-Quellcode der Datei blink.asm
.set PINB, 0x03
.set DDRB, 0x04
.set TCCR0B, 0x2
.set PINB, 0x03
.set DDRB, 0x04
.set TCCR0B, 0x25
.set TCNT0, 0x26
.set LED_MASK, 0b00100000
.set PS_1024, 0b00000101
setup:
ldi r16, PS_1024 ; Set r16 with prescaler 1024 value
out TCCR0B, r16 ; Set the TCCROB to 1024
ldi r16, LED_MASK ; Set r16 to the LED bit
out DDRB, r16 ; Set LED pin to output
clr r18 ; Clear the saved timer
loop:
ldi r20, 61 ; Initialize our software counter
check_timer:
in r17, TCNT0 ; Read the timer
cp r17, r18 ; Compare with previous value
mov r18, r17 ; Save current value
brsh check_timer ; unless the timer has decreased, repeat
decrement:
dec r20 ; decrement the software counter
brne check_timer ; if not zero, go back to checking the timer
toggle:
out PINB, r16 ; toggle the LED
rjmp loop
(Wichtig ist ein Zeilenumbruch am Ende)
Links und weitere Informationen
Quelle des Arbeitsablaufs (leider derzeit nicht mehr erreichbar)
Quelle der angepassten m328Pdef.inc: .equ wurde durch .set ersetzt, “=” durch “,” und ein paar Zeilen auskommentiert, damit es genutzt werden kann
Hinweis zur Nachnutzung als Open Educational Resource (OER)
Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “Assembler mit dem Arduino programmieren” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/assembler-mit-arduino-ide-programmieren veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/assembler/assemblereinstieg-mit-dem-arduinoboard. Stand: 22.02.2024.
[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]
