STM32L073RZ ADC通道切换失效问题求助
STM32L073RZ ADC通道切换异常问题
我使用STM32L073RZ的ADC通道6、7、8分别读取100个样本数据,因无法采用连续序列模式,需读完一个通道的100个样本后切换至下一通道。为简化测试,将样本数改为1并编写了对应代码。按电路设计,通道6、7、8应分别读取到78、130、190的ADC值,但实际代码运行后三个通道均返回78;调整读取顺序为7、6、8后,结果变为130、78、78。请问切换至通道6后出现了什么问题?
测试代码
MX_ADC_Init(); MX_TIM2_Init(); /* USER CODE BEGIN 2 */ HAL_TIM_Base_Start(&htim2); select_adc_channel(6); HAL_ADC_Start_IT(&hadc); HAL_Delay(250); uint8_t adc=HAL_ADC_GetValue(&hadc); HAL_ADC_Stop_IT(&hadc); select_adc_channel(7); HAL_ADC_Start_IT(&hadc); HAL_Delay(250); adc=HAL_ADC_GetValue(&hadc); HAL_ADC_Stop_IT(&hadc); select_adc_channel(8); HAL_ADC_Start_IT(&hadc); HAL_Delay(250); adc=HAL_ADC_GetValue(&hadc); HAL_ADC_Stop_IT(&hadc);
MX_ADC_Init()代码
static void MX_ADC_Init(void) { /* USER CODE BEGIN ADC_Init 0 */ /* USER CODE END ADC_Init 0 */ ADC_ChannelConfTypeDef sConfig = {0}; /* USER CODE BEGIN ADC_Init 1 */ /* USER CODE END ADC_Init 1 */ /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) */ hadc.Instance = ADC1; hadc.Init.OversamplingMode = DISABLE; hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1; hadc.Init.Resolution = ADC_RESOLUTION_8B; hadc.Init.SamplingTime = ADC_SAMPLETIME_1CYCLE_5; hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD; hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc.Init.ContinuousConvMode = DISABLE; hadc.Init.DiscontinuousConvMode = DISABLE; hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; hadc.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO; hadc.Init.DMAContinuousRequests = DISABLE; hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV; hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED; hadc.Init.LowPowerAutoWait = DISABLE; hadc.Init.LowPowerFrequencyMode = DISABLE; hadc.Init.LowPowerAutoPowerOff = DISABLE; if (HAL_ADC_Init(&hadc) != HAL_OK) { Error_Handler(); } }
select_adc_channel()代码
void select_adc_channel(uint8_t channel) { ADC_ChannelConfTypeDef sConfig = {0}; switch(channel) { case 0: sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = 1; if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) { Error_Handler(); } break; case 6: sConfig.Channel = ADC_CHANNEL_6; sConfig.Rank = 1; if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) { Error_Handler(); } break; case 7: sConfig.Channel = ADC_CHANNEL_7; sConfig.Rank = 1; if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) { Error_Handler(); } break; case 8: sConfig.Channel = ADC_CHANNEL_8; sConfig.Rank = 1; if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) { Error_Handler(); } break; case 9: sConfig.Channel = ADC_CHANNEL_9; sConfig.Rank = 1; if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) { Error_Handler(); } break; } }
问题根源分析
转换完成判断不可靠:
代码中用HAL_Delay(250)等待转换完成,但外部触发(TIM2_TRGO)的转换时机与延时没有绑定,可能出现新通道的转换未实际执行,HAL_ADC_GetValue()读取的是ADC数据寄存器中保留的旧值(因配置了ADC_OVR_DATA_PRESERVED)。通道配置参数缺失:
select_adc_channel()函数仅配置了通道编号和Rank,未显式设置采样时间。虽然初始化时全局配置了采样时间,但部分HAL库版本在重新配置通道时,需要显式指定采样时间参数才能生效,否则可能导致新通道采样异常。ADC状态未正确复位:
切换通道前未清除ADC的EOC(转换完成)、OVR(溢出)标志,也未确保ADC完全停止,直接启动新通道可能导致触发逻辑混乱,无法正确触发新通道的转换。
修复方案
方案1:用硬件标志判断转换完成
替换HAL_Delay(),通过ADC的EOC标志确认转换完成:
MX_ADC_Init(); MX_TIM2_Init(); HAL_TIM_Base_Start(&htim2); // 读取通道6 select_adc_channel(6); HAL_ADC_Start_IT(&hadc); while(!__HAL_ADC_GET_FLAG(&hadc, ADC_FLAG_EOC)); // 等待转换完成 uint8_t adc = HAL_ADC_GetValue(&hadc); HAL_ADC_Stop_IT(&hadc); __HAL_ADC_CLEAR_FLAG(&hadc, ADC_FLAG_EOC); // 清除标志 // 读取通道7 select_adc_channel(7); HAL_ADC_Start_IT(&hadc); while(!__HAL_ADC_GET_FLAG(&hadc, ADC_FLAG_EOC)); adc = HAL_ADC_GetValue(&hadc); HAL_ADC_Stop_IT(&hadc); __HAL_ADC_CLEAR_FLAG(&hadc, ADC_FLAG_EOC); // 读取通道8 select_adc_channel(8); HAL_ADC_Start_IT(&hadc); while(!__HAL_ADC_GET_FLAG(&hadc, ADC_FLAG_EOC)); adc = HAL_ADC_GetValue(&hadc); HAL_ADC_Stop_IT(&hadc); __HAL_ADC_CLEAR_FLAG(&hadc, ADC_FLAG_EOC);
方案2:完善通道配置参数
在select_adc_channel()中添加采样时间配置,与初始化保持一致:
void select_adc_channel(uint8_t channel) { ADC_ChannelConfTypeDef sConfig = {0}; sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5; // 新增采样时间配置 switch(channel) { case 0: sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = 1; if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) { Error_Handler(); } break; case 6: sConfig.Channel = ADC_CHANNEL_6; sConfig.Rank = 1; if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) { Error_Handler(); } break; case 7: sConfig.Channel = ADC_CHANNEL_7; sConfig.Rank = 1; if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) { Error_Handler(); } break; case 8: sConfig.Channel = ADC_CHANNEL_8; sConfig.Rank = 1; if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) { Error_Handler(); } break; case 9: sConfig.Channel = ADC_CHANNEL_9; sConfig.Rank = 1; if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) { Error_Handler(); } break; } }
方案3:切换通道前复位ADC状态
在切换通道前停止ADC并清除所有标志,确保状态干净:
// 切换通道前执行通用复位逻辑 HAL_ADC_Stop_IT(&hadc); __HAL_ADC_CLEAR_FLAG(&hadc, ADC_FLAG_EOC | ADC_FLAG_OVR); select_adc_channel(channel);
内容的提问来源于stack exchange,提问作者vishnu m c
相关产品推荐
相关产品推荐

