Hi!
In letzter Zeit häufen sich hier die Fragen zu dem AnalogDigitalConverter.
Auch reichen für viele Zwecke die Arduino Funktionen nicht aus.
Darum habe ich mal einen Teil meines ADC Geschreibsels zu einer Klasse zusammen gefasst.
Der Code ist für den ATMega328P geschrieben.
Sollte unverändert auch auf dem ATMega168P laufen.
Also für alle ProMini, Nano und UNO Boards.
Oft findet sich in meinem Code ein unübersichtliches per Hand Register setzen. Wenn man nicht gerade das Datenblatt vor Augen hat, ist das Gedöns kaum lesbar.
Dabei bietet C++ doch so schöne und viele Möglichkeiten das zu abstrahieren.
Was kann es besser, als das Original?
- Den Vorteiler für den ADC Takt manipulieren
- Reduzierung auf 8 Bit Wandlung
- Den internen Temperatur Sensor auslesen
- Die Band Gab Referenz lesen
- Etwas schneller, unnötiges Register setzen wird vermieden
Was fehlt noch?
- Wandeln im Hintergrund (free running mode)
- Triggern durch Timer
- Eine Abstraktion des gleitenden Mittelwertes.
Im Anhang finden sich zwei Dateien, eine Adc.h und eine Adc.cpp, in welcher sich die ganze Magie verbirgt.
Man nehme die beiden Dateien, und werfe sie in den gleichen Order, wo auch die *.ino Datei liegt.
Zu späterer Zeit wird dann auch daraus eine Lib zusammengefügt werden.
Bereit gestellt werden zwei Klassen
- Adc, welche die Hauptarbeit erledigt
- AdcDebug, welche zusätzlich die aktuelle Registerbelegung zeigt
Jetzt erstmal ein paar Beispiele, wie man den internen Temperatursensor ausließt. Es wird der Integerwert ausgegeben, keine berechnete Temperatur in Celsius. Das soll in einem weiteren Beitrag folgen.
Diese Beispiele machen alle das gleiche, zeigen nur die unterschiedlichen Möglichkeiten, wie man die Adc Klasse nutzen kann.
Da möge sich jeder die Variante aussuchen, die ihm mehr liegt.
Zu Anfang die flache Variante:
#include "Adc.h"
Adc adc;
void setup()
{
Serial.begin(9600);
adc.enable();
adc.setClockDivisor(Adc::DIV_128);
adc.setReference(Adc::REF_11);
adc.setResolution(Adc::RES_10BIT);
adc.setSource(Adc::MUX_THERMO);
}
void loop()
{
int t = adc.getValue();
Serial.print("Value: ");
Serial.println(t);
Serial.println("--------------");
delay(1000);
}
Methodenverkettung und kürzerer Zugriff:
#include "Adc.h"
Adc adc;
void setup()
{
Serial.begin(9600);
adc.enable().setClockDivisor(Adc::DIV_128).setReference(Adc::REF_11).setResolution(Adc::RES_10BIT).setSource(Adc::MUX_THERMO);
}
void loop()
{
int t = adc();
Serial.print("Value: ");
Serial.println(t);
Serial.println("--------------");
delay(1000);
}
So weit zum grundsätzlichen....
In Setup wird die Grundeinstellung gemacht.
Details kann man beim konkreten Zugriff noch festlegen.
Hier jetzt die Aufgabe, Pin A0 unter Zuhilfenahme der 1,1V Referenz lesen und A1 mit der Vcc Referenz
#include "Adc.h"
Adc adc;
void setup()
{
Serial.begin(9600);
adc .enable()
.setClockDivisor(Adc::DIV_128)
.setResolution(Adc::RES_10BIT);
}
void loop()
{
Serial.print("A0 Value: ");
Serial.println(adc.setReference(Adc::REF_11)(Adc::MUX_ADC0));
Serial.print("A1 Value: ");
Serial.println(adc.setReference(Adc::REF_VCC)(Adc::MUX_ADC1));
Serial.println("--------------");
delay(1000);
}
Es ist völlig gleichwertig, ob man dieses schreibt:
Serial.println(adc.setReference(Adc::REF_11)(Adc::MUX_ADC0));
Oder es lieber so hat:
adc.setReference(Adc::REF_11);
adc.setSource(Adc::MUX_ADC0);
Serial.println(adc());
Auch die Arrayschreibweise ist genehm:
adc.setReference(Adc::REF_11);
Serial.println(adc[Adc::MUX_ADC0]);
Über Kommentare und Verbesserungsvorschläge würde ich mich sehr freuen.
// edit: 08.09.2017
Hier jetzt die neueste Version, als Arduino Lib
ATMega328P wird unterstützt
ATMega168P wird unterstützt
ATMega168 wird unterstützt
ATMega88 wird unterstützt
Fehlerbereinigung
// edit: 15.09.2017
Default Prescaler Berechnung hinzugefügt
ATMega32U4 wird unterstützt
ATMega16U4 wird unterstützt
ATMega2560 wird unterstützt
ATMega1280 wird unterstützt
ATTiny 45/85 werden unterstützt
Weitere Beispiele:
Betriebsspannung messen mit Kalibrierwert im EEPROM
Interner Temperatursensor mit Kalibrierung
Adc.zip (11.2 KB)
