1. 项目概述与设计思路
这个基于STC15单片机的电流电压采集系统,是我在实验室里打磨了两周的实战成果。核心功能是通过ADC采集模拟信号,实现双参数实时监测与阈值报警。别看它硬件简单——就两个电位器加四个按键,但软件设计上藏着不少工程师级别的细节处理。
系统采用分层架构设计:
- 感知层:P1.0/P1.1作为ADC输入通道
- 控制层:STC15W4K56S4主控芯片
- 人机交互:4个轻触按键+LCD1602显示屏
- 报警输出:红色LED指示
硬件选型上,我放弃了传统的STC89C52,选择了内置12位ADC的STC15系列。这个决策让电路板面积缩小了40%,BOM成本降低15元/套。实际测试中,12位ADC在5V量程下分辨率达到1.22mV,完全满足工业现场0.5%精度的要求。
2. 核心电路设计解析
2.1 信号调理电路
电压检测通道采用电阻分压网络:
code复制Vin --[R1 10k]--+--[R2 10k]--GND
|
ADC0
计算分压比时要注意阻抗匹配:ADC输入阻抗约10kΩ,因此R2取值不宜过大。我通过实验发现,当R2>20kΩ时,采样值会出现明显衰减。
电流检测则用0.1Ω/2W的锰铜采样电阻配合INA199A1放大器(增益100倍)。这种设计在10A满量程时,采样电阻功耗仅1W,温升控制在15℃以内。关键计算公式:
code复制Vout = I × Rsense × Gain = 10A × 0.1Ω × 100 = 1V
实际调试中发现,必须在放大器输出端加RC滤波(R=1kΩ, C=100nF),否则ADC值会有±3LSB的抖动。
2.2 抗干扰设计
PCB布局时有三个要点:
- 模拟地与数字地单点连接,接地点选在ADC芯片下方
- 电源入口处放置100μF电解电容并联104瓷片电容
- ADC参考电压引脚用TL431基准源替代LDO输出
曾因忽略这些细节导致采样值漂移,后来用示波器捕捉到电源纹波达50mVpp。整改后纹波降至5mVpp以下,ADC读数稳定性显著提升。
3. 关键代码实现详解
3.1 ADC驱动优化
原始代码中ADC结果计算方式可改进:
c复制uint16_t get_adc(uchar ch) {
ADC_CONTR = 0x80 | (ch & 0x07); // 通道号限幅
_nop_(); _nop_();
while(!(ADC_CONTR & 0x20)); // 用硬件标志位替代延时
ADC_CONTR &= ~0x20; // 清除完成标志
return (ADC_RES << 4) | (ADC_RESL & 0x0F); // 标准拼接方式
}
改进点包括:
- 增加通道号有效性检查
- 及时清除中断标志
- 显式使用位操作拼接数据
实测表明,这种写法比原方案转换时间缩短1.5μs,且消除了通道串扰风险。
3.2 按键状态机增强版
原始状态机扩展为支持长按加速功能:
c复制void key_scan() {
static struct {
uchar state;
uint16_t cnt;
} key[4] = {0};
for(int i=0; i<4; i++) {
switch(key[i].state) {
case 0: // 等待按下
if(!KEY_PORT & (1<<i)) {
key[i].state = 1;
delay_ms(15); // 消抖
}
break;
case 1: // 首次触发
if(KEY_PORT & (1<<i)) {
key[i].state = 0;
} else {
adjust_param(i); // 参数调整
key[i].state = 2;
key[i].cnt = 300; // 长按阈值
}
break;
case 2: // 长按检测
if(KEY_PORT & (1<<i)) {
key[i].state = 0;
} else if(--key[i].cnt == 0) {
key[i].state = 3;
key[i].cnt = 50; // 连发间隔
}
break;
case 3: // 连发模式
if(KEY_PORT & (1<<i)) {
key[i].state = 0;
} else if(--key[i].cnt == 0) {
adjust_param(i);
key[i].cnt = 20; // 加速连发
}
break;
}
}
}
这个增强版状态机实现了:
- 按下立即响应
- 长按1秒后进入连发
- 连发速度逐渐加快
- 支持四键独立检测
4. 系统调试与性能优化
4.1 校准流程设计
在工厂环境下需进行两点校准:
- 零点校准:短接ADC输入,记录偏移值
- 满度校准:输入标准信号(如4.096V),计算增益系数
存储校准参数到EEPROM的代码示例:
c复制typedef struct {
float volt_offset;
float volt_gain;
float curr_offset;
float curr_gain;
uint16_t crc;
} CalibParams;
void save_calibration() {
CalibParams param = {
.volt_offset = 0.012,
.volt_gain = 1.018,
.curr_offset = 0.005,
.curr_gain = 0.987,
.crc = 0
};
param.crc = calc_crc16((uint8_t*)¶m, sizeof(param)-2);
STC_EEPROM_write(0, (uint8_t*)¶m, sizeof(param));
}
4.2 动态滤波算法
针对工业现场干扰,采用递推平均滤波:
c复制#define FILTER_LEN 8
float adc_filter(float new_val) {
static float buf[FILTER_LEN] = {0};
static uint8_t idx = 0;
static float sum = 0;
sum -= buf[idx];
buf[idx] = new_val;
sum += new_val;
idx = (idx + 1) % FILTER_LEN;
return sum / FILTER_LEN;
}
该算法内存占用固定,适合资源有限的单片机。实测可将随机干扰降低到原来的1/5。
5. 工程经验与避坑指南
5.1 常见问题排查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| ADC值跳变大 | 参考电压不稳 | 增加10μF钽电容到VREF引脚 |
| 按键响应迟钝 | 上拉电阻过大 | 将10kΩ改为4.7kΩ |
| 电流显示为0 | 采样电阻开路 | 检查锰铜电阻焊接 |
| LCD显示乱码 | 初始化时序不对 | 增加20ms上电延时 |
5.2 量产注意事项
-
PCB设计:
- ADC走线远离数字信号线
- 采样电阻采用开尔文接法
- 预留测试点(TP1~TP4)
-
软件防护:
- 增加看门狗定时器
- 关键参数CRC校验
- 设置软件滤波开关位
-
环境适应性:
- -20℃~70℃温度补偿
- 通过EFT/Burst测试
- 符合IEC61000-4标准
这个项目最让我自豪的是将理论精度0.1%的设计,通过工程化实现做到了实际0.3%的稳定性。秘诀在于:每个环节都预留10倍余量——ADC用12位而非刚好够用的10位,采样电阻功率按3倍选取,软件滤波深度可配置。这种过度设计思想,正是工业级产品与实验样机的本质区别。
