Forum: Mikrocontroller und Digitale Elektronik STM32F429 ADC DMA


von Klaus L. (keyel80)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich versuche gerade, mit meinem STM32F429 discovery einen 
Sharp-Entfernungsmesser auszulesen und die Werte im Log auf dem Display 
anzuzeigen. Dazu ist es einfach nur notwendig, mit dem ADC eine Spannung 
zu messen. Ich nutze im Übrigen CubeMX 4.2.0 und die aktuelle Firmware 
1.1.0
Das Auslesen & Anzeigen gelingt mir bereits im ACD-Polling-Mode. Nun 
möchte ich auf DMA umstellen, habe aber Probleme.

Bewaffnet mit Debugger und Reference-Manual habe ich mich auf die 
Fehlersuche begeben und einen möglichen Fehler eingegrenzt. Im 
Screenshot seht ihr den Aufruf __HAL_DMA_ENABLE(hdma). Dahinter steckt 
ein Makro, das das Enable-Bit auf "1" setzen sollte. Dennoch ist das 
Enable-Bit in der nächsten Codezeile wieder "0". Dazwischen kam kein 
Interrupt o.ä., weil ich den Debugger im Single-Step-Modus laufen ließ. 
Das Reference-Manual des 429 sagt zum "EN"-Bit auf S. 328:
---------------------
This bit may be cleared by hardware:
– on a DMA end of transfer (stream ready to be configured)
– if a transfer error occurs on the AHB master buses
– when the FIFO threshold on memory AHB port is not compatible with the 
size of the burst.
---------------------

Hmm, mir sagt das alles reichlich wenig. Es wäre super, wenn mir jemand 
bei der weiteren Fehlersuche oder -eingrenzung helfen könnte. Vielen 
Dank!

Grüße
Klaus

von Frank B. (f-baer)


Lesenswert?

Auch im Single-Step-Betrieb kommen Interrupts, von denen du nichts 
merkst. Ausschliessen kannst du das nur, wenn du in der ISR einen 
Breakpoint setzt und der nicht angelaufen wird.

von Jim M. (turboj)


Lesenswert?

Klaus L. schrieb:
> Dazwischen kam kein
> Interrupt o.ä., weil ich den Debugger im Single-Step-Modus laufen ließ.

Aber der ADC und das DMA Peripherial selbst wird beim Debugging nicht 
angehalten - und das Enable Bit wird durch die Hardware am Ende des 
Transfers gelöscht.

Das Bit würde nur dann bei "1" bleiben, wenn der Transfer länger dauert 
als das Auslesen via Debugger. Man könnte es mal mit einer niedriegen 
ADC Abtastrate probieren (z.B. < 1 Hz).

von Klaus L. (keyel80)


Lesenswert?

Vielen Dank für Eure Antworten. Es gibt Neuigkeiten:

In meiner Applikation waren alle Peripherials des 429discovery 
konfiguriert, weil ich as 429discovery-Template des CubeMX verwendet 
habe. Damit habe ich mir natürlich potentielle Seiteneffekte 
eingehandelt, die ich auf jeden Fall vermeiden wollte.

Ich erzeugte deshalb ein neues Projekt, in dem ausschließlich der ADC 
und der DMA2 konfiguriert und aktiviert werden. Den Wert der Variablen, 
die per DMA2 aktualisiert werden sollte , habe ich mir per semihosting 
auf meinem PC ausgegeben. Die Konfiguration des ADC und des DMA habe ich 
1:1 vom Vorprojekt übernommen.

Zunächst waren keine Interrupts außer SysTick aktiviert. Der 
Variablenwert wurde nicht aktualisiert.

Aus purem Zufall aktivierte ich dann den DMA-Interrupt und traute meinen 
Augen kaum: Der Variablenwert wurde regelmäßig aktualisiert.
Bitte beachtet: Ich habe keine eigene Callback-Funktion geschrieben; 
beim Interrupt wurde also nur der Handler der HAL und der weak-Callback 
ausgeführt!

Ich ging einen Schritt weiter und wollte das Log-Display auf dem 
429discovery wieder aktivieren, um die Werte auf dem Board direkt 
auszugeben. Der erste aufzurufende Initialisierungsfunktion dazu lautet 
"BSP_LCD_Init();". Allein dieser Funktionsaufruf genügt, um das DMA 
meines ADC wieder lahm zu legen. Sobald dieser Funktionsaufruf 
auskommentiert ist, läuft die Sache wieder. Ich habe das Reference 
Manual zum DMA2 durchgelesen und erkenne eigentlich keine Konflikte mit 
dem Display Controller.

Ich verstehe zwei Dinge nicht:
- Weshalb muss ich den DMA-Interrupt aktivieren, um den zyklischen 
Transfer am Leben zu halten?
- Weshalb bewirkt der Aufruf von der Display-Initialisierungsfunktion, 
dass mein ADC-DMA lahm gelegt wird?

Das Projekt habe ich hier hochgeladen: 
https://stm32tutor.googlecode.com/svn/trunk/429AdcDma

Danke für Eure Hilfe!

Klaus

von Klaus L. (keyel80)


Lesenswert?

Weiß hier keiner Rat?

Vielen Dank!

von fuelre F. (fuelre)


Lesenswert?

vorweg - ich haben noch keinen stm32 vor mir gehabt

aber zu deiner 1. Frage betüglich DMA interrupt:
für mich klingt das recht logisch - denn die DMA ist ja im grunde nur 
ein schieberegister - wenn da kein interrupt die Daten weiterschiebt 
können auch keine neuen Daten mehr kommen

zu der 2. Frage:
aktiviert die LCD_init eventuell irgendwelche Teile die du schon 
irgendwo nutzt? timer, I/Os, DMA ...
hast du schon getestet ob wirklich die DMA lahm liegt oder funktioniert 
der ADC an sich nicht mehr?

ich hoffe ich konnte helfen

von tom57 (Gast)


Lesenswert?

Hallo,
vielleicht kollidiert Dein DMA mit dem DMA des TFT.
Schonmal darüber nachgedacht?

von Klaus L. (keyel80)


Lesenswert?

@fuelre zu 1: Hmm, die Logik erschließt sich mir leider noch nicht. Wenn 
ich DMA nutze, möchte ich doch explizit auf interrupt-basierte 
Datenschieberei verzichten. Das End-of-Conversion-Event soll also 
lediglich einen DMA-Transfer auslösen, aber keinen Interrupt erzeugen. 
Verstehe ich da etwas falsch?

@fuelre zu 2: in meinem zweiten Testprojekt konfiguriere ich 
ausschließlich den ADC und den DMA2. Gemäß Reference Manual hat das LCD 
nicht den DMA2 zu tun. Ich habe mir den Initialisierungscode des LCDs 
angeschaut: An keiner Stelle wird da irgendwas mit dem DMAx oder dem ADC 
gemacht.

Der ADC an sich funktioniert weiterhin, allerdings nicht im DMA-Modus, 
sondern nur noch im Polling-Modus. Diesen Polling-Modus nutze ich jetzt 
auch mal in meinem Projekt, aber schön ist das nicht...

@tom: Ja, darüber habe ich nachgedacht (siehe "@fuelre zu 2")


Vielen Dank für Eure weitere Hilfe!

von ./. (Gast)


Lesenswert?

Hier mal ein Schnipsel von einem STM32F103.
Da tut DMA ohne jeden Interrupt.
So ist es ja auch gedacht.
1
  /* DMA1 channel1 configuration ----------------------------------------------*/
2
  DMA_DeInit(DMA1_Channel1);
3
  DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
4
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADCConvertedValue;
5
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
6
  DMA_InitStructure.DMA_BufferSize = 256;
7
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
8
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
9
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
10
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
11
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
12
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
13
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
14
  DMA_Init(DMA1_Channel1, &DMA_InitStructure);
15
  
16
  /* Enable DMA1 channel1 */
17
  DMA_Cmd(DMA1_Channel1, ENABLE);
18
  
19
  /* ADC1 configuration ------------------------------------------------------*/
20
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
21
  ADC_InitStructure.ADC_ScanConvMode = ENABLE;
22
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
23
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
24
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
25
  ADC_InitStructure.ADC_NbrOfChannel = 1;
26
  ADC_Init(ADC1, &ADC_InitStructure);
27
28
  /* ADC1 channel10 */
29
  ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_55Cycles5);
30
31
  /* Enable ADC1 DMA */
32
  ADC_DMACmd(ADC1, ENABLE);
33
  
34
  /* Enable ADC1 */
35
  ADC_Cmd(ADC1, ENABLE);
36
37
  /* Enable ADC1 reset calibration register */   
38
  ADC_ResetCalibration(ADC1);
39
  /* Check the end of ADC1 reset calibration register */
40
  while(ADC_GetResetCalibrationStatus(ADC1));
41
42
  /* Start ADC1 calibration */
43
  ADC_StartCalibration(ADC1);
44
  /* Check the end of ADC1 calibration */
45
  while(ADC_GetCalibrationStatus(ADC1));
46
     
47
  /* Start ADC1 Software Conversion */ 
48
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);

von Uwe B. (derexponent)


Lesenswert?

also fakt ist, das ADC per DMA auch ohne Interrupt funktioniert
(siehe Beispiel von ./.)

ich habe deinen Code nicht überprüft, aber
kann es sein, das vlt. nur der Clock vom DMA nicht aktiviert ist ?

von Klaus L. (keyel80)


Lesenswert?

Hallo Uwe,

in deinem Beispiel für den F1 nutzt Du die alte "Standard Peripherial 
Library". Für meinen F4 nutze ich bereits die neue HAL. Aus Deinem 
Beisiel lässt sich also leider nur ableiten, dass ADC-DMA auf einem 
anderen Prozessor mit einer anderen Firmware funktioniert. Mir hilft das 
somit leider nur wenig weiter.

Die HAL abstrahiert etwas stärker, als die alte SPL. Insbesondere das 
Einschalten der Clock wird von den Bibliotheksroutinen intern 
realisiert. Insofern kann man das Einschalten glücklicherweise nicht 
mehr vergessen:-)

In LCD_Init() muss es irgendeine magische Wechselwirkung geben, aber ich 
kann mir einfach nicht vorstellen, wo. Sicherlich könnte ich jetzt Zeile 
für Zeile aus den Initialisierungsfunktionen des LCDs rauslöschen und 
wieder testen, aber sowas möchte man doch nur im äußersten Notfall 
machen...

von Uwe B. (derexponent)


Lesenswert?

Klaus L. schrieb:
> Aus Deinem
> Beisiel lässt sich also leider nur ableiten, dass ADC-DMA auf einem
> anderen Prozessor mit einer anderen Firmware funktioniert. Mir hilft das
> somit leider nur wenig weiter.
>

1. Hinweis : das Beispiel ist nicht von mir
2. ich habe einen laufenden Code für den STM32F429 mit ADC per DMA
   (ohne Interrupt)
3. Ja, ich benutze nicht die neuen HAL Libs


> Insofern kann man das Einschalten glücklicherweise nicht
> mehr vergessen:-)

wenn dem so wäre, würde es funktionieren...
...tut es ja aber anscheinend nicht

also gehe ich davon aus, das man doch noch "manuell" etwas
programmieren (oder hinzufügen) muss

und um eine Fehlersuche wirst du wohl nicht rumkommmen,
es sei denn, dir reicht der ADC im polling mode

von ./. (Gast)


Lesenswert?

Alte Lib - Neue Lib ist doch voellig egal.

Ich kann die Werte auch mit einem Debugger in die Register schreiben
und der DMA wird die Werte aus dem ADC in den Speicherbereich schreiben.

Mein Beispiel sollte Dir nur entsprechende Mindestkonfiguration
vermitteln, mit der es bei einem F103 funktioniert.
Und so anders gestrickt sind die F4 da auch nicht.

Wenn es bei Dir nicht funktioniert, wirst Du halt jede Einstellung
von Clock, ADC und DMA an Hand des Referenzhandbuches pruefen muessen.

Magische Effekte gibt es nur in Maerchen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.