1. 项目概述
最近完成了一个基于STM32的多功能信号发生器项目,它能够输出正弦波、三角波和矩形波三种基本波形,通过4.3寸LCD显示屏实时显示当前波形参数,并支持按键控制波形切换和频率调节。这个项目的核心在于利用STM32的DAC(数字模拟转换器)功能,通过软件算法生成各种波形数据,再配合简单的硬件电路实现信号输出。
信号发生器是电子工程师和爱好者的必备工具之一,传统的高性能信号发生器价格昂贵,而基于STM32的方案成本低廉且功能足够满足大多数基础应用场景。我设计的这个信号发生器输出峰峰值稳定在3.0V,频率范围覆盖5kHz到40kHz,误差控制在5%以内。如果需要其他频率范围,只需简单修改代码中的相关参数即可。
2. 硬件设计与选型
2.1 核心硬件组件
整个系统的硬件架构主要由以下几个部分组成:
-
STM32主控芯片:选用STM32F103C8T6作为主控,这款芯片具有丰富的外设资源,包括12位DAC、多个定时器和GPIO,完全满足本项目需求。它的72MHz主频也能保证波形生成的实时性。
-
LCD显示模块:采用4.3寸TFT LCD显示屏,分辨率为480×272。选择这个尺寸主要是考虑到需要同时显示波形图形和参数信息,小尺寸屏幕会显得拥挤。
-
按键输入模块:使用三个独立按键分别控制波形切换、频率增加和频率减少。按键电路采用简单的上拉电阻设计,通过GPIO输入检测按键状态。
-
波形输出电路:STM32的DAC输出信号经过一级运放缓冲后,再通过电位器调节输出幅度,最终得到峰峰值为3.0V的稳定输出。
2.2 关键电路设计要点
注意:DAC输出电路设计对信号质量影响很大,以下是几个关键设计要点:
-
参考电压选择:STM32的DAC参考电压直接影响输出精度。本项目使用芯片内部的参考电压(约3.3V),虽然精度略低于外部精密参考源,但对于5%的误差要求已经足够。
-
输出缓冲电路:DAC输出端接入一个电压跟随器(使用LM358运放),主要目的是提高驱动能力并隔离负载对DAC的影响。
-
低通滤波:在输出端加入简单的RC低通滤波器(截止频率约50kHz),可以滤除高频噪声,使波形更加平滑。
-
电源设计:为模拟电路部分提供干净的电源非常重要。建议使用LC滤波网络隔离数字和模拟电源,必要时可以使用线性稳压器如LM1117为模拟部分单独供电。
3. 软件架构与实现
3.1 系统软件框架
整个软件系统采用前后台架构,主循环负责波形生成和显示更新,中断服务程序处理按键扫描和系统定时。这种架构简单可靠,适合资源有限的嵌入式系统。
主要软件模块包括:
- 波形生成模块(正弦波、三角波、矩形波)
- LCD显示驱动模块
- 按键扫描处理模块
- 系统定时器模块
3.2 波形生成原理与实现
3.2.1 正弦波生成
正弦波的生成采用查表法,预先计算好一个周期的正弦波样点,存储在数组中。这种方法计算量小,实时性高,适合嵌入式系统实现。
c复制#define SINE_TABLE_SIZE 256
const uint16_t sineTable[SINE_TABLE_SIZE];
void initSineTable(void) {
for (uint16_t i = 0; i < SINE_TABLE_SIZE; i++) {
sineTable[i] = (uint16_t)(1023 * (1 + sin(2 * PI * i / SINE_TABLE_SIZE)) / 2);
}
}
void outputSineWave(uint32_t frequency) {
uint32_t period = SystemCoreClock / frequency;
for (uint16_t i = 0; i < SINE_TABLE_SIZE; i++) {
DAC_SetChannel1Data(DAC_Align_12b_R, sineTable[i]);
for (volatile int j = 0; j < period / SINE_TABLE_SIZE; j++);
}
}
这里有几个关键点需要注意:
SINE_TABLE_SIZE决定了波形的分辨率,256点已经能够产生相当平滑的正弦波- 计算周期时使用了
SystemCoreClock,这是STM32的系统时钟频率 - 简单的for循环延时虽然不够精确,但对于kHz级别的频率已经足够
3.2.2 三角波生成
三角波的实现相对简单,通过一个增减计数器即可实现:
c复制void outputTriangleWave(uint32_t frequency) {
uint32_t period = SystemCoreClock / frequency;
uint16_t value = 0;
uint8_t direction = 1;
while (1) {
if (direction) {
value++;
if (value >= 4095) {
value = 4095;
direction = 0;
}
} else {
value--;
if (value <= 0) {
value = 0;
direction = 1;
}
}
DAC_SetChannel1Data(DAC_Align_12b_R, value);
for (volatile int j = 0; j < period / 4096; j++);
}
}
三角波的线性度取决于计数步长,这里使用1LSB的步长能够获得较好的线性度。如果需要更平滑的三角波,可以适当增加DAC分辨率或降低频率。
3.2.3 矩形波生成
矩形波(方波)的实现最为简单,只需在两个电平间切换:
c复制void outputSquareWave(uint32_t frequency) {
uint32_t period = SystemCoreClock / frequency;
uint8_t value = 0;
while (1) {
value =!value;
DAC_SetChannel1Data(DAC_Align_12b_R, value? 4095 : 0);
for (volatile int j = 0; j < period / 2; j++);
}
}
这个实现产生的是占空比为50%的标准方波。如果需要调节占空比,可以修改高低电平的持续时间比例。
3.3 频率控制机制
频率控制是整个系统的关键功能之一。本设计采用软件延时的方式控制波形周期,虽然精度不如硬件定时器,但实现简单且能满足5%的误差要求。
频率计算公式为:
code复制实际频率 = SystemCoreClock / (周期计数值 × 循环次数)
在代码中,period = SystemCoreClock / frequency计算出每个波形周期对应的系统时钟周期数,然后在输出每个样点时进行相应的延时。
提示:如果需要更高精度的频率控制,可以考虑使用STM32的定时器触发DAC转换,这样可以得到更稳定的频率输出。
4. 人机交互设计
4.1 按键控制实现
系统使用三个独立按键实现用户交互:
- 按键1:波形切换(正弦波→三角波→矩形波→正弦波...)
- 按键2:频率增加(步进5kHz)
- 按键3:频率减少(步进5kHz)
按键扫描采用简单的轮询方式:
c复制void keyScan(void) {
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0) {
// 按键1按下,切换波形
currentWaveform = (currentWaveform + 1) % 3;
while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0);
}
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0) {
// 按键2按下,增加频率
if (currentFrequency < 40000) {
currentFrequency += 5000;
}
while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0);
}
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_2) == 0) {
// 按键3按下,降低频率
if (currentFrequency > 5000) {
currentFrequency -= 5000;
}
while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_2) == 0);
}
}
4.2 LCD显示实现
4.3寸LCD显示屏用于显示当前波形类型和频率信息。显示部分主要实现两个功能:
- 显示当前波形名称和频率值
- 实时绘制波形图形(可选)
基础信息显示实现如下:
c复制void displayInfo(void) {
char buffer[20];
switch (currentWaveform) {
case 0:
sprintf(buffer, "Sine: %d Hz", currentFrequency);
break;
case 1:
sprintf(buffer, "Triangle: %d Hz", currentFrequency);
break;
case 2:
sprintf(buffer, "Square: %d Hz", currentFrequency);
break;
}
LCD_SetCursor(0, 0);
LCD_Puts(buffer);
}
如果需要实现波形图形显示,可以考虑在LCD上开辟一个区域,根据当前波形类型和频率实时绘制波形曲线。这需要更复杂的图形处理代码,会占用较多的MCU资源。
5. 性能优化与扩展
5.1 提高输出频率精度
当前设计使用软件延时控制频率,精度有限。要提高频率精度,可以考虑以下改进:
-
使用硬件定时器:配置一个定时器以所需频率触发DAC转换,这样可以获得更精确的频率控制。
-
增加样点数量:对于正弦波,增加
SINE_TABLE_SIZE可以提高波形质量,特别是在高频时。 -
使用DMA传输:将波形数据通过DMA传输到DAC,可以减轻CPU负担,提高系统响应性。
5.2 扩展波形类型
除了基本的三种波形,还可以扩展更多波形类型:
-
锯齿波:类似三角波,但只有上升沿或下降沿。
-
任意波形:允许用户自定义波形样点,实现任意波形输出。
-
调幅/调频波:在基础波形上增加调制功能。
5.3 频率范围扩展
当前设计频率范围为5kHz-40kHz。要扩展频率范围:
-
低频扩展:降低最小频率需要增加延时时间,注意不要超过定时器的最大计数值。
-
高频扩展:提高最大频率需要考虑STM32的处理能力,可能需要优化代码或降低波形分辨率。
频率范围修改示例:
c复制// 修改频率范围判断条件
if (currentFrequency < 100000) { // 提高到100kHz
currentFrequency += 5000;
}
if (currentFrequency > 100) { // 降低到100Hz
currentFrequency -= 5000;
}
6. 常见问题与解决方案
6.1 波形失真问题
问题现象:输出波形出现明显失真,特别是高频时。
可能原因及解决方案:
- 样点数量不足 → 增加
SINE_TABLE_SIZE - DAC转换速度不够 → 检查DAC配置,确保时钟设置正确
- 输出电路带宽不足 → 检查运放带宽和PCB布局
6.2 频率不准问题
问题现象:实际输出频率与设定值偏差较大。
解决方法:
- 校准系统时钟 → 检查
SystemCoreClock值是否正确 - 使用更精确的延时方法 → 改用硬件定时器
- 考虑中断响应时间影响 → 优化中断服务程序
6.3 LCD显示异常
问题现象:LCD显示内容错乱或无显示。
排查步骤:
- 检查LCD初始化序列是否正确
- 确认SPI/I2C通信时序符合LCD要求
- 检查电源电压是否稳定
- 确认背光控制信号正常
7. 实际应用建议
-
PCB设计要点:
- 将模拟部分和数字部分分开布局
- DAC输出走线尽量短,避免干扰
- 为模拟部分提供干净的电源和地
-
校准方法:
- 使用频率计测量实际输出频率,与代码中的设定值比较
- 如有偏差,可以调整
SystemCoreClock值或延时参数 - 使用示波器检查波形幅度,必要时调整输出电路增益
-
扩展应用:
- 添加串口通信功能,实现远程控制
- 增加SD卡存储,支持波形数据导入导出
- 设计外壳和前面板,提升产品化程度
这个基于STM32的信号发生器项目虽然简单,但涵盖了嵌入式开发的多个重要方面:硬件设计、外设驱动、算法实现和人机交互。通过这个项目,不仅可以学习STM32的各种外设使用,还能深入理解信号生成的基本原理。我在实际开发过程中遇到的最大挑战是保证高频时的波形质量,最终通过优化样点数量和输出电路解决了这个问题。