Hallo!
Ich bin Anfänger im Bereich der Microcontroller und war bislang
erfolgreich beim Programmieren eines ATtiny85 unter Verwendung eines
Raspberry Pi. Die üblichen "hello world" Dinge, also LED blinken lassen
usw. funktionieren bislang alle super. Nun wollte ich den ADC verwenden
und habe mir bereits einige Tutorials/Foren durchgesehen und mich
weitestgehend an die Anleitungen gehalten.
Mein Ziel: Ich möchte eine Photodiode und einen Widerstand verwenden um
zunächst eine LED anzusteuern welche leuchten soll, wenn der ADC-Wert >
128, also größer 2.5 V ist (der Spannungsregler liefert 5V und VCC ist
Vref). Damit die Spannung messbar ist muss der Widerstand natürlich
hinreichend groß sein (R1 = 30k => bei einem Photostrom von 80 µA ergibt
das eine Spannung von 2.4 V über R1). Die Spannung habe ich natürlich
mit einem Oszilloskop überprüft und die Änderung der Spannungs
beobachtet sobald die Photodiode mit einer Taschenlampe beleutet wird.
Daher gehe ich davon aus, dass das soweit funktioniert.
Der Schaltungsaufbau ist im Anhang beigefügt, mein Code (kompiliert und
lässt sich ohne Fehlermeldung auf den ATtiny übertragen) sieht wie folgt
aus:
1
#define F_CPU 1000000L
2
#include<avr/io.h>
3
4
intmain(void){
5
//initials:
6
PORTB&=~(1<<PB3);//PB3 initially low
7
DDRB|=(1<<PB3);//PB3 is output
8
9
//ADC Multiplexer Selection Register, see p. 134 datasheet
10
ADMUX|=(1<<ADLAR)|//left shift result (for 8 bit)
11
(0<<REFS1)|//sets ref. voltage to VCC, p. 134 datasheet
12
(0<<REFS0)|//sets ref. voltage to VCC
13
(0<<MUX3)|//use ADC2 for input (PB4), MUX bit 3, see p. 135 datasheet
14
(0<<MUX2)|//use ADC2 for input (PB4), MUX bit 2
15
(1<<MUX1)|//use ADC2 for input (PB4), MUX bit 1
16
(0<<MUX0);//use ADC2 for input (PB4), MUX bit 0
17
18
//ADC Control and Status Register A, see p. 136 datasheet
19
ADCSRA|=(1<<ADEN)|//enable ADC
20
(1<<ADATE)|//enable ADC Auto Trigger Mode
21
(1<<ADPS2)|//set prescaler to 16, bit 2, see p. 136 datasheet
22
(0<<ADPS1)|//set prescaler to 16, bit 1
23
(0<<ADPS0);//set prescaler to 16, bit 0
24
//prescaler set to 16 - 64.5 kHz @ 1 MHz
25
26
while(1){
27
ADCSRA|=(1<<ADSC);//start ADC measurement
28
while(ADCSRA&(1<<ADSC));//wait until conversion is complete
29
30
if(ADCH<128){
31
PORTB&=~(1<<PB3);//turn off PB3
32
}
33
elseif(ADCH>=128){
34
PORTB|=(1<<PB3);//turn on PB3
35
}
36
}
37
return0;
38
}
Im Moment weiß ich leider nicht wo ich ansetzen soll um den Fehler zu
finden. Mir ist nicht klar ob der Fehler im Programm oder im
Schaltungsaufbau liegt. Ich würde mich sehr freuen wenn mir jemand
weiterhelfen könnte=) Vielen Dank!
Herzliche Grüße,
Christoph
Christoph schrieb:> (1<<ADATE) | //enable ADC Auto Trigger Mode
Ich bin mir jetzt nicht ganz sicher, aber soweit ich mich erinnere, wird
im Auto Trigger Mode das ADSC-Bit nicht mehr gelöscht.
Das würde bedeuten, dass du in der Warteschleife hängen bleibst
Christoph schrieb:> while(ADCSRA & (1<<ADSC)); //wait until conversion is complete
Außerdem macht Auto Trigger hier sowieso keinen Sinn, weil du den ADC ja
sowieso immer manuell startest.
Lass also mal das ADATE BIt auf 0
Funktioniert der Controller? Hast du die LED mal ohne weiteres Programm
eingeschaltet?
Du könntest ein Potentiometer an den ADC anschließen. Damit kannst du
schonmal Fehler mit der Schaltung erkennen oder ausschließen.
Das Programm scheint auf den ersten Blick gut zu sein.
Du könnest PB3 mal einschalten. Wenn dann die LED trotzdem aus ist, weiß
du, dass der Controller in den entsprechenden Zweig gesprungen ist.
Eventuell gibt es Probleme, weil du ADCH zweimal liest. Das glaube ich
eigentlich überhaupt nicht, aber ausschließen will ich es auch nicht.
Wie ist das, eines der ADC-Ergebnisregister ist doch gesperrt, bis das
andere Register ausgelesen wurde. Ich glaube, nur ADCH auslesen sollte
aber gehen.
Hallo, Dussel hat ja schon auf der doppelte Lesen hingewiesen.
Also verwendet man eine lokale Variable.
Logisch ist die zweite Abfrage und Bedingung unnötig !
Es gilt entweder oder !
Karl M. schrieb:> Hallo, Dussel hat ja schon auf der doppelte Lesen hingewiesen.
Aber warum ist das ein Problem?
Karl M. schrieb:> Logisch ist die zweite Abfrage und Bedingung unnötig !
Das sollte aber kein Fehler sein. Zumindest in 'PC-C' ist das egal. Ob
die Register da ein komisches Verhalten haben, weiß ich nicht, deshalb
wollte ich es nicht ausschließen.
chris schrieb:> Christoph schrieb:>> (1<<ADATE) | //enable ADC Auto Trigger Mode>> Ich bin mir jetzt nicht ganz sicher, aber soweit ich mich erinnere, wird> im Auto Trigger Mode das ADSC-Bit nicht mehr gelöscht.> Das würde bedeuten, dass du in der Warteschleife hängen bleibst
So ist es.
Datenblatt zwischen Figure 17-6 und 7:
"In Free Running mode, a new conversion will be started immediately
after the conversion completes, while ADSC remains high."
Wir haben es hier mit dem 'Free Running mode' zu tun, ich denke nicht,
dass man da überhaupt von einem 'trigger signal' sprechen kann.
Fakt ist, dass das Programm tatsächlich in der Warteschleife
'while(ADCSRA & (1<<ADSC))' hängt.
ADIF abfragen sollte immer gehen. Es empfiehlt sich aber, das Bit beim
Starten des ADC dann immer explizit durch schreiben einer 1 zu löschen,
wenn man keine Interrupts benutzt.
Aber wenn man statt Free Running den ADC im Single Shot benutzt, gehts
auch mit ADSC.
Sehe ich auch so, free running und manueller Start beißt sich. Entweder
oder.
Wenn ich nur einen Eingang einlese, setze ich auf free running und
starte einmalig.
Und in der Mainloop lese ich dann einfach nur den ADC aus.
Wenn die Mainloop sehr kurz ist, liest und vergleicht man eben mehrfach
den selben Wert, stört doch nicht.
Hallo zusammen!
Vielen Dank für eure zahlreichen und sehr hilfreichen Antworten! =)
Elektronisch war die Schaltung in Ordnung - LED, Photodiode und µC
funktionieren.
Der Fehler lag tatsächlich beim Autotrigger und der Warteschleife (
1
while(ADCSRA&(1<<ADSC));
). Das Programm funktioniert einwandfrei wenn ich beide oder jeweils den
Autotrigger oder die Warteschleife auskommentiere.
Nochmal für mich zusammenfassend, damit ich alles richtig verstanden
habe:
Der Autotrigger bedingt, dass das ADSC bit niemals 0 wird, daher bleibe
ich in der Warteschleife hängen.
=> Wenn ich den Autotrigger auskommentiere bzw. das bit auf 0 schreibe
funktioniert das Programm
Aber: Wenn ich die Warteschleife auskommentiere und den Autotrigger auf
1 bzw. 0 habe funktioniert das Programm trotzdem. Wieso und was passiert
dann? Das Programm funktioniert auch wenn ich sowohl den Autotrigger als
auch die Warteschleife auskommentiere - warum?
Viele Grüße,
Christoph
Anbei noch einmal der aktuelle Code:
1
#define F_CPU 1000000L
2
#include<avr/io.h>
3
4
intmain(void){
5
unsignedintadc_value;
6
//initials:
7
PORTB&=~(1<<PB3);//PB3 initially low
8
//initialisation:
9
DDRB|=(1<<PB3);//PB3 is output, logic one
10
11
//ADC Multiplexer Selection Register, see p. 134 datasheet
12
ADMUX|=(1<<ADLAR)|//left shift result (for 8 bit)
13
(0<<REFS1)|//sets ref. voltage to VCC, p. 134 datasheet
14
(0<<REFS0)|//sets ref. voltage to VCC
15
(0<<MUX3)|//use ADC2 for input (PB4), MUX bit 3, see p. 135 datasheet
16
(0<<MUX2)|//use ADC2 for input (PB4), MUX bit 2
17
(1<<MUX1)|//use ADC2 for input (PB4), MUX bit 1
18
(0<<MUX0);//use ADC2 for input (PB4), MUX bit 0
19
20
//ADC Control and Status Register A, see p. 136 datasheet
21
ADCSRA|=(1<<ADEN)|//enable ADC
22
//(1<<ADATE) | //enable ADC Auto Trigger Mode
23
(1<<ADPS2)|//set prescaler to 16, bit 2, see p. 136 datasheet
24
(0<<ADPS1)|//set prescaler to 16, bit 1
25
(0<<ADPS0);//set prescaler to 16, bit 0
26
//prescaler set to 16 - 64.5 kHz @ 1 MHz
27
28
while(1){
29
ADCSRA|=(1<<ADSC);//start ADC measurement
30
while(ADCSRA&(1<<ADSC));//wait until conversion is complete
Christoph S. schrieb:> Das Programm funktioniert auch wenn ich sowohl den Autotrigger als> auch die Warteschleife auskommentiere - warum?
Du startest dann die Wandlung, und liest anschließend sofort das
ADC-Register aus.
Dort steht demnach noch ein altes Ergebnis drin.
Da du hier aber immer nur einen Kanal liest (und nicht umschaltest) ist
das in deinem speziellen Fall egal, da das Ergebnis zwar ein paar µs
veraltet ist, aber trotzdem vom richtigen Kanal stammt.
Sobald du aber abwechselnd zwei verschiedene Kanäle liest (z.B.
Betriebsspannung), schießt du dir ins Knie, wenn du nicht auf die
Beendigung der Wandlung wartest.
chris schrieb:> Du startest dann die Wandlung, und liest anschließend sofort das> ADC-Register aus.
Hö? Wo hast du denn das gesehen? Der Kollege wartet doch auf das Ende
der Wandlung mit:
Christoph S. schrieb:> while(ADCSRA & (1<<ADSC));
M. K. schrieb:> Hö? Wo hast du denn das gesehen? Der Kollege wartet doch auf das Ende> der Wandlung mit:
Meine Antwort war darauf bezogen, dass es auch funktioniert, wenn er die
Warteschleife auskommentiert, siehe Zitat:
Christoph S. schrieb:> Das Programm funktioniert auch wenn ich sowohl den Autotrigger als> auch die Warteschleife auskommentiere - warum?
chris schrieb:> Meine Antwort war darauf bezogen, dass es auch funktioniert, wenn er die> Warteschleife auskommentiert, siehe Zitat:
Achso…das hab ich gestern Abend nicht mehr registriert. Da hast du
natürlich recht.
So als Tipp am Rande: ADATE und ADSC sollte man nicht gleichzeitig
benutzen sondern entweder das eine oder das andere. Benutzt man ADATE
(Autotrigger) dann sollte man stets die ISR des ADCs benutzen. Diese
wird immer dann angesprochen wenn eine Wandlung fertig ist. Benutzt man
ADSC kann man natürlich auch die ISR benutzen und muss nicht auf das
Wandlungsergebnis warten.
Ingo L. schrieb:> Wenn Vref = 5V, dann entspricht 128 1,25V!
Wenn ich das richtig sehe hat er Vcc als Referenzspannungsquelle
angegeben und er benutzt ADLAR (liest nur ADCH aus), sprich er hat auf 8
Bit ADC geschaltet. 128 entspricht dann tatsächlich 2,5V bei Vcc = 5 V