Forum: Compiler & IDEs ADC geht nicht


von Guenter B. (gbl)


Lesenswert?

Hallo
mein ADC-Code funktioniert nicht.
In Assembler habe ich es hinbekommen, das er funktioniert.
Nun wollte ich es in C probieren.
Weiß jemand warum dieser Code nicht funktioniert ?

1
#include <avr/io.h>        
2
#define F_CPU 3686400
3
#include <util/delay.h>
4
5
int main (void) 
6
{          
7
unsigned int x;
8
DDRB  = 0xff; 
9
ADMUX = (0 << MUX0) | (0 << MUX1) | (0 << MUX2) | (0 << MUX3) ;
10
ADMUX = (1 << REFS0) | (1 << REFS1) | (0 << ADLAR) ;
11
ADCSRA =(1 << ADEN) ; 
12
ADCSRA = (1 << ADPS2) | (0 << ADPS1) | (1 << ADPS0) ;        
13
while(1)
14
{       
15
16
ADCSRA =(1<<ADSC);
17
loop_until_bit_is_clear (ADCSRA,ADSC);
18
x = ADCL + ADCH * 256;
19
if (x<20)
20
{
21
PORTB = 0x00;
22
}
23
else
24
{
25
PORTB = 0x01;
26
} 
27
}                         
28
return 0;                 
29
}

von Daniel B. (inox5) Benutzerseite


Lesenswert?

spuckt der compiler medlungen aus?

von Stefan E. (sternst)


Lesenswert?

> ADCSRA =(1 << ADEN) ;
> ADCSRA = (1 << ADPS2) | (0 << ADPS1) | (1 << ADPS0) ;

Du überschreibst mit der 2. Zeile die Einstellung von der 1. Zeile. Und 
in der Endlosschleife überschreibst du es wieder.

"=" -> "|="

http://www.mikrocontroller.net/articles/Bitmanipulation

von Johannes M. (johnny-m)


Lesenswert?

Guenter B. wrote:
> Hallo
> mein ADC-Code funktioniert nicht.
> In Assembler habe ich es hinbekommen, das er funktioniert.
> Nun wollte ich es in C probieren.
> Weiß jemand warum dieser Code nicht funktioniert ?
Ich kann es mir zumindest denken, auch wenn Du in mehrfacher Hinsicht 
selbst gegen einfachste Forenregeln verstoßen hast (Controllertyp 
angeben, erzählen, was nicht funktioniert bzw. wie sich das 
nicht-Funktionieren manifestiert usw.)...
1
x = ADCL + ADCH * 256;
...Wenn Du in Assembler programmiert hast, dann weißt Du ja, dass beim 
Auslesen der ADC-Datenregister eine ganz bestimmte Reihenfolge 
einzuhalten ist (warum das so ist, steht im Datenblatt und im 
AVR-GCC-Tutorial, bitte dort nachlesen, falls noch nicht 
geschehen!). Bei dieser Zeile ist aber nicht definiert, in welcher 
Reihenfolge die Operanden ausgewertet werden, weshalb das mit ca. 
50-prozentiger Wahrscheinlichkeit schiefgeht.

Mit Deinem C-Compiler (ich vermute in mangels Angaben von Deiner 
Seite, dass es sich um den AVR-GCC handelt) kann Dir die Arbeit mit der 
richtigen Reihenfolge aber abnehmen. Dazu musst Du anstelle von ADCH und 
ADCL das 16-Bit-Register ADCW auslesen. Und lies Dir die betreffenden 
Abschnitte im AVR-GCC-Tutorial durch.

EDIT:
...und lies Dir das Posting von Stefan ganz genau durch...

von Guenter B. (gbl)


Lesenswert?

@Stefan
Also ich hatte folgende Zeile immer so interpretiert:

ADCSRA = (1 << ADPS2) | (0 << ADPS1) | (1 << ADPS0) ;
Setzte ADPS2 in ADCSRA auf 1
Setzte ADPS1 in ADCSRA auf 0
Setzte ADPS0 in ADCSRA auf 1

unabhängig von der Reihenfolge.

Also als einfache Zuweisung nicht als Schiebevorgang.
In Assembler hatte ich mit sbi und cbi gearbeitet.

@Johannes
Ich hatte es auch schon mit
{x = ADCL;Y = ADCH;}
usw versucht.

Die Zeile {x = ADCL + ADCH * 256;} hatte ich in einem Buch gefunden.

Meine Verstöße gegen die Forenregeln vergebt mir bitte.
Da ich vermutet habe, daß die Erfahrerenen hier die Fehler in
meinen simplen Code Umgebungs- und Controllerunabhängig sofort erkennen,
habe ich diese Angaben für unnötig gehalten.

Ich danke euch für die schnelle Hilfe.

Gruß

Günter

von Chris (Gast)


Lesenswert?

> Die Zeile {x = ADCL + ADCH * 256;} hatte ich in einem Buch gefunden.

Dann würde ich dem Buch nicht mehr vertrauen. Such mal im von Johannes 
genannten AVR-GCC-Tutorial nach "ADCW". Die Lösung ist sehr einfach, du 
brauchst dafür ADCL und ADCH überhaupt nicht.


> ADCSRA = (1 << ADPS2) | (0 << ADPS1) | (1 << ADPS0) ;
> Setzte ADPS2 in ADCSRA auf 1
> Setzte ADPS1 in ADCSRA auf 0
> Setzte ADPS0 in ADCSRA auf 1

(0 << ADPS1) ist das gleiche wie 0. Deine Zuweisung setzt also nicht nur 
ADPS1 auf 0, sondern auch alle anderen Bits, ausgenommen ADPS2 und 
ADPS0. Du musst bedenken, dass = in C für eine Variablenzuweisung steht, 
bei der grundsätzlich die komplette Variable mit dem überschrieben wird, 
was rechts vom = steht. Da ADCSRA ein 8-Bit-Register ist, werden durch 
deine Zuweisung auch 8 Bits überschrieben.

Wenn du stattdessen schreibst:
ADCSRA |= (1 << ADPS2) | (1 << ADPS0);
(also |= statt = )
dann werden tatsächlich nur die Bits ADPS2 und ADPS0 selektiv gesetzt. 
ADPS1 und alle anderen Bits werden nicht angefasst. Um dann ADPS1 zu 
löschen, kannst du sowas nehmen:
ADCSRA &= ~(1 << ADPS1);

von Stefan E. (sternst)


Lesenswert?

Guenter B. wrote:
> @Stefan
> Also ich hatte folgende Zeile immer so interpretiert:
>
> ADCSRA = (1 << ADPS2) | (0 << ADPS1) | (1 << ADPS0) ;
> Setzte ADPS2 in ADCSRA auf 1
> Setzte ADPS1 in ADCSRA auf 0
> Setzte ADPS0 in ADCSRA auf 1

Genau, und implizit noch dazu: setze den nicht erwähnten Rest auch auf 
0, also in dieser Zeile auch ADEN.

> unabhängig von der Reihenfolge.

Habe ich irgendetwas von der Reihenfolge innerhalb der Zeile 
geschrieben?

> Also als einfache Zuweisung nicht als Schiebevorgang.

Wo war denn von einem Schiebevorgang die Rede?

von Guenter B. (gbl)


Lesenswert?

@Chris

Es lag nicht am Buch sondern an meiner Interpretation.
Es stand dort nämlich:

"Der effektive Messwert ergibt sich dann zu
x = ADCL + ADCH * 256 "

Weiter oben im Text stand dann auch, daß man ADCL unbedingt zuerst 
auslesen muß. Allerdings hatte ich es ja wie gesagt schon auf anderem 
Wege (erfolglos) versucht.

Inzwischen funktioniert das Programm.

von Guenter B. (gbl)


Lesenswert?

@Stefan

hier ein Auszug aus dem Tutorial dessen Link du angehängt hattest:

[Tutorial-Auszug]

Die letzte Zeile "entschlüsselt":

   1. (1 << n) : Zuerst wird durch die '<<'-Ausdrücke eine "1" n-mal 
nach links geschoben. Dies ergibt somit (in Binärschreibweise) 
0b00000001 für (1 << MEINBIT0) und 0b00000100 für (1 << MEINBIT2).

[/Tutorial-Auszug]

Hier mein vorläufiger Workarround:

ADMUX = 192;
ADCSRA =133;

:-)

Gruß

Günter

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> Die Zeile {x = ADCL + ADCH * 256;} hatte ich in einem Buch gefunden.
Das das der Compiler selber richtig, wenn ich schreibe
1
x = ADC;
oder
1
x = ADCW;

Siehe auch Beitrag "Re: ADC Conversion"

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.