1. 项目概述:当STM32遇上信号发生器
作为一名在嵌入式领域摸爬滚打多年的工程师,我一直对信号发生器和示波器这类基础测试仪器有着特殊的情结。记得刚入行时,实验室那台笨重的台式信号发生器要价近万元,而今天我们要聊的这个项目——基于STM32的示波器信号发生器,成本不到200元,却能实现80%的基础功能。
这个设计的核心思路很简单:利用STM32F103这颗性价比之王,通过其内置的DAC(数模转换器)生成各种波形,同时用ADC(模数转换器)采集外部信号,配合TFT显示屏实现波形可视化。就像把瑞士军刀的多功能理念应用到测试仪器领域,一个设备解决波形生成和测量两大需求。
2. 硬件设计:精打细算的电路艺术
2.1 主控选型:为什么是STM32F103?
在芯片缺货涨价的当下,STM32F103C8T6依然是性价比的首选。这款72MHz主频的Cortex-M3内核芯片,具备:
- 2个12位DAC(最高1MHz转换速率)
- 3个12位ADC(最高1MHz采样率)
- 多达11个定时器
- 丰富的GPIO资源
实际选型时要注意:STM32F103有多个子型号,C8T6的64KB Flash完全够用,而更高端的ZET6虽然资源更丰富,但价格可能翻倍,对预算敏感的项目不推荐。
2.2 信号调理电路设计
DAC输出的信号需要经过调理才能达到理想效果,这里分享几个关键设计细节:
-
电压抬升电路:STM32的DAC默认输出0-3.3V,通过OP07运放构建的同相放大电路,将输出范围扩展到0-5V。具体电路参数:
code复制R1 = 10kΩ R2 = 6.8kΩ 增益 = 1 + R2/R1 ≈ 1.68倍 -
低通滤波:在DAC输出端添加二阶Butterworth滤波器,截止频率设为150kHz(略高于设计最高频率100kHz),有效抑制高频噪声。电容建议选用NPO材质,温度稳定性更好。
-
输入保护:信号采集通道前端加入TVS二极管和100Ω限流电阻,防止过压损坏ADC。实测中这个设计多次拯救了因误接24V电源而濒临报废的开发板。
2.3 电源设计:稳定性的基石
采用两级稳压方案:
- 第一级:LM2596开关稳压将输入7-12V降至5V
- 第二级:AMS1117线性稳压输出3.3V
特别注意:模拟部分和数字部分的电源要用磁珠隔离,我在早期版本中曾因共地问题导致ADC采样出现周期性毛刺,后来用0Ω电阻分割地平面才解决。
3. 软件架构:FreeRTOS带来的灵活性
3.1 任务划分与优先级设置
系统运行在FreeRTOS上,任务划分如下表:
| 任务名称 | 优先级 | 功能描述 | 堆栈大小 |
|---|---|---|---|
| WaveGen | 3 | 波形生成 | 512字节 |
| ADCTask | 2 | 信号采集 | 1024字节 |
| Display | 1 | 界面刷新 | 2048字节 |
| KeyScan | 0 | 按键处理 | 256字节 |
关键经验:Display任务堆栈要预留足够空间,我曾因堆栈溢出导致屏幕花屏,调试了整整两天才发现问题。
3.2 波形生成的核心算法
正弦波实现:查表法的艺术
采用256点预计算正弦表,通过定时器触发DAC输出:
c复制// 预计算正弦表
const uint16_t SineTable[256] = {
2048, 2098, 2148, 2198, 2248, 2298, 2348, 2398,
// ... 省略中间值
2047, 2098 // 循环回到起点
};
// 定时器中断服务程序
void TIM2_IRQHandler(void) {
static uint8_t index = 0;
DAC->DHR12R1 = SineTable[index++];
TIM2->SR &= ~TIM_SR_UIF; // 清除中断标志
}
通过调整定时器频率改变输出波形频率,计算公式:
code复制f_out = f_timer / 256
方波的精细控制
利用定时器的PWM模式生成方波,关键是要处理好死区时间。当频率高于10kHz时,建议开启定时器的刹车功能,防止因软件异常导致输出持续高电平损坏后端电路。
3.3 信号采集的优化技巧
-
双缓冲技术:配置ADC为DMA双缓冲模式,一组缓冲区处理数据时,另一组继续采集,确保不丢失任何采样点。
-
软件触发同步:在波形生成的同时记录时间戳,实现信号发生与采集的同步。这点在测量电路响应特性时特别有用。
-
数字滤波:采用移动平均滤波算法,窗口大小根据信号频率动态调整:
c复制#define FILTER_WINDOW 5 uint16_t moving_average(uint16_t *buf) { uint32_t sum = 0; for(int i=0; i<FILTER_WINDOW; i++) { sum += buf[i]; } return (uint16_t)(sum / FILTER_WINDOW); }
4. 人机交互:旋转编码器的正确打开方式
4.1 编码器消抖方案对比
测试了三种方案后,最终选择硬件+软件复合消抖:
- 硬件端:104电容并联在AB相
- 软件端:状态机算法检测有效跳变
实测表明,这种方案在成本增加不到1元的情况下,将误触发率从15%降至0.3%以下。
4.2 菜单系统设计
采用分层式菜单结构,通过结构体数组定义菜单项:
c复制typedef struct {
char *text;
uint8_t submenu_num;
void (*action)(void);
} MenuItem;
MenuItem main_menu[] = {
{"波形设置", 3, NULL},
{"频率设置", 0, FreqSet},
{"幅值设置", 0, AmpSet},
// ...其他菜单项
};
这种设计后期新增功能时,只需简单添加菜单项即可,扩展性极佳。
5. 实测性能与优化记录
5.1 频率精度测试数据
| 设定频率 | 实测频率 | 相对误差 |
|---|---|---|
| 100Hz | 99.8Hz | -0.2% |
| 1kHz | 998Hz | -0.2% |
| 10kHz | 9.97kHz | -0.3% |
| 50kHz | 49.8kHz | -0.4% |
| 100kHz | 99.3kHz | -0.7% |
5.2 那些年踩过的坑
-
DAC输出毛刺:当快速切换波形类型时,DAC输出会出现尖峰脉冲。解决方案是在切换前先将DAC输出缓降至0V,切换完成后再恢复。
-
屏幕刷新撕裂:直接刷屏会导致波形显示不连续。后来改为先写入内存再整体刷新,并利用TFT的垂直消隐期同步更新。
-
高频信号失真:当输出超过80kHz时,波形明显变形。通过优化PCB布局,缩短信号走线长度后改善明显。
6. 升级路线:从够用到好用
当前版本已经能满足基础需求,但还有提升空间:
-
硬件升级:
- 替换STM32H743,ADC采样率可达3.6MHz
- 增加前端程控放大器,实现自动量程
- 添加锂电池管理电路,提升便携性
-
软件增强:
- 移植emWin图形库,实现更专业的UI
- 加入FFT频谱分析功能
- 开发PC端配套软件,支持波形导入导出
这个项目最让我欣慰的是,它已经成功应用于本地三所高校的电子实验室,替代了部分昂贵的商用设备。有学生反馈说:"原来测试仪器也可以这么好玩!"——这或许就是对开发者最好的奖励。