Forum: Mikrocontroller und Digitale Elektronik ADC Wert ungenau


von Susanne (Gast)


Lesenswert?

Hallo zusammen,

ich habe ein frage bezüglich des AD Wandlers in einem Mikrocontroller 
ATMEga644p

ich habe dort einen Lichtwiderstand angeschlossen.
dieser schickt mir die Werte von 1-255 über den uart an meinen PC

Funktioniert auch alles sehr gut, leider kommen bei mir immer nur ganz 
zahlen an.
Ich hätte eigentlich erwartet dass auch Fließkommazahlen ankommen.
1,1....20,1

hab ich da vielleicht was falsches in den Datentypen gemacht?
kann der Controller vielleicht gar keine Fließkommazahlen übermitteln?

bin leider Anfänger und für jede Hilfe dankbar.

hier der Code:
#ifdef F_CPU
#undef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#include <inttypes.h>
#include <util/delay.h>
#include <stdint.h>
#include <stdio.h>




// Diese Beispiel zeigt die Anwendung des ADC eines ATmega644

/* ADC initialisieren */
void ADC_Init(void)
{
  // die Versorgungsspannung AVcc als Referenz wählen:
  //ADMUX = (1<<REFS0);
  // oder interne Referenzspannung als Referenz für den ADC wählen:
  ADMUX = (0<<REFS1) | (1<<REFS0);

  // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
  // schon auf 0, also single conversion
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren

  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man
liest
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu
lassen" */

  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung
  while (ADCSRA & (1<<ADSC) ) {         // auf Abschluss der 
Konvertierung warten
  }
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
     Wandlung nicht übernommen. */
  (void) ADCW;
}

/* ADC Einzelmessung */
uint16_t ADC_Read( uint8_t channel )
{
  // Kanal waehlen, ohne andere Bits zu beeinflußen
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
  while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung 
warten
  }
  return ADCW;                    // ADC auslesen und zurückgeben
}


void USART_Init( unsigned int baud )
  {
    /*Set baud rate*/
    UBRR0H = (unsigned char)(baud>>8);
    UBRR0L = (unsigned char)baud;
    /* Enable receiver and transmitter*/
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);
    /* Set frame format: 8data, 2stop bit*/
    UCSR0C = (1<<USBS0)|(3<<UCSZ00);
  }


  void USART_Transmit( unsigned char data )
  {
    /* Wait for empty transmit buffer*/
    while
     ( !( UCSR0A & (1<<UDRE0)) );
    /* Put data into buffer, sends the data*/
    UDR0 = data;
  }

  void uart_puts( char *s )
  {
    /* while *s != '\0' so unequally "string-end characters (terminator)
*/
    while (*s)
    {
      USART_Transmit(*s);

      s++;
    }
  }


void main(void)
{
  PORTB = (1<<PD0)|(1<<PD1);
  DDRB = (1<<DDD0)|(1<<DDD1);
  USART_Init(0x6);
  char String1[32] = {"Test "};
  char String2[32] = {"Test "};
  char String3[32] = {"Test "};
  char String4[32] = {"Test "};
  long ADCWert1;
  long ADCWert2;
  long ADCWert3;
  long ADCWert4;
  ADC_Init();
  //ADCWert = 123;
  while(1)
  {
    ADCWert1 = ADC_Read(0b00001);//Seite 249 Datenblatt Kanal 1
    ADCWert2 = ADC_Read(0b00010);//Seite 249 Datenblatt Kanal 2
    ADCWert3 = ADC_Read(0b00000);//Seite 249 Datenblatt Kanal 0
    ADCWert4 = ADC_Read(0b00011);//Seite 249 Datenblatt Kanal 3


    sprintf(String1,"LDR1: %i g \n", ADCWert1);
    sprintf(String2,"LDR2: %i g \n", ADCWert2);
    sprintf(String3,"LDR3: %i g \n", ADCWert3);
    sprintf(String4,"LDR4: %i g \n", ADCWert4);
    uart_puts(String1);
    uart_puts(String2);
    uart_puts(String3);
    uart_puts(String4);
    //USART_Transmit(S);
        _delay_ms(100);
  }

}

von klupp (Gast)


Lesenswert?

Zeig mir mal, wo in Deinem Code ne "Fließkommazahlen" sein könnte.

von akku (Gast)


Lesenswert?

Der Wandler gibt nur Interger (Ganzzahlen) aus.

von Susanne (Gast)


Lesenswert?

Und wie umgehe ich das?
Wie mach ich das genauer?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Susanne schrieb:
> Funktioniert auch alles sehr gut, leider kommen bei mir immer nur ganz
> zahlen an.

Klingt richtig

Susanne schrieb:
> Ich hätte eigentlich erwartet dass auch Fließkommazahlen ankommen.

Wo sollen die herkommen? Der ADC des Mega hat ein 10-Bit Ergebnis 
Register, in dem nur ganze Zahlen zwischen 0 und 1023 ausgegeben werden.
Erst, wenn du diesen Wert mit einer Gleitkommazahl multiplizierst oder 
durch eine teilst, könnten Nachkommastellen hinzukommen.
Um die höchstmögliche Auflösung des ADC zu nutzen, solltest du 
allerdings die Ergebnisse in 16-bit (words) Wertten darstellen und nicht 
nur als 8-Bit Bytes.

von Wolfgang (Gast)


Lesenswert?

Susanne schrieb:
> kann der Controller vielleicht gar keine Fließkommazahlen übermitteln?

Der Controller macht genau das, was du ihm sagst. Ob das identisch mit 
dem ist, was du möchtest, hängt von der Umsetzung in Form des Programms 
ab.

Was der Controller in seiner Hardware unterstützt, findest du im 
Datenblatt.
Im Kapitel 23. ADC - Analog-to-digital converter unter 23.1 Features 
steht zum Beispiel, dass der ADC eine Auflösung von 10 Bit besitzt, was 
einer Anzahl von 2^10=1024 Stufen entspricht. Und mehr als diese 10 Bit 
rückt der ADC über das ADC DATA REGISTER nicht raus. Was du damit machst 
und wie du das skalierst, steht auf einem anderen Blatt.

Mit einer Umwandlung der Form
1
sprintf(String1,"LDR1: %i g \n", ADCWert1);
 für die Ausgabe wirst du allerdings bei "%i" nie etwas anderes als 
Ganzzahlen erhalten. Guck dir mal die Beschreibung der Funktion 
sprintf() und die Beschreibung für die Format-Anweisung an.

von Arduinoquäler (Gast)


Lesenswert?

Susanne schrieb:
> ich habe dort einen Lichtwiderstand angeschlossen.

Wenn du nur einen Widerstand an einen ADC anschliesst wirst
du kein vernünftiges Ergebnis bekommen, denn ein ADC kann
keine Widerstände messen sondern nur Spannungen. Und solange
kein Strom durch deinen Widerstand fliest wird dort nahe
bei Null Volt gemessen.

von Susanne (Gast)


Lesenswert?

Leider bin ich noch nicht auf die entsprechende Lösung gekommen.
auch den Wert: "i" durch "n" ersetzen erbrachte nicht den gewünschten 
Erfolg.
dann kommt nämlich ein ? an.

vorher:
sprintf(String1,"LDR1: %i g \n", ADCWert1);

nachher:
sprintf(String1,"LDR1: %n g \n", ADCWert1);

von M. K. (sylaina)


Lesenswert?

Susanne schrieb:
> Leider bin ich noch nicht auf die entsprechende Lösung gekommen.
> auch den Wert: "i" durch "n" ersetzen erbrachte nicht den gewünschten
> Erfolg.

Wieso sollte das auch was ändern? Deine Variable ADCWert1 hast du als 
long definiert. Wie kommst du jetzt darauf, dass das ein Gleitkommatyp 
ist? Ein Integer bleibt ein Integer und wird nicht zum Gleitkommatyp nur 
weil man in sprint die Formatierung für Gleitkommatypen benutzt. Ich 
werd ja auch nicht Einstein, nur weil ich mir einen Bart anklebe.
Dann schaun wir uns noch an wie du ADCWert1 befüllst und stellen fest: 
Selbst wenn ADCWert1 als Gleitkommatyp definiert wäre würde das nichts 
bringen denn du befüllst ADCWert1 mit einem Integer.

Ich empfehle dir daher unbedingt, dass du dir noch mal die 
Funktionsweise des ADCs betrachtest denn du hast anscheinend noch gar 
nicht verstanden, was genau dir der ADC liefert.

von Sascha_ (Gast)


Lesenswert?

Arduinoquäler schrieb:
> Susanne schrieb:
>> ich habe dort einen Lichtwiderstand angeschlossen.
>
> Wenn du nur einen Widerstand an einen ADC anschliesst wirst
> du kein vernünftiges Ergebnis bekommen, denn ein ADC kann
> keine Widerstände messen sondern nur Spannungen. Und solange
> kein Strom durch deinen Widerstand fliest wird dort nahe
> bei Null Volt gemessen.

Da steht Lichtwiderstand. Also nen Photowiderstand.

WIE der jetzt angeschlossen wurde ist allerdings interessant.

von Route_66 H. (route_66)


Lesenswert?

Sascha_ schrieb:
> Da steht Lichtwiderstand. Also nen Photowiderstand.

Mein bevorzugter Lichtwiderstand heisst "Sonnenbrille".

von Ralph B. (rberres)


Lesenswert?

Susanne schrieb:
> ich habe dort einen Lichtwiderstand angeschlossen.
> dieser schickt mir die Werte von 1-255 über den uart an meinen PC

Susanne schrieb:
> sprintf(String1,"LDR1: %i g \n", ADCWert1);

Ich vermute mal das du einen LDR Fotowiderstand an den Eingang des ADCs 
angeschlossen hast.

Bitte mal Typenbezeichnung oder Datenblatt des LDRs.

Ein LDR änderst sein Widerstand etwa zwischen ( dunkel ) mehreren Megohm 
bis zu ( hell ) einigen hundert Ohm. Und das an einer recht unlinearen 
Kennlinie

Um daraus eine Spannung zu machen, welche der ADC auswerten kann müsste 
zumindest über einen Widerstand von mehreren 10K bis mehrere 100K eine 
Spannung angelegt werden. ( Vorzugsweise die Spannung , die der ADC auch 
als Referenzspannung verwendet.

Der ADV macht dann daraus einen 10Bit breites digitales Wort ( Integer )

welches der Eingangspannung proportional ist ( nicht der der Lichtstärke 
wegen des stark nichtlinearen Zusammenhanges des LDR ).

Mit der Größe des Widerstandes kann man in gewissen Grenzen festlegen 
bei welcher Lichtstärke die größte Auflösung der Lichtänderung ist.

Also wie groß man den Widerstand wählt hängt von dem Helligkeitsbereich 
ab den man messen will.

Ralph Berres

von meckerziege (Gast)


Lesenswert?

Ich glaube da sollten wir nochmal einfacher anfangen... Da mangelt es 
massiv an Grundlagen zu Datentypen, Ausgabe von Daten ("%i" zu tippen 
ergibt niemals float...), Verwendung des ADCs etc.

Ich sehe hier noch zu viele Baustellen, würde das Projekt erstmal 
zurückstellen, Grundlagen aneignen und dann nochmal probieren.

von Threadversteher (Gast)


Lesenswert?

Ralph B. schrieb:
> Ich vermute mal das du einen LDR Fotowiderstand an den Eingang des ADCs
> angeschlossen hast.
> ..........

Alles Quatsch. Sascha hast doch den vollen Durchblick:

Sascha_ schrieb:
> Da steht Lichtwiderstand. Also nen Photowiderstand.

Damit ist doch alles gesagt.

von Threadversteher (Gast)


Lesenswert?

Ralph B. schrieb:
> Um daraus eine Spannung zu machen, welche der ADC auswerten kann müsste
> zumindest über einen Widerstand von mehreren 10K bis mehrere 100K eine
> Spannung angelegt werden. ( Vorzugsweise die Spannung , die der ADC auch
> als Referenzspannung verwendet.
>

Deine Ausführungen in Ehren, aber das alles will unsere
"Susanne" gar nicht hören bzw lesen.

Sie will einfach nur aus einem Integer ein Float machen, so
einfach ist das ....

von W.S. (Gast)


Lesenswert?

Susanne schrieb:
> ich habe dort einen Lichtwiderstand angeschlossen.
> dieser schickt mir die Werte von 1-255 über den uart an meinen PC
>
> Funktioniert auch alles sehr gut, leider kommen bei mir immer nur ganz
> zahlen an.
> Ich hätte eigentlich erwartet dass auch Fließkommazahlen ankommen.
> 1,1....20,1

Tja, den Spruch "Es gibt nichts Gutes, es sei denn, man tut es" kennst 
du hoffentlich.

Also fang an, deine Aufgabe selbst zu lösen. Du willst eine 
Textausgabe per serieller Schnittstelle haben? Dann mußt du dies 
organisieren. Dein ADC liefert dir nur geordnete Bitmuster. Jaja, keine 
Zahlen, sondern geordnete Bitmuster. Alle ADC's machen das so. Es gibt 
ein LSB (also der Benjamin) und ein MSB (das größte) und einige Bits 
dazwischen, jeweils mit einer Staffelung der Wertigkeit um den Faktor 2.

Aber du willst vermutlich eine Maßzahl haben, also 20.1 für 20.1 Volt 
(oder Grad Celsius oder weiß der Geier). Also mußt du dein Bitmuster vom 
ADC eben geeignet skalieren und dann in eine zugehörige Textausgabe 
wandeln.

Jaja, klingt kompliziert, ja? Aber da muß man durch, zwecks 
grundlegenden Verständnisses. Überflieger, die nur copy&paste können, 
haben wir schon genug.

Man kann das Ergebnis des ADC als simple Integerzahl ansehen, die bei 
dir eben von 0 bis 1023 geht (wenn du beide Teile richtig aus dem ADC 
ausgelesen hast). Kann, nicht muss.

Nun kannst du diese Zahl ins Gleitkommaformat wandeln lassen, such dir 
dafür die geeignete Funktion aus, zumeist reicht eine simple Zuweisung 
aus. Anschließend kannst du die gewonnene Gleitkommazahl mit einer 
selbst erfundenen Gleitkommazahl multiplizieren und dann mußt du sie nur 
noch in eine Folge von Textzeichen wandeln. Sowas ist bei fast jedem 
Compiler mit dabei, aber es kostet einen Haufen Speicherplatz.

Für spezielle Fälle geht es auch sehr viel simpler, also äußere dich mal 
über die konkreten Umstände.

W.S.

von Threadversteher (Gast)


Lesenswert?

W.S. schrieb:
> also äußere dich mal über die konkreten Umstände.

Das war schon viel mehr als zuviel des Guten.

Susanne ist wohl längst untergetaucht ob des Überangebots
an Wissen .....

von W.S. (Gast)


Lesenswert?

Na dann

Gute Nacht zusammen.

W.S.

von Johnny B. (johnnyb)


Lesenswert?

W.S. schrieb:
> Na dann
>
> Gute Nacht zusammen.
>
> W.S.

Danke, Dir auch gute Nacht.

von Susanne (Gast)


Lesenswert?

Hallo zusammen,

sorry, hätte nicht gedacht, dass doch noch ernsthafte hilfe kommt.
wenn ich ehrlich bin weiß ich nicht wo anfangen.
Ich weiß aber schonmal an was es liegt.

vielleicht kann mir jemand die Lösung sagen dann kann ich anhand deren 
mich in die Thematik einarbeiten. Schließlich ist noch kein Meister vom 
Himmel gefallen.

von ui (Gast)


Lesenswert?

ist ganz einfach...

Du machst einfach aus
1
sprintf(String1,"LDR1: %i g \n", ADCWert1);
1
sprintf(String1,"LDR1: %f g \n", ADCWert1*1.0);

und schon hast du deine Gleitkommazahlen.

von Threadversteher (Gast)


Lesenswert?

Susanne schrieb:
> Schließlich ist noch kein Meister vom Himmel gefallen.

Ja, Lernfaule dagegen schon viele.

Susanne schrieb:
> vielleicht kann mir jemand die Lösung sagen

Mit oder ohne Silbertablett?

von Ralph B. (rberres)


Lesenswert?

Susanne schrieb:
> vielleicht kann mir jemand die Lösung sagen dann kann ich anhand deren
> mich in die Thematik einarbeiten. Schließlich ist noch kein Meister vom
> Himmel gefallen.

Erkläre mir doch mal bitte was es dir hilft Gleitkommazahlen zu 
bekommen.

Der 10 Bit ADC liefert nun mal exakt 1023 feste Werte. ( Dazwischen gibt 
es nichts ). Das sind meines Erachtens Integerwerte.

Wenn du daraus Gleitkommazahlen machen willst gewinnst du nicht mehr 
Auflösung.

Ralph Berres

von Threadversteher (Gast)


Lesenswert?

Ralph B. schrieb:
> Der 10 Bit ADC liefert nun mal exakt 1023 feste Werte.

Sorry, 1024 (0...1023)!

SCNR

von ms (Gast)


Lesenswert?

Hallo,

ich habe das mal so gemacht 32xADC wert gelesen (long) und addiert.
Wert durch 32 in ein float geschrieben (musste wegen genauigkeit sein).
einen UNION datentyp erstellt mit 4xchar und einen float.
float in UNION.FLOAT geschrieben und 4xchar über UART an PC.
Auf PC seite die 4 char auch in UNION.CHAR1..4 und zur Darstellung
das float genommen.

ms

von Susanne (Gast)


Lesenswert?

sprintf(String1,"LDR1: %f g \n", ADCWert1*1.0);

Das habe ich schon probiert, leider kommt dann auf meiner Konsole der 
Wert:"?" an

irgendwas ist komisch, ich weiß nur noch nicht was...

von ms (Gast)


Lesenswert?

Hallo Susanne

teste das mal

#include <stdio.h>
#include <math.h>
int main(void)
{
   char buffer[80];
   sprintf(buffer, "An approximation of Pi is %f\n", M_PI);
   puts(buffer);
   return 0;
}


ms

von ms (Gast)


Lesenswert?

oder das

float tmp_1;
char string[20];


tmp_1 = (1.271 * ADCW); //ADC Wert multiplizieren
dtostrf(tmp_1,1,3,string);

der ADC-Wert wird immer ganzzahlig sein.
Bei meinen Sensoren gibt es immer ein Datenblatt mit Werten
zur umrechnung in einen anderen Einheit (Volt in bar)
Meist ist das eine einfache geradengleichung(y=m*x+b)
daraus ergeben sich automatisch Fließkommawert

ms

von Susanne (Gast)


Lesenswert?

was bedeutet das ganeu?
"double to String Fließkommazahl" dtostrf temperatursensor1

was bedeutet aber das 1,3?

Ausgabe als String.

dtostrf(tmp_1,1,3,string);

von Sherlock Holmes (Gast)


Lesenswert?

Susanne schrieb:
> was bedeutet das ganeu?

Es reicht .... ich beschliesse:

----- das ist ein Troll Thread -----

So dämlich kann man sich nicht anstellen.

Bitte füttert den Troll nicht noch weiter.

von W.A. (Gast)


Lesenswert?

Susanne schrieb:
> was bedeutet aber das 1,3?

Das Problem hatten wir doch "neulich" gerade:
Beitrag "dtostrf funktion allgemein"

von Susanne (Gast)


Lesenswert?

Ich weiß was eine Funktion ist.
Ich weiß aber nicht für was das 1,3 steht.

von ms (Gast)


Lesenswert?

Hallo Susanne,

http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__stdlib_1ga060c998e77fb5fc0d3168b3ce8771d42.html

1. ADC ist immer Ganzzahlig.
2. Diese Zahl musst Du umrechnen in Lux,Lummen oder was auch immer.
3. das Ergebnis must du in ASCII umwandeln mit dtostrf oder sprintf

Hast Du einen JTAG?

Bei meiner Ausbildung habe ich immer die Simulation vom AVR-Studio
verwendet (sichwort Breakpoint).

Ich könnte Dir die Lösung anbieten aber das ist nicht das Ziel.

ms

von Sherlock Holmes (Gast)


Angehängte Dateien:

Lesenswert?

Susanne schrieb:
> Ich weiß aber nicht für was das 1,3 steht.

von Dietrich L. (dietrichl)


Lesenswert?

Susanne schrieb:
> Ich weiß aber nicht für was das 1,3 steht.

W.A. hatte den Link doch schon genannt:
Beitrag "Re: dtostrf funktion allgemein"

Muss man Dir den Text von Karl Heinz noch vorlesen? Da steht alles drin, 
sogar mit Beispiel.
In dem Beispiel steht allerdings 7,2 und nicht 1,3. Das sollte doch 
hoffentlich nicht das Problem sein...

von ms (Gast)


Lesenswert?

Hallo Susanne,

Ich mache das immer so sobald was nicht so func wie ich das will.

1.BinTerm runterladen und Parameter einstellen.
2.In Main einen definierten Text in einer endlos loop schicken
Kommt der Text i.O. am PC an ist die Peripherie in Ordnung.

danach mit einer funktion rumspielen (dtostrf) und selber schluesse
daraus ziehen.Das vergisst du nie.

ms

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.