【问题标题】:I cannot configure my STM32F411RE DMA with ADC correctly我无法使用 ADC 正确配置我的 STM32F411RE DMA
【发布时间】:2019-11-12 00:00:49
【问题描述】:

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

因此,我使用 DMA / ADC 用来自 3 个引脚的所有 2048 个样本填充adc_buffer[6144],使用中断来查找此缓冲区何时已满。当缓冲区已满时,应将数据转换为电压并放入正确的 bin(数据进入 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 中没有为我生成这段代码,而对其他人来说显然是。

【问题讨论】:

    标签: c stm32 dma adc


    【解决方案1】:

    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)continuous mode (DMA transfer unlimited, whatever number of conversions). 此参数可以设置为 ENABLE 或 DISABLE。 注意:在连续模式下,DMA 必须配置为循环模式。否则当达到 DMA 缓冲区最大指针时将触发溢出。

    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。

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

    请注意,ST 使用了一种委托初始化的形式。 HAL_ADC_Init 调用用户定义的HAL_ADC_MspInit。这是在stm32f4xx_hal_msp.c 中定义的,应该将 ADC 链接到 DMA。它应该包含类似于:__HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);

    的行

    【讨论】:

    • 嘿伙计,感谢您提供的信息丰富的回复!我修改了我的主要帖子以反映更新:由于某些奇怪的原因,我似乎没有在使用 CubeMX 时生成MX_DMA_Init() 函数。 In this following imgur link I have depicted my settings and result,即使对于新生成的项目,它也会产生相同的输出。
    • 我找到了 stm32f4xx_hal_msp.c,我是否只是在 HAL ADC init 中引用这个函数,同时引用 ADC 实例,ergo HAL_ADC_MspInit(&amp;hadc1) 来满足这个?
    • HAL_ADC_MspInitHAL_ADC_Init 内部被调用。如果您查看HAL_ADC_Init 的定义,它应该包含以下行:HAL_ADC_MspInit(hadc);。请使用调试器来验证它确实被调用了,而不是在定义之前带有__weak 的虚拟HAL_ADC_MspInit
    • 我生成的 HAL_ADC_Init 不包含 HAL_ADC_MspInit(hadc);函数调用,所以我手动添加了这个。我使用实例 &hadc1 代替这个函数,并且我的中断开始工作:) 在我将adc_buffer[] 修改为兼容类型(uint32_t 而不是float)之后,我的缓冲区现在也充满了数据正确,非常感谢您的帮助!
    • 不幸的是,我仍然可以为您提供一点帮助,因为 adc_buffer 填充得很好,但是这些数据没有写入我的特定 Xin[]、Yin[] 和 Zin[] 数据数组(它们保持为 0)。这有点奇怪,因为 ISR 的唯一功能是将这些数据写入这些数据数组 - 也许 ISR 没有正确运行?我知道通过调试正确调用了 ISR
    猜你喜欢
    • 2021-10-13
    • 1970-01-01
    • 2021-12-19
    • 1970-01-01
    • 2020-11-19
    • 2019-12-22
    • 2020-11-08
    • 2020-07-08
    • 2020-10-17
    相关资源
    最近更新 更多