Forum: Mikrocontroller und Digitale Elektronik ATmega88PA mehrere ADC Kanäle auslesen


von eTchz (Gast)


Angehängte Dateien:

Lesenswert?

Guten Tag liebe Community,

Ich möchte 4 ADC Kanäle des ATmega88PA auslesen und die digitalisierten 
Werte via uart an meinen Rechner schicken.

Harware:

Ich habe 4 Potis, an einem Labornetzteil bei der Spannung 3,3V 
angeschlossen, den Abgriff je an einen ADC Pin des Mikrocontrollers.
Zur seriellen Übertragung verwende ich eine "Silicon Labs CP210x USB to 
UART Bridge".
Referenzspannung des ADC sind ebenfalls 3,3V.

Der Code:
1
#include <avr/io.h>
2
#define F_CPU 1000000UL
3
#include <util/delay.h>
4
#include <avr/interrupt.h>
5
#include <avr/pgmspace.h>
6
#include <stdlib.h>
7
#include "uart.h"
8
9
10
#define BOOT_UART_BAUD_RATE 9600
11
12
13
uint16_t readADC(uint8_t channel) {
14
  uint8_t i;
15
  uint16_t result = 0;
16
    
17
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);  
18
  ADMUX = channel | (1<<REFS1) | (1<<REFS0);  
19
  
20
  ADCSRA |= (1<<ADSC);
21
  while(ADCSRA & (1<<ADSC));  
22
  
23
  for(i=0; i<3; i++) {    
24
    ADCSRA |= (1<<ADSC);    
25
    while(ADCSRA & (1<<ADSC));    
26
    result += ADCW;
27
  }
28
  
29
  result /= 3;  
30
  return result;
31
}
32
33
34
int main(void)
35
{
36
  uart_init(UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU));
37
  sei();  
38
  
39
  uint16_t adc_ch1 = 0;
40
  uint16_t adc_ch2 = 0;
41
  uint16_t adc_ch3 = 0;
42
  uint16_t adc_ch4 = 0;
43
44
  char ch1_chr[] = "";
45
  char ch2_chr[] = "";
46
  char ch3_chr[] = "";
47
  char ch4_chr[] = "";
48
49
  while(1){
50
    
51
    adc_ch1 = readADC(0);
52
    adc_ch2 = readADC(1);
53
    adc_ch3 = readADC(2);
54
    adc_ch4 = readADC(3);  
55
56
    itoa(adc_ch1, ch1_chr, 10);
57
    itoa(adc_ch2, ch2_chr, 10);
58
    itoa(adc_ch3, ch3_chr, 10);
59
    itoa(adc_ch4, ch4_chr, 10);
60
61
    uart_puts(ch1_chr);
62
    uart_puts(",");
63
    uart_puts(ch2_chr);
64
    uart_puts(",");
65
    uart_puts(ch3_chr);
66
    uart_puts(",");
67
    uart_puts(ch4_chr);
68
    uart_puts(";");
69
  }
70
}

Problem:

Im Anhang ist ein Screenshot des Outputs der im Terminal Programm zu 
sehen ist.
Alle Potis befinden sich etwa in Mittelstellung und sollten daher einen 
Wert von ~500 ausgeben.
Ich habe die Software zur Fehleranalyse einmal soweit abgespeckt, dass 
ich nur einen Kanal auslese und das Ergebnis ausgebe, dies hat tadellos 
funktioniert.

Sobald ich allerdings auf zwei Kanäle erweitere tritt der im Screenshot 
zu sehende Fehlerfall ein.

Ich hoffe auf schnelle und konstruktive Antworten.

MfG

eTchz

von Rainer U. (r-u)


Lesenswert?

irgendwie meine ich mich zu erinnern, das es mir damals geholfen hat, 
nach dem Umschalten die erste Messung zu verwerfen oder zu warten / bin 
aber nicht mehr sicher - versuch mal.

von Axel S. (a-za-z0-9)


Lesenswert?

eTchz schrieb:
1
...
2
>   uint16_t adc_ch1 = 0;
3
>   uint16_t adc_ch2 = 0;
4
>   uint16_t adc_ch3 = 0;
5
>   uint16_t adc_ch4 = 0;
6
> 
7
>   char ch1_chr[] = "";
8
>   char ch2_chr[] = "";
9
>   char ch3_chr[] = "";
10
>   char ch4_chr[] = "";
11
...
12
>     itoa(adc_ch1, ch1_chr, 10);
13
>     itoa(adc_ch2, ch2_chr, 10);
14
>     itoa(adc_ch3, ch3_chr, 10);
15
>     itoa(adc_ch4, ch4_chr, 10);

Aua. itoa() braucht einen Buffer, in den es den String schreibt. Mit
1
char ch1_chr[] = "";

legst du aber nur einen String der Länge 0 an. In den paßt dann auch 
nichts größeres als ein String der Länge 0. Was dein itoa() Aufruf dann 
macht, ist über das Ende des Buffers hinauszuschreiben und dabei andere 
Variablen zu überschreiben. Welche das sind, hängt davon ab wie der 
Compiler die Variablen im RAM plaziert hat.

Auf einem PC hättest du im Ergebnis wahrscheinlich einen schönen 
segmentation fault (vulgo: Crash) bekommen.

Einen Buffer legt man an, indem man ein char-Array mit einer bestimmten 
Länge definiert. Ein 16-Bit uint kann bis zu 5 Dezimalstellen ergeben. 
Dazu noch das \0 ans Endekennzeichen macht mindestens 6 Zeichen 
Buffer-Länge:
1
char ch1_chr[6];

Dito für die anderen Buffer.

: Bearbeitet durch User
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

eTchz schrieb:
> char ch1_chr[] = "";
>   char ch2_chr[] = "";
>   char ch3_chr[] = "";
>   char ch4_chr[] = "";

Hmm, was meinst du denn, wieviel Speicher du für die Buffer reservierst?
Icj zitiere mal kurz aus der avr-libc Hilfe zu itoa:
"Convert an integer to a string.

The function itoa() converts the integer value from val into an ASCII 
representation that will be stored under s. The caller is responsible 
for providing sufficient storage in s"

Edit: Zu spät, Axel war schneller.

: Bearbeitet durch User
von eTchz (Gast)


Lesenswert?

Hallo Liebe Poster,

Vielen Dank für die Ratschläge!
Ich konnte das Problem lösen, vielleicht nicht mit dem Höchstmaß an 
Eleganz aber mit voller Funktionalität.

Ich habe für jeden Kanal eine eigene Funktion zum Auslesen geschrieben.
Der Code ist zwar jetzt um einiges größer, aber wie gesagt, es 
funktioniert.

Ich werde mir eure Tipps trotzdem zu Gemüte führen und sie in die 
Optimierung einfließen lassen.

Nochmals vielen Dank und einen schönen Tag wünsche ich!

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.