Guten Abend,
ich versuche mich gerade an ADC mti dem Atmega8, es will mir aber nicht
ganzgelingen.
Ich habe versucht mich an
Beitrag "ATmega8 ADC-Durchschnittsberechnung" zu orientieren.
Meine Schaltung ist einfach
+3,3V (da über Raspberry versorgt)
_ 3,3V PB0 & AREF
|
|
330Ohm
|
|
|-- PC1(ADC1)
|
|
PT100
|
|
_ GND
Die Genauigkeit der Messschaltung ist mir aktuell noch egal, ADC an sich
ist interessant
mein Code:
while(ADCSRA&(1<<ADSC))// auf Abschluss der Konvertierung warten
22
{}
23
(void)ADCW;
24
}
25
26
uint16_tADC_Read(uint8_tchannel)
27
{
28
ADCSRA|=(1<<ADSC);// eine Wandlung "single conversion"
29
while(ADCSRA&(1<<ADSC)){// auf Abschluss der Konvertierung warten
30
}
31
returnADCW;// ADC auslesen und zurückgeben
32
}
33
34
intmain(void)
35
{
36
37
uint16_ta;
38
charstr[16];
39
40
DDRB|=(1<<PB0);
41
init_ADC(0x01);
42
while(1)
43
{
44
init();//LCD initialisieren
45
46
PORTB|=(1<<PB0);
47
48
a=ADC_Read(0x01);
49
itoa(a,str,10);
50
message(str);
51
message(" ");
52
a=ADC_Read(0x01);
53
itoa(a,str,10);
54
message(str);
55
message(" ");
56
a=ADC_Read(0x01);
57
itoa(a,str,10);
58
message(str);
59
message(" ");
60
cmd(0xC0,0);
61
message("->");
62
itoa(ADMUX,str,10);
63
message(str);
64
65
PORTB&=~(1<<PB0);
66
67
_delay_ms(5000);//warte damit Display nicht so flackert
68
}
69
}
Die Ausgabe beinhaltet drei zahlen in der ersten und eine in der zweiten
Zeile des LCD-Displays.
Die Fragen die ich hätte:
1. Die erste Zahl schwankt zwischen 180/240 und 270. 180 nur am Anfang,
danach halt 240. Das wundert mich nicht wirklich, ich habe nicht einen
Kondensator verbaut und rien gar nichts gegen eine Eigenerwärmung des
PT100 getan. Der Effekt stört mich auch noch nicht. Aber Zahl 2 und 3
zeigen dauerhaft 1023 an. Warum ist das so? Die Read-Funktion ist ja die
gleiche.
2. in der zweiten Display-Zeile will ich mir das Register ADMUX ansehen.
Ich würde also eine 1 erwarten, da das LSB des Registers auf 1 gesetzt
wurde. Das Display zeigt aber 0. Spreche ich das Register falsch an?
Funktioniert eine "gesamtheitliche Ausgabe" überhaupt oder muss ich
jedes Bit einzeln lesen wenn ich das sehen will?
Liebe Grüße
Matthias
Walter S. schrieb:> Matthias schrieb:>> ADMUX &= ~(1<<REFS1) & ~(1<<REFS0) & (1<<MUX0);>> die Zeile solltest du mal überdenken
ja nach kurzem Nachdenken seh ich ein, dass ich da Handlungsbedarf habe.
Aber: das LSB ist bei allen drei Thermen 1
Und während ich schreibe merke ich, durch das &= ist es egal was ich an
Thermen anfüge, wenn ADMUX vorher 0 ist, dann ist es danach auch 0.
Richtig?
Matthias schrieb:> ADMUX &= ~(1<<REFS1) & ~(1<<REFS0) & (1<<MUX0);
Ich schätz mal du wolltest
1
ADMUX&=~((1<<REFS1)|(1<<REFS0)|(1<<MUX0));
schreiben. Was ich mich dabei frage: Warum glaubst du diese Bits löschen
zu müssen? Die sind doch eh schon 0.
Dann fällt mir deine Funktion
1
uint16_tADC_Read(uint8_tchannel)
bei der du den Parameter channel gar nicht verwendest. Warum nicht?
Hast du dir das Tutorial hier auf der Seite zum ADC mal angeschaut? Und
nachvollzogen?
Michael K. schrieb:> Ich schätz mal du wolltestADMUX &= ~((1 << REFS1) | (1 << REFS0) | (1 <<> MUX0));schreiben. Was ich mich dabei frage: Warum glaubst du diese Bits> löschen> zu müssen? Die sind doch eh schon 0.
Ich wollte:
Matthias schrieb:> Michael K. schrieb:>> Ich schätz mal du wolltestADMUX &= ~((1 << REFS1) | (1 << REFS0) | (1 <<>> MUX0));schreiben. Was ich mich dabei frage: Warum glaubst du diese Bits>> löschen>> zu müssen? Die sind doch eh schon 0.>> Ich wollte:
1
ADMUX|=~(1<<REFS1)&~(1<<REFS0)&(1<<MUX0);
Also eigentlich wolltest du nur
1
ADMUX|=(1<<MUX0);
schreiben? Denn eine 0 zu verodern macht ja mal gar keinen Sinn.
Matthias schrieb:> Ich wollte:ADMUX |= ~(1<<REFS1) & ~(1<<REFS0) & (1<<MUX0);
ich rate mal was du wolltest:
ADMUX &= ~(1<<REFS1) & ~(1<<REFS0);
ADMUX |= (1<<MUX0);
damit löscht du die Bits REFS0 und 1
und setzt Bit MUX0
ist aber auch nicht ganz logisch denn was ist mit MUX1 2 3 ?
Walter S. schrieb:> ist aber auch nicht ganz logisch denn was ist mit MUX1 2 3 ?
Sind alle 0. Er ist im Init und Init-Value von ADMUX ist 0 bzw.
0b00000000. Deshalb macht es ja keinen Sinn Bits zu löschen, sind ja eh
alle gelöscht.
EDIT:
Also die Funktion ist ja nur zum Initialisieren. Der Aufruf der Funktion
ist auch Quatsch, der Compiler müsste eigentlich meckern denn der Aufruf
erfolgt so:
1
init_ADC(0x01);
Was soll das 0x01? Die Funktion hat keine Parameter, die man ihr
übergeben könnte. Das sollte zum Fehler führen.
ich vermisse "letzter post löschen" ;)
Was ich wollte ist, dass die Bits die ich definiere auch genau den Wert
haben und alle anderen 0 sind. Da denke ich aber selber nochmal drüber
nach ;)
Michael K. schrieb:> Dann fällt mir deine Funktion> uint16_t ADC_Read( uint8_t channel )bei der du den Parameter channel gar> nicht verwendest. Warum nicht?
Ich hatte in der Funktion erst noch das Umschalten der Kanäle mit drin,
dazu dann auch die Variable "channel". Nachdem es nicht funktioniert hat
habe ich das erstmal entfernt.
Michael K. schrieb:> Hast du dir das Tutorial hier auf der Seite zum ADC mal angeschaut? Und> nachvollzogen?
ja, aber ehr überflogen. Nachdem ich oben erwähnten Beitrag als Vorlage
hatte wollte ich ehr versuchen meine Anpassungen anhand des Datenblattes
vom Atmega8 zu machen.
Nach Anpassung der oben erwähnten Zeile kommt die Ausgabe von
PORTB&=~(1<<PB0);//Stromversorgung für Schaltung aus
55
_delay_ms(5000);//warte 5s
56
}
57
}
Die 0en für mein Verständnis habe ich auch rausgeschmissen.
Mir ist auch aufgefallen, dass ADSC in der init_ADC() zweimal direkt
hintereinander gesetzt wurde. Das war im Beispiel zwar auch so, macht
aber keinen Sinn. Nachdem das gesetzt wurde bleibt es ja eh 1 bis die
Conversion abgeschlossen ist. Korrekt?
Die Frage nach der Ausgabe von Register ADMUX hat sich damit erledigt,
das funktioniert.
Die erste Zeile ist allerdings sehr konstant "0 1023 1023".
Soweit als Update, ich suche mal weiter ;)
Das schaut fast gut aus.
1. Der Max Value von ADC ist 1023, nicht 1024. Er kennt 1024 Zustände
aber 0 ist ja auch ein zustand ;)
2. Du setzt keinen der REFS in ADMUX, damit wird der AREF-Pin bzw die
Spannung an diesem als Referenz benutzt(glaube ich). Hast du den da auch
eine Referenzquelle angeschlossen?
Michael schrieb:> 1. Der Max Value von ADC ist 1023, nicht 1024. Er kennt 1024 Zustände> aber 0 ist ja auch ein zustand ;)
hast du recht, danke für den Hinweis.
Michael schrieb:> 2. Du setzt keinen der REFS in ADMUX, damit wird der AREF-Pin bzw die> Spannung an diesem als Referenz benutzt(glaube ich). Hast du den da auch> eine Referenzquelle angeschlossen?Matthias schrieb:> +3,3V (da über Raspberry versorgt)> _ 3,3V PB0 & AREF> |> |> 330Ohm> |> |> |-- PC1(ADC1)> |> |> PT100> |> |> _ GND
oben bei den 3,3V
Ich würde ja ein Bild von meinem Steckbrett machen aber das bringt aus
meiner Sicht überhaupt keinen Mehrwert! Kraut&Rüben
Ich rätsel weiter, lese mir jetzt doch das ADC-Tutorial nochmal genauer
durch.
Wie gesagt:
- keinerlei Kondensatoren auf dem ganzen Steckbrett. Ich gehe aber davon
aus, dass das nur für die Signalqualität notwendig ist.
Matthias schrieb:> ADMUX |= (1 << MUX0);> ADCSRA |= (1<<ADEN) | (1<<ADSC) | (1<<ADPS2) | (1<<ADPS0);
Fehler 1
Auszug aus dem Datenblatt:
"The ADC is enabled by setting the ADC Enable bit, ADEN in ADCSRA.
Voltage
reference and input channel selections will not go into effect until
ADEN is set."
Die beiden Zeilen tauschen sollte also reichen?
Ich suche weiter ;)
SChmeiss mal das init() aus der Hauptschleife raus. Das hat da nichts
verloren.
Dinge werden am Programmanfang initialisiert und danach lässt du sie in
Ruhe.
Statt dessen siehst du in deinen LCD Funktionen nach, ob du eine
Funktion hast, mit der du den Cursor wieder in die erste Zeile schicken
kannst.
1
....
2
while(1)
3
{
4
init();//LCD initialisiere
5
...
Was soll eigentlich das sein?
1
cmd(0xC0,0);
Ich vermute mal, dass das irgendein Commando fürs LCD ist. Allerdings
bin ich jetzt zu faul rauszusuchen, welches. Aber egal welches, es hat
es sich auf jeden Fall verdient, dass du es zu deinen LCD Funktionen
unter einem sprechenden Namen hinzufügst.
Bau dir doch erst mal einen Satz von Hilfsfunktionen für dein LCD. Das
ist doch Unsinn, wenn du bei jedem neuen Projekt das Rad immer wieder
neu erfinden musst. Du willst Zahlen ausgeben. Nun, das soll vorkommen.
Das soll sogar ziemlich oft vorkommen. Das soll so oft vorkommen, dass
es sich schon verdient hätte, wenn du in deinem Vorrat an LCD Funktionen
eine Funktion eigens nur dafür abstellen würdest. Du hast ja auch eine
Funktion, die einen String ausgeben kann. Warum dann nicht eine für
Zahlen?
1
voidlcd_putu(uint16_tzahl)
2
{
3
chartxt[10];
4
5
utoa(zahl,txt,10);
6
message(txt);
7
}
(und jetzt sieht man auch, dass der Funktionsname 'message' nicht so
glücklich gewählt ist. Denn 'message' eignet sich nicht besonders gut um
damit eine Reihe von Funktionen zu beginnen. Funktionen die Strings,
Character, Zahlen unsigned, Zahlen signed, Zahlen als Hexadezimalzahl,
.... ausgeben. message an sich ist kein schlechter Name. Aber in der
Reihe lcd_putc, lcd_puts, lcd_putu, lcd_puti erkennt man am letzten
Buchstaben sofort, was diese Funktion ausgeben kann. lcd_put.. sagt mir
das diese Funktionenfamilie dafür steht etwas asuzugeben, der letzte
Buchstabe sagt mir, was diese Funktion ausgeben kann. c steht für char,
s für String, u für unsigned, i für int, ....
Oh und by the way. das i in itoa steht für int. Du hast keinen int. Du
hast einen unsigned int. Die Funktion dafür heisst utoa. Selbes Prinzip.
Eine ganze Familie von Funktionen, bei denen 1 Buchstabe Auskunft
darüber gibt, wofür die Funktion gedacht ist.)
Karl H. schrieb:> SChmeiss mal das init() aus der Hauptschleife raus. Das hat da nichts> verloren.> Dinge werden am Programmanfang initialisiert und danach lässt du sie in> Ruhe.
Da hast du Pauschal recht, das Kommando gibt es bestimmt irgendwo, muss
ich aber raussuchen .... das setzte ich mal auf die ToDo-Liste, aber mit
geringerer Prio als die Funktion des ADC.
Karl H. schrieb:> Was soll eigentlich das sein? cmd(0xC0,0);
Zeilenumbruch
Zu dem Rest wo das Zitat jetzt zu lang wäre:
Ja du hast recht ich gebe es zu. Aber auch das schiebe ich mal nach
hinten.
Ach wäre das schön wenn ein Tag 34 Stunden hätte ;)
Matthias schrieb:> Karl H. schrieb:>> SChmeiss mal das init() aus der Hauptschleife raus. Das hat da nichts>> verloren.>> Dinge werden am Programmanfang initialisiert und danach lässt du sie in>> Ruhe.>> Da hast du Pauschal recht, das Kommando gibt es bestimmt irgendwo, muss> ich aber raussuchen .... das setzte ich mal auf die ToDo-Liste, aber mit> geringerer Prio als die Funktion des ADC.
Nein. Nicht mit geringerer Prio. Sondern jetzt.
Denn für die reproduzierte 0 in deiner ersten Zeile gibt es nur eine
Erklärung.
Deine LCD Beschreibere ist Mist.
Dein ADC zählt sicher nicht mit und denkt sich: Ooooch, da liefere ich
bei jedem dritten mal auslesen einfach mal 0 um den Matthias zu ärgern.
Bau dir einen ordentlichen und verlässlich funktionierenden Satz an
Hilfsfunktionen für dein LCD. Du brauchst Funktionen um
* den Cursor an eine bestimmte Position zu schicken
* das LCD löschen zu können
* wenn du willst, kann man auch eine 'Cursor Home' Funktion manchmal
gebrauchen
* Funktionen für alle möglichen Ausgabefälle.
Das alles ist keine Hexerei und keine Raketentechnik. Es gibt genügend
LCD-'Bibliotheken' bei denen man sich abschauen kann, was die so an
Basisvorrat anbieten und wie einfach diese Funktionen zu schreiben sind.
erneut ein Auszug aus dem Datenblatt:
"• Bits 2:0 – ADPS2:0: ADC Prescaler Select Bits
These bits determine the division factor between the XTAL frequency and
the input clock to the
ADC."
Ich habe keinen Quarz dran. Ich gehe davon aus, dass man von der
"build-in"-Frequenz auch von XTAL spricht?
Der eingestellte Divisor (0b101 bzw. 31,25kHz) war auf jeden Fall
außerhalb der empfohlenen 50-200kHz. Ohne Erfolg geändert auf 0b011 bzw
125kHz. Mit 0b100 / 125kHz habe ich es auch versucht, alles ohne
Veränderung.
Und es gibt Fortschritte!
Weil es meistens die billigsten Sachen sind habe ich einfach mal die
3,3V nicht vom PB0 genommen sondern direkt an Plus gesteckt.
Oh Wunder: "306 293 305" in der ersten Zeile des Displays.
Hat jemand eine Erklärung dafür?
Matthias schrieb:> Hat jemand eine Erklärung dafür?
Wackelkontakt auf dem Steckbrett. Das sind eigentlich die ersten Dinge,
die man prüft. Liegt auch wirklich an jedem Pin des uC das Signal an,
das ich meine dass es anliegt.
Michael K. schrieb:> Matthias schrieb:>> Hat jemand eine Erklärung dafür?>> Wackelkontakt auf dem Steckbrett. Das sind eigentlich die ersten Dinge,> die man prüft. Liegt auch wirklich an jedem Pin des uC das Signal an,> das ich meine dass es anliegt.
liegt an ja.
Da ich kein Oszi habe kann ich meine Theorie nicht wirklich bestätigen,
aber ich habe das Gefühl, dass der Raspberry als Versorgung für die
Schaltung langsam zu klein wird.
Als nächste Projekte sollte ich mir wohl mal einen ISP-Adapter für den
PC und eine ausreichend starke Strom-/Spannungsquelle bauen.
Selbst wenn es das dann nicht ist, ist es auf jeden Fall schonmal ein
Fehler weniger.
Matthias schrieb:> Oh Wunder: "306 293 305" in der ersten Zeile des Displays.>> Hat jemand eine Erklärung dafür?
das ist kein Wunder:
du schaltest aref und die zu messende Spannung unmittelbar vor der
Messung ein, das taugt nicht, aref sollte stabil anliegen.
An aref gehört deshalb ein Kondensator.
Ich verstehe auch nicht warum du scheinbar stolz darauf bist keine
Kondensatoren zu verwenden, an Vcc gehört auf jeden Fall auch einer.
Matthias schrieb:> liegt an ja.
Ja offensichtlich lags ja nicht an, oder glaubst du der AREF-Pin sieht
von wo die 3V3 kommen? Das ist dem total egal ob die vom Nachbar-Pin
kommen oder von Alpha Centauri. Wichtig ist nur dass sie auch (stabil)
anliegen.
Daher auch der Einwand von Walter: Kondensatoren immer auch da einsetzen
wo es das Datenblatt empfiehlt. Verkehrt ist das nie.