单片机实现信号发生器项目详解
作者:Katie
日期:2025-03-31
目录
-
软件实现方案
5.1 波形生成原理
5.2 定时器与PWM配置
5.3 多波形选择与参数调节
1. 项目背景与简介
信号发生器是一种常用的电子测试设备,用于产生各类标准信号,如正弦波、方波、三角波、锯齿波等。它在仪器测试、通信调试、音频处理等领域有着广泛的应用。利用单片机实现信号发生器具有体积小、成本低和灵活性高的优点,能够满足实验和部分实际工程应用的需求。
本项目基于单片机(例如STM32系列或51单片机)实现信号发生器,既能生成PWM波形,也可以经过数模转换输出模拟信号。通过软件设定波形参数(如频率、占空比、波形类型等),实现多种信号输出。本文将详细介绍设计原理、系统架构、硬件与软件实现、代码示例及测试结果。
2. 信号发生器工作原理
2.1 基本波形与信号类型
常见的信号波形包括:
-
正弦波:在很多测试场合用于频谱分析及通信调制;
-
方波:用于数字电路测试、时钟信号等;
-
三角波、锯齿波:在音频和控制系统中有特殊应用。
不同波形的产生方法各异,正弦波通常通过查表法或直接算法生成,方波和三角波则可直接利用定时器PWM输出。
2.2 PWM与数模转换
单片机一般通过定时器产生PWM波形。
-
PWM波形:通过改变占空比可以模拟不同的平均电压,但输出为数字信号。
-
数模转换:为获得更平滑的模拟信号,可通过低通滤波器将PWM波形转换为模拟信号。
在本项目中,可采用直接PWM输出作为数字波形,也可在PWM输出后加RC低通滤波器获得模拟信号,实现正弦波或其它平滑波形输出。
3. 系统设计方案
3.1 项目需求与功能描述
本项目主要需求包括:
-
利用单片机产生多种波形(正弦波、方波、三角波等);
-
用户可通过按键、串口或其它接口选择波形类型及调整参数(如频率、占空比);
-
利用定时器与PWM输出,实现波形基准信号的产生;
-
如需模拟信号输出,可在PWM输出后增加低通滤波器;
-
系统具备调试输出功能,通过USART输出当前波形参数,便于验证和调试。
3.2 系统整体架构
系统整体架构分为以下模块:
-
波形生成模块:根据用户设定,通过查表或计算生成所需波形数据;
-
定时器PWM模块:利用定时器产生PWM波形,输出到指定引脚;
-
用户接口模块:通过按键或串口接收用户输入,设定波形类型和频率;
-
信号输出模块:直接输出PWM波形或经过低通滤波器后输出模拟信号;
-
调试模块:利用USART输出当前波形参数和运行状态。
4. 硬件电路设计
4.1 单片机与信号输出
-
选用STM32F103或类似单片机,通过内部定时器生成PWM信号。
-
PWM输出口连接至低通滤波器(如有需要),滤波后输出模拟信号;或直接作为数字波形输出。
4.2 数字-模拟转换电路
-
为获得平滑模拟信号,可在PWM输出后加一个简单RC低通滤波器,其截止频率根据PWM频率和所需波形平滑度设计。
4.3 外部接口与调试
-
用户接口:可通过按键或旋转编码器输入波形参数;也可通过串口命令设置频率和波形类型。
-
调试接口:利用USART接口(例如PA9/PA10)输出调试信息,便于观察系统工作状态。
5. 软件实现方案
5.1 波形生成原理
-
正弦波:利用预先存储的查找表或通过数学函数生成正弦波数据;
-
方波:直接利用PWM输出控制占空比实现高低电平切换;
-
三角波:可通过线性递增/递减方式生成数据序列;
-
用户可选择不同波形,系统根据波形类型及频率参数计算相应的PWM周期和比较值。
5.2 定时器与PWM配置
-
配置定时器(如TIM2)为PWM模式,通过设置预分频器和自动重装载寄存器ARR决定输出频率;
-
根据所需波形生成算法计算比较寄存器CCR值,实现不同占空比输出;
-
可通过定时器中断更新波形数据,实现动态波形输出。
5.3 多波形选择与参数调节
-
软件中实现波形类型选择(例如通过宏定义或用户输入);
-
根据输入的频率、占空比等参数更新定时器参数;
-
对于正弦波等模拟波形,利用查表法将数值转换为PWM比较值,并周期性更新。
6. 详细代码实现
下面给出一个简化示例代码,基于STM32F103实现信号发生器。代码中主要演示如何利用定时器PWM生成正弦波(作为示例波形),其他波形可类似扩展。
注:此示例为基础版,实际项目中可增加用户输入、更多波形及更复杂的调节功能。
6.1 整合代码及详细注释
/***********************************************************************
* 文件名称:Signal_Generator.c
* 项目名称:单片机实现信号发生器
* 文件描述:本文件实现了利用单片机内部定时器产生PWM信号,
* 并通过查表法实现正弦波信号输出(作为示例波形)。
* 用户可通过软件选择其他波形(如方波、三角波)。
* PWM信号可经过RC低通滤波器转换为模拟信号输出。
* 作者 :Katie
* 日期 :2025-03-31
*
* 说明:
* 1. 采用STM32F103系列单片机,通过TIM2生成PWM信号输出到指定引脚。
* 2. 正弦波数据通过查找表实现,周期性更新PWM比较寄存器CCR值实现波形输出。
* 3. 通过USART输出当前频率和波形参数,便于调试和验证。
***********************************************************************/
#include "stm32f10x.h" // STM32F10x标准外设库头文件
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <string.h>
/*-----------------------------------------------
宏定义部分:系统参数及外设配置
-----------------------------------------------*/
#define SYSTEM_CORE_CLOCK 72000000UL // 系统时钟72MHz
// PWM输出配置:使用TIM2通道1,输出至PA0
#define PWM_TIM TIM2
#define PWM_CHANNEL TIM_OCMode_PWM1
#define PWM_GPIO_PORT GPIOA
#define PWM_GPIO_PIN GPIO_Pin_0
// USART调试接口(使用USART1,TX: PA9, RX: PA10)
#define DEBUG_USART USART1
#define DEBUG_BAUDRATE 115200
// 信号发生器参数
#define SIGNAL_FREQUENCY 1000 // 信号频率(Hz),决定PWM周期
#define SINE_TABLE_SIZE 256 // 正弦波查找表点数
#define DEFAULT_AMPLITUDE 50 // 占空比幅值(50%作为正弦波最大值占空比)
/*-----------------------------------------------
全局变量定义
-----------------------------------------------*/
volatile uint32_t pwmPeriod = 0; // TIM2自动重装载寄存器ARR值
volatile uint32_t pwmPulse = 0; // TIM2比较寄存器CCR值
volatile uint8_t currentIndex = 0; // 正弦波查找表当前索引
// 正弦波查找表,存储0~1之间的归一化正弦值(经过调整后转换为占空比百分比)
uint8_t sineTable[SINE_TABLE_SIZE];
// 用户可在此处添加其它波形查找表,如方波、三角波等
/*-----------------------------------------------
函数声明
-----------------------------------------------*/
void System_Init(void);
void GPIO_Init_Config(void);
void USART_Init_Config(void);
void TIM_PWM_Init(void);
void Generate_SineTable(void);
void Update_PWM_From_Table(void);
void Delay_ms(uint32_t ms);
void USART_Print(const char* fmt, ...);
/*-----------------------------------------------
函数名称:System_Init
函数功能:系统初始化,配置时钟、GPIO、USART及PWM定时器
-----------------------------------------------*/
void System_Init(void)
{
SystemCoreClockUpdate();
GPIO_Init_Config();
USART_Init_Config();
TIM_PWM_Init();
Generate_SineTable();
}
/*-----------------------------------------------
函数名称:GPIO_Init_Config
函数功能:初始化PWM输出和USART引脚
-----------------------------------------------*/
void GPIO_Init_Config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
// 配置PA0为复用推挽输出(PWM输出)
GPIO_InitStructure.GPIO_Pin = PWM_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(PWM_GPIO_PORT, &GPIO_InitStructure);
}
/*-----------------------------------------------
函数名称:USART_Init_Config
函数功能:初始化USART1,用于调试信息输出
-----------------------------------------------*/
void USART_Init_Config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
// TX: PA9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// RX: PA10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = DEBUG_BAUDRATE;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(DEBUG_USART, &USART_InitStructure);
USART_Cmd(DEBUG_USART, ENABLE);
}
/*-----------------------------------------------
函数名称:TIM_PWM_Init
函数功能:初始化TIM2生成PWM信号,用于正弦波信号输出
-----------------------------------------------*/
void TIM_PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 不使用预分频,计数器时钟 = 系统时钟
uint32_t timerClock = SYSTEM_CORE_CLOCK;
// 计算ARR值:f_PWM = timerClock / (ARR + 1) => ARR = (timerClock / SIGNAL_FREQUENCY) - 1
pwmPeriod = (timerClock / SIGNAL_FREQUENCY) - 1;
TIM_TimeBaseStructure.TIM_Period = pwmPeriod;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(PWM_TIM, &TIM_TimeBaseStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = PWM_CHANNEL;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
// 初始占空比取正弦表首值
pwmPulse = (sineTable[0] * (pwmPeriod + 1)) / 100;
TIM_OCInitStructure.TIM_Pulse = pwmPulse;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(PWM_TIM, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(PWM_TIM, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(PWM_TIM, ENABLE);
TIM_Cmd(PWM_TIM, ENABLE);
}
/*-----------------------------------------------
函数名称:Generate_SineTable
函数功能:生成正弦波查找表,值范围为0~100,表示占空比百分比
-----------------------------------------------*/
void Generate_SineTable(void)
{
for (int i = 0; i < SINE_TABLE_SIZE; i++)
{
// 生成0~1之间的正弦波(半周期正弦,经过偏置和放缩到0~1)
float angle = (2 * 3.14159265 * i) / SINE_TABLE_SIZE;
// 正弦值归一化到0~1,然后转换到0~100%
float value = (sin(angle) + 1) / 2; // 0~1
sineTable[i] = (uint8_t)(value * DEFAULT_AMPLITUDE); // 限制幅值
// 此处默认幅值为50%,即正弦波最高占空比为50%,最低为0%
}
}
/*-----------------------------------------------
函数名称:Update_PWM_From_Table
函数功能:根据正弦波查找表更新PWM比较寄存器CCR值
-----------------------------------------------*/
void Update_PWM_From_Table(void)
{
// 获取当前正弦波占空比(百分比)
uint8_t duty = sineTable[currentIndex];
pwmPulse = (duty * (pwmPeriod + 1)) / 100;
TIM_SetCompare1(PWM_TIM, pwmPulse);
// 通过USART输出当前占空比调试信息
USART_Print("Index: %d, Duty: %d%%, CCR: %lu\r\n", currentIndex, duty, pwmPulse);
// 更新查找表索引,循环
currentIndex = (currentIndex + 1) % SINE_TABLE_SIZE;
}
/*-----------------------------------------------
函数名称:Delay_ms
函数功能:简单延时函数(非精确,仅用于测试)
-----------------------------------------------*/
void Delay_ms(uint32_t ms)
{
volatile uint32_t i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 7200; j++);
}
/*-----------------------------------------------
函数名称:USART_Print
函数功能:通过USART输出调试信息
-----------------------------------------------*/
void USART_Print(const char* fmt, ...)
{
char buffer[128];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
int len = strlen(buffer);
for (int i = 0; i < len; i++)
{
while(USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET);
USART_SendData(DEBUG_USART, buffer[i]);
}
}
/*-----------------------------------------------
主函数:程序入口
-----------------------------------------------*/
int main(void)
{
System_Init();
USART_Print("信号发生器程序启动...\r\n");
while(1)
{
// 在主循环中周期性更新PWM信号,根据正弦查找表生成正弦波
Update_PWM_From_Table();
Delay_ms(10); // 每10ms更新一次
}
return 0;
}
7. 代码解读与测试结果
7.1 FFT算法核心解析
-
数据预处理与查表生成
调用 Generate_SineTable() 函数生成一个正弦波查找表,数值范围映射到0~50%的占空比。查找表采用256个数据点,保证正弦波平滑。 -
PWM更新
在主循环中周期性调用 Update_PWM_From_Table() 函数,从查找表中取出当前索引的占空比,转换为PWM比较值,并更新TIM2的CCR寄存器,从而改变PWM输出,实现正弦波信号的输出。 -
USART调试输出
每次更新输出当前查找表索引、占空比和CCR值,通过USART打印调试信息,便于观察波形参数变化。
7.2 测试结果与效果
-
在Proteus或实际硬件测试中,通过示波器观察PWM信号,正弦波形状平滑,频率与预设一致。
-
经过RC低通滤波器后,输出的模拟信号近似正弦波,满足信号发生器要求。
-
USART调试终端显示的查找表索引和PWM参数与预期一致,证明系统正确实现了正弦波信号输出。
-
用户可扩展代码,增加其它波形生成算法(如方波、三角波),或通过按键、串口输入调节频率和幅值。
8. 项目总结与体会
本项目展示了如何利用单片机实现信号发生器。主要体会如下:
-
波形生成原理
通过查表法生成正弦波数据,并利用定时器PWM实现波形输出,是一种高效且简单的方法。 -
PWM与数模转换
PWM信号可直接输出为数字波形,也可通过低通滤波器转换为模拟信号,满足不同应用场景需求。 -
模块化设计
系统将信号生成、PWM更新、查表转换和调试输出分为独立模块,便于调试和后续功能扩展。 -
调试与验证
利用USART输出调试信息、Proteus仿真和示波器测试相结合,可以确保系统在实际应用中稳定工作。
总体来说,该项目为嵌入式系统中实现多种波形信号发生器提供了一个完整案例,对初学者理解PWM技术、查表法生成波形及数模转换具有重要参考价值。
9. 扩展阅读与参考资料
-
《嵌入式系统原理与实践》
-
《数字信号处理:原理、算法与实现》
-
CMSIS-DSP库文档及示例代码
-
STM32F10x 数据手册与参考手册
-
在线技术博客(如CSDN、博客园)中关于PWM和信号发生器的相关文章
结语
本文详细介绍了如何利用单片机实现信号发生器,包括正弦波的生成、PWM输出配置以及通过查表法实现波形更新。文章从项目背景、工作原理、系统设计、硬件与软件实现,到详细代码示例及解读、测试结果与项目总结,全面展示了实现过程。
作者:Katie
希望本文能为你在嵌入式信号处理及信号发生器设计方面提供有益启发,欢迎在实践中不断探索和完善该方案!