Hallo,
ich verwende einen STM32F410RB-Controller und die CubeMx-Libraries.
Unter anderem muss die Software in diesem Projekt 11 ADC-Kanäle
kontinuierlich messen.
Die primäre Funktion ist jedoch eine andere, für die ich relativ kurze
Zykluszeiten haben muss.
Da der ADC das System unnötig aufhalten würde (und weils aus meiner
Sicht sowieso die schönere Lösung als "Polling" ist) wird der DMA
verwendet um die ADC-Daten in ein Array zu laden.
Nun stelle ich aber teilweise sehr komische Verhaltensweisen fest.
Einzelne Messungen zeigen extreme Ausreißen (sowohl nach oben als auch
nach unten um bis zu 50%) obwohl diese eigentlich nicht vorhanden sein
dürften (ich habs mit dem Oszilloskop nachgeprüft).
Kleinere Schwankungen kann ich mir ja noch vorstellen (Rauschen oder
so), aber hier muss es fast ein Fehler in meiner Konfiguration bzw.
meinem Programmablauf geben, den ich leider nicht finde.
Eines vielleicht vorweg: Ein Überlauf bzw. ein Rundungsfehler beim
Umrechnen vom ADC-Wert kann ich bereits ausschließen.
Meine ADC-Konfig sieht so aus:
1 | handle_adc.Instance = ADC1;
|
2 | handle_adc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
|
3 | handle_adc.Init.Resolution = ADC_RESOLUTION_12B;
|
4 | handle_adc.Init.ScanConvMode = ENABLE;
|
5 | handle_adc.Init.ContinuousConvMode = ENABLE;
|
6 | handle_adc.Init.DiscontinuousConvMode = DISABLE;
|
7 | handle_adc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
|
8 | handle_adc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
|
9 | handle_adc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
|
10 | handle_adc.Init.NbrOfConversion = 11;
|
11 | handle_adc.Init.DMAContinuousRequests = ENABLE;
|
12 | handle_adc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
|
13 | if (HAL_ADC_Init(&handle_adc) != HAL_OK) {
|
14 | system_debugMessage("ADC - Init failed");
|
15 | system_errorHandler();
|
16 | }
|
Dann Konfigurier ich mir noch den Sequencer:
1 | sConfig.Channel = P12V_MON_AdcChannel;
|
2 | sConfig.Rank = 1;
|
3 | sConfig.SamplingTime = ADC_SAMPLE_TIME;
|
4 | if (HAL_ADC_ConfigChannel(&handle_adc, &sConfig) != HAL_OK) {
|
5 | system_debugMessage("ADC - Init failed (P12V_MON)");
|
6 | system_errorHandler();
|
7 | }
|
Für die anderen 10 Kanäle sieht das dann ähnlich aus.
Dann muss noch der DMA-Konfiguriert werden:
1 | handle_dma.Instance = DMA2_Stream0;
|
2 | handle_dma.Init.Channel = DMA_CHANNEL_0;
|
3 | handle_dma.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
4 | handle_dma.Init.PeriphInc = DMA_PINC_DISABLE;
|
5 | handle_dma.Init.MemInc = DMA_MINC_ENABLE;
|
6 | handle_dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
|
7 | handle_dma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
|
8 | handle_dma.Init.Mode = DMA_CIRCULAR;
|
9 | handle_dma.Init.Priority = DMA_PRIORITY_HIGH;
|
10 | handle_dma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
|
11 | if (HAL_DMA_Init(&handle_dma) != HAL_OK) {
|
12 | system_debugMessage("ADC - Init failed (DMA)");
|
13 | system_errorHandler();
|
14 | }
|
15 |
|
16 | __HAL_LINKDMA(&handle_adc, DMA_Handle, handle_dma);
|
Schlussendlich starte ich dann noch die Messung mit
1 | HAL_ADC_Start_DMA(&handle_adc, adcResults, 11);
|
und sollte dann zyklisch die Werte in meinen "adcResults"-Array
reinkriegen.
Im normal Programmzyklus wird dann geprüft ob die Werte aktualisiert
wurden und werden dann entsprechend umgerechnet (Der normale
Programmzyklus ist mit ca. 60us Durchlaufzeit wesentlich schneller als
der ADC, der ca. 2ms für alle Messungen braucht).
1 | if(adcResults[10] != 0) {
|
2 | //Factor 1:16 (Volt)
|
3 | states->chan1_mV = (adcResults[0] * 16 * 3300) / 4096;
|
4 | states->chan2_mV = (adcResults[1] * 16 * 3300) / 4096;
|
5 |
|
6 | //...
|
7 |
|
8 | adcResults[10] = 0;
|
9 | }
|
Hat jemand eine Idee was das sein könnte bzw. wie man den Code
verbessern könnte?
Danke im Voraus!