Assembler-Listing generieren | Assembler-Listing generieren

Die Arduino-IDE und auch die Visual-Studio-Erweiterung Visual Micro bieten die Möglichkeit, den Build-Prozess anzupassen. Dieser wird i.W. durch die Einträge in der Steuer-Datei "platform.txt" des jeweiligen Prozessor-Typs geregelt. Details zum Build-Prozess findet man unter Build Process und die Dokumentation der Inhalte der Steuerdateien unter Arduino IDE 1.5 3rd party Hardware specification.

Leider liefert der Standard-Prozess kein Assembler-Listing. Dies kann man jedoch recht einfach ergänzen.

Die GNU Compiler Collection enthält das Programm objdump (meist mit einem Präfix, z.B. avr-objdump). Mit diesem Programm lässt sich aus der erstellten .elf-Datei (Executable and Linking Format) ein Assembler-Listing generieren. Ein entsprechender Aufruf wäre z.B.:

XXX-objdump -D -S -h program.elf" > program.lss

Die Optionen "-D -S- h" sorgen für ein lesbares Listing. Ob man noch "--demangle" hinzufügt, das aus den C++-Signaturen die originalen Methodennamen generiert, ist Geschmackssache.

Über Einträge in der "platform.txt" lassen sich an vielen Build-Prozess-Schritten zusätzliche Kommandos ausführen (s. Arduino IDE 1.5 3rd party Hardware specification). Die .elf-Datei steht nach dem Linken zur Verfügung. Über die Eintrag "recipe.hooks.linking.postlink.postlink.X.pattern" können Kommandos zur Ausführung nach dem Linken eingefügt werden. Die Parameter {compiler.path}, {build.path} und {build.project_name} erlauben den Aufbau passender Datei-Pfade. Theoretisch könnte man das Kommando zum Erzeugen des Assembler-Listings direkt in die "platform.txt" eintragen. Bedauerlicherweise klappt dabei die Umleitung des Outputs in eine Datei nicht. Deshalb muss ein kleines zwischengeschaltetes Batch-Programm helfen (Generate-lss-File.cmd).

@ECHO OFF
%1 -D -S -h %2 > %3

%1: Vollständiger Pfad zu XXX-objdump ("{compiler.path}XXX-objdump.exe").
%2: Vollständiger Pfad zur .elf-Datei ("{build.path}/{build.project_name}.elf").
%3: Vollständiger Pfad zur Ausgabe-Datei ("{build.path}/{build.project_name}.lss").

"platform.txt" benötigt dann diesen Eintrag (eine Zeile!):

recipe.hooks.linking.postlink.postlink.1.pattern=Generate-lss-File.cmd 
     "{compiler.path}XXX-objdump.exe" "{build.path}/{build.project_name}.elf"  "{build.path}/{build.project_name}.lss"

Wenn "Generate-lss-File.cmd" nicht über die PATH-Angabe gefunden werden kann, muss der vollständige Pfad angegeben werden.

Wenn man mit Visual-Studio (respektive Visual Micro) arbeitet, kann die erzeugte .lss-Datei als "vorhandenes Element" in das Projekt mit einbinden. Die Datei steht dann über den Projekt-Explorer per Klick zur Verfügung.

Eine Variante für den ESP8266 ist in ESP8266 FAQ beschrieben.


Arduino-IDE und Prototypen | Arduino-IDE und Prototypen

Wer mit der Arduino-IDE programmiert, erhält manchmal merkwürdige Fehlermeldungen. Wenn man dann in die angegebene Zeile schaut, scheint alles richtig zu sein. Manchmal können von der IDE selbständig eingefügte Prototypen in die ino-Datei die Ursache sein. Die Probleme treten in jeder ino-Datei auf, nicht nur bei der Hauptdatei (mit setup und loop).

Das folgende gilt sowohl für die Arduino-IDE als auch in ähnlicher Weise für die Visual Studio Extension Visual Micro.

Schauen wir uns einmal das folgende kleine Programm an:

01: void setup() { 
02:   foo();
03: }
04: 
05: void loop() { }
06: 
07: void foo(){ }

Wenn man dieses Programm außerhalb der IDE übersetzen würde, würde der Compiler in Zeile 2 einen Fehler melden. Funktion foo ist an dieser Stelle unbekannt. Da der Arduino auch für Anfänger geeignet sein soll, hilft die IDE hier ein wenig aus. Sie fügt selbständig passende Prototypen ein. Die IDE generiert aus der ino-Datei eine cpp-Datei, die in etwa so aussieht:

#include <arduino.h>
#line 1 "C:\\Users\\Ulli\\Documents\\Arduino\\ArduinoPrototypes\\ArduinoPrototypes.ino"

void foo();

#line 1 "C:\\Users\\Ulli\\Documents\\Arduino\\ArduinoPrototypes\\ArduinoPrototypes.ino"
void setup() { 
  foo();
}

void loop() { }

void foo(){ }

Als erstes wird ein include-Statement für "ardoino.h" eingefügt. Etwas darunter wird ein Prototyp für foo eingeschoben. Die Anweisungen, die mit #line beginnen, sollen dafür sorgen, dass Fehlermeldungen des Compiler den Dateinamen und die Zeilennummern der ursprünglichen ino-Datei erhalten.

Das hinzufügen der Prototypen erfolgt nur in der ino-Datei. Normale cpp-Dateien bleiben unangetastet. Fügt man eine cpp-Datei (ExternFuncs.cpp) folgenden Inhalts in das Projekt ein:

01: void dummy() {
02:   bar();
03: }
04: 
05: void bar() {}

meckert der Compiler:

ExternFuncs.cpp: In function void dummy()

Error compiling project sources
ExternFuncs.cpp: 2:7: error: 'bar' was not declared in this scope
Build failed for project 'ArduinoPrototypes'

Eine andere Konfiguration, die mächtig verwirrend scheinen kann:

class Foo{};
void bar(Foo f) {}

ergibt die Fehlermeldung:

error: 'Foo' was not declared in this scope

Schaut man sich die generierte cpp-Datei an, die dem Compiler vorgelegt wird, wird alles klar:

void bar(Foo f);

#line 1 "C:\\Users\\Ulli\\Documents\\Arduino\\ArduinoPrototypes\\ArduinoPrototypes.ino"
class Foo {};
void bar(Foo f) {}

Die Fehlermeldung bezieht sich auf eingefügten den Prototyp für bar. Je nach dem, was vor oder nach dem Code steht, ist die Zeilenangabe falsch.

Auch gilt wieder: Es wird nur die ino-Datei analysiert und modifiziert. Schreibt den obigen Code in eine Header-Datei und bindet diese in die ino-Datei ein, wird das Programm einwandfrei kompiliert. Auch in einer cpp-Datei wird kein Fehler gemeldet.

In der ino-Datei müssen entsprechende Prototypen eingefügt werden. So geht's:

class Foo;
void bar(Foo f);

class Foo {};
void bar(Foo f) {}

Noch ein wenig undurchsichtiger wird es, wenn man mit Templates arbeitet:

template <typename T> T foo(T a, T b) { return a + b; }

führt zu

error: 'T' does not name a type

mit falscher Zeilenangabe.

Schaut man wieder auf die generierte cpp-Datei, findet man:

T foo(T a, T b);

template <typename T> T foo(T a, T b) { return a + b; }

Kein Wunder, dass das nicht funktioniert.

Auch hier ist es am einfachsten, die Funktionsdefinition über eine Header-Datei einzubinden.

Alternativ kann man einen Funktionsprototyp explizit angeben:

template <typename T> T foo(T a, T b);
template <typename T> T foo(T a, T b) { return a + b; }

STM32F103 Arduino Bootloader installieren | STM32F103 Arduino Bootloader installieren

Es gibt mehrere Arten den SMT32F103 über die Arduino-IDE zu programmieren. Am bequemsten scheint der Stm32duino Bootloader zu sein. Bei anderen z.B. bei Benutzung des STLink muss der BOOT0-Jumper umgesetzt werden, wenn man in den Programmiermodus wechseln möchte.

Die Entwicklungsboards mit dem STM32F103C8T6 haben leider bei Auslieferung keinen passenden Bootloader installiert. Das muss man selbst machen.

Benötigt wird:

Ein Board mit einem STM32F103C8T6 ("Bluepill")

Verbindung:

GND ↔ GND
3.3V ↔ 3.3V
TX    ↔ A10
RX   ↔ A9

Die Bootloader-Firmware: rogerclarkmelbourne/STM32duino-bootloader  (Binary: generic_boot20_pc13.bin). Es gibt auch andere Versionen von anderen Entwicklern. Die haben z.T. nicht funktioniert.

Und den Flasher von hier: flasher-stm32. ST hat neuere Flasher, der hat aber nicht funktioniert.

Den Bluepill mit dem RS232-USB-Dongle verbinden und den Dongle an den PC anschließen. Den BOOT0-Jumper auf dem Bluepill auf Position 1 setzen und den Reset-Button drücken.

Den Flasher starten, COM-Port auswählen (ggf. über den Device-Manager ermitteln). Mehrfach die Schaltfläche Next drücken. Später "Download to Device"  auswählen und das Binary auswählen. Nach erneutem Drücken von Next beginnt der Upload.

Danach den BOOT0-Jumper auf dem Bluepill wieder auf Position 0 setzen und den Reset-Button drücken.

 

Die Arduino-Bibliothek:

In den Voreinstellungen als zusätzliche Boardverwalter-URL diesen Eintrag machen: "http://dan.drown.org/stm32duino/package_STM32duino_index.json".

Die Bibliothek, die geladen werden sollte, heißt

 

Nun kann der Bluepill per USB verbunden werden. Er meldet sich als CDC-Device (USB virtueller COM-Port, "Maple Serial") am PC an. Als letztes müssen die passenden Einstellungen in der IDE vorgenommen werden:

Es kann sein, dass beim Upload eines Arduino-Programm Fehlermeldungen erscheinen:

Starting download: [##################################################] finished!
state(8) = dfuMANIFEST-WAIT-RESET, status(0) = No error condition is present
Picked up _JAVA_OPTIONS: -Xmx1024m
error resetting after download: usb_reset: could not reset device, win error: Ein nicht vorhandenes Gerät wurde angegeben.
Done!
Resetting USB to switch back to runtime mode
timeout waiting for COM5 serial
	The upload process has finished.

Der Bluepill meldet ein neues USB-Device an, wenn er in den Upload-Modus wechselt. Wenn der Upload beendet wurde, wird wieder das CDC-Device angemeldet. Offensichtlich klappt hier das Timing / die Synchronisation nicht ganz. Das Programm wurde jedoch einwandfrei geladen.

 

Weitere Infos zur Arduino-Library findet man im STM32duino Wiki.


STM32: Senden über USB Serial | STM32: Senden über USB Serial

Wenn man das STMicroelectronics-Paket zur Entwicklung von Programmen über die Arduino-Umgebung für den STM32 benutzt, kann über die serielle USB-Schnittstelle (CDC) nur dann gesendet werden, wenn DTR eingeschaltet ist!


Timer Verwendung | Timer Verwendung

Der Atmega328P des Arduino Uno besitzt 3 Timer, die zum Teil in Arduino-Funktionen und/oder zum Teil in Libraries bereits in Verwendung sind. Ein Überschreiben von Timer-Register kann also zu Komplikationen führen und sollte mit Vorsicht angewendet werden.

  • 8 Bit-Timer0: Verwendung für Funktionen millis(), micros(), delay() und für PWM bei Pin D5 und D6
  • 16 Bit Timer1: Verwendung z.B. für der Servo-, VirtualWire- und TimerOne-Library und für PWM bei Pin D9 und D10
  • 8 Bit Timer2: Verwendung für Funktion tone() und für PWM bei Pin D3 und D11

c++ Logging | c++ Logging

Das Problem in der Arduino-Umgebung ist, dass bereits Code ausgeführt wird (z.B. die Konstruktoren von globalen Variablen) bevor setup aus geführt wird und damit die serielle Schnittstelle zur Ausgabe von Informationen bereit steht. Bis dahin gehen print-Befehle einfach ins Leere. Man kann dem abhelfen, indem man prüft, ob die Schnittstelle zur Verfügung steht und ggf. die Log-Daten zwischenspeichert. Z.B. so:

static String msgLog = "";
void log(String msg) {
   if (!Serial) {
      msgLog += msg + "\r\n";
      return;
   }
   if (msgLog.length() > 0) {
      Serial.print(msgLog);
      msgLog = "";
   }
   Serial.println(msg);
}