Probleme mit dem ADC des ATmega32

OP #3251919
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
Angehängte Dateien:
#3251933
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.
OP #3251951
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.
Angehängte Dateien:
#3251981
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.
OP #3252019
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 ;)
#3252092
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.
OP #3252209
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?
Gast #3252226
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
#3252274
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
}
#3252283
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.
OP #3252328
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.
#3252431
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren