Um nicht immer nachschauen zu müssen, wie man eigentlich den ADC ansteuert, hier einige Bibliotheks-Routinen.
Übersicht
2. Spannungsmessung mit Mittelwertbildung
3. Messen der Betriebsspannung AVCC
4. Zufallswert zur Initialisierung des Zufallszahlengenerators
Download (Zur Verwendung der Dateien siehe Bibliothek: Kein zweites Mal!)
Die im Code verwendete Definition für ADC_PRESCALER_MASK findet man bei ADC-Prescaler: Automatik bringt's!
Funktionsbeschreibung | ||
---|---|---|
Bezeichnung | Funktion | Beispiel |
uint16_t ReadAdc(uint8_t channel) |
Einzelne ADC-Messung. ADC wird zu Beginn konfiguriert und eingeschaltet und nach der Messung wieder abgeschaltet (via AEN). channel: lfd. ADC-Nummer, wird direkt an ADMUX weitergegeben. Source-Code |
uint16_t x = ReadAdc(2); |
uint16_t ReadAdcAvg(uint8_t channel) |
Mehrmals messen und Mittelwert bilden. ADC wird zu Beginn konfiguriert und eingeschaltet und nach der Messung wieder abgeschaltet (via AEN). Anzahl Messungen wird über AdcAvgCount festgelegt (s. ADC.cfg.h). Source-Code |
uint16_t x = ReadAdc(Avg2); |
uint16_t ReadVcc() |
Misst die Betriebsspannung (AVCC) in mV. AVCC wird gegen die interne
Bandgap-Referenzspannung gemessen. Der Spannungswert der Bandgap-Referenzspannung ist ADC.cfg.h im Feld VccVbg (Volt Bandgap) hinterlegt. Die Bandgap-Referenzspannung kann mit einem Voltmeter gemessen werden. Dazu legt man den ADC-Referenz-Kanal auf die 1.1 Volt Bandgap-Referenz (REFS1=1, REFS0=1) und kann anschließend die Spannung an AREF abgreifen. Es erfolgt eine Mittelwertbildung über VccReadCount Messungen (s. ADC.cfg.h). Es werden VccDummyReads Messungen vorab durchgeführt um die Bandgap-Spannung nach Zuschaltung zu stabilisieren (s. ADC.cfg.h). Hinweis: Es ist intern eine 32-Bit-Integer-Division erforderlich. Source-Code |
uint16_t x = ReadVcc(); |
uint16_t GetSeed() |
Liefert einen Zufallswert für die Initialisierung des Zufallszahlen-Generators
rand. Es werden die untersten Bit einer Spannungsmessung eines unbenutzten ADC-Kanals genutzt. Source-Code |
srand(GetSeed()); |
Konfiguration der Bibliothek | ||
---|---|---|
Konstante | Wirkung | Beispiel |
AdcAvgCount | Festlegung der Anzahl Messungen zur Mittelwertbildung bei ReadAdcAvg. Bei Werten größer als 63 besteht das Risiko, dass ein Überlauf des internen 16-Bit-Summenspeichers stattfindet. | #define AdcAvgCount 4 |
VccDummyReads | Anzahl der Dummy-Readouts nach Hinzuschalten der Bandgap-Referenz
zu deren Stabilisierung |
#define VccDummyReads 10 |
VccVbg | (Gemessene) Bandgap-Spannung in mV |
#define VccVbg 1015 |
VccReadCount | Anzahl Messungen zur Mittelwertbildung bei
ReadVcc. |
#define VccReadCount 4 |
SeedChanel | Unbenutzter ADMUX-Kanal, der zur Messung
genommen werden soll. Kanal 6 ist bei Prozessoren im DIL-Gehäuse nicht herausgeführt somit unbeschaltet. |
#define Unbenutzter 6 |
Ich bevorzuge die CamelCaseNotation (bin halt ein alter Basic-Fan). Deshalb ist jede Funktion sowohl im C-Stil als auch im CamelCase verfügbar.
/***************************************************************
* read_adc (uint8_t channel) *
***************************************************************/
uint16_t read_adc(uint8_t channel)
{ uint16_t result = 0;
// Den ADC aktivieren
ADCSRA = (1<<ADEN) | ADC_PRESCALER_MASK;
// Kanal des Multiplexers wählen
// Interne Spannungsreferenz verwenden (also 2,56 V)
ADMUX = channel; // | (1<<REFS1) | (1<<REFS0);
// Den ADC initialisieren und eine Blind-Messung machen
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));
// Messung
ADCSRA |= (1<<ADSC);
// Auf Ergebnis warten...
while(ADCSRA & (1<<ADSC));
result = ADCW;
// ADC wieder deaktivieren
ADCSRA = 0;
ADMUX = 0;
// Messwert zurückliefern
return result;
}
Eigentlich ein Standard-Vorgang, wie vielfach im Netz zu finden. Der ADC wird Initialisiert, eine Dummy-Messung gemacht und dann die eigentliche Messung. Zuletzt wird der ADC wieder abgeschaltet.
Die im Code verwendete Definition für ADC_PRESCALER_MASK findet man bei ADC-Prescaler: Automatik bringt's!
/***************************************************************
* read_adc_avg (uint8_t channel) *
***************************************************************/
uint16_t read_adc_avg(uint8_t channel)
{ uint8_t i;
uint16_t result = 0;
// Den ADC aktivieren
ADCSRA = (1<<ADEN) | ADC_PRESCALER_MASK;
// Kanal des Multiplexers wählen
// VCC als Spannungsreferenz verwenden.
ADMUX = channel;
// Den ADC initialisieren und eine Blind-Messung machen
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));
// Jetzt 'AdcAvgCount' mal die analoge Spannung and Kanal 'channel' auslesen
// und dann Durchschnittswert ausrechnen.
for(i=0; i< AdcAvgCount; i++)
{ // Eine Wandlung
ADCSRA |= (1<<ADSC);
// Auf Ergebnis warten...
while(ADCSRA & (1<<ADSC));
result += ADCW;
}
// ADC wieder deaktivieren
ADCSRA = 0;
ADMUX = 0;
// Mittelwert zurückliefern
return result / AdcAvgCount;
}
Die Spannungsmessung wird gemäß der vorgegeben Anzahl mehrfach durchgeführt, die Einzelnen Werte werden addiert und zum Schluss durch die Anzahl der Messungen geteilt (Arithmetischer Mittelwert). Zuletzt wird der ADC wieder ausgeschaltet.
Die Anzahl der Messungen wird über die Konstante AdcAvgCount festgelegt, die in der Datei ADC.cfg.h applikationsspezifisch festgelegt werden kann. Der Standard ist 4. Es empfiehlt sich, eine Zweierpotenz zu wählen. Dann kann die Division durch Shift-Operationen realisiert werden. Das Einbinden der Divisions-Routinesn ist dann nicht notwendig.
Der Maximalwert sollte 63 sein, ansonsten drohen Überläufe.
Die im Code verwendete Definition für ADC_PRESCALER_MASK findet man bei ADC-Prescaler: Automatik bringt's!
/***************************************************************
* read_vcc *
***************************************************************/
uint16_t read_vcc() // Ergebnis in mV
{ uint8_t i;
uint16_t result = 0;
ADMUX = 0x0E // Kanal wählen
| (1<<REFS0); //Referenz: AVCC
ADCSRA = (1<<ADEN) | ADC_PRESCALER_MASK; // ADC einschalten
// Dummy-Readout zur Stabilisierung der Bandgap-Spannungsquelle
for(i = 0; i < VccDummyReads; i++)
{ ADCSRA |= (1<<ADSC); // eine ADC-Wandlung
while ( ADCSRA & (1<<ADSC) ); // auf Abschluss der Konvertierung warten
}
// Eigentliche Messung - Mittelwert aus VccReadCount aufeinanderfolgenden Wandlungen
// Der Messwert liegt zwischen etwa 220 bei 5V und 380 bei 3 V
for(i = 0; i < VccReadCount; i++)
{ ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
while ( ADCSRA & (1<<ADSC) ); // auf Abschluss der Konvertierung warten
result += ADCW; // Ergebnisse aufaddieren
}
// ADC wieder deaktivieren
ADCSRA = 0;
ADMUX = 0;
uint32_t r = (uint32_t)VccVbg * 1024 * VccReadCount / result;
return r;
}
Die interne Bandgap-Spannung wird gegen AVCC als Referenz gemessen. Aus der bekannten Bandgap-Spannung (nominal 1,1 Volt) kann dann AVCC berechnet werden.
Der Spannungswert der Bandgap-Referenzspannung ist ADC.cfg.h im Feld VccVbg (Volt Bandgap) hinterlegt. Hier ist eine Anpassung an die individuellen Gegebenheiten möglich.
Die Bandgap-Referenzspannung kann mit einem Voltmeter gemessen werden. Dazu
legt man den ADC-Referenz-Kanal auf die 1.1 Volt Bandgap-Referenz (REFS1=1,
REFS0=1) und kann anschließend die Spannung an AREF abgreifen.
Es erfolgt
eine Mittelwertbildung über VccReadCount Messungen
(s. ADC.cfg.h).
Es werden VccDummyReads Messungen
vorab durchgeführt um die Bandgap-Spannung nach Zuschaltung zu stabilisieren
(s. ADC.cfg.h).
Hinweis: Es ist intern eine 32-Bit-Integer-Division erforderlich.
Die im Code verwendete Definition für ADC_PRESCALER_MASK findet man bei ADC-Prescaler: Automatik bringt's!
/***************************************************************
* get_seed *
***************************************************************/
uint16_t get_seed()
{ ADMUX = SeedChanel; // zu benutzender Kanal
ADCSRA = _BV(ADPS2) |_BV(ADPS1) |_BV(ADPS0); //Prescaler auf größtmöglichen Wert setzen (128)
ADCSRA |= _BV(ADEN); // ADC einschalten
ADCSRA |= _BV(ADSC); // 1.Messung
while (ADCSRA & _BV(ADSC));
uint8_t byte1 = ADCL;
ADCSRA |= _BV(ADSC); //2. Messung
while (ADCSRA & _BV(ADSC));
uint8_t byte2 = ADCL;
uint16_t seed = byte1 << 8 | byte2;
// ADC wieder deaktivieren
ADCSRA &= ~(1<<ADEN);
return seed;
}
Es wird zweimal die Spannung an einem nicht angeschlossenen ADC-Kanal gemessen. Die Messwerte an einem nicht angeschlossen Kanal streuen extrem. Von diesen Werte werden die niederwertigsten Bits genommen und zu einer 16-Bit-Zahl zusammen gesetzt.
Der zu verwendende unbeschalte ADMUX-Kanal, der zur Messung genommen werden soll. Kanal 6 ist bei Prozessoren im DIL-Gehäuse nicht herausgeführt somit unbeschaltet. Dies ist der Standard-Wert.