Forum: Mikrocontroller und Digitale Elektronik Probleme mit AVR ADC


von Thomas I. (Gast)


Lesenswert?

Hallo,

Ich habe Probleme mit dem ADC im AVR. Ich habe das Gefühl, der misst 
Mist.
Folgendes habe ich:
1
EMPTY_INTERRUPT(ADC_vect);
2
3
#define _IMPL_ADC
4
static uint8_t oldchan;
5
6
void init_adc() {
7
  /* Reference = internal reference (REFS0 = 1, REFS1 = 1)
8
   * ADLAR = 0 (right adjust)
9
   * REFS0 = 1, REFS1 = 0 => Vdd = Vref
10
   * MUX = 00000 (ADC0, no gain, SE )
11
   */
12
   ADMUX = (1 << REFS0);
13
   /*
14
    * ADC is enabled, and the intrrupt is enabled
15
    * The interrupt is empty !!,it's only for getting out of the sleep
16
    * mode
17
    *
18
    * ADC_clk = sys_clk/64
19
    */
20
   ADCSRA = (1 << ADEN) | (1 << ADIE) | 6;
21
   oldchan = 0;
22
}
23
24
uint16_t get_adc_val(uint8_t chan) {
25
  int16_t adc_val;
26
27
  if (oldchan != chan) {
28
    ADMUX = (1 << REFS0) | (chan & 31);
29
    oldchan = chan;
30
  }
31
32
  set_sleep_mode(SLEEP_MODE_IDLE);
33
        sleep_enable();
34
  sei();
35
36
  ADCSRA |= (1 << ADSC); /* start cvonversion */
37
38
  while (ADCSRA & (1 << ADSC))
39
    sleep_cpu();
40
41
  sleep_disable();
42
  adc_val = ADC;
43
44
  return adc_val;
45
}

Das Device ist ein AT90can128, Clock ist 8MHz, der ADC sollte also mit 
125 kHz laufen, was zwischen 50 und 200kHz liegt.

Vdd ist 3,3V, was auch als Vref verwendet wird.

In meinem main habe ich folgendes:
1
    
2
if (strncmp_P((const char*) uart_rx_buf, PSTR("meas_bg\n"), 8) == 0) {
3
  get_adc_val(30);
4
  printf_P(PSTR("BG = %d\n"), get_adc_val(30));
5
}

Laut datasheet sollte kanal 30 ja die interne BG Spannung von 1,1 V sein 
(Seite 288)...

Ich dachte also, ich bekomme dann einen wert von
ADC = (1,1V * 1023 / 3,3V) = ~ 340.

Statdessen bekomme ich:
1
BG = 546
2
BG = 547
3
BG = 546
4
BG = 531

Was GROB daneben liegt.

Was mache ich falsch???

von Hmm (Gast)


Lesenswert?

Ich bin der Ansicht, dass der Einsatz des Sleep-Modes hier unangebracht 
ist.

Magst Du das Programm mal bitte kommentieren, vielleicht erstmal in 
Hinblick auf den Sleep-Mode? Was soll mit dessen Verwendung errreicht 
werden und in welcher Weise`?

von Thomas I. (Gast)


Lesenswert?

Hmm schrieb:
> Ich bin der Ansicht, dass der Einsatz des Sleep-Modes hier unangebracht
> ist.
>
> Magst Du das Programm mal bitte kommentieren, vielleicht erstmal in
> Hinblick auf den Sleep-Mode? Was soll mit dessen Verwendung errreicht
> werden und in welcher Weise`?

Der Sleep-Mode soll einfach weniger Saft ziehen wie eine IDLE-Loop
z.B.:
1
  while (ADCSRA & (1 << ADSC))
2
    ;
Ich will warten, bis die CPU fertig ist, dann den ADC wert auslesen, und 
weiter geht's im Programm.

Die while-schleife ist da, da der timer ständig Interrupts abfeuert (und 
die werden auch gebraucht!).

Ich habe aber festgestellt, dass nach jedem 2. Mess-versuch von VBG der 
darauf folgende Messwert auch grottenfalsch ist (viel zu niedrig, eher 
das, was ich für VBG erwartet hätte).
Ich habe das Problem aber jetzt gelöst:
1
uint16_t get_adc_val(uint8_t chan) {
2
  int16_t adc_val;
3
4
  if (oldchan != chan) {
5
    ADMUX = (1 << REFS0) | (chan & 31);
6
    oldchan = chan;
7
    /* we disable and re-enable the ADC to avoid shitty readings */
8
    ADCSRA &= ~(1 << ADEN);
9
    ADCSRA |= (1 << ADEN);
10
11
    /* then we do a dummy reading (NB: old_chan is already changed here), and then we sleep for 100ms */
12
    get_adc_val(chan);
13
    _delay_ms(1.0);
14
15
  }
16
17
  set_sleep_mode(SLEEP_MODE_IDLE);
18
        sleep_enable();
19
  sei();
20
21
  ADCSRA |= (1 << ADSC); /* start cvonversion */
22
23
  while (ADCSRA & (1 << ADSC))
24
    sleep_cpu();
25
26
  sleep_disable();
27
  adc_val = ADC;
28
29
  return adc_val;
30
}

Also, beim Kanal wechseln, einen vollen Reset des adc, dummy-messung, 1 
ms warten, messen.

Hier habe ich die Busy-loop, weil ich normalerweise den Kanal nicht so 
oft wechsle. Jetzt gib't auch sinnvolle Messwerte.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.