目录
扫描二维码关注公众号,回复:
17546913 查看本文章

一、引言
在嵌入式系统中,正弦波生成是信号处理、通信系统、电机控制等领域的基础功能。本文将以 STM32F103ZET6 为例,介绍三种生成正弦波的方法:DAC 直接输出、PWM 合成和定时器触发 ADC 采集,重点探讨 DAC 方案的实现细节,并结合模块化编程和 Doxygen 注释规范进行代码解析。
二、硬件准备
- 开发板:STM32F103ZET6 核心板
- 外设:
- DAC1 通道(PA4)
- TIM2 定时器(触发 DAC 更新)
- 示波器(观察输出波形)
- 硬件连接:
STM32F103ZET6 示波器 ------------------------ PA4 (DAC_OUT1) CH1 GND GND
三、生成方法对比
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
DAC | 高精度、低失真 | 占用专用外设 | 信号发生器 |
PWM | 无需额外外设 | 需低通滤波,频率受限 | 简单波形合成 |
ADC | 软件定义波形 | 占用 CPU 资源,频率较低 | 实验性波形生成 |
重点:DAC 直接输出方案(精度 12 位,频率可调范围 0-400kHz)
四、DAC 正弦波生成实现
1. 数学原理
- 公式:
- 参数:
- A:幅度(0-3.3V)
- f:频率(Hz)
- offset:直流偏置(1.65V)
2. 代码结构
project/
├── Drivers/
│ ├── dac_driver/
│ │ ├── dac_driver.h
│ │ └── dac_driver.c
│ └── tim_driver/
│ ├── tim_driver.h
│ └── tim_driver.c
├── User/
│ ├── main.c
│ └── sine_table.h
└── document/
└── readme.md
3. DAC 驱动实现(dac_driver.h)
/**
* @file dac_driver.h
* @brief DAC驱动头文件
* @version 1.0
* @date 2025-04-01
* @author Ye_Huai
* @brief 本文件包含DAC初始化和输出函数声明
*/
#ifndef __DAC_DRIVER_H
#define __DAC_DRIVER_H
#include "stm32f10x.h"
/**
* @brief 初始化DAC1通道
* @details 配置DAC为触发模式,使用TIM2更新事件触发
*/
void DAC_Init_Configuration(void);
/**
* @brief 设置DAC输出值
* @param dacValue 输出值(0-4095)
*/
void DAC_SetValue(uint16_t dacValue);
#endif
4. 定时器驱动(tim_driver.h)
/**
* @file tim_driver.h
* @brief 定时器驱动头文件
* @version 1.0
* @date 2025-04-01
* @author Ye_Huai
* @brief 本文件包含TIM2初始化和频率设置函数声明
*/
#ifndef __TIM_DRIVER_H
#define __TIM_DRIVER_H
#include "stm32f10x.h"
/**
* @brief 初始化TIM2为触发模式
* @param frequency 输出频率(Hz)
* @note 最大频率受限于系统时钟
*/
void TIM2_Init(uint32_t frequency);
#endif
5. 正弦波表(sine_table.h)
/**
* @file sine_table.h
* @brief 正弦波表头文件
* @version 1.0
* @date 2025-04-01
* @author Ye_Huai
* @brief 本文件包含正弦波查找表定义
*/
#ifndef __SINE_TABLE_H
#define __SINE_TABLE_H
#include "stm32f10x.h"
/**
* @brief 正弦波查找表(256点)
* @details 数值范围:0-4095(对应0-3.3V)
*/
extern const uint16_t sine_table[256];
#endif
6. 主程序逻辑(main.c)
/**
* @file main.c
* @brief 正弦波生成主程序
* @version 1.0
* @date 2025-04-01
* @author Ye_Huai
* @brief 本文件实现正弦波生成功能,支持频率调节
*/
#include "stm32f10x.h"
#include "Drivers/dac_driver/dac_driver.h"
#include "Drivers/tim_driver/tim_driver.h"
#include "sine_table.h"
/**
* @brief 当前正弦波频率(Hz)
*/
#define SINE_FREQUENCY 1000
int main(void)
{
// 初始化DAC和定时器
DAC_Init_Configuration();
TIM2_Init(SINE_FREQUENCY);
while (1)
{
// 自动触发DAC更新
}
}
五、关键代码解析
1. 正弦波表生成(sine_table.c)
/**
* @file sine_table.c
* @brief 正弦波表实现文件
* @version 1.0
* @date 2025-04-01
* @author Ye_Huai
* @brief 本文件生成256点正弦波查找表
*/
#include "sine_table.h"
#include <math.h>
const uint16_t sine_table[256] = {
#define PI 3.1415926535
[0 ... 255] = (uint16_t)(2048 * (1 + sin((2 * PI * _I) / 256)))
};
2. DAC 初始化(dac_driver.c)
/**
* @file dac_driver.c
* @brief DAC驱动实现文件
* @version 1.0
* @date 2025-04-01
* @author Ye_Huai
* @brief 本文件实现DAC初始化和输出功能
*/
#include "dac_driver.h"
void DAC_Init_Configuration(void)
{
DAC_InitTypeDef DAC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA和DAC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_DAC, ENABLE);
// 配置PA4为模拟输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 初始化DAC通道1
DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
// 使能DAC通道1
DAC_Cmd(DAC_Channel_1, ENABLE);
}
void DAC_SetValue(uint16_t dacValue)
{
DAC_SetChannel1Data(DAC_Align_12b_R, dacValue);
}
3. 定时器配置(tim_driver.c)
/**
* @file tim_driver.c
* @brief 定时器驱动实现文件
* @version 1.0
* @date 2025-04-01
* @author Ye_Huai
* @brief 本文件实现TIM2初始化和频率设置功能
*/
#include "tim_driver.h"
#include "sine_table.h"
void TIM2_Init(uint32_t frequency)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 使能TIM2时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 计算预分频系数和周期
uint32_t period = SystemCoreClock / frequency / 256 - 1;
uint32_t prescaler = 1;
// 配置TIM2基本参数
TIM_TimeBaseStructure.TIM_Period = period;
TIM_TimeBaseStructure.TIM_Prescaler = prescaler;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 配置触发输出
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = period / 2;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
// 使能TIM2和触发输出
TIM_Cmd(TIM2, ENABLE);
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_OC1Ref);
}
六、调试与测试
-
示波器观察:
- 通道 1 连接 PA4
- 设置垂直档位:1V/div
- 设置时基:根据频率调整(如 1kHz 对应 1ms/div)
-
频率调节测试:
// 动态调节频率示例 #define SINE_FREQUENCY 500 // 修改此处测试不同频率
-
波形失真分析:
- 检查电源稳定性
- 确认正弦波表精度(256 点 vs 512 点)
- 检查 DAC 输出缓冲配置
七、优化建议
-
频率范围扩展:
// 通过调整预分频系数扩展频率范围 #define MAX_FREQUENCY 100000 // 100kHz
-
幅度调节:
// 在正弦波表生成时调整幅度 #define AMPLITUDE 1500 // 0-2048 [0 ... 255] = (uint16_t)(AMPLITUDE * (1 + sin(...)))
-
低功耗优化:
- 使用 TIM2 的低功耗模式
- 关闭未使用的外设时钟
扩展应用:
- 结合 ADC 实现波形采集与回放
- 添加按键调节频率和幅度
- 通过串口输出波形参数
- 实现多通道波形合成
工具推荐:
- Mathematica/Matlab:生成高精度正弦波表
- CubeMX:快速生成外设初始化代码
- DSO-X 2002A:实时观察波形细节