Drei Poster zur Pinbelegung
Folgende Interrupts werden von der Arduino-Bibliothek (Arduino Uno) genutzt. Bei anderen Arduino-Typen können andere Hardware-Einheiten (z.B. Timer) genutzt werden.
Interrupt | Bezeichnung | Funktion | Bibliotheks- modul |
Modulfunktionen |
---|---|---|---|---|
Int0: INT0 | External Interrupt Request 0 | Externer Interrupt an PD2 (Arduino Pin 2) | WInterrupts.c | Der Interrupt kann über attachInterrupt() mit einer Funktion verbunden werden. detachInterrupt() hebt diese Verbindung wieder auf. |
Int1: INT1 | External Interrupt Request 1 | Externer Interrupt an PD3 (Arduino Pin 2) | WInterrupts.c | Der Interrupt kann über attachInterrupt() mit einer Funktion verbunden werden. detachInterrupt() hebt diese Verbindung wieder auf. |
Int7: TIMER2 COMPA | Timer/Counter2 Compare Match A | Tonerzeugung | tone.cpp | tone(...), noTone(...) |
Int16: TIMER0 OVF | Timer / Counter0 Overflow | Zeitmessung | wiring.c | wiring.c wird immer eingebunden. Dies geschieht über die dort definierte Funktion init(), die vom Arduino-Framework noch vor setup() aufgerufen wird. In init() wird ein großer Teil der Hardware (Timer, ADC, etc.) für das Framework initialisiert. U.a. werden auch die Interrupts global freigeschaltet (sei()). |
Int18: USART RX | USART Rx Complete | Ein empfangenes Zeichen wird in den Empfangspuffer übertragen. | HardwareSerial.cpp | Methoden der Klasse Serial. |
Int19: USART UDRE | USART, Data Register Empty | Das nächste Zeichen kann dem Ausgabepuffer entnommen und an die USART übergeben werden | HardwareSerial.cpp | Methoden der Klasse Serial. |
Ressource | Verwendung | Initialisierung | Ort | Anmerkung |
---|---|---|---|---|
Global Interrupt | - | Interrupts sind freigegeben (sei()) | init() wiring.c |
- |
Timer0 | Systemzeit / PWM | Prescaler = 64 (CS00, CS01) Timer Overflow Interrupt (TOIE0) |
init() wiring.c |
millis(), micros(), delay(), analogWrite() nutzt die Compare Match Output Unit zur Erzeugung der PWM-Signale (s. AVR-Doku) |
Timer1 | PWM | Prescaler = 64 (CS10, CS11) PWM, Phase Correct, 8-bit (WGM10) |
init() wiring.c |
Details noch unklar |
Timer2 | PWM | Prescaler = 64 (CS22) PWM, Phase Correct (WGM20) |
init() wiring.c |
Details noch unklar |
ADC | Analogmessung | ADC-Prescaler = 128 (ADPS0, ADPS1, ADPS2) ADC freigegeben (ADEN) |
init() wiring.c |
16 MHz / 128 = 125 KHz |
USART | Serial-IO | Explizit deaktiviert (UCSR0B = 0) | init() wiring.c |
Bootloader initialisiert die USART |
Im Windows Temp-Ordner werden jedes Mal, wenn ein Sketch geöffnet und das erste mal kompiliert wird, drei Verzeichnisse angelegt:
Wie funktioniert das mit der Ausgabe über die "Serial.print"-Methoden? Zuerst einmal die groben Zusammenhänge.
Zunächst gibt es die Klasse Print. Print ist eine abstrakte Klasse, kann also selbst nicht instanziiert werden, stellt aber Methoden zur Verfügung, die die Ausgabe von einer Reihe von ObjektTypen erleichtern. Die diversen print()- und println()-Methoden der Klasse Print erzeugen einen Text, der für das übergebene Objekt ausgegeben werden soll, und übergeben diesen Text zeichenweise an die Methode write(). write() selbst ist in Print nicht implementiert (abstrakte Methode), sondern muss in einer konkreten Klasse, die für die Ausgabe über eine vorhandene Hardware-Einheit zuständig ist, für diese Hardware passend zugeschnitten sein. Damit wird die Anbindung einer Hardware-Einheit zur Ausgabe beliebiger Objekte extrem einfach. Um z.B. eine komfortable Ausgabe über die UART des ATmega zu realisieren, muss man nur noch die Ausgabe eines einzelnen Zeichen implementieren. Wie man das macht, kann man tausendfach im Internet nachlesen, z.B. bei Roboternetz.de.
Von Print abgeleitet ist die ebenfalls abstrakte Klasse Stream. Stream stellt neben den geerbten Methoden zur Ausgabe, Methoden zum Einlesen von Objekten bereit. Stream benötigt auch wieder die Unterstützung durch die hardwarespezifische Implementierungen der elementaren Funktionen zum Einlesen eines einzelnen Zeichens. Stream stellt Methoden zur Verfügung, um z.B. den eingelesenen Text in eine Zahl zu konvertieren.
Von Stream sind dann konkrete Klassen für verschiedene Ausgabe-Medien abgeleitet. Die interessanteste ist wohl HardwareSerial zur Ausgabe über die UART des ATmega. Weiterhin gibt es die Klassen UDP und Client, die die Ausgabe per LAN ermöglichen, sofern ein entsprechendes Modul vorhanden ist. Von der Klasse HardwareSerial werden die von Print und Stream abstrakt vorgegeben Methoden implementiert.
Alle drei Klassen definieren weitere Methoden, die zu deren Verwaltung benötigt werden, z.B. Setzen, Lesen und Löschen eines Fehlercodes wie getWriteError() oder Methoden zur Synchronisation wie available() und flush() oder Methoden zur Hardware-Steuerung wie begin() und end().
In der Datei HardwareSerial.h wird das Objekt Serial als extern HardwareSerial Serial deklariert und in HardwareSerial.cpp zur Verfügung gestellt. HardwareSerial.h wiederum wird von Arduino.h ausfgerufen. Diese Datei wird explizit oder durch die IDE in jeden Arduino-Sketch eingebunden.
Bei dem Zugriff auf Daten macht es einen Unterschied, ob sie sich im RAM oder im Flash befinden. Bei der AVR-Programmierung werden konstante Zeichenkette häufig im Flash abgelegt, um den knappen Speicherplatz im SRAM zu schonen. In der Arduino-Umgebung steht hierfür das Makro F() (wie Flash) zur Verfügung. Mit diesem Makro erzeugte Strings liegen im Flash und haben den Typ __FlashStringHelper. Print steht Methoden zur Verfügung um solche Strings auszugeben. Serial.print(F("Nachricht")) legt "Nachricht" im Flash ab und ruft die Methode size_t Print::print(const __FlashStringHelper *) auf.
Für die Ausgabe eigener Klassen steht die Funktion size_t Print::print(const Printable&) zur Verfügung. Die eigene Klasse muss hierzu von Printable abgeleitet werden und die Methode size_t printTo(Print& p) const implementieren. Über p können dann die Ausgaben erfolgen.
Details zu den einzelnen Klassen:
Print
Printable
Stream
HardwareSerial
Weitere Hinweise gibt es hier:
:: How to Use the Print Class ::