STM32F103 DAC实战:不写一行循环代码,用CubeMX配置双通道正弦波发生器
STM32F103 DAC实战:不写一行循环代码,用CubeMX配置双通道正弦波发生器
在嵌入式开发领域,STM32系列微控制器因其强大的性能和丰富的外设资源而广受欢迎。其中,数字模拟转换器(DAC)作为连接数字世界与模拟世界的重要桥梁,在音频处理、信号生成等场景中扮演着关键角色。本文将展示如何利用STM32CubeMX这一强大的图形化配置工具,在不编写任何底层驱动代码的情况下,快速实现双通道正弦波发生器的开发。
1. 环境准备与项目创建
首先确保已安装STM32CubeMX和对应的IDE(如Keil MDK或IAR Embedded Workbench)。打开CubeMX后,选择STM32F103系列芯片型号(如STM32F103C8T6),系统会自动加载该芯片的外设资源视图。
提示:建议使用最新版本的STM32CubeMX,以获得最完整的功能支持和最佳的代码生成体验。
创建新项目时,需要注意几个关键配置点:
- 时钟配置 :确保系统时钟设置正确,这将影响后续定时器和DAC的工作频率
- 调试接口 :通常选择SWD模式,便于后续程序下载和调试
- 引脚分配 :PA4和PA5默认对应DAC通道1和通道2,避免与其他功能冲突
2. DAC外设图形化配置
在CubeMX的Pinout & Configuration界面中,找到DAC外设进行配置:
-
启用DAC通道 :
- 勾选DAC Channel1和Channel2
- 设置输出缓冲(Output Buffer)为Disable,以获得更精确的输出
-
触发源配置 :
- 选择TIM6作为触发源(后续会配置定时器)
- 触发模式选择为Timer Trigger
-
DMA设置 :
- 为DAC Channel2添加DMA请求
- 配置DMA为循环模式(Circular),方向为内存到外设(Memory to Peripheral)
- 数据宽度选择Word(32位)
// CubeMX自动生成的DAC初始化代码片段
static void MX_DAC_Init(void)
{
DAC_ChannelConfTypeDef sConfig = {0};
hdac.Instance = DAC;
if (HAL_DAC_Init(&hdac) != HAL_OK)
{
Error_Handler();
}
sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;
if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
}
3. 定时器与DMA配置
定时器负责以固定频率触发DAC转换,而DMA则负责自动将波形数据从内存传输到DAC数据寄存器。
3.1 定时器配置
-
启用TIM6(基本定时器)
-
设置预分频器(Prescaler)和周期(Counter Period)值
- 假设系统时钟为72MHz,若希望生成1kHz正弦波(32点采样)
- 定时器频率 = 1kHz × 32 = 32kHz
- 预分频器 = 0(不分频)
- 自动重装载值 = (72MHz / 32kHz) - 1 = 2249
-
启用定时器触发输出(Trigger Output)
// 定时器配置示例
static void MX_TIM6_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim6.Instance = TIM6;
htim6.Init.Prescaler = 0;
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.Period = 2249;
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
3.2 DMA配置
在CubeMX的DMA配置界面中,为DAC Channel2添加DMA通道:
| 参数 | 值 | 说明 |
|---|---|---|
| Direction | Memory to Peripheral | 数据从内存传输到外设 |
| Increment Address | Memory | 内存地址自动递增 |
| Data Width | Word | 32位数据传输 |
| Mode | Circular | 循环模式,自动重复传输 |
4. 正弦波数据准备与工程集成
4.1 生成正弦波数据
可以使用Python、MATLAB或在线工具生成正弦波采样数据。以下是一个Python示例:
import numpy as np
points = 32 # 采样点数
amplitude = 2047 # 12位DAC最大值4095的一半
offset = 2048 # 使波形居中
sine_wave = amplitude * np.sin(np.linspace(0, 2*np.pi, points, endpoint=False)) + offset
dual_channel_data = [(int(sine_wave[i]) << 16) | int(sine_wave[i]) for i in range(points)]
print("const uint32_t DualSine12bit[32] = {")
print(", ".join([f"0x{x:08X}" for x in dual_channel_data]))
print("};")
4.2 在工程中使用波形数据
将生成的数据数组添加到工程中,通常放在main.c或单独的波形数据头文件中:
/* USER CODE BEGIN PV */
const uint32_t DualSine12bit[32] = {
0x00000800, 0x09280928, 0x11A711A7, 0x18F218F2, 0x1EAD1EAD,
0x229E229E, 0x24922492, 0x24722472, 0x224F224F, 0x1E4E1E4E,
0x18A918A9, 0x11B711B7, 0x09B909B9, 0x00F800F8, 0xF7B0F7B0,
0xEF0CEF0C, 0xE73AE73A, 0xE0A4E0A4, 0xDB9EDB9E, 0xD86BD86B,
0xD72BD72B, 0xD7E5D7E5, 0xDA7CDA7C, 0xDEB1DEB1, 0xE42AE42A,
0xEA7EEA7E, 0xF13DF13D, 0xF7E5F7E5, 0xFDF8FDF8, 0x030F030F,
0x06E006E0, 0x093F093F
};
/* USER CODE END PV */
5. 启动波形生成
在main函数中,添加以下代码启动DAC和DMA传输:
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start(&htim6); // 启动定时器
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_2, (uint32_t*)DualSine12bit, 32, DAC_ALIGN_WORD_B); // 启动DAC DMA传输
HAL_DAC_Start(&hdac, DAC_CHANNEL_1); // 启动DAC通道1(软件触发)
/* USER CODE END 2 */
6. 调试与优化
6.1 波形验证
使用示波器观察PA4和PA5引脚输出,应能看到两个同步的正弦波信号。如果波形有失真,可以尝试:
- 调整定时器频率,确保不超过DAC的转换速率
- 检查DMA传输是否正常,没有溢出或错误
- 验证正弦波数据是否正确
6.2 性能优化
- 采样点数 :增加采样点数(如64或128)可提高波形质量,但会占用更多内存
- 双缓冲技术 :使用DMA双缓冲模式,可在波形播放时动态更新另一半缓冲区
- 动态频率调整 :通过修改定时器重装载值,实现输出频率的动态调整
7. 扩展应用
基于此基础框架,可以轻松实现更多高级功能:
- 多波形切换 :预存多种波形数据(方波、三角波等),通过按键或通信接口切换
- 幅度调制 :动态调整DAC输出值,实现幅度调制
- 频率扫描 :通过改变定时器触发频率,实现扫频功能
- 音频应用 :结合DAC的DMA传输,实现简单的音频播放功能
在实际项目中,这种基于CubeMX的配置方法大幅减少了底层驱动开发时间,让开发者能够专注于应用逻辑的实现。通过合理利用STM32的外设协同工作能力,仅用少量配置就能实现复杂的模拟信号生成功能。
更多推荐



所有评论(0)