Forum: Mikrocontroller und Digitale Elektronik AVR ADC fällt anscheinend in Free Running Mode - Schuld ist das Noise Canceler Feature


von Tuxpilot (Gast)


Lesenswert?

Nach längerem rätseln habe ich rausgefunden, warum meine ADCs so gerne 
in den Free Running Mode gehen, obwohl ich ADATE nicht gesetzt habe. Der 
Grund wird im Datenblatt nicht im Zusammenhang mit dem Symptom 
(anscheinend Free Running Mode) genannt, darum habe ich meine ADCs 
bisher nach jeder Messung abgeschaltet. Das ist nicht nötig, denn das 
seltsame Verhalten ist beabsichtigt...

Falls noch jemand an dem Problem sitzt: Der Grund für den anscheinenden 
Free Running Mode ist eine Kombination hiervon:
* Aus Gewohnheit lässt man den ADC eingeschaltet, um die wiederholte 
Initialisierung zu sparen.
* Aus Gewohnheit setzt man den AVR in den Idle Sleep Mode, wenn main() 
nichts mehr zu tun hat, die Timer aber weiterlaufen müssen.
* Aus Gewohnheit deaktiviert man die Interrupts nicht, weil man meint, 
die Interrupts unter Kontrolle zu haben. (ADIE Flag in ADCSRA)

Was passiert?
Der ADC wird dann und nur dann automatisch getriggert, wenn alle 3 
Punkte zusammen auftreten! Leider völlig unabhängig von ADATE, und bei 
ADIE nicht erwähnt. Das ganze nennt sich „ADC Noise Canceler“.

Lösung:
Nach der letzten beabsichtigten Messung das ADIE-Flag löschen. Schon 
wartet der ADC geduldig auf ADCS.

Danke, Atmel...

von Rudolph R. (rudolph)


Lesenswert?

Eine AD-Wandlung wird getriggert wenn der AVR in den IDLE Mode versetzt 
wird,
das ist so auch dokumentiert.
Das kann man ausnutzen indem man auf den ADC-Interrupt verzichtet und 
sich das Ergebnis der AD-Wandlung abholt wenn der AVR durch den nächsten 
Timer-Interrupt wieder aufwacht.

von Peter D. (peda)


Lesenswert?

"• Make sure that the ADC is enabled and is not busy converting. Single 
Conversion mode must be selected and the ADC conversion complete 
interrupt must be enabled.
• Enter ADC Noise Reduction mode (or Idle mode). The ADC will start a 
conversion once the CPU has been halted."

von Tuxpilot (Gast)


Lesenswert?

Rudolph R. schrieb:
> Eine AD-Wandlung wird getriggert wenn der AVR in den IDLE Mode versetzt
> wird,
> das ist so auch dokumentiert.
> Das kann man ausnutzen indem man auf den ADC-Interrupt verzichtet und
> sich das Ergebnis der AD-Wandlung abholt wenn der AVR durch den nächsten
> Timer-Interrupt wieder aufwacht.

Hä, das steht in meinem Datenblatt (vom Attiny841) genau andersrum. Der 
ADC startet nur, wenn man den Interrupt benutzt.

Peter D. hat praktischerweise das Datenblatt zitiert.

Was mich stört ist einfach das hier: Leider wird das Feature im Rest des 
Datenblattes nicht erwähnt. Weder bei den Sleep Modes*, bei den 
Interrupts, noch im Abschnitt „Starting a Conversion”**. Wenn man auf 
das Feature trifft und es nicht kennt, hat man keine Chance es zu 
finden. Man muss sich explizit für Noise Canceling interessieren, oder 
das Datenblatt noch einmal komplett durchlesen (wenn man nicht zufällig 
das Kapitel zum ADC komplett durchlesen möchte).

Tuxpilot schrieb:
> Schon wartet der ADC geduldig auf ADCS.

ADC sucht Freunde... ;) Ich wollte „ADSC“ schreiben.

* Dort steht lediglich:
> If the ADC is enabled, a conversion starts automatically when this mode is 
entered.
was aber kaum weiterhilft und nur halb richtig ist.

** Dort wird sogar angedeutet, dass es diese Feature nicht gibt.

* Anzeige-Link

von Rudolph R. (rudolph)


Lesenswert?

Boah, präziser finde ich das auch gerade nicht, auf jeden Fall habe ich 
das Jahrelang auf diversen AVRs genau so benutzt.

ADC im Single Conversion Mode, kein ADC Interrupt, IDLE und Wakeupt per 
Timer-Interrupt.
1
void init_adc(void)
2
{
3
  ADCSRA = 0x00;       // disable adc
4
  ADMUX  = (1<<REFS1) | (1<<REFS0) | 2; // Referenz = 2,56V intern, Input = ADC2
5
  ADCSRB = (1<<AREFEN); // mit Kondensator am ARef Pin
6
  DIDR0 = (1<<ADC3D) | (1<<ADC2D); // Digital-Inputs an ADC3+ADC2 abschalten
7
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // ADC enabled, prescaler = 128 -> 104µs conversion time @ 16 MHz
8
}

1
...
2
  set_sleep_mode(SLEEP_MODE_IDLE); // einfach nur schlummern...
3
4
  while(1)
5
  {
6
    if(GPIOR1 != 0) // Register durch Timer-Interrupt auf Null gesetzt oder nicht?
7
    {
8
      cli();
9
      sleep_enable();
10
      sei();
11
      sleep_cpu();
12
      sleep_disable();
13
    }
14
    else
15
    {
16
      GPIOR1 = 42;
17
18
      if( (ADCSRA & (1<<ADSC)) == 0) // letzte Wandlung fertig? - nächste Wandlung wird mit dem Sleep angestossen
19
      {
20
        switch(ADMUX & 0x1f)
21
        {
22
          case 10:
23
            adc_buffer[adc_index] = ADC;
24
            adc_index++;
25
            if(adc_index > 7)
26
            {
27
              adc_good = 42;
28
            }
29
            adc_index &= 0x07;            
30
31
            ADMUX  = (1<<REFS1) | (1<<REFS0) | 10; // Referenz = 2,56V intern, Input = ADC10
32
            break;
33
          default:
34
            scratch = ADCH; // einmal lesen um ADC freizuschalten
35
            scratch = scratch; // Warnung für nicht-benutze Variable los werden...
36
            ADMUX  = (1<<REFS1) | (1<<REFS0) | 10; // Referenz = 2,56V intern, Input = ADC10
37
            break;
38
        }  
39
      }
40
...

Okay, da wird nicht mal der Kanal gewechselt, das war das erste was mir 
in die Finger gekommen ist bei dem ich den ADC benutzt habe.

Und ja, das funktioniert, das liest einen Temperatur-Sensor und wirft 
das Ergebnis auf dem CAN aus.

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.