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...
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.
"• 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."
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.