Forum: Mikrocontroller und Digitale Elektronik ADC - fragliche Registerwerte


von Florian Z. (zoechi)


Lesenswert?

Guten Tag,

und zwar habe ich folgendes Problem: Ich benutze einen ATmega128L und 
möchte damit einen AD-Wandler realisieren. Nun liegt beim AREF-Pin eine 
Spannung von 3,27 V und beim ADC0-Pin eine Spannung von 0,22 V an. 
Bisher auch noch alles ok. Nur wenn ich mir die Registerwerte von ADCL 
und ADCH am Display anzeigen lasse, dann sehe ich dass im ADCH eine 3 
und im ADCL 255 steht. Demnach sind alle 10 Bits des ADC-Wert-Registers 
mit einer 1 besetzt. Was eigentlich bedeuten müsste, dass eine Spannung 
von 3,27 Volt (oder höher?) anliegt. Was aber laut Multimeter nicht der 
Fall ist.

Die Konfiguration des ADC's sieht wie folgt aus:
ADMUX=0x00;
ADCSRA=0x80;

Desweiteren gibt es noch eine Besonderheit, welche ich mir nicht ganz 
erklären kann. Denn wenn ich das Multimeter an Ground und ADC0 anlege, 
so messe ich die 0,22 Volt, allerdings wechselt dann der Registerwert 
von ADCH auf 0, und der Wert von ADCL schwankt in etwa zwischen 20 und 
160. Warum schwankt dann der ADC-Wert? Ich messe die Spannung ja 
parallel, da dürfte sich doch eigentlich nichts ändern oder?!

Ich stelle nun noch den kompletten Programmcode hier rein, auch wenn ich 
denke dass vielen mein Programmierstil nicht wirklich gefallen wird:

Kurze Beschreibung noch:
Ein Timer startet alle paar ms die ISR des ADC's. Dort wird solange 
gewartet bis die Umwandlung abgeschlossen ist, und anschließend werden 
die Registerwerte am LCD ausgegeben.

1
#include <avr/interrupt.h>
2
#include <avr/io.h>
3
#include <stdio.h>
4
#include <lcd.h>
5
#include <lcd.c>
6
7
#define adc_configuration        ADMUX=0x00; ADCSRA=0x80
8
#define adc_get_temperature      ADCSRA|=0x4C
9
10
#define timer_configuration      (TCCR1A=0x00);(TCCR1B=0x04);(TCCR1C=0x00)
11
#define timer_interrupt_enable   (TIMSK|=0x04)
12
13
unsigned char  adc_temperature;
14
15
int main()
16
{  
17
  adc_configuration;
18
  timer_configuration;
19
  timer_interrupt_enable;
20
  DDRC=0xFF;          //Display Ausgänge
21
  lcd_init(LCD_DISP_ON);
22
  DDRF&=0xEF;          //ADC0 auf Eingang setzen
23
  SREG|=0x80;
24
  while(1)
25
  {
26
  }
27
  return 0;
28
}
29
30
ISR(ADC_vect)
31
{
32
  while((ADCSRA&0x40)==0x40)  //Warten bis Umwandlung abgeschlossen ist
33
  {
34
  }
35
  unsigned int value = ADCL;
36
  unsigned int high_value= ADCH;
37
  char temp[1] = {0};
38
  sprintf(temp, "%d", value);
39
  lcd_puts(temp);
40
  lcd_putc(' ');
41
  sprintf(temp, "%d", high_value);
42
  lcd_puts(temp);
43
}
44
45
ISR(TIMER1_OVF_vect)
46
{
47
  adc_get_temperature;
48
  lcd_clrscr();
49
  lcd_home();  
50
}


Nachdem ihr mir bisher immer helfen konntet, zähle ich auch dieses mal 
auf euch!

Danke und freundliche Grüße,
zoechi

von Karl H. (kbuchegg)


Lesenswert?

Florian Z. schrieb:

> und ADCH am Display anzeigen lasse, dann sehe ich dass im ADCH eine 3
> und im ADCL 255 steht.

Fass die beiden zusammen und lass sie dir als Zahl zwischen 0 und 1023 
anzeigen. Das ist doch Unsinn sich in C die Einzelbytes anzeigen zu 
lassen. SChmeiss dir doch nicht selber Prügel zwischen die Beine wenn es 
nicht sein muss
1
  while((ADCSRA&0x40)==0x40)  //Warten bis Umwandlung abgeschlossen ist
2
  {
3
  }
4
  unsigned int value = ADC;
5
  char temp[10];
6
  sprintf(temp, " %d ", value);
7
  lcd_puts(temp);
8
}

und definiere die das nächste mal dein Array temp groß genug, dass du 
auch einen String der länger als 0 Zeichen ist, da reinbringst.



> Ich stelle nun noch den kompletten Programmcode hier rein, auch wenn ich
> denke dass vielen mein Programmierstil nicht wirklich gefallen wird:

Fang damit an, die Hex-Werte durch eine vernünftige Bit-Schreibweise zu 
ersetzen. Hier im Forum werden nicht viele Lust dazu haben, sich das 
Datenblatt deines Prozessors zu holen und zu kontrollieren was du da 
eigentlich bei den Hex-Zahlen für Bits gesetzt hast und ob die beim M128 
eventuell an anderer Stelle sitzen als du denkst.

von spess53 (Gast)


Lesenswert?

Hi

>ADCSRA=0x80

-Keine Interruptfreigabe für den ADC
-Vorteiler 2, also mit Sicherheit viel zu schnell

>  SREG|=0x80;

Kryptischer geht es kaum.

> while((ADCSRA&0x40)==0x40)  //Warten bis Umwandlung abgeschlossen ist

Blödsinn. Der Interrupt kommt wenn die Wandlung abgeschlossen ist.

....

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

spess53 schrieb:
> Hi
>
>>ADCSRA=0x80
>
> -Keine Interruptfreigabe für den ADC
> -Vorteiler 2, also mit Sicherheit viel zu schnell

Du hast den hier übersehen.

#define adc_get_temperature      ADCSRA|=0x4C

(und ich habs auch erst gesehen, nachdem ich 20 mal zwischen dem 
Datenblatt und dem Code hin und her gewechselt habe)

Alles in allem ein deutlicher Hinweis für den TO, dass seine 
Schreibweise scheisse ist.
Anstatt zu lametieren, dass sie uns nicht gefallen wird, sollte er sie 
lieber ändern. Es gibt schon einen Grund dafür, warum wir das nicht so 
schreiben.

von spess53 (Gast)


Lesenswert?

Hi

>Du hast den hier übersehen.

Stimmt.

MfG Spess

von Klaus T. (gauchi)


Lesenswert?

Florian Z. schrieb:
> Desweiteren gibt es noch eine Besonderheit, welche ich mir nicht ganz
> erklären kann. Denn wenn ich das Multimeter an Ground und ADC0 anlege,
> so messe ich die 0,22 Volt, allerdings wechselt dann der Registerwert
> von ADCH auf 0, und der Wert von ADCL schwankt in etwa zwischen 20 und
> 160. Warum schwankt dann der ADC-Wert? Ich messe die Spannung ja
> parallel, da dürfte sich doch eigentlich nichts ändern oder?!

klingt, als ob dir irgendwas sehr hochohmiges den Eingang hochzieht. So 
hochohmig, dass dein Messgerät es wieder runterziehen kann.

von Florian Z. (zoechi)


Lesenswert?

grüß euch,

zu meiner kryptischen hex-registerwert-setzung: unser lehrer hat es uns 
drei jahre so beigebracht. in der letzten klasse hätte ich es auch mit 
der anderen variante (einzelne bits setzen) versucht - allerdings meinte 
er dann ich soll es so weiter handhaben wie er es uns beigebracht hat - 
und ehrlich gesagt finde ich diese schreibweise auch nicht 
allzuschlecht, zumindest wenn man sich daran gewöhnt hat. für jemand 
anderen ist es natürlich schlechter nachvollziehbar. ihr könnt diesen 
absatz eventuell als "quasi-entschuldigung" oder auf jeden fall als 
kurze erklärung hinnehmen.

@ Karl heinz Buchegger:
danke für den tipp - wusste gar nicht dass ich den adc als ganzes 
auslesen kann.

@ Klaus T.:
werde ich mir gleich mal anschaun, danke!

von avion23 (Gast)


Lesenswert?

Hi Florian,
mit ADCSRA |= 0x80; usw. lese ich deinen Code nicht, kann dir also nicht 
helfen.

von Florian Z. (zoechi)


Lesenswert?

ich glaube ich habe den fehler gefunden (hardwareseitig), bin mir zwar 
noch nicht sicher ob es die lösung des gesamten problems ist, allerdings 
ist es ein fehler. müsst euch also einstweilen nicht mehr mit meinem 
code quälen. falls ich doch noch fragen habe werde ich mich nochmal 
melden, und dementsprechend auch den code in eine besser lesliche form 
bringen. danke soweit!

von Lukas K. (carrotindustries)


Lesenswert?

Florian Z. schrieb:
> ser lehrer hat es uns
> drei jahre so beigebracht

Wie soll ich mir das Vorstellen?
Ihr nehmt das Datenblatt, kreuzt die zu setzenden Bits an und rechnet 
euch den Hex-Wert aus?
Oder könnt ihr das inzwischen aus dem 0xFF?
Florian Z. schrieb:
> ich glaube ich habe den fehler gefunden (hardwareseitig)
Schön für dich, dürfen wir auch wissen woran es denn lag?
Glauben ist nicht wissen....

von Karl H. (kbuchegg)


Lesenswert?

Florian Z. schrieb:
> grüß euch,
>
> zu meiner kryptischen hex-registerwert-setzung: unser lehrer hat es uns
> drei jahre so beigebracht.

Schick deinen Lehrer hier ins Forum.
Dann waschen wir ihm den Kopf.
Der ist doch gemeingefährlich!

Ein Lehrer sollte ein Vorbild sein. Aber kein schlechtes Vorbild. Wer 
selbst nicht vernünftig programmierern kann, sollte nicht in die Lehre 
gehen, sondern vielleicht selbst erst einmal einen Kurs machen.

Und nein. Dafür gibt es für deinen Lehrer keine wie immer geartete 
Entschuldigung. Wer so etwas macht, dem sollte man mit dem nassen Fetzen 
eine drüberziehen. Und wenn er es dann immer noch nicht geschnallt hat, 
dann gehört ihm die Lehrbefugnis entzogen.

von Simon K. (simon) Benutzerseite


Lesenswert?

Es geht ja auch nicht unbedingt darum, was schneller geht. Je nach Übung 
geht die Hexadezimal-Variante ja auch schneller, keine Frage.

Allerdings ist die Namen-Methode weitaus fehlerunanfälliger, portabler 
und falls man mal den rückwärtigen Weg beschreitet (Aus dem Code wieder 
wissen will, welche Bits gesetzt werden), geht es damit auch schneller.

von Rolf P. (rolfp)


Lesenswert?

Florian Z. schrieb:
>
> und zwar habe ich folgendes Problem: Ich benutze einen ATmega128L und
> möchte damit einen AD-Wandler realisieren. Nun liegt beim AREF-Pin eine
> Spannung von 3,27 V und beim ADC0-Pin eine Spannung von 0,22 V an.

Wie kommen die 3.27 V auf den AREF-Pin? Schaltschema?

>
> Desweiteren gibt es noch eine Besonderheit, welche ich mir nicht ganz
> erklären kann. Denn wenn ich das Multimeter an Ground und ADC0 anlege,
> so messe ich die 0,22 Volt, allerdings wechselt dann der Registerwert
> von ADCH auf 0, und der Wert von ADCL schwankt in etwa zwischen 20 und
> 160. Warum schwankt dann der ADC-Wert? Ich messe die Spannung ja
> parallel, da dürfte sich doch eigentlich nichts ändern oder?!
>

ADC0 ist doch ein Eingang. Da musst du erst mal eine zu messende
Spannung anlegen. (Zum ersten Austesten vielleicht ein Spannungsteiler
mittels 2 Widerstaenden zwischen Vcc und Gnd)

> Ich stelle nun noch den kompletten Programmcode hier rein, auch wenn ich
> denke dass vielen mein Programmierstil nicht wirklich gefallen wird:
>
Wenn du den Programmcode so umschreibst dass er gefaellt, findest du
bestimmt selbst noch einige Fehler ;-)

> Kurze Beschreibung noch:
> Ein Timer startet alle paar ms die ISR des ADC's. Dort wird solange
> gewartet bis die Umwandlung abgeschlossen ist, und anschließend werden
> die Registerwerte am LCD ausgegeben.
>

Anscheinend hast du das Prinzip der Interrupts noch nicht so ganz
verstanden. In deinem Programm brauchst du den Timer gar nicht.
Wenn du den Interrupt fuer den AD-Wandler benutzt, dann wird
die entsprechende Interruptroutine automatisch aufgerufen sobald
die AD-Wandlung fertig ist. Um Interrupts einzuschalten musst
du im Hauptprogramm noch ein sei() einfuegen.
In einer Interrrup-Routine (also in ISR(ADC_vect) zum Beispiel)
macht man niemals irgendwelche langen Berechnungen wie z.B.
ein sprintf() oder lcd_puts().
Definiere doch einfach eine globale Variable, in die du den
neuen Wert reinschreibst. Dann noch ein Flag setzen dass der
Wert jetzt gueltig ist. Dann kannst du im Hauptprogramm auf
einen gueltigen Wert warten, den du dann ausgibst.
Also vielleicht etwa so:

<progcode>
static volatile unsigned char adc_low,adc_high; //neuer Messwert
static volatile char adc_flag=0; //1=gueltiger Wert vorhanden
//Das volatile wird gebraucht da verschiedene Pragrammteile
//darauf zugreifen sollen.

ISR(ADC_vect)
{
 adc_low=ADCL;
 adc_high=ADCH;
 adc_flag=1; //Wert als gueltig markieren
 ADCSRA |= (1<<ADSC);//naechste Wandlung starten (AD Start Conversion)
}
</progcode>

und im Hauptprogramm:

<progcode>
   sei();
   while(1)
     {if(adc_flag==1) //falls gueltiger Wert vorhanden
        {unsigned int value=(adc_high<<8)+adc_low;
         flag=0; //Flag wieder ruecksetzen
         sprintf(temp, "%d", value);
         lcd_puts(temp);
        }
     }
</progcode>

Ich hoffe diese Hinweise helfen dir weiter.
Rolf

von spess53 (Gast)


Lesenswert?

Hi

>Um Interrupts einzuschalten musst du im Hauptprogramm noch ein sei()
>einfuegen.

Hat er doch: SREG|=0x80;

MfG Spess

von rolfp (Gast)


Lesenswert?

spess53 schrieb:
> Hat er doch: SREG|=0x80;

Stimmt, habe ich uebersehen.
Lustigerweise macht der Compiler daraus 3 Befehle:
1
     508:  8f b7         in  r24, 0x3f  ; 63
2
     50a:  80 68         ori  r24, 0x80  ; 128
3
     50c:  8f bf         out  0x3f, r24  ; 63
im Gegensatz zum sei():
1
     50e:  78 94         sei

Rolf

von spess53 (Gast)


Lesenswert?

Hi

>Stimmt, habe ich uebersehen.

Auf so eine Idee muss man auch erst mal kommen.

Wenn man nach 3 Jahren Informatik (oder was auch immer) sei() nicht 
kennt, kann es nicht nur am Lehrer liegen.

MfG Spess

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.