news 2026/6/7 2:58:20

手把手教你用STM32F103的DAC+DMA输出正弦波(附完整代码和波形分析)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手教你用STM32F103的DAC+DMA输出正弦波(附完整代码和波形分析)

STM32F103 DAC+DMA正弦波生成实战指南:从原理到示波器验证

第一次接触STM32的DAC功能时,看着开发板上那两个神秘的模拟输出引脚,我总好奇它们能否像函数发生器那样输出流畅的正弦波。直到某次智能家居项目中需要生成音频测试信号,才真正深入研究了DAC与DMA的黄金组合。本文将分享如何用STM32F103的片上DAC配合DMA控制器,实现高效、低CPU占用的正弦波输出方案。不同于简单的代码罗列,我会带你理解每个配置参数背后的设计考量,并教你用Python验证波形数据、用示波器捕捉真实输出效果。

1. 硬件基础与工程准备

STM32F103C8T6芯片内置两个12位DAC通道,最大转换速率可达1MHz。但直接通过CPU写入DAC数据寄存器会产生两个问题:一是波形更新间隔不稳定,二是会占用大量CPU资源。这就是我们需要引入定时器触发和DMA传输的原因。

开发环境准备清单

  • STM32F103C8T6开发板(如BluePill)
  • ST-Link调试器
  • Keil MDK或STM32CubeIDE
  • 示波器(带宽≥20MHz)
  • Python环境(用于波形验证)

注意:确保开发板的VDDA和VSSA引脚已正确连接3.3V和GND,这是DAC工作的电压基准。

配置工程时,需要开启以下外设时钟:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);

GPIO配置为模拟输入模式(AIN)至关重要,虽然DAC输出不需要输入功能,但这个模式会禁用内部上拉/下拉电阻,确保输出信号纯净:

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure);

2. 正弦波数据生成与验证

DAC本身没有内置正弦波发生器,我们需要预先计算好波形数据点。一个周期采样32点的12位正弦数组如下:

uint16_t Sine12bit[32] = { 2047, 2447, 2831, 3185, 3498, 3750, 3939, 4056, 4095, 4056, 3939, 3750, 3495, 3185, 2831, 2447, 2047, 1647, 1263, 909, 599, 344, 155, 38, 0, 38, 155, 344, 599, 909, 1263, 1647 };

数据生成原理

  • 2047对应0V(12位DAC的中间值)
  • 4095对应3.3V
  • 每个点计算公式:2047 + 2047 * sin(2π*i/32),其中i=0~31

用Python可以直观验证波形质量:

import matplotlib.pyplot as plt import numpy as np points = 32 x = np.linspace(0, 2*np.pi, points) y = 2047 + 2047 * np.sin(x) plt.plot(x, y, 'bo-') plt.show()

实际项目中,可以通过以下方法优化波形:

  • 增加采样点数(如128点)提升分辨率
  • 添加谐波补偿算法改善THD(总谐波失真)
  • 使用查表法替代实时计算

3. DMA双通道配置技巧

STM32的DAC支持双通道同步更新,这需要将两个通道的数据打包成32位字。低位16位是通道1数据,高位16位是通道2数据:

uint32_t DualSine12bit[32]; for(int i=0; i<32; i++) { DualSine12bit[i] = (Sine12bit[i] << 16) | Sine12bit[i]; }

DMA配置的关键参数解析:

参数设置值说明
PeripheralBaseAddr0x40007420DAC双通道数据寄存器地址
MemoryBaseAddrDualSine12bit数组地址波形数据源
DirectionPeripheralDST内存到外设传输
BufferSize32一个周期的采样点数
PeripheralIncDisableDAC寄存器地址固定
MemoryIncEnable数组地址自动递增
ModeCircular循环模式实现连续输出

完整初始化代码:

DMA_InitStructure.DMA_PeripheralBaseAddr = 0x40007420; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DualSine12bit; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = 32; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_Init(DMA2_Channel4, &DMA_InitStructure);

4. 定时器触发与系统联调

TIM8作为触发源,其更新频率决定了正弦波的输出频率。计算公式为:

输出频率 = TIM8时钟 / (TIM_Prescaler + 1) / (TIM_Period + 1) / 采样点数

例如,当TIM8时钟为72MHz,Prescaler=0,Period=71时:

72,000,000 / 1 / 72 / 32 ≈ 31.25kHz

实际配置示例:

TIM_TimeBaseStructure.TIM_Period = 71; TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure); TIM_SelectOutputTrigger(TIM8, TIM_TRGOSource_Update);

常见问题排查表

现象可能原因解决方案
无输出DMA未使能检查DMA_Cmd和DAC_DMACmd调用
波形畸变缓冲区太小增加采样点数到64或128
频率不准定时器配置错误重新计算TIM_Period值
噪声大未禁用输出缓冲设置DAC_OutputBuffer_Disable

示波器测量时,如果发现波形阶梯明显,可以:

  1. 在DAC输出端添加RC低通滤波器(如1kΩ+100nF)
  2. 提高采样点数并相应降低输出频率
  3. 启用DAC的输出缓冲(但会限制最高输出电压)

5. 进阶应用与性能优化

当系统需要同时处理其他任务时,可以考虑以下优化策略:

动态频率调整

void Set_Sine_Freq(uint32_t freq) { uint32_t clock = 72000000; // TIM8时钟频率 uint32_t arr = (clock / (32 * freq)) - 1; TIM8->ARR = arr; }

双缓冲区技巧

  1. 配置两个波形缓冲区
  2. DMA完成中断中切换缓冲区
  3. 允许实时更新波形数据而不影响输出

输出幅度控制

void Set_Sine_Amplitude(float ratio) { for(int i=0; i<32; i++) { DualSine12bit[i] = (uint32_t)(Sine12bit[i] * ratio) << 16 | (uint32_t)(Sine12bit[i] * ratio); } }

在电机控制测试中,我发现将DMA缓冲区设置为128点,配合TIM8的精确触发,可以生成THD<1%的优质正弦波。这种方案比PWM+滤波更高效,特别适合需要同时输出多路模拟信号的场合。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/7 2:56:17

【字节跳动】本文披露了字节跳动巨量引擎AdOS系统的150项核心工业级参数配置,涵盖广告竞价、机器学习推理、系统调度、硬件管理等关键维度。主要技术参数包括:广告内核初始化栈帧28KB、CTR/CVR前

巨量引擎 AdOS 底层工业级参数 500 条&#xff08;字节内部生产级固化&#xff09; 本文披露了字节跳动巨量引擎AdOS系统的150项核心工业级参数配置&#xff0c;涵盖广告竞价、机器学习推理、系统调度、硬件管理等关键维度。主要技术参数包括&#xff1a;广告内核初始化栈帧28K…

作者头像 李华
网站建设 2026/6/7 2:55:14

AKShare的stock_zh_a_hist函数避坑指南:参数错误、数据缓存与批量处理实战

AKShare股票数据获取实战&#xff1a;从参数解析到高效缓存的完整解决方案在金融数据分析领域&#xff0c;获取准确、完整的股票历史数据是量化研究和策略回测的基础。AKShare作为新兴的金融数据接口库&#xff0c;其stock_zh_a_hist函数提供了便捷的A股历史数据获取途径。但在…

作者头像 李华
网站建设 2026/6/7 2:53:04

Onekey Steam清单下载器:5分钟掌握高效游戏备份与管理

Onekey Steam清单下载器&#xff1a;5分钟掌握高效游戏备份与管理 【免费下载链接】Onekey Onekey Steam Depot Manifest Downloader 项目地址: https://gitcode.com/gh_mirrors/one/Onekey 你是否曾因Steam下载速度缓慢而焦虑&#xff1f;是否在更换电脑时面对数百GB的…

作者头像 李华