Forum: Mikrocontroller und Digitale Elektronik Probleme mit dem ADC des ATmega32


von Michael S. (redrabbit)


Angehängte Dateien:

Lesenswert?

Hallo alle zusammen,

ich möchte gerne eine digitale Temperaturdiffernzschaltung aufbauen und 
dazu einen ATmega32, bzw. seinen ADC verwenden.
Zu Testzwecken, habe ich ADC0 direkt über mein Labornetzgerät gespeist 
und den Controller an 5V gehängt. Die Beschaltung habe ich im Anhang. 
Den ADC habe ich wie folgt eingestellt:

ADMUX = (1<<REFS0);
ADCSRA = (1<<ADPS1) | (1<<ADPS2);

Bedauerlicherweise liefert mir der ADC keine sonderlich guten Werte.
Hier eine kleine Messreihe:

anliegende Spannung  |  ADC-Wert
---------------------------------------
          0V         |  33020 / 32932
          1V         |  32988 / 177774
          2V         |   289 / 465
          3V         |   706 / 674
          4V         |   963 / 899
          5V         |   963 / 899

An AREF liegen 5,04V an. Genauso wie an AVCC und VCC. Die Schwankungen 
bei den ADC-Werten würde ich auf das Netzteil schieben. Aber kann mir 
Jemand sagen, wieso der ADC Werte über 1023 ausgibt und bei ~963 am 
Anschlag zu sein scheint?

mfg
Michi

von spess53 (Gast)


Lesenswert?

Hi

>ADMUX = (1<<REFS0);
>ADCSRA = (1<<ADPS1) | (1<<ADPS2);

Ist das die ganze Initialisierung?

Wenn ja, brauchst du dich nicht wundern. Der ADC ist nicht 
eingeschaltet.

MfG

von Cyblord -. (cyblord)


Lesenswert?

Michael Stoffels schrieb:

> Aber kann mir
> Jemand sagen, wieso der ADC Werte über 1023 ausgibt und bei ~963 am
> Anschlag zu sein scheint?

Klar, du hast riesen Mist programmiert. Wenn man aus einem 10 Bit ADC 
Werte über 1023 rausholt, dann sollte man einfach mal sein Programm 
debuggen anstatt einen Thread aufzumachen. Und wenn man schon, debugging 
via Forum machen will, dann sollte man wenigstens den Code anhängen. 
Irgendeine arme Seele (Karlheinz meistens) wird sich schon erbarmen und 
dir den Fehler suchen. Du kannst ja solange bisschen in der Sonne 
chillen.

von Kurt (Gast)


Lesenswert?

Michael Stoffels schrieb:
> bei den ADC-Werten würde ich auf das Netzteil schieben. Aber kann mir
> Jemand sagen, wieso der ADC Werte über 1023 ausgibt und bei ~963 am

Liegt am Programm

von Mario G. (mario)


Lesenswert?

aus dem Datenblatt (Table 122, Seite 294):

VREF Reference Voltage 2.0 to AVCC-0.5

AREF sollte (um mind. 0.5V) kleiner als AVCC sein! Besser du benutzt die 
interne Referenz und einen Spannungsteiler davor...

von Michael S. (redrabbit)


Angehängte Dateien:

Lesenswert?

Nein, das war nicht die ganze Initialisierung, sondern nur die 
Konfiguration der Register. Den Code habe ich aus dem gcc tutorial für 
ADC kopiert. Also ist anhängen überflüssig -> 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Analoge_Ein-_und_Ausgabe
Trotzdem häng ichs mal an. (habs n bischen verändert)

Und ja, ich hab mich auch mit dem Text drüber beschäftigt ;-)

@mario: Ja, das probier ich mal aus

EDIT: hab nen kleinen Fehler gefunden. Ich geb die Register in der 
flaschen Reihenfolge aus.

von Stefan E. (sternst)


Lesenswert?

Michael Stoffels schrieb:
> Ich geb die Register in der
> flaschen Reihenfolge aus.

Außerdem schreibst du ihren Inhalt mittels nicht initialisierter Pointer 
weiß Gott wohin.

von Michael S. (redrabbit)


Lesenswert?

Ach du Schande...
Ja, das hab ich gestern Abend noch gemacht. Danke dir :)

von Karl H. (kbuchegg)


Lesenswert?

Die Frage ist, warum gist du überhaupt irgendwelche Register aus?

> (habs n bischen verändert)

Darf man fragen wozu?

Was war an der Original-Routine so schlimm, dass du da was verändern 
musst?

wäre ein
1
int main()
2
{
3
  uint16_t adc_wert;
4
5
  RS_init();
6
  ADC_init();
7
  PORTA= 0x00;
8
  
9
  while(1)
10
  {
11
    switch( RS_receive() )
12
    {
13
      case 'a': {
14
            adc_wert = ADC_read(0);
15
            RS_send_integer( adc_wert );
16
            break;
17
            }
18
    }
19
  }
20
  
21
  return 0;
22
}
wirklich so schlimm gewesen?
Gut, du müsstest dir für die UART eine Sende-Funktion machen, die einen 
uint16_t ausgeben kann(*), aber das soll ja erstens nicht so wahnsinnig 
schwer und auf der anderen Seite auch nicht so wahnsinnig unnötig sein. 
Denn eine derartige Routine kann man immer wieder mal brauchen.

(*) wobei. Deinen Ausgabewerten nach zu urteilen kann die vorhandene 
Funktion RS_send das sogar.

von Michael S. (redrabbit)


Lesenswert?

Ja, es ist vllt. wirklich schlauer mir nen int zurückgeben zu lassen und 
den dann zu zerlegen. Ich war mir nur nicht sicher, ob der mega32 n ADCW 
register hat. In nem anderen Tutorial stand nähmlich, dass die 
einfacheren Controller das nicht hätten. Gut, ich hätt vllt. im 
Datenblatt nachschauen können. Ich änder das heut Abend mal ab.
RS_send verschickt nur unsigned chars. RS_send_int werd ich wohl noch 
implementieren. Vielen Dank für die Anregung.
Jetzt is mir das Wetter aber doch zu schön ;)

von Karl H. (kbuchegg)


Lesenswert?

Michael Stoffels schrieb:
> Ja, es ist vllt. wirklich schlauer mir nen int zurückgeben zu lassen und
> den dann zu zerlegen.

Mach es dir zur Regel zunächst mit den Datentypen zu arbeiten, die am 
natürlichsten sind.
Für einen 16 Bit Wert ist das ein uin16_t.

> Ich war mir nur nicht sicher, ob der mega32 n ADCW
> register hat.

Hat er auch nicht.
Aber der C-Compiler erlaubt es dir so zu tun als ob.
Der Compiler zerlegt das dann richtig bzw. setzt umgekehrt die 
Einzelregister wieder zusammen.

von Michael S. (redrabbit)


Lesenswert?

Ja, ich hab erst vor Kurzem mit µC-Programmierung angefangen. Vorher 
habe ich nur mit Delphi und C für den PC programmiert und da ist das 
nicht sooo kritisch. War auch nicht sehr hardwarenah. Auf jeden Fall 
dake für die Tipps. Meinst Du, dass das das Problem löst?

mfg

von Michael S. (redrabbit)


Lesenswert?

So, ich habe es jetzt so umgeändert, wie Du es mir empfohlen hast. 
Leider hat sich an den Werten nicht viel geändert. Jetzt möchte ich 
nochmal gernen den Punkt mit AREF aufgreifen. Obwohl ich, meines 
Erachtens nach, schon die interne Referenzspannung (von AVCC) verwende 
und AREF über 100nF auf GND gesetzt habe, liegen an AREF, genau wie an 
AVCC und VCC, 5,04V an. Was soll ich dagegen machen? Weil die Spannung 
kommt vom Controller und sollte doch eig min. 0.5V kleiner als AVCC 
sein. Wäre es vllt. hier sonnvoll eine externe Referenzspannung (durch 
Spannungsteiler aus Betreibsspannung erzeugt), zu verwenden?

von Spess53 (Gast)


Lesenswert?

Hi

>Was soll ich dagegen machen? Weil die Spannung
>kommt vom Controller und sollte doch eig min. 0.5V kleiner als AVCC
>sein.

Gar nichts. Wenn das ein Problem wäre würde Atmel keine Referenzspannung 
VCC über die Vref-Bits anbieten.

Außerdem steht im aktuellen Datenblatt

VREF Reference Voltage   2.0 - AVCC V

Also nichts mit VCC-0,5V.

MfG Spess

von Michael S. (redrabbit)


Lesenswert?

So, ich habs ENDLICH hinbekommen! :D
Hatte wiedermal nen fehler beim auseinanderziehen des words zum senden.
Bitmanipulation muss ich wohl noch n bissl üben ;)
Vielen Dank für die Hilfe

mfg
Michi

von Karl H. (kbuchegg)


Lesenswert?

Michael Stoffels schrieb:
> So, ich habe es jetzt so umgeändert, wie Du es mir empfohlen hast.
> Leider hat sich an den Werten nicht viel geändert.

> Was soll ich .... machen?

Dein Programm herzeigen.
Und zwar komplett, inklusive der USART Funktionen.

Denn
> 1V | 32988 / 177774
die 177-tausend machen mich stutzig

: Wiederhergestellt durch User
von Michael S. (redrabbit)


Angehängte Dateien:

Lesenswert?

Ja, hatte mich wohl doch zu früh gefreut. Die Genauigkeit stimmt glaub 
noch ned ganz.
Jop, hab mal alles angehängt.

von Karl H. (kbuchegg)


Lesenswert?

Hast du auf dem PC ein spezielles Programm laufen, oder warum 
verschickst du Zahlenwerte
1
void RS_send(unsigned char c)
2
{
3
  while (!(UCSRA & (1<<UDRE))) {} // warten bis Senden moeglich
4
 
5
    UDR = c;  
6
}
7
8
unsigned char RS_receive()
9
{
10
  while(!(UCSRA & (1<<RXC))) {} //warten bis zeichen verfügbar
11
  
12
  return UDR;
13
}
14
15
void RS_send_word(uint16_t word)
16
{
17
  uint8_t h, l;
18
  
19
  RS_send( word >> 8 );
20
  RS_send( word & 0xff );
21
}

in Byteform?

Wenn da ein Terminal ist, dann lass dir doch um Himmels Willen vom µC 
eine Textform der Daten schicken! Ist doch für dich viel einfacher, als 
wenn du laufend Bytes analysieren musst. Deine Computer sollen für DICH 
arbeiten und nicht du ihren Job auch noch übernehmen.
1
void RS_send_string( const char* str )
2
{
3
  while( *str )
4
    RS_send( *str++ );
5
}
6
7
void RS_send_uint( uint16_t value )
8
{
9
  char text[6];
10
  utoa( value, text, 10 );
11
  RS_send_string( text );
12
}

von Karl H. (kbuchegg)


Lesenswert?

Und dann
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <rs232.h>
4
#include <ADC.h>
5
6
int main()
7
{
8
  uint16_t adc_wert;
9
10
  RS_init();
11
  ADC_init(5000);
12
  PORTA= 0x00;
13
  
14
  while(1)
15
  {
16
    switch( RS_receive() )
17
    {
18
      case 'a':
19
      {
20
          adc_wert = ADC_read(0);
21
          RS_send_string( "ADC Raw Value (" );
22
          RS_send_uint( 0 );
23
          RS_send_string( "): " );
24
          RS_send_uint( adc_wert );
25
          RS_send_string( "\n" );
26
          break;
27
      }
28
        
29
    case 'u':
30
      {
31
          adc_wert = ADC_read_voltage(0);
32
          RS_send_string( "ADC Voltage Value (" );
33
          RS_send_uint( 0 );
34
          RS_Send_string( "): " );
35
          RS_send_uint( adc_wert );
36
          RS_send_string( " mV\n" );
37
      }
38
    }
39
  }
40
  
41
  return 0;
42
}

dann kann man auf einem hundsordinären Textterminal dann auch was lesen, 
ohne erst mal 2 Stunden Kopfrechnen zu müssen, wobei man sich 12 mal 
vertut.

von Stefan E. (sternst)


Lesenswert?

Michael Stoffels schrieb:
> Die Genauigkeit stimmt glaub
> noch ned ganz.

??? Soll das etwa heißen, du bekommst tatsächlich was anderes als 0 
heraus?
1
unsigned char u_ref;
2
...
3
  return adc*(u_ref/1024);

von Michael S. (redrabbit)


Lesenswert?

Danke, auf das mit dem Text bin ich nicht gekommen. Habs immer mit dem 
Taschenrechner von Windows aus der binären Darstellung rausgerechnet. 
Ich arbeite mit hTerm. Jetzt muss ich nurnoch die Berechnung der 
Spannung ändern. Ich werds wohl in µV rechnen, damit ich keine floats 
brauch. Die hinteren 3 Stellen werden dann mit u-= u%1000 abgeschnitten.

Ich sagte ICH GLAUBE, dass die Genauigkeit nicht stimmt. Jetzt hab ich 
die Betriebspannung auf exakt 5,00V eingestellt und die Genauigkeit 
stimmt. (zumindest bei der manuellen Berechnung aus dem ADC-Wert). Mit 
Genauiglkeit habe ich nicht die Auflösung, sondern die Tatsache gemeint, 
dass die errechnete Spannung von der Gemessenen zu stark abwich.

von Karl H. (kbuchegg)


Lesenswert?

Michael Stoffels schrieb:
> Danke, auf das mit dem Text bin ich nicht gekommen. Habs immer mit dem
> Taschenrechner von Windows aus der binären Darstellung rausgerechnet.

Wir sind Programmierer. Und als solche notorisch faul. Ehe wir selber 
uns die Fingern mit einem Taschenrechner schmutzig machen, lassen wir 
ein Programm die Dinge ausrechnen oder so aufbereiten, dass WIR 
möglichst wenig Arbeit haben. Frei nach dem Motto: lieber einmal und 
dann dafür aber ordentlich.

> Ich arbeite mit hTerm. Jetzt muss ich nurnoch die Berechnung der
> Spannung ändern. Ich werds wohl in µV rechnen, damit ich keine floats
> brauch. Die hinteren 3 Stellen werden dann mit u-= u%1000 abgeschnitten.

Dann rechne dir doch mal aus, wievielen Millivolt es entspricht, wenn 
der ADC Wert um 1 schwankt. Noch genauer brauchst du ganz offensichtlich 
nicht rechnen.

> Ich sagte ICH GLAUBE, dass die Genauigkeit nicht stimmt.
Stefan hat aber trotzdem recht (und ich habs übersehen).
Deine 5000 werden in einem unsigned char nicht so recht reinpassen.

Ich geb dir einen Tip. Wir verwenden hier nicht umsonst als Datentypen 
uint8_t oder uint16_t oder int16_t oder.... Denn dann kann man am 
Datentyp sofort ablesen, wieviele Bits der hat und damit auch ohne groß 
Überlegen, welches die größte/kleinste speicherbare Zahl da drinnen ist.

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.