用STM32CubeMX的DAC输出一个正弦波:从配置定时器触发到示波器验证(STM32F103C8T6)
2026/6/5 10:30:06 网站建设 项目流程

STM32CubeMX实战:用DAC+DMA生成高精度正弦波信号

在嵌入式开发中,信号生成是一个常见需求,无论是音频合成、电机控制测试还是传感器模拟,都需要稳定可靠的波形输出。STM32的DAC模块配合DMA和定时器触发,能够实现高效、精确的波形生成,完全由硬件自动完成,不占用CPU资源。本文将带你从CubeMX配置到示波器验证,完整实现一个可调频率的正弦波发生器。

1. 硬件设计与工程创建

使用STM32F103C8T6(Blue Pill开发板)时,DAC通道1默认对应PA4引脚。为获得最佳信号质量,建议:

  • 在PA4引脚串联一个100Ω电阻作为保护
  • 添加一个0.1μF的滤波电容到地
  • 确保VDDA和VREF+连接稳定的3.3V电源
  • 若驱动低阻抗负载,可增加电压跟随器电路

工程创建关键步骤

  1. 在CubeMX中选择STM32F103C8系列
  2. 配置HSE时钟为8MHz,PLL倍频至72MHz系统时钟
  3. 调试接口选择Serial Wire(避免锁死芯片)
  4. 将PA4设置为DAC_OUT1(自动配置为模拟输入模式)

提示:使用独立电源为模拟部分供电时,需确保数字地(DGND)和模拟地(AGND)单点连接

2. DAC与定时器协同配置

2.1 DAC参数设置

在CubeMX的Analog→DAC选项卡中:

// DAC通道1配置参数 Mode: Normal Mode Output Buffer: Enabled Trigger: Timer 6 Trigger Out Event

关键参数解析

参数选项作用
输出缓冲使能增强驱动能力,但限制输出范围为0.2V~3.1V
触发源TIM6 TRGO定时器自动触发DAC转换
数据对齐右对齐12位分辨率时最常用

2.2 定时器配置

选择TIM6作为触发源(基本定时器更适合简单波形生成):

// TIM6配置 Prescaler: 71 // 72MHz/(71+1)=1MHz计数器时钟 Counter Mode: Up // 向上计数模式 Period: 99 // 自动重装载值(ARR) Trigger Output: Update // 更新事件作为触发输出

频率计算公式: $$ f_{DAC} = \frac{f_{TIM}}{(ARR+1)} = \frac{1MHz}{100} = 10kHz $$

这意味着DAC将以10kHz的速率更新输出值。要改变输出正弦波频率,需调整ARR值或预分频系数。

3. 正弦波表生成与DMA配置

3.1 生成正弦波数据表

使用Python生成优化后的正弦波样本(保存为sine_wave.h):

import numpy as np SAMPLES = 128 # 样本点数 amplitude = 2047 # 12位DAC中间值范围(0-4095) offset = 2048 # 添加直流偏置(1.65V) sine = np.sin(2 * np.pi * np.arange(SAMPLES) / SAMPLES) * amplitude + offset wave_table = np.clip(sine, 0, 4095).astype(np.uint16) print("const uint16_t sine_wave[{}] = {{".format(SAMPLES)) print(", ".join(map(str, wave_table))) print("};")

样本点选择考量

  • 128点:平衡内存占用与波形平滑度
  • 直流偏置:避免负电压(单电源系统)
  • 限幅处理:确保不超出DAC量程

3.2 DMA流配置

在CubeMX的DAC配置中启用DMA:

  1. 添加DMA请求:DAC1通道1
  2. 配置参数:
    • Mode: Circular(循环模式)
    • Data Width: Half Word(16位)
    • Increment Address: Enable(存储器地址递增)

生成代码后,DMA会自动将波形数据从内存传输到DAC数据寄存器。

4. 代码实现与优化

4.1 初始化代码分析

CubeMX生成的初始化代码包含三个关键部分:

// DAC初始化片段 hdac.Instance = DAC; HAL_DAC_Init(&hdac); // DMA初始化片段 hdma_dac1.Instance = DMA1_Channel3; hdma_dac1.Init.Direction = DMA_MEMORY_TO_PERIPH; // TIM6初始化片段 htim6.Instance = TIM6; htim6.Init.Prescaler = 71; htim6.Init.Period = 99;

4.2 用户代码实现

在main.c中添加应用逻辑:

// 包含波形数据头文件 #include "sine_wave.h" int main(void) { HAL_Init(); SystemClock_Config(); MX_DAC_Init(); MX_TIM6_Init(); MX_DMA_Init(); // 启动DAC的DMA传输 HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)sine_wave, sizeof(sine_wave)/sizeof(uint16_t), DAC_ALIGN_12B_R); // 启动定时器触发 HAL_TIM_Base_Start(&htim6); while (1) { // 主循环可添加频率调整逻辑 } }

实时频率调整技巧

// 动态改变正弦波频率 void set_sine_freq(uint32_t freq) { uint32_t tim_clock = 1000000; // TIM6时钟1MHz uint32_t arr = (tim_clock / (freq * 128)) - 1; __HAL_TIM_SET_AUTORELOAD(&htim6, arr); }

5. 示波器验证与性能优化

5.1 基础测试步骤

  1. 连接PA4到示波器探头
  2. 调整示波器时基观察单个周期
  3. 测量峰峰值和频率
  4. 检查波形失真情况

典型问题排查

现象可能原因解决方案
波形阶梯明显样本点太少增加SAMPLES值
顶部/底部削波超出DAC量程减小振幅或启用缓冲
频率不准时钟配置错误检查TIM6时钟源

5.2 高级优化技巧

内存优化版波形生成

// 实时计算正弦值(节省内存,增加CPU负载) uint16_t compute_sine(uint32_t index) { static const uint16_t quarter_sine[32] = {...}; uint8_t pos = index % 32; if(index < 32) return quarter_sine[pos] + 2048; else if(index < 64) return quarter_sine[31-pos] + 2048; else if(index < 96) return 4095 - quarter_sine[pos] - 2048; else return 4095 - quarter_sine[31-pos] - 2048; }

使用定时器中断动态更新波形

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim == &htim6) { static uint32_t phase; HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, compute_sine(phase)); phase = (phase + 1) % 128; } }

6. 扩展应用:多波形发生器

基于现有框架,只需修改波形数据表即可实现不同波形输出:

方波生成表

const uint16_t square_wave[128] = { [0...63] = 4095, // 高电平 [64...127] = 0 // 低电平 };

三角波生成算法

uint16_t compute_triangle(uint32_t index) { uint8_t pos = index % 128; return (pos < 64) ? pos * 64 : (127 - pos) * 64; }

通过组合不同波形和调制技术,可以进一步开发出:

  • AM/FM调制信号源
  • 任意波形发生器
  • 音频合成器
  • 控制系统测试信号源

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询