ADC geht nicht

#990856
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
}
#990874
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...
#990953
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
Gast #990994
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);
#990998
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?
#991019
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.
#991031
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren