Hallöchen,
ich habe eine Frage bzw. benötige etwas Hilfe beim Einstellen der
SampleTime beim STM32F4 Disco. Vielleicht könnt ihr mir da ein wenig
helfen.
Ich brauche eine stabile Abtastfrequenz von 160kHz. Aber ich finde
nichts, wo man die Abtastfrequenz beim ADC selbst so genau einstellen
kann. In den Registern gibt es leider immer nur Prescaler, Resolution,
SampleTime(3,5,...,480 Cycles) und ADC_TwoSamplingDelay.
Wobei mir auch nicht 100% klar ist, wie sich letzendlich die komplette
Zeit zusammensetzt.
Bisher habe ich es so gelöst, dass ich den ADC auf Channel 10 im
continuouse mode laufen lasse, und mit einem Timer IRQ in einer Frequenz
von 160kHz einen Interrupt auslöse und mir dann den momentanwert des
AD-Wandlers hole. Aber das muss doch auch irgendiwe mit dem ADC selbst
möglich sein! Wäre ja schon irgendwie ideal.
Schon mal Danke im Voraus. Wenn gewünscht, kann ich auch gerne mal
meinen Code mitschicken.
Gruß
Justus
Die ADC's haben keine präzisen Timer. Aber du kannst einen der normalen
Timer auf 160 kHz einstellen und damit den ADC triggern lassen. Im
Reference Manual steht wie das geht.
Dr. Sommer schrieb:> Die ADC's haben keine präzisen Timer. Aber du kannst einen der normalen> Timer auf 160 kHz einstellen und damit den ADC triggern lassen. Im> Reference Manual steht wie das geht.
Naja, das habe ich ja oben geschrieben. So mache ich es ja bisher. Ich
dachte nur, dass müsste doch auch direkt mittels ADC gehen.
Oder hat jemand noch eine adequate Lösung, wie man das doch über den ADC
direkt machen kann?
Aber vielleicht kann mir noch jemand behilflich sein, wie ich die Zeit
berechne, die mein ADC benötigt, um einen Wert umzuwandeln. Und ja, ich
weiß im HAnd-Manual steht diese Gleichung auf Seite 400. Aber die lässt
mich da nicht ganz hintersteigen.
Du kannst den adc entweder einschalten oder ausschalten. Samplerate
richtet sich dann nach der von dir erwähnten Gleichung.
Je schneller du dir die werte abholst, auf welchem Weg auch immer, desto
schwieriger wird es sicherzustellen, dass du genau den Wert abgeholt
hast, den du auch haben wolltest. Zumindest, wenn nebenher noch anderes
zeug läuft. Da musst du dann die Priorität für den adc-teil hochsetzen.
Justus Z. schrieb:> Naja, das habe ich ja oben geschrieben
Nein, du hast continuous mode und interrupts geschrieben. Ich habe
gesagt, dass der Timer automatisch eine ADC Konvertierung ohne
Interrupt auslösen kann, über die interne Synchronisations Verschaltung
im Controller. Nein, es gibt keine andere Möglichkeit, weil dies die
dafür vorgesehene Möglichkeit ist. Zusammen mit DMA kann man auch ganz
auf Interrupts verzichten.
Dr. Sommer schrieb:> Nein, du hast continuous mode und interrupts geschrieben. Ich habe> gesagt, dass der Timer automatisch eine ADC Konvertierung ohne> Interrupt auslösen kann, über die interne Synchronisations Verschaltung> im Controller. Nein, es gibt keine andere Möglichkeit, weil dies die> dafür vorgesehene Möglichkeit ist. Zusammen mit DMA kann man auch ganz> auf Interrupts verzichten.
Und wo kann ich das genau nachlesen? Das würde mich auch nochmal
interessieren, wenigstens um mal davon gehört zuhaben und noch eine
ausweich Möglichkeit zuhaben.
Welche Vorzüge hätte denn diese Variante?
Edit: Oder würde das Gedanklich so funktionieren, dass der Timer dann
alle 160kHz einen Signal an den ADC gibt eine Konvertierung zu starten?
Würde mich über guten Input freuen.
Momentan habe ich das Problem, dass irgendwas mit der Zeit nicht so
genau funktioniert. Ich habe den Timer 3 (84MHz) mit einem Prescaler von
105 auf 800kHz. Einen Counter mit Auto-Reloader habe ich auf 800kHz /
160kHz - 1 = 4 gestellt. Somit sollte der Timer einen Interrupt alle
6,25µs auslösen. In dem Timer Interrupt steht folgender Code:
[...]
if( iBufCho ) {
aiBuffer1[iBufPos] = ADC1->DR;
iBufPos++;
if( iBufPos == iAnzahlAbtast) {
iBufPos = 0;
iBufCho = 0;
fFrequenz = iCalc();
}
}
else {
aiBuffer2[iBufPos] = ADC1->DR;
iBufPos++;
if( iBufPos == iAnzahlAbtast ) {
iBufPos = 0;
iBufCho = 1;
fFrequenz = iCalc();
}
}
[...]
Es wird also der konvertierte Wert des AD Wandlers in ein Puffer
geschrieben.
Wenn der Puffer voll ist, wird eine Rechenfunktion aufgerufen. die ist
etwas länger und benötigt ca. 60000 Takten. Habe gerade nicht im Kopf,
wie groß die Rechenleistung beim F4 ist. Aber müsste diese Zeit dann
noch irgendwie mit beachtet werden?
Weil ich habe das Gefühl, das der Timer nicht richtig läuft. in der
Calc() lasse ich alle 50 Durchgänge (das müsste eigentlich eine Zeit von
genau 1s entsprechen) eine LED togglen und die toggled um ca. das
1.7-Fache zu langsam. Oder vergesse ich bei dem ganzen irgendwas.
Justus Z. schrieb:> Aber müsste diese Zeit dann> noch irgendwie mit beachtet werden?
Na klar, genauso wie die ADC_Conversion_Time. Der ADC sampled natürlich
durchgehend. Aber du holst dir die Daten ja ab.
Reginald L. schrieb:> Na klar, genauso wie die ADC_Conversion_Time. Der ADC sampled natürlich> durchgehend. Aber du holst dir die Daten ja ab.
Gut. Hehe. Das dachte ich mir schon. Ich bin mir halt nur relativ
unsicher, wie viele Dinge der Microcontroler parallel laufen haben kann,
ohne das zeitlich etwas beeinflusst wird.
Also die Funktionen die bearbeitet werden sind:
- ADC
- Beschreiben zweier Buffer (abwechselnd)
- Verrechnen der Buffer
ADC:
Der ADC läuft meines Erachtens im Hintergrund (also die Wandlung
selbst). Wenn diese vorrüber ist, wird ein Interrupt ausgelöst und der
Wert in eine "Warte-Variable" geschrieben. Das dürfte mit einer
Geschwindigkeit von t_ADC passieren. Der ADC läuft über den APB2 mit
84MHz und wird mit einem Prescaler von 6 auf 14MHz runter "gedrosselt".
Bedient wird nur ein Channel, ein ADC und eine Wandlung. Die Zeit der
Conversion selbst berechnet sich mit:
T_conv = SamplingTime + 12 Cycles -> 12 Cycles werden denke mal die
Resolution sein. Leider weiß ich nicht, woraus sich die SampleTime
zusammen setzt. Dazu kommen noch die ADC_TwoSamplingDelay, die man
einstellen kann (mind. 5 * T_ADCCLK). Auch hier weiß ich leider nicht,
warum man die benötigt. Also berechnet sich die Zeit für einen
kompletten Durchgang des AD-Wandlers mit:
T_ADC = T_conv + T_delay. Richtig? Wobei halt die Wandlung selbst im
Hintergrund läuft. Die interrupt Funktion des ADC selbst beinhaltet wie
erwähnt, nur das beschreiben einer Variable und das zurücksetzen eines
Bits. Also 3 Takte
Timer/Verrechnen der Buffer:
Der läuft wie oben schon Berechnet mit einer Geschwindigkeit von 160kHz.
Der Code darin benötigt 6 Takte, wenn der Buffer noch nicht gefüllt ist
und ca. 50000 (nochmal genau nachgerechnet). Somit Berechnet sich die
Zeit für den Timer zu:
T_timer = 6 bzw. 50000 * T_HCLK. Korrekt?
Und da sehe ich auch gleich eine Frage. Wenn diese timer Zeit über
160kHz kommt, wird dann ein Interrupt in einen Interrupt ausgelöst?
Und wie könnte ich dieses Problem umgehen. Gibt es eine Möglichkeit den
Buffer zu befüllen, währenddessen schön gerechnet wird?
Hoffentlich bekomme ich jetzt nicht zu stark den Kopf gewaschen.
Gruß
Justus
Ich würde sie sache ganz anders angehen:
Adc clock: würde ich versuchen auf das mögliche Maximum zu stellen. Je
höher, desto höher wird die Chance, dass du genau den Sample abholst den
du haben möchtest. Bei den relativ niedrigen sampling raten die du
erzielen möchtest reichen die 14mhz aber alle mal.
Conversion time: je höher, desto länger wird vom adc die "anliegende
Spannung gemessen und gemittelt". Die sollte man also auch auf das
mögliche Maximum einstellen. Der wert hängt natürlich von deiner
gewünschten frequenz und der adc clock ab.
Die Abholung der Daten würde ich bei der sampling rate schon fast dem
dma überlassen. Double buffering und du kannst zwischendurch gemütlich
deine Werte berechnen.
Justus Z. schrieb:> Hoffentlich bekomme ich jetzt nicht zu stark den Kopf gewaschen.
Zu der Sorte microcontroller.net-Menschen gehöre ich nicht ;)
Hier mal ein alter Codeschnipsel von mir. Da triggert ein Timer den DMA,
der die Daten abholt. Aber pass auf, da sind noch einige Fehler
enthalten, so darf z.B. Timer.Prescaler nur 16bit groß sein. Aber vom
Prinzip her hilft dir das vllt weiter.
Und der DMA läuft im manuellen DoubleBuffer, der kann das natürlich auch
von selber.
Justus Z. schrieb:> Und wo kann ich das genau nachlesen?
Im Reference Manual natürlich. S. 400-402.
Justus Z. schrieb:> Edit: Oder würde das Gedanklich so funktionieren, dass der Timer dann> alle 160kHz einen Signal an den ADC gibt eine Konvertierung zu starten?
Ja.
Justus Z. schrieb:> Welche Vorzüge hätte denn diese Variante?
Dass du so die Samplerate auf fast jeden beliebigen Wert einstellen
kannst (alle Teiler des Systemtakts von max. 168MHz). Durch Kaskadierung
mehrerer Timer auch auf extrem langsame Werte, falls nötig.
Reginald L. schrieb:> Adc clock: würde ich versuchen auf das mögliche Maximum zu stellen. Je> höher, desto höher wird die Chance, dass du genau den Sample abholst den> du haben möchtest.
Dadurch Jittert die Sample-Rate aber im Endeffekt. Nicht gut für
DSP-Anwendungen.
Dafür ist die Timer-Synchronisation da, für exakte Sample-Rates, warum
die nicht einfach benutzen?!
Reginald L. schrieb:> Aber pass auf, da sind noch einige Fehler> enthalten
Ja, z.B. ist ein Klassenname von _DAQ in C++ verboten (Unterstrich +
Großbuchstabe, oder Doppelunterstrich, am Anfang, sind der Standard
Library vorbehalten).
Hier mal ein Codeschnippsel, hier wird Timer2 dazu genutzt den ADC mit
100kHz zu trigger (Samplerate), der DMA holt die Daten ab und wenn der
Buffer voll ist gibts n Interrupt: