Forum: Mikrocontroller und Digitale Elektronik ATtiny85 ADC Spannungswert einlesen


von Christoph (Gast)


Angehängte Dateien:

Lesenswert?

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
int main(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
    else if(ADCH >= 128){
34
      PORTB |= (1<<PB3); //turn on PB3
35
    }
36
  }
37
  return 0;
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

von chris (Gast)


Lesenswert?

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

von Dussel (Gast)


Lesenswert?

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.

von Karl M. (Gast)


Lesenswert?

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 !
1
if (ADCH<128) {
2
  PORTB &=~ (1<<PB3); //turn off PB3
3
} else {
4
  PORTB |= (1<<PB3); //turn on PB3
5
}

von Dussel (Gast)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

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."

von Dennis K. (scarfaceno1)


Lesenswert?

Hmmm. In 17-4 steht aber er startet nicht erneut...
1
 If the trigger signal still is set when the conversion completes, a
2
new conversion will not be started. If another positive edge occurs on the trigger signal during conversion, the edge
3
will be ignored. Note that an Interrupt Flag will be set even if the specific interrupt is disabled or the Global Interrupt
4
Enable bit in SREG is cleared. A conversion can thus be triggered without causing an interrupt. However, the Interrupt
5
Flag must be cleared in order to trigger a new conversion at the next interrupt event.

Hängt aber auf jeden Fall damit zusammen, würde ich behaupten...

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

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.

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


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

: Bearbeitet durch User
von Christoph S. (triple_seven)


Lesenswert?

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
int main(void){
5
        unsigned int adc_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
31
                adc_value = ADCH;
32
33
                if(adc_value < 128){
34
                        PORTB &=~ (1<<PB3); //turn off PB3
35
                }
36
                else if(adc_value >= 128){
37
                        PORTB |= (1<<PB3); //turn on PB3
38
                }
39
        }
40
        return 0;
41
}

von Karl M. (Gast)


Lesenswert?

Naja,

weiterhin ist das unnötig !
1
if(adc_value < 128){
2
  PORTB &=~ (1<<PB3); //turn off PB3
3
}
4
else if(adc_value >= 128){
5
  PORTB |= (1<<PB3); //turn on PB3
6
}

von chris (Gast)


Lesenswert?

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.

von M. K. (sylaina)


Lesenswert?

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

von chris (Gast)


Lesenswert?

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?

von M. K. (sylaina)


Lesenswert?

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.

von Ingo L. (corrtexx)


Lesenswert?

Christoph schrieb:
> wenn der ADC-Wert >
> 128, also größer 2.5 V ist
Wenn Vref = 5V, dann entspricht 128 1,25V!

von M. K. (sylaina)


Lesenswert?

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

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.