Hallo,
ich habe momentan ein Problem bei der Initialisierung des ADC beim
AT90PWM3B. Es wäre schön, wenn einer bei meinem lauten denken versucht
mir zu folgen.
Ich initialisiere wie folgt:
1
...
2
ADMUX=2;
3
ADCSRA=(1<<ADSC)|(1<<ADPS2)|(1<<ADPS1);
4
ADCSRA=(1<<ADEN)// start
5
6
while(bit_is_set(ADCSRA,6));
7
ADC_Val=result;
8
...
Mein Problem ist, dass im Handbuch zum Bit 6 folgendes steht:
"Set this bit to start a conversion in single conversion mode or to
start the first conversion in free
running mode.Cleared by hardware when the conversion is complete."
Nun möchte ich aber gerne den free running mode nutzen um nicht jedes
mal von vorne Initialisieren zu müssen. Wie macht man das? Ich vermute,
dass es mit dem ADC Auto trigger Enable Bit zu tun hat. Wenn man das Bit
setzt und "ADTS3 ADTS2 ADTS1 ADTS0" löscht müsste er doch im free
running mode laufen.
Dann müsste man nur einmal initialisieren und könnte dann den Wert wann
man möchte abholen:
Hab mir die Register nicht angeschaut, aber löscht du mit ADCSRA =
(1<<ADEN) nicht die anderen Bit im Register.
Sollte doch eher ADCSRA |= (1<<ADEN); heissen.
Strichpunkt danach sollte auch sein.
Oh das kann sein. Ich bin (wie viele hier) noch recht neu auf dem Gebiet
der uc's. Habe den Code jetzt so editiert, aber es funktioniert
immernoch nicht mit dem kontinuierlichen ADC.
1
ADMUX=2;
2
ADCSRA=(1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADPS2)|(1<<ADPS1);// init und Start
3
4
intmain(void){
5
6
while(1){
7
8
while(bit_is_set(ADCSRA,6));
9
10
ADC_Val=result;
11
12
};
13
}
Woran liegt das? Sobald ich das ADATE Bit setze geht nix mehr.
So ein dummer Fehler. Ich komme hier irgendwie ständig mit der
Portierung ins Straucheln -.- .
Danke, dass du mitdenkst, aber leider geht es nach wie vor nicht und da
du ja auch keinen weiteren Tip geben konntest, verstehe ich erst recht
nicht, warum der ADC nicht im kontinuierlichen Modus arbeiten will. Aber
vielleicht hat ja noch wer ne Idee...
Hier der "richtigste" Code :-) :
1
ADMUX=2;
2
ADCSRA=(1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADPS2)|(1<<ADPS1);// init und Start
folgendermaßen:
Bei folgendem Code halte ich eine Lampe an den Helligkeitssensor und die
LED geht aus. Lampe weg -> LED geht an. Lampe hin -> LED geht aus.
1
intmain(void){
2
3
while(1){
4
5
ADMUX=2;
6
ADCSRA=(1<<ADEN)|(1<<ADSC)|(1<<ADPS2)|(1<<ADPS1);// init und Start
7
8
while(bit_is_set(ADCSRA,6));
9
10
result=ADC;
11
12
};
13
}
wenn ich jetzt die Initialisierung rausziehe funktioniert gar nix mehr.
Die LED geht nicht mehr aus. Und ich habe definitiv nur die
Initialisierung geändert.
1
intmain(void){
2
3
ADMUX=2;
4
ADCSRA=(1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADPS2)|(1<<ADPS1);// init und Start
> Aber vielleicht hat ja noch wer ne Idee...
Wenn ich den ADC im Free-Run-Mode (adate in adcsra gesetzt, adcsrb=0)
laufen lasse, dann lesee ich ihn zyklisch in einem Timer-Interrupt aus,
der nebenher noch andere Dinge des Programms synchronisiert (z.B.
Tasten-Entprellung, Byte-Transfer zum LCD, Zeitbasis für Blinken, usw.).
Dabei achte ich darauf, dass das Timer-Intervall größer ist als das
Abtastintervall des ADCs.
Wenn ich schnell samplen muss, dann nutze ich den Interrupt des ADC. Da
dieser auch eine Art "Timer" ist, bekommt er ggf. auch noch andere Jobs
mit aufgebrummt, wie z.B. das Multiplexing von
7-Segment-Lichtschachtanzeigen.
Wenn ich mehrere Kanäle brauche, dann lege ich mir je Array für Messwert
und MUX-Wert der nächsten Messung an und lasse in der ISR den Index
rotieren. Also Messwert auslesen und in Array legen, Index erhöhen und
begrenzen, MUX-Wert aus Array holen und in ADMUX schreiben, Messung
starten, fertig. Die Mainloop (oder einer ihrer Jobs) holt sich dann den
entsprechenden Messwert (atomar) aus dem Array.
Diese Varianten haben den Vorteil, dass in der Mainloop kein Busywait
stattfindet, der Controller in dieser Zeit also etwas Sinvolles tun
kann. Denn meist muss das Programm ja noch ein paar Dinge mehr tun als
nur den ADC auslesen.
Mit C-Code kann ich nicht dienen, ich werkele in ASM.
...
Gleich vorweg, ich hab mich mit dem Free running Mode noch nicht
beschäftigt.
Aber welchen Sinn soll es haben, wenn du das Bit 6 abfrägst um dann doch
wieder auf den ADC zu warten. Wird dieses Bit überhaupt im Free Running
Modus bedient? Und wenn der ADC es tatsächlich setzt, während eine
Wandlung läuft, was denkst du, wie gross sind die Chancen, dass dein
Code das Bit genau in dem Moment erwischt, wenn eine Wandlung fertig ist
und die nächste noch nicht begonnen hat. Auch ein
while (bit_is_set(ADCSRA,6));
kann keine Bitabfrage in 0-Zeit machen.
Nimm die Warterei mal raus oder starte alternativ den ADC jedesmal neu
wenn du unbedingt warten willst
1
intmain(void){
2
3
ADMUX=2;
4
ADCSRA=(1<<ADEN)|(1<<ADSC)|(1<<ADPS2)|(1<<ADPS1);// init und Start
Martin T. schrieb:
> Bei folgendem Code halte ich eine Lampe an den Helligkeitssensor und die> LED geht aus. Lampe weg -> LED geht an. Lampe hin -> LED geht aus.
Bin ich der einzige, der in dem angeblich funktionierenden Code keine
LED sieht, die irgendwie geschaltet werden könnte?
Justus Skorps schrieb:
> Martin T. schrieb:>> Bei folgendem Code halte ich eine Lampe an den Helligkeitssensor und die>> LED geht aus. Lampe weg -> LED geht an. Lampe hin -> LED geht aus.>> Bin ich der einzige, der in dem angeblich funktionierenden Code keine> LED sieht, die irgendwie geschaltet werden könnte?
Das nehme ich mal als:
Ist während des Copy&Paste im Bithimmel verloren gegangen.
Hannes Lux schrieb:
> Wenn ich den ADC im Free-Run-Mode (adate in adcsra gesetzt, adcsrb=0)>> laufen lasse, dann lesee ich ihn zyklisch in einem Timer-Interrupt aus,>> der nebenher noch andere Dinge des Programms synchronisiert (z.B.>> Tasten-Entprellung, Byte-Transfer zum LCD, Zeitbasis für Blinken, usw.).>> Dabei achte ich darauf, dass das Timer-Intervall größer ist als das>> Abtastintervall des ADCs.
Meine Anwendung ist nicht soo zeitkritisch und ich hatte mir gedacht,
dass ich ihn "frei laufen" lasse und wenn er halt 50 mal pro Sekunde nen
Wert bekommt ist das genauso gut wie 500 mal pro Sekunde.
Das mit den Interrupts ist ne spannende Sache, da ich momentan noch kein
Gefühl habe was zu lange und was OK für ein Interrupt wäre. Mein uc
läuft mit 8MHz und die ISR dauert 8MHz/1024 also 128us. Nun habe ich bis
jetzt nur ein paar kleinere if's drin für ne Zeitbasis. Wieviel kann ich
da reinpacken? Wie geht man da vor? Es ist klar, dass ich bis zum
nächsten Interrupt alles fertig abgearbeitet haben muß, aber was ist mit
den Prozessen in der main? Ich kann nur sehr rudimentär Assembler und
weiß daher nicht wie lange ne for (x=0;x<50;x++) oder ne if (x == 0) x =
1 dauern. Im Tutorial findet man hierzu auch nichts. Und debuggen im AVR
Studio ist die reinste Qual. ;-)
Karl heinz Buchegger schrieb:
> Aber welchen Sinn soll es haben, wenn du das Bit 6 abfrägst um dann doch>> wieder auf den ADC zu warten.
Das Problem ist glaube ich, dass ich die Arbeitsweise des ADC im
Allgemeinen nicht richtig verstanden habe. Im Datenblatt steht
"When a positive edge occurs on the selected trigger signal,
the ADC prescaler is reset and a conversion is started. This provides a
method of starting conversions
at fixed intervals. If the trigger signal is still set when the
conversion completes, a new
conversion will not be started."
Somit werden nur positive und keine negativen Flanken erkannt, da mein
Wert aber auch mal absacken kann sind negative Flanken essentiell.
Außerdem warum sollte ich in nem frei laufenden ADC exakte Intervalle
haben wollen? Dafür habe ich doch dann den single mode?!
Ich werde das ganze nochmal von vorne überdenken und dann, falls mich
keiner haut, nochmal ne Frage dazu stellen.
Martin T. schrieb:
> Somit werden nur positive und keine negativen Flanken erkannt, da mein> Wert aber auch mal absacken kann sind negative Flanken essentiell.
Das hat damit nichts zu tun.
Da geht es darum, was den ADC dazu bringt eine Konvertierung zu machen.
zb. ein regelmässiges Taktsignal, welches jede 1 Sekunde eine ADC
Wandlung anstößt.
Dieses Signal hat aber nichts mit dem Eingang zu tun, der vom ADC
ausgemessen wird.
Martin T. schrieb:
> Meine Anwendung ist nicht soo zeitkritisch und ich hatte mir gedacht,> dass ich ihn "frei laufen" lasse und wenn er halt 50 mal pro Sekunde nen> Wert bekommt ist das genauso gut wie 500 mal pro Sekunde.
Ja, mache ich auch so, besonders wenn nur ein Kanal gebraucht wird.
Allerdings brauchen fast alle meiner Programme eine Zeitbasis, die
mittels Timer sehr "billig" zu haben ist. Daher lese ich den ADC in
einem vom Timer synchronisierten Job (es muss ja nichtmal in der ISR
sein) aus.
>> Das mit den Interrupts ist ne spannende Sache, da ich momentan noch kein> Gefühl habe was zu lange und was OK für ein Interrupt wäre.
Da ich in ASM werkele, kann ich "Takte zählen" (vorhersagen, wie lange
ein Stück Code braucht). Der Programmierstil richtet sich dabei etwas
nach den zu erledigenden Aufgaben.
Es kann (bei einfachen Programmen) durchaus sein, dass ich das gesamte
Programm in die ISR lege, dann weiß ich aber, dass das
Timer-Interrupt-Intervall groß genug ist und dass keine anderen
Interrupts gebraucht werden.
Meist erledige ich in den ISRs aber nur zeitkritische Dinge, wie das
Retten (sichern) flüchtiger Werte (also Werte, die bereits überschrieben
sein könnten, ehe die Mainloop (oder einer ihrer Jobs) Zeit dafür hat)
und setze der Mainloop einen Merker, also einen Jobauftrag zum
Weiterverarbeiten des gesicherten Wertes.
Sind mehrere verschiedene Interrupts aktiv, dann kommt es darauf an, die
ISRs möglichst kurz (schnell) zu machen, damit sie sich nicht
gegenseitig blockieren bzw. verzögern. Verzweigungen (Fallabfragen)
sollten dann schon kritisch gesehen werden. Und Warteschleifen haben in
ISRs ja sowiso nichts zu suchen, es sei denn, es geht um wenige µs.
> Mein uc> läuft mit 8MHz
Mir reichen oftmals die 1MHz der Grundeinstellung.
> und die ISR dauert
Du meinst vermutlich, die ISR wird im Abstand von ... aufgerufen? Denn
wie lange sie dauert, hängt davon ab, wie schnell der darin liegende
Code abgearbeitet wird.
> 8MHz/1024 also 128us.
Warum erst in Zeit umrechnen? Es ist doch einfacher, die Rechenzeit in
Takten zu betrachten. Und in 1024 Takten kann man schon allerhand tun.
> Nun habe ich bis> jetzt nur ein paar kleinere if's drin für ne Zeitbasis. Wieviel kann ich> da reinpacken? Wie geht man da vor? Es ist klar, dass ich bis zum> nächsten Interrupt alles fertig abgearbeitet haben muß, aber was ist mit> den Prozessen in der main?
Deshalb teilt man ja die zu erledigenden Aufgaben auf. Die ISR macht
das, was bei jedem Durchlauf/Aufruf nötig ist. Für Aufgaben, die
seltener nötig sind, setzt die ISR nur einen Merker. Die Mainloop
erledigt dann den zugehörigen Job und löscht den Merker (die Arbeit ist
ja getan). Dabei kann sie ohne Weiteres von weiteren Interrupts
unterbrochen werden, sie muss nur fertig sein, ehe dieser Job erneut
fällig wird.
Ich organisiere das meist so, dass die Mainloop alle Job-Merker abfragt
und ggf. zum Job verzweigt und dann, wenn alle Jobs erledigt sind, den
in den Controller in den Sleep schickt, aus dem er vom nächsten
Interrupt wieder geweckt wird. Bei umfangreicheren Programmen nutze ich
zum Ermitteln der gebrauchten Rechenzeit einen I/O-Pin (Ausgang), der
von jeder ISR auf H gesetzt wird und von der Mainloop beim Erreichen von
Sleep auf L. Ein Oszi zeigt mir dann sehr genau die "Lastkurve" des
Prozessors. ;-)
> Ich kann nur sehr rudimentär Assembler und> weiß daher nicht wie lange ne for (x=0;x<50;x++)
Das sind 50 bedingte Rücksprünge je 2 Takte + 50 mal Abarbeitung des
Schleifeninhalts (des Codes in der Schleife). Dazu noch 50 mal die
RAM-Zugriffe, da Hochsprachen ja ihre Variablen im SRAM halten.
> oder ne if (x == 0) x = 1 dauern.
Um das einschätzen zu können, ist es hilfreich, die Architektur des
Controllers zu kennen (das erfordert nun wieder etwas ASM-Wissen, denn
der Controller kann nunmal nur Maschinencode und der ist nur in ASM 1 zu
1 notierbar) und auch etwas Hintergrundwissen zur Arbeitsweise des
Compilers. C-Programmierer, die guten hardwarenahen Code schreiben
können, kennen sich auch mit ASM aus. Ansonsten wären sie nicht in der
Lage, den C-Code so zu formulieren, dass genau der gewünschte ASM-Code
erzeugt wird. Mir persönlich ist das alles zu kryptisch, deshalb
schreibe ich in ASM.
> Im Tutorial findet man hierzu auch nichts. Und debuggen im AVR> Studio ist die reinste Qual. ;-)
In C kann ich es nicht beurteilen, in ASM ist der Debugger (Simulator)
aber recht brauchbar. Das Hardware-Debugging (mittels AVR-Dragon) ist
zwar nicht ganz mein Geschmack, das liegt aber nur daran, dass meine
Programme oft Impulsfolgen verarbeiten müssen und das HW-Debugging nicht
richtig echtzeitfähig ist. Da bekomme ich mit LED oder UART oft
sinnvollere Debug-Ausgaben.
>> Karl heinz Buchegger schrieb:>> Aber welchen Sinn soll es haben, wenn du das Bit 6 abfrägst um dann doch>>>> wieder auf den ADC zu warten.>>> Das Problem ist glaube ich, dass ich die Arbeitsweise des ADC im> Allgemeinen nicht richtig verstanden habe. Im Datenblatt steht>> "When a positive edge occurs on the selected trigger signal,> the ADC prescaler is reset and a conversion is started. This provides a> method of starting conversions> at fixed intervals. If the trigger signal is still set when the> conversion completes, a new> conversion will not be started.">> Somit werden nur positive und keine negativen Flanken erkannt, da mein> Wert aber auch mal absacken kann sind negative Flanken essentiell.> Außerdem warum sollte ich in nem frei laufenden ADC exakte Intervalle> haben wollen? Dafür habe ich doch dann den single mode?!>> Ich werde das ganze nochmal von vorne überdenken und dann, falls mich> keiner haut, nochmal ne Frage dazu stellen.
Bei konkreten Fragen wird Dich kaum jemand "verhauen".
...