CW32开发板ADC电压采集与优化实践
📅 2026/7/17 9:22:07
👁️ 阅读次数
📝 编程学习
1. CW32饭盒派开发板ADC电压采集测试概述
CW32饭盒派开发板是一款基于CW32F030微控制器的嵌入式开发平台,内置12位高精度ADC模块。本次测试将重点验证开发板的ADC电压采集功能,通过测量外部输入电压并输出到串口调试助手,评估其精度和稳定性。对于嵌入式开发者而言,掌握ADC采集技术是传感器数据采集、电池监测等应用的基础技能。
2. 硬件环境搭建与原理分析
2.1 核心硬件配置
CW32F030微控制器内置的12位ADC模块具有以下特性:
- 12位分辨率(4096个量化等级)
- 最高1Msps采样速率
- 支持单次/连续/扫描/间断四种工作模式
- 16个外部通道+3个内部信号源
- 参考电压可选VREF+/VSSA或内部2.5V基准
开发板上的ADC测试点已通过分压电路设计,允许输入0-3.3V电压信号。特别注意:超过3.3V的输入电压可能损坏ADC引脚,必要时需外接分压电阻。
2.2 电路连接方案
推荐两种电压输入方式:
- 电位器调节:使用板载10KΩ电位器,输出可调电压到ADC引脚
- 外部信号源:通过杜邦线连接信号发生器,注意信号幅值限制
典型连接示意图:
电压源 -> 分压电阻网络 -> ADC输入引脚 -> 0.1μF去耦电容 -> GND3. 软件开发环境配置
3.1 工程模板准备
- 使用Keil MDK 5.33开发环境
- 安装CW32器件支持包(DFP)
- 从官方库复制ADC示例代码作为基础模板
关键库文件:
cw32f030_adc.h:ADC寄存器定义cw32f030_rcc.h:时钟配置cw32f030_gpio.h:引脚复用配置
3.2 ADC初始化代码解析
void ADC_Config(void) { ADC_InitTypeDef ADC_InitStruct; // 时钟配置 RCC_AHBPeriphClockCmd(RCC_AHBEN_ADC, ENABLE); // 基本参数设置 ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b; ADC_InitStruct.ADC_ScanMode = ADC_ScanMode_Continuous; ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_Init(ADC1, &ADC_InitStruct); // 通道配置 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_41_5Cycles); // 使能ADC ADC_Cmd(ADC1, ENABLE); // 校准流程(必须步骤) ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); }4. 电压采集实现与优化
4.1 基础采集流程
- 启动转换:
ADC_SoftwareStartConvCmd(ADC1, ENABLE);- 等待转换完成:
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));- 读取结果:
uint16_t adcValue = ADC_GetConversionValue(ADC1);- 电压换算公式:
float voltage = (float)adcValue * 3.3f / 4095.0f;4.2 软件滤波算法
为提高测量稳定性,推荐采用以下滤波方法:
- 移动平均滤波:
#define SAMPLE_SIZE 10 uint16_t samples[SAMPLE_SIZE]; float GetFilteredVoltage(void) { static uint8_t index = 0; samples[index++] = ADC_GetConversionValue(ADC1); if(index >= SAMPLE_SIZE) index = 0; uint32_t sum = 0; for(uint8_t i=0; i<SAMPLE_SIZE; i++){ sum += samples[i]; } return (float)sum * 3.3f / (4095.0f * SAMPLE_SIZE); }- 中值滤波:
int cmp(const void *a, const void *b) { return (*(uint16_t*)a - *(uint16_t*)b); } float GetMedianVoltage(void) { static uint16_t samples[5]; for(uint8_t i=0; i<5; i++){ samples[i] = ADC_GetConversionValue(ADC1); Delay(1); } qsort(samples, 5, sizeof(uint16_t), cmp); return (float)samples[2] * 3.3f / 4095.0f; }5. 串口输出与数据可视化
5.1 串口初始化配置
void USART_Config(void) { USART_InitTypeDef USART_InitStruct; // 使能时钟 RCC_AHBPeriphClockCmd(RCC_AHBEN_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1EN_USART1, ENABLE); // 引脚复用配置 GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); // USART参数设置 USART_InitStruct.USART_BaudRate = 115200; USART_InitStruct.USART_WordLength = USART_WordLength_8b; USART_InitStruct.USART_StopBits = USART_StopBits_1; USART_InitStruct.USART_Parity = USART_Parity_No; USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(USART1, &USART_InitStruct); USART_Cmd(USART1, ENABLE); }5.2 数据格式化输出
void SendVoltage(float voltage) { char buffer[32]; int len = sprintf(buffer, "Voltage: %.3fV\r\n", voltage); for(int i=0; i<len; i++){ USART_SendData(USART1, buffer[i]); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } }5.3 上位机显示建议
推荐使用以下工具进行数据可视化:
- 串口调试助手:SecureCRT、Putty等
- 数据绘图工具:SerialPlot、CoolTerm
- 自定义上位机:基于Python+PySerial开发
6. 实测问题排查与优化
6.1 常见问题解决方案
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| ADC值跳变大 | 电源噪声 | 增加去耦电容(0.1μF+10μF) |
| 读数始终为0 | 引脚配置错误 | 检查GPIO模拟输入模式 |
| 数值饱和 | 输入超量程 | 检查分压电路设计 |
| 线性度差 | 参考电压不稳 | 使用外部精密基准源 |
6.2 精度提升技巧
参考电压处理:
- 使用外部精密基准源(如REF3030)
- 在VDDA引脚添加LC滤波电路
采样时序优化:
// 适当增加采样时间(适用于高阻抗源) ADC_SampleTimeConfig(ADC1, ADC_Channel_0, ADC_SampleTime_239_5Cycles);- 硬件布局建议:
- ADC走线远离数字信号线
- 使用独立接地层
- 缩短传感器到ADC的路径
7. 进阶应用示例
7.1 多通道扫描模式
void MultiChannel_ADC_Config(void) { ADC_InitStruct.ADC_ScanMode = ADC_ScanMode_Enable; ADC_Init(ADC1, &ADC_InitStruct); // 配置3个通道 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55_5Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55_5Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55_5Cycles); // 使能DMA ADC_DMACmd(ADC1, ENABLE); }7.2 定时器触发采样
void TimerTrigger_Config(void) { // 配置TIM3作为触发源 TIM_TimeBaseInitTypeDef TIM_InitStruct; RCC_APB1PeriphClockCmd(RCC_APB1EN_TIM3, ENABLE); TIM_InitStruct.TIM_Period = 1000-1; TIM_InitStruct.TIM_Prescaler = 64000-1; // 1kHz触发 TIM_InitStruct.TIM_ClockDivision = 0; TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_InitStruct); // ADC外部触发配置 ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; ADC_Init(ADC1, &ADC_InitStruct); TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update); TIM_Cmd(TIM3, ENABLE); }在实际项目中,ADC采集的稳定性往往决定了整个系统的可靠性。通过本实验我们发现,CW32的ADC模块在软件滤波和适当硬件处理的情况下,可以达到±2LSB的测量精度,满足大多数工业检测需求。特别要注意的是,长时间采集时应定期进行校准,以消除温度漂移带来的影响。
编程学习
技术分享
实战经验