STM32CubeMX教程18 DAC - DMA输出自定义波形
1、准备材料
STM32CubeMX软件(Version 6.10.0)
keil µVision5 IDE(MDK-Arm)
一台示波器
逻辑分析仪nanoDLA
2、实验目标
使用STM32CubeMX软件配置STM32F407开发板的DAC OUT1实现输出0-3.3V 周期为12.8ms的正弦波形
3、实验流程
3.0、前提知识
由于STM32F407的两个DAC输出通道只能自动生成三角波和噪声波,因此如果想要输出其他的波形可以自己手动定义一个周期内DAC要输出的值,并选择定时器的更新事件作为DAC输出的触发源按顺序输出
这样按照波形采样值的顺序,在每一个触发源到来的时候,手动指定DAC将要输出的值,理论上就可以输出任何我们想要输出的波形,比如正弦波,本实验将以正弦波为例,讲解如何通过DAC的DMA输出正弦波型
当DAC参考电源引脚VREF+接VDDA(3.3V)时,可设置的DAC输出寄存器值范围为0~4095,而DAC的输出范围为0-3.3V,要输出的正弦波sin(x)波形幅值范围为-1~1,因此可以对该波形做一些平移伸缩,将其幅值范围缩放到DAC设置范围0~4095内,变换后的正弦波公式为:y=2047*(sin(x)+1)
在该正弦波形的一个周期0-2pi内平均取128个采样点,然后按照时间先后顺序定义在数组中,每当0.1ms触发源到来的时候,我们就递归的从数组中取出一个值将其设置为DAC的输出值,直到128个采样点全部设置完毕,然后再反复从第一个重新设置,这样就可以大致实现正弦波型
因为需要频繁的从内存取出数据然后写入DAC外设,因此这里比较合适的做法是使用DMA的方式进行,通过上述设置的DAC输出的正弦波形的周期应该为0.1ms*128=12.8ms,正弦波的幅值范围应该为0-3.3V
3.1、CubeMX相关配置
3.1.0、工程基本配置
打开STM32CubeMX软件,单击ACCESS TO MCU SELECTOR选择开发板MCU(选择你使用开发板的主控MCU型号),选中MCU型号后单击页面右上角Start Project开始工程,具体如下图所示


开始工程之后在配置主页面System Core/RCC中配置HSE/LSE晶振,在System Core/SYS中配置Debug模式,具体如下图所示


详细工程建立内容读者可以阅读“STM32CubeMX教程1 工程建立”
3.1.1、时钟树配置
系统时钟使用8MHz外部高速时钟HSE,HCLK、PCLK1和PCLK2均设置为STM32F407能达到的最高时钟频率,具体如下图所示

3.1.2、外设参数配置
在Pinout & Configuration页面左边功能分类栏目Analog中单击其中DAC
在Mode中勾选OUT1 Configuration
选择TIM6的外部触发事件作为DAC OU1输出的触发源,不选择波形生成模式,因为本实验要生成自定义波形
具体配置如下图所示

单击Configuration中的DMA Settings选项卡对DAC的DMA请求进行设置
单击ADD按键增加DMA请求,这里可选的只有一个DAC1
选择想要使用的DMA Stream,并设置优先级,将DMA请求模式设置为循环模式,外设地址不增加,内存地址递增,数据宽度选择字Word
上述配置如下图所示

3.1.3、外设中断配置
在Pinout & Configuration页面左边System Core/NVIC中勾选DMA1 Stream5 全局中断,然后选择合适的中断优先级即可

3.2、生成代码
3.2.0、配置Project Manager页面
单击进入Project Manager页面,在左边Project分栏中修改工程名称、工程目录和工具链,然后在Code Generator中勾选“Gnerate peripheral initialization as a pair of 'c/h' files per peripheral”,最后单击页面右上角GENERATE CODE生成工程,具体如下图所示


详细Project Manager配置内容读者可以阅读“STM32CubeMX教程1 工程建立”实验3.4.3小节
3.2.1、外设初始化调用流程
在生成的工程代码主函数中新增了MX_DMA_Init()函数,该函数对DAC使用的DMA1时钟使能,由于启用了该DMA的中断,因此还对中断优先级及使能进行了配置,如下图所示

DAC的初始化调用流程与“STM32CubeMX教程16 DAC - 输出3.3V内任意电压”实验一致,只是因为本实验配置了DMA,因此在HAL_DAC_MspInit()函数中增加了对使用的DAC1 DMA请求的相关配置代码,如下图所示

3.2.2、外设中断调用流程
DMA全局中断事件回调函数为一个函数指针,当使用HAL_DAC_Start_DMA()函数启动DAC传输时,会将DMA全局中断事件回调函数指针指向具体的函数,这里指向了DAC_DMAConvCpltCh1()函数
在DAC_DMAConvCpltCh1()函数中最终调用了DAC OU1 DMA传输完成中断回调函数HAL_DAC_ConvCpltCallbackCh1(),该函数为虚函数,需要用户重新实现
启用DMA的外设中断调用流程可参考“STM32CubeMX教程12 DMA 直接内存读取”实验3.2.2、外设中断调用流程小节,上述具体过程如下图所述

3.2.3、添加其他必要代码
采集正弦波y=2047*(sin(x)+1)的一个周期2pi内n个采样点,并将其定义在一个uint32_t 数组中,笔者这里定义了128个采样点
为什么非要正弦波函数为y=2047*(sin(x)+1)?
因为DAC的输出范围为0~4095,而sin(x)的输出范围为-1~1,因此需要采集的正弦波采样点最好缩放到0-4095范围,这样输出的波形更好显示
源代码如下所示 (注释1)
/*正弦波数据,12bit,1个周期128个点, 0-4095之间变化*/
const uint32_t userWave[] =
{
2047, 2147, 2248, 2347, 2446, 2544, 2641, 2737,
2830, 2922, 3012, 3099, 3184, 3266, 3346, 3422,
3494, 3564, 3629, 3691, 3749, 3803, 3852, 3897,
3938, 3974, 4006, 4033, 4055, 4072, 4084, 4092,
4094, 4092, 4084, 4072, 4055, 4033, 4006, 3974,
3938, 3897, 3852, 3803, 3749, 3691, 3629, 3564,
3494, 3422, 3346, 3266, 3184, 3099, 3012, 2922,
2830, 2737, 2641, 2544, 2446, 2347, 2248, 2147,
2047, 1947, 1846, 1747, 1648, 1550, 1453, 1357,
1264, 1172, 1082, 995 , 910 , 828 , 748 , 672 ,
600 , 530 , 465 , 403 , 345 , 291 , 242 , 197 ,
156 , 120 , 88 , 61 , 39 , 22 , 10 , 2 ,
0 , 2 , 10 , 22 , 39 , 61 , 88 , 120 ,
156 , 197 , 242 , 291 , 345 , 403 , 465, 530 ,
600 , 672 , 748 , 828 , 910 , 995 , 1082, 1172,
1264, 1357, 1453, 1550, 1648, 1747, 1846, 1947
};
在dac.c中重新实现DAC OU1 DMA传输完成中断回调函数HAL_DAC_ConvCpltCallbackCh1(),源代码如下
/*DAC OUT1 DMA传输完成中断回调函数*/
void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef *hdac)
{
/*翻转RED_LED引脚状态*/
HAL_GPIO_TogglePin(RED_LED_GPIO_Port,RED_LED_Pin);
}
在main.c文件主函数main中以DMA方式启动DAC输出,源代码如下
HAL_DAC_Start_DMA(&hdac,DAC_CHANNEL_1,userWave,128,DAC_ALIGN_12B_R);
HAL_TIM_Base_Start(&htim6);
4、常用函数
/*以DMA启动DAC输出*/
HAL_StatusTypeDef HAL_DAC_Start_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel, const uint32_t *pData, uint32_t Length,uint32_t Alignment)
/*停止以DMA启动的DAC输出*/
HAL_StatusTypeDef HAL_DAC_Stop_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel)
/*DAC OUT1 DMA传输完成时间中断回调函数*/
void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef *hdac)
5、烧录验证
烧录程序,单片机上电后,将示波器的探头挂钩与DAC OUT1引脚PA4相连接,接地环与开发板上的GND引脚连接,将示波器每格电压幅值调节为1.00V,将每格子采集时间调节为10ms,然后开启示波器对DAC OU1输出的波形采集
因为定时器溢出时间为0.1ms,而DMA传输的数据为正弦波一个周期内的128个样本点,因此生成的正弦波周期为128*0.1ms=12.8ms,这与示波器采集到的正弦波波形周期一致,如下图所示为示波器采集到的正弦波形

在DAC OUT1 DMA传输完成时间中断回调函数中翻转了RED_LED(PF9)引脚的状态,经过上述分析知道,传输完成一次所需事件应该为输出正弦波形的周期,也即12.8ms,因此使用逻辑分析仪器采集PF9引脚的状态,发现PF9引脚确实12.8ms翻转一次状态,逻辑分析仪采集的波形如下图所示

6、注释详解
注释1:正弦波数组定义来源 DAC输出正弦波帖子
更多内容请浏览 STM32CubeMX+STM32F4系列教程文章汇总贴
STM32CubeMX教程18 DAC - DMA输出自定义波形的更多相关文章
- 【STM32H7教程】第60章 STM32H7的DAC应用之定时器触发实现DMA方式双通道波形
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第60章 STM32H7的DAC应用之定时器触发实 ...
- STM32 使用DMA+DAC+TIMER 输出正弦波
之前已经简单论述过,根据我个人菜鸟的了解与认识,对之前的知识进行整理回顾: DMA:我的理解就是一个通道,或者是一座桥梁.在静态内存到静态内存,或者外设到静态内存间的一个通讯的通道.建立这个通道的好处 ...
- python Django教程 之 模型(数据库)、自定义Field、数据表更改、QuerySet API
python Django教程 之 模型(数据库).自定义Field.数据表更改.QuerySet API 一.Django 模型(数据库) Django 模型是与数据库相关的,与数据库相关的代码 ...
- Directx11教程(18) D3D11管线(7)
原文:Directx11教程(18) D3D11管线(7) 光栅化阶段(RS)之后,将进入PS/OM阶段. 参考外文资料:http://fgiesen.wordpress.com/2011/07/01 ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(九):自定义菜单接口说明
上一篇<Senparc.Weixin.MP SDK 微信公众平台开发教程(八):通用接口说明>介绍了如何通过通用接口获取AccessToken,有了AccessToken,我们就可以来操作 ...
- asp.net mvc输出自定义404等错误页面,非302跳转。
朋友问到一个问题,如何输出自定义错误页面,不使用302跳转.当前页面地址不能改变. 还要执行一些代码等,生成一些错误信息,方便用户提交反馈. 500错误,mvc框架已经有现成解决方法: filters ...
- STM32F4_TIM输出PWM波形(可调频率、占空比)
Ⅰ.概述 上一篇文章关于STM32基本的计数原理明白之后,该文章是在其基础上进行拓展,讲述关于STM32比较输出的功能,以输出PWM波形为实例来讲述. 提供实例工程中比较实用的函数:只需要调用该函数, ...
- asp.net mvc输出自定义404等错误页面,非302跳转
朋友问到一个问题,如何输出自定义错误页面,不使用302跳转.当前页面地址不能改变. 还要执行一些代码等,生成一些错误信息,方便用户提交反馈. 500错误,mvc框架已经有现成解决方法: filte ...
- STM32 F4 DAC DMA Waveform Generator
STM32 F4 DAC DMA Waveform Generator Goal: generating an arbitrary periodic waveform using a DAC with ...
- [译]Vulkan教程(18)命令buffers
[译]Vulkan教程(18)命令buffers Command buffers 命令buffer Commands in Vulkan, like drawing operations and me ...
随机推荐
- AtCoder_abc328
A - Not Too Hard 题目链接 题目大意 给出\(N\)个数(\(S_1\) \(S_2\)...\(S_n\))和一个\(X\),输出所有小于等于\(X\)的\(S_i\)之和 解题思路 ...
- Vue入门(1)安装vue环境,创建Vue2 Vue3的项目并且安装IIS环境和发布到IIS
Vue环境的搭建 一. 背景 vue的运行方式有两种 一种是在页面引用vue的js包, 一种是搭建脚手架来vue框架. 我们在这里使用的是第二种方式. 需要安装的软件 npm 16.13.1 ...
- 如何根据月份查询每月Xxx的总数
我以我的项目根据月份查询每月新增会员的总数为例 Controller @GetMapping("/getMemberReport.do") public R getMemberRe ...
- KNN算法之KD树(K-dimension Tree)实现 K近邻查询
KD树是一种分割k维数据空间的数据结构,主要应用于多维空间关键数据的搜索,如范围搜索和最近邻搜索. KD树使用了分治的思想,对比二叉搜索树(BST),KD树解决的是多维空间内的最近点(K近点)问题.( ...
- 第三方登陆--QQ登陆--前后端分离版本
从零玩转第三方QQ登陆 下面有源码 第三方GITEE登陆 https://www.cnblogs.com/Yangbuyi/p/yangbuyi.html 在真正开始对接之前,我们先来聊一聊后台的方案 ...
- Pikachu漏洞靶场 RCE(远程命令执行/代码执行)
RCE 文章目录 RCE 概述 exec "ping" exec "eval" 概述 RCE(remote command/code execute),远程命令 ...
- Weblogic获取端口IP
Weblogic获取端口IP 获取端口IP只为了判断哪个节点 调用 private WebMBeanServer server=new WebMBeanServer(); synlog.info(&q ...
- spring与jndi(数据库连接)SpringBoot打war包并整合jsp
spring与jndi(数据库连接)SpringBoot打war包并整合jsp,这里的jndi指的是jdbc连接,不是其他的.有传统servlet+spring和springboot的jndi连接操作 ...
- 请注意,你的 Pulsar 集群可能有删除数据的风险
在上一篇 Pulsar3.0新功能介绍中提到,在升级到 3.0 的过程中碰到一个致命的问题,就是升级之后 topic 被删除了. 正好最近社区也补充了相关细节,本次也接着这个机会再次复盘一下,毕竟这是 ...
- 《代码整洁之道 Clean Code》学习笔记 Part 2 - 写出优雅的函数的10条建议
大师级程序员把系统当作故事来讲,而不是当作程序来写. TLDR 短小(不超过 20 行.缩进不超过 2 层) 只做一件事 保持在同一抽象层级 用多态替代 switch 取个好的函数名 函数参数越少越好 ...