Hallo!
Ich möchte herausfinden, wie man den DAC zusammen mit DMA richtig
verwendet, um eine Sequenz (=Gruppe) von Channels zu konvertieren.
Da ich DMA bisher noch nie verwendete, kann es sein, dass ich eine
falsche Vorstellung davon habe, daher bitte ich, mich zu korrigieren.
Im Reference Manual steht im Kapitel ADC:
When the DMA mode is enabled (DMA bit set to 1 in the ADC_CR2 register),
after each conversion of a regular channel, a DMA request is generated.
Angenommen ich habe in der Gruppe 3 Channels, CH0, CH1, CH3, und diese
werden kontinuierlich konvertiert.
Wenn CH1 fertig ist, wird ein DMA Request gestartet, der die Daten in
den Speicher an die Stelle MEM0 schreibt. Ist dann CH2 fertig, wird
wiederum so ein Request gestartet, der Pointer der Speicherzelle wird
aber incrementiert und somit auf MEM1 = MEM0 + 1 geschrieben. Gleiches
für CH3. Was passiert dann aber, wenn wieder CH0 drankommt? Muss ich da
den Circular Mode aktivieren, damit der DMA_SxNDTR Inhalt wieder mit 3
beschrieben wird und alles von vorn losgeht?
Wie kann ich erreichen, dass der Beginn der DMA Übertragung mit CH0
zusammenfällt, und CH0 wirklich auf MEM0 geschrieben wird. Der DMA
Controller weiss ja nicht, ob CH0, CH1 oder CH2 gerade konvertiert
worden ist. Oder gehe ich einfach davon aus, dass der Mechanismus nie
außer Tritt fällt, und nach Millionen von Konversionen, die Reihenfolge
immer noch stimmt?
Ich hoffe, der Kern meiner Frage ist "rübergekommen". Vielleicht kann
mir jemand in kurzen Worten erklären, wie man DMA mit dem ADC richtig
verwendet.
Danke!
ttl schrieb:> in den STM libraries gibt es dazu ein Bespiel
Ja, danke, es mangelt ja nicht an Beispielen. Ich habe eine 2 Kanal ADC
Konvertierung mit DMA anhand eines Beispiels implementiert, wo CH0/CH1
auf Adresse AD0/AD1 geschrieben werden - und es funktioniert (bisher)
makellos!
Trotzdem habe ich da irgendwie ein "flaues Gefühl im Magen", da die
Abfolge
CH0, CH1, CH0, CH1, ...
AD0, AD1, AD0, AD1, ...
durch das erstmalige Auftreten eines fertigen Konversionszyklus
zugeordnet wird. Sollte aus irgendeinem Grund (vielleicht bin ich hier
übersensibel, aber was ist wenn die MCU monatelang läuft und ein
interner HW Fehler oder sonst was auftritt...) die Zuordnung
durcheinander kommen
CH0, CH1, CH0, CH1, ...
AD1, AD0, AD1, AD0, ...
würde ich das nicht merken!!!
1) Ist diese Angst unbegründet?
2) Oder gibt es dagegen ein Patentrezept?
Ich gebe mich nicht damit zufrieden, dass das Beispiel "halt
funktioniert", sondern möchte wissen, was ich tun muss, um die Daten
100% (und nicht 99.999%) zuordnen zu können.
Abgesehen davon glaube ich nicht, dass man diesen kontinuierlichen Modus
wirklich benötigt. Bei allem was ich mir an sinnvollen Anwendungen
vorstellen kann, möchte man Konversionen in einem bestimmten zeitlichen
Abstand anstoßen, und dann würde ich Interrupts verwenden zusammen mit
dem ADC single-shot Modus.
Danke, Michael
Michael W. schrieb:> Trotzdem habe ich da irgendwie ein "flaues Gefühl im Magen", da die> Abfolge>> CH0, CH1, CH0, CH1, ...> AD0, AD1, AD0, AD1, ...>> durch das erstmalige Auftreten eines fertigen Konversionszyklus> zugeordnet wird. Sollte aus irgendeinem Grund (vielleicht bin ich hier> übersensibel, aber was ist wenn die MCU monatelang läuft und ein> interner HW Fehler oder sonst was auftritt...) die Zuordnung> durcheinander kommen>> CH0, CH1, CH0, CH1, ...> AD1, AD0, AD1, AD0, ...>> würde ich das nicht merken!!!
Etwas nekrophil, aber genau das Problem hatte ich gerade.
Wenn man das DDS bit ungesetzt lässt, läuft der DMA genau einmal durch.
Da ich in meinem Falle nur alle 10ms ein paar Messwerte benötige, habe
ich die ADC-DMA-Geschichte wie im Reference Manual beschrieben neu
gestartet.
Also:
1
// startet alle 10ms den ADC neu
2
voidstart_adc(){
3
// DMA bit löschen
4
// und wieder setzen für nächste Konvertierung
5
// RM0383 11.8.1 Data management -> Using the DMA
6
ADC1->CR2&=~(ADC_CR2_DMA);
7
ADC1->CR2|=ADC_CR2_DMA;
8
}
Und dort trat genau dieser Effekt auf. Also manchmal hatte ich AD0-AD1
als Ergebnis, manchmal AD1-AD0.
Zudem, obwohl im RM genau das Gegenteil erzählt wird, sollte durch DDS
== 0 nie ein Overrun entstehen. Aber das hatte ich jetzt auch.
Was also machen? Nach langem probieren kam ich letzendlich auf die
einzige Lösung in diesem Falle:
1
// startet alle 10ms den ADC neu
2
voidstart_adc(){
3
ADC1->CR2&=~(ADC_CR2_ADON);// A/D aus
4
ADC1->CR2&=~(ADC_CR2_DMA);
5
ADC1->CR2|=ADC_CR2_DMA;
6
ADC1->CR2|=ADC_CR2_ADON;// A/D an
7
ADC1->CR2|=ADC_CR2_SWSTART;// und nächste Runde starten
Den ADC auszuschalten ist eigentlich keine gute Idee. Der braucht laut
Datenblatt bis zu 3µs um genau zu laufen.
Um da keine Probleme zu bekommen habe ich jetzt den Transfer Interrupt
Complete vom DMA eingeschaltet und im zugehörigen Interrupt dann das
DMA_CR2_CONT-bit gelöscht. Das ganze funktioniert dann genau so.
Also CONT bit löschen im DMA Handler. Und dann im start_adc() einmal
wieder das CONT bit setzen und den ADC über SWSTART anstupsen.