Forum: Mikrocontroller und Digitale Elektronik Atmega32 ADC


von cube4 (Gast)


Angehängte Dateien:

Lesenswert?

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
}

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

>  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

von Dietrich L. (dietrichl)


Lesenswert?

cube4 schrieb:
> Testweise habe ich ein Poti(1MOhm) angeschlossen

Das ist zu hochohmig. Nimm 10kOhm oder weniger.

von cube4 (Gast)


Lesenswert?

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

von Uwe (de0508)


Lesenswert?

so so !  nicht ? oder doch?
1
ADCSRA = (1<<ADSC);

Was steht nun in diesem Register ?

von cube4 (Gast)


Lesenswert?

1
ADCSRA |= 1<<ADSC;

von Karl H. (kbuchegg)


Lesenswert?

Ü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
}

von cube4 (Gast)


Lesenswert?

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 :-(

von cube4 (Gast)


Lesenswert?

Was ich vieleicht noch vergessen habe zu erwähnen ist das der Atmega32 
mit 1Mhz läuft.

von Karl H. (kbuchegg)


Lesenswert?

cube4 schrieb:
> 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 ?

Welchen UNterstrich?
Da wird nirgends ein UNterstrich ausgegeben.

von Karl H. (kbuchegg)


Lesenswert?

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 );

von cube4 (Gast)


Lesenswert?

:-)))))) Sau stark funktioniert Einwandfrei !!!!

Vielen Dank ! Vorerst ! ;-)

von Karl H. (kbuchegg)


Lesenswert?

Und?
Haben sich deine Ableseprobleme bzw. die seltsamen Zahlenwerte dadurch 
gegeben?

von cube4 (Gast)


Lesenswert?

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 ?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von cube4 (Gast)


Lesenswert?

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.

von cube4 (Gast)


Lesenswert?

#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);

  }


}

von Karl H. (kbuchegg)


Lesenswert?

Mein Fehler.
Ich habe deinen Code von weiter oben unkritisch runterkopiert
1
  ADCSRA = (1<<ADSC);

ist natürlich Blödsinn.

Das muss
1
  ADCSRA |= (1<<ADSC);
heißen.

von cube4 (Gast)


Lesenswert?

Mist :-) auch mein Fehler ich kopiere zuviel !
Aber so läuft es

DANKE !!

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.