Hallo Leute,
bitte verzeiht mir, wenn ich euch mit solch einer Fingerübung belästige.
Aber ich sehe leider den Wald vor lauter Bäumen nicht mehr.
Ich schrieb ein einfaches Programm in C auf einen ATmega32 der lediglich
den Analogwert am Channel 0 auslesen und die LEDs mit dem Ergebnis am
Port B ansteuern soll.
Das Programm läuft im Pollingmode und fragt halt das entsprechende
Statusbit ab:
1
#include<avr/io.h>
2
3
uint16_tResult=0;
4
uint8_tDisplay=0;
5
6
intmain(void)
7
{
8
DDRB=0xFF;// LEDs | pins set as inputs
9
PORTB=0xFF;// all LEDs off
10
DDRA&=~0x01;// Port A Pin 0 set as output
11
ADCSRA=0x8E;// 1000|1110
12
ADMUX=0x00;// 0000|0000
13
ADCSRA|=0x40;// start first conversion
14
while(1)
15
{
16
while(~(ADCSRA&0x10));//wait for conversion
17
Result=ADCL;
18
Result+=(ADCH<<8);
19
// Result = ADCW;
20
Display=8*(0xFFFF/Result);// convert the result from 0x0000..0xFFFF to 1..8
21
PORTB=~(1<<Display);
22
}
23
}
Und genau da ist das Problem. Aus irgendeinem Grund bleibt er beim
Auslesen des Statusbits hängen und macht nicht weiter. (Das habe ich mit
diversen LEDausgaben getestet, die jetzt hier nicht zu sehen sind.)
1
while( ~(ADCSRA & 0x10)); //wait for conversion
Mich wundert es auch, dass ich nirgends den Modus (Free running mode)
angeben konnte. Wie mache ich das beim ATmega32?
Könnt Ihr mir helfen?
Danke
BrEin
> while( ~(ADCSRA & 0x10)); //wait for conversion
Das ~ ist da seltsam. Vergleich das doch mal mit der funktionierenden
ADC Routine im AVR-GCC-Tutorial. BTW. Deine Schaltung funktioniert mit
den bekannt funktionierenden Routinen (um Hardwarekäfer
auszuschliessen)?
Die Bits (0x10 usw.) habe ich nicht kontrolliert. Tipp: Benutze die
allgemein gängige Schreibweise und die Bitnamen. Dann weiss man direkt
ob du Bit ADSC oder ADIF... prüfst oder nicht. Wie das geht steht auch
im Tutorial bzw bei Bitmanipulation
Die 0xFFFF bei der Umrechnung ist IMHO falsch. 1023..0 => 8..1 würde ich
so machen: Display = (7*Result)/1024+1; Der AVR hat nur einen max.
10-Bit ADC, benutzt du 10-Bit ADC dann gilt die 1024 in der Formel.
Innerhalb der while(1) Schleife sollte die Single Conversion (wenn du
die benutzt) auch regelmäßig angestoßen werden.
Freerunning mit ADFR bei älteren ist bei neueren AVRs durch ADATE und
passende Konfigurationsbits ersetzt.
Krapao schrieb:>> while( ~(ADCSRA & 0x10)); //wait for conversion>> Das ~ ist da seltsam. Vergleich das doch mal mit der funktionierenden> ADC Routine im AVR-GCC-Tutorial. BTW. Deine Schaltung funktioniert mit> den bekannt funktionierenden Routinen (um Hardwarekäfer> auszuschliessen)?
Genau das Tutorial macht in diesem Punkt wenig Sinn.
1
ADCSRA|=(1<<ADSC);// eine ADC-Wandlung
2
while(ADCSRA&(1<<ADSC)){}// auf Abschluss der Konvertierung warten
Warum das mit dem ADSC - Flag gemacht wird, also dem der eine Messung
anstößt erschließt sich mir nicht. Ich lesen den Interrupt-Flag aus,
denn dafür ist er da.
Die Tilde ~ negiert den Ausdruck dahinter. Ich vergleiche ob das Flag
gesetzt wird. Ist es noch Null, so bleibt der Ausdruck Null und wird
durch die Tilde so lange Eins, bis die Konvertierung erledigt ist und
das Flag auf eins gesetzt wird. Denn dann wird der Ausdruck zu Eins und
durch die Tilde Null. Dann endet wie While-Schleife und eben genau das
passiert hier nicht.
>> Die Bits (0x10 usw.) habe ich nicht kontrolliert. Tipp: Benutze die> allgemein gängige Schreibweise und die Bitnamen. Dann weiss man direkt> ob du Bit ADSC oder ADIF... prüfst oder nicht. Wie das geht steht auch> im Tutorial bzw bei Bitmanipulation>
Ja, werde ich gleich ergänzen.
> Die 0xFFFF bei der Umrechnung ist IMHO falsch. 1023..0 => 8..1 würde ich> so machen: Display = (7*Result)/1024+1; Der AVR hat nur einen max.> 10-Bit ADC, benutzt du 10-Bit ADC dann gilt die 1024 in der Formel.>
Finde ich besser als meine Lösung. Danke, wird auch übernommen.
> Innerhalb der while(1) Schleife sollte die Single Conversion (wenn du> die benutzt) auch regelmäßig angestoßen werden.>> Freerunning mit ADFR bei älteren ist bei neueren AVRs durch ADATE und> passende Konfigurationsbits ersetzt.
Es soll ja der Free running Mode sein. Ich weiß auch, dass ich ihn schon
mal auf einem ATmega32 und einem ATmega128 genutzt habe. Warum ich jetzt
nicht sehe, wo ich ihn einstellen kann ist mir absolut schleierhaft. Das
war ganz einfach. Leider war das jetzt 2 Jahre her und ich habe alles
wieder vergessen.
:(
Fabian Hoemcke schrieb:> Warum das mit dem ADSC - Flag gemacht wird, also dem der eine Messung> anstößt erschließt sich mir nicht. Ich lesen den Interrupt-Flag aus,> denn dafür ist er da.
Ähm, das Datenblatt meint da aber was anderes: Das ADSC-Bit bleibt so
lange gesetzt, wie die Messung andauert. Im Freerunning-Modus bleibt es
gesetzt, setzt man es auf 0, ist die Messung beendet. Das Interrupt-Flag
wird (glaube ich) im Freerunning-Modus nicht benutzt. Also:
Single-Conversion-Modus und ADSC-Bit nehmen.
Gruß
Jonathan
Jonathan Strobl schrieb:> Fabian Hoemcke schrieb:>> Warum das mit dem ADSC - Flag gemacht wird, also dem der eine Messung>> anstößt erschließt sich mir nicht. Ich lesen den Interrupt-Flag aus,>> denn dafür ist er da.>> Ähm, das Datenblatt meint da aber was anderes: Das ADSC-Bit bleibt so> lange gesetzt, wie die Messung andauert. Im Freerunning-Modus bleibt es> gesetzt, setzt man es auf 0, ist die Messung beendet. Das Interrupt-Flag> wird (glaube ich) im Freerunning-Modus nicht benutzt. Also:> Single-Conversion-Modus und ADSC-Bit nehmen.>>> Gruß> Jonathan
OK! Wenn Du das sagst, probiere ich das mal aus!
Finde es aber komisch, dass ich den Free Running Mode nicht mehr hin
bekomme.
_
Hier im übrigen mal der angepasste Code:
Fabian Hoemcke schrieb:> Genau das Tutorial macht in diesem Punkt wenig Sinn.ADCSRA |= (1<<ADSC);
// eine ADC-Wandlung
> while (ADCSRA & (1<<ADSC) ) {} // auf Abschluss der Konvertierung warten> Warum das mit dem ADSC - Flag gemacht wird, also dem der eine Messung> anstößt erschließt sich mir nicht. Ich lesen den Interrupt-Flag aus,> denn dafür ist er da.
Im Datenblatt steht jedoch, fast schon als Empfehlung ausgedrückt, dass
man zum Start einer Wandlung das ADSC-Bit setzen soll. Die Hardware
setzt es dann selbsttätig nach Abschluss der Wandlung wieder auf 0. Das
kann man dann pollen. Das Tutorial ergibt also einen Sinn.
Wenn du es Interruptgetrieben machen möchtest, dann fehlt mir im
geposteten Code irgendwie die Interruptroutine.
mfg mf
> Die Tilde ~ negiert den Ausdruck dahinter. Ich vergleiche ob das Flag> gesetzt wird. Ist es noch Null, so bleibt der Ausdruck Null und wird...
durch ~ zu einem 0xFF! Die Tilde ~ ist der Bitoperator NOT. Du hast eine
Endlosschleife gebaut. Nimm für diesen Zweck den Logikoperator NOT (!).
Jonathan Strobl schrieb:> Du musst die Negierung im while() noch wegmachen. Sonst wartest Du,> solange keine Messung stattfindet ;)
Nein! Das ist so schon richtig. Das habe ich weiter oben beschrieben.
Ich frage ja ab, wann das Flag und somit der Ausdruck 1 wird. Und dass
muss ich halt negieren.
Mini Float schrieb:> Fabian Hoemcke schrieb:>> Genau das Tutorial macht in diesem Punkt wenig Sinn.ADCSRA |= (1<<ADSC);> // eine ADC-Wandlung>> while (ADCSRA & (1<<ADSC) ) {} // auf Abschluss der Konvertierung warten>> Warum das mit dem ADSC - Flag gemacht wird, also dem der eine Messung>> anstößt erschließt sich mir nicht. Ich lesen den Interrupt-Flag aus,>> denn dafür ist er da.>> Im Datenblatt steht jedoch, fast schon als Empfehlung ausgedrückt, dass> man zum Start einer Wandlung das ADSC-Bit setzen soll. Die Hardware> setzt es dann selbsttätig nach Abschluss der Wandlung wieder auf 0. Das> kann man dann pollen. Das Tutorial ergibt also einen Sinn.>> Wenn du es Interruptgetrieben machen möchtest, dann fehlt mir im> geposteten Code irgendwie die Interruptroutine.>> mfg mf
Danke, aber im Datenblatt steht aber auch, dass das ADIF auf 1 gesetzt
wird, sobald die Messung beendet und das Ergebnis im DATA REGISTER
geschrieben ist.
Es bleibt sich also gleich, ob ich jetzt nun warte bis ADIF auf 1
gesetzt wurde oder ADSC auf 0. Oder etwa nicht?
Die Version mit dem Abfragen des ADSC auf 0 habe ich auch ausprobiert.
Das Ergebniss ist immer 2. Auch wenn ich VTG anlege. Dabei spielt es
keine Rolle ob ich die Konvertierung nur einmal starte (also das ADSC
bit setze) oder es jedes Mal in der Schleife starte. Es scheint sich
hier also schon um den Free Running Mode zu handeln.
Durch eine Ausgabe an den LEDS (1 für „vor der Flagabfrage” und 2 für
„nach der Flagabfrage” - beide waren an!) konnte ich sehen, das bei
dieser Version also die Schleife ständig durchlaufen wird. Ich muss also
was an den Einstellungen des ADCs oder des Muxers falsch gemacht haben,
dass er einfach nicht die Werte vom PA0 auslesen will.
Krapao schrieb:>> Die Tilde ~ negiert den Ausdruck dahinter. Ich vergleiche ob das Flag>> gesetzt wird. Ist es noch Null, so bleibt der Ausdruck Null und wird...>> durch ~ zu einem 0xFF! Die Tilde ~ ist der Bitoperator NOT. Du hast eine> Endlosschleife gebaut. Nimm für diesen Zweck den Logikoperator NOT (!).
Probiere ich gleich aus!
Krapao schrieb:>> Die Tilde ~ negiert den Ausdruck dahinter. Ich vergleiche ob das Flag>> gesetzt wird. Ist es noch Null, so bleibt der Ausdruck Null und wird...>> durch ~ zu einem 0xFF! Die Tilde ~ ist der Bitoperator NOT. Du hast eine> Endlosschleife gebaut. Nimm für diesen Zweck den Logikoperator NOT (!).
Jup! Das hat geholfen! Blöd von mir die anderen Nullen auch ständig auf
1 zu setzen! **Facepalm**.
OK, das war der erste Baum!
Warum zeigt mir der ADC nun ständig immer das selbe Ergebnis an???
Lese ich falsch aus? Habe ich falsche Settings?
Der Prescaler soll 64 sein. Bei einer Frequenz von 3.686.400Hz sind das
57.600Hz. Das müsste im Grunde doch passen.
**Dummguck**
Danke aber für die Hilfe bisher!
BrEin
Fabian Hoemcke schrieb:> Warum zeigt mir der ADC nun ständig immer das selbe Ergebnis an???
Weil du überhaupt nur eine Messung machst. Du stößt ja keine neue an.
Und das Warten funktioniert nach dem ersten Mal auch nicht mehr, weil du
das Flag ja nicht zurücksetzt.
(letzteres ist auch der Grund, warum es einfacher ist, auf ADSC zu
warten)
Stefan Ernst schrieb:> Fabian Hoemcke schrieb:>> Warum zeigt mir der ADC nun ständig immer das selbe Ergebnis an???>> Weil du überhaupt nur eine Messung machst. Du stößt ja keine neue an.> Und das Warten funktioniert nach dem ersten Mal auch nicht mehr, weil du> das Flag ja nicht zurücksetzt.> (letzteres ist auch der Grund, warum es einfacher ist, auf ADSC zu> warten)
Was habt Ihr denn nur mit dem ADSC?
Das habe ich so nie gelernt. Auch im Pollingmode mutze ich doch den
dafür vorgesehenen ADIF. Aber sei es drum. Egal ob ich den ADIF mit 1
zurücksetzte oder gleich den ADSC auslese, das Ergebnis ist immer gleich
*2*:
Ich wollte mich nur mal bei euch bedanken und sagen, dass Ihr super
seid.
Zu so später Stunde schon so viel Hilfe bekommen.
Danke!
Nur verstehe ich nicht, warum ich mich so harte anstelle bei einem ADC!
Naja! Vielleicht bekommen wir das noch hin. ;)
Danke euch!
> Display = (7*Result)/1024+1
Die Klammern brauchst du! Und ich die 7 ist eigentlich eine 8 :-)
max. Result = 1023
8*1023 = 8184
8184/1024 (= 7,9921875 mathematisch) = 7 (Ganzzahlrechnung!)
7+1 = 8
Fabian Hoemcke schrieb im Beitrag #2384166:
> Kann ich das mit nem floatcast verbessern?
Du könntest mit Float arbeiten, oder aber einfach die Rechnung etwas
umstellen (wie von Krapao schon gezeigt).
Stefan Ernst schrieb:> Fabian Hoemcke schrieb:>> das Ergebnis ist immer gleich>> *2*:>> Logisch, da (Result / 1024) immer 0 ist, ist Display immer 1.Krapao schrieb:>> Display = (7*Result)/1024+1>> Die Klammern brauchst du! Und ich die 7 ist eigentlich eine 8 :-)>> max. Result = 1023> 8*1023 = 8184> 8184/1024 (= 7,9921875 mathematisch) = 7 (Ganzzahlrechnung!)> 7+1 = 8
Danke euch beiden!
Ich habe die Passage aber mal umgeändert:
1
Display = (8*Result)/1024;
Da ich ja noch:
1
PORTB = ~(1 << Display);
;)
Aber vielen lieben Dank!
Jetzt kann ich endlich beruhigt schlafen gehen.
Danke
BrEin
Display=(8*Result)/1024;// convert the result from 0..1024 to 1..8
20
PORTB=~(1<<Display);
21
}
22
}
Warum der Free Running Mode hier nicht funktioniert ist mir zwar absolut
schleierhaft, aber ist jetzt auch nicht so wichtig.
Vielen Dank nochmal an alle Helfer!
Gute Nacht!
BrEin