STM32正弦波生成实践指南:原理详解与代码实现

目录

一、引言

二、硬件准备

三、生成方法对比

四、DAC 正弦波生成实现

1. 数学原理

2. 代码结构

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

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

5. 正弦波表(sine_table.h)

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. 外设
  3. DAC1 通道(PA4)
  4. TIM2 定时器(触发 DAC 更新)
  5. 示波器(观察输出波形)
  6. 硬件连接
    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. 示波器观察

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

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

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

    七、优化建议

    1. 频率范围扩展

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

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

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

    扩展应用

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

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

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32正弦波生成实践指南:原理详解与代码实现

    发表回复