STM32L476裸机ADC始终输出最大值4095问题排查求助
STM32L476裸机ADC始终输出最大值4095问题排查
我在STM32L476上实现裸机ADC读取GPIO引脚电压(因效率需求未使用HAL库),但无论输入电压如何,每次转换结果都是最大值4095。尝试使用VREFINT、VBAT等内部通道,结果依旧为4095,推测问题出在ADC寄存器配置而非GPIO引脚。以下是相关代码,请求协助排查原因。
adc.c 代码
/* * adc.c */ #include "adc.h" //USES ADC1 void adc_init(){ RCC->AHB2ENR |= RCC_AHB2ENR_ADCEN; //使能ADC时钟 RCC->CCIPR &= ~( RCC_CCIPR_ADCSEL ); //清除ADC时钟选择位 RCC->CCIPR |= ( 3 << RCC_CCIPR_ADCSEL_Pos ); //设置ADC时钟为系统时钟 ADC1->CR &= ~( ADC_CR_DEEPPWD );//确保ADC不在深度掉电模式 ADC1->CR |= ADC_CR_ADVREGEN; //使能ADC电压调节器 nop(10000); //延时等待 ADC1->CR &= ~(ADC_CR_ADCALDIF); //设置为单端模式 ADC1->CR |= ADC_CR_ADCAL; //启动ADC校准 while ((ADC1->CR & ADC_CR_ADCAL) != 0) { } //等待校准完成 } void adc_enable(){ ADC1->ISR |= ADC_ISR_ADRDY; // 使能前清除ADRDY标志 ADC1->CR |= ADC_CR_ADEN; //使能ADC while ((ADC1->ISR & ADC_ISR_ADRDY) == 0) { } // 等待ADRDY被硬件置位(原注释错误) } void adc_configGPIO(){ RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN; //使能GPIOC时钟 while (GPIOC->OTYPER == 0xFFFFFFFF); //等待时钟使能完成 GPIOC->OTYPER &= ~( GPIO_OTYPER_OT0 ); //复位C0引脚的OTYPE位 GPIOC->PUPDR &= ~( GPIO_OSPEEDR_OSPEED0 ); // 此处寄存器混淆,应为GPIO_PUPDR_PUPD0 GPIOC->OSPEEDR &= ~( GPIO_PUPDR_PUPD0 ); // 此处寄存器混淆,应为GPIO_OSPEEDR_OSPEED0 GPIOC->MODER &= ~( GPIO_MODER_MODE0 ); //复位C0引脚的模式位 GPIOC->ASCR |= ( GPIO_ASCR_ASC0 ); //连接C0引脚的模拟开关到ADC通道 GPIOC->MODER |= ( 0x3 << GPIO_MODER_MODE0_Pos ); //设置为模拟模式 } void adc_setChannel(){ //采样时间默认,后续修改为最大 ADC1->SQR1 &= ~( ADC_SQR1_L ); //设置序列长度为1 ADC1->SQR1 &= ~(ADC_SQR1_SQ1); //复位序列通道选择 ADC1->SQR1 |= (1 << ADC_SQR1_SQ1_Pos); //设置序列第一个通道为通道1(对应PA1,非PC0) ADC1->SMPR1 &= ~( ADC_SMPR1_SMP1 ); //复位通道1的采样时间 ADC1->SMPR1 |= ( 0x7 << ADC_SMPR1_SMP1_Pos ); //设置最大采样时间:640.5周期/采样 } // 执行单次ADC转换 // (假设序列中只有一个通道) uint16_t adc_singleConversion() { // 启动ADC转换 ADC1->CR |= ( ADC_CR_ADSTART ); // 等待转换完成标志 while ( !( ADC1->ISR & ADC_ISR_EOC ) ) {}; // 读取转换值(此操作会清除EOC标志) uint16_t adc_val = ADC1->DR; // 等待序列完成标志并清除它 while ( !( ADC1->ISR & ADC_ISR_EOS ) ) {}; ADC1->ISR |= ( ADC_ISR_EOS ); // 返回ADC值 return adc_val; }
主程序代码
nop()为微秒级延时函数,printMsg()用于串口打印输出,init_platform()处理其他初始化操作,确保使用GPIO前引脚配置已完全重置。
void branch_main(){ //测试ADC adc_init(); adc_enable(); adc_configGPIO(); adc_setConstantGPIOValue(); //注:此函数未提供实现 adc_setChannel(); while(1){ printMsg("ADC value: %d \r\n", adc_singleConversion()); nop(1000000); } } int main() { init_platform(); branch_main(); }
关键问题分析与修正方案
ADC通道与引脚映射错误
STM32L476的PC0对应ADC1的通道10,而非通道1。当前设置SQ1为1,实际采样的是PA1引脚,若PA1悬空或接高电平,会直接输出最大值4095。
修正:将通道设置改为10:ADC1->SQR1 |= (10 << ADC_SQR1_SQ1_Pos); // 设置序列通道为10(对应PC0)测试内部通道时需对应正确通道号并使能:
- VREFINT对应通道17,需先添加
ADC1->CCR |= ADC_CCR_VREFEN;,再设置SQ1为17 - VBAT对应通道18,需先添加
ADC1->CCR |= ADC_CCR_VBATEN;,再设置SQ1为18
- VREFINT对应通道17,需先添加
GPIO配置寄存器混淆
adc_configGPIO()中复位PUPDR和OSPEEDR时寄存器搞反,修正后:GPIOC->PUPDR &= ~( GPIO_PUPDR_PUPD0 ); // 复位C0的上下拉配置 GPIOC->OSPEEDR &= ~( GPIO_OSPEEDR_OSPEED0 ); // 复位C0的速度配置内部通道未使能
测试VREFINT/VBAT等内部通道时,必须先在ADC1->CCR寄存器中开启对应使能位,否则无法正确采样。
内容的提问来源于stack exchange,提问作者MysticDirt
相关产品推荐
相关产品推荐

