Wenn ich meinen AD-Wandler sagen will dass er: -im FreeRunning-Mode laufen soll (ADEN=1) (ADCS=1) (ADFR=1) -den Interrupt Flag setzen soll, aber > keinen Interrupt < auslösen soll (ich will das Flag bei einem anderen Timer-Interrupt auslesen) Dann muss ich doch nur (ADIE=1) setzen und kein Interrupt vom ADC funkt mir dazwischen, weil ich ja das I-bit im SREG nicht gesetzt habe. Ist das so erstmal richtig? Ich würd gern bei einem anderen Interrupt (der eh zur rechten Zeit kommt) abfragen ob die Wandlung fertig ist und dann den Wert in dieser Interrupt-Routine weiter verarbeiten. Ich dachte da (bei 16MHz Takt) an einem Prescaler von 128 (9.6KHz Samplingrate bei 13 Takte/Umwandlung und 5kHz bei 25 Takte/Umwandlung)
Hi Da geht ewas durcheinander: Mit ADIE erlaubst du den ADC-Interrupt. Das I-Bit zeigt global einen Interrupt an. Ein ADC-Interrupt wird durch ADIF oder so ähnlich angezeigt. MfG Spess
Wenn das I-Bit im SREG gelöscht (0) ist, wird gar kein Interrupt mehr ausgelöst. Wird das I-Bit wieder gesetzt, fallen alle in der Zwischenzeit ausgelösten Interrupts an. Den Interrupt des ADC kannst Du sperren, indem Du ADIE auf 0 läßt. Die Wandlung ist fertig, wenn ADIF gesetzt ist. Dieses Flag mußt Du nach dem Auslesen der ADC-Ergebnis-Register manuell auf 1 setzen, um es zu löschen und als Indikator für das nächste Wandlungsende zu nutzen. Im Free-Running Mode besteht, wenn Du den ADC-Interrupt nicht nutzt, die Gefahr, daß Du Wandlungen verpaßt.
Danke für die Erklärung, lass ich ADIE auf null und lösch dann einfach ADIF durch setzen auf eins. Hier hab ich etwas geschrieben, ich hoffe es macht das was es soll.
1 | void adc_init(){ |
2 | // wähle AREF, intern 2.54V
|
3 | ADMUX |= (1 << REFS1) | (1 << REFS0); |
4 | // Enable ADC, AD-Wandlung starten, FreeRunning,AD-Interrupt Enable , Divisor 128 -> 125kHz
|
5 | ADCSRA |= ( 1<<ADEN )|( 1<<ADSC ) | (1<<ADFR) | (1<<ADIE) |( 1<<ADPS0 )| (1<<ADPS1) |( 1<<ADPS2 ); |
6 | |
7 | //AD-Kanal wechsel ermöglichen
|
8 | // ADCSRA &= ~((1 << ADFR)|(1<<ADEN));
|
9 | // ADCSRA |= (1 << ADSC); //AD-Wandlung starten
|
10 | // ADCSRA &= ~(1 << ADSC); //AD-Wandlung stoppen
|
11 | }
|
12 | |
13 | uint16_t getADC(void){ |
14 | // ohne AD-Wert Verlust
|
15 | //while (ADCSRA & _BV(ADSC) ){}
|
16 | //int adc_wert = ADC;
|
17 | |
18 | // Verlust einer AD-Wertes möglich
|
19 | if (bit_is_set(ADCSRA,ADIF)){ |
20 | int adc_wert = ADC; // wert auslesen |
21 | |
22 | ADCSRA |=(1<<ADIF); // Interrupt Flag mit 1 löschen |
23 | return adc-wert; // gebe (10)16-bit Wert zurück? |
24 | }
|
25 | }
|
26 | |
27 | // Kanal von 0 bis 7 , 14 , 15
|
28 | void setMux(byte kanal){ |
29 | ADMUX = (ADMUX & 0b11110000); // ich will die Referenz Spannung so lassen |
30 | ADMUX |= kanal; // Kanal einstellen |
31 | }
|
Fehler (... lass ich ADIE auf null ...)
1 | void adc_init(){ |
2 | // wähle AREF, intern 2.54V
|
3 | ADMUX |= (1 << REFS1) | (1 << REFS0); |
4 | // Enable ADC, AD-Wandlung starten, FreeRunning,AD-Interrupt Enable , Divisor 128 -> 125kHz
|
5 | ADCSRA |= ( 1<<ADEN )|( 1<<ADSC ) | (1<<ADFR) |( 1<<ADPS0 )| (1<<ADPS1) |( 1<<ADPS2 ); |
6 | }
|
7 | |
8 | uint16_t getADC(void){ |
9 | // ohne AD-Wert Verlust (max. 13 Takte warten)
|
10 | //while (ADCSRA & _BV(ADSC) ){}
|
11 | //int adc_wert = ADC;
|
12 | |
13 | // Verlust eines AD-Wertes ist möglich
|
14 | if (bit_is_set(ADCSRA,ADIF)){ |
15 | int adc_wert = ADC; // wert auslesen |
16 | |
17 | ADCSRA |=(1<<ADIF); // Interrupt Flag mit 1 löschen |
18 | return adc-wert; // gebe (10)16-bit Wert zurück? |
19 | }
|
20 | }
|
21 | |
22 | // Kanal von 0 bis 7 , 14 , 15
|
23 | void setMux(byte kanal){ |
24 | //AD-Kanal wechsel ermöglichen
|
25 | ADCSRA &= ~((1 << ADFR)|(1<<ADEN)); |
26 | ADCSRA &= ~(1 << ADSC); //AD-Wandlung stoppen |
27 | |
28 | ADMUX = (ADMUX & 0b11110000); // ich will die Referenz Spannung so lassen |
29 | ADMUX |= kanal; // Kanal einstellen |
30 | |
31 | ADCSRA |= (1 << ADSC); //AD-Wandlung starten |
32 | }
|
Hab ich irgend was vergessen? Was macht man sonst noch normalerweise so bei einer AD-Wandlung?
>>Hab ich irgend was vergessen?
1) ja, du kannst ADSC nämlich nicht löschen, das passiert aber
automatisch, wenn der ADC abgescaltet wird
2) ja, setMux() schaltet den ADC nicht wieder ein
3) ja, Was macht deine get_ADC Funktion, wenn der ADC noch nicht fertig
ist? (du hast keine Warteschleife oder einen brauchbaren
Rückgabe-'Code').
Das ganze könnte man z.B. mit dem ADC-complete interrupt lösen:
- eine globale Var. enthält den nächsten Kanal.
- eine Weitere enthält Flags:
- eines für 'fertig'
- ... (fällt mir grad nichts weiter ein ;) )
- der ADC wird einmal gestartet (als "single conversion"!)
- die ISR
- liest das Ergebnis aus (in die Globale Var.)
- stellt den neuen Kanal ein
- startet die nächste Wandlung
- get_adc_val:
- fragt das flag ab, wenn das gesetzt ist:
- setzt den nächsten kanal
- gibt das Ergebnis zurück
hth. Jörg
@ Jörg X. Ich muss mir das noch mal genau aufzeichnen und mal deine Lösung umsetzen. Erstmal die beiden angesprochenen Methoden:
1 | // globale Variablen
|
2 | adc_wert = 0; // mit null initialisiert |
3 | |
4 | void setMux(byte kanal){ |
5 | //AD-Kanal wechsel ermöglichen
|
6 | ADCSRA &= ~((1 << ADFR)|(1<<ADEN)); |
7 | ADCSRA &= ~(1 << ADSC); //AD-Wandlung stoppen |
8 | |
9 | ADMUX = (ADMUX & 0b11110000); // ich will die Referenz Spannung so lassen |
10 | ADMUX |= kanal; // Kanal einstellen |
11 | |
12 | ADCSRA |= (1 << ADFR) | (1<<ADEN); // FreeRunning Mode und ADC Enabled |
13 | ADCSRA |= (1 << ADSC); //AD-Wandlung starten |
14 | get_ADC(); // eine Wandlung verwerfen |
15 | }
|
16 | |
17 | uint16_t getADC(void){ |
18 | // Verlust eines AD-Wertes ist möglich
|
19 | if (bit_is_set(ADCSRA,ADIF)){ |
20 | adc_wert = ADC; // wert auslesen |
21 | ADCSRA |=(1<<ADIF); // Interrupt Flag mit 1 löschen |
22 | return adc-wert; // gibt neuen (8 bis 10)16-bit Wert zurück |
23 | }
|
24 | return adc_wert; // gibt den alten Wert zurück |
25 | }
|
26 | |
27 | uint16_t get_ADC(void){ |
28 | // ohne AD-Wert Verlust (max. 13 Takte warten)
|
29 | while ( !bit_is_set(ADCSRA,ADIF) ){} |
30 | adc_wert = ADC; // auslesen |
31 | ADCSRA |=(1<<ADIF); // Interrupt Flag mit 1 löschen |
32 | return adc-wert; // gebe (8 bis 10)16-bit Wert zurück |
33 | }
|
Wird mir der Compiler das get_ADC() weg optimieren (Optimization flag -0s), weil ich damit nichts mache?
>> Wird mir der Compiler das get_ADC() weg optimieren [...]
ein kurzer Test sagt: Nein (Test im Anhang)
- ist aber auch nicht so tragisch: Wandlung starten und dann abwarten
geht kaum schneller (Eine Wandlung dauert min. 13,5 * 128(-> Prescaler)
Takte ), da kommt es doch auf 6-10 unnötige (ASM-)Anweisungen (Aufruf,
Einlesen etc.) nicht wirklich an, oder?
hth. Jörg
Hab mir jetzt die neue AVR Studio Version installiert und festgestellt dass die Speicher- Nutzung/Belegung angezeigt wird. Ist das erst seit 4.13 Build 528 so ? AVR Memory Usage ---------------- Device: atmega8 Program: 444 bytes (5.4% Full) (.text + .data + .bootloader) Data: 5 bytes (0.5% Full) (.data + .bss + .noinit) Build succeeded with 0 Warnings... Also morgen kann ich jedenfalls testen :-)
So, hab es jetzt alles aufgebaut. 1.) Läuft aber noch nicht :-( Über "PORTD" lass ich mir den ADC-Wert ausgeben (mit Leuchtdioden) An ADC7 hab ich mein Poti. Ich kann drehen wie ich will, das Signal scheint für mein Programm immer High, 100%, 5V zu sein. An ADC7 liegen jetzt 0.63V an (mit Multimeter gemessen) und während der Simulation in AVR Studio war auch Kanal 7 ausgewählt. 2.) (merkwürdiges Problem) ADSC wird bei der Simulation immer gelöscht und zwar in get_ADC_8() nachdem ich ADIF lösche. Als Fehlerumgehung hab ich ADSC einfach in get_ADC_8() nochmal gesetzt, eigentlich läuft mein AD-Wandler doch im FreeRunning Modus ... da müsste ich dieses bit eigentlich nicht immer wieder neu setzen.
1 | main:
|
2 | -----
|
3 | |
4 | main(){ |
5 | uint8_t adc_wert; |
6 | |
7 | set_ADC_8_Kanal(7); // Teste ADC auf Kanal 7 (Pin 22) |
8 | init_ADC(); |
9 | |
10 | // Port C
|
11 | // 7 = intern | 6=reset | 5,4 = 8bit | 3,2,1,0 = 10bit
|
12 | DDRC = 0b00000000; // Port C für analoge Eingänge |
13 | PORTC= 0b00000000; // (ohne Pull-Up's da ADC aktiviert) |
14 | |
15 | // ADC6+ADC7 (TQFP Pin19+22)
|
16 | // nutze ADC6 und ADC7
|
17 | |
18 | // Port D
|
19 | // 7,6,5,4,3,2 = ?????? | 1 = TXD, 0 = RXD (U(S)ART)
|
20 | DDRD = 0b11111111; // alles Ausgänge |
21 | |
22 | for (;;) |
23 | {
|
24 | adc_wert = get_ADC_8(); |
25 | ad = (adc_wert /32 ); // unsigned Int |
26 | |
27 | switch(ad){ |
28 | case 0: PORTD = 0b00000001; break; |
29 | case 1: PORTD = 0b00000010; break; |
30 | case 2: PORTD = 0b00000100; break; |
31 | case 3: PORTD = 0b00001000; break; |
32 | case 4: PORTD = 0b00010000; break; |
33 | case 5: PORTD = 0b00100000; break; |
34 | case 6: PORTD = 0b01000000; break; |
35 | case 7: PORTD = 0b10000000; break; |
36 | }
|
37 | }
|
38 | }
|
39 | |
40 | |
41 | adc.c |
42 | -----
|
43 | |
44 | volatile uint8_t adc_wert_8 = 0; // mit null initialisiert |
45 | |
46 | void init_ADC(){ |
47 | // wähle AREF, intern 2.54V
|
48 | ADMUX |= (1 << REFS1) | (1 << REFS0); |
49 | // Enable ADC, AD-Wandlung starten, FreeRunning,AD-Interrupt Enable , Divisor 128 -> 125kHz
|
50 | ADCSRA |= ( 1<<ADEN )|( 1<<ADSC ) | (1<<ADFR) | (1<<ADPS0 ) | (1<<ADPS1) |( 1<<ADPS2 ); |
51 | }
|
52 | |
53 | uint8_t get_ADC_8(void){ |
54 | |
55 | // Verlust eines AD-Wertes ist möglich
|
56 | if ( CHECKBIT(ADCSRA,ADIF) ){ |
57 | adc_wert_8 = ADCL; // LOW - wert auslesen |
58 | |
59 | ADCSRA |=(1<<ADIF); // Interrupt Flag mit 1 löschen |
60 | |
61 | // ADCSRA |= (1 << ADIF); // Interrupt Flag mit 1 löschen
|
62 | |
63 | // Zur behebung des "ADSC Fehlers
|
64 | // ADSC wird nach einer weile immer gelöscht :-( "
|
65 | ADCSRA |= (1<<ADEN) | (1 << ADSC); |
66 | |
67 | return adc_wert_8; // gibt neuen (8 bis 10)16-bit Wert zurück |
68 | }
|
69 | |
70 | return adc_wert_8; // gibt den alten Wert zurück |
71 | }
|
72 | |
73 | // Kanal von 0 bis 7
|
74 | void set_ADC_8_Kanal(uint8_t kanal){ |
75 | //AD-Kanal wechsel ermöglichen
|
76 | ADCSRA &= ~(1 << ADFR); // stop free runing |
77 | ADCSRA &= ~(1 << ADEN); // ADC disable |
78 | ADCSRA &= ~(1 << ADSC); //AD-Wandlung stoppen |
79 | |
80 | ADMUX = (ADMUX & 0b11110000); // ich will die Referenz Spannung so lassen |
81 | ADMUX |= kanal; // Kanal einstellen |
82 | |
83 | ADCSRA |= (1 << ADFR) | (1<<ADEN); // FreeRunning Mode und ADC Enabled |
84 | ADCSRA |= (1 << ADSC); //AD-Wandlung starten |
85 | get_ADC_8(); // eine AD-Wandlung verwerfen |
86 | }
|
In der Simulation beim AVR Studio geht das alles ... weiß echt nicht weiter ... heul
Es muss grundsätzlich immer das ADCH ausgelesen werden! Steht auch im Datenblatt. Abgesehen davon, dass es sinnfrei ist, nur das Low-Byte auszuwerten, wird das Ergebnisregister erst wieder freigegeben, wenn ADCH gelesen wurde. Wird nur ADCL gelesen, dann kann der ADC keinen neuen Wert im Ergebnisregister ablegen. Ist klar, dass Du immer den selben Wert ausliest. Wenn Du nur 8 Bit Auflösung brauchst, dann setze ADLAR und lies nur das High-Byte aus. Dann gibts auch keine Probleme. Deine switch-case Abfrage macht m.E. auch nicht viel Sinn. Was soll die Division durch 32 und das Abfragen auf 0...7 ? Dass das ADSC gelöscht wird, hängt möglicherweise damit zusammen, dass der ADC im Simulator nur teilweise unterstützt wird. In Hardware sollte das nicht passieren, wenn der Free Running Mode korrekt aktiviert wurde.
@ johnny-m Danke, hab mir das Datenblatt durchgelesen und viel Zeit dafür aufgewendet. War aber wohl noch nicht genug. Dann setze ich ADLAR ... hab mich schon gefragt wozu dieses bit gut ist. Zu der Division durch 32, das ist quasi ein bit-shift nach links. z.B.: 10110011 >> 5 = 00000101 --- --- Das sind dann Zahlen von 0 bis 7 (3 Bit) dann brauch ich keine 8 if(zahl<x && zahl>y) Abfragen ... Ich will nur eine LED an PORTD leuchten lassen, je nach Wert. Bei 0 soll PinD0 high sein und bei 255 soll PinD7 high sein.
>Dann setze ich ADLAR ...
Auch dann mußt Du ADCH einlesen. ADCL muß dann nicht gelesen werden.
Probier's wirklich mal mit dem Interrupt, statt Free-Runinng - vereinfacht, z.B. den Kanalwechsel. hth. Jörg
Chris J. wrote: > Zu der Division durch 32, das ist quasi ein bit-shift nach links. > > z.B.: 10110011 >> 5 = 00000101 > --- --- > Das sind dann Zahlen von 0 bis 7 (3 Bit) > dann brauch ich keine 8 if(zahl<x && zahl>y) Abfragen ... Jau, ist mir mittlerweile auch aufgegangen... Hatte es nur überflogen. Aber: Wie Travel Rec. anmerkte: ADCH muss immer gelesen werden, unabhängig von irgendwelchen anderen Einstellungen.
@ johnny-m Hab's jetzt so gemacht und hat funktioniert. > Dass das ADSC gelöscht wird Das hat in der Simulation nicht funktionier und der Mega8 hat sich wie die Simulation verhalten und ADSC intern auch gelöscht. Also wird ADSC bei fehlerhaften auslesen der ADCL/H Register gelöscht. @ Travel Rec. Ich weiß jetzt wie es richtig geht :-) @ Jörg X. Genau das werde ich jetzt machen. Muss dazu nur noch meine Testplatine umlöten und zusätzlich 3 Taster anlöten. Ich hab jetzt aber noch etwas festgestellt, wenn ich den Poti drehe und die Leuchtdioden beobachte hab ich nicht das Verhalten welches ich mir vorgestellt habe. z.B.: Die LED an Pin3 ist an, jetzt dreh ich weiter und die LED an Pin3 wird sozusagen gedimmt und die LED an Pin4 wird heller. Wie kann das sein, ich dachte das ist eine rein logische Angelegenheit. Kann es sein dass die Versorgungsspannung/Referenzspannung etwas schwankt und zeitweise der Pin3 und der Pin4 high sind? Sozusagen Pulsweitenmoduliert?
Hab's einfach mal getestet, es sind bei mir 2.05kHz die an den beiden Pin's dann anliegen. (Also sie schalten voll durch!) Meine Abblockkondensatoren: VCC - 330µF (Versorgung von USB(5V, GND) ) AVCC - 330µF + 470nF(mit Spule zu VCC und kleine SMD-Spule nach GND) ... ob da was schwingt?
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.