Forum: Mikrocontroller und Digitale Elektronik ATMega32, Falsche Ausgabe vom AD-Wandler


von Mike M. (mikehochdrei)


Angehängte Dateien:

Lesenswert?

Moin Moin,

ich bastel seit geraumer Zeit (3-4 Wochen) mit einem ATMega32 auf einem 
STK-500 herum. Zum programmieren benutze ich das AVR Studio 4.

Soviel dazu... nun zu meinem Problem:

Der unten angehängte Code soll im Grunde genommen nur die Spannung, den 
Strom, die Leistung und den Widerstand mit Hilfe des integrierten ADC 
angeben.
Leider misst er derzeit völlig falsch (teilweise totaler Daten-quatsch), 
und ich weiß auch nicht mehr woran es liegen kann. Obwohl ich, mit genau 
diesem Code, vor ein paar Tagen noch wunderbar die vier Sachen (mit 
leichten Einschränkungen) messen konnte.

Zur Schaltung: Ich habe einen Poti mit max. 25k und einen Widerstand mit 
4.7k in Reihe geschaltet. An Pin A1 vom µC messe ich die Gesamtspannung 
und am Pin A4 den Spannungsabfall über dem Widerstand. Mit hilfe des 
Widerstandes berechne ich den Strom.

Evtl. sieht ja jemand einen Fehler, der mir entgangen ist. Oder ich 
mache grundlegend etwas verkehrt.

Das ist mein erstes Projekt in dieser Richtung, deswegen sollten dem 
einen oder anderem bestimmte Zeilen im Code aus dem Forum hier bekannt 
sein. Sollte irgendwas völlig schief gelaufen oder der Beitrag nicht 
direkt verständlich sein, dann bitte fragen und nicht auf mich rumholzen 
:)

Lieben Gruß
Mike


EDIT:

Spannung, Strom und Leistung habe ich wieder hinbekommen.
Fehler: Eins der Kabel hatte sich ein wenig selbstständig gemacht...
Jedoch nach wie vor berechnet er den Widerstand verkehrt.

von Karl H. (kbuchegg)


Lesenswert?

Wenn du die Messwerte in Verdacht hast, dann lass dir die Werte vom ADC 
mal direkt (ohne Umrechnung) ausgeben.


1
    while(loop1==1)
2
    {
3
       wert = 0.004887585*analogwert;       //  5/1023=0.00488...
4
       spannung = wert * 100;
5
       itoa( spannung, wert3, 10 );
6
       if(spannung >= 100){
7
       UDR = wert3[0];
8
       UDR = ',';

Heeee! Du kannst doch nicht einfach Bytes ins UDR stopfen wie du lustig 
bist!

Warum machst du dir nicht einfach erst mal ein paar UART 
Basisfunktionen.
* Eine Funktion, die ein einzelnes Zeichen ausgibt
* Darauf aufbauend eine Funktion, die einen String ausgibt
* Darauf wiederrum aufbauend eine Funktion, die einen Fixpoint-Wert 
ausgibt (und da das bei dir anscheinend nicht besonders zeitkritisch 
ist, könnte man sprintf zur Codevereinfachung einsetzen)
1
/* Ein einzelnes Zeichen ausgeben */
2
void uart_putc( char c )
3
{
4
  while (!(USR & (1<<UDRE)))  /* warten bis Senden moeglich */
5
    ;
6
 
7
  UDR = c;
8
}
9
10
/* Einen String ausgeben */
11
void uart_puts( const char* string )
12
{
13
  while( *string )
14
    uart_putc( *string++ );
15
}
16
17
/* Eine Fixed Point Zahl ausgeben. Die Zahl wird mit 2 Nachkommastellen
18
   angenommen
19
*/
20
void uart_putn( int FixedPointNumber )
21
{
22
  char buffer[10];
23
24
  sprintf( buffer, "%d,%02d" ), FixedPointNumber / 100, FixedPointNumber % 100 );
25
  uart_puts( buffer );
26
}
27
28
int main()
29
{
30
  ....
31
32
33
     wert = .....
34
     uart_putn( wert );
35
     uart_puts( "mA" );
36
     ...

Dein ganzes Hauptprogramm schrumpft dann zu
1
int main(void)
2
{
3
4
   uint16_t analogwert;
5
   uint16_t analogwert2;
6
   uint16_t spannung ;
7
   uint16_t strom ;
8
   uint16_t leistung ;
9
   uint16_t ohm;
10
   float    wert;
11
   float    wert4;
12
   char     buffer[20];
13
14
15
   // USART initialisieren
16
  uart_init();
17
18
  while(1)
19
  {
20
    analogwert2 = ReadChannel(4);
21
    analogwert = ReadChannel(1);
22
    analogwert = analogwert - analogwert2;
23
24
    wert = 0.004887585*analogwert;       //  5/1023=0.00488...
25
    spannung = wert * 100;
26
    uart_putn( spannung );
27
    uart_puts( "V " );
28
29
    wert = 0.004887585*analogwert2;       //  5/1023=0.00488...
30
    wert4 = wert * 0.2127;
31
    strom = wert4 * 100000;
32
    uart_putn( strom );
33
    uart_puts( "mA " );
34
35
    leistung = (spannung*0.01) * (strom*0.01);
36
    uart_putn( leistung );
37
    uart_puts( "mW " );
38
39
    strom2 = strom;
40
    ohm = spannung / (strom2*0.000001);
41
    sprintf( buffer, "%d", (int)ohm );
42
    uart_puts( buffer );
43
    uart_puts( "Ohm" );
44
  }
45
}

und das ist dann gleich eine ganze Ecke übersichtlicher, als das 
Code-Monster das du da hast.


Hier
1
       wert = 0.004887585*analogwert2;       //  5/1023=0.00488...
2
       wert4 = wert * 0.2127;
3
       strom = wert4 * 100000;
gehts du ein bischen naiv an die Sache ran. Schnapp dir einen 
Taschenrechner und rechne die Konstanten alle zusammen und mach nur 1 
Multiplikation.

von Mike M. (mikehochdrei)


Lesenswert?

Erst einmal Dankeschön für deine Bemühung und für die schnelle Antwort.

Das ich nicht beliebig viel Kram in UDR rein packen kann, musste ich 
leider auch merken... Jedoch wusste ich nicht, wie ich mir anders helfen 
sollte.

Erstaunlich wie klein der Text geworden ist!
Das hätte ich nach meinem jetzigen Wissensstand niemals so hinbekommen.
Dankeschön nochmal!

Ich werde das bei mir jetzt alles nach deinen Vorschlägen editieren und 
würde mich danach nochmal melden.

Bis dahin...

Lieben Gruß
Mike

von Karl H. (kbuchegg)


Lesenswert?

Mike M. schrieb:

> Das ich nicht beliebig viel Kram in UDR rein packen kann, musste ich
> leider auch merken... Jedoch wusste ich nicht, wie ich mir anders helfen
> sollte.

Das avr-gcc-Tutorial ist dein Freund.
Da gibt es einen Abschnitt über die UART

> Erstaunlich wie klein der Text geworden ist!
> Das hätte ich nach meinem jetzigen Wissensstand niemals so hinbekommen.
> Dankeschön nochmal!

Das Geheimnis besteht darin, sich für immer wiederkehrende 
Funktionalität eine eigene Funktion zu bauen.

Das Geheimnis besteht darin, den kompletten Code in sinnvolle Funktionen 
aufzuteilen, selbst dann, wenn die Funktion nur 1 mal aufgerufen wird.

Das Geheimnis besteht darin, mehrere zusammengehörende Codezeilen als 
'Funktionalität' zu begreifen, die es sich verdient haben, in eine 
eigene Funktion ausgelagert zu werden. Im Hauptcode sinkt dadurch die 
Zeilenzahl und man arbeitet weniger auf C-Anweisungen sondern auf der 
Aneinanderreihung von 'Funktionalitäten'.
Das ist wie in der Elektronik. Man veschaltet da auch auf logischer 
Ebene nicht mehr einen Widerstand mit einem Transistor und einem 
Kondensator. Statt dessen verschaltet man Black-Boxen. Da wird der 
Ausgang eines "Taktgenerators" in ein "Monoflop" gesteckt. Dass 
Taktgenerator und Monoflop selbst wieder aus anderen Einheiten bestehen 
ist klar, aber aus Gesamtsicht ziemlich uninteressant, wenn es darum 
geht die Funktion der kompletten Schaltung zu verstehen.

von Mike M. (mikehochdrei)


Angehängte Dateien:

Lesenswert?

nochmal danke für deine Mühe
so ich habe jetzt einen neuen Text mit deinen Funktionen zusammengesetzt 
und leider bekomme ich jetzt keine vernüftige Ausgabe
er gibt nur wirkürliche Zeichen aus
ich habe deine Funktion zum ausgeben eines einzelnen Zeichens durch eine 
andere ersetzt der er USR undecleard hat
es gibt keine Fehlermeldung aber 8 Warnungen

d:/programme/winavr/lib/gcc/../../avr/include/util/delay.h:85:3: 
warning: #warning "F_CPU not defined for <util/delay.h>"
../Test2.c:4:1: warning: "F_CPU" redefined
d:/programme/winavr/lib/gcc/../../avr/include/util/delay.h:86:1: 
warning: this is the location of the previous definition
../Test2.c: In function 'uart_putn':
../Test2.c:81: warning: implicit declaration of function 'sprintf'
../Test2.c:81: warning: incompatible implicit declaration of built-in 
function 'sprintf'
../Test2.c:81: warning: left-hand operand of comma expression has no 
effect
../Test2.c:81: warning: passing argument 2 of 'sprintf' makes pointer 
from integer without a cast
../Test2.c: In function 'main':
../Test2.c:122: warning: incompatible implicit declaration of built-in 
function 'sprintf'

Gruß Mike M.

von Karl H. (kbuchegg)


Lesenswert?

Mike M. schrieb:

> d:/programme/winavr/lib/gcc/../../avr/include/util/delay.h:85:3:
> warning: #warning "F_CPU not defined for <util/delay.h>"
> ../Test2.c:4:1: warning: "F_CPU" redefined
> d:/programme/winavr/lib/gcc/../../avr/include/util/delay.h:86:1:
> warning: this is the location of the previous definition

#include <util/delay.h>

#define F_CPU       4530000UL


Setz das F_CPU vor den include. util/delay.h benötigt das F_CPU schon 
richtig gesetzt. Das sagt ja auch schon die erste Warnung.

> ../Test2.c: In function 'uart_putn':
> ../Test2.c:81: warning: implicit declaration of function 'sprintf'

Für sprintf benötigst du noch den
#include <stdio.h>

> ../Test2.c:81: warning: incompatible implicit declaration of built-in
> function 'sprintf'
> ../Test2.c:81: warning: left-hand operand of comma expression has no
> effect
> ../Test2.c:81: warning: passing argument 2 of 'sprintf' makes pointer
> from integer without a cast

Das war mein Fehler.
Ich schrub

  sprintf( buffer, "%d,%02d" ), FixedPointNumber / 100, FixedPointNumber 
% 100 );


und anstelle die überzählige schliessende Klammer zu entfernen, hast du 
einfach noch eine öffnende dazugemacht.


sprintf will so bedient werden
1
  sprintf(   wo_soll_der_String_abegelegt_werden,
2
             nach_welchen_Formatierregeln_sollen_die_Argument_formatiert_werden,
3
             die_Argumente_selber );


> ../Test2.c: In function 'main':
> ../Test2.c:122: warning: incompatible implicit declaration of built-in
> function 'sprintf'

Ist mit dem #include <stdio.h> erledigt

von Mike M. (mikehochdrei)


Angehängte Dateien:

Lesenswert?

da bin ich leider wieder

die Wahrnungen sind jetzt alle weg
doch leider gibt er jetzt gar nichts mehr aus

habe auch mal probiert mir analogwert und analogwert2 auszugeben um zu 
gucken ob bei der Rechnung ein Fehler ist
aber auch diese Werte werden nicht ausgegeben

tut mir leid für die Mühe die ich mache

liebe Grüße

Maik M.

von Karl H. (kbuchegg)


Lesenswert?

Mike M. schrieb:

> tut mir leid für die Mühe die ich mache

kein Problem

Du hast neue Funktionen hinzubekommen und die sollte man jetzt erst mal 
durchtesten.
Neues Testprogramm
1
int main()
2
{
3
  ...
4
5
6
  while( 1 )
7
    uart_putc( 'X' );
8
}

Kommt die Ausgabe?
Wenn ja, dann gehts weiter
1
int main()
2
{
3
  ...
4
5
6
  while( 1 )
7
    uart_puts( "Hallo World\n" );
8
}

Funktioniert auch das, dann
1
int main()
2
{
3
  ...
4
5
6
  while( 1 )
7
    uart_putn( 578 );
8
}

von Mike M. (mikehochdrei)


Lesenswert?

Also die Funktionen funktionieren
bei uart_putc gibt er X aus
bei uart_puts gibt er Hallo World aus aber den Zeilenumbruch macht er 
nicht
und bei uart_putn gibt er 5,78 aus

liebe grüße

Mike M.

von Karl H. (kbuchegg)


Lesenswert?

Mike M. schrieb:
> Also die Funktionen funktionieren
> bei uart_putc gibt er X aus
> bei uart_puts gibt er Hallo World aus aber den Zeilenumbruch macht er
> nicht

Das kann durchaus ok sein.
Dann müsste das
  uart_puts( "Hallow World\r\n" );
lauten.

D.h. die UART Funktionen sind es nicht. Das ist ja schon mal was. Das 
bedeutet, dass du die UART Funktionen für Testausgaben benutzen kannst 
:-)

Neues Testprogramm
1
int main(void)
2
{
3
  ....
4
5
   // USART initialisieren
6
  uart_init();
7
8
  while(1)
9
  {
10
    uart_puts( "Starte Messung\r\n" );
11
12
    analogwert2 = ReadChannel(4);
13
    uart_putn( analogwert2 );
14
  }
15
}

Und so gehts jetzt sukzessive weiter. Es kommt immer mehr Code von 
deinem Programm dazu, bis klar ist, wo es hängt.

von Mike M. (mikehochdrei)


Lesenswert?

Es läuft!
Hatte, bevor ich die Testversion eingefügt habe, den PC neu gestartet.
Nun hab ich die ganz normale Code-Version genommen, quasi vor dem 
Test-Versuch, und siehe da: Bis auf den Zeilenumbruch funktioniert alles 
bestens :)

Großes Dankeschön an dich Karl! :)

Sollte ich demnächst nocheinmal Probleme beim programmieren haben, weiß 
ich nun, wo ich damit auftauchen kann.

mikrocontroller.net kann man definitiv weiterempfehlen.

Lieben Gruß
Mike

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.