Forum: Mikrocontroller und Digitale Elektronik Atmega32 ADC Problem


von Jakob (Gast)


Lesenswert?

Hi,

bin dabei mich an ADC her ran zu arbeiten. Dabei habe ich das Tutorial 
genutzt, verstanden hab ich es in groben auch. Wollte es jetzt mal in 
der Praxis an meinen Test-Board ausprobieren. Wenn ich an meinen Board 
ein Taster drücke soll er eine Messung durchführen und dann anschließend 
an geben ob die Spannung großer oder kleine als 2,5V ist. Ich habe denn 
Code jetzt übernommen und auf den Teiler 128 eingestellt(da 16MHz). Groß 
verändern wollte ich erst mal nix. Ich glaube ich habe etwas übersehen 
oder ein Denkfehler bei der Ausführung im Hauptprogramm.

Er gibt zur Zeit an, dass die Spannung unter 2.5V ist.

Nur der Code für den ADC
1
void ADC_init(void){
2
  
3
  
4
  uint16_t result;
5
  
6
  ADMUX =  (1<<REFS0);      //  ACC als Referenzspannung nutzen
7
  ADCSRA = (1<<ADPS1) | (1<<ADPS0) | (1<<ADPS2);    // Frequenzvorteiler 128
8
  ADCSRA |= (1<<ADEN);   // ADC aktivieren
9
  
10
   ADCSRA |= (1<<ADSC);                // eine ADC-Wandlung 
11
   while (ADCSRA & (1<<ADSC) ) {}      // auf Abschluss der Konvertierung warten
12
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
13
     Wandlung nicht übernommen. */
14
  result = ADCW;
15
  
16
}
17
18
19
uint16_t ADC_read( uint8_t channel )
20
21
{
22
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
23
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
24
  while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
25
  return ADCW;
26
}
27
28
29
int main(void)
30
{
31
  DDRB = 0xff;
32
  DDRD = 0x00;
33
  DDRA = 0x00;
34
35
  uint16_t adcval;
36
  uint16_t Messwert;
37
38
ADC_init();
39
40
 while(1)
41
    {
42
if (Auswahl==5) //ADC MESSUNG
43
  {
44
    ADC_read(0);
45
    Messwert = 5.0*ADC_read(0)/1023;
46
    
47
    if (Messwert < 2.5)
48
    {
49
      PORTB = 0x0F;
50
      _delay_ms(5000);
51
      PORTB = 0x00;
52
    }
53
    
54
    if (2.5 < Messwert)
55
    {
56
      PORTB = 0x0F;
57
      _delay_ms(5000);
58
      PORTB = 0x00;
59
    }
60
    
61
    
62
    Auswahl=0;
63
  }
64
}
65
}

von Karl H. (kbuchegg)


Lesenswert?

Jakob schrieb:

> der Praxis an meinen Test-Board ausprobieren. Wenn ich an meinen Board
> ein Taster drücke

Das lässt sich in deinem Code nirgends finden.

Machs doch bei deinen ersten SChritten nicht komplizierter als 
notwendig. Das sind alles nur potentielle Fehlerquellen, die vom 
eigentlich zu testenden Vorhaben ablenken und ihrerseits Fehler 
enthalten können.

Du willst den ADC testen?
Dann tu das auch. Und zwar genau nur das.
Es spricht nichts dagegen, dass der ADC in der Hauptschleife einfach 
dauernd arbeitet. Du bruachst auch nix umrechnen (weitere Fehlerquelle). 
Deine 2.5V sind die Hälfte der Referenzspannung (die offenbar 5V ist). 
Wenn der ADC also bei der Referenzspannung (5V) einen Wert von 1023 
liefert, welchen Wert liefert er dann bei ca. 2.5V? Genau. 512. Werte 
kleiner als 512 bedeuten also kleiner 2.5V. Werte größer 512 bedeuten 
mehr als 2.5V. Ob das jetzt ein paar Zehntelvolt mehr oder weniger sind, 
spielt erst mal keine große Rolle. Wenn du die zu messende Spannung (wie 
erzeugst du die überhaupt?) von 0 bis 5V durchdrehst, dann muss die LED 
in der Mitte dieses Durchdrehens ein bzw. aus gehen, je nachdem, wie du 
die LED angeschlossen hast.

Das ist einfach genug, dass sich nur wenige Fehler einschleichen können, 
die nichts mit dem ADC zu tun haben
1
void ADC_init(void)
2
{
3
  ...
4
}
5
6
7
uint16_t ADC_read( uint8_t channel )
8
{
9
  ...
10
}
11
12
int main(void)
13
{
14
  uint16_t adcval;
15
16
  DDRB = 0xff;
17
18
  ADC_init();
19
20
  while(1)
21
  {
22
    adcval = ADC_read(0);
23
24
    if( adcval < 512 )
25
      PORTB = 0x0F;
26
    else
27
      PORTB = 0x00;
28
  }
29
}

Fertig. Einfach und simpel. Mit wenig Potential, dass Fehler abseits des 
ADC das Programm lahmlegen könnten.
Wenn das dann wie erwartet funktioniert, dann baust du weitere Dinge 
ein. Aber nicht vorher.
Gerade Neulinge neigen gerne dazu, mit einer zu komplexen 
Aufgabenstellung anzufangen. Das Ergebnis ist dann oft, das nichts 
funktioniert und er keine Ahnung hat, wo das Problem liegen könnte. Wenn 
obiges nicht funktioniert, dann gibt es nur 2 Möglichkeiten: entweder 
die LED funktionieren nicht oder im ADC ist was faul. Da nichts anderes 
im Programm vorkommt, kann auch nichts anderes fehlerhaft sein.

: Bearbeitet durch User
von Helper (Gast)


Lesenswert?

Eine int Variable mit einem Float Wert vergleichen? Alleine dort beginnt 
das Problem.
Die Zeile:
1
Messwert = 5.0*ADC_read(0)/1023;
liefert kein sauberes Resultat, wenn Messwert als integer definiert ist.

Sonst ein paar Tipps:

Wenn du dein Programm ein bisschen schneller gestalten willst, lass das 
Messwert rechnen komplett weg. Berechne dir lieber was für einen 
Digitalisierten Wert 2,5V bei deiner Auflösung von 10 Bit hätten und 
vergleich direkt diesen. Falls du 5V Referenz hast also:
1
if(ADCW < 512) {...

Zu deiner Funktion:
1
Jakob schrieb im Beitrag #3657686:
2
> return ADCW;
Diese Zeile ist unnütz. ADCW ist ein Register, welches vom Controller 
vorgegeben ist und somit an jeder Stelle der Programms darauf 
zugegriffen werden kann. Das ist das selbe wie du eine Globale Variable 
als Return Wert zurück gibst. Ein ganz normales Return tuts da auch.

von Amateur (Gast)


Lesenswert?

Nur gut, dass Du uns nicht mit so Kleinigkeiten wie: Woher kommt 
"Auswahl" oder wie bekommst Du 2.5 in eine Ganzzahlvariable 
belästigst;-)
>Groß  verändern wollte ich erst mal nix.
Wo auch immer Du das abgeschrieben hast - viel scheint nicht dahinter 
zustehen.

von San L. (zwillingsfreunde)


Lesenswert?

Amateur schrieb:
> Woher kommt
> "Auswahl"

Alleine aus dem Grund bin ich überrascht, dass der Compiler sowas 
überhaupt akzeptiert. Müsste doch eine Fehlermeldung erscheinen, wenn 
eine nicht definierte Variable abgefragt wird? Zumindest bei MPLAB war 
das immer so.

Amateur schrieb:
> 2.5 in eine Ganzzahlvariable

Kunst. Magie. Man weiss es nicht.

Genau das ist wieder einmal ein typisches Problem. Neulinge stürzen sich 
auf fertige Tutorials, welche schon X-Sonderfunktionen eingebaut haben 
und erhöhen sich damit die möglichen Fehlerquellen selbst.
Kann nur anraten, lieber einmal das Datenblatt zu öffnen und da selbst 
etwas versuchen hinzukriegen, als ständig irgendwelche Tutorials 
übernehmen und dann danach herumbasteln.
Wenn du nämlich ein Tutorial nicht richtig verstehst, also den Code 
nicht richtig interpretierst, wie willst du mit diesem dann arbeiten? Du 
kannst auch kein Auto fahren, wenn du nicht weisst welches Fusspedal 
wofür ist.

von Jakob (Gast)


Lesenswert?

Vielen Dank.
Ja es funktioniert.

Sie haben Recht, ich muss klein Anfangen aber man neigt gerne dazu 
komplexere Code zu schreiben. Jeglich muss ich es mir angewöhnen. Der 
Fehler bei mir lagt daran das ich bei der PortB ausgabe 2 mal das selbe 
stehen hatte also gabs keinen Unterschied
1
 if (Messwert < 2.5)
2
    {
3
      PORTB = 0x0F;
4
      _delay_ms(5000);
5
      PORTB = 0x00;
6
    }
7
    
8
    if (2.5 < Messwert)
9
    {
10
      PORTB = 0x0F;
11
      _delay_ms(5000);
12
      PORTB = 0x00;
13
    }

von Oliver (Gast)


Lesenswert?

Helper schrieb:
> Die Zeile:
>
1
> Messwert = 5.0*ADC_read(0)/1023;
2
>
> liefert kein sauberes Resultat, wenn Messwert als integer definiert ist.

Klar tut die das. Es ergibt nicht genau das, was Jakob sich vorstellt, 
aber es gibt ein ganz sauberes Ergebnis, und das kann auch kleiner als 
2.5 werden. Selbst der Vergleich zwischen float und int ist in C sauber 
definiert, da passiert nichts unerwartetes.

Oliver

von Helper (Gast)


Lesenswert?

Oliver schrieb:
> Klar tut die das. Es ergibt nicht genau das, was Jakob sich vorstellt,
> aber es gibt ein ganz sauberes Ergebnis, und das kann auch kleiner als
> 2.5 werden. Selbst der Vergleich zwischen float und int ist in C sauber
> definiert, da passiert nichts unerwartetes.

Natürlich kann das Resdultat kleiner als 2,5 bzw in diesem Fall eher 3 
werden. Ein anderes Resultat als 0, 1, 2, 3, 4 oder 5 wird er ja nie in 
der Variable haben.
Es ist allerdings nicht sauber gelöst. Wenn man schon solche 
Berechnungen macht, dann entweder das Resultat auch in Float lassen oder 
von Anfang an eine solche Berechnung weglassen und den Vergleich einfach 
mit dem Digitalisierten Wert von 2,5V also 512 bei einer Auflösung von 
10-Bit vergleichen.

Ich habe nie behauptet es funktioniert so nicht. Es war lediglich ein 
kleiner Tipp für die Zukunft, was er besser unterlassen sollte. Sonst 
wird er einer von diesen Programmierern a la 10'000 Zeilen Programm und 
alle Variablen Global definiert.

von Amateur (Gast)


Lesenswert?

Übrigens bei Vergleichen:

Genaugenommen gibt es bei einem Vergleich 3 mögliche Resultate.

Du fragst kleiner 2.5 und größer 2.5 ab. Was soll aber mit gleich 
passieren.

Aus Gründen der Übersichtlichkeit schreibt man so etwas:

XXX  < 2.5
XXX  > 2.5
XXX == 2.5

Dabei sollten unnötige Vergleiche durch den else-Zweig ausgeklammert 
werden.

Alternativ könnte auch ein Konstrukt wie
if ( XXX <= 2.5 ) {
  // kleiner oder gleich
} else {
  // definitiv > 2.5 auch ohne weitere Abfrage
}

verwendet werden oder
if ( XXX < 2.5 ) {
  // kleiner
} else {
  if ( XXX > 2.5 ) {
    // größer
  } else {
    // gleich bleibt über
    // fehlt dieser Zweig, so ignorierst Du ==
  }
}

von San L. (zwillingsfreunde)


Lesenswert?

Amateur schrieb:
> Du fragst kleiner 2.5 und größer 2.5 ab. Was soll aber mit gleich
> passieren.

Dieser Fall wird nie eintreffen, da sein Resultat in einer Variable vom 
Typ INT gespeichert wird.

: Bearbeitet durch User
von Amateur (Gast)


Lesenswert?

>Dieser Fall wird nie eintreffen, da sein Resultat in einer Variable vom
>Typ INT gespeichert wird.

Das stimmt nur eingeschränkt!

Dein Compiler macht aus der Abfrage:
XXX < 2.5 --> XXX < 2
bzw.
2.5 < XXX --> 2 < XXX
und somit fällt zwar 2.5 nicht aber == 2 durchs Raster.

von Oliver (Gast)


Lesenswert?

Amateur schrieb:
> Dein Compiler macht aus der Abfrage:
> XXX < 2.5 --> XXX < 2

Nö. Macht er nicht. Ist ein Operand eines Operators float und der andere 
ein int, wird der int in ein float umgewandelt.

Oliver

von San L. (zwillingsfreunde)


Lesenswert?

Oliver schrieb:
> Nö. Macht er nicht. Ist ein Operand eines Operators float und der andere
> ein int, wird der int in ein float umgewandelt.

Korrekt.
Somit bliebe dann dann
XXX < 2.5 ---> XXX.0 < 2.5
Wobei XXX immer eine volle Zahl sein wird.

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.