Atmega32 ADC

Gast #3214620
Hallo,

ich habe einen Fototransistor über den PA0 (ADC0) an meinem Atmega 32 
angeschlossen.
Die Werte werden ausgelesen und an das Display von Pollin TC1602A-09 
ausgegeben.
Das Display funktioniert einwandfrei nur meine Werte "spinnen" etwas, 
meistens kann ich nur die ersten beiden Ziffern des 10bit Wertes 
erkennen, die restlichen werden meiner Meinung nach zu schnell hin und 
her gewechselt so dass man es nicht vom Display ablesen kann. Auch ein 
Kondensator am AREF Pin gegen VCC brachte keinen Unterschied.

Testweise habe ich ein Poti(1MOhm) angeschlossen und ein Delay von 
1000ms eingefügt bis zum nächsten anzeigen eines neuen Wertes.
Die Werte gingen von 0 bis ca. 800 dann über in einen negativen Wert von 
300 und mehr.
Was mache ich falsch ?

Danke

hier mein Code:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include "LCD.h"
4

5
int main(void)
6
{
7
InitializeLCD(); 
8
Send_A_StringToLCDWithLocation(1,1,"ADC Result:");
9
ADCSRA |= 1<<ADPS2;
10
ADMUX |= (1<<REFS0) | (1<<REFS1);
11
ADCSRA |= 1<<ADIE;
12
ADCSRA |= 1<<ADEN;
13

14
sei();
15

16
ADCSRA |= 1<<ADSC;
17

18
while (1)
19
{
20
}
21
}
22
ISR(ADC_vect)
23
{
24
uint8_t LowADC = ADCL;
25
uint16_t theTenBitResults = ADCH<<8 | LowADC;
26
Send_An_IntegerToLCD(1,2,theTenBitResults, 4);
27

28
ADCSRA |= 1<<ADSC; 
29
}
Angehängte Dateien:
#3214640
Wozu der ganze Hackmack mit Interrupts? Gerade bei einem ADC ist das 
meistens nicht wirklich sinnvoll.
Als Faustregel kannst du dir merken: IN dem Moment, in dem du eine 
Ausgabe in einer ISR auf LCD oder UART hast, hast du falsch designed. So 
etwas macht man niemals.

> uint8_t LowADC = ADCL;
> uint16_t theTenBitResults = ADCH<<8 | LowADC;

Auf einem AVR mit gcc brauchst du dich nicht darum kümmern, dass du 2 
Register auslesen musst um ein 16 Bit Ergebnis zu erhalten. Das ist ein 
Detail um das sich der COmpiler kümmert

uint16_t Result = ADC;


fertig.


Und den eigentlich spannenden Teil, nämlich die Funktion 
Send_An_IntegerToLCD, den hast du natürlich unterschlagen.


Nciht lesbare Warte am LCD haben 2 Ursachen
* zum einen ist die Update Frequenz viel zu hoch. Du kannst als Mensch 
nicht mehr als 3 oder 4 Werte pro Sekunde ablesen. Es ist daher völlig 
sinnlos, wenn du 100-tausend Updates pro Sekunde am LCD machst
* aber selbst wenn deine UPdate-Frequenz angepasst ist, dann ist es für 
uns Menschen sehr hilfreich, wenn zb die Einerstelle einer Zahl immer an 
derselben Position am LCD aufscheint, so dass die Zahl nicht ständig 
links rechts wandert. D.h. es ist sinnvoll eine formatierte Ausgabe in 
ein Feld mit konstanter Größe zu machen.
Ob du das tust und wie du das tust, kann man nicht sagen, denn die 
Funktion selbst zeigst du ja nicht her. Dass aber mit der was nicht 
stimmt, folgt schon daraus, dass ein uint16_t als unsigned WErt per 
Definition schon kein  Vorzeichen und damit immer positiv ist, was aber 
deine Ausgabefunktion anscheinend nicht so beeindruckt. Ok, bei 
maximalen Werten von 0 bis 1023 sollte das keinen Unterschied machen ob 
signed oder unsigned. In diesem Wertebereeich gibt es kein BItmuster, 
welches als signed interpretiert jemals als negative Zahl aufgefasst 
werden könnte.
#3214656
>  Auch ein Kondensator am AREF Pin gegen VCC brachte keinen Unterschied.

Was heißt da AUCH?

Der Kondensator gehört da mindestens hin.
Genauso wie ein 100nF Kondensator an VCC/GND bzw. AVcc/GND.

Ohne diese 3 Kondensatoren brauchen wir erst mal gar nicht weiter 
diskutieren
Gast #3215365
Erstmal Danke !
also Kondensatoren sind alle dran,

Meine Code sieht jetzt so aus:
1
#include <avr/io.h>
2
#include "MrLCD.h"
3
#include <util/delay.h>
4

5

6
int main(void)
7
{
8
  
9
  InitializeMrLCD(); //LCD Initialisieren
10

11
  Send_A_String("ADC Result:"); 
12

13
  //ADC Initialisieren
14
  
15
  ADCSRA = (1<<ADPS2) | (1<<ADEN); // Prescaler 16 | ADC-An 
16
  ADMUX  = (1<<REFS0);        // AVCC als Referenz Kondensator bei AREF
17
  
18
  uint16_t Ergebnis = 0;
19
  
20

21
while (1)
22
  {
23
  
24
  //Messung starten
25
  
26
  ADCSRA = (1<<ADSC);
27
  while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
28
  
29
  Ergebnis = ADC;      // Ergebnis 
30
  
31
  Send_An_IntegerToMrLCD(1, 2, Ergebnis, 4); //Ergebnis an Display senden
32
    
33
  }
34
}
und die MrLCD.h :
1
#ifndef MrLCD
2
#define MrLCD
3

4
#include <avr/io.h>
5
#include <util/delay.h>
6
#include <stdlib.h>
7

8
#define MrLCDsCrib PORTB
9
#define DataDir_MrLCDsCrib DDRB
10
#define MrLCDsControl PORTD
11
#define DataDir_MrLCDsControl DDRD
12
#define LightSwitch 5
13
#define ReadWrite 7
14
#define BiPolarMood 2
15

16
char firstColumnPositionsForMrLCD[4] = {0, 64, 20, 84};
17

18
void Check_IF_MrLCD_isBusy(void);
19
void Peek_A_Boo(void);
20
void Send_A_Command(unsigned char command);
21
void Send_A_Character(unsigned char character);
22
void Send_A_String(char *StringOfCharacters);
23
void GotoMrLCDsLocation(uint8_t x, uint8_t y);
24
void InitializeMrLCD(void);
25
void Send_A_StringToMrLCDwithLocation(uint8_t x, uint8_t y, char *StringOfCharacters);
26
void Send_An_IntegerToMrLCD(uint8_t x, uint8_t y, int IntegerToDisplay, char NumberofDigits);
27

28

29
void Check_IF_MrLCD_isBusy()
30
{
31
  DataDir_MrLCDsCrib = 0;
32
  MrLCDsControl |= 1<<ReadWrite;
33
  MrLCDsControl &= ~1<<BiPolarMood;
34

35
  while (MrLCDsCrib >= 0x80)
36
  {
37
  Peek_A_Boo();
38
  }
39

40
  DataDir_MrLCDsCrib = 0xFF; //0xFF means 0b11111111
41
}
42

43
void Peek_A_Boo()
44
{
45
  MrLCDsControl |= 1<<LightSwitch;
46
  asm volatile ("nop");
47
  asm volatile ("nop");
48
  MrLCDsControl &= ~1<<LightSwitch;
49
}
50

51
void Send_A_Command(unsigned char command)
52
{
53
  Check_IF_MrLCD_isBusy();
54
  MrLCDsCrib = command;
55
  MrLCDsControl &= ~ ((1<<ReadWrite)|(1<<BiPolarMood));
56
  Peek_A_Boo();
57
  MrLCDsCrib = 0;
58
}
59

60
void Send_A_Character(unsigned char character)
61
{
62
  Check_IF_MrLCD_isBusy();
63
  MrLCDsCrib = character;
64
  MrLCDsControl &= ~ (1<<ReadWrite);
65
  MrLCDsControl |= 1<<BiPolarMood;
66
  Peek_A_Boo();
67
  MrLCDsCrib = 0;
68
}
69

70
void Send_A_String(char *StringOfCharacters)
71
{
72
  while(*StringOfCharacters > 0)
73
  {
74
  Send_A_Character(*StringOfCharacters++);
75
  }
76
}
77

78
void Send_A_StringToMrLCDwithLocation(uint8_t x, uint8_t y, char *StringOfCharacters)
79
{
80
  GotoMrLCDsLocation(x, y);
81
  Send_A_String(StringOfCharacters);
82
}
83

84
void GotoMrLCDsLocation(uint8_t x, uint8_t y)
85
{
86
  Send_A_Command(0x80 + firstColumnPositionsForMrLCD[y-1] + (x-1));
87
}
88

89
void InitializeMrLCD()
90
{
91
  DataDir_MrLCDsControl |= 1<<LightSwitch | 1<<ReadWrite | 1<<BiPolarMood;
92
  _delay_ms(15);
93

94
  Send_A_Command(0x01); //Clear Screen 0x01 = 00000001
95
  _delay_ms(2);
96
  Send_A_Command(0x38);
97
  _delay_us(50);
98
  Send_A_Command(0b00001110);
99
  _delay_us(50);
100
}
101

102
  void Send_An_IntegerToMrLCD(uint8_t x, uint8_t y, int IntegerToDisplay, char NumberofDigits)
103
{
104
  char StringToDisplay[NumberofDigits];
105
  itoa(IntegerToDisplay, StringToDisplay, 10);
106
  for (int i=0; i<NumberofDigits; i++) Send_A_String(" ");
107
  Send_A_StringToMrLCDwithLocation(x, y, StringToDisplay);
108
}
109

110
#endif
#3215483
Über die Funktion
1
  void Send_An_IntegerToMrLCD(uint8_t x, uint8_t y, int IntegerToDisplay, char NumberofDigits)
2
{
3
  char StringToDisplay[NumberofDigits];
4
  itoa(IntegerToDisplay, StringToDisplay, 10);
5
  for (int i=0; i<NumberofDigits; i++) Send_A_String(" ");
6
  Send_A_StringToMrLCDwithLocation(x, y, StringToDisplay);
7
}

solltest du nochmal nachdenken. Die ist ziemlicher Quatsch.
Vor allen Dingen erreichst du nicht das was du willst: eine 
rechtsbündige Ausgabe in einem Feld gewisser Größe. Diese Ausgabe ist 
wieder linksbündig und damit tanzen die Zahlen vor dem Benutzer je nach 
Größenordnung hin und her.
Wenn du es dir leisten kannst, dann nimm halt sprintf um dir das Leben 
erst mal leichter zu machen.
1
void Send_An_IntegerToMrLCD(uint8_t x, uint8_t y, int IntegerToDisplay, uint8_t NumberofDigits)
2
{
3
  char StringToDisplay[7];
4

5
  sprintf( StringToDisplay, "%0*d", NumberofDigits, IntegerToDisplay );
6
  Send_A_StringToMrLCDwithLocation(x, y, StringToDisplay);
7
}
Gast #3215807
Ok habe ich so verändert, sowie die stdio.h eingefügt.

Jetzt habe ich den Unterstrich in der 2 Zeile ganz links am Display aber 
keine Zahlen ?

Irgendwo kann noch etwas nicht stimmen in meinem Code :-(
#3215835
OK.
Bin mir nicht sicher, ob die avr-libc den * in der Formatangabe kennt. 
Ist zwar eine C-Standardsache, aber auf dem AVR wurde da einiges 
abgespeckt.

Mach da erst mal eine Konstante rein
1
  sprintf( StringToDisplay, "%04d", IntegerToDisplay );
Gast #3215928
Also die ersten 3 Ziffern kann ich Einwandfrei ablesen die letzte 
"wackelt" schon noch etwas, außer das Poti das ich gerade angeschlossen 
habe ist auf 0 oder 1023 dann sind alle stabil.

Ich überlege noch einen Mittelwert über ein Array zu realisieren ?
#3215961
cube4 schrieb:
> Also die ersten 3 Ziffern kann ich Einwandfrei ablesen die letzte
> "wackelt" schon noch etwas

Das ist ziemlich normal.
So um die +-2 ADC_Werte wackeln wirst du haben. Man müsste sonst 
wesentlich mehr Aufwand treiben um die Referenzspannung stabil zu halten 
bzw. die 'Sensorschaltung' wird etwas Rauschen einbringen. Kurz und gut: 
rechne damit, dass die letzte Stelle nicht wie festgenagelt stabil steht 
und du machst dir selber das Leben leichter.

>, außer das Poti das ich gerade angeschlossen
> habe ist auf 0 oder 1023 dann sind alle stabil.
>
> Ich überlege noch einen Mittelwert über ein Array zu realisieren ?

Wozu Array?
Brauchst du doch nicht.

Steck den ADC-Ausleseteil in eine Funktion, damit du ihn leicht 
handhabbar hast
1
uint16_t ReadADC()
2
{
3
  ADCSRA = (1<<ADSC);
4
  while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
5
  
6
  return ADC;
7
}

und mach dir daraus aufbauend eine Funktion, die zb 8 Messungen macht 
und daraus den Mittelwert errechnet
1
uint16_t ReadADCAverage()
2
{
3
  uint16_t Result = 0;
4
  uint8_t i;
5

6
  for( i = 0; i < 8; i++ )
7
    Result += ReadADC();
8

9
  return Result / 8;
10
}

und ruf die in deiner Hauptschleife auf
1
  ...
2
  while (1)
3
  {
4
    Ergebnis = ReadADCAverage();
5
    Send_An_IntegerToMrLCD(1, 2, Ergebnis, 4);
6
  }
7
}

Und schon siehst du auf deinem LCD jeweils den Mittelwert aus 8 
Messungen. Und das ganze in leicht handhabbaren Funktionen aufgeteilt.
#3215975
Du könntest auch mal den Unterschied ausprobieren, den eine Änderung im 
Formatstring hier
1
  sprintf( StringToDisplay, "%4d", IntegerToDisplay );
ergibt. Jetzt gibt es keine führenden 0-en mehr, sondern das 4-stellige 
Feld wird links mit Leerzeichen aufgefüllt.
Gast #3216553
Also irgendwo ist der Hund begraben.

Wenn ich de Funktionen einfüge bekomme ich nur 0000 angezeigt.
Bin nochmal mein C++-Buch durchgegangen aber finde den Fehler nicht.
Gast #3216613
#include <avr/io.h>
#include "MrLCD.h"
#include <util/delay.h>

uint16_t Ergebnis = 0;

uint16_t ADCAuslesen(void);
uint16_t Mittelwert(void);



int main(void)
{

  InitializeMrLCD(); //LCD Initialisieren

  Send_A_String("ADC Result:");

  //ADC Initialisieren

  ADCSRA = (1<<ADPS2) | (1<<ADEN); // Prescaler 16 | ADC-An
  ADMUX  |= (1<<REFS0);        // AVCC als Referenz Kondensator bei AREF


    uint16_t ADCAuslesen(void)
  {
    ADCSRA = (1<<ADSC);
    while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung 
warten

    return ADC;
  }

  // Mittelwert bilden

    uint16_t Mittelwert(void)
  {
    uint16_t Summe = 0;
    uint8_t i;

    for( i = 0; i < 8; i++ )
    Summe += ADCAuslesen();

    return Summe / 8;
}


while (1)
  {

  Ergebnis = ADCAuslesen();
    Send_An_IntegerToMrLCD(1, 2, Ergebnis, 4);

  }


}

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 13 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.