Forum: Mikrocontroller und Digitale Elektronik STM32H7 Multichannel ADC DMA


von MAX M. (maxmax123)


Angehängte Dateien:

Lesenswert?

Ich möchte mittels DMA 6 ADC Kanäle (STM32H7, ADC3) kontinuierlich 
sampeln. Anyway das Problem alle Daten sind 0 und es wird ebenfalls kein 
Interrupt bez DMA ausgelöst.

Relevantes im code in korrekter Reihenfolge:
_attribute_ ((aligned (32))) unsigned int ADC3Values[6]={0,0,0,0,0,0};
-- dann im main()
  SCB_EnableDCache();
  MX_ADC3_Init();
  MX_BDMA_Init();
  HAL_ADCEx_Calibration_Start(&hadc3, 
ADC_CALIB_OFFSET,ADC_SINGLE_ENDED);
  HAL_ADC_Start_DMA(&hadc3, (uint32_t*)ADC3Values, 6);
-- dann im while(1)
SCB_InvalidateDCache_by_Addr((uint32_t*)ADC3Values, 24);

Die ADC3Values werte bleiben alle bei 0. Versuchweise hatte ich den 
Cache deaktiviert - es scheint nicht ein cache problem zu sein.
Da die Interupts (nach deren Aktivierung) ebenfalls nicht ausgelöst 
werden, scheint es so dass der ADC gar nicht beginnt (oder der DMA nicht 
ausgelöst wird). Auffällig ist ebenfalls, dass der ADC3 init vor dem 
BDMA init erfolgt (dies hat der cube so generiert).

von Kevin M. (arduinolover)


Lesenswert?

In welchem RAM liegt denn dein Array? Der BDMA kann nur auf den RAM in 
D3 zugreifen, wenn ich mich richtig erinnere.

Früher wurde bei Cube Projekten standardmäßig der DTCM RAM benutzt, 
mittlerweile sind meine ich alle sections angelegt und er benutzt 
standardmäßig den RAM in D1.

Das Speichermanagement mit DMA beim H7 ist äußerst komplex, falls das 
dein erstes Projekt in die Richtung ist, kann das spaßig werden.

von MAX M. (maxmax123)


Lesenswert?

Kevin M. schrieb:
> In welchem RAM liegt denn dein Array? Der BDMA kann nur auf den RAM in
> D3 zugreifen, wenn ich mich richtig erinnere.

Das Array startet bei 0x24000c40 also im 0x24 Bereich welches meines 
wissens das D1 ist (!). Habe diesen Address Bereich auch in einem DMA 
tutorial gesehen, und war der default im cube...
Muss das morgen mal testen, danke für den Tipp! Bus Matrix 1 und 3 
sollten doch zwar paralelle AHB sein welche jedoch an einer AXI Matrix 
sind.

Lese gerade:
AXI (Advanced eXtensible Interface) SRAM (D1 domain) is
mapped at address 0x2400 0000 and accessible by all
system masters except BDMA through D1 domain AXI bus
matrix

Anscheinend doch nicht?!

von Kevin M. (arduinolover)


Lesenswert?

MAX M. schrieb:
> Anscheinend doch nicht?!

Was sich immer Lohn its ein Blick ins Reference Manual:

BDMA main features
• Single AHB master
• Peripheral-to-memory, memory-to-peripheral, memory-to-memory and 
eripheral-to-peripheral data transfers
• Access to D3 domain SRAM and AHB/APB peripherals (BDMA)
.....


Tu dir einen gefallen und leg dir für alle Speicher Bereiche sections an 
(sofern nicht schon geschehen). Wenn du sie initialisiert haben willst, 
pass das startup file entsprechend an (sofern nicht schon geschehen). 
Und leg dir letztlich Makros für die Speicher Bereiche an, damit du 
deine Variablen einfach da hin schieben kannst wo du sie brauchst.

Der H7 ist ein sehr komplexer Controller, dir werden noch mehr solcher 
Fallstricke begegnen.

von KM (Gast)



Lesenswert?

Zur Übersicht beim Programmieren des H7 hab ich diese beiden Diagramme 
immer zur Hand.

von Hermann S. (diphtong)


Lesenswert?

Ich hab was ähnliches, bei mir funktionierts (allerdings mit einem 
F103):

ADC hab ich eigentlich gleich eingestellt, beim DMA hab ich nur "Half 
Word" und ich hab noch beim ADC das "global interrupt" angehacklt.

Beim initialisieren kommt bei mir der DMA vor dem ADC (hat auch die IDE 
so rausgehauen).

Das:
SCB_EnableDCache();
und das:
SCB_InvalidateDCache_by_Addr((uint32_t*)ADC3Values, 24);
hab ich nicht (was macht das eigentlich?)

Bei mir siehts dann so aus:
Variable:
volatile uint16_t ADCWerte[6]; //ADC Werte
.
.
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_TIM1_Init();
MX_I2C1_Init();
MX_TIM3_Init();
MX_TIM2_Init();
MX_TIM4_Init();
/* USER CODE BEGIN 2 */
HAL_Delay(100);
SSD1306_Init();
SSD1306_Clear();
HAL_Delay(100);
HAL_TIM_IC_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_IC_Start(&htim2, TIM_CHANNEL_3);
HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);
HAL_ADC_Start(&hadc1);
HAL_ADCEx_Calibration_Start(&hadc1);
HAL_Delay(150);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*) ADCWerte, 6); //ADC -> DMA start
HAL_Delay(500);

und dann auslesen:

HAL_ADC_Stop_DMA(&hadc1); //ADC -> DMA stop

//ADC Werte aufsummieren
pot += ADCWerte[4];
sens1 += ADCWerte[0];
sens2 += ADCWerte[1];
tsens1 += ADCWerte[2];
tsens2 += ADCWerte[3];
Schalter = ADCWerte[5];

HAL_ADC_Start_DMA(&hadc1, (uint32_t*) ADCWerte, 6); //ADC -> DMA start


Ich lese die Werte in einem Interrupt mit einem bestimmten 
Timerintervall ein, währenddessen stoppe ich den DMA. Das ist aber nicht 
das ADC interrupt.

Vll. solltest Du auch ein Delay nach der ADC Kalibration einfügen, hab 
mal irgendwo gelesen, dass das sinnvoll sein soll.

: Bearbeitet durch User
von MAX M. (maxmax123)


Lesenswert?

Kevin M. schrieb:
> Tu dir einen gefallen und leg dir für alle Speicher Bereiche sections an
> (sofern nicht schon geschehen).

Danke, Nein habe ich nicht gemacht. Sehe im startup file keinen user 
editierbaren Bereich. Wie mache ich das dass es vom cube nicht 
überschrieben wird? (Kann man dies im Cube irgendwie einstellen, dass er 
diese sections ins startup file schreibt?)

Kevin M. schrieb:
> Der H7 ist ein sehr komplexer Controller, dir werden noch mehr solcher
> Fallstricke begegnen.

Das HAL sollte einem diese Probleme eigentlich abnehmen. Anyway schon 
etwas besonders, dass in der HAL_ADC_Start_DMA dies mit keinem Wort 
erwähnt wird.
Andere Frage:
Wie wird eigentlich der DMA controller ge'debug't?

Hermann S. schrieb:
> Das:
> SCB_EnableDCache();
> und das:
> SCB_InvalidateDCache_by_Addr((uint32_t*)ADC3Values, 24);
> hab ich nicht (was macht das eigentlich?)

Der H7 hat einen Cache (schneller Zwischenspeicher 480MHz zwischen der 
schnellen CPU und dem langsamen RAM/Flash (meines wissens 70MHz)). Durch 
diesen Cache wird die performance des H7 erst ermöglicht, daher wichtig 
dass dieser aktiviert wird (SCB_EnableDCache(); sowie 
SCB_EnableICache();, Daten und Instruktionen).
Das Problem ist nun, dass der DMA Kontroller ins memory und nicht in den 
Cache schreibt, und dies dem CPU nicht mitteilt. Also wenn die CPU die 
daten Lesen will schaut er zuerst nach obs im Cache vorhanden ist und 
falls ja holt er sie von dort (falsche daten). Aus diesem Grund muss der 
Cache in Bezug zu DMA unterhalten werden. Eine Möglichkeit ist 
SCB_InvalidateDCache_by_Addr( wo aktiv mitgeteilt wird, dass bei einem 
Abruf von daten aus diesem Adressbereich diese nicht aus dem Cache 
geholt werden.

von MAX M. (maxmax123)


Lesenswert?

Hab das Problem umgangen indem ich den BDMA durch den DMA ersetzt habe, 
dieser kann in 0x24 addr Bereich schreiben.
Das Eregebnis: Kanal eins erhält daten: "127" (erwartet gem analoger 
Spannung sind ca. 26200) und alle anderen werte des arrays bleiben bei 
"0"?!?

von Kevin M. (arduinolover)


Lesenswert?

Hermann S. schrieb:
> Ich hab was ähnliches, bei mir funktionierts (allerdings mit einem
> F103):

Bitte nicht Äpfel mit Birnen vergleichen.

MAX M. schrieb:
> (Kann man dies im Cube irgendwie einstellen, dass er
> diese sections ins startup file schreibt?)

Die sections legst du im Linker File (X_FLASH.ld) an, das ASM für die 
Initialisierung beim Booten im startup_x.s

Beide werden beim Erstellen des Projekts einmalig generiert und nicht 
mehr überschrieben, du kannst dort also bedenkenlos Änderungen 
vornehmen.

MAX M. schrieb:
> Das HAL sollte einem diese Probleme eigentlich abnehmen.

Das ist eine recht blauäugige Annahme. Die HAL nimmt dir vielleicht das 
Ansprechen der einzelnen Peripherien "ab" aber dennoch muss man das 
Zusammenspiel zwischen diesen verstehen. Auch mit der HAL kommt man 
nicht drum rum sich mit dem Gesamtsystem auseinander zu setzen und einen 
Blick ins Reference Manual zu werfen, gerade bei einem solch komplexen 
Controller.

MAX M. schrieb:
> Hab das Problem umgangen indem ich den BDMA durch den DMA ersetzt habe,

Du solltest auf Nummer sicher gehen, dass der DMA auch auf den ADC kommt 
nur der RAM nützt dir nichts. Das sollte aber hinhauen.

MAX M. schrieb:
> as Eregebnis: Kanal eins erhält daten: "127" (erwartet gem analoger
> Spannung sind ca. 26200) und alle anderen werte des arrays bleiben bei
> "0"?!?

Ist der DMA richtig konfiguriert, Wortbreite etc.

MAX M. schrieb:
> Wie wird eigentlich der DMA controller ge'debug't?

Der hat Status Register wie alle andere Peripherie auch.

PS: Statt immer SCB_InvalidateDCache_by_Addr zu verwenden kannst du dir 
auch einen gewissen Speicher Bereich mit der MPU als "non cacheable" 
markieren und Buffer für den DMA dort rein legen.

von J. S. (jojos)


Lesenswert?

In den HAL Beispielen zu DMA sind lange Hinweise zur Cache Problematik.

von MAX M. (maxmax123)


Angehängte Dateien:

Lesenswert?

Kevin M. schrieb:
> Ist der DMA richtig konfiguriert, Wortbreite etc.

Nun viel giebts da nicht einzustellen. Siehe Anhang. Ansonnsten alles 
gleich wie im orrig Post.

J. S. schrieb:
> In den HAL Beispielen zu DMA sind lange Hinweise zur Cache Problematik.

Denke nicht dass ich ein Cache problem habe. Obwohl ich dieses aktuell, 
wie Kevin M. richtig bemerkt hat) auf eine inefiziente weise löse.

von J. S. (jojos)


Lesenswert?

hier ist ein ADC circular Beispiel, da wird DMA1 verwendet, was 
natürlich von dem jeweiligen H7 abhängt. Wenn Cube verwendet wird, dann 
sollte auch der IRQ Handler generiert werden.
https://github.com/STMicroelectronics/STM32CubeH7/tree/c94252df7cec24a8fef67b28933353476a1edd3c/Projects/STM32H7B3I-EVAL/Examples/ADC/ADC_DMA_Transfer

Das kein Interrupt generiert wird läßt mich an meinem Problem mit dem 
SDMMC1 auch gerade verzweifeln, Ints gibt es komischerweise nur im 
PowerSafe Modus. Aber andere Baustelle.

von Kevin M. (arduinolover)


Lesenswert?

MAX M. schrieb:
> und alle anderen werte des arrays bleiben bei
> "0"?!?

Hast du die Interrupts an? Beim ADC bin ich gerade nicht sicher aber 
z.B. bei UART ist es so, dass neben dem DMA IQR auch der für den UART an 
sein muss, sonst bleibt der DMA nach dem ersten Transfer hängen. Das 
solltest du aber in den Statusregistern des DMA bzw. in der 
Datenstruktur des ADC sehen.

von MAX M. (maxmax123)


Lesenswert?

Kevin M. schrieb:
> Hast du die Interrupts an?

ja ist an aber HAL_ADC_ConvCpltCallback wird bez hdac3 nicht aufgerufen. 
Auch auffällig ist, dass der DMA für den ersten wert etwas macht.
Bez der ADC einstellungen im ersten Post sollte doch der ADC durchlaufen 
oder - habe fast der Verdacht DMA funktioniert der ADC jedoch nicht.

von MAX M. (maxmax123)


Lesenswert?

Habs:
Der cube initialisiert per defailt den DMA nach dem ADC. Nach dem ich 
dies geändert habe funktionierts (!).

Nächste frage: wie mache ich diese Änderung (im nicht user editablen 
bereich) ohne das der cube dies überschreibt?

von Kevin M. (arduinolover)


Lesenswert?

MAX M. schrieb:
> Nächste frage: wie mache ich diese Änderung (im nicht user editablen
> bereich) ohne das der cube dies überschreibt?

Project Manager -> Advanced Settings -> Abschnitt "Generated Function 
Calls"

Da gibt es einen "Rank"

von MAX M. (maxmax123)


Lesenswert?

Kevin M. schrieb:
> Project Manager -> Advanced Settings -> Abschnitt "Generated Function
> Calls"
>
> Da gibt es einen "Rank"

Super danke.
Funktioniert nun, selbst wenn ich ADC interrupt deaktiviere. :-)
Anyway der DMA interrupt lässt sich im cube nicht deaktivieren. Bei 
deaktivierem ADC3 interrupt und nicht deaktivierem DMA interrupt wird 
HAL_ADC_ConvCpltCallback aufgerufen (kommt vom DMA)?

Wie kann ich sämtliche interrupts bez. ADC3 deaktivieren. (An ADC3 
hängen nur unspezielle signale, die jeweils einmal im while(1) loop 
geprüft werden (spannungsversorgunen, temps etc.). Also es genügt wenn 
der STM städig im mein array DMAt und ich dieses einmal pro programloop 
auslese/prüfe.

von Hinweis (Gast)


Lesenswert?

Die cube hat da irgendwo noch einen fetten Bug. Ich kann nicht mehr 
genau sagen, welchen ADC/DMA-Parameter es betrifft, aber dieser geht 
beim Code generieren verloren oder wird von der Cube übersprungen. Prüfe 
doch mal, ob auch alle deine relevanten Cube-Parameter es in den 
Initialisierungscode geschafft haben.

von Chris R. (rodi)


Lesenswert?

Hallo zusammen,
ich stehe vor dem gleichen Problem wie der TO.
Ich möchte den ADC3 auf einem Nucelo H743ZI2 verwenden.
Bei mir wurde der Code korrekt generiert, zuerst wird der BDMA, dann der 
ADC3 initialisiert.

Mein Werte Array enthält mit jedem Startup die gleichen Werte, ich 
vermute, dass der BDMA auf das Array nicht zugreifen kann.

Habe es wie folgt definiert:
ALIGN_32BYTES (uint16_t adc3_buf[ADC3_BUF_LEN]) 
__attribute__((section(".ARM.__at_0x38000000")));

In den Memory Regions wird für RAM_D3 used 0% angegeben.
Fragen:
- wie finde ich heraus, wo das Array gespeichert worden ist?
- wie schaffe ich es, dass der Linker das Array in den RAM_D3 legt?

von J.Pan (Gast)


Lesenswert?

Kevin M. schrieb:
> Wenn du sie initialisiert haben willst,
> pass das startup file entsprechend an (sofern nicht schon geschehen).

Hallo,

ich würde gerne wissen, wie man die in startup file initialisieren kann.
Vielen Dank
Beste Grüße
J.Pan

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.