www.mikrocontroller.net

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


Autor: Mangosniper (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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:
/* Init ADC*/
  
  ADMUX |= (1 << REFS1);
  ADMUX |= (1 << ADLAR);
  ADMUX |= (1 << MUX3);     // mit gain 10x
//  ADLAR |= (1 << ADC1);

  ADCSRA |= (1 << ADEN);
  ADCSRA |= (1 << ADSC);
  ADCSRA |= (1<< ADPS1);
  ADCSRA |= (1<< ADPS2);

//  for(x = 0; x < 5; x++){
  while ( ADCSRA & (1<<ADSC) )  //warten bis ADSC Bit gelöscht wird
    {
      ;
   }
  
  spannung = ADCW;

  // warte bis TX bereit
  while (!(UCSR1A & (1 << UDRE1)))
    ;

  UDR1 = spannung;        


Im Anhang befinde sich die Ausgabe des HyperTerminals.

Autor: der mechatroniker (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Gast (Gast)
Datum:

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

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der falsche Ausschnitt, wie sieht die Ausgabe denn aus ?

Autor: der mechatroniker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Der falsche Ausschnitt, wie sieht die Ausgabe denn aus ?

na ja, so:
UDR1 = spannung;

hat er doch gepostet.

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Mangosniper (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:



/* Einbeziehen von Header- Dateien*/
#include <avr/io.h>

#include <inttypes.h>
#include <stdlib.h>
#ifndef F_CPU

#include <string.h>
#include <stdio.h>

#define F_CPU 8000000UL    //Taktfrequenz Atmega

#endif


//-----------------------------------------------


/*Init Variables*/
float spannung;
float Z_array;
float V_mess;
int counter;
unsigned char array[4]; // Array für den Spannungswert der aus maximal 4 Zeichen besteht (0-1023)
            // anlegen.
int zerlegen(void)
{
  

  memset ( array, '\0', sizeof(array));
  spannung = ADCW;    //ADCW-Wert in die Wariable Spannung aufnehmen
  
  ///////////////////////////////////////////////////////////////////////////////////////////////
  //Dieser Teil des Codes ist dafür zuständig die Variable spannung in 4 Zeichen aufzuteilen
  //und in das Array zu legen. Bei angelegter Spannung 1,9V (ADCW-Wert : 0644)
  //z.B. 0 in Array[0] 6 in Array[1] 4 in Array[2] und in Array[3] 4
  //Damit im Hyperterminal die Zahlen korrekt angezeigt werden, 48 addieren um in der 
  //ASCII-Tabelle auf Zahlenwerte zu kommen
  ///////////////////////////////////////////////////////////////////////////////////////////////

  
  if( spannung > 1000) 
  {
    array[0] = 1 + 48;
    spannung -= 1000;
  
  }
  else
  {
    array[0] = 0 + 48;
    
  }
  UDR1 = array[0];
  //*************************************

  while( spannung >= 100)
  {
    if( spannung >= 100)
    {
      spannung -= 100;
      counter += 1;
    }
  }
  array[1] = counter + 48;
        
  counter = 0;
  UDR1 = array[1];

  //************************************
  
  while (spannung >= 10)
  {
    if( spannung >= 10)
    {
      spannung -= 10;
      counter +=1;
    }
  }
  array[2] = counter + 48;
  counter = 0;
  UDR1 = array[2];
  
  //************************************

  while (spannung >= 1)
  {
    if( spannung >= 1)
    {
      spannung -= 1;
      counter +=1;
    }
  }
  array[3] = counter + 48;
  UDR1 = array[3];
   
  
  Z_array = ((array[0] - 48) * 1000) + ((array[1] - 48) * 100) + ((array[2] - 48)* 10) + array[3];

  V_mess = (Z_array * 3,02) / 1024;

  /////////////////////////////////////////////////////////////////////////////////////////////
  //Hier müsste jetzt der Teil des Codes kommen um den Wert von V_mess an das Hyperterminal
  //weiterzugeben.
  /////////////////////////////////////////////////////////////////////////////////////////////

    // UDR1 = V_mess; geht natürlich nicht.Wieder in ein Array packen kann ich den Wert auch nicht.
  // da er viele Nachkommastellen hat die mitangezeigt werden müssen (jedenfalls mind. 2-3)  
}



int main (void)
{


  UCSR1B = 0;  //USART1 Interrupts OFF

  /*init BAUD*/

  UBRR1H = 51 >>8;    //9600 Baud @ 8MHz
  UBRR1L = 51 & 0xFF;
  
  /*init UART*/  

  UCSR1C |= (1 << UCSZ10);
  UCSR1C |= (1 << UCSZ11);

  UCSR1B &= (0 << UCSZ12);
  UCSR1B |= (1 << TXEN);
    
  /* Init ADC*/
  
  ADMUX |= (1 << REFS1);
  ADMUX |= (1 << REFS0);


  ADCSRA |= (1 << ADEN);
  ADCSRA |= (1 << ADSC);

  ADCSRA |= (1<< ADPS1);
  ADCSRA |= (1<< ADPS2);

  PORTF = 0x01;

  while ( ADCSRA & (1<<ADSC) )  //warten bis ADSC Bit gelöscht wird
    {
      ;
   }
  
  

  // warte bis TX bereit
  while (!(UCSR1A & (1 << UDRE1)))
    ;

  
  zerlegen();
  
} 


Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)
void AdcValOut (uint16_t AdcValue)
{
    uint8_t AdcValArray [5];                  // Integer Array
    uint8_t m = 4;                            // Schleifenzähler für Ausgabe

    do  {
        AdcValArray [m] = '0' + AdcValue %10; // Berechnung der Stellen
        AdcValue /= 10;                       // und Wandlung nach ASCII
    } while (m--);
    while (m != 3)  {
        UartPutC (AdcValArray [++m]);         // Integer Wert ausgeben
        if (m == 0) UartPutC ('.');           // Dezimalpunkt setzen
    }
    UartPutC ('\r');

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

Autor: Mangosniper (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.