Um nicht immer nachschauen zu müssen, wie man eigentlich den ADC ansteuert, hier einige Bibliotheks-Routinen.

Übersicht

1. Einfache Spannungsmessung

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)
uint16_t read_Adc(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)
uint16_t read_adc_avg(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()
uint16_t read_vcc()
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()
uint16_t get_seed()
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.


1. Einfache Spannungsmessung

/***************************************************************
 * 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!


2. Spannungsmessung mit Mittelwertbildung

/***************************************************************
 * 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!


3. Messen der Betriebsspannung AVCC

/***************************************************************
 * 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!


4. Zufallswert zur Initialisierung des Zufallszahlengenerators

/***************************************************************
 * 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.