去年在调试一个传感器模块时,我发现自己需要不同频率的方波信号来测试电路响应。当时手头没有专业信号源,临时用单片机GPIO模拟的效果又太粗糙。这次经历让我萌生了自己动手做简易信号发生器的想法。这个基于AD/DA转换的项目,不仅能输出正弦波、方波、三角波等基础波形,还能通过电位器实时调节频率和幅值,完全满足日常电子实验的需求。
AD(模数转换)和DA(数模转换)是嵌入式系统与模拟世界交互的桥梁。DA负责将数字信号转换为模拟电压,AD则执行反向操作。在这个项目中,我们主要利用DA模块生成连续可调的模拟信号,配合嵌入式控制器实现波形参数调节。这种方案相比纯数字方式生成的PWM波,输出信号更平滑,波形质量更高。
选择AD/DA芯片时需要考虑几个核心参数:
我最终选择了TI的DAC121C081(12位I2C接口)和ADS1115(16位ADC)组合。这套方案的优势在于:
完整的信号发生器需要以下几个模块协同工作:
code复制MCU核心 → DA转换 → 滤波放大 → 信号输出
↑ ↓
← 电位器调节 ←
具体接线时要注意:
重要提示:DA芯片的输出端一定要加电压跟随器(运放缓冲),否则直接接负载会导致输出电压不准。这是我调试时踩过的第一个坑。
不同波形的数字生成方法各有特点:
正弦波:
cpp复制// 预生成正弦表(256点)
for(int i=0; i<256; i++){
sinTable[i] = 2048 + 2047 * sin(2*PI*i/256);
}
// 定时器中断中循环输出
void TIM_IRQHandler(){
static uint16_t phase = 0;
DAC_Write(sinTable[phase++]);
if(phase >= 256) phase = 0;
}
方波:
通过比较计数值和阈值即可实现,调节占空比只需改变阈值
三角波:
数字累加器配合方向标志位,达到峰值后反转计数方向
常见新手容易犯的错误是直接用delay函数控制频率,这会导致:
正确的做法是使用定时器中断+查表法:
code复制中断周期 = 波形周期 / 采样点数
例如:1kHz正弦波(256点采样)
中断周期 = 1ms / 256 ≈ 3.9μs
我在实际测试中发现,当频率超过10kHz时,中断处理时间会成为瓶颈。这时可以采用DMA自动传输波形数据到DA,解放CPU资源。
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 波形阶梯明显 | DA分辨率不足/采样点少 | 增加采样点数或换高分辨率DA |
| 高频段幅值衰减 | 运放带宽不足 | 选用GBW>10MHz的运放 |
| 波形畸变 | 电源噪声大 | 增加LC滤波,改用线性稳压 |
| 频率不准 | 定时器计算错误 | 检查时钟源分频设置 |
过采样技术:
即使DA只有12位,通过4倍过采样+数字滤波,等效分辨率可提升到14位
抖动注入:
在DA输入值上叠加随机噪声,能有效改善低幅值时的量化噪声
同步时钟设计:
用同一个晶振为MCU和DA芯片提供时钟,避免时钟漂移导致的抖动
PCB布局要点:
基础版本稳定后,可以考虑:
cpp复制typedef struct {
uint8_t waveType; // 波形类型
float frequency; // 频率值
float amplitude; // 幅值
} WaveConfig;
这个项目最让我惊喜的是,用成本不到50元的器件就实现了市面千元级信号源的基础功能。虽然性能指标还有差距,但对于日常电路实验已经完全够用。最近我正在尝试加入任意波形生成功能,通过SD卡读取预存的波形数据,这需要优化内存管理算法。如果你也在做类似项目,欢迎交流遇到的问题和解决方案。