1. 项目概述:用单片机实现函数信号发生器的核心价值
十年前我刚接触电子设计时,实验室的函数信号发生器动辄上万元,而如今用一片不到10元的STM32单片机就能实现同等功能。这个项目将带你用单片机搭建一个可输出正弦波、方波、三角波等多种波形的便携式信号发生器,输出频率范围覆盖1Hz-1MHz(具体取决于单片机性能),幅度可通过电位器调节,还能通过OLED屏实时显示波形参数。
相比商业设备,自制信号发生器在精度和稳定性上可能稍逊一筹,但特别适合电子爱好者进行电路调试、传感器测试和教学演示。我曾用这个装置配合示波器快速验证过滤波器电路特性,比用电脑声卡输出信号稳定得多。下面分享的具体方案基于STM32F103C8T6(蓝色药丸开发板),成本控制在50元以内,所有代码和电路图都已开源。
2. 硬件设计关键解析
2.1 核心器件选型要点
主控选用STM32F103C8T6主要考虑三点:首先其72MHz主频能满足1MHz以下波形生成需求;其次内置DAC和高级定时器,可直接输出模拟信号和PWM;最后开发社区资源丰富,遇到问题容易找到解决方案。实际测试发现,使用DMA+DAC组合输出波形时,在100kHz以下失真度小于1%,完全满足一般实验需求。
信号调理电路包含三个关键部分:
- 低通滤波器:采用Sallen-Key二阶有源滤波器架构,截止频率设为2MHz(公式:fc=1/(2π√(R1R2C1C2))),可有效平滑DAC输出的阶梯波形
- 幅度调节:用10kΩ精密电位器配合OP07运放构成同相放大器,增益调节范围0.5-5倍
- 输出保护:串联100Ω电阻并并联6V稳压管,防止意外短路损坏电路
2.2 硬件设计避坑指南
新手最容易犯的三个错误:
- 未考虑阻抗匹配:DAC输出要接至少1kΩ负载,否则会导致输出电压异常。我在第一版设计中直接接500Ω负载,结果幅度衰减了30%
- 电源去耦不足:每个运放电源引脚要加0.1μF陶瓷电容,主电源入口加100μF电解电容。有次波形出现高频振荡,就是去耦电容焊盘虚焊导致的
- 地线布局混乱:模拟地和数字地要用磁珠单点连接,否则会引入数字噪声。建议使用星型接地,核心接地点选在DAC输出端
3. 软件实现核心技术
3.1 波形生成算法实现
正弦波采用查表法优化性能,预先计算256点采样值存入数组。通过调整定时器中断频率改变输出频率(公式:fout = ftimer/N,N为采样点数)。实测在72MHz主频下,定时器最高可稳定工作在1MHz中断频率,对应正弦波输出上限约10kHz。
方波生成更简单,直接配置定时器PWM模式即可。关键是要处理好占空比调节时的过渡过程,我的做法是在中断里逐步增减CCR寄存器值,避免占空比突变导致谐波分量激增。
三角波采用实时计算方式,在DMA中断中动态调整DAC输出值。一个技巧是使用定点数运算加速计算:比如用int32_t变量,高16位作为整数部分,低16位作为小数部分,运算效率比浮点数提升5倍以上。
3.2 关键代码片段解析
c复制// 正弦波查表生成
const uint16_t sine_table[256] = {2048,2098,...}; // 预计算DAC值
void TIM2_IRQHandler() {
static uint8_t index = 0;
DAC->DHR12R1 = sine_table[index++];
if(index >= 256) index = 0;
TIM2->SR &= ~TIM_SR_UIF;
}
// PWM方波配置
void PWM_Init(void) {
TIM_OC_InitTypeDef sConfigOC = {0};
htim3.Instance->ARR = 719; // 100kHz PWM
sConfigOC.Pulse = 360; // 50%占空比
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
}
// 三角波实时生成
void DAC_DMA_IRQHandler(void) {
static int32_t slope = 1;
static int32_t value = 0;
if(DMA1->ISR & DMA_ISR_TCIF1) {
value += slope;
if(value >= 4095) slope = -1;
if(value <= 0) slope = 1;
DAC->DHR12R1 = value;
DMA1->IFCR |= DMA_IFCR_CTCIF1;
}
}
4. 系统优化与性能提升
4.1 输出频率扩展技巧
要突破单片机主频限制输出更高频率,可以采用以下方法:
- 谐波利用:方波包含丰富谐波,配合带通滤波器可提取特定频率分量。例如用100kHz方波通过10阶切比雪夫滤波器可获得300kHz纯正弦波
- 倍频电路:用74HC4046等PLL芯片可将输出频率倍增,实测可将1MHz信号稳定倍频到10MHz
- DDS技术:采用AD9833等专用DDS芯片,通过SPI与单片机通信,轻松实现0-12MHz可调信号
4.2 波形质量改善方案
降低THD(总谐波失真)的三个关键措施:
- 增加采样点数:将正弦波表从256点扩展到512点,高频失真可从1.2%降到0.6%
- 软件滤波:在DAC输出前对采样值进行FIR滤波,特别适合消除方波振铃现象
- 硬件校准:用精密电压基准源校准DAC输出,我用的REF3025基准源将直流精度提升到±2mV
5. 实用功能扩展
5.1 添加扫频功能
通过旋转编码器设置起止频率和扫频时间,系统自动线性或对数扫频。关键是要处理好定时器ARR寄存器的动态调整,我的实现方案是每10ms计算一次下一频率点:
c复制void Sweep_Update(void) {
static uint32_t last_time = 0;
if(HAL_GetTick() - last_time < 10) return;
float freq = start_freq + (stop_freq - start_freq) * (progress / 100.0);
uint32_t arr = (uint32_t)(72000000 / freq) - 1;
TIM3->ARR = arr;
TIM3->CCR1 = arr / 2;
progress += step;
if(progress > 100) progress = 0;
last_time = HAL_GetTick();
}
5.2 上位机控制接口
添加CH340G USB转串口芯片,通过自定义协议实现PC控制。协议设计示例:
code复制[SETWVF:1] // 设置波形类型 1正弦
[SETFRQ:1000] // 设置频率1kHz
[SETAMP:2500] // 设置幅度2.5V
实测用Python的PySerial库控制,指令响应时间小于10ms。
6. 常见问题排查手册
6.1 典型故障现象与解决
-
无输出信号:
- 检查DAC/TIM时钟使能(__HAL_RCC_DAC_CLK_ENABLE)
- 测量运放供电电压(±5V是否正常)
- 用示波器探头直接测单片机DAC输出引脚
-
波形失真严重:
- 降低输出频率测试(可能超过系统处理能力)
- 检查滤波器电容值(建议用1%精度NPO电容)
- 确保DAC参考电压稳定(可并联10μF钽电容)
-
频率显示不准:
- 校准系统时钟(HSE晶振起振电容通常22pF)
- 检查定时器分频系数(PSC寄存器设置)
- 避免在中断中进行浮点运算(会导致周期抖动)
6.2 性能优化检查清单
- [ ] 启用DAC输出缓冲(DAC_CR_BOFFx位)
- [ ] 将波形表放在CCM内存(STM32专用高速内存区)
- [ ] 使用定时器触发DAC而非软件触发
- [ ] 优先采用硬件SPI控制外设(如AD9833)
- [ ] 对时间敏感代码使用寄存器级操作
这个项目最让我惊喜的是,用如此低成本实现的装置,在100kHz范围内的性能居然媲美入门级商业信号发生器。最近我又给它加装了锂电池和充电模块,现在可以脱离电源工作4小时以上,成为外出调试的必备工具。
