STM32H7A3 ADC+DMA使用问题

本文介绍STM32中ADC模块配合DMA进行数据采集的具体配置过程,并探讨了两个常见问题:一是为何存储ADC数据的数组大小必须为采集次数的两倍;二是全局变量申请对ADC采集的影响及解决方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题1:DMA采用半字传输16位ADC值,用于存储ADC数据的数组一定是采集数的两倍,否则会产生ADC溢出的错误中断HAL_ADC_ErrorCallback,从而无法进入ADC采集完成中断HAL_ADC_ConvCpltCallback。具体为什么是两倍,网上有很多解释,有兴趣的可以去查一下。

问题2:在使用ADC+DMA采集的时候突然发现一个奇怪的现象,就是只要在程序的任何一个地方再申请一个全局变量,ADC就会出现错误中断HAL_ADC_ErrorCallback。最后发现程序没有什么问题,就怀疑和初始化顺序相关,然后就使用STM32CUBEMX配置了一下ADC+DMA,看一下配置出来的初始化顺序,然后改成和CUBEMX配置顺序一样的,这个问题就解决了。


void ADC_Init(void)
{  
    ADC_ChannelConfTypeDef sConfig = {0};
	
	AdcHandle.Instance = ADC1;
    AdcHandle.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2; 
    AdcHandle.Init.Resolution = ADC_RESOLUTION_16B; 
    AdcHandle.Init.ScanConvMode = ADC_SCAN_ENABLE; 
    AdcHandle.Init.EOCSelection = ADC_EOC_SINGLE_CONV; 
    AdcHandle.Init.LowPowerAutoWait = DISABLE; 
    AdcHandle.Init.ContinuousConvMode = ENABLE; 
    AdcHandle.Init.NbrOfConversion = 3; 
    AdcHandle.Init.DiscontinuousConvMode = DISABLE; 
    AdcHandle.Init.NbrOfDiscConversion = 1; 
    AdcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START; 
    AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; 
    AdcHandle.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR; 
    AdcHandle.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN; 
    AdcHandle.Init.OversamplingMode = DISABLE; /* 禁止过采样 */
    /* 初始化 ADC */
    if (HAL_ADC_Init(&AdcHandle) != HAL_OK)
    {
    }
    /* 校准 ADC,采用偏移校准 */
    if (HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK)
    {
    }
	
	
	/*黄灯对应采集通道*/
    sConfig.Channel = ADC_CHANNEL_3; /* 配置使用的 ADC 通道 */
    sConfig.Rank = ADC_REGULAR_RANK_1; /* 采样序列里的第 1 个 */
    sConfig.SamplingTime = ADC_SAMPLETIME_387CYCLES_5; /* 采样周期 */
    sConfig.SingleDiff = ADC_SINGLE_ENDED; /* 单端输入 */
    sConfig.OffsetNumber = ADC_OFFSET_NONE; /* 无偏移 */
    sConfig.Offset = 0; /* 无偏移的情况下,此参数忽略 */
    sConfig.OffsetRightShift = DISABLE; /* 禁止右移 */
    sConfig.OffsetSignedSaturation = DISABLE; /* 禁止有符号饱和 */
	HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);
	

	sConfig.Channel = ADC_CHANNEL_7; /* 配置使用的 ADC 通道 */
    sConfig.Rank = ADC_REGULAR_RANK_2; /* 采样序列里的第 1 个 */
    HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);
	

	sConfig.Channel = ADC_CHANNEL_4; /* 配置使用的 ADC 通道 */
    sConfig.Rank = ADC_REGULAR_RANK_3; /* 采样序列里的第 1 个 */
    HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);
	

	sConfig.Channel = ADC_CHANNEL_9; /* 配置使用的 ADC 通道 */
    sConfig.Rank = ADC_REGULAR_RANK_4; /* 采样序列里的第 1 个 */
    HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);
	
	
    /* 启动 ADC 的 DMA 方式传输 */
    if (HAL_ADC_Start_DMA(&AdcHandle, (uint32_t*)adc_buf, 3) != HAL_OK)
    {
    }

}

void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(adcHandle->Instance==ADC1)
  {
    /* ADC1 clock enable */
    __HAL_RCC_ADC12_CLK_ENABLE();
    __HAL_RCC_DMA1_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_4|GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
	
	
	ADC1DMA_Handler.Instance = DMA1_Stream1; 
    ADC1DMA_Handler.Init.Request = DMA_REQUEST_ADC1; 
    ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY; 
    ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE;
    ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; 
    ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; 
    ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; 
    ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; 
    ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; 
    ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; 
    ADC1DMA_Handler.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; 
    ADC1DMA_Handler.Init.MemBurst = DMA_MBURST_SINGLE; 
    ADC1DMA_Handler.Init.PeriphBurst = DMA_PBURST_SINGLE;
    if(HAL_DMA_Init(&ADC1DMA_Handler) != HAL_OK)
    {
    }

	__HAL_LINKDMA(&AdcHandle, DMA_Handle, ADC1DMA_Handler);
	/* DMA interrupt init */
    /* DMA1_Stream0_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
  }
}

void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{

  if(adcHandle->Instance==ADC1)
  {
    /* Peripheral clock disable */
    __HAL_RCC_ADC12_CLK_DISABLE();
    __HAL_RCC_DMA1_CLK_DISABLE();

    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0|GPIO_PIN_4|GPIO_PIN_5);
  }
}


void DMA1_Stream1_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&ADC1DMA_Handler);
}


void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)
{
  
}

<think>嗯,用户需要查找关于STM32ADC HAL库的使用方法和资料。首先,我得回想一下自己之前接触过的STM32相关文档和资源。HAL库是ST官方推出的硬件抽象层库,用来简化开发流程,特别是对于不同型号的STM32芯片,HAL库提供了统一的接口。ADC模块在STM32中常用于模拟信号的采集,比如温度传感器、电压检测等。 用户提到了几个引用,其中引用[1]提到了A3 STM32_HAL库函数中的ADC通用驱动器,但看起来这个文档可能是一个用户自己编写的资料,可能存在不完整或者适配性问题。引用[2]提到原始工程适配的是STM32L152,用户的项目用的是STM32L151,可能需要调整。引用[3]和[4]是关于STM32H7和F4系列的ADC配置,涉及单通道采集和中断触发,这可能对用户有帮助,但用户需要的是通用的HAL库教程。 首先,我应该建议用户查阅ST官方的资料,比如HAL库的用户手册,特别是UM1850,这个文档应该详细介绍了ADC模块的使用方法。另外,STM32CubeMX工具能自动生成初始化代码,这对用户配置ADC参数非常有用。用户可能需要配置时钟、ADC通道、采样时间、触发方式等,STM32CubeMX能直观地完成这些配置,并生成对应的HAL库代码。 然后,代码示例部分,用户可能需要单通道轮询、多通道扫描、中断模式或DMA传输的例子。比如单通道轮询模式,使用HAL_ADC_Start和HAL_ADC_PollForConversion函数来获取数据。多通道的话需要配置扫描模式和连续转换,可能还需要DMA来高效传输数据。中断模式则要启用ADC全局中断,并在回调函数中处理数据,如引用[4]中的示例。 常见问题部分,用户可能会遇到ADC精度问题,比如参考电压不稳定或电源噪声,这时候需要检查硬件设计,比如添加滤波电容。校准ADC也是重要的一步,HAL_ADCEx_Calibration_Start函数可以帮助校准。时钟配置错误也是一个常见问题ADC的时钟不能超过数据手册中的最大值,通常需要分频处理。 另外,用户可能需要参考具体的应用笔记,比如AN5354,它详细讨论了ADC的配置和优化技巧,这对深入理解ADC的工作原理和解决实际问题很有帮助。社区资源如ST社区论坛、GitHub上的开源项目也能提供实际案例和问题解答。 总结下来,用户需要的是从官方文档到工具使用,再到代码示例和常见问题解决的一站式指南。确保覆盖配置步骤、不同模式的使用方法以及调试技巧,这样用户就能全面掌握ADC HAL库的应用了。</think>### STM32 ADC HAL库使用指南 #### 一、核心资源推荐 1. **官方文档** - **HAL库用户手册**(UM1850):包含ADC模块的初始化、配置函数及中断处理流程[^1] - **数据手册**:查看芯片ADC特性(如分辨率、采样率) - **应用笔记**(如AN5354):提供ADC噪声抑制、校准等高级技巧[^3] 2. **STM32CubeMX工具** - 图形化配置ADC参数(通道、采样时间、触发方式),自动生成初始化代码[^3] - 支持**单次/连续转换**、**扫描模式**、**DMA传输**等配置 #### 二、ADC配置步骤(以单通道为例) 1. **CubeMX配置** ```c // ADC1配置示例(12位分辨率,单次转换) hadc1.Instance = ADC1; hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; // ADC时钟分频 hadc1.Init.Resolution = ADC_RESOLUTION_12B; // 12位精度 hadc1.Init.ScanConvMode = DISABLE; // 关闭扫描模式 hadc1.Init.ContinuousConvMode = DISABLE; // 单次转换模式 HAL_ADC_Init(&hadc1); ``` 2. **通道校准** ```c HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED); // 执行校准 ``` 3. **触发转换与数据读取** ```c HAL_ADC_Start(&hadc1); // 启动转换 if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK) { uint16_t adc_value = HAL_ADC_GetValue(&hadc1); // 获取结果 } ``` #### 三、进阶模式示例 1. **中断模式**(参考引用[4]) ```c // 启动中断转换 HAL_ADC_Start_IT(&hadc1); // 中断回调函数 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { printf("ADC Value: %d\r\n", HAL_ADC_GetValue(hadc)); } ``` 2. **DMA多通道采集** ```c // CubeMX中启用DMA,设置循环模式 uint16_t adc_buffer[4]; // 存储4通道数据 HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, 4); ``` #### 四、常见问题解决 1. **ADC精度问题** - 检查参考电压稳定性(建议使用外部参考) - 添加RC滤波电路(如100nF电容并联10μF电容)[^3] 2. **时钟配置错误** - 确保ADC时钟不超过数据手册限制(例如STM32H7 ADC时钟≤50MHz) 3. **通道间串扰** - 在切换通道后增加延迟(例如调用`HAL_Delay(1)`)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值