Forum: Mikrocontroller und Digitale Elektronik Problem bei ADC Messung


von Florian (Gast)


Lesenswert?

Hallo zusammen,

ich habe ein Problem bei einer ADC-Messung. Ich benutzte das 
Entwicklungsboard AL-ERAM128_CAN, mit dem Controller AT90CAN128. Der ADC 
Eingang ist wie auch im Tutorial beschrieben über eine Spannungsteiler 
beschaltet.
Temperaturmessung erfolgt über einen KTY81-210.
Am ADC Eingang messe ich bei Raumtemperatur ca. 2,15V, ich nutze die 
interne Referenzspg.

Beim debuggen des Programms bleibe ich immer in der Loop-Schleife hängen 
weil das ADSC Bit nicht mehr auf 0 gesetzt wird also die Wandlung nicht 
beendet wird.

Hier mal mein Code:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <stdio.h>
4
5
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!
6
#define MCU at90can128
7
#define F_CPU 14000000//7372800UL
8
#define ADC_PORT 0
9
10
11
#include <util/delay.h>
12
#include <ctype.h>
13
#include <stdlib.h>
14
#include <util/delay.h>
15
16
17
18
/* ADC initialisieren                                      */
19
/* Parameter: void                                         */
20
void adc_init(void) {
21
22
  DDRF = 0x00;
23
  ADMUX |= (1<<REFS1) | (1<<REFS0);        // interne Referenzspannung nutzen
24
  ADMUX &= ~(1 << ADLAR);            // Ergebnis linksbündig ausgeben
25
26
  ADCSRB = 0x00;                  // Free Running Modus
27
  ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);  // Frequenzvorteiler = 128 mit 14,7456MHz / 128 = 115,2kHz 
28
 
29
  DIDR0= 0xF0;                   // Port PF7 - PF4 für ADC abschalten 
30
  ADCSRA &= ~(1<<ADEN);                // ADC wieder deaktivieren
31
32
}
33
 
34
/* ADC Einfachmessung                                      */
35
/* Parameter: mux ADC-Kanal                                */
36
uint16_t adc_read( uint8_t mux ) {
37
uint16_t result=0;  
38
                        // Kanal waehlen, ohne andere Bits zu beeinflußen
39
  ADMUX= (ADMUX & ~(0x1F)) | (mux & 0x1F);    // Es kann auch eine Verstärkung genutzt werden: vgl. Datenblatt S. 288 
40
  
41
  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung "single conversion"
42
  ADCSRA |= (1<<ADEN);                   // ADC aktivieren
43
44
  loop_until_bit_is_clear(ADCSRA, ADSC);    // auf Abschluss der Konvertierung warten
45
  
46
  result= ADCW;
47
48
  return result;                // ADC auslesen und zurückgeben
49
}
50
 
51
/* ADC Mehrfachmessung mit Mittelwertbbildung              */
52
/* Parameter: mux ADC-Kanal, average: Anzahl der Messungen */
53
uint16_t adc_read_avg( uint8_t mux, uint8_t average ) {
54
uint32_t result = 0;
55
 
56
  for (uint8_t i = 0; i < average; ++i )
57
    result += adc_read( mux );          // mehrmals ADC Einzelmessung aufrufen
58
 
59
  return (uint16_t)( result / average );
60
}
61
62
63
64
65
int main(void) {
66
67
68
  adc_init();          // ADC Initialisieren
69
70
    adc_read( ADC_PORT );    // Nach Aktivieren wird ein "Dummy-Read" empfohlen, man liest
71
                   // einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" 
72
73
74
75
    while (1) {
76
77
           _delay_ms (500);      // Messwerte jede 1/2 Sekunde erfassen
78
79
      
80
      adc_read_avg( ADC_PORT, 7 ); 
81
82
  }
83
84
return 0;
85
}

--------------------------------------------------------------------

vielen dank schon mal für eure hilfe!!!

: Bearbeitet durch User
von holger (Gast)


Lesenswert?

>  ADCSRA |= (1<<ADSC);      // eine ADC-Wandlung "single conversion"
>  ADCSRA |= (1<<ADEN);                   // ADC aktivieren

Mach das mal so rum

  ADCSRA |= (1<<ADEN);                   // ADC aktivieren
  ADCSRA |= (1<<ADSC);      // eine ADC-Wandlung "single conversion"

von Florian (Gast)


Lesenswert?

egal in welcher Reihenfolge ich die Register initialisiere, ich bleib 
immer wieder bei der Loop schleife stehen.

ich weiss nicht mehr wo ich noch ansetzen kann, die Initialisierung 
denke ich, ist soweit korrekt und die Funktionen für die messungen auch.
ich habe zum test alles andere was nicht zur adc-wandlung gehört aus dem 
code rausgenommen, um andere fehler auszuschließen.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Florian schrieb:
> Beim debuggen des Programms bleibe ich immer in der Loop-Schleife hängen
> weil das ADSC Bit nicht mehr auf 0 gesetzt wird also die Wandlung nicht
> beendet wird.

 Welches AVR Studio ?
 Ich glaube ADC und TWI kannst du nicht im Simulator abarbeiten.
 Programmiere dein Chip, es wird schon gehen.

von Bernhard F. (bernhard_fr)


Lesenswert?

holger schrieb:

> Mach das mal so rum
>
>   ADCSRA |= (1<<ADEN);                   // ADC aktivieren
>   ADCSRA |= (1<<ADSC);      // eine ADC-Wandlung "single conversion"

Wäre auch mein erster Gedanke gewesen, aber das Schlimme ist: Es 
funktioniert sogar auch in umgekehrter Reihenfolge in diesem Code...
Begründung: Beim ersten Warmlaufen lassen des ADC wird noch keine 
Messung gestartet sondern nur der ADC auf Enable gesetzt. Da er nie 
ausgeschaltet wird läuft es ab da...

Ich würde sogar einen Schritt weiter gehen und die letzte Zeile der 
"adc_init":

>   ADCSRA &= ~(1<<ADEN);                // ADC wieder deaktivieren
durch
    ADCSRA |= (1<<ADEN);                   // ADC aktivieren
ersetzen.

Da die erste Wandlung direkt auf die Initialisierung folgt, sehe ich 
keinen Vorteil, den nochmal kurz auszuschalten und dann gleich wieder 
ein. Dannach wird er ja eh nichtmehr ausgeschaltet...

Dann fällt auch die Zeile:

>   ADCSRA |= (1<<ADEN);                   // ADC aktivieren

komplett aus der "adc_read" raus.


Marc Vesely schrieb:
> Florian schrieb:
>> Beim debuggen des Programms bleibe ich immer in der Loop-Schleife hängen
>> weil das ADSC Bit nicht mehr auf 0 gesetzt wird also die Wandlung nicht
>> beendet wird.
>
>  Welches AVR Studio ?
>  Ich glaube ADC und TWI kannst du nicht im Simulator abarbeiten.
>  Programmiere dein Chip, es wird schon gehen.

Würde ich auch sagen.
Hab das eben mal schnell in Atmel Studio 6.2 geworfen und auf ein 
ATMega2560 (achja das liebe ArduinoMega 2560rev3...)gespielt.

Resultat:
Das Programm läuft wie es soll und A/D-wandelt fleißig. Ob es richtige 
Werte aufnimmt kann ich aber nicht sagen... Hab jetzt nichts an den ADC 
gehangen.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Bernhard F. schrieb:
> Würde ich auch sagen.
> Hab das eben mal schnell in Atmel Studio 6.2 geworfen und auf ein
> ATMega2560 (achja das liebe ArduinoMega 2560rev3...)gespielt.

 Irgendwie ist "ältere Version" aus dem Beitrag rausgefallen.
 Ich habe 6.2 deinstalliert, weil der unbedingt ins VS2010 wollte,
 fahre wieder Version 4.
 Und gegen ArduinoMega hab ich gar nichts...

von Florian (Gast)


Lesenswert?

ich benutze das avr-studio4 v.4.19

ich werde jetzt mal meine lcd Routinen dazu nehmen und lasse mir den adc 
wert mal auf dem lcd ausgeben, dann sehe ich ja ob es funktioniert oder 
nicht.

von Florian (Gast)


Lesenswert?

vielen dank für eure schnelle hilfe.

ich melde mich wieder ob es läuft oder nicht ;)

von florian (Gast)


Lesenswert?

Hallo zusammen, ich habe nun mal wieder etwas Zeit gehabt und weiter 
gemacht. Funktioniert soweit alles super, ich Messe sie Temperatur und 
das sogar ziemlich genau.

Jetzt mein Problem, beim adc wert auslesen "adc1 = adc_read_avg(0_64)" 
ist es nicht möglich die ganze Rechnung anzuhängen.

Mache ich eine extra Rechnung und speichere das Ergebnis in eine 
variable, bleibt diese ja immer gleich. Wie kann ich die Rechnung 
ständig wiederholen, sodass immer der aktuelle adc wert benutzt wird?

Danke für eure Hilfe

von Karl H. (kbuchegg)


Lesenswert?

florian schrieb:

> Mache ich eine extra Rechnung und speichere das Ergebnis in eine
> variable, bleibt diese ja immer gleich. Wie kann ich die Rechnung
> ständig wiederholen, sodass immer der aktuelle adc wert benutzt wird?


Deine Fragestellung ist unklar.
Denn bei
1
....
2
int main()
3
{
4
  uint16_t Wert;
5
6
  ...
7
8
  while( 1 ) {
9
10
    _delay_ms (500);      // Messwerte jede 1/2 Sekunde erfassen
11
      
12
    Wert =  adc_read_avg( ADC_PORT, 7 ); 
13
14
    errechne aus Wert die Temperatur
15
16
    gib die Temperatur auf dem LCD aus
17
18
  }
19
}

wird ja genau die Sequenz ADC_einlesen -> Temperatur_errechnen -> 
Temperatur_ausgeben immer wieder aufs neue wiederholt und führt 
natürlich dazu, dass
* bei jedem Durchlauf der dann vorliegende ADC Wert ermittelt
* daraus die damit verbundene Temperatur errechnet
* und die auch ausgegeben wird

: Bearbeitet durch User
von florian (Gast)


Lesenswert?

Ok, mein Fehler. Die Rechnung funktioniert und wird ständig mit 
aktualisieren der ADC-Messwerte ausgeführt. Ich denke ich komme einfach 
nicht mit den Datentypen und der LCD-Ausgabe klar.
Ich gebe die Daten ans LCD mit dieser Funktion aus:
1
void lcd_print_itoa(unsigned int zahl) {
2
char buffer [17];
3
    itoa(zahl, buffer, 10); 
4
    lcd_string(buffer);
5
  }
Meine Berechnung und ADC-Erfassung sieht so aus:
1
uint16_t ADCausgabe1;
2
uint32_t aktspg;
3
uint32_t deltaspg;
4
uint16_t temp1;
5
ADCausgabe1 = (adc_read_avg( 0,64));
6
  aktspg=(GRADausgabe1*5000);
7
  deltaspg=aktspg/1024;
8
  temp1=(deltaspg-2731);
9
10
  lcd_set_cursor(0,2);
11
  lcd_print_itoa(temp1);
Über einen Tipp wäre ich sehr dankbar.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

florian schrieb:

> Ich denke ich komme einfach
> nicht mit den Datentypen und der LCD-Ausgabe klar.

du musst dich daran gewöhnen, genau zu beobachten und zu beschreiben, 
was du siehst.

Ich denke mal, was dich momentan verblüfft, dass ist das die Ausgaben 
manchmal 'seltsame' Zahlen ergeben. Kühlst du den KTY ab, dann hast du 
zb plötzlich 90 auf der Ausgabe stehen.

Das liegt daran, dass die Funktion lcd_print_itoa nur genau das ausgibt, 
was sich aus der Zahl ergibt. Errechnest du zb 10 Grad, dann schreibt 
die Funktion auch 10 hin. Errechnet sie aber nur 9 Grad, dann wird auch 
nur 9 hingeschrieben. Das führt dann dazu, dass die Ausgabe des Textes 
"9" nur genau den Text auf dem LCD überschreibt, der seiner Länge 
entspricht. Steht auf dem LCD vorher "10"
1
  +----------------------------+
2
  |10                          |
3
  |                            |
4
  +----------------------------+

dann wird durch die Ausgabe des Textes "9" da nur 1 Zeichen neu 
geschrieben. Denn länger ist der Text ja nicht. D.h. aber der 0-er von 
"10" wird nie überschrieben und bleibt stehen. Obwohl der Text für "9" 
korrekt ausgegeben wird, sieht dein LCD danach so aus
1
  +----------------------------+
2
  |90                          |
3
  |                            |
4
  +----------------------------+

Die 9 stehen korrekt dort, aber der 0-er von vorher steht auch noch dort 
und ganz automatisch liest du daher 90 (neunzig).

-> du musst dafür sorgen, dass auch alle Textposition überschrieben 
werden!

Im besten Fall geschieht das dadurch, dass du zb vor einer Zahl 
entsprechend viele Leerzeichen ausgibst, die dafür sorgen, dass die 
'Zahl' eine immer gleiche Textlänge hat. Das hat dann auch den Vorteil, 
dass dir auf dem LCD der Text nicht ständig hin und her wandert, sondern 
die Einerstelle immer an derselben Position stehen bleibt, was das 
Ablesen deutlich erleichtert.

Im einfachsten Fall (wenn auch nicht der speichersparendste oder 
schnellste) benutzt du die Funktion sprintf, mit der man Ausgaben 
wunderbar formatieren kann. Zum Beispiel auf immer gleiche Feldbreite 
(=Textlänge bei Zahlen)
1
...
2
  while( 1 ) {
3
    ADCausgabe1 = (adc_read_avg( 0,64));
4
    aktspg=(GRADausgabe1*5000);
5
    deltaspg=aktspg/1024;
6
    temp1=(deltaspg-2731);
7
8
    sprintf( buffer, "%4u Grad", temp1 );
9
    lcd_set_cursor(0,2);
10
    lcd_string(buffer);
11
  }

Wenn das nicht dein Problem ist, dann beschreibe dein Problem besser!

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> Wenn das nicht dein Problem ist, dann beschreibe dein Problem besser!


PS: Temperaturen als unsigned?

Du warst offenbar noch nie in Österreich. Da ist es ganz normal, dass es 
im Winter auch mal Temperaturen unter 0 Grad Celsius hat. Etwas mehr 
über Datentypen nachdenken und was sinnvoll ist. Für Temperaturen wird 
wohl unsigned nicht so prickelnd sein.

: Bearbeitet durch User
von florian (Gast)


Lesenswert?

Bei meiner Anwendung wird es zu keinen negativen Temperaturen kommen 
deshalb ohne Vorzeichen 8-)

Mein Problem nochmal, angenommen mein adc wert ist 610, berechne ich in 
meinen Programm (((610*5000)/1024)-2731)=247,515625


uint16_t ADCausgabe1;
uint32_t aktspg;
uint32_t deltaspg;
uint16_t temp1;
ADCausgabe1 = (adc_read_avg( 0,64));
  aktspg=(ADCausgabe1*5000);
  deltaspg=aktspg/1024;
  temp1=(deltaspg-2731);

  lcd_set_cursor(0,2);
  lcd_print_itoa(temp1);

Abgesehen davon dass ich jetzt eine KommaZahl in einen integer schreibe, 
sollte doch wenigstens 247 ausgegeben werden. Ich bekomme aber -2697

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.