mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ADC bei Atmega 32


Autor: Christoph A. (shadowrunner93)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Leute!

Ich bin neu hier und habe natürlich eine Frage:

Ich versuche zurzeit einen Temperatursensor zu bauen.
Die Temperatur habe ich schon in Form von Spannung (10mV / °Kelvin).

Nun will ich diese in Form von Grad auf 3 Siebensegmentanzeigen 
ausgeben. (falls die Temperatur dreistellig ist).

Dies ist das erste mal dass ich mit einem ADC arbeite, deswegen 
funktioniert es natürlich nicht gleich..

Hier der Code:
#include <avr/io.h>

#define LOOP 5
#define COMM_ANODE_R 0
#define COMM_ANODE_M 0
#define COMM_ANODE_L 0

void ADC_init(void);
void test_readout(void);
double measurement(void); 
void sev_segm_l(int number);
void sev_segm_m(int number);
void sev_segm_r(int number);

void main()
{

  DDRB=0xFF; // als Ausgang
  DDRC=0xFF; 
  DDRD=0xFF; 

  double erg;


  ADC_init();  
  test_readout();
  
  while(1)
  {
    erg=measurement();
    
    erg*=19,1;    //4,87V / 255 = 19,1 mV
    erg*=10;

    erg/=10;
    
    sev_segm_l((int)(erg/100));
    // die letzte Stelle des Ergebnises
    sev_segm_m((int)(erg/10-(erg/100)*10));
    // die mittlere Stelle...
    sev_segm_r((int)(erg-(erg/10)*10));
    // usw.
  }

}

void ADC_init(void)
{
  /*  

  ADEN=0;   aktiviert ADC (Analog-Digital-Converter)

  ADSC=0;    setzt auf (nicht)freilaufenden Betrieb
           Der ADC misst (nicht)durchgehend

  ADATE=1;   Für freilaufenden Modus
  
  ADIF=0;    Interrupt-Flag aus

  ADIE=0;   Kein Interrupt Flag

  ADPS0...ADPS2 : Teilungsfaktor 
          CPU_Clk/Fakt. = >40kHz & <150 kHz

  */

  ADCSRA=0b01000101;
  
  /*

  REFS0..REFS1 Referenzspannungsmodus

  ADLAR Darstellung des Ergebnisses

  MUX Pinnummer des eingehenden A/D Ports

  */

  ADMUX=0b00000000;
}

void test_readout(void)
{
  
  double dummy_erg;
  

  ADCSRA |= (1<<ADSC); // "Warmlaufrunde" für ADC

  while(ADCSRA & (1<<ADSC) )
  {
    //auf Abschluss der Konvertierung warten  
  }
  
  dummy_erg=ADCW; //ADCW muss einmal gelesen werden 
          //sonst wird das Ergebnis der
          //nächsten Wandlung nicht übernommen
  
}

double measurement(void)
{

  double erg=0;
  

  for(int i=0;i<LOOP;i++)    // zur Genauigkeit wird der
  {              // Mittelwert von LOOP-Messungen
                // verwendet      
    ADCSRA |= (1<<ADSC);  // Eine Wandlung      
    
    while(  ADCSRA & (1<<ADSC) )
    {
      ;//auf Abschluss der Konvertierung warten  
    }    
      
    erg += ADCW;
  }
  
  erg  /= LOOP;
  
  return erg;

}

void sev_segm_l(int number)
{
  if(COMM_ANODE_L)
  {
    switch(number)
    {
      case 0:

      //wobei PORT1 = a usw.  
      PORTB=~(0b00111111);
      break;

      case 1:

      PORTB=~(0b00000110);
      break;

      case 2:

      PORTB=~(0b01011011);
      break;

      case 3:

      PORTB=~(0b01001111);
      break;

      case 4:

      PORTB=~(0b01100110);
      break;

      case 5:

      PORTB=~(0b01101101);
      break;

      case 6:

      PORTB=~(0b01111101);
      break;

      case 7:

      PORTB=~(0b00000111);
      break;

      case 8:

      PORTB=~(0b01111111);
      break;

      case 9:

      PORTB=~(0b01101111);
      break;

      default:

      PORTB=0b01000000;
      break;
    }
  }

  else
  {
    switch(number)
    {
      case 0:

      //wobei PORT1 = a usw.  
      PORTB=0b00111111;
      break;

      case 1:

      PORTB=0b00000110;
      break;

      case 2:

      PORTB=0b01011011;
      break;

      case 3:

      PORTB=0b01001111;
      break;

      case 4:

      PORTB=0b01100110;
      break;

      case 5:

      PORTB=0b01101101;
      break;

      case 6:

      PORTB=0b01111101;
      break;

      case 7:

      PORTB=0b00000111;
      break;

      case 8:

      PORTB=0b01111111;
      break;

      case 9:

      PORTB=0b01101111;
      break;

      default:

      PORTB=0b01000000;
      break;
    }
  }
}

void sev_segm_m(int number)
{
  if(COMM_ANODE_M)
  {
    switch(number)
    {
      case 0:

      //wobei PORT1 = a usw.  
      PORTC=~(0b00111111);
      break;

      case 1:

      PORTC=~(0b00000110);
      break;

      case 2:

      PORTC=~(0b01011011);
      break;
USW...



Die Siebensegmentanzeigen geben nur Nullen aus.

Die analoge Spannung habe ich auf den PORT A0 angelegt. Ist das bei 
diesem Code richtig?

Mfg
Christoph

Autor: Gerald D. (saleru)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also in diner ADC_init funktion schaltest du den ADC nicht ein sondern 
startest ihn blos (du setzt enable auf 0 aber start auf 1). es müsste 
eigentlich genau umgekehrt sein:

ADCSRA=0b10000101;

und nicht ADCSRA=0b01000101;


probier das mal ... mir fallen in dem code zwar noch andre sachen auf 
aber die sollten zumindest die konvertierung nicht verhindern.

Autor: Christoph A. (shadowrunner93)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Okay, probier ich!

Schonmal Danke für die Hilfe ;)

Welche Dinge fallen dir denn noch auf?

Mfg
Christoph

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

Bewertung
0 lesenswert
nicht lesenswert
Gerald D. schrieb:
> Also in diner ADC_init funktion schaltest du den ADC nicht ein sondern
> startest ihn blos (du setzt enable auf 0 aber start auf 1). es müsste
> eigentlich genau umgekehrt sein:
>
> ADCSRA=0b10000101;
>
> und nicht ADCSRA=0b01000101;
>
>
> probier das mal ... mir fallen in dem code zwar noch andre sachen auf
> aber die sollten zumindest die konvertierung nicht verhindern.

Und ändere deine Syntax

ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);

ist doch leichter zu lesen als

ADCSRA=0b10000101

Autor: Gerald D. (saleru)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
beispielsweise verwendest du den datentyp double für das ergebnis. ich 
meine es ist ein atmega32 ... mit limitiertem speicher und befehlssatz. 
eine double operation benötigt unmengen an rechenleistung ... wennste 
stattdessen einen int16 hernimmst und die werte schön aufskalierst 
kommst auch auf das selbe ergebnis ohne rießen platzbedarf und 
performanceverlust.

weiters ...

erg*=19,1;

wieso kompiliert dieser ausdruck ? sind commazahlen nicht eigentlich mit 
punkt festgelegt?

oder die zeilen

erg*=10;
erg/=10;

nacheinander wirken ein wenig naja überflüssig, weis nicht was du damit 
erreichen willst, da es sich ja um einen double handelt.

aber das sind nur eben kleinigkeiten die hier nicht ins gewicht fallen 
aber bei projekten die mehr logik erfordern dir den speicher aussaugen 
könnten :)

Autor: Christoph A. (shadowrunner93)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

>
> Und ändere deine Syntax
>
> ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);
>
> ist doch leichter zu lesen als
>
> ADCSRA=0b10000101

Okay, mach ich ;)

So, folgendes:

Die erste Siebensegmentanzeige zeigt 0 an.
Die zweite - (also den default-Wert bei der switch-case Anweisung)
Die dritte : PortC gibt bei Pin 4 einen Wechselspannung mit dem RMS-Wert 
von 0,6V aus. Der Rest ist wie bei der ersten Siebensegmentanzeige.

Nur zur Info: Die angelegte Wechselspannung beträgt ungefähr 2,9V.

Liefert das STK500 bei AVCC und AGND die nötige Spannung automatisch?

Mfg
Christoph

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

Bewertung
0 lesenswert
nicht lesenswert
Christoph Anlauf schrieb:
> Okay, probier ich!
>
> Schonmal Danke für die Hilfe ;)
>
> Welche Dinge fallen dir denn noch auf?

Oh. Da gibt es noch jede Menge.
Zb. Schon mal was von Arrays gehört?
unsigned char digits[] = { 0b00111111,    // 0
                           0b00000110,    // 1
                           0b01011011,    // 2
                           0b01001111,    // 3
                           0b01100110,    // 4
                           0b01101101,    // 5
                           0b01111101,    // 6
                           0b00000111,    // 7
                           0b01111111,    // 8
                           0b01101111,    // 9
                           0b01000000     // alles andere
                         };

void sev_segm_l(unsigned char number)
{

  if(COMM_ANODE_L)
  {
    if( number < 10 )
      PORTB = ~digits[number];
    else
      PORTB = ~digits[10];
  }
  else
  {
    if( number < 10 )
      PORTB = digits[number];
    else
      PORTB = digits[10];
  }
}

void sev_segm_m(unsigned char number)
{

  if(COMM_ANODE_M)
  {
    if( number < 10 )
      PORTA = ~digits[number];
    else
      PORTA = ~digits[10];
  }
  else
  {
    if( number < 10 )
      PORTA = digits[number];
    else
      PORTA = digits[10];
  }
}

....


Ist doch viel kürzer :-)

Die richtigen Datentypen benutzen. int ist kein Allheilmittel für alles.

Autor: Gerald D. (saleru)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Soweit ich weis wird beim stk500 avcc auf die versorgungsspannung also 
5V gesetzt. außer man setzt den jumper um und stellt was eigenes ein.

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Soweit ich weis wird beim stk500 avcc auf die versorgungsspannung also
>5V gesetzt. außer man setzt den jumper um und stellt was eigenes ein.

Sowas kann man auch im AVRStudio nachgucken.
Die Spannung ist in einem bestimmten Bereich frei einstellbar; 
grundsätzlich aber kleiner als VCC.

Autor: Christoph A. (shadowrunner93)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zuerst eine Ausbesserung meinerseits :

 "Die angelegte Wechselspannung beträgt ungefähr 2,9V."

Hierbei meinte ich analoge Spannung^^.

 Gerald D. schrieb:

>
>
> erg*=10;
> erg/=10;
>
>

Das ist Absicht, wegen der Übersicht. So wird mein Gedankenvorgang 
gezeigt ;)

 Karl heinz Buchegger schrieb:


>Ist doch viel kürzer :-)
>
>Die richtigen Datentypen benutzen. int ist kein Allheilmittel für alles.

Natürlich kenne ich Arrays!
Ja, das ist ja jetzt nur der Prototyp Code. ;)

Mfg
Christoph

Autor: Gerald D. (saleru)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>>
>>
>> erg*=10;
>> erg/=10;
>>
>>
>
> Das ist Absicht, wegen der Übersicht. So wird mein Gedankenvorgang
> gezeigt ;)

hihi ;) ... hast in deinen gedanken an eine andere schreibweise für noop 
instruktionen gedacht ? ;)

Autor: Christoph A. (shadowrunner93)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also hier mal ein paar Daten:

Die Taktfrequenz ist bei 4 Mhz.
Somit stimmt der Teiler hoffentlich ;)

Der AREF-Jumper auf dem STK500 ist gesetzt.

Hmm, sonst noch irgendwelche Tipps?

Mfg
Christoph

Autor: Christoph A. (shadowrunner93)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gerald D. schrieb:
>>>
>>>
>>> erg*=10;
>>> erg/=10;
>>>
>>>
>>
>> Das ist Absicht, wegen der Übersicht. So wird mein Gedankenvorgang
>> gezeigt ;)
>
> hihi ;) ... hast in deinen gedanken an eine andere schreibweise für noop
> instruktionen gedacht ? ;)

Na klar doch ;)

Auch wenn ihr keine Noobs seit, habt ihr den Code dadurch wahrscheinlich 
trotzdem schneller kapiert^^. (Wenn ihr über den Zusammenhang überhaupt 
eine Gedanken verliert)

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Die Taktfrequenz ist bei 4 Mhz.
>Somit stimmt der Teiler hoffentlich ;)
Wie hast du das eingestellt?
Das STK500 kann maximal 3,686MHz von sich aus erzeugen.

Der AREF-Jumper auf dem STK500 ist gesetzt.
Damit sollte zumindest die interne Referenz angeschlossen sein.
Wie hoch die Spannung ist, kannst du im AVRStudio auslesen und 
einstellen.

Autor: Gerald D. (saleru)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
(1) erg=measurement();
(2) erg*=19,1;    //4,87V / 255 = 19,1 mV
(3) erg*=10;
(4) erg/=10;


Ahlso:

nach dem schritt 1 sollte erg einen wert von 0-1024 haben (0-2^10) da 
der adc eine 10 Bit genauigkeit hat.

bei schritt 2 multiplizierst du dann erg mit 19.1 ... aber nimmst 
gleiczeitig an, nur eine adc auflösung von 8 bit zu haben da du durch 
255 dividierst. das wäre mal der erste wiederspruch

schritte 3 und 4 wären dann mal überflüssig

danach gibst du erg auf der 7 segmentanzeige aus. wo du hergehst und 
völlig wirr herummultiplizierst und dividierst. also ich kann aus der 
ausgabe auch nicht wirklich was schließen.

Autor: Christoph A. (shadowrunner93)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
STK500-Besitzer schrieb:
>>Die Taktfrequenz ist bei 4 Mhz.
>>Somit stimmt der Teiler hoffentlich ;)
> Wie hast du das eingestellt?
> Das STK500 kann maximal 3,686MHz von sich aus erzeugen.
>
> Der AREF-Jumper auf dem STK500 ist gesetzt.
> Damit sollte zumindest die interne Referenz angeschlossen sein.
> Wie hoch die Spannung ist, kannst du im AVRStudio auslesen und
> einstellen.

Es heißt im STK500 zwar 4 Mhz int. Clk, aber sind tatsächlich nur 3,6 
Mhz.

Autor: Christoph A. (shadowrunner93)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gerald D. schrieb:
> (1) erg=measurement();
> (2) erg*=19,1;    //4,87V / 255 = 19,1 mV
> (3) erg*=10;
> (4) erg/=10;
>
>
> Ahlso:
>
> nach dem schritt 1 sollte erg einen wert von 0-1024 haben (0-2^10) da
> der adc eine 10 Bit genauigkeit hat.
>
> bei schritt 2 multiplizierst du dann erg mit 19.1 ... aber nimmst
> gleiczeitig an, nur eine adc auflösung von 8 bit zu haben da du durch
> 255 dividierst. das wäre mal der erste wiederspruch
>
> schritte 3 und 4 wären dann mal überflüssig
>
> danach gibst du erg auf der 7 segmentanzeige aus. wo du hergehst und
> völlig wirr herummultiplizierst und dividierst. also ich kann aus der
> ausgabe auch nicht wirklich was schließen.

So stimmt es aber, keine Ahnung was ich mir vorher dabei gedacht habe..

erg*=4,7;    //4,87V / 1023 = 4,7 mV
erg/=10;    //Ergebnis in °Kelvin
erg-=273.15*10          //Ergebnis in °Celsius

Nachtrag:
Dass mit der Rechnerei im Funktionsaufruf war nur ein Versuch folgendes 
hinzubekommen:

Ich möchte einfach nur eine 3-stellige Zahl auf 3 Anzeigen aufteilen.

Also z.b die Zahl ist 450.

1.Anzeige: 4
2. 5
3. 0

Aber nichts sagen, ich bin gerade am Überlegen ;-)

Autor: Christoph A. (shadowrunner93)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eine Frage:

Wird das Ergebnis einer Division in C gerundet?
Wenn nicht, habe ich eine Lösung^^

Mfg
Christoph

Autor: Christoph A. (shadowrunner93)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So ich hab's jetzt!
Anscheinend wird in C nicht automatisch gerundet.
sev_segm_l((int)(erg/100));
// die letzte Stelle des Ergebnises
sev_segm_m((int)((erg%100)/10);
// die mittlere Stelle...
sev_segm_r((int)(erg%10));
// usw.

Mfg
Christoph

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

Bewertung
0 lesenswert
nicht lesenswert
Christoph Anlauf schrieb:

> erg*=4,7;    //4,87V / 1023 = 4,7 mV

ist immer noch falsch.
Das ist keine Multiplikation mit 4.7 (man beachte den Dezimalpunkt(!)) 
sondern eine Multiplikation mit 7

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

Bewertung
0 lesenswert
nicht lesenswert
Christoph Anlauf schrieb:
> So ich hab's jetzt!
> Anscheinend wird in C nicht automatisch gerundet.

Nein.
Das steht aber auch in jedem noch so grindigem C-Buch.
Bei der Konvertierung von double auf int, werden Kommastellen 
abgeschnitten.

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Wird das Ergebnis einer Division in C gerundet?
>Wenn nicht, habe ich eine Lösung^^

Wie sieht die aus?
Die einfachste Methode ist:

double x;

int y;

y = x+0.5; // Aufrunden

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.