You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32L052 ADC寄存器配置异常:输入3.3V读取0x00问题排查

排查ADC配置错误导致返回0x00的问题

咱们一步步拆解你代码里的核心问题,这些配置错误和流程混乱直接导致了ADC返回无效值:

1. GPIO模拟模式配置错误

你设置GPIOB14/15为模拟输入时,只置位了MODE14_1和MODE15_1,但STM32的GPIO模拟输入要求MODER对应两位全为1(即MODEx[1:0] = 11)。只设一位的话,引脚处于复用功能模式,根本不是模拟输入状态,自然读不到电压。

修正代码:

// 直接用掩码设置模拟模式,更直观
GPIOB->MODER |= GPIO_MODER_MODE14_Msk | GPIO_MODER_MODE15_Msk;

// 等价写法:
// GPIOB->MODER |= GPIO_MODER_MODE14_0 | GPIO_MODER_MODE14_1;
// GPIOB->MODER |= GPIO_MODER_MODE15_0 | GPIO_MODER_MODE15_1;

2. ADC初始化顺序完全颠倒

ADC的正确启动流程是「使能→等待就绪→校准→启动转换」,你的代码完全搞反了顺序,还手动破坏了硬件标志:

  • 提前手动置位ADRDY标志:ADC1->ISR |= ADC_ISR_ADRDY;,这会覆盖硬件的就绪状态,导致后续等待逻辑失效
  • 等待ADRDY的条件写反:while ((ADC1->ISR & ADC_ISR_ADRDY)); 是当ADRDY为1时死等,正确应该是等待ADRDY置位:while (!(ADC1->ISR & ADC_ISR_ADRDY));
  • 校准放在转换之后:校准必须在ADC首次启动前执行,否则校准无法生效,ADC精度会异常

3. 标志位清除方式错误

你清除EOCAL和AWD标志的写法有语法问题,按位与优先级低于取反,实际执行逻辑不符合预期。正确的清除方式是先将目标标志位按位或,再取反:

ADC1->ISR &= ~(ADC_ISR_EOCAL | ADC_ISR_AWD);

另外,ADRDY标志是写1清除,你注释里的ADC1->CR &= ~ADC_ISR_ADRDY是错误的,正确写法是ADC1->ISR |= ADC_ISR_ADRDY;

4. 转换逻辑混乱

你配置了连续转换模式(CFGR1_CONT),却在循环里反复禁用ADC、重启校准,这完全没必要——连续模式下ADC会自动循环转换,不需要每次重新初始化。同时,你选了3个通道(8、9、17),只在主循环读一次DR只能拿到最后一个通道的值,前面的会被覆盖,建议用中断或DMA来接收多通道数据。

修正后的核心代码示例

int main(void) {
  // 1. 使能时钟(注意用|=,避免关闭其他已启用的GPIO时钟)
  RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
  RCC->IOPENR |= RCC_IOPENR_GPIOAEN | RCC_IOPENR_GPIOBEN;

  // 2. 配置GPIO为模拟输入
  GPIOB->MODER |= GPIO_MODER_MODE14_Msk | GPIO_MODER_MODE15_Msk;
  // 如果使用通道17(内部温度传感器),必须额外使能:
  ADC->CCR |= ADC_CCR_TSEN;

  // 3. 配置ADC参数
  ADC1->CR &= ~ADC_CR_ADSTART; // 确保ADSTART为0,才能修改采样周期
  ADC1->SMPR |= ADC_SMPR_SMP_0 | ADC_SMPR_SMP_1 | ADC_SMPR_SMP_2; // 160.5个时钟周期
  ADC1->CFGR1 &= ~ADC_CFGR1_SCANDIR; // 向上扫描
  // 去掉AUTOFF,连续模式下自动关闭会中断转换流程
  ADC1->CFGR1 |= ADC_CFGR1_AWDCH_3 | ADC_CFGR1_AWDEN | ADC_CFGR1_WAIT | ADC_CFGR1_CONT;
  ADC->CCR |= ADC_CCR_LFMEN | ADC_CCR_VREFEN;
  ADC1->CHSELR |= ADC_CHSELR_CHSEL8 | ADC_CHSELR_CHSEL9 | ADC_CHSELR_CHSEL17;
  
  // 配置中断
  ADC1->IER |= ADC_IER_EOCIE | ADC_IER_EOSEQIE | ADC_IER_OVRIE | ADC_IER_EOSMPIE;
  NVIC_EnableIRQ(ADC1_COMP_IRQn);
  NVIC_SetPriority(ADC1_COMP_IRQn, 3);

  // 4. 初始化ADC
  ADC1->ISR &= ~(ADC_ISR_EOCAL | ADC_ISR_AWD); // 清除初始标志
  ADC1->CR |= ADC_CR_ADEN; // 使能ADC
  while (!(ADC1->ISR & ADC_ISR_ADRDY)); // 等待ADC就绪
  ADC1->ISR |= ADC_ISR_ADRDY; // 清除就绪标志

  // 5. 执行校准(必须在转换前)
  ADC1->CR |= ADC_CR_ADCAL;
  while (!(ADC1->ISR & ADC_ISR_EOCAL)); // 等待校准完成
  ADC1->ISR |= ADC_ISR_EOCAL; // 清除校准标志

  // 6. 启动连续转换
  ADC1->CR |= ADC_CR_ADSTART;

  // 主循环:专注处理业务,转换数据交给中断
  while(1) {
    delay(100);
  }
}

// 中断服务函数:处理多通道转换结果
uint16_t adc_results[3] = {0};
uint8_t adc_channel_idx = 0;

void ADC1_COMP_IRQHandler(void) {
  if(ADC1->ISR & ADC_ISR_EOC){
    // 逐个存储通道结果
    adc_results[adc_channel_idx++] = ADC1->DR;
    if(adc_channel_idx >= 3){
      adc_channel_idx = 0;
      // 这里可以处理完整的序列数据,比如打印、存储
    }
    ADC1->ISR |= ADC_ISR_EOC; // 清除EOC标志
  }
  if(ADC1->ISR & ADC_ISR_EOSEQ){
    ADC1->ISR |= ADC_ISR_EOSEQ; // 清除序列完成标志
  }
  // 处理溢出等其他中断...
}

这些修改应该能解决你返回0x00的问题,核心是纠正GPIO模拟模式配置、ADC初始化顺序和校准时机。如果使用通道17(内部温度传感器),一定要记得使能TSEN位,否则该通道也会返回错误值。

内容的提问来源于stack exchange,提问作者user5713188

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:49:10