Forum: Mikrocontroller und Digitale Elektronik Fehler beim ADC in meinem C-programm


von fragender (Gast)


Lesenswert?

Hallo Leute,
Ich habe vor mir eine µC Steuerung für mein Netzteilprojekt zu bauen. 
Verwendet wird ein Atmega8 bei 8Mhz intern (ersmal). Da ich grad am üben 
mit C bin (etwas ehrfahrener Anfänger :D) hab ich mich mal an die ganze 
Sache mit ADC, PWM, und LCD gesetzt und versucht was zu erreichen. Ich 
hab mir zur Hilfe und zur Erklärung das AVRGCC tutorial von 
mikrocontroller.net zu rate gezogen wie man im Quelltext unschwer 
erkennen kann. Die Sachen zur ansteuerung von PWM und ADC des Atmega hab 
ich verstanden Dank dem Tutorial nur beim LCD hab ich noch nicht ganz 
alles verstanden (kommt mit der Zeit und Erfahrung :-) denk ich mal  ) 
deswegen hab ich das mal so übernommen nicht wundern das ist jetzt Ja 
auch nicht mein anliegen.

Nun ist es so das ich drei ADC kanäle (ADC0 ADC1 ADC2) nutzen möchte 
aber irgendwie trennt er die einzelnen adc ergebnisse nicht von den 
Aufgaben was damit geschehen soll (siehe unten bei Int main(void)) also 
muss ja meinerseits ein Fehler sein.Könnt ihr mir helfen/sagen wie man 
DIESEN Fehler behebt?

(Ja ich weiß der Code ist naja nicht der beste aber naja)

1
#define F_CPU 8000000            //interner 1MHz OSzillator
2
#include <avr/io.h>
3
#include <lcd_routines.h>
4
#include <util/delay.h>
5
#include <stdlib.h>
6
#include <stdio.h>
7
8
void ADC_Init(void){            //ADC initialisieren
9
  
10
  uint16_t result;            //Ergebnisvariable "result"
11
  
12
  ADMUX = (1<<REFS0);            //Referenzspannung von Avcc  
13
  
14
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);    //Vorteiler = 8
15
  ADCSRA = (1<<ADEN);            //ADC aktivieren
16
  
17
  result = ADCW;
18
    
19
}
20
21
/* ADC Einzelmessung */
22
uint16_t ADC_Read( uint8_t channel )
23
{
24
  // Kanal waehlen, ohne andere Bits zu beeinflußen
25
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
26
  ADCSRA |= (1<<ADSC);          // eine Wandlung "single conversion"
27
  while (ADCSRA & (1<<ADSC) ) {      // auf Abschluss der Konvertierung warten
28
  }
29
  return (ADCW);              // ADC auslesen und zurückgeben
30
}
31
32
/* ADC Mehrfachmessung mit Mittelwertbbildung */
33
/* beachte: Wertebereich der Summenvariablen */
34
uint16_t ADC_Read_Avg( uint8_t channel, uint8_t nsamples )
35
{
36
  uint32_t sum = 0;
37
  
38
  for (uint8_t i = 0; i < nsamples; ++i ) {
39
    sum += ADC_Read( channel );
40
  }
41
  
42
  return (uint16_t)( sum / nsamples );
43
}
44
45
 ////////////////////////////////////////////////////////////////////////////////
46
 // Erzeugt einen Enable-Puls
47
 static void lcd_enable( void )
48
 {
49
   LCD_PORT |= (1<<LCD_EN);     // Enable auf 1 setzen
50
   _delay_us( LCD_ENABLE_US );  // kurze Pause
51
   LCD_PORT &= ~(1<<LCD_EN);    // Enable auf 0 setzen
52
 }
53
 
54
 ////////////////////////////////////////////////////////////////////////////////
55
 // Sendet eine 4-bit Ausgabeoperation an das LCD
56
 static void lcd_out( uint8_t data )
57
 {
58
   data &= 0xF0;                       // obere 4 Bit maskieren
59
   
60
   LCD_PORT &= ~(0xF0>>(4-LCD_DB));    // Maske löschen
61
   LCD_PORT |= (data>>(4-LCD_DB));     // Bits setzen
62
   lcd_enable();
63
 }
64
 
65
 ////////////////////////////////////////////////////////////////////////////////
66
 // Initialisierung: muss ganz am Anfang des Programms aufgerufen werden.
67
 void lcd_init( void )
68
 {
69
   // verwendete Pins auf Ausgang schalten
70
   uint8_t pins = (0x0F << LCD_DB) |           // 4 Datenleitungen
71
   (1<<LCD_RS) |                // R/S Leitung
72
   (1<<LCD_EN);                 // Enable Leitung
73
   LCD_DDR |= pins;
74
   
75
   // initial alle Ausgänge auf Null
76
   LCD_PORT &= ~pins;
77
   
78
   // warten auf die Bereitschaft des LCD
79
   _delay_ms( LCD_BOOTUP_MS );
80
   
81
   // Soft-Reset muss 3mal hintereinander gesendet werden zur Initialisierung
82
   lcd_out( LCD_SOFT_RESET );
83
   _delay_ms( LCD_SOFT_RESET_MS1 );
84
   
85
   lcd_enable();
86
   _delay_ms( LCD_SOFT_RESET_MS2 );
87
   
88
   lcd_enable();
89
   _delay_ms( LCD_SOFT_RESET_MS3 );
90
   
91
   // 4-bit Modus aktivieren
92
   lcd_out( LCD_SET_FUNCTION |
93
   LCD_FUNCTION_4BIT );
94
   _delay_ms( LCD_SET_4BITMODE_MS );
95
   
96
   // 4-bit Modus / 2 Zeilen / 5x7
97
   lcd_command( LCD_SET_FUNCTION |
98
   LCD_FUNCTION_4BIT |
99
   LCD_FUNCTION_2LINE |
100
   LCD_FUNCTION_5X7 );
101
   
102
   // Display ein / Cursor aus / Blinken aus
103
   lcd_command( LCD_SET_DISPLAY |
104
   LCD_DISPLAY_ON |
105
   LCD_CURSOR_OFF |
106
   LCD_BLINKING_OFF);
107
   
108
   // Cursor inkrement / kein Scrollen
109
   lcd_command( LCD_SET_ENTRY |
110
   LCD_ENTRY_INCREASE |
111
   LCD_ENTRY_NOSHIFT );
112
   
113
   lcd_clear();
114
 }
115
 
116
 ////////////////////////////////////////////////////////////////////////////////
117
 // Sendet ein Datenbyte an das LCD
118
 void lcd_data( uint8_t data )
119
 {
120
   LCD_PORT |= (1<<LCD_RS);    // RS auf 1 setzen
121
   
122
   lcd_out( data );            // zuerst die oberen,
123
   lcd_out( data<<4 );         // dann die unteren 4 Bit senden
124
   
125
   _delay_us( LCD_WRITEDATA_US );
126
 }
127
 
128
 ////////////////////////////////////////////////////////////////////////////////
129
 // Sendet einen Befehl an das LCD
130
 void lcd_command( uint8_t data )
131
 {
132
   LCD_PORT &= ~(1<<LCD_RS);    // RS auf 0 setzen
133
   
134
   lcd_out( data );             // zuerst die oberen,
135
   lcd_out( data<<4 );           // dann die unteren 4 Bit senden
136
   
137
   _delay_us( LCD_COMMAND_US );
138
 }
139
 
140
 ////////////////////////////////////////////////////////////////////////////////
141
 // Sendet den Befehl zur Löschung des Displays
142
 void lcd_clear( void )
143
 {
144
   lcd_command( LCD_CLEAR_DISPLAY );
145
   _delay_ms( LCD_CLEAR_DISPLAY_MS );
146
 }
147
 
148
 ////////////////////////////////////////////////////////////////////////////////
149
 // Sendet den Befehl: Cursor Home
150
 void lcd_home( void )
151
 {
152
   lcd_command( LCD_CURSOR_HOME );
153
   _delay_ms( LCD_CURSOR_HOME_MS );
154
 }
155
 
156
 ////////////////////////////////////////////////////////////////////////////////
157
 // Setzt den Cursor in Spalte x (0..15) Zeile y (1..4)
158
 
159
 void lcd_setcursor( uint8_t x, uint8_t y )
160
 {
161
   uint8_t data;
162
   
163
   switch (y)
164
   {
165
     case 1:    // 1. Zeile
166
     data = LCD_SET_DDADR + LCD_DDADR_LINE1 + x;
167
     break;
168
     
169
     case 2:    // 2. Zeile
170
     data = LCD_SET_DDADR + LCD_DDADR_LINE2 + x;
171
     break;
172
     
173
     case 3:    // 3. Zeile
174
     data = LCD_SET_DDADR + LCD_DDADR_LINE3 + x;
175
     break;
176
     
177
     case 4:    // 4. Zeile
178
     data = LCD_SET_DDADR + LCD_DDADR_LINE4 + x;
179
     break;
180
     
181
     default:
182
     return;                                   // für den Fall einer falschen Zeile
183
   }
184
   
185
   lcd_command( data );
186
 }
187
 
188
 ///////
189
 /////////////////////////////////////////////////////////////////////////
190
 // Schreibt einen String auf das LCD
191
 
192
 void lcd_string( const char *data )
193
 {
194
   while( *data != '\0' )
195
   lcd_data( *data++ );
196
 }
197
 
198
 ////////////////////////////////////////////////////////////////////////////////
199
 // Schreibt ein Zeichen in den Character Generator RAM
200
 
201
 void lcd_generatechar( uint8_t code, const uint8_t *data )
202
 {
203
   // Startposition des Zeichens einstellen
204
   lcd_command( LCD_SET_CGADR | (code<<3) );
205
   
206
   // Bitmuster übertragen
207
   for ( uint8_t i=0; i<8; i++ )
208
   {
209
     lcd_data( data[i] );
210
   }
211
}   
212
213
214
void st(void){
215
  while (bit_is_set(PINB,0)){}
216
  PORTB |= _BV(PB4);
217
  lcd_setcursor(0,1);
218
  lcd_string("  PSU 30/1500");
219
  lcd_setcursor(0,2);
220
  lcd_string("(Soft.-vers 1.1)");
221
  _delay_ms(2000);
222
  PORTB |= _BV(PB5);
223
  lcd_clear();
224
  lcd_setcursor(15,1);
225
  lcd_string("V");
226
  lcd_setcursor(6,1);
227
  lcd_string("A");
228
}
229
230
231
void standby(void){
232
  lcd_clear();
233
  lcd_setcursor(1,1);
234
  lcd_string("   Goodbye!");
235
  _delay_ms(2000);
236
  PORTB &= ~_BV(PB4);
237
  PORTB &= ~_BV(PB5);
238
  while (bit_is_set(PINB,0)){}
239
  st();
240
  
241
}
242
243
int main(void){
244
  
245
  DDRB = 0b11110010;
246
  PORTB = PORTB | _BV(PB0) | _BV(PB2) | _BV(PB3);
247
  TCCR1A = (1<<WGM11) | (1<<WGM10) | (1<<COM1A1); 
248
  TCCR1B = (1<<CS10); 
249
  
250
  ADC_Init();
251
  lcd_init();
252
  st();
253
    
254
  uint16_t delay = 0;
255
  uint16_t adcval;
256
  uint16_t volt;
257
  uint16_t amp;
258
  double resv;
259
  double resa;
260
  char strv[10];
261
  char stra[10];
262
  
263
  while(1){
264
    
265
  adcval = ADC_Read(0);
266
  volt = ADC_Read(1);
267
  amp = ADC_Read(2);
268
  OCR1A = adcval;
269
  
270
  if (bit_is_clear(PINB,2)){
271
    standby();  
272
  }  
273
  
274
  if (delay > 40000){
275
      resa = amp;
276
      resa /= 100 * 4.8876;
277
      dtostrf(resa, 5, 2, stra);
278
      lcd_setcursor(0,1);
279
      lcd_string(stra);
280
      
281
      resv = volt;
282
      resv /= 1000 * 4.8876;
283
      dtostrf(resv, 5, 2, strv);
284
      lcd_setcursor(9,1);
285
      lcd_string(strv);
286
      delay = 0;
287
  }
288
  delay ++;
289
  }
290
}

von holger (Gast)


Lesenswert?

>aber irgendwie trennt er die einzelnen adc ergebnisse nicht von den
>Aufgaben was damit geschehen soll

Bahnhof?

von hawo1997 (Gast)


Lesenswert?

Es geht mir halt darum das ich ich egal an welchen pin ich eine Spannung 
anschließe (von den adc pins) das die Ergebnisse in den variablen "volt" 
"amp" und "adcval" gleich sind obwohl sie ja mit

adcval = ADC_Read(0); volt = ADC_Read(1); amp = ADC_Read(2);

eigendlich jede von einem anderen adc pin (halt adc0 adc1 und 
adc2)festgestellt wurden
so denke ich das ich wahrscheinlich für jede variable den gleichen adc 
kanal festgelegt hab nur warum passiert das an jedem adc pin von adc0 - 
5?...

ich vermute das da der fehler liegt.

wenn ja wie behebt man ihn?

sorry für das missachten von groß und Kleinschreibung

Gruß hawo

von spess53 (Gast)


Lesenswert?

Hi

#define F_CPU 8000000            //interner 1MHz OSzillator

Was nun, 8MHz oder 1MHz?

>  ADCSRA = (1<<ADPS1) | (1<<ADPS0);    //Vorteiler = 8

Passend für 1MHz. Für 8MHz zu wenig.

MfG Spess

von al_guesto (Gast)


Lesenswert?

hawo1997 schrieb:
> Es geht mir halt darum das ich ich egal an welchen pin ich eine Spannung
> anschließe (von den adc pins) das die Ergebnisse in den variablen "volt"
> "amp" und "adcval" gleich sind obwohl sie ja mit

Was ist denn gleich? =0 oder =1023 oder passt das Ergebnis wenigstens 
zur angelegten Spannung, also wenn du an ADC0 die Spannung anlegst ist 
das Ergebnis für ADC0 korrekt und quasi "nur" an den anderen beiden 
Kanälen falsch?

von Uwe (de0508)


Lesenswert?

Lege mal alle ADC Eingänge mit 10k Ohm auf Masse, dann wird auch der 
sample and hold Kondensator umgeladen. Bei offenen Eingängen geht das 
nicht!

von fragender (Gast)


Lesenswert?

Es ist so das ich ja mit

  adcval = ADC_Read(0);
  volt = ADC_Read(1);
  amp = ADC_Read(2);

so dachte ich jede Variable auf einen eigenen ADC channel gepackt habe. 
(adcval an ADC0, volt an ADC1 und amp an ADC2) so dachte ich. Jetzt ist 
es so das adcval zum steuern der PWM an PB1 ist, volt und amp für 
Messung von Strom und Spannung Also müsste ja nach dem was ich grad 
sagte die PWM an ADC0 (=PC0), die zu messende Spannung (volt) an ADC1 
(=PC1) und der zu messende Strom (amp) an ADC2 (=PC2) anliegen. Nun ist 
es aber so das ich wenn ich mit einem Poti zwischen GND und +5V an EINEN 
der drei ADCs gehe werden immer alle drei Sachen (PWM, Spannungsanzeige 
und Stromanzeige) geändert egal ob an ADC0 ADC1 oder ADC2 und das ist 
das was nicht läuft.

Und mein anliegen ist es wie man das jetzt korrigiert.

MfG
hawo

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

fragender schrieb:
> Nun ist
> es aber so das ich wenn ich mit einem Poti zwischen GND und +5V an EINEN
> der drei ADCs gehe werden immer alle drei Sachen (PWM, Spannungsanzeige
> und Stromanzeige) geändert egal ob an ADC0 ADC1 oder ADC2 und das ist
> das was nicht läuft.

Das liegt daran, das unbeschaltete ADC Eingänge am S&H Kondensator 
'kleben'. Du musst alle Kanäle mit Potis oder anderen Spannungsquellen 
beschicken und dann mal messen.

von fragender (Gast)


Lesenswert?

ok mach ich mal

von fragender (Gast)


Lesenswert?

Achso ... ich probiers mal aus

MfG

von fragender (Gast)


Lesenswert?

So mit den Widerständen von den ADCs zu Masse hat es mehr oder weniger 
geklappt naja ... die werte von den anderen ADCs verändern sich nicht 
mehr bis nur noch sehr wenig aber der ADCwert ist höchstens nur noch 560 
anstatt 1023 egal welchen wert das Poti oder die Widerstände haben.

von Karl H. (kbuchegg)


Lesenswert?

fragender schrieb:
> So mit den Widerständen von den ADCs zu Masse hat es mehr oder weniger
> geklappt naja ... die werte von den anderen ADCs verändern sich nicht
> mehr bis nur noch sehr wenig aber der ADCwert ist höchstens nur noch 560
> anstatt 1023 egal welchen wert das Poti oder die Widerstände haben.

Nur zur Sicherheit:
Du hast aber nicht auch an dem Eingang, an dem das Poti hängt auch noch 
einen Widerstand zugeschaltet? Oder?

Du brauchst
* entweder Poti
* oder Widerstand

: Bearbeitet durch User
von fragender (Gast)


Lesenswert?

Nein hab ich ich nicht.
An ADC0 hängt ein Poti
An ADC1 und 2 ein Widerstand gegen Masse.

von fragender (Gast)


Angehängte Dateien:

Lesenswert?

Also ich hab jetzt an ADC0 ein 200k Poti und an ADC1 und 2 jeweils einen 
7,5k Widerstand gegen Masse geschaltet. Das Poti steht auf Vollanschlag 
richtung Vcc (4,9V) dir gemessene Spannung An PortC.0 des Atmega beträgt 
4.85V. Im Bild sieh man die ADC Werte von ADC0 ADC1 und ADC2.

MfG

von fragender (Gast)


Lesenswert?

Ich verstehe es nicht mit einem einzelnen ADC geht es ja.

von Reiner F. (reiner)


Lesenswert?

Hallo,
ich habe den Beitrag interessiert mitgelesen und bin jetzt neugierig.
Ist das Problem gelöst? Woran lag's?

von Uwe (Gast)


Lesenswert?

> Also ich hab jetzt an ADC0 ein 200k Poti
zu hochohmig

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

spess53 schrieb:
> Hi
>
> #define F_CPU 8000000            //interner 1MHz OSzillator
>
> Was nun, 8MHz oder 1MHz?
>
>>  ADCSRA = (1<<ADPS1) | (1<<ADPS0);    //Vorteiler = 8
>
> Passend für 1MHz. Für 8MHz zu wenig.
>
> MfG Spess

Hast du das korrigiert? Les dir nochmal die ADC Kapitel im Datenblatt 
durch und setzt den ADC auf eine Samplerate, die er auch verträgt:
"By default, the successive approximation circuitry requires an input 
clock frequency between 50kHz and 200 kHz to get maximum resolution. If 
a lower resolution than 10 bits is needed, the input clock frequency to 
the ADC can be higher than 200 kHz to get a higher sample rate."

Du musst also 8Mhz mindestens durch 32 teilen (= 250kHz, immer noch 
knapp zu viel), um die volle Auflösung zu bekommen, besser ist 64 als 
Teilerfaktor.

: Bearbeitet durch User
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.