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.
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.
@ 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
voidInit_ADC(void){
2
3
ADMUX=(1<<REFS0)|(1<<ADLAR);// ref=VCC, left adujst
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.
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.
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_tget_adc(uint8_tchannel){
2
ADMUX=(ADMUX&~0x1F)|(channel&0x1F);
3
ADCSRA|=(1<<ADSC);
4
while(ADCSRA&(1<<ADSC));
5
returnADCH;
6
}
1
voidset_adcchannel(uint8_tchannel){
2
ADMUX=(ADMUX&~0x1F)|(channel&0x1F);
3
}
4
5
uint8_tread_adcvalue(void){
6
ADCSRA|=(1<<ADSC);
7
while(ADCSRA&(1<<ADSC));
8
returnADCH;
9
}
Das könnte deiner Vorstellung evtl. entsprechen und stellt immer noch
eine saubere funktionale Programmierung dar.
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.
@ 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.