ADC DMA auf STM32, Nucleo F334R8 stoppt die Ausführung der Main While-Schleife

Ich lese ADC über DMA auf dem STM32 Nucleo F334R8. Nachdem ich jedoch den DMA mit dem folgenden Code gestartet habe, wird die Haupt-while-Schleife nicht ausgeführt

HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&ADC_Raw, 4);

Ich verstehe, dass die DMA-Interrupts möglicherweise so oft auftreten, dass andere Interrupts möglicherweise nicht auftreten können. Daher habe ich die Priorität für den DMA so geändert, dass sie so niedrig wie möglich ist:

HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 15);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

Ich weiß, dass andere Interrupts behandelt werden, da die folgenden Module in Betrieb sind:

  • UART, Senden und Empfangen von Nachrichten über einen COM-Port
  • TIMER, schaltet alle 500 ms eine LED um
  • PUSH BUTTON, schaltet eine LED basierend auf einem Druckknopfdruck ein.

Ich verstehe nicht, warum die Haupt-While-Schleife nicht ausgeführt wird, wenn der ADC-DMA aktiviert ist. Wenn ich den folgenden Code auskommentiere, wird die Haupt-While-Schleife ausgeführt

HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&ADC_Raw, 4);

Hier ist die ADC-Konfiguration:

void MX_ADC1_Init(void)
{
    ADC_MultiModeTypeDef multimode;
    ADC_ChannelConfTypeDef sConfig;

    hadc1.Instance = ADC1;
    hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
    hadc1.Init.Resolution = ADC_RESOLUTION_12B;
    hadc1.Init.ScanConvMode = ENABLE;
    hadc1.Init.ContinuousConvMode = ENABLE;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.NbrOfConversion = 4;
    hadc1.Init.DMAContinuousRequests = ENABLE;
    hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
    hadc1.Init.LowPowerAutoWait = DISABLE;
    hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;

    if(HAL_ADC_Init(&hadc1) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }

    if(HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }
    /**Configure the ADC multi-mode
    */
  multimode.Mode = ADC_MODE_INDEPENDENT;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure Regular Channel
     */
    sConfig.Channel = ADC_CHANNEL_1;
    sConfig.Rank = 1;
    sConfig.SingleDiff = ADC_SINGLE_ENDED;
    sConfig.SamplingTime = ADC_SAMPLETIME_19CYCLES_5;
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
    sConfig.Offset = 0;
    if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }

    /**Configure Regular Channel
     */
    sConfig.Channel = ADC_CHANNEL_2;
    sConfig.Rank = 2;
    sConfig.SingleDiff = ADC_SINGLE_ENDED;
    sConfig.SamplingTime = ADC_SAMPLETIME_19CYCLES_5;
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
    sConfig.Offset = 0;
    if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }

    /**Configure Regular Channel
     */
    sConfig.Channel = ADC_CHANNEL_6;
    sConfig.Rank = 3;
    sConfig.SingleDiff = ADC_SINGLE_ENDED;
    sConfig.SamplingTime = ADC_SAMPLETIME_19CYCLES_5;
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
    sConfig.Offset = 0;
    if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    } 

    /**Configure Regular Channel
     */
    sConfig.Channel = ADC_CHANNEL_7;
    sConfig.Rank = 4;
    sConfig.SingleDiff = ADC_SINGLE_ENDED;
    sConfig.SamplingTime = ADC_SAMPLETIME_19CYCLES_5;
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
    sConfig.Offset = 0;
    if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
       _Error_Handler(__FILE__, __LINE__);
    }
}

void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{

    GPIO_InitTypeDef GPIO_InitStruct;
    if(adcHandle->Instance == ADC1)
    {
        /* USER CODE BEGIN ADC1_MspInit 0 */

        /* USER CODE END ADC1_MspInit 0 */
        /* ADC1 clock enable */
        __HAL_RCC_ADC12_CLK_ENABLE()
        ;

        /**ADC1 GPIO Configuration
         PC0     ------> ADC1_IN6
         PC1     ------> ADC1_IN7
         PA0     ------> ADC1_IN1
         PA1     ------> ADC1_IN2
         */
        GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

        GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        /* ADC1 DMA Init */
        /* ADC1 Init */
        hdma_adc1.Instance = DMA1_Channel1;
        hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
        hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
        hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
        hdma_adc1.Init.Mode = DMA_CIRCULAR;
        hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
        if(HAL_DMA_Init(&hdma_adc1) != HAL_OK)
        {
            _Error_Handler(__FILE__, __LINE__);
        }

        __HAL_LINKDMA(adcHandle, DMA_Handle, hdma_adc1);

        /* ADC1 interrupt Init */
        HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 14);
        HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
        /* USER CODE BEGIN ADC1_MspInit 1 */

        /* USER CODE END ADC1_MspInit 1 */

        /* USER CODE BEGIN ADC1_MspInit 1 */

        /* USER CODE END ADC1_MspInit 1 */
    }
}

Hier ist die DMA-Konfiguration:

void MX_DMA_Init(void) 
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 15);
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

}

Hier ist das Wichtigste:

int main(void)
{

    /* USER CODE BEGIN 1 */

    /* USER CODE END 1 */

    /* MCU Configuration----------------------------------------------------------*/
    CPU_CACHE_Enable();
    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* USER CODE BEGIN Init */

    /* USER CODE END Init */

    /* Configure the system clock */
    SystemClock_Config();

    /* USER CODE BEGIN SysInit */

    /* USER CODE END SysInit */

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_ADC1_Init();
    MX_I2C1_Init();
    MX_SPI1_Init();
    MX_TIM1_Init();
    MX_TIM3_Init();
    MX_TIM6_Init();
    MX_USART2_UART_Init(); 

    HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&ADC_Raw, 4); 
    my_printf("Version 1.0\n");
    InitializeControllerCommand();


    while (1)
    {
        if(start_pwm == true)
        {
            fade_LED();
        }

        if(readInput == 1)
        {
            readInputPin();
        }
    }
}

Antworten (1)

Ich verstehe, dass die DMA-Interrupts möglicherweise so oft auftreten, dass andere Interrupts möglicherweise nicht auftreten, daher habe ich die Priorität für den DMA so geändert, dass sie so niedrig wie möglich ist

[...]

Ich verstehe nicht, warum die Haupt-While-Schleife nicht ausgeführt wird, wenn der ADC-DMA aktiviert ist.

Wenn DMA-Interrupts so oft auftreten würden, dass sie andere Interrupt-Handler am Laufen hindern würden, würden sie den Hauptcode nicht mitlaufen lassen.

Es ist möglich, das Hauptprogramm mit der CMSIS-Funktion so einzustellen, dass es mit einer höheren Priorität als bestimmte Interrupts ausgeführt wird, __set_PRIMASK()was in eine Anweisung übersetzt würde MSR PRIMASK, Rx, aber dies würde effektiv alle Interrupts mit niedrigerer Priorität deaktivieren, da das Hauptprogramm niemals beendet wird (es sei denn, es trifft zu ein nicht behebbarer Fehler).

Meiner Meinung nach solltest du

  • Überlegen Sie es sich und finden Sie eine Lösung, die keinen Interrupt nach jeder Konvertierungssequenz (und auf halbem Weg) benötigt,
  • Verwenden Sie HAL nicht für zeitkritischen Code.
  • oder die Messungen verlangsamen

Erhöhung der Abtastzeit

Die Abtastzeit für jeden Kanal kann in den Registern ADC->SMPR1und ADC->SMPR2oder durch Einstellen des SamplingTimeFelds in der Kanalinitialisierungsstruktur von 1,5 bis 601,5 ADC-Taktzyklen eingestellt werden.

Reduzierung der ADC-Taktfrequenz

CKMODEDer ADC-Takt kann aus dem AHB-Takt getaktet werden, optional dividiert durch 2 oder 4. Dies wird durch die Felder des ADC->CCRRegisters oder durch das Init.ClockPrescalerFeld der ADC-Initialisierungsstruktur gesteuert .

Alternativ kann der ADC von der Haupt-PLL getaktet werden, optional geteilt durch 2,4,6,8,10,12,16,32,64,128 oder 256. Die CKMODEBits oder Init.ClockPrescalermüssen 0 ( ADC_CLOCK_ASYNC_DIV1) sein, und der Divisor kann ausgewählt werden in RCC->CFGR2, oder eingestellt durchHAL_RCCEx_PeriphCLKConfig()

Die oben genannten Methoden beruhen auf der Erhöhung der Zeit, die für jede Konvertierung in der Sequenz benötigt wird, dies ist möglicherweise nicht geeignet für einige Anwendungen, bei denen die Kanäle in einem kurzen Zeitintervall abgetastet werden sollten, um mehr oder weniger synchrone Ergebnisse zu erhalten.

Erhöhen des Intervalls zwischen Konvertierungssequenzen

Die ADC-Konvertierungssequenz kann auch durch ein Timer-Ereignis gestartet werden. In diesem Fall sollte der kontinuierliche Konvertierungsmodus deaktiviert werden, und eine Ereignisquelle muss in den Bits EXTENund EXTSELdes ADC->CFGRRegisters ausgewählt werden. Mögliche Ereignisquellen sind in Kapitel 13.3.18 Konvertierung bei externem Trigger und Triggerpolarität im Referenzhandbuch aufgeführt . Timer bieten eine große Flexibilität bei der Auswahl einer geeigneten Abtastfrequenz, aber sie sind eine begrenzte Ressource. Wenn eine Abtastfrequenz von 1 kHz oder weniger ausreichen würde, wäre es am einfachsten, die Konvertierung jedes Mal einfach vom periodischen Interrupt-Handler aus zu starten, falls Sie einen haben.

Ist es möglich, dass dieses Problem mit der Taktrate zusammenhängt? Ich verwende den gleichen Code auf dem Nucleo-F722ZE, der mit 200 MHz läuft, die Hauptschleife läuft ordnungsgemäß.
Ja. Der ADC des F3 kann mit 72 MHz laufen, genau wie der Prozessorkern, während er beim F7 höchstens mit 36 ​​MHz läuft.
Ich habe den Clock Prescaler für den ADC geändert, um ihn zu verlangsamen, und jetzt wird die Hauptschleife ausgeführt. Wenn Sie Ihre Antwort mit Anmerkungen zur Taktgeschwindigkeit bearbeiten, akzeptiere ich sie als die richtige Antwort.
@notransients Ich habe die Antwort erweitert
bestätigte, dass dies ein Problem für das STM32F746G-DISCO-Board war. Wenn ich den ADC verlangsame, indem ich APB2 auf 25 MHz (/8 bei 200 MHz) setze, reagiert das Board wieder