Hallo Ich habe ein atmega8 und habe ihn in c mit AVR Studio4 programmiert. Er soll eine Spannung am ADC0 und ADC1 Port einlesen und in der zweiten Zeile eines LCD ausgeben. Dies funktioniert auch wenn ich nur einen Wert anzeigen lasse. Sobalt ich beide Werte ( einen links einen rechts ) anzeigen lasse, wechseln sich die Werte im Sekunden Tackt. Also die Anzeige von ADC0 zeigt dann für ca. ne Sekunde den Wert von ADC1 und umgekehrt an. Woran könnte das liegen? interne 5V AREF mit Kondensator mit 100N auf GND ADC0 Poti 0 ADC1 Poti 1 LCD Ausgabe: Zeile1: Gestartet Zeile2: 0000mV 0000mV Vielen Dank Jens
Sorry habe den Code vergessen MIST Hier ist der Ausschnitt der für denn ADC und Die Ausgabe zuständig ist. #include <avr/io.h> #include <util/delay.h> #include <stlib.h> #include <lcd-routines.h> unsigned short wertadc0; unsigned short wertadc1; ... // Hauptprogramm void main() { ... Start: ADC0(); mV(); ADC1(); mA(); _delay_ms(300); goto Start; while(1) { } return 0; } void ADC0() { ADMUX = 0b01000000; ADCSRA = 0b10000110; ADCSRA |=(1<<ADSC); while (!(ADCSRA & ( 1<<ADSC))); wertadc0 = ADCW; } void ADC1() { ADMUX = 0b01000001; ADCSRA = 0b10000110; ADCSRA |=(1<<ADSC); while (!(ADCSRA & ( 1<<ADSC))); wertadc1 = ADCW; } void mV() { unsigned char lcdnummer1 = 0x00; unsigned char lcdnummer2 = 0x00; unsigned char lcdnummer3 = 0x00; unsigned char lcdnummer4 = 0x00; NO1: if ( wertadc0 >=1000) { wertadc0-=1000; lcdnummer1 ++; goto NO1; } lcdnummer1 |= 0x30; NO2: if ( wertadc0 >=100) { wertadc0-=100; lcdnummer2 ++; goto NO1; } lcdnummer2 |= 0x30; NO3: if ( wertadc0 >=10) { wertadc0-=10; lcdnummer3 ++; goto NO1; } lcdnummer3 |= 0x30; NO4: if ( wertadc0 >=1) { wertadc0-=1; lcdnummer4 ++; goto NO1; } lcdnummer4 |= 0x30; set_cursor(0,2); lcd_data(lcdnummer1); set_cursor(1,2); lcd_data(lcdnummer2); set_cursor(2,2); lcd_data(lcdnummer3); set_cursor(3,2); lcd_data(lcdnummer4); set_cursor(4,2); lcd_string("mV"); } void mA() { unsigned char lcdnummer1 = 0x00; unsigned char lcdnummer2 = 0x00; unsigned char lcdnummer3 = 0x00; unsigned char lcdnummer4 = 0x00; NO1: if ( wertadc1 >=1000) { wertadc1-=1000; lcdnummer1 ++; goto NO1; } lcdnummer1 |= 0x30; NO2: if ( wertadc1 >=100) { wertadc1-=100; lcdnummer2 ++; goto NO1; } lcdnummer2 |= 0x30; NO3: if ( wertadc1 >=10) { wertadc1-=10; lcdnummer3 ++; goto NO1; } lcdnummer3 |= 0x30; NO4: if ( wertadc1 >=1) { wertadc1-=1; lcdnummer4 ++; goto NO1; } lcdnummer4 |= 0x30; set_cursor(8,2); lcd_data(lcdnummer1); set_cursor(9,2); lcd_data(lcdnummer2); set_cursor(10,2); lcd_data(lcdnummer3); set_cursor(11,2); lcd_data(lcdnummer4); set_cursor(12,2); lcd_string("mA"); }
Komisch jetzt springt er nicht mehr sondern vertauscht permanent denn Wert.
> while (!(ADCSRA & ( 1<<ADSC)));
Das ADSC wird gelöscht, wenn die Wandlung beendet ist! Da es eine
Zeile weiter oben erst gesetzt wird, wird die Schleife direkt
übersprungen. Warten auf das Wandlungsergebnis geht anders...
Und benutze bitte zum Schreiben der Steuerregister die Bitnamen! Dafür
sind die da. Nicht nur für ADSC...
1 | void Output( unsigned char Column, unsigned char Row, |
2 | unsigned short Value, const char* Unit ) |
3 | {
|
4 | char Text[20]; |
5 | |
6 | sprintf( "%04u%s", Value, Unit ); |
7 | set_cursor( Column, Row ); |
8 | lcd_string( Text ); |
9 | }
|
10 | |
11 | void mV() |
12 | {
|
13 | Output( 0, 2, wertadc0, "mV" ); |
14 | }
|
15 | |
16 | void mA() |
17 | {
|
18 | Output( 8, 2, wertadc1, "mA" ); |
19 | }
|
Zu deinem ADC Problem. while (!(ADCSRA & ( 1<<ADSC))); Das ist falsch rum. Nachdem du den ADC auf einen neuen Kanal stellst, solltest du die erste Messung verwerfen. Die ist normalerweise Müll. Es empfiehlt sich auch ein paar mal hintereinander zu messen und den Mittelwwert davon zu nehmen. Also so wie im Tutorial: Erste Messung verwerfen, dann zb 4 mal messen und Mittelwert davon. Warum verwendest du eigentlich nicht die Funktion aus dem Tutorial? http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Aktivieren_des_ADC
1 | void main() |
2 | {
|
3 | ...
|
4 | Start:
|
5 | |
6 | ADC0(); |
7 | mV(); |
8 | ADC1(); |
9 | mA(); |
10 | _delay_ms(300); |
11 | goto Start; |
12 | |
13 | while(1) |
14 | {
|
15 | }
|
16 | return 0; |
17 | }
|
Der goto da drinn ist Nonsense. Die Idee ist es eine Endlosschleife zu kreieren. Die ist im Prinzip ja auch schon da: while( 1 ) Nur müsstest du sie noch mit Leben füllen.
1 | void main() |
2 | {
|
3 | ...
|
4 | |
5 | while(1) |
6 | {
|
7 | ADC0(); |
8 | mV(); |
9 | ADC1(); |
10 | mA(); |
11 | _delay_ms(300); |
12 | }
|
13 | return 0; |
14 | }
|
Gewöhn dir goto gleich wieder ab (auch in deinen Umwandlungsfunktionen). Wenn du zum jetzigen Zeitpunkt einen goto benutzen willst, gibt es immer eine einfachere strukturierte Möglichkeit dasselbe zu erreichen. Wenn ich es (ohne grosse Probleme oder darüber Nachzudenken) schaffe eine Dreiviertel-Million Lines of Code Programm ohne goto zu schreiben, dann wirst du ja wohl die 80 Zeilen ohne goto machen können.
Karl heinz Buchegger wrote: > Nachdem du den ADC auf einen neuen Kanal stellst, solltest du die erste > Messung verwerfen. Die ist normalerweise Müll. Kannst du diese Aussage belegen? Im Datenblatt steht nur, dass man nach dem Einschalten/Wechseln der Referenz die erste Messung verwerfen soll: The first ADC conversion result after switching reference voltage source may be inaccurate, and the user is advised to discard this result. Bei mir funktioniert der Wechsel des Kanals nämlich auch problemlos.
Benedikt K. wrote: > Karl heinz Buchegger wrote: > >> Nachdem du den ADC auf einen neuen Kanal stellst, solltest du die erste >> Messung verwerfen. Die ist normalerweise Müll. > > Kannst du diese Aussage belegen? Im Datenblatt steht nur, dass man nach > dem Einschalten/Wechseln der Referenz die erste Messung verwerfen soll: > The first ADC conversion result after switching reference voltage source > may be inaccurate, and the user is advised to discard this result. > > Bei mir funktioniert der Wechsel des Kanals nämlich auch problemlos. Wenn das so im Datenblatt steht, dann hast hast du da wohl recht und ich war überereifrig.
>Nachdem du den ADC auf einen neuen Kanal stellst, solltest du die erste >Messung verwerfen. Die ist normalerweise Müll. Nachdem daß ja immer wieder postuliert wird, und auch im Beispiel-Code des Tutorials so steht, kann mir mal jemand sagen, wo genau das im Datenblatt steht? Ich finde das einfach nicht. Oliver
Das Problem liegt im Ablauf des Umschaltens des Multiplexers. Wenn du den Befehl gibst, den Kanal zu Wechseln, wird dieser Befehl logischerweise erst nach Ablauf der letzten Wandlung ausgeführt. Nach Ablauf der Wandlung wird aber auch, das Flag gesetzt, dass neue Daten bereitstehen. Somit hast du also nachdem du den Multiplexer umgeschaltet hast, noch ein Wandlungsergebnis vom vorher ausgewählten Kanal. Deshalb verwerfen viele Entwickler einfach das erste Wandlungsergebnis nach dem Kanalwechsel. Wenn man es aber geschickt aufgleist, geht es natürlich auch ohne verwerfen von Daten. Man kann nämlich auch einfach den Befehl zum Umschalten des Mux einfach vor der letzten Wandlung des vorhergehenden Kanals absetzen. Klar soweit?
@ Karl Heijnz dein Code: void Output( unsigned char Column, unsigned char Row, unsigned short Value, const char* Unit ) { char Text[20]; sprintf( "%04u%s", Value, Unit ); set_cursor( Column, Row ); lcd_string( Text ); } void mV() { Output( 0, 2, wertadc0, "mV" ); } void mA() { Output( 8, 2, wertadc1, "mA" ); } funktioniert bei mir nicht es wirt auf dem LCD zweimal( I'schwartzes kästchien schräges X ) ausgegeben.
@ Ralf Schwarz ich setze doch erst den ADMUX und dann den ADCSRA und starte dann erst den ADC. Oder verstehe ich da was falsch?
@Ralf Schwarz
>Klar soweit?
Nö. Ein " ham' wer schon immer so gemacht" ist genauso hilfreich wie
"viel hilft viel", das einmauern toter Katzen in Brückenfundamente, oder
"schad' ja nix".
Etwas genauer: Die Anwendung hier sind einzelne Wandlungen, der ADC wird
jedes mal neu gestartet, VREF wird nicht umgeschaltet. Ein
Standard-Szenario für ADC-Betrieb ohne free-run.
Wo steht im Danteblatt, daß ein Umschalten des Kanals vor dem Start des
ADC's zu einem ungültigem Wandlungsergebnius führt?
Oliver
@Oliver: Sorry für meine unqualifizierte Aussage. Du hast natürlich Recht, der ADC läuft hier nicht im Free Running mode, somit hat mein Hinweis natürlich nichts mit dem Problem zu tun. Aber da viele (auch erfahrene) Leute sich schwer tun mit dem Timing des ADCs, hab ich hier mal meinen Senf dazugegeben und dümmer ist dabei sicher niemand geworden.
elomt wrote: > funktioniert bei mir nicht es wirt auf dem LCD zweimal( > I'schwartzes kästchien schräges X ) ausgegeben. My fault: sprintf( Text, "%04u%s", Value, Unit ); **** Mann muss dem sprintf schon auch sagen, wo der Ergebnistext hin soll :-) Tschuldigung.
So jetzt funktioniert alles habe : while (!(ADCSRA & (1<<ADSC))); gegen while (ADCSRA & (1<<ADSC)); und die Ausgabe von Karl Heinz Vielen Vielen Vielen Dank
zu dem GOTO Befehl Ich habe hier im Forum schon oft gelesen das der goto oldschool ist und nicht mehr genutzt werden soll. Andere sagen benutzt ihn ( ist einfach und funktioniert). Meine Frage ist nun was für Altanatieven gibt es denn?
elomt wrote: > Ich habe hier im Forum schon oft gelesen das der goto oldschool ist und > nicht mehr genutzt werden soll. Andere sagen benutzt ihn ( ist einfach > und funktioniert). Meine Frage ist nun was für Altanatieven gibt es > denn? Nichts dagegen goto zu benutzen, wo es einen Sinn ergibt. Aber wenn man eine Schleife implementiert, duerfte while() fast immer erste Wahl sein. Dein Code sieht eher aus wie Markoassembler mit den ganzen "von Hand" implementierten Schleifen. Im Uebrigen sind sind deine Spruenge in den mA und mV Funktionen hoechst zweifelhaft...
elomt wrote: > Ich habe hier im Forum schon oft gelesen das der goto oldschool ist und > nicht mehr genutzt werden soll. Prinzipiell richtig, da man schnell Fehler damit machen kann und das Programm unübersichtlich werden kann. > Andere sagen benutzt ihn ( ist einfach und funktioniert). Und hat einige Vorteile gegenüber anderen Lösungen. Generell verbieten ist also keine Lösung. > Meine Frage ist nun was für Altanatieven gibt es > denn? In diesem Fall eine beliebige Endlosschleife:
1 | Start:
|
2 | |
3 | ADC0(); |
4 | mV(); |
5 | ADC1(); |
6 | mA(); |
7 | _delay_ms(300); |
8 | goto Start; |
Dies kann man wunderbar durch folgendes ersetzen:
1 | while (1) |
2 | {
|
3 | ADC0(); |
4 | mV(); |
5 | ADC1(); |
6 | mA(); |
7 | _delay_ms(300); |
8 | }
|
Die anderen gotos kann man auch durch ein while ersetzen. Dazu muss lediglich das if durch ein while ersetzt werden, wenn ich das richtig sehe.
Sorry wenn ich nochmal was schreibe aber ich habe gerade gesehen das mein adc wert nicht unter 14 geht wenn ich gnd auf den adc port gebe. Weis einer woran das liegen könnte?
Schlechte Masseanbindungvom GND Pin, ADC Pin auf Ausgang + High geschaltet usw.
mit einem Multimeter messe ich aber 0,000V am Port dann sollte der ADC doch auch 0 anzeigen.
Auch Multimeter messen Mist. Aber 14 ist schon ein bischen viel. Welchen Wert kriegst du, wenn du mal direkt GND an den Pin hängst? Edit: Grade gesehen. Du hast ja schon Masse mal direkt an den Pin angeschlossen. Zeig nochmal das Pgm, so wie es jetzt aussieht. Schaltbild wär auch nicht verkehrt, damit wir nicht aneinander vorbeireden.
Hallo Ich schreibe erstmal ein neues Programm da mein jetziges mit der ganzen LCD Menüführung zu umfangreich ist. Vielen Dank noch mal für die guten tips.
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.