Hallo Leute,
ich versuch mich gerade das erste Mal am ADC des ATmega32. Beschaltet
habe ich den ATmega wie folgt:
AVCC -> Direkt an VCC = 3,3V
AREF -> Mit einem 100nF Kondensator an GND
ADC0 -> Potentiometer zwischen VCC und GND, am Mittelabgriff hängt ADC0
Parallel zum ADC, also zwischen ADC0 und GND hängt noch ein Multimeter,
mit dem ich die Spannung am ADC-Eingang kontrollieren kann. Hiermit
überwache ich auich, dass die Spannung nicht über 2,56V geht (interne
Referenzspannung gewählt).
Der Code auf dem ATmega sieht wie folgt aus:
Die Main-Datei:
1
#include"uart.h"
2
#include"ADC.h"
3
4
#include<avr/io.h>
5
#include<util/delay.h>
6
#include<string.h>
7
#include<stdio.h>
8
9
intmain(void)
10
{
11
uint16_tadc_val;
12
doublevolt_val;
13
charbuf[50];
14
15
// Initialize the UART
16
init_UART(9600);
17
// Initialize the ADC
18
init_ADC();
19
20
while(1)
21
{
22
// Get a new voltage value
23
adc_val=readNewValueFromADC();
24
25
sprintf(buf,"The actual adc value is: %d \n\r",adc_val);
26
uart_write_msg(buf,strlen(buf));
27
28
_delay_ms(1000);
29
}
30
}
Und die ADC-Datei:
1
#include<avr/io.h>
2
#include"ADC.h"
3
#include"uart.h"
4
5
uint16_tADC_Read(uint8_tchannel);
6
7
// Function to initialize the ADC
8
voidinit_ADC(void)
9
{
10
uint16_tresult;
11
12
// Select the reference voltage source (internal reference)
13
ADMUX=(1<<REFS0)|(1<<REFS1);
14
15
// Select prescaler to clk/64
16
ADCSRA=(1<<ADPS2)|(1<<ADPS1);
17
18
// Enable the ADC
19
ADCSRA|=(1<<ADEN);
20
21
// Do one conversion, for start-up of the ADC
22
ADCSRA|=(1<<ADSC);
23
// Wait for conversion complete
24
while(ADCSRA&(1<<ADSC)){}
25
26
// T>Dummy read first adc-value
27
result=ADCW;
28
}
29
30
// Function to read a new value from the ADC
31
uint16_treadNewValueFromADC(void)
32
{
33
// Select channel 0
34
ADMUX=(ADMUX&~(0x1F))|(0&0x1F);
35
36
// Single conversion
37
ADCSRA|=(1<<ADSC);
38
// Wait for conversion complete
39
while(ADCSRA&(1<<ADSC)){}
40
41
// Return ADC-Value
42
returnADCW;
43
}
Im Prinzip ein ganz einfaches Programm, dass mir auf der Konsole am PC
jede Sekunde den aktuellen ADC-Wert anzeigen soll.
Leider pendelt der ausgegebene Wert einfach nur um 470 herum, also 460,
475, 455, 483, ... unabhängig davon ob ich am Potentiometer drehe.
Wo liegt mein Fehler?
Jens K. schrieb:> Wo liegt mein Fehler?
Was mir spontan einfällt:
- Wie stabil ist Vcc? Das liefert Dir ja über das Poti den Messwert.
- Ist Vcc mit Stützkondenstor abgeblockt?
- Wie hochohmig ist das Poti? Lt. Datenblatt sollte die Quellimpedanz
max. 10kOhm sein. Eventuell auch einen Filterkondensator an ADC0 hängen.
- "Wilder" Gesamtaufbau / Masseschleifen ...
Die Initialisierung, besonders den Prescaler habe ich jetzt nicht
kontrolliert.
Gruß Dietrich
- VCC ist recht stabil, wird von einem Linearregler erzeugt
- VCC hat einen 100nF Stützkondensator
- Das Poti hat maximal 5K
- Hatte ich vergessen: zwischen ADC0 und GND hängt auch ein 100nF
Kondensator
- Der Aufbau ist zwar auf einem Steckbrett, aber an sich relativ
ordentlich würde ich sagen.
Außerdem müsste man ja, selbst bei Störeinkopplungen, Änderungen am
ADC-Wert sehen, wenn ich am Poti drehe. Es treten aber immer die
besagten Werte auf, unabhängig von der Position des Potentiometers.
Mehrere Messungen auf addieren und durch die Anzahl teilen,
dann hast Du per Software den Wert gemittelt.
Oft kommt es ja nicht darauf an, ob eine Spannung z.B.
4,72 V oder 4,74 V oder 4,67 Volt ist.
Gerundet ergibt sich halt 4,7 Volt.
Jens K. schrieb:> Es treten aber immer die> besagten Werte auf, unabhängig von der Position des Potentiometers.
Das hattest Du ja schon geschrieben, ich aber leider überlesen ;-(
Dann fällt mir nichts mehr ein, höchsten Kontaktprobleme am Steckbrett.
Hast Du die ADC0-Spannung mal direkt am Pin des µC gemessen?
Gruß Dietrich
Jetzt funktioniert es! Ganz dummer Fehler:
Es war tatsächlich ein Kontaktproblem. Der Pin ADC0 des ATmega´s ist
abgebrochen und steckte somit garnicht im Board :-)
Danke für den Tipp Dietrich!
>Ich habe jetzt mal eine Mittlung über 10 Werte eingefügt.
Das ist etwas suboptimal.
Nimm 8 16 32 dann kann der Compiler den aufsummierten Wert einfach
nach rechts shiften. Das spart Zeit.
Jens K. schrieb:> Parallel zum ADC, also zwischen ADC0 und GND hängt noch ein Multimeter,> mit dem ich die Spannung am ADC-Eingang kontrollieren kann. Hiermit> überwache ich auich, dass die Spannung nicht über 2,56V geht (interne> Referenzspannung gewählt).
Um ein Poti einzulesen nimmt man Vcc als Referenz. Dann hast du immer
den vollen Wertebereich von Anschlag zu Anschlag. Die Spannung ist
dabei egal, da das Verhältnis der beiden Widerstände bestimmt wird. Und
das ist unabhängig von der Spannung immer gleich.
mfg.
Bastler schrieb:> Das ist etwas suboptimal.> Nimm 8 16 32 dann kann der Compiler den aufsummierten Wert einfach> nach rechts shiften. Das spart Zeit.
genau
mal mein Codeschnipsel
Jetzt habe ich allerdings ein anderes Problem. Mein Multimeter
(eigentlich ziemlich genau) zeigt einen Messwert von z.B. 0,986V. Am PC
erhalte ich aber einen ADC-Wert von ca. 480, was nach meiner Umrechnung
U = (2,56 * ADC) / 1024
einen Spannungswert von 1,2V ergibt. Wodurch ensteht diese große
Abweichung? Und vorallem, wie kann man diese Kompensieren?
Schön.
Aber leider falsch.
Jungs. Eure Compilerbauer sind keine Vollidioten. Die haben ihrem
Compiler schon beigebracht, wann man eine DIvision durch ein Schieben
ersetzen kann! Das brauchst du nicht selber im Code machen. Wenn die
logische Operation eine Division ist, dann schreib auch Division hin.
Dann machst du auch nicht so dumme Fehler, dass du 3 Messwerte
summierst, dann aber durch 8 dividierst.
Schreib den Code so, wie ihn jeder erwarten würde!
1
#define READS 8U
2
3
uint16_tresult=0;
4
for(i=0;i<READS;i++)
5
{// Eine Wandlung
6
ADCSRA|=(1<<ADSC);
7
// Auf Ergebnis warten...
8
while(ADCSRA&(1<<ADSC));
9
10
result+=ADCW;// aufsummieren
11
}
12
13
result/=READS;// Mittelwertbildung
14
}
Und wenn der Wert bei READS derart ist, dass man die Division durch eine
SChiebeoperation ersetzen kann (kurz gesagt: wenn er eine 2-er Potenz
ist), dann ersetzt der Compiler die Division durch eine
Schiebeoperation. Das ist nichts, worüber man sich als Programmierer
groß Gedanken machen muss. Wenn schon, dann mach dir darüber Gedanken,
wie du den Wert bei READS so schreiben kannst, dass es möglichst
schwierig ist, eine Nicht-2-er Potenz hinzuschreiben. Aber die
Optimierung fürs Schieben überlässt du trotzdem dem Compiler. Denn
irgendwann kommt ein genialer Nachfolger, der aus dem Konstrukt
1
#define NR_READ_BASE 3
2
#define READS (1 << NR_READ_BASE )
mit dem du eine 2-er POtenz erzwingen willst, dann doch wieder eine
schöde 5 für die Anzahl der Messungen macht und dann erst mal 10 Minuten
suchen muss, bis er rausfindet warum seine Werte nicht stimmen.
Ich habe auch keine Schiebeoperation eingetzt? Ich habe die Werte
einfach aufsummiert und am Ende durch die Anzahl der Aufsummierungen
dividiert. Das ergibt dann doch den Mittelwert oder sehe ich das falsch?
Jens K. schrieb:> Ich habe auch keine Schiebeoperation eingetzt? Ich habe die Werte> einfach aufsummiert und am Ende durch die Anzahl der Aufsummierungen> dividiert. Das ergibt dann doch den Mittelwert oder sehe ich das falsch?
Passt schon.
Aber es gibt immer Schlauberger, die meinen, sie müssten selbst im C
Code 'clevere' Optimierungen anbringen, die jeder Wald und Wiesen
Nasenpopel Compiler, der in den letzten 40 Jahren geschrieben wurde,
perfekt und aus dem FF beherrscht.
Und pikanterweise hauen sie sich dann bei dieser 'cleveren Optimierung'
einen Hund hinein, der dem Compiler nicht passiert wäre.
Jens K. schrieb:> Jetzt habe ich allerdings ein anderes Problem. Mein Multimeter> (eigentlich ziemlich genau) zeigt einen Messwert von z.B. 0,986V. Am PC> erhalte ich aber einen ADC-Wert von ca. 480, was nach meiner Umrechnung>> U = (2,56 * ADC) / 1024>> einen Spannungswert von 1,2V ergibt. Wodurch ensteht diese große> Abweichung? Und vorallem, wie kann man diese Kompensieren?
Hast du schon mal zurückgerechnet:
vom Messwert und der bekannten Spannung auf die Referenzspannung.
Denn die Referenzspannung ist vieles, vor allen Dingen ist sie in der
Höhe stabil und konstant. Sie ist allerdings nicht 2.56V.
2.56V ist ein typischer Wert, der im Einzelfall abweichen kann und für
dein µC Examplar festgestellt werden müsste. Welche Spannung misst du
denn am AREF Pin?
Karl Heinz schrieb:> Schön.> Aber leider falsch.
das begründe mal du Schlaumeier ! was soll daran falsch sein !
EDIT ah das hier Zeile vergessen
for(i=0; i<(1<<READS); i++)
hättest du auch sagen können
> Jungs. Eure Compilerbauer sind keine Vollidioten.
das mag sein und interessiert mich nicht die Bohne !
kenne ich alle Kompilerbauer ? weiss ich wie die ticken ? und nein ich
habe keinen Bock in den compilierten Code zu schauen.
Ob und wie die Bauer optimieren ist mir doch Banane, glauben tut man wo
anders, DU glaubst das alle Compilerbauer das richtig machen, ich weiss
es nicht, schaue auch nicht nach und bleibe bei meinem Code der nicht
falsch ist und im Ergebnis dasselbe wie die Division liefert.
ich hatte auch noch nie die Aref messen können die ich eingestellt
hatte.
Deswegen messe ich die am AREF Pin und nehme den Messwert als
Rechengrundlage und nicht die theoretisch eingestellte AREF
Joachim B. schrieb:> Karl Heinz schrieb:>> Schön.>> Aber leider falsch.>> das begründe mal du Schlaumeier ! was soll daran falsch sein !>> EDIT ah das hier Zeile vergessen> for(i=0; i<(1<<READS); i++)>> hättest du auch sagen können
Ja, wenn man darauf aufmerksam gemacht wird, ist es immer leicht
hinterher Fehler zu finden. Bei den paar Zeilen Code ist das ja auch
keine Hexerei.
>> Jungs. Eure Compilerbauer sind keine Vollidioten.>> das mag sein und interessiert mich nicht die Bohne !
Schön.
Dann mach weiter deine Blödsinnsfehler. Aber hör auf, anderen deinen
Unsinn einzureden.
> und bleibe bei meinem Code der nicht falsch ist
Doch, das waer er. Nachweislich.
Was du programmierst oder nicht programmierst ist mir wurscht. Und auch
wenn ich auch manchmal Fehler im Code habe, den ich hier veröffentliche,
so sind es wenigstens nicht solche 'Ich schiess mir selber ins Knie und
bin auch noch stolz drauf was für ein toller Trick-Programmierer ich
doch nicht bin' Fehler. Und vor allen Dingen reagiere ich anders, wenn
mir jemand einen Fehler in meinem veröffentlichten Code zeigt.
Karl Heinz schrieb:> Und vor allen Dingen reagiere ich anders, wenn> mir jemand einen Fehler in meinem veröffentlichten Code zeigt.
da hast du zum Teil recht, nur frag ich mich wo du einen Fehler gezeigt
hast ?
Du hast erst mal falsch gerufen, Fehler gezeigt hast du nicht, klingt
komisch ist aber so.
Ich weiss auch nicht was dich am Code stört nur weil er dir nicht
schmeckt.
Ich freue mich für dich das du alles besser weisst, nur was hat das mit
dem zu tun ob der eine lieber shiftet und der andere dividiert und
deiner Meinung nach alle Compiler das gleich optimieren ?
sonst zickst du doch nicht so rum.......
Joachim B. schrieb:> da hast du zum Teil recht, nur frag ich mich wo du einen Fehler gezeigt> hast ?> Du hast erst mal falsch gerufen, Fehler gezeigt hast du nicht, klingt> komisch ist aber so.
eventuell hier?
Karl Heinz schrieb:> Joachim B. schrieb:>>>> #define READS 3>> // oder 4 oder 5 nach belieben>> uint16_t result=0;>> for(i=0; i<READS; i++)>> { // Eine Wandlung>> ADCSRA |= (1<<ADSC);>> // Auf Ergebnis warten...>> while(ADCSRA & (1<<ADSC));>>>> result += ADCW; // aufsummieren>> }>> result >>= READS; // Mittelwertbildung>>> [..]> Dann machst du auch nicht so dumme Fehler, dass du 3 Messwerte> summierst, dann aber durch 8 dividierst.
lesen will gelernt sein schrieb:> eventuell hier?lesen will gelernt sein schrieb:>> Dann machst du auch nicht so dumme Fehler, dass du 3 Messwerte>> summierst, dann aber durch 8 dividierst.
dazu müsste man verstehen was gemeint war, lesen gelernt reicht manchmal
nicht !
der Fehler war hier:
for(i=0; i<READS; i++)
die Korrektur so:
for(i=0; i<(1<<READS); i++)
Joachim B. schrieb:>> der Fehler war hier:>
wir alle wissen was der eigentliche Fehler war und wie er entstanden
ist.
Das ändert aber nichts daran, dass ein unbedarfter Neuling est mal
falsche Messergebnisse kriegt, die er ganz sicher nicht mit einem
fehlenden Links-Shift in der Abfragebedingung der for-Schleife in
Beziehung setzt.
Aus Fehlersicht rückwärts aufgerollt, hat er erst mal falsche
Ergebnisse. Die entstehen dadurch, dass nur 3 Messwerte summiert werden,
aber durch 8 zur Mittelwertbildung dividiert wird. Das wiederrum
entsteht dadurch, dass die 3 eiegentlich 8 sein sollten, was wiederrum
auf den vergessenen Shift zurückzuführen ist.
Das ist bereits eine Latte von Schlussfolgerungen und
Rückwärtsverfolgungen, die gerade ein Neuling erst mal bringen muss.
Und warum das Ganze?
Wegen nichts!
Und genau das ist der springende Punkt. Es ist ein Fehler der 'wegen
nichts' entstanden ist. Denn diese Optimierung macht jeder Compiler
genauso gut. Nur macht der eben dabei keinen Fehler.
Jens K. schrieb:> An AREF messe ich 2,062V. Ist das ein plausibler Wert?
Dazu gibt es das Datenblatt. Da steht:
Table 121. ADC Characteristics, Single Ended channels, TA = -40°C to
85°C
Symbol Parameter Condition Min Typ Max Units
-----------------------------------------------------------------
VINT Internal Voltage Reference 2.3 2.56 2.7 V
Dein Wert ist daher außerhalb der Spezifikation - warum auch immer ...
Gruß Dietrich
Karl Heinz schrieb:> Jungs. Eure Compilerbauer sind keine Vollidioten. Die haben ihrem> Compiler schon beigebracht, wann man eine DIvision durch ein Schieben> ersetzen kann! Das brauchst du nicht selber im Code machen.
vielleicht weil ich mal an Assembler dachte, da ist Schieben normaler
als DIV
Karl Heinz schrieb:> Aber es gibt immer Schlauberger, die meinen, sie müssten selbst im C> Code 'clevere' Optimierungen anbringen,
und was soll das willst du moderieren oder provozieren ? (das ist
einfach nur unwürdig)
Karl Heinz schrieb:> Ja, wenn man darauf aufmerksam gemacht wird, ist es immer leicht> hinterher Fehler zu finden.
den habe ich selber gefunden (c&p Fehler Zeile verrutscht) habe nur
Fehler gelesen und rübergeschaut, aber egal (der Moderator hat immer
Recht)
Karl Heinz schrieb:> Aus Fehlersicht rückwärts aufgerollt, hat er erst mal falsche> Ergebnisse.
klar und wer hier ohne Fehler ist der poste den perfekten Code ....
Karl Heinz schrieb:> Und genau das ist der springende Punkt. Es ist ein Fehler der 'wegen> nichts' entstanden ist. Denn diese Optimierung macht jeder Compiler> genauso gut.
na wenn es jeder Compiler macht dann ist es der Streit um Kaisers Bart
weil immer dasselbe rauskommt, also kann ich weiter so verfahren, mir
gefällts und bis auf den Fehler beim Einstellen sehe ich auch nix
verwerfliches was derlei derbe Worte eines Moderator rechtfertigt.
Joachim B. schrieb:>> Aber es gibt immer Schlauberger, die meinen, sie müssten selbst im C>> Code 'clevere' Optimierungen anbringen,>> und was soll das willst du moderieren oder provozieren ? (das ist> einfach nur unwürdig)
Nach dem 3000-ten Fehler, den du langwierig gesucht hast und der wieder
mal auf einen cleveren Hack des Programmierers zurückzuführen ist, mit
dem er sich selbst ausgetrickst hat, würdest du auch allergisch auf
derartige clevere Hacks reagieren.
Karl Heinz schrieb:> Nach dem 3000-ten Fehler....., würdest du auch allergisch auf> derartige clevere Hacks reagieren.
nun ja verstehe ich schon, auch ein Grund warum ich nicht mehr Moderator
sein mag. Manchmal muss man sich aber gerade als Moderator denn als User
eine Pause gönnen um danach wieder gelassener zu werden.
Es ist doch nix weiter passiert, oder ?
Ich hatte einen c & p Fehler, der Code funktioniert, ob sinnvoll darüber
kann man ja unterschiedlicher Meinung sein. Man muss auch mal andere,
nicht unbedingt falsche, Sichtweisen akzeptieren können sonst hat man es
unnötig schwer.
Ich denke halt du warst genervt was nicht zwingend ausschliesslich an
meinem c & p Fehler lag.