我无法使用 ADC 正确配置我的 STM32F411RE DMA

I cannot configure my STM32F411RE DMA with ADC correctly

我正在尝试使用带 ADC 的 STM32F411RE DMA 为 3 个输入通道/引脚获取 2048 个样本。 A0、A1 和 A2 引脚连接到要使用图像处理(傅里叶变换)的传感器。

因此,我使用 DMA / ADC 将来自 3 个引脚的所有 2048 个样本填充 adc_buffer[6144],使用中断来查找此缓冲区何时已满。当缓冲区已满时,数据应转换为电压并放入正确的容器中(数据为 x、y、z、x、y、z)。

尽管由于某种原因我的所有这些都在工作,但我的 DMA 循环缓冲区没有填满数据。在以前的程序中,它运行良好,但我现在不能再使它工作了:adc_buffer 在整个程序 运行 中保持为 0,因此 ISR HAL_ADC_ConvCpltCallback 永远不会被触发。


#include "main.h"
#include "arm_const_structs.h"
#include "core_cm4.h"
#include "math.h"
#include "arm_math.h"
#include <stdio.h>
#include <stdbool.h>
#include "main.h"

ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
UART_HandleTypeDef huart2;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC1_Init(void);
static void MX_USART2_UART_Init(void);

float adc_buffer[6144] = {0}; // 2048 samples * 3 channels

/*
 * Interrupt called when the input buffer adc_buffer is full.
 * TO DO: Add   void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)  for double buffering, when conversion is half complete also run Fourier transform
 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC1)  //  if(hadc->Instance == ADC1 && !conversionPaused)
    {
        conversionPaused = 1;   // Temporarily disable the conversion process
        for (int samples= 0; samples < fftLen; samples++)   // Allocates data from the 1 ADC buffer into the 3 separate axis buffers
        {
            Xin[samples] = ((3.3-0)/4096) * adc_buffer[0+3*samples];    // Allocate samples to X array, whilst transforming them into voltage: every 3rd value starting at 0 (0, 3, 6, 9)
            Yin[samples] = ((3.3-0)/4096) * adc_buffer[1+3*samples];    // Allocate samples to X array, whilst transforming them into voltage: every 3rd value starting at 1 (1, 4, 7, 10)
            Zin[samples] = ((3.3-0)/4096) * adc_buffer[2+3*samples];    // Allocate samples to X array, whilst transforming them into voltage: every 3rd value starting at 2 (2, 5, 8, 11)
        }
    }
}

int main(void)
{

    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_ADC1_Init();
    MX_USART2_UART_Init();

    HAL_ADC_Start_IT(&hadc1);
    HAL_ADC_Start_DMA(&hadc1, adc_buffer, bufferLen);   // Maybe DMA and ADC IT are mutually exclusive?

    while (1)
    {
        // Signal processing on Xin, Yin, Zin
    }


/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 16;
  RCC_OscInitStruct.PLL.PLLN = 393;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV8;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV8;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief ADC1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_ADC1_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};

  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = ENABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;           // Maybe this needs to be set to off for use with the circular buffer?
  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 = 3;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.EOCSelection = DISABLE; //ADC_EOC_SINGLE_CONV
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_56CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = 2;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = 3;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
}

/** 
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void) 
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA2_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

}

我怀疑问题出在ADC和DMA的配置上,相关文献很少。有许多示例 1 2 3 4 可用,但这几乎没有补救措施,因为它们的代码看起来与我的相同。



作为对 Tarick Welling 回复的编辑,我在 CubeMX 中生成代码时似乎没有得到完整的包。特别是我错过了完整的 MX_DMA_Init() 因为我只生成以下内容:

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

  /* DMA interrupt init */
  /* DMA2_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}

然而,尽管我在 CubeMX 中的设置被配置为应该生成,如下图所示:

我完全不明白为什么在 CubeMX 中没有为我生成这段代码,而对于其他人显然是。

MX_DMA_Init 没有初始化 hdma_adc1。所以DMA无法使用

hadc1.Init.DMAContinuousRequests = DISABLE; 禁用 DMA 的连续使用,因此它将是单次传输。

uint32_t DMAContinuousRequests; Specify whether the DMA requests are performed in one shot mode (DMA transfer stops when number of conversions is reached) or in continuous mode (DMA transfer unlimited, whatever number of conversions). This parameter can be set to ENABLE or DISABLE. Note: In continuous mode, DMA must be configured in circular mode. Otherwise an overrun will be triggered when DMA buffer maximum pointer is reached.

ADC 未初始化为使用 DMA,因此它甚至不知道要使用它。

typedef struct
{
  ADC_TypeDef                   *Instance;              /*!< Register base address */

  ADC_InitTypeDef               Init;                   /*!< ADC required parameters */

  DMA_HandleTypeDef             *DMA_Handle;            /*!< Pointer DMA Handler */

  HAL_LockTypeDef               Lock;                   /*!< ADC locking object */

  __IO uint32_t                 State;                  /*!< ADC communication state (bitmap of ADC states) */

  __IO uint32_t                 ErrorCode;              /*!< ADC Error code */
}ADC_HandleTypeDef;

DMA_Handle ADC 需要初始化才能使用 DMA。

这可以在 CubeMX 中设置,方法是在 Configuration 选项卡上的 ADC 配置的 DMA Settings 添加一行: 可以通过选择此 DMA 传输并在对话框底部配置其参数来设置更多设置。

请注意,ST 使用委托初始化的形式。 HAL_ADC_Init 调用用户定义的 HAL_ADC_MspInit。这是在 stm32f4xx_hal_msp.c 内部定义的,应该 link ADC 到 DMA。它应该包含类似于以下的行:__HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);