STM32使用DMA时ADC转换始终无法完成问题求助
STM32F429 ADC_FLAG_EOC始终未置位问题分析与解决
核心问题定位
你的代码存在两个关键问题,其中ADC时钟超限是导致转换完全无法进行的根本原因,其次是对ADC_FLAG_EOC触发逻辑的理解偏差。
1. ADC时钟配置错误(致命问题)
STM32F4系列的ADC模块时钟上限为36MHz,而你的配置中:
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
STM32F429的APB2总线时钟默认是90MHz(当AHB为180MHz时),经过2分频后ADC时钟为45MHz,远超36MHz的最大允许值,直接导致ADC硬件无法正常工作,自然不会产生转换完成标志。
修正方案:
将预分频改为ADC_Prescaler_Div4或更高,确保ADC时钟≤36MHz:
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; // 90MHz /4 =22.5MHz,符合要求
2. ADC_FLAG_EOC触发逻辑误解
在你启用了扫描模式(ADC_ScanConvMode = ENABLE)且未配置ADC_EOCSelection的情况下,STM32F4的ADC默认仅在整个序列转换完成后才置位ADC_FLAG_EOC,而非每个通道转换完成都置位。
你的TIM中断代码中两次等待ADC_FLAG_EOC并尝试读取ADC_GetValue(0)、ADC_GetValue(1)是错误的:
ADC_GetValue函数不需要参数,它直接返回ADC_DR寄存器的值- 读取DR寄存器会自动清除
ADC_FLAG_EOC标志,第一次读取后标志被清除,第二次等待会直接卡死
修正方案:
如果需要每个通道转换完成都触发EOC,添加如下配置:
ADC_InitStructure.ADC_EOCSelection = ADC_EOCSelection_Single;
如果使用扫描模式+DMA,无需手动轮询EOC,DMA会自动搬运每个通道的转换结果;若关闭DMA,单次序列转换完成后读取一次DR即可获取最后一个通道的结果(或通过DMA方式获取所有通道数据)。
3. 其他辅助排查点
- 采样时间过短:
ADC_SampleTime_3Cycles是最短采样时间,对于部分模拟输入可能不足,建议先改用ADC_SampleTime_15Cycles测试 - GPIO配置确认:确保PA7(通道7)、PC3(通道13)确实配置为模拟输入,无其他外设占用引脚
- 连续转换模式逻辑:启用连续转换后,ADC会自动循环转换,无需在TIM中断中重复启动,若需要定时采样,应改用单次转换模式,每次中断启动一次转换
简化测试代码
以下是修正后的单通道测试代码,可快速验证ADC是否正常工作:
void GPIO_ADC_Config() { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); } void ADC_Config(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); ADC_CommonInitTypeDef ADC_CommonInitStructure; ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; ADC_CommonInit(&ADC_CommonInitStructure); ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfConversion = 1; ADC_Init(ADC1, &ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 1, ADC_SampleTime_15Cycles); ADC_Cmd(ADC1, ENABLE); } // 在主循环或TIM中断中测试 void Test_ADC() { ADC_SoftwareStartConv(ADC1); while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); uint16_t adc_value = ADC_GetConversionValue(ADC1); // 处理adc_value }
内容的提问来源于stack exchange,提问作者Oriolshhh
相关产品推荐
相关产品推荐

