Hallo, ich hoffe mir kann jemand einen Tipp geben was ich falsch gemacht
habe. Ich will einen NTC mit einem Atmega32 auswerten. Am Pin PA0 liegen
momentan ca. 2.3V.
Mein Programm sieht etwa so aus: (Vereinfachte Version, damit sich
keiner durch meinen gesamten Code wühlen muss.)
void Init_ADC1(void)
{
ADMUX |= (1<<REFS0); //Referenzspannung
ADMUX |= (1<<ADLAR); //linksbündig Schreiben (8bit mode)
}
int main(void)
{
DDRA = 0b00000000;
Init_ADC1();
sei();
while(1)
{ ADCSRA |= ((1<<ADSC)|(1<<ADIF)); }
ISR (ADC_vect)
{
analog1 = ADCH;
}
Eigentlich müsste doch so eine Messung gestartet werden und sobald sie
beendet ist wird der Interrupt ausgelöst und das Register an die
Variable übergeben werden???? Der Wert der Variablen verändert sich aber
nicht.
Damit der Interrupt für den AD Wandler aktiv wird, musst Du noch zusätzlich das ADIE Flag im ADCSRA aktivieren. Deine Endlosschleife startet ständig neue Konvertierungen ohne auf das Ende zu warten. Das ständige Setzen von ADIF könnte den Interrupt auch ausschalten. Also ADIF und ADSC erst wieder setzen, wenn die Konvertierung abgeschlossen ist.
Und noch ein Hinweis: Die Variable analog1 muss unbedingt als volatile deklariert sein. Sonst könnten Aktualisierungen verloren gehen.
Ach ja, hatte ADIF und ADIE verwechselt. Danke. Im eigentlichen Programm wird die Messung nur ca. alle 4sec ausgelöst. Mit dem Thema Messgenauigkeit usw. beschäftige ich mich erst wenn ich weiß dass es so schon mal "funktioniert". Aber das tut es leider immer noch nicht.
Hallo, ADC Enable fehlt und einiges mehr. Mit welchen ADC-Takt läuft der ADC ? Wird über die Bits ADPS2:0 eingestelt. Welcher Datentyp hat analog1 ? /ADC Control and Status Register A – ADCSRA/ ADEN ADSC ADATE ADIF ADIE ADPS2 ADPS1 ADPS0 ADCSRA.ADEN = 1 ADCSRA.ADPS2:0 = xyz -- mit 50kHz <= ADC-Takt <= 200kHz ADCSRA = ADCSRA -- clear pendig adc interrupt ADCSRA.ADIE = 1 -- enable adc interrupt /Special FunctionIO Register – SFIOR/ ADTS2 ADTS1 ADTS0 – ACME PUD PSR2 PSR10 SFIOR.ADTS2:0 = 0b000 -- Free Running mode /ADC Multiplexer Selection Register – ADMUX/ REFS1 REFS0 ADLAR MUX4 MUX3 MUX2 MUX1 MUX0 ADMUX.MUX4:0 = 0b00000 -- ADC channel 'ADC0' ADMUX.REFS0 = 1 -- AVCC with external capacitor at AREF pin ADMUX.ADLAR = 1 -- left adjust ADCSRA.ADSC = 1 -- ADC Start Conversion
@ HansDampf (Gast) >Mein Programm sieht etwa so aus: (Vereinfachte Version, damit sich >keiner durch meinen gesamten Code wühlen muss.) NÖ! https://www.mikrocontroller.net/articles/Netiquette#.C3.84u.C3.9Fere_Form "Quelltext nie abtippen, sonder immer das direkt kopierte Original posten, anderenfalls schleichen sich neue Fehler ein oder die existierenden werden nicht abgeschrieben." >Eigentlich müsste doch so eine Messung gestartet werden und sobald sie >beendet ist wird der Interrupt ausgelöst und das Register an die >Variable übergeben werden???? Der Wert der Variablen verändert sich aber >nicht. Fang mal deutlich kleiner an, ohne Interrupts. EInfach auf das Ende der Wandlung per Schleife warten. Außerdem stimmt deine Initialisierung nicht.
1 | void Init_ADC(void) { |
2 | |
3 | ADMUX = (1<<REFS0) | (1<<ADLAR); // ref=VCC, left adujst |
4 | ADSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // ADC einschalten, prescaler 128 |
5 | }
|
6 | |
7 | uint8_t get_adc(uint8_t channel) { |
8 | |
9 | ADMUX = (ADMUX & ~0x1F) | (channel & 0x1F); |
10 | ADCSRA |= (1<<ADSC); |
11 | while (ADCSRA & (1<<ADSC)); |
12 | return ADCH; |
13 | }
|
14 | |
15 | int main(void) { |
16 | uint8_t x; |
17 | |
18 | DDRA = 0b00000000; |
19 | Init_ADC(); |
20 | |
21 | sei(); |
22 | |
23 | while(1) { |
24 | x = get_adc(0); |
25 | }
|
26 | }
|
Karl M. schrieb: > Da kann man sich nur Frage, liest denn keiner mehr das Datenblatt ? Ich habe mich lange mit dem Datenblatt beschäftigt, natürlich. Aber anscheinend habe ich wohl ein paar Sachen übersehen was bei einem Datenblatt mit 346 Seiten schon mal passieren kann, wie ich finde.
@ HansDampf (Gast) >anscheinend habe ich wohl ein paar Sachen übersehen was bei einem >Datenblatt mit 346 Seiten schon mal passieren kann, wie ich finde. Das Kapitel über die ADC-Register ist DEUTLICH kürzer. Eagl.
Temperaturmessungen macht man sinnvollerweise nur im Abstand von mehreren Sekunden. Wenn andere Aufgaben des µC wirklich zeitkritisch sind, sollten DIESE mit Interrupts erledigt werden! Alle paar Sekunden mal im Hauptprogramm eine Warteschleife für den Temperatur-ADC kostet effektiv "keine" Zeit und stört die wirklich zeitkritischen Reaktionen des µC nicht.
Ok vielen Dank. Werde eure Vorschläge jetzt mal in Ruhe durcharbeiten. Da bin ich erst mal wieder ein paar Stunden beschäftigt.
Also Initialisierung des ADC habe ich jetzt verstanden (mit den vier Seiten aus dem Datenblatt -- ihr habt ja recht, so viel ist es gar nicht) Ich versuche jetzt nur noch diesen Teil deines Codes zu verstehen: korrigiere mich wenn ich falsch liege. Falk B. schrieb: > uint8_t get_adc(uint8_t channel) { > > ADMUX = (ADMUX & ~0x1F) | (channel & 0x1F); > ADCSRA |= (1<<ADSC); > while (ADCSRA & (1<<ADSC)); > return ADCH; > } Messung wird gestartet, es wird gewartet bis die Messung fertig ist und der Inhalt des ADCH-Registers wird an get_adc übergeben.?? Aber was hat es mit der ADMUX-Zeile und diesem channel auf sich? Ich verstehs leider nicht.
@ HansDampf (Gast) >Messung wird gestartet, es wird gewartet bis die Messung fertig ist und >der Inhalt des ADCH-Registers wird an get_adc übergeben.?? Richtig. >Aber was hat es mit der ADMUX-Zeile und diesem channel auf sich? Ich >verstehs leider nicht. Das ist Bitmanipulation. Nicht zu verwechseln mit Bitcoinmanipulation ;-) ADMUX = (ADMUX & ~0x1F) | (channel & 0x1F); (ADMUX & ~0x1F) ADMUX mit dem invertierten (~) 0x1F UND verknüpfen, auf gut Deutsch, lösche die unteren 5 Bits MUX0-4. 0x1F = 0b00011111 ~0x1F = 0b11100000 (channel & 0x1F); channel mit 0x1F UND verknüpfen, auf gut Deutsch, lösche alles außer den untersten 5 Bits. Damit ein Aufruf der Funktion mit einem channel >31 keinen Unsinn macht. Teilergebnisse ODER verknüpfen. Die ganze Aktion bewirkt praktisch, daß channel auf 0-31 begrenzt wird und diese 5 Bits in die untersten 5 Bits von ADMUX kopiert werden.
Jakob schrieb: > Temperaturmessungen macht man sinnvollerweise nur im Abstand von > mehreren Sekunden. Das kommt wohl ein bisschen drauf an, was sich an dem Temperatursensor so tut und wie groß dessen thermische Trägheit ist. Bei ein Pt-Drahtsensor z.B. kann es durchaus lohnend sein, im Abstand von 10ms zu messen.
Falk B. schrieb: > Die ganze Aktion bewirkt praktisch, daß channel auf 0-31 begrenzt wird > und diese 5 Bits in die untersten 5 Bits von ADMUX kopiert werden. Aber mit MUX0-4 lege ich doch nur fest an welchem PIN mein analog-Signal hängt. Wozu brauche ich da so eine Begrenzung und wenn ich alle Bits in ADMUX lösche läuft ja gar nichts mehr. Da kann ich jetzt überhaupt nicht mehr folgen.
@HansDampf (Gast)
>Da kann ich jetzt überhaupt nicht mehr folgen.
Dann lies es noch einmal in Ruhe und denk drüber nach.
Falk B. schrieb: > Dann lies es noch einmal in Ruhe und denk drüber nach. Mach ich immer und immer wieder, nebenbei noch Datenblatt lesen um das irgendwie zu verstehen. Bevor die Messung gestartet wird, werden also MUX0..4 gelöscht und durch Channel neu gesetzt. Meine Methode wäre gewesen MUX0..4 bereits beim Initialisieren zu setzten, je nach dem welchen PIN ich verwende aber dann könnte ich die anderen ja nicht mehr benutzen. Ich hoffe das ist soweit richtig. Aber um "Channel" zu verwenden müsste ich doch erstmal definieren welchen PIN ich für die nächste Messung verwenden möchte?? Bitte noch ein kleiner TIP, im Tutorial ist es leider auch nicht so erklärt das es für mich Sinn ergibt. Und ich will es ja verstehen. Bin kein Fan von copy and paste.
Hallo, vielleicht mal so, Falk Brunner hat dir eine Basis Funktion für fast alle ADC-Wendungen geliefert. *Table 84. Input Channel and Gain Selections* Dabei entspricht ADC0 = 0b00000 = 0 ADC1 = 0b00001 = 1 usw. Interessanter weise stimmen die Zahlen mit der ADC-Kanalnummer im "Single Ended Input" Modus zusammen. Also ADC7 = 0b00111 = 7 ! Man könnte auch die eine Funktion uint8_t get_adc(uint8_t); in zwei Funktionen aufspalten.
1 | uint8_t get_adc(uint8_t channel) { |
2 | ADMUX = (ADMUX & ~0x1F) | (channel & 0x1F); |
3 | ADCSRA |= (1<<ADSC); |
4 | while (ADCSRA & (1<<ADSC)); |
5 | return ADCH; |
6 | }
|
1 | void set_adcchannel(uint8_t channel) { |
2 | ADMUX = (ADMUX & ~0x1F) | (channel & 0x1F); |
3 | }
|
4 | |
5 | uint8_t read_adcvalue(void) { |
6 | ADCSRA |= (1<<ADSC); |
7 | while (ADCSRA & (1<<ADSC)); |
8 | return ADCH; |
9 | }
|
Das könnte deiner Vorstellung evtl. entsprechen und stellt immer noch eine saubere funktionale Programmierung dar.
Nachtrag, zu den Pinzuordnungen, siehe *Figure 1. Pinout ATmega32* PA0 (ADC0) PA1 (ADC1) PA2 (ADC2) PA3 (ADC3) PA4 (ADC4) PA5 (ADC5) PA6 (ADC6) PA7 (ADC7)
Karl M. schrieb: > void set_adcchannel(uint8_t channel) { > ADMUX = (ADMUX & ~0x1F) | (channel & 0x1F); > } Heißt das dann wenn ich z.B. ADC1 verwende, müsste ich schreiben: > void set_adcchannel(uint8_t channel = 1) { > ADMUX = (ADMUX & ~0x1F) | (channel & 0x1F); > }
HansDampf schrieb: > Karl M. schrieb: >> void set_adcchannel(uint8_t channel) { >> ADMUX = (ADMUX & ~0x1F) | (channel & 0x1F); >> } > > Heißt das dann wenn ich z.B. ADC1 verwende, müsste ich schreiben: > >> void set_adcchannel(uint8_t channel = 1) { >> ADMUX = (ADMUX & ~0x1F) | (channel & 0x1F); >> } Nein. Du solltest lernen, was eine Funktion in C ist, insbesondere was Funktionsparameter sind. Falls dir das aus irgendwelchen Gründen nicht behagt (z.B. weil das C-Buch zu viele Seiten für deinen Geschmack hat) dann schlage ich vor, du läßt das mit dem Program- mieren ganz und wählst ein etwas anspruchsloseres Hobby. Vielleicht etwas mit Blumen. Oder Briefmarken.
@ Axel Schwenke (a-za-z0-9)
>Vielleicht etwas mit Blumen. Oder Briefmarken.
;-)))))
@ HansDampf (Gast) >Bevor die Messung gestartet wird, werden also MUX0..4 gelöscht und durch >Channel neu gesetzt. Stimmt. Ist das nicht toll? > Meine Methode wäre gewesen MUX0..4 bereits beim >Initialisieren zu setzten, je nach dem welchen PIN ich verwende aber >dann könnte ich die anderen ja nicht mehr benutzen. Eben. >Ich hoffe das ist soweit richtig. Ja. >Aber um "Channel" zu verwenden müsste ich doch erstmal definieren >welchen PIN ich für die nächste Messung verwenden möchte?? Ja, beim Aufruf der Funktion. Was glaubst du wohl, was diese Zeile hier macht?
1 | x = get_adc(0); |
>Bitte noch ein kleiner TIP, im Tutorial ist es leider auch nicht so >erklärt das es für mich Sinn ergibt. Hmm. Dann ist entweder das Tutorial schlecht oder dein Verständnis. > Und ich will es ja verstehen. Schon mal eine löbliche Einstellung.
Franz Schlüter schrieb: > Und noch ein Hinweis: Die Variable analog1 muss unbedingt als volatile > deklariert sein. Sonst könnten Aktualisierungen verloren gehen. Falsche Begründung für das richtige Ziel. Ohne volatile gehen keine Aktualisierungen verloren sondern, je nach Compiler-Einstellung, kann es passieren, dass der Compiler die Variable weg optimiert da er nicht sieht dass sie woanders auch noch gebraucht wird. Mit volatile sagt man dem Compiler also: "Diese Variable nicht optimieren sondern so benutzen wie es im Quellcode steht." ;)
Hallo, ich habe mit meinem ersten Beitrag auch noch nicht erkannt, dass HansDampf noch in den Anfängen steckt. #msmiamouz sagt dazu "nullpe" das hier nicht als Beleidigung zitiert wird, nur als neu deutsches Wort "Anfänger". So muss man als Anfänger viel lesen, probieren und lernen. Es kann schon mal 2 Jahre dauern, bis man ein wenig "Durchblick" erreicht hat. In der Zeit ohne Internet, waren die Lernphasen noch viel Länger, da die Quellen und die Hilfestellungen nicht so einfach verfügbar waren. Also HansDampf, sei ehrlich mit Dir und bei deinen Beiträgen hier im Forum. Dann kann sich jeder auf deine Lernkurve einstellen, der es will.
Karl M. schrieb: > Also HansDampf, sei ehrlich mit Dir und bei deinen Beiträgen hier im > Forum. Dann kann sich jeder auf deine Lernkurve einstellen, der es will Natürlich bin ich ein Neuling auf dem Gebiet, ich habe ja auch nie was anderes behauptet. Vor drei Wochen hatte ich zum ersten Mal einen Mikrocontroller in der Hand. Und meine C-Kenntnisse waren bis dahin auch nur Grundlagen. Es ist auch nicht mein Plan Programmierung zu meinem Hobby zu machen. Freizeitgestaltung findet bei mir eher an der frisch Luft statt. Ich mach halt ein kleines Projekt um meinen Horizont zu erweitern und wer sich lieber mit Gleichgesinnten Profis unterhalten will braucht ja meine teilweise "naiven" Fragen nicht zu beantworten. Allen die meine Fragen ernst genommen haben und ihre wertvolle Zeit geopfert haben sag ich vielen Dank.
Falk B. schrieb: > Ja, beim Aufruf der Funktion. Was glaubst du wohl, was diese Zeile hier > macht? > x = get_adc(0); Jetzt hab ich es wenigstens kapiert: Die (0) ist ja mein Pin0 und wird als Funktionsparameter an die Funktion get_adc übergeben. Und Channel entspricht dann 0x00; je nach dem welchen Pin man auswerten will. Das Ergebnis wird dann an x "zurückgeschickt". Ich hatte nur keine Ahnung von Funktionsparametern, tja jetzt weiß ich es und kann weiter basteln. Danke noch mal. Das ist halt meine Methode: learning by doing :-)
HansDampf schrieb: > Die (0) ist ja mein Pin0 Sorry, falsch. Ich hatte Dir doch die Zuordnungen hingeschrieben ! 0 entspricht ADC0 und dieser Eingang liegt an welchem Pin ?
Karl M. schrieb: > 0 entspricht ADC0 und dieser Eingang liegt an welchem Pin ? Das sollte aber leicht im Datenblatt zu finden sein: ganz am Anfang unter "Pin Configurations". Die Pin-Nr. hängt dann noch von dem verwendeten Gehäuse ab.
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.