Forum: Compiler & IDEs ADC 0 und 1 springen


von elomt (Gast)


Lesenswert?

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

von Johannes M. (johnny-m)


Lesenswert?

Das liegt an Zeile 42 in Deinem Code. Ganz klarer Fall...

von elomt (Gast)


Lesenswert?

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");

}

von elomt (Gast)


Lesenswert?

Komisch jetzt springt er nicht mehr sondern vertauscht permanent denn 
Wert.

von Johannes M. (johnny-m)


Lesenswert?

> 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...

von Karl H. (kbuchegg)


Lesenswert?

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.

von Benedikt K. (benedikt)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Oliver (Gast)


Lesenswert?

>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

von Ralf S. (spacedog) Benutzerseite


Lesenswert?

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?

von elomt (Gast)


Lesenswert?

@ 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.

von elomt (Gast)


Lesenswert?

@ Ralf Schwarz

ich setze doch erst den ADMUX und dann den ADCSRA und starte dann erst 
den ADC. Oder verstehe ich da was falsch?

von Oliver (Gast)


Lesenswert?

@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

von Ralf S. (spacedog) Benutzerseite


Lesenswert?

@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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von elomt (Gast)


Lesenswert?

So
jetzt funktioniert alles habe :

while (!(ADCSRA & (1<<ADSC))); gegen while (ADCSRA & (1<<ADSC));
und die Ausgabe von Karl Heinz

Vielen Vielen Vielen Dank

von elomt (Gast)


Lesenswert?

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?

von P. S. (Gast)


Lesenswert?

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...

von Benedikt K. (benedikt)


Lesenswert?

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.

von elomt (Gast)


Lesenswert?

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?

von Benedikt K. (benedikt)


Lesenswert?

Schlechte Masseanbindungvom GND Pin, ADC Pin auf Ausgang + High 
geschaltet usw.

von elomt (Gast)


Lesenswert?

die GND's und der ADC liegen auf dem selben GND in der Schaltung

von Karl H. (kbuchegg)


Lesenswert?

Dein Poti wird halt nicht exakt 0 Ohm erreichen können.

von elomt (Gast)


Lesenswert?

mit einem Multimeter messe ich aber 0,000V am Port dann sollte der ADC 
doch auch 0 anzeigen.

von Karl H. (kbuchegg)


Lesenswert?

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.

von elomt (Gast)


Lesenswert?

auch 14

von elomt (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.