Forum: Mikrocontroller und Digitale Elektronik STM32 Cube ADC DMA


von Peter K. (peterka2000)


Lesenswert?

Guten Tag,

jetzt ist es wieder soweit: Ich will/muss mich mit 
STM32F4-Cube-HAL-Libraries rumschlagen.

Was ich gerne hätte: Ein ADC, der mir auf Anfrage drei Kanäle per DMA in 
einen Puffer schiebt.

Ich hab mich (vorerst) mal auf reine Code-Generierung mit CubeMX 
beschränkt. Raus kam folgendes:
1
/* ADC1 init function */
2
static void MX_ADC1_Init(void)
3
{
4
5
  ADC_ChannelConfTypeDef sConfig;
6
7
    /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
8
    */
9
  hadc1.Instance = ADC1;
10
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
11
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
12
  hadc1.Init.ScanConvMode = ENABLE;
13
  hadc1.Init.ContinuousConvMode = DISABLE;
14
  hadc1.Init.DiscontinuousConvMode = DISABLE;
15
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
16
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
17
  hadc1.Init.NbrOfConversion = 3;
18
  hadc1.Init.DMAContinuousRequests = DISABLE;
19
  hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
20
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
21
  {
22
    Error_Handler();
23
  }
24
25
    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
26
    */
27
  sConfig.Channel = ADC_CHANNEL_7;
28
  sConfig.Rank = 1;
29
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
30
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
31
  {
32
    Error_Handler();
33
  }
34
35
    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
36
    */
37
  sConfig.Channel = ADC_CHANNEL_10;
38
  sConfig.Rank = 2;
39
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
40
  {
41
    Error_Handler();
42
  }
43
44
    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
45
    */
46
  sConfig.Channel = ADC_CHANNEL_9;
47
  sConfig.Rank = 3;
48
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
49
  {
50
    Error_Handler();
51
  }
52
53
}
54
55
...
56
57
hdma_adc1.Instance = DMA2_Stream0;
58
    hdma_adc1.Init.Channel = DMA_CHANNEL_0;
59
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
60
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
61
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
62
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
63
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
64
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
65
    hdma_adc1.Init.Priority = DMA_PRIORITY_MEDIUM;
66
    hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
67
    if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
68
    {
69
      Error_Handler();
70
    }
71
72
    __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);
73
74
...
75
76
/** 
77
  * Enable DMA controller clock
78
  */
79
static void MX_DMA_Init(void) 
80
{
81
  /* DMA controller clock enable */
82
  __HAL_RCC_DMA1_CLK_ENABLE();
83
  __HAL_RCC_DMA2_CLK_ENABLE();
84
85
  /* DMA interrupt init */
86
  /* DMA1_Stream5_IRQn interrupt configuration */
87
  HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0);
88
  HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
89
  /* DMA2_Stream0_IRQn interrupt configuration */
90
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
91
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
92
93
}

Dazu kommen noch ein paar andere Funktionen, die möchte ich jetzt aber 
(sofern nicht nötig) ungerne zusammensammeln.

Starten tue ich das ganze mit:
1
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adc1Values[0], 3);

adc1Values ist ein uint16_t-Array (fest deklariert auf drei Werte).

Interessanterweise geht das ganze auch. Einmal. Nach dem Aufruf macht 
der eine Messung, und ruft am Ende die entsprechende Callback-Routine 
auf ( void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) ). Dabei 
kommen auch plausible Werte raus. Wenn ich die Wandlung aber noch mal 
starte, passiert scheinbar nichts. Die Callback-Routine wird nicht 
aufgerufen, und auch die Werte erhalten kein Update.

Wenn ich aber in der Callback-Routine den ADC stoppe (HAL_ADC_Stop), 
dann kann ich eine erneute Messung starten. Allerdings denke ich, dass 
das nicht das Ziel der Sache ist. Außerdem dauert es dann recht lange 
(ca. 60µs von Aufruf der HAL_ADC_Start_DMA bis zum Callback, für drei 
Kanäle), denke ich zumindest. (Eine einzelne Messung mit Polling, dazu 
noch ein wenig Gleitkommaberechnung schafft der in 6µs).

Hat jemand schon mal soetwas gesehen und hat spontan eine Idee? An 
welchen Punkten sollte ich jetzt nocheinmal nachbohren?

Denke nicht, dass es relevant ist, aber trotzdem: Prozessor ist ein 
STM32F446RE, Sysclk 180Mhz

Grüße und Vielen Dank im Voraus

von Vincent H. (vinci)


Lesenswert?

1
hadc1.Init.DMAContinuousRequests = DISABLE;

Klingt für mich nach "einmal und nie wieder".

von Peter K. (peterka2000)


Lesenswert?

Hab ich gerade ausprobiert, die Symptomatik bleibt dieselbe. In meinem 
Gedankengang möchte ich ja auch eigentlich gar keine Continuous Request. 
Die sorgt ja - so wie ich es verstanden hab - dafür, dass der ADC immer 
weiter hintereinander sampelt. Ich möchte aber eigentlich nur, dass der 
durch einen Softwaretrigger sampelt.

Ich bin gerade mal die HAL_ADC_Start_DMA Routine im Debugger 
schrittweise durchgegangen, aber ich konnte keinen offensichtlichen 
unterschied zwischen dem Ersten und allen anderen Versuchen erkennen. 
Ich schaue mir gleich noch mal die ADC-Handle an, ob da vielleicht 
irgendein Status klemmt.

von Peter K. (peterka2000)


Angehängte Dateien:

Lesenswert?

Oh, sorry, ich habe mich oben verlesen. Ich meinte zu lesen, dass da 
ContinousConversionMode stand. Das mit dem DMA Continous war aber 
richtig, jetzt geht es auch. Danke Vincent.

Weiter gehts aber: Triple ADC, jeder ADC mit drei Kanälen. Dazu 
natürlich DMA. Die Init-Funktionen hab ich jetzt im Anhang, die werden 
doch recht lang.

Starten möchte ich das ganze mit
1
HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t*)&adc1Values[0], 9);
Allerdings werden keine Daten in den Puffer geschrieben, auch die 
Conversion Complete Funktion wird nicht aufgerufen. Ich denke, dass ich 
vorher noch irgendwas starten muss. Sieht ein Experte was auf den ersten 
Blick?

von Vincent H. (vinci)


Lesenswert?

Schau mal ob die MultiModeStart_DMA Funktion die 3x ADCs überhaupt 
enabled. Jeder ADC sollte ein eigenes Enable und Start Bit besitzen.

So ins Blaue getippt würde ich raten, dass die Funktion ADC1 vielleicht 
enabled und startet, ADC2 und 3 aber vielleicht unangetastet lässt?

von Peter K. (peterka2000)


Lesenswert?

Vincent H. schrieb:
> Schau mal ob die MultiModeStart_DMA Funktion die 3x ADCs überhaupt
> enabled. Jeder ADC sollte ein eigenes Enable und Start Bit besitzen.
>
> So ins Blaue getippt würde ich raten, dass die Funktion ADC1 vielleicht
> enabled und startet, ADC2 und 3 aber vielleicht unangetastet lässt?

Vielen Dank, das war es!

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.