基于 STM32 的正弦波生成实践 —— 从原理到代码实现

目录

一、引言

二、硬件准备

三、生成方法对比

四、DAC 正弦波生成实现

1. 数学原理

2. 代码结构

3. DAC 驱动实现(dac_driver.h)

4. 定时器驱动(tim_driver.h)

5. 正弦波表(sine_table.h)

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

6. 主程序逻辑(main.c)

五、关键代码解析

1. 正弦波表生成(sine_table.c)

2. DAC 初始化(dac_driver.c)

3. 定时器配置(tim_driver.c)

六、调试与测试

七、优化建议


一、引言

在嵌入式系统中,正弦波生成是信号处理、通信系统、电机控制等领域的基础功能。本文将以 STM32F103ZET6 为例,介绍三种生成正弦波的方法:DAC 直接输出、PWM 合成和定时器触发 ADC 采集,重点探讨 DAC 方案的实现细节,并结合模块化编程和 Doxygen 注释规范进行代码解析。

二、硬件准备

  1. 开发板:STM32F103ZET6 核心板
  2. 外设
    • DAC1 通道(PA4)
    • TIM2 定时器(触发 DAC 更新)
    • 示波器(观察输出波形)
  3. 硬件连接
    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. 示波器观察

    • 通道 1 连接 PA4
    • 设置垂直档位:1V/div
    • 设置时基:根据频率调整(如 1kHz 对应 1ms/div)
  2. 频率调节测试

    // 动态调节频率示例
    #define SINE_FREQUENCY 500  // 修改此处测试不同频率
    
  3. 波形失真分析

    • 检查电源稳定性
    • 确认正弦波表精度(256 点 vs 512 点)
    • 检查 DAC 输出缓冲配置

七、优化建议

  1. 频率范围扩展

    // 通过调整预分频系数扩展频率范围
    #define MAX_FREQUENCY 100000  // 100kHz
    
  2. 幅度调节

    // 在正弦波表生成时调整幅度
    #define AMPLITUDE 1500  // 0-2048
    [0 ... 255] = (uint16_t)(AMPLITUDE * (1 + sin(...)))
    
  3. 低功耗优化

    • 使用 TIM2 的低功耗模式
    • 关闭未使用的外设时钟

扩展应用

  • 结合 ADC 实现波形采集与回放
  • 添加按键调节频率和幅度
  • 通过串口输出波形参数
  • 实现多通道波形合成

工具推荐

  • Mathematica/Matlab:生成高精度正弦波表
  • CubeMX:快速生成外设初始化代码
  • DSO-X 2002A:实时观察波形细节