In­halts­ver­zeich­nis

1. 1-ms-Interrupt

2. Grafik in C-Array konvertieren

3. Visual Micro

Quellen eingebundener Bibliotheken sichtbar machen

Weitere Tipps zu Visual Micro

4. ESP8266

Debugging-Ausgaben aktivieren

"Komisches Verhalten" abstellen

5. Leonardo

Bootloader

Serielle Schnittstelle

6. Auto-Reset

7. Aufkleber


1-ms-Interrupt

Die Systemzeit -z.B. abrufbar über die Funktion millis()- wird mit Hilfe des Timer0 generiert. Der Timer wird so eingestellt, dass etwa jede Millisekunde ein Overflow-Interrupt erzeugt wird. Innerhalb der Interrupts wird ein Zähler hochgezählt, der ausgelesen werden kann. Der Timer/Counter0 Output Compare Match A Interrupt wird in der Arduino-Umgebung nicht genutzt. Er kann benutzt werden, um einen applikationsgenutzten Zyklus von etwa 1 ms zu erzeugen.

Um nicht mit der Arduino-Bibliothek in Konflikt zu kommen, muss man etwas tiefer in die Erzeugung der Systemzeit und der PWM-Signale einsteigen. Zunächst einmal die Initialisierung des Timers, die in der Funktion init() erfolgt. init() befindet sich in der Datei wiring.c und wird ausgeführt bevor setup() aufgerufen wird. (In den folgenden Code-Auszügen werden nur die relevanten Stellen aufgeführt und auch nur die, die den Arduino Uno (ATmega328p mit 16 MHz Takt) betreffen. Der Code entstammt der Arduino-Version 1.0.5)

void init()
{ ...
	
  sbi(TCCR0A, WGM01); // Fast PWM Mode
  sbi(TCCR0A, WGM00);

  ...
  
  // this combination is for the standard 168/328/1280/2560
  sbi(TCCR0B, CS01); // Prescaler = 64
  sbi(TCCR0B, CS00);

  // enable timer 0 overflow interrupt
  sbi(TIMSK0, TOIE0);
  
  ...
}

Der Timer0 wird im Fast PWM Mode mit einem Prescaler-Wert von 64 initialisiert und der Overflow-Interrupt freigegeben. Der Timer läuft somit mit einer Taktdauer von 64 * 256 / 16 µs = 1024 µs entsprechend einer Frequenz von 976,5625 Hz, also sehr nahe an 1 ms. Für den applikationsnutzbaren 1-ms-Takt muss man nur den Timer/Counter0 Output Compare Match A Interrupt aktivieren. Dazu wird das Output Compare Register A OCR0A auf einen beliebigen Wert festgelegt und der Interrupt im Timer/Counter Interrupt Mask Register TIMSK0 freigegeben. Dies kann z.B. in setup() erfolgen.

OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);

Jetzt fehlt nur noch die entsprechende ISR:

// Interrupt wird etwa jede Millisekunde einmal aufgerufen 
ISR(TIMER0_COMPA_vect) 
{ //ToDo: insert code here
}

Ein möglicher Konflikt mit der PWM-Steuerung wurde noch nicht besprochen. Einfluss auf das PWM-System wird über zwei Methoden genommen: AnalogWrite()  schaltet den PWM-Modus ein, DigitalWrite() schaltet ihn wieder aus.

// Right now, PWM output only works on the pins with
// hardware support.  These are defined in the appropriate
// pins_*.c file.  For the rest of the pins, we default
// to digital output.
void analogWrite(uint8_t pin, int val)
{ // We need to make sure the PWM output is enabled for those pins
  // that support it, as we turn it off when digitally reading or
  // writing with them.  Also, make sure the pin is in output mode
  // for consistenty with Wiring, which doesn't require a pinMode
  // call for the analog output pins.
  pinMode(pin, OUTPUT);
  if (val == 0)
  { digitalWrite(pin, LOW);
  }
  else if (val == 255)
  { digitalWrite(pin, HIGH);
  }
  else
  { switch(digitalPinToTimer(pin))
    { case TIMER0A:
	  // connect pwm to pin on timer 0, channel A
	  sbi(TCCR0A, COM0A1);
	  OCR0A = val; // set pwm duty   <------------ Konflikt!!!!
	  break;
      ...

Der Konflikt mit der Arduino-Bibliothek tritt beim Einschalten des PWM-Modus oder bei Veränderungen des Tastgrades (duty cycle) auf. Hier wird OCR0A verändert. Dies hat zur Konsequenz, dass der folgende Aufruf der TIMER0 COMPA ISR zu einem früheren oder späteren Zeitpunkt erfolgt, je nach dem, ob der neue OCR0A-Wert größer oder kleiner als der vorherige war. Darauf folgende Interrupts erfolgen dann wieder im Rhythmus von 1024 µs. Die Änderungen am Pin-Mode und an den Compare Match Output A Mode Bits COM0A1 haben keinen Einfluss auf das Interrupt-Geschehen. DigitalWrite() passt ebenfalls nur COM0A1 an und führt somit zu keinen Konflikten.

void digitalWrite(uint8_t pin, uint8_t val)
{ uint8_t timer = digitalPinToTimer(pin);
  ...
  // If the pin that support PWM output, we need to turn it off
  // before doing a digital write.
  if (timer != NOT_ON_TIMER) turnOffPWM(timer);
  ...
}

static void turnOffPWM(uint8_t timer)
{ switch (timer)
  {	case  TIMER0A:  cbi(TCCR0A, COM0A1);    break;
	...
  }
}

Grafik in C-Array konvertieren

Hier ist die Anleitung.


Visual Micro

Quellen eingebundener Bibliotheken sichtbar machen

Benutzt man zur Entwicklung das Microsoft Visual Studio mit dem Visual Micro Add-On, ist es praktisch, auf die Quellen der Bibliotheken zurückgreifen zu können. Das ist äußerst nützlich, wenn's mal wieder nicht klappt und die Bibliotheken schlecht dokumentiert sind. Ich vergesse immer wieder, wo man es einstellen kann. So geht's:

Im Reiter "vMicro" findet sich der Menüpunkt "Toggle Hidden Files". Diesen Anklicken.

Projekt-Menü

Im Projekt-Explorer werden nun zusätzlich die entsprechenden Dateien zur Auswahl angeboten:

Einfache Explorer Ansicht   Erweiterte Explorer-Ansicht

Weitere Tipps zu Visual Micro

Reste des Build-Vorgangs löschen

Serieller Monitor VbTermie während des Uploads ausschalten.


ESP8266

Siehe auch ESP8266 FAQ


Debugging-Ausgaben aktivieren

Beispiel-Protokoll ohne aktivierte Debug-Ausgaben
Beispiel-Protokoll ohne aktivierte Debug-Ausgaben

Die erste Maßnahme ist das Debugging für die serielle Schnittstelle zu aktivieren. Dies geschieht durch die Anweisung Serial.setDebugOutput(true). Diese muss nach Serial.begin() erfolgen.

Serial.begin(9600);
Serial.setDebugOutput(true);

Die Dokumentation hierzu besagt:

By default the diagnostic output from WiFi libraries is disabled when you call Serial.begin. To enable debug output, call Serial.setDebugOutput(true). To redirect debug output to Serial1 instead, call Serial1.setDebugOutput(true).

You also need to use Serial.setDebugOutput(true) to enable output from the Arduino printf() function.

Macht man dies, erhält man zusätzliche Ausgaben im Protokoll:

Beispiel-Protokoll mit aktivierter Debug-Ausgabe (Serial.setDebugOutput(true))
Beispiel-Protokoll mit aktivierter Debug-Ausgabe (Serial.setDebugOutput(true))
Die Debug-Ausgaben sind rot umrandet.

Wenn man sich das Protokoll anschaut, scheint das Programm erfolgreich durchgeführt worden zu sein. Auf Grund eines Fehlers in einer der Bibliotheken wird ein falscher Rückgabewert geliefert! Erweitert man das Debugging, kann man es sehen:

Beispiel-Protokoll mit vollständige Debug-Ausgabe (DEBUGV ist definiert)
Beispiel-Protokoll mit vollständige Debug-Ausgabe (DEBUGV ist definiert)
Die Debug-Ausgaben sind rot umrandet.

Es fehlt die Zeile, die auf den Timeout hinweist. Das vollständige Debugging-Protokoll erhält man, wenn man in _core-Verzeichnis die Datei debug.h anpasst. Wenn man Visual Micro benutzt, kann man wie oben beschrieben, Zugriff auf das _core-Verzeichnis erlangen. Gleich zu Beginn der Datei steht diese Zeile:

//#define DEBUGV(...) ets_printf(__VA_ARGS__)

Entfernt man die Kommentarzeichen am Anfang der Zeile, werden die Ausgaben, die mit dem Makro DEBUGV gemacht werden, ebenfalls ausgegeben. Es empfiehlt sich nach dieser Anpassung dafür zu sorgen, dass das Programm komplett neu kompiliert wird (make clean bzw. "Projektmappe bereinigen").

In der Version 2.1.0 hängt die Freigabe von DEBUGV von der Definition der Konstanten DEBUG_ESP_CORE ab:

#ifdef DEBUG_ESP_CORE
#define DEBUGV(...) ets_printf(__VA_ARGS__)
#endif

Benutzt man Visual Micro trägt man die Definition in die Projekt-Eigenschaften ein. Danach "Projekt neu erstellen".

Defines in Projekteigenschaften

Die obigen stammen aus einem Test zu WPS-Konfiguration. Für den Fall, dass im Router WPS aktiviert ist, ist die Debug-Ausgabe sehr "geschwätzig".

Beispiel-Protokoll mit vollständige Debug-Ausgabe (DEBUGV ist definiert)
Beispiel-Protokoll mit vollständige Debug-Ausgabe (DEBUGV ist definiert)
Die Debug-Ausgaben sind rot umrandet.

 


Komisches Verhalten" abstellen

Wenn der ESP8266 nicht das macht, was er soll, z.B. andauernd ein Reset durchführt, helfen drei Dinge:

  1. Die Ausgabe von Debugging-Informationen aktivieren (s. vorhergehenden Absatz)
  2. Nach einem Reset sendet der ESP8266 Informationen zum Boot-Vorgang mit 115200 Baud. Also den seriellen Monitor passend einstellen, damit diese Informationen sichtbar werden.
  3. Je nach dem was vorher mit dem gerät geschehen ist, wurden ungültige Konfigurationsdaten gespeichert. Hier hilft es, den ESP8266 als Access-Point zu initialisieren, diesen AP aber nicht zu benutzen.
    WiFi.mode(WIFI_AP);
    WiFi.softAP("MyESPAP", "MyPassword");
  4. Nicht vergessen, regelmäßig ein delay() (z.B. delay(0)) einzufügen, um dem WiFi-Stack Rechenzeit zu gewähren. Alternativ kann yield() aufgerufen werden. Siehe esp8266/Arduino Reference


Leonardo

Der "Standard"-Arduino enthält einen eigenständigen Baustein zum Aufbau der seriellen USB-Schnittstelle. Beim Leonardo, der auf einem ATmega32U4 basiert, wird diese Schnittstelle direkt über die im Chip integrierten USB-Komponenten hergestellt. Dies hat einige Auswirkungen für den Betrieb.

Tipps für den Leonardo gibt es auf Getting Started with the Arduino Leonardo.


Bootloader

Das Verhalten des  Arduino Leonardo unterscheidet sich deutlich vom Standard-Arduino, die ein separates USB-TO-RS232-Modul besitzen. Während bei diesen die USB-Verbindung bei einem Reset bestehen bleibt, ist dies beim Leonardo nicht der Fall. Hier wird die Schnittstelle ebenfalls zurück gesetzt.

Nach dem Reset verweilt der Leonardo für etwa 8 Sekunden im Bootloader-Modus. Dies ist am oszillieren der On-Board-LED L zu erkennen.

Während dieser Zeit steht der COM-Port, der im Normalbetrieb existiert, nicht zur Verfügung (bei mir COM4). Stattdessen wird ein neuer Port angelegt, der nur zur Kommunikation mit dem Bootloader ausgelegt ist (bei mir COM5).

Anzeige im
Windows-Geräte-Manager 
COM-Ports im Modus "Betrieb" COM-Ports im Modus "Bootloader"
  COM-Ports im Modus "Betrieb" COM-Ports im Modus "Bootloader"

 

Hinweis: Wenn man im Geräte-Manager die Option "Ausgeblendete Geräte anzeigen" einstellt, sieht man auch die aktuell nicht angeschlossenen Geräte. Diese werden grau dargestellt.

Einstellung: "Ausgeblendete Geräte anzeigen" Alle bekannten seriellen Schnittstellen
Einstellung: "Ausgeblendete Geräte anzeigen" Alle bekannten seriellen Schnittstellen

Nur wenn sich der Chip im Bootloader-Modus befindet, kann avrdude den neuen Code laden.

Normalerweise befindet sich der Chip jedoch nicht im Bootloader-Modus. D.h. der Upload-Prozess muss zunächst einen Auto-Reset des Arduino durchführen. Das geschieht durch Aufbau einer Verbindung mit 1200 Baud, die sofort wieder geschlossen wird. Durch das Schließen der Verbindung wird der Reset ausgelöst, wobei der bestehende Port abgebaut wird. Der Chip geht nach dem Reset in den Bootloader-Modus und wartet darauf, dass der Programmer sich meldet. Der Programmer seinerseits achtet darauf, ob ein neuer COM-Port erscheint und versucht über diesen den Upload.

Wenn alles ordnungsgemäß funktioniert, sieht es wie in der folgenden Abbildung aus. Man achte auf die rot markierten Zeilen.

Bootloader erfolgreich gestartet

Beim Leonardo klappt das leider nicht immer. Das sieht dann so aus:

Erfolglose Bootloader-Initialisierung

Der Programmer konnte keinen neuen Port finden und versucht statt dessen über den "Betriebs"-Port. Das kann nicht funktionieren!

Am einfachsten ist es, man hilft dem Programmer, der vergeblich versucht den Reset auslösen, manuell durch drücken der Reset-Taste.

manuelles Reset


Serielle Schnittstelle

Wie der vorgehende Absatz bereits vermuten lässt, hat das serielle Interface auch seine Besonderheiten. Zunächst einmal muss die virtuelle Schnittstelle per USB aufgebaut werden (CDC). Das benötigt Zeit. Die Schnittstelle steht also nicht sofort zur Verfügung. Da der virtuelle COM-Port vom PC her aufgebaut wird, muss es irgendeine Rückmeldung zum Arduino geben, dass der der Aufbau erfolgreich verlaufen ist. Dies geschieht durch PC-seitiges setzen des RTS oder des DTR-Signals (oder beide). Also im Prinzip ein Hardware-Handshake. Abgefragt werden kann der Schnittstellenzustand über den Konvertierungsoperator bool.

//Auszug aus CDC.cpp:
//------------------------

01: // This operator is a convenient way for a sketch to check whether the
02: // port has actually been configured and opened by the host (as opposed
03: // to just being connected to the host).  It can be used, for example, in 
04: // setup() before printing to ensure that an application on the host is
05: // actually ready to receive and display the data.
06: Serial_::operator bool() {
07:   bool result = false;
08:   if (_usbLineInfo.lineState > 0) 
09:     result = true;
10:   delay(10);
11:   return result;
12: }

In der Zeile 8 wird _usbLineInfo.lineState überprüft. In dieses Feld schreibt der USB-Treiber die Signalzustände für DTR (Bit 0) und RTS (Bit 1).

_usbLineInfo.lineState wird auch vor jeder Schreiboperation geprüft. Schreiboperationen sind nur möglich, wenn _usbLineInfo.lineState nicht 0 ist.

Man sollte sich sein Terminal-Programm entsprechend einstellen.

Gut prüfen kann man dies anhand dieses kleinen Programms. Je nachdem welchen Wert (bool) Serial besitzt, wird die On-Board-LED ein- bzw. ausgeschaltet.

// Arduino Leonardo
// Hardware-Handshake Test

void setup()
{ Serial.begin(57600);
}

void loop()
{ if (Serial)
    digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  else                        // wait for a second
    digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
}

Auto-Reset

Damit man beim Arduino via Reset in den Bootloader-Modus wechseln kann, gibt es die Möglichkeit über die serielle Schnittstelle einen Reset auszulösen. Wie genau dieser Reset ausgelöst wird, ist von Board zu Board verschieden. Tiefergehende Information und wie man das Auto-Reset abschalten kann ist hier zu finden: DisablingAutoResetOnSerialConnection. Google findet findet knapp 170.000 Einträge zum Thema Arduino Auto-Reset.


Arduino Uno

Hier wird der Reset über das setzen des DTR-Signals ausgelöst. Die im Netz häufig zu findende Aussage, dass der Verbindungsaufbau den Reset auslöst, ist nur dann richtig, wenn entsprechendes Hardware-Handshake eingestellt ist. Beim seriellen Arduino-Monitor ist das der Fall. Bei dem Monitor, der im Visual Micro Addon für das Visual Studio enthalten ist, kann man es einstellen.

Visual Micro Addon: Serial Monitor


Arduino Leonardo

Tiefergehende Informationen zum Leonardo findet man hier:  Arduino Leonardo. Insbesondere der folgende Abschnitt erklärt einiges:

Automatic (Software) Reset and Bootloader Initiation

Rather than requiring a physical press of the reset button before an upload, the Leonardo is designed in a way that allows it to be reset by software running on a connected computer. The reset is triggered when the Leonardo's virtual (CDC) serial / COM port is opened at 1200 baud and then closed. When this happens, the processor will reset, breaking the USB connection to the computer (meaning that the virtual serial / COM port will disappear). After the processor resets, the bootloader starts, remaining active for about 8 seconds. The bootloader can also be initiated by pressing the reset button on the Leonardo. Note that when the board first powers up, it will jump straight to the user sketch, if present, rather than initiating the bootloader.

Because of the way the Leonardo handles reset it's best to let the Arduino software try to initiate the reset before uploading, especially if you are in the habit of pressing the reset button before uploading on other boards. If the software can't reset the board you can always start the bootloader by pressing the reset button on the board.

Auch hier sind die diversen Erläuterungen ein wenig unscharf. Der Reset erfolgt, wenn bei einer bestehenden 1200 Baud Verbindung DTR gelöscht ist/wird. Soll heißen: Verbindung ohne gesetztes DTR öffnen oder bei einer bestehenden Verbindung DTR löschen. Der folgende Code-Auszug aus CDC.cpp zeigt's genau.

01: // auto-reset into the bootloader is triggered when the port, already
02: // open at 1200 bps, is closed.  this is the signal to start the watchdog
03: // with a relatively long period so it can finish housekeeping tasks
04: // like servicing endpoints before the sketch ends
05: if (1200 == _usbLineInfo.dwDTERate) {
06:      // We check DTR state to determine if host port is open (bit 0 of lineState).
07:     if ((_usbLineInfo.lineState & 0x01) == 0) {
08:         *(uint16_t *)0x0800 = 0x7777;
09:         wdt_enable(WDTO_120MS);
10:     } else {
11:         // Most OSs do some intermediate steps when configuring ports and DTR can
12:         // twiggle more than once before stabilizing.
13:         // To avoid spurious resets we set the watchdog to 250ms and eventually
14:         // cancel if DTR goes back high.
15:        
16:         wdt_disable();
17:         wdt_reset();
18:         *(uint16_t *)0x0800 = 0x0;
19:     }
20: }

Zeile 5: Test auf 1200 Baud
Zeile 7: Test auf DTR
Zeile 9: Watchdog-Reset einleiten


7. Aufkleber

Beim Arduino ist es manchmal schwer zu erkennen, welches Loch der Buchsenleiste zu welcher Funktion gehört. Kleine Aufkleber schaffen Abhilfe.

Arduino Aufkleber   Arduino Aufkleber

Excel-Datei und PDF zum Download. Wenn es mit der Größe nicht genau hinkommt, einfach den Vergrößerungsfaktor beim Seite-Einrichten anpassen.