Forum: Mikrocontroller und Digitale Elektronik Berechnung mit Analogwert


von EagleEye (Gast)


Lesenswert?

Hallo zusammen,

ich habe ein Problem bei einer Rechnung mit einem analogen Messwert.

Folgender Hintergrund: Eingesetzt wird ein Atmega16 sowie ein IR-Sensor. 
Über einen analogen Eingang X1 wird der Spannungswert des IR-Sensors 
eingelesen. Dieser muss nun zur Weiterverarbeitung umgerechnet werden in 
einen entsprechenden Abstand in Metern. Die Formel dazu lautet: 
((x+0.28136)/10.8429)^(-1/0.63745), wobei x den analogen Spannungswert 
darstellt.

Hier mein Quellcode:

#include <nibobee/iodefs.h>
#include <nibobee/delay.h>
#include <nibobee/analog.h>
#include <nibobee/math.h>
#include <nibobee/cdefs.h>
int main() {
// ***Initialisierung***
  enable_interrupts();
  uint16_t R;
  double Abstand;
  analog_init();
  sei();

  while(1==1) {
  R = analog_getValue(ANALOG_EXT0);

  // Umrechnung von [V] in [m]
  Abstand = 0.2 - (pow((R+0.28136)/10.8429, (-1/0.63745)))/100;
  }
  return 0;
}

Das Einlesen des analogen Messwertes funktioniert soweit, wenn ich 
allerdings versuche den Abstand zu berechnen streikt der Compiler "Build 
failed with 1 errors...". Eine genauere Fehlermeldung gibt es leider 
nicht.
Ersetzt man "R = analog_getValue(ANALOG_EXT0);" durch einen beliebigen 
Wert z.B. "R =240;" funktioniert es.

Was mache ich falsch?

MfG

von Krapao (Gast)


Lesenswert?

In dem gezeigten Quellcode ist nirgends eine Variable oder ein Makro 
ANALOG_EXT0 definiert. In die Includefiles kann ich nicht reinsehen. 
Wenn dort auch keine Definition steht, ist es klar, dass die Stelle R = 
analog_getValue(ANALOG_EXT0); einen Übersetzungsfehler produziert.

In dem gezeigten Quellcode ist nirgends eine Funktion analog_getValue 
definiert. In die Includefiles und die Projekteinstellungen kann ich 
nicht reinsehen. Wenn dort auch keine Definition steht oder im Projekt 
keine Datei (C oder Library) vorhanden ist in der die Funktion 
analog_getValue implementiert ist, ist es klar, dass die Stelle R = 
analog_getValue(ANALOG_EXT0); einen Übersetzungsfehler produziert.

von Christian G. (christian_g83)


Lesenswert?

EagleEye schrieb:
> Eine genauere Fehlermeldung gibt es leider
> nicht.

Was ist denn das für ein Compiler?

Abgesehen davon: bist Du dir sicher, dass die Funktion analog_getValue 
wirklich einen Wert in Volt zurückliefert und nicht Inkremente?

von Karl H. (kbuchegg)


Lesenswert?

EagleEye schrieb:

> allerdings versuche den Abstand zu berechnen streikt der Compiler "Build
> failed with 1 errors...". Eine genauere Fehlermeldung gibt es leider
> nicht.

Klick mal ins Ausgabefenster und scrolle nach oben.
Da ist mit Sicherheit irgendwo eine Fehlermeldung.

von EagleEye (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

erstmal Danke für die schnelle Rückmeldung!
@ Krapao:
Die Funktion "analog_getValue(ANALOG_EXT0);" ist in den eingebundenen 
Dateien analog.h und cdefs.h (siehe PFDs im Anhang) definiert, sowohl 
die Funktion "analog_getValue()" als auch die Variable "ANALOG_EXT0". 
ANALOG_EXT0 ist, wenn ich das richtig verstanden habe nur ein Zeiger auf 
die Adresse des Portpins 1 des Ports X1.
Im Datenblatt des Atmega16 auf S.216 
(http://www.atmel.com/Images/doc2466.pdf) steht, dass die Ausgabe nach 
folgender Formel erfolgt:

ADC = (Vin*1024)/Vref

Das liefert mir also die Werte 0 bis 1024 (immer in Bezug auf die 
Versorgungsspannung Vref von ca. 5V).
Das Erfassen der Analogwerte funktioniert. Ich habe probeweise einfach 
mal ein kleines Programm geschrieben, das mir entsprechend des 
Rückgabewertes R verschiedene LEDs einschaltet, also z.B. bis 2V 
(ADC-Wert R = 409) LED1 bis 2,1V (ACD-Wert R = 431) LED2 usw.:

****************************
#include <nibobee/iodefs.h>
#include <nibobee/led.h>
#include <nibobee/delay.h>
#include <nibobee/analog.h>
#include <nibobee/math.h>
#include <nibobee/cdefs.h>
int main() {
// ***Initialisierung***
  enable_interrupts();
  int16_t R;
  analog_init();
  sei();
  while(1==1) {
    U = 0;
  R = analog_getValue(ANALOG_EXT0);
  if(R < (409)){led_set(0,0); led_set(1,0); led_set(2,0); led_set(3,1);}
  if(R <(431) && R > (409)){led_set(0,0); led_set(1,0); led_set(2,1); 
led_set(3,0);}
  if(R <(460) && R > (431)){led_set(0,0); led_set(1,0); led_set(2,1); 
led_set(3,1);}
  if(R <(490) && R > (460)){led_set(0,0); led_set(1,1); led_set(2,0); 
led_set(3,0);}
  }
  return 0;
}
*****************************

Das hat auch funktioniert!
Mit dem Multimeter habe ich für die tatsächliche Spannung am Port in 
Bezug auf Masse für einen Abstand von 10 cm 2,23V gemessen. Das wären 
umgerechnet nach obiger Formel R = 457 als ADC-Wert. Wenn ich den 
Abstand dann ein wenig variiert habe, haben die Leds entsprechend in der 
Reihenfolge geleuchtet. Damit war für mich klar, dass ich einen 
Integer-Wert zwischen 0 und 1024 in Bezug auf die Referenzspannung 
erhalte.
In der Datei analog.h wird die Funktion "analog_getValue" folgendermaßen 
definiert
uint16_t analog_getValue(uint8_t idx);
Der Rückgabewert der Funktion sollte also ein unsigned interger sein.
Jetzt zur Berechnung der Potenzfunktion pow(). Diese ist in math.h 
(siehe PDF im Anhang) folgendermaßen definiert:
extern double pow __P((double, double));
Sie erwartet also als Eingabe zwei double Werte. Daher war mein erster 
Gedanke, dass es wegen der Variablen-Typen zu Problemen kommt. Testweise 
habe ich eine weitere Variable Q nacheinander als uint16_t, double, 
float definiert mit Werten belegt und an Stelle von R als ersten 
Parameter übergeben. Beim Compilieren gab es keine Fehler. Dann habe ich 
der Variable R den festen Wert 409 zugewiesen und es compiliert --> kein 
Fehler. Sobald ich R wieder den Rückgabewert der Funktion 
"analog_getValue(ANALOG_EXT0)" zugewiesen habe ist das Compilieren 
wieder fehlgeschlagen. Letzter Versuch: ich habe R weiterhin den 
Rückgabewert der Funktion zugewiesen und anschließend R über (double)R 
in eine double-Variable konvertiert. Auch hier tritt ein Fehler beim 
Compilieren auf.
Warum klappt es bei allen möglichen Variationen, nur nicht mit dem 
Rückgabewert des AD-Wandlers???

@ Christian Gudrian:
Ich verwende AVR Studio 4 in der Version 4.19 mit dem Compiler AVR GCC.

Gruß
Matthias

von EagleEye (Gast)


Lesenswert?

Und nochmal ich,

@ Karl Heinz Buchegger:

Hier die Ausgabe aus dem Ausgabefenster wenn ich das Programm 
erfolgreich mit fester Zuweisung R = 409 compiliere:

*******************************
Build started 11.5.2012 at 14:37:20
avr-gcc -I"C:\Program Files (x86)\NIBObeeLib\include"  -mmcu=atmega16 
-Wall -gdwarf-2 -std=gnu99  -D_NIBOBEE_   -DF_CPU=15000000UL -Os 
-funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP 
-MT PI_IR_Regler.o -MF dep/PI_IR_Regler.o.d  -c
 ../PI_IR_Regler.c

In file included from ../PI_IR_Regler.c:20:0:
C:\Program Files (x86)\NIBObeeLib\include/nibobee/math.h:12:0: warning: 
ignoring #pragma ident
In file included from ../PI_IR_Regler.c:21:0:
C:\Program Files (x86)\NIBObeeLib\include/nibobee/cdefs.h:32:0: warning: 
"__P" redefined
C:\Program Files (x86)\NIBObeeLib\include/nibobee/math.h:19:0: note: 
this is the location of the previous definition
avr-gcc -mmcu=atmega16 -Wl,-Map=PI_IR_Regler.map PI_IR_Regler.o 
-L"C:\Program Files (x86)\NIBObeeLib\lib"  -lnibobee_line -lnibobee_base 
-lnibobee_utils  -o PI_IR_Regler.elf
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature 
PI_IR_Regler.elf PI_IR_Regler.hex
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" 
--change-section-lma .eeprom=0 --no-change-warnings -O ihex 
PI_IR_Regler.elf PI_IR_Regler.eep || exit 0
avr-objdump -h -S PI_IR_Regler.elf > PI_IR_Regler.lss

AVR Memory Usage
----------------
Device: atmega16

Program:    1466 bytes (8.9% Full)
(.text + .data + .bootloader)

Data:         29 bytes (2.8% Full)
(.data + .bss + .noinit)


Build succeeded with 2 Warnings...
***************************************


Und hier wenn ich R wieder den Rückgabewert der Funktion 
analog_getValue() zuordne:

***************************************
Build started 11.5.2012 at 14:34:46
 ../PI_IR_Regler.c

In file included from ../PI_IR_Regler.c:20:0:
C:\Program Files (x86)\NIBObeeLib\include/nibobee/math.h:12:0: warning: 
ignoring #pragma ident
In file included from ../PI_IR_Regler.c:21:0:
C:\Program Files (x86)\NIBObeeLib\include/nibobee/cdefs.h:32:0: warning: 
"__P" redefined
C:\Program Files (x86)\NIBObeeLib\include/nibobee/math.h:19:0: note: 
this is the location of the previous definition
avr-gcc -mmcu=atmega16 -Wl,-Map=PI_IR_Regler.map PI_IR_Regler.o 
-L"C:\Program Files (x86)\NIBObeeLib\lib"  -lnibobee_line -lnibobee_base 
-lnibobee_utils  -o PI_IR_Regler.elf
collect2: ld returned 1 exit status
make: *** [PI_IR_Regler.elf] Fehler 1
Build failed with 1 errors and 2 warnings...
****************************************

Ich kann daraus nicht ersehen, wo genau das Problem liegt...

Gruß
Matthias

von Bernhard S. (b_spitzer)



Lesenswert?

Puuuh, der arme Controller...
Ich nehme mal an, bei dem Sensor handelt es sich um einen Sharp für 
20-80cm (GP2D12 und Nachfolger). Wie um alles in der Welt kommt man auf 
die Idee, Exponential-Funktionen mit gebrochenem Exponenten auf einem 
8-Bit Controller rechnen zu wollen.
Die Umkehrfunktion für die Sensoren lässt sich mit 3-4 Geradenstücken 
linearisieren, die man sogar komplett ganzzahlig rechnen kann. Die 
Pseudo-Genauigkeit die Dir float vorgaukelt, bekommst Du doch niemals 
aus dem Sensor raus. Dessen Kennlinie im Datenblatt ist doch auch nur 
durch Geradenstücke angegeben (die Knicke erkennt man bei genauem 
hinsehen).
Aus dem Datenblatt liest Du dir ein paar Stützstellen aus der Kennlinie 
heraus (oder lässt Excel die Stütztstellen im 5cm-Abstand berechnen). 
Mit jeweis 2 Stützstellen wird eine Geradengleichung für die 
Umkehrfunktion Abstand = f' (Digitalwert) erstellt. Eine Abfrage des 
Digitalwertes entscheidet dann, welche Geradengleichung für den 
Abschnitt gilt. An den Übergangsstellen ergeben beide Geraden den selben 
Wert, es ist also egal ob man da auf > oder >= vergleicht.
Bei deinen Vergleichen im 2. Beispiel ist auch noch 
Optimierungspotential... Wenn der AD-Wert größer als die erste Schwelle 
ist, dann sind die übrigen Vergleich hinfällig. Daher nimmt man da else 
if:
 if(R > 490){led_set(0,0); blablabla...}  // für alle Werte über 490
 else if(R > 460){led_set(0,0); bläbläblä...} // von 461 bis 490
 else if(R > 431){led_set(0,0); usw.usw.usw.} // von 432 bis 460
 else   // ales Werte kleiner gleich 431
 { }

Für die Erstellung einer Geradengleichung habe ich mal was angehängt. 
Der Trick mit der höheren Genauigkeit ist, dass man einfach nicht in cm 
sondern in mm rechnet. (sogar mit ganzzahlingen Long-Werten in µm wird 
das Programm noch schneller und kleiner als mit Float).

tschuessle
Bernhard

von Christian G. (christian_g83)


Lesenswert?

EagleEye schrieb:

> make: *** [PI_IR_Regler.elf] Fehler 1

Steht bei dir im Makefile irgendwo ein .SILENT oder verschluckt sonst 
noch jemand irgendwelche Konsolen-Meldungen?

Man sieht nur, dass dem Linker irgend etwas nicht gefällt. Er sagt 
bestimmt auch, was -- nur sieht man das nirgendwo.

von EagleEye (Gast)


Angehängte Dateien:

Lesenswert?

Hey,

@ Bernhard:

Danke für die Hinweise! Bin eben was µC-Programmierung belangt ein 
blutiger Anfänger. Die Annäherung mit Geradenstücken wäre auch mein 
nächster Schritt gewesen, ich dachte nur, es ginge (für mich) einfacher 
durch Einprogrammieren der Umkehrfunktion des Sensors. Das das nicht die 
rechnerisch schnellste Lösung für den µC ist leuchtet mir ein ;). Ich 
werde die Alternative mit den Geraden jetzt mal versuchen umzusetzen!

Prinzipiell interessiert es mich aber schon, warum das nicht 
funktioniert. Hier kann ich mir vllt mit der Approximation durch Geraden 
helfen, aber es könnte ja auch mal vorkommen, dass ich die 
Potenzfunktion unbedingt brauche?

@ Christian Gudrian:

Also ich habe die Makefile-Datei mal in den Anhang gehängt, ich finde 
dort aber kein .SILENT.
Und sonstige Ausgaben, Hinweise, Fehlermeldungen sehe ich wie gesagt 
auch nicht???

Gruß
Matthias

von Karl H. (kbuchegg)


Lesenswert?

EagleEye schrieb:

> Also ich habe die Makefile-Datei mal in den Anhang gehängt, ich finde
> dort aber kein .SILENT.
> Und sonstige Ausgaben, Hinweise, Fehlermeldungen sehe ich wie gesagt
> auch nicht???

Eigenartig.

Aber grundsätzlich fehlt mir da bei den Linkerlibraries ein -lm um die 
Math-Library einzubinden. Da du Funktionen wie pow() benutzen willst, 
wirst du die brauchen
1
...
2
## Libraries
3
LIBS = -lnibobee_line -lnibobee_base -lnibobee_utils -lm

von Bernhard S. (b_spitzer)


Lesenswert?

EagleEye schrieb:
> Hier kann ich mir vllt mit der Approximation durch Geraden
> helfen, aber es könnte ja auch mal vorkommen, dass ich die
> Potenzfunktion unbedingt brauche?
Man kann auch mit vielen Stützstellen aus einem Array arbeiten und dann 
dazwischen interprolieren. Das ganze natürlich nur Ganzzahlig :-)

tschuessle
B.

von EagleEye (Gast)


Lesenswert?

Hallo,

Danke nochmal für die flotte Hilfe, habe das ganz nun über die 
Linearisierung der Exponentialfunktion gelöst, funktioniert einwandfrei.

Meine Frage ist damit gelöst und der Thread kann geschlossen werden.

Gruß
Matthias

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.