STM32G030C8T6 ADC多通道扫描与内部温度传感器校准实践
1. STM32G030C8T6 ADC多通道扫描实战
第一次用STM32G030C8T6做多通道ADC采样时,我踩了个大坑——以为配置好DMA就能自动轮询所有通道,结果发现数据全混在一起了。后来才发现G0系列的ADC扫描模式有讲究,这里分享下我的实战经验。
STM32G030的ADC支持两种扫描模式:
- Sequencer fully configurable:像自助餐随便选通道,但只能选ADC_IN0~ADC_IN14
- Sequencer not fully configurable:固定菜单,按顺序吃(采样)
我用的板子要同时采集PA5电压(通道5)和芯片温度(内部通道),实测两种模式都能用。但如果你需要采样通道15以上的信号,就必须选第一种模式。
配置时最容易忽略的三个参数:
ScanConvMode:要设为ENABLENbrOfConversion:必须等于实际使用的通道数DMAContinuousRequests:建议开启,否则每次转换完DMA就停了
// CubeMX生成的初始化代码关键点 hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE; hadc1.Init.NbrOfConversion = 2; // 两个通道 hadc1.Init.DMAContinuousRequests = ENABLE;2. 内部温度传感器校准秘籍
这个芯片的温度传感器绝对是个"娇气包",不校准的话误差能差10℃以上。我拆解过官方例程,发现他们偷偷做了三件事:
- 硬件校准:上电先调用
HAL_ADCEx_Calibration_Start() - 软件滤波:连续采样32次取平均
- 公式修正:用了藏在Flash里的校准参数
温度计算公式看着复杂,其实拆解开来很直观:
float Get_MCU_Temperature(void) { uint16_t TS_CAL1 = *(__IO uint16_t *)(0x1FFF75A8); // 出厂校准值 float raw_temp = (float)adcAverageBuff[TEMP_CH-1]; return (30.0f / TS_CAL1) * (raw_temp - TS_CAL1) + 30.0f; }这个公式的物理意义是:已知芯片在30℃时的ADC值(TS_CAL1),用当前ADC值与校准值的比例关系推算温度。
实测发现两个优化技巧:
- 采样时间建议设为
ADC_SAMPLETIME_160CYCLES_5(温度传感器响应慢) - 每次读取温度前最好延迟10ms,让传感器稳定
3. DMA配置的隐藏陷阱
用DMA传输ADC数据时,我遇到过数据错位的灵异事件——通道5的数据跑到温度通道去了。后来发现是DMA缓冲区没对齐导致的。
正确的缓冲区定义姿势:
#define ADC_VALUE_NUM 32 // 每个通道采样32次 #define ADC_CHANNEL_NUM 2 // 2个通道 // 关键点:二维数组第一维是采样次数,第二维是通道号 __IO uint16_t adcCovValueBuff[ADC_VALUE_NUM][ADC_CHANNEL_NUM] = {0};启动DMA时要注意:
// 第三个参数是总数据量 = 采样次数 × 通道数 HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adcCovValueBuff, ADC_VALUE_NUM * ADC_CHANNEL_NUM);常见问题排查:
- 数据错位 → 检查DMA的
MemInc和DataAlignment - DMA不触发 → 确认
ContinuousConvMode和DMAContinuousRequests都开启 - 数据不更新 → 检查是不是忘了调用
HAL_ADC_Start_DMA
4. 精度提升的实战技巧
经过三个项目的打磨,我总结出这套精度优化组合拳:
电源去耦:
- 在VDDA和VSSA之间加10uF+100nF电容
- 模拟部分走线远离数字信号
软件优化:
// 中值滤波+滑动平均 uint16_t filtered_adc(uint8_t ch) { uint16_t buf[5]; // 取5次采样值排序 qsort(buf, 5, sizeof(uint16_t), compare); return (buf[1] + buf[2] + buf[3]) / 3; // 取中间3个平均值 }温度补偿: 实测发现芯片工作时自发热会影响温度传感器,我的解决方案是:
- 连续采样时每次间隔至少100ms
- 在代码中补偿自发热系数(每MHz时钟约+0.3℃)
校准验证: 用标准温度计对比时,我发现30℃附近最准,但高温段误差大。于是做了分段补偿:
if(temp > 70.0f) temp -= 2.5f; else if(temp > 50.0f) temp -= 1.0f;
5. 完整代码框架解析
分享我的项目代码结构,关键文件如下:
adc.h中明确定义通道映射:
#define CSD_NUMBER 1 // 通道5对应数组下标0 #define TEMP_NUMBER 2 // 温度通道对应下标1adc.c里的核心函数:
// 获取指定通道ADC值(自动滤波) uint16_t Get_Adc_value(uint8_t ch) { uint32_t sum = 0; for(uint8_t i=0; i<ADC_VALUE_NUM; i++) { sum += adcCovValueBuff[i][ch-1]; // 注意-1转换数组下标 } return sum / ADC_VALUE_NUM; } // 获取精确温度值 float Get_MCU_Temperature(void) { uint32_t sum = 0; for(uint8_t i=0; i<ADC_VALUE_NUM; i++) { sum += adcCovValueBuff[i][TEMP_NUMBER-1]; } uint16_t avg = sum / ADC_VALUE_NUM; uint16_t TS_CAL1 = *(__IO uint16_t *)(0x1FFF75A8); return (30.0f / TS_CAL1) * (avg - TS_CAL1) + 30.0f; }在main.c中的典型用法:
CDS_ADC_Init(); // 初始化ADC while(1) { float vol = Get_Adc_value(CSD_NUMBER) * 3.3f / 4095; float temp = Get_MCU_Temperature(); HAL_Delay(500); }调试时发现一个隐蔽的坑:STM32G0的温度传感器输出电压范围是0~3.3V对应0~4095,但实际有效范围只有约300-1600(对应-40~125℃)。超出这个范围的值要视为异常数据。
