Forum: Compiler & IDEs AD- Wandler für Atmega 128


von Mangosniper (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich möchte einen ADC für den Atmega 128 programmieren (er soll die 
Spannungswerte die er intern misst auf dem Computer über den UART1 
ausgeben)
Das Problem ist, dass der Prozessor zwar Werte aus gibt, diese aber 
Zeichen aus der Ascii- Tabelle ausgibt.

Hier ist mal ein Ausschnitt aus dem Quellcode meines Programmes:
1
/* Init ADC*/
2
  
3
  ADMUX |= (1 << REFS1);
4
  ADMUX |= (1 << ADLAR);
5
  ADMUX |= (1 << MUX3);     // mit gain 10x
6
//  ADLAR |= (1 << ADC1);
7
8
  ADCSRA |= (1 << ADEN);
9
  ADCSRA |= (1 << ADSC);
10
  ADCSRA |= (1<< ADPS1);
11
  ADCSRA |= (1<< ADPS2);
12
13
//  for(x = 0; x < 5; x++){
14
  while ( ADCSRA & (1<<ADSC) )  //warten bis ADSC Bit gelöscht wird
15
    {
16
      ;
17
   }
18
  
19
  spannung = ADCW;
20
21
  // warte bis TX bereit
22
  while (!(UCSR1A & (1 << UDRE1)))
23
    ;
24
25
  UDR1 = spannung;

Im Anhang befinde sich die Ausgabe des HyperTerminals.

von der mechatroniker (Gast)


Lesenswert?

Zwei Möglichkeiten:

1. Terminalprogramm benutzen, das Binärwerte anzeigen kann (etwa HTerm).
2. Auf dem uC die Werte vor dem Versenden durch utoa schicken.

von Gast (Gast)


Lesenswert?

Du musst mit "itoa" die Zahl in einen String umwandeln und dann bessen 
Zeichen einzeln senden.

von Bernd (Gast)


Lesenswert?

Der falsche Ausschnitt, wie sieht die Ausgabe denn aus ?

von der mechatroniker (Gast)


Lesenswert?

> Der falsche Ausschnitt, wie sieht die Ausgabe denn aus ?

na ja, so:
1
UDR1 = spannung;

hat er doch gepostet.

von Bernd (Gast)


Lesenswert?

Aha :-) wie wärs die Zahl in nen String zu zerlegen und + 30 dann ist es 
ASCII.

AD Wert 12345

/ 10 = erste Stelle usw. Dann + 30 = ASCII.

Das wars.

von Karl H. (kbuchegg)


Lesenswert?


von Mangosniper (Gast)


Lesenswert?

Ok das Programm funktioniert soweit das ich einen Wert im Hyperterminal 
ausgegeben bekomme( für angelegte Spannung 1,9V z.b. 0644).
Jetzt wäre es natürlich schöner den Wert z.b als 1,9 auszugeben.
Die dafür nötige Rechnung ist nach Vin umgestellt:

(ADC * Vref) / 1024 = Vin

Vref ist laut Datenblatt 2,56V gemessen habe ich allerdings 3,11V und 
die Rechnung stimmt auch nur mit 3,03V daher nehme ich diesen Wert.

nehmen wir den Wert von 1,9V = 0644

(644 * 3,03) / 1024 = 1,9055859375

soweit so gut, jetzt noch den Wert in ein float speichern.

Aber wie kann ich den Wert jetzt ausgeben?

Wieder in ein Array speichern und ausgeben wäre ziemlich umständlich da 
ich nie weiß wieviele Nachkommstellen der Wert hat.

Hier noch der Code:
1
/* Einbeziehen von Header- Dateien*/
2
#include <avr/io.h>
3
4
#include <inttypes.h>
5
#include <stdlib.h>
6
#ifndef F_CPU
7
8
#include <string.h>
9
#include <stdio.h>
10
11
#define F_CPU 8000000UL    //Taktfrequenz Atmega
12
13
#endif
14
15
16
//-----------------------------------------------
17
18
19
/*Init Variables*/
20
float spannung;
21
float Z_array;
22
float V_mess;
23
int counter;
24
unsigned char array[4]; // Array für den Spannungswert der aus maximal 4 Zeichen besteht (0-1023)
25
            // anlegen.
26
int zerlegen(void)
27
{
28
  
29
30
  memset ( array, '\0', sizeof(array));
31
  spannung = ADCW;    //ADCW-Wert in die Wariable Spannung aufnehmen
32
  
33
  ///////////////////////////////////////////////////////////////////////////////////////////////
34
  //Dieser Teil des Codes ist dafür zuständig die Variable spannung in 4 Zeichen aufzuteilen
35
  //und in das Array zu legen. Bei angelegter Spannung 1,9V (ADCW-Wert : 0644)
36
  //z.B. 0 in Array[0] 6 in Array[1] 4 in Array[2] und in Array[3] 4
37
  //Damit im Hyperterminal die Zahlen korrekt angezeigt werden, 48 addieren um in der 
38
  //ASCII-Tabelle auf Zahlenwerte zu kommen
39
  ///////////////////////////////////////////////////////////////////////////////////////////////
40
41
  
42
  if( spannung > 1000) 
43
  {
44
    array[0] = 1 + 48;
45
    spannung -= 1000;
46
  
47
  }
48
  else
49
  {
50
    array[0] = 0 + 48;
51
    
52
  }
53
  UDR1 = array[0];
54
  //*************************************
55
56
  while( spannung >= 100)
57
  {
58
    if( spannung >= 100)
59
    {
60
      spannung -= 100;
61
      counter += 1;
62
    }
63
  }
64
  array[1] = counter + 48;
65
        
66
  counter = 0;
67
  UDR1 = array[1];
68
69
  //************************************
70
  
71
  while (spannung >= 10)
72
  {
73
    if( spannung >= 10)
74
    {
75
      spannung -= 10;
76
      counter +=1;
77
    }
78
  }
79
  array[2] = counter + 48;
80
  counter = 0;
81
  UDR1 = array[2];
82
  
83
  //************************************
84
85
  while (spannung >= 1)
86
  {
87
    if( spannung >= 1)
88
    {
89
      spannung -= 1;
90
      counter +=1;
91
    }
92
  }
93
  array[3] = counter + 48;
94
  UDR1 = array[3];
95
   
96
  
97
  Z_array = ((array[0] - 48) * 1000) + ((array[1] - 48) * 100) + ((array[2] - 48)* 10) + array[3];
98
99
  V_mess = (Z_array * 3,02) / 1024;
100
101
  /////////////////////////////////////////////////////////////////////////////////////////////
102
  //Hier müsste jetzt der Teil des Codes kommen um den Wert von V_mess an das Hyperterminal
103
  //weiterzugeben.
104
  /////////////////////////////////////////////////////////////////////////////////////////////
105
106
    // UDR1 = V_mess; geht natürlich nicht.Wieder in ein Array packen kann ich den Wert auch nicht.
107
  // da er viele Nachkommastellen hat die mitangezeigt werden müssen (jedenfalls mind. 2-3)  
108
}
109
110
111
112
int main (void)
113
{
114
115
116
  UCSR1B = 0;  //USART1 Interrupts OFF
117
118
  /*init BAUD*/
119
120
  UBRR1H = 51 >>8;    //9600 Baud @ 8MHz
121
  UBRR1L = 51 & 0xFF;
122
  
123
  /*init UART*/  
124
125
  UCSR1C |= (1 << UCSZ10);
126
  UCSR1C |= (1 << UCSZ11);
127
128
  UCSR1B &= (0 << UCSZ12);
129
  UCSR1B |= (1 << TXEN);
130
    
131
  /* Init ADC*/
132
  
133
  ADMUX |= (1 << REFS1);
134
  ADMUX |= (1 << REFS0);
135
136
137
  ADCSRA |= (1 << ADEN);
138
  ADCSRA |= (1 << ADSC);
139
140
  ADCSRA |= (1<< ADPS1);
141
  ADCSRA |= (1<< ADPS2);
142
143
  PORTF = 0x01;
144
145
  while ( ADCSRA & (1<<ADSC) )  //warten bis ADSC Bit gelöscht wird
146
    {
147
      ;
148
   }
149
  
150
  
151
152
  // warte bis TX bereit
153
  while (!(UCSR1A & (1 << UDRE1)))
154
    ;
155
156
  
157
  zerlegen();
158
  
159
}

von Karl H. (kbuchegg)


Lesenswert?

Wenn du das in deinem Eröffnungsposting gezeigt hätte, hätte ich nichts 
gesagt. Aber nach dem Hinweis auf itoa bzw. dem Link in die FAQ ist die 
Funktion zerlegen() ja wohl ein Hohn auf alle die dir geholfen haben.

Fang mal damit an, dir eine Funktion zu schreiben, die 1 Zeichen 
ausgibt. Und zwar nach den Regeln der Kunst und nicht nur indem auf gut 
Glück ein Zeichen an UDR1 zugewiesen wird.

Dann machst du eine Funktion, die einen String ausgeben kann. Da kommt 
dir jetzt die eben geschrieben Funktion für Einzelzeichen zu gute.

Und dann schreibst du dir eine Funktion die einen int ausgeben kann. 
Dazu benutzt du eine Umwandlungsfunktion, die dir den Zahlenwert in 
einen String umwandelt und gibst diesen mit der String-Ausgabefunktion 
aus.

Das alles lässt sich in 3 Funktionen abbilden, von denen keine einzige 
mehr als 4 Zeilen Code umfasst und universell einsetzbar ist.

Man kann dein Problem natürlich auch mit Floating Point lösen. Man muss 
es aber nicht. Das Stichwort heisst Fixpunktarithmetik. Ob du 
Geldbeträge als Kommazahlen in Euro zusammenzählst oder ob du das mit 
ganzen Zahlen in Cent machst, kommt aufs gleiche raus. Wenn du die 
Centbeträge hast und zwischen die 2.te und 3.te Stelle bei der Ausgabe 
ein , einschmuggelst, dann merkt kein Benutzer, dass du in Wirklichkeit 
nicht mit Kommazahlen gerechnet hast

    1,20 €           120 Cent
  + 3,40 €           340 Cent
  + 5,60 €           560 Cent
  ------           ----------
   10,20 e          1020 Cent

                      |
                      |  Zur Anzeige ein , zwischen Stelle 2
                      |  und Stelle 3
                      v

                    10,20

> Wieder in ein Array speichern und ausgeben wäre ziemlich umständlich
> da ich nie weiß wieviele Nachkommstellen der Wert hat.

Er hat soviele Stellen wie du gewillt bist auszugeben.

> nehmen wir den Wert von 1,9V = 0644
> (644 * 3,03) / 1024 = 1,9055859375

Das ist ziemlich sinnlos.
Dein ADC löst in 1024 Stufen aus, kann daher eine Spannung auf 
1/1024*Referenzspannung  = 0.0029 Volt (also rund 0.003V) auflösen.
Es ist daher völlig sinnlos, die Ausgabe auf 10 Nachkommastellen zu 
machen. Alles nach der 3ten Nachkommastelle ist sowieso gelogen. Und ob 
die Tausendstel stimmen, ist mehr als fraglich. Man hat ja schliesslich 
auch Messfehler, die im Ergebnis auftauchen.

Nur weil ein Taschenrechner auf 10 Stellen nach dem Komma rechnen kann, 
heist das noch lange nicht, dass so ein Ergebnis auch Sinn macht. Das 
ist wie mit den Meinungsforschern, die vor einer Wahl ihre Prognose am 
liebsten auf 2 Nachkommastellen veröffentlichen, basierend auf einer 
Befragung von 100 Leuten :-) Wenn von den 100 nur 1 anders antwortet, 
ändert sich das Ergebnis um einen ganzen Prozentpunkt, aber sie geben 
ihre Hochrechnung auf 2 Nachkommastellen an :-)

von Bernd (Gast)


Lesenswert?

>> Vref ist laut Datenblatt 2,56V gemessen habe ich allerdings 3,11V und
die Rechnung stimmt auch nur mit 3,03V daher nehme ich diesen Wert.

Hast du dir mal überlegt wo dieser Fehler nun begründet ist ?

AD Wandler haben Offset, Gain, AREF Fehler um mal bei den linearen 
Fehlern zu bleiben. Den Offset solltest du dir mal genauer anschauen und 
auch bestimmen können.

von Bernd (Gast)


Lesenswert?

Aller Anfang ist schwer aber nehm mal den Tipp von Karl Heinz an. 
Schreib dir mal das Ganze in ein paar Funktionen. Eine gebe ich dir vor 
aber du erklärst sie mir dann :-)
1
void AdcValOut (uint16_t AdcValue)
2
{
3
    uint8_t AdcValArray [5];                  // Integer Array
4
    uint8_t m = 4;                            // Schleifenzähler für Ausgabe
5
6
    do  {
7
        AdcValArray [m] = '0' + AdcValue %10; // Berechnung der Stellen
8
        AdcValue /= 10;                       // und Wandlung nach ASCII
9
    } while (m--);
10
    while (m != 3)  {
11
        UartPutC (AdcValArray [++m]);         // Integer Wert ausgeben
12
        if (m == 0) UartPutC ('.');           // Dezimalpunkt setzen
13
    }
14
    UartPutC ('\r');

Wenn man auf ein Phänomen trifft dann sollte das zum nachdenken 
auffordern.

von Mangosniper (Gast)


Lesenswert?

Ich denke ich hab im Hinblick auf meine momentanen Kentnisse etwas zu 
sehr nach den Sternen gegriffen.

Ich werde mich jetzt erstmal noch etwas genauer mit den einzelnen Teilen 
des späteren Programms auseinandersetzen.(sprich: Ausgaben über den 
UART)

Ich komme dann später auf den Thread zurück wenn ich mich gewappnet 
genug fühle^^

Vielen Dank an alle die mir geholfen haben, aber ich denke, dass ich mit 
dem Programm im ganzen erst wieder etwas später beschäftigen.

von Karl H. (kbuchegg)


Lesenswert?

Mangosniper schrieb:
> Ich denke ich hab im Hinblick auf meine momentanen Kentnisse etwas zu
> sehr nach den Sternen gegriffen.

Das ist nicht schlimm.
Das passiert vielen, dass sie die Schwierigkeit der Programmierung 
unterschätzen. Massiv unterschätzen.

Wichtig ist nur, dass du auf den Boden der Tatsachen zurückkommst und 
das Zeugs von der Pieke auf lernst. Wir alle hier wissen, dass das am 
Anfang nicht einfach ist. Es gibt einfach zu vieles, das gleichzeitig 
berücksichtigt werden muss. Am besten lernt mal alles gleichzeitig. Aber 
das geht nun mal nicht.

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.