DMA控制两路DAC同时输出指定波形

news2024/12/23 22:35:38

0. 实验目标

使用 DMA 控制 DAC1 和 DAC2 同时输出不一样的波形,实验平台STM32F407ZG

1. 原理部分

1.1 DMA

DMA 全称为:Direct Memory Access,即直接存储器访问。DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使CPU的效率大为提高。
STM32F407 最多有 2 个 DMA 控制器( DMA1 和 DMA2 ),两个 DMA 控制器总共有 16 个数据流。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个 DMA 请求的优先权。
使用DMA主要有以下几个配置项需要注意

  • DMA 通道选择:外设或者存储器是挂载在不同的DMA通道的不同流中的,查看手册找到要控制的外设挂载在哪里
  • DMA 外设地址:DMA所连接的外设的地址,下面会详细写如何查找
  • DMA 存储器 0 地址:若配置数据流方向为存储器到外设,则是要传给外设的数据的地址
  • DMA 传输方向:支持外设到存储器、存储器到外设和存储器到存储器传输的常规通道
  • 传输数据量:一次传输多少数据
  • 外设是否为增量模式:根据 DMA_SxCR 寄存器中 PINCMINC 位的状态,外设和存储器指针在每次传输后可以
    自动向后递增或保持常量。如果使能了递增模式,则根据在 DMA_ SxCR 寄存器 PSIZEMSIZE 位中编程的数据宽度,下一次传输的地址将是前一次传输的地址递增 1 (对于字节)、2 (对于半字)或 4 (对于字)。为了优化封装操作,可以不管 AHB 外设端口上传输的数据的大小,将外设地址的增量偏移大小固定下来。DMA_ SxCR 寄存器中的 PINCOS 位用于将增量偏移大小与外设 AHB 端口或 32 位地址(此时地址递增 4 )上的数据大小对齐。PINCOS 位仅对 AHB 外设端口有影响。
  • 存储器是否为增量模式:同上
  • 外设数据长度:用多少位的方式去解析或者发送数据
  • 存储器数据长度:若是从存储器给外设发送数据,则存储器和外设的数据长度应该一致
  • DMA 模式:普通模式或者循环模式,选择普通模式
  • 优先级:有低、中等、高、非常高的优先级可以选择
  • 是否使用FIFO:FIFO 用于在源数据传输到目标之前临时存储这些数据,这里不需要
  • 存储器突发传输模式:在直接模式下,数据流只能生成单次传输,而 MBURST[1:0] 和 PBURST[1:0] 位由硬件强制 配置。
  • 外设突发传输模式:同上

配置完成后初始化 DMA 然后使能 DMA 就可以完成把数据传输给DAC的寄存器了。

1.2 DAC

我们通过DMA往DAC的寄存器中写入值依次让DAC来产生波形,主要用到下面两个寄存器 DHR12R1DHR12R2 ,分别对应 DAC channel1DAC channel2 的 12 位右对齐数据保持寄存器(更详细的 DAC 解析可以查看讲解DAC的博客)

2. STM32代码

2.1 dma.c

#include "dma.h"																	   	  
//DMAx的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7
//chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7
//par:外设地址
//mar:存储器地址
//ndtr:数据传输量  
//DACch:DAC通道选择,@ref DAC_Channel_1,DAC_Channel_2
void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr,u16 DACch)
{ 
    DMA_InitTypeDef  DMA_InitStructure;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);                    // DMA1时钟使能 
    DMA_DeInit(DMA_Streamx);
    while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}                     // 等待DMA可配置 
    /* 配置 DMA Stream */
    DMA_InitStructure.DMA_Channel = chx;                                   // 通道选择
    DMA_InitStructure.DMA_PeripheralBaseAddr = par;                        // DMA外设地址
    DMA_InitStructure.DMA_Memory0BaseAddr = mar;                           // DMA存储器0地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;                // 存储器到外设模式
    DMA_InitStructure.DMA_BufferSize = ndtr;                               // 数据传输量 
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;       // 外设非增量模式
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                // 存储器增量模式
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;// 外设数据长度:16位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;        // 存储器数据长度:16位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ;                       // 使用普通模式 
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                // 非常高优先级
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;            // 存储器突发单次传输
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;    // 外设突发单次传输
    DMA_Init(DMA_Streamx, &DMA_InitStructure);                             // 初始化DMA Stream
    DMA_Cmd(DMA_Streamx, ENABLE); 
    DAC_DMACmd(DACch,ENABLE);
}  

2.2 dac.c

#include "dac.h"
#include "sys.h"

void Dac1_Init(void)
{  
    GPIO_InitTypeDef  GPIO_InitStructure;
    DAC_InitTypeDef DAC_InitType;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);             // 使能GPIOA时钟
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;                     // 模拟输入
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    //GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                  // 推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);                            // 初始化
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);               // 使能DAC时钟 
    DAC_InitType.DAC_Trigger=DAC_Trigger_T2_TRGO;	                  // 不使用触发功能 TEN1=0
    DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;          // 不使用波形发生
    DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;// 屏蔽、幅值设置
    DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ;	      // DAC1输出缓存关闭 BOFF1=1
    DAC_Init(DAC_Channel_1,&DAC_InitType);	                          // 初始化DAC通道1
    DAC_Cmd(DAC_Channel_1, ENABLE);                                   // 使能DAC通道1
    DAC_SetChannel1Data(DAC_Align_12b_R, 0);                          // 12位右对齐数据格式设置DAC值
} 

void Dac2_Init(void)
{  
    GPIO_InitTypeDef  GPIO_InitStructure;
    DAC_InitTypeDef DAC_InitType;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);             // 使能GPIOA时钟
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;                     // 模拟输入
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    //GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                  // 推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);                            // 初始化
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);               // 使能DAC时钟 
    DAC_InitType.DAC_Trigger=DAC_Trigger_T2_TRGO;	                  // 不使用触发功能 TEN1=0
    DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;          // 不使用波形发生
    DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;// 屏蔽、幅值设置
    DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ;	      // DAC2输出缓存关闭 BOFF1=1
    DAC_Init(DAC_Channel_2,&DAC_InitType);	                          // 初始化DAC通道2
    DAC_Cmd(DAC_Channel_2, ENABLE);                                   // 使能DAC通道2
    DAC_SetChannel2Data(DAC_Align_12b_R, 0);                          // 12位右对齐数据格式设置DAC值
} 

2.3 main.c

#include "sys.h"
#include "delay.h"
#include "dma.h"	
#include "dac.h"
#include "tim.h"

#define SEND_BUF_SIZE 100//发送数据长度

int main(void)
{ 
    u32 DATA1[SEND_BUF_SIZE]={
        0.,   84.,  167.,  251.,  334.,  418.,  501.,  585.,  669.,
        752.,  836.,  919., 1003., 1086., 1170., 1254., 1337., 1421.,
        1504., 1588., 1671., 1755., 1839., 1922., 2006., 2089., 2173.,
        2256., 2340., 2424., 2507., 2591., 2674., 2758., 2841., 2925.,
        3009., 3092., 3176., 3259., 3343., 3426., 3510., 3594., 3677.,
        3761., 3844., 3928., 4011., 4095., 4095., 4011., 3928., 3844.,
        3761., 3677., 3594., 3510., 3426., 3343., 3259., 3176., 3092.,
        3009., 2925., 2841., 2758., 2674., 2591., 2507., 2424., 2340.,
        2256., 2173., 2089., 2006., 1922., 1839., 1755., 1671., 1588.,
        1504., 1421., 1337., 1254., 1170., 1086., 1003.,  919.,  836.,
        752.,  669.,  585.,  501.,  418.,  334.,  251.,  167.,   84.,
        0.
    };
    u32 DATA2[SEND_BUF_SIZE]={
        0.,   84.,  167.,  251.,  334.,  418.,  501.,  585.,  669.,
        752.,  836.,  919., 1003., 1086., 1170., 1254., 1337., 1421.,
        1504., 1588., 1671., 1755., 1839., 1922., 2006., 2089., 2173.,
        2256., 2340., 2424., 2507., 2591., 2674., 2758., 2841., 2925.,
        3009., 3092., 3176., 3259., 3343., 3426., 3510., 3594., 3677.,
        3761., 3844., 3928., 4011., 4095., 2048., 2006., 1964., 1923.,
        1881., 1839., 1797., 1755., 1714., 1672., 1630., 1588., 1546.,
        1505., 1463., 1421., 1379., 1337., 1296., 1254., 1212., 1170.,
        1128., 1087., 1045., 1003.,  961.,  920.,  878.,  836.,  794.,
        752.,  711.,  669.,  627.,  585.,  543.,  502.,  460.,  418.,
        376.,  334.,  293.,  251.,  209.,  167.,  125.,   84.,   42.,
        0.
    };
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置系统中断优先级分组2
    delay_init(168);                               // 初始化延时函数
    Dac1_Init();
    Dac2_Init();
//    TIM2_Int_Init(100-1,7200-1);//100*7200/84Mhz
    TIM2_Int_Init(21-1,2-1);                       // 21*2/84Mhz=0.5us 采样率为2M
    MYDMA_Config(DMA1_Stream5,DMA_Channel_7,(u32)&DAC->DHR12R1,(u32)DATA1,SEND_BUF_SIZE,DAC_Channel_1);// DMA1,Stream5,CH7,外设dac1,长度为:SEND_BUF_SIZE,DAC1----PA4
    MYDMA_Config(DMA1_Stream6,DMA_Channel_7,(u32)&DAC->DHR12R2,(u32)DATA2,SEND_BUF_SIZE,DAC_Channel_2);// DMA1,Stream6,CH7,外设dac2,长度为:SEND_BUF_SIZE,DAC2----PA5
    while(1)
    { 
        
    }
}

这里需要注意几点:

  • SEND_BUF_SIZE 可以自行设定大小,不适合设置太大,DAC 会循环的生成数组里面的数据
  • 我们配置的 DMA 传输数据长度为16位,但是我们传输的数据是 32 位的数据,此时会发生高位丢失,只出传输了低位过去,由于 DAC 可接收的最大的数据是 4096 ,所以高位丢失不会对结果产生任何的影响
  • 数组里面的每一个数字与输出电压的对应关系为0~4095------0~3.3V
  • 定时器的时间决定了DAC发出的每一个点的间隔时间,所以可以通过定时器的配置来修改采样率。上面程序的 DAC 采样率为2M,周期为 50us

3. 电脑生成数据

由于数字和电压存在一一对应的关系,所以可以通过Python来进行批量化数据的生成,下面以三角波为例,由于设置的SEND_BUF_SIZE=100 所以上升和下降都分别为 50 个点,从 0V 到 3.3V 再到 0V,然后复制 x 的值到 stm32 程序中,由于要验证两路DAC的同时输入,可以修改一下数组的数据,然后给第二个数组,让两路DAC输出不一样的波形

#%%
import matplotlib.pyplot as plt
import numpy as np
#%%
x = np.linspace(0,4095,50)
y = np.linspace(4095,0,50)
x = np.append(x,y)
x = np.round(x)
plt.figure()
plt.plot(x)
plt.show()
# %%

下面是我设定的两个波形
在这里插入图片描述
在这里插入图片描述

4. 验证

连接示波器两个探头分别连接 PA4 和 PA5 ,可以看到如下的图,周期和波形和预期一致,实验完成
在这里插入图片描述

5. 源码下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/567208.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mycat2主从配置实现读写分离

mycat2主从配置实现读写分离 在https://blog.csdn.net/zhangxue_wei/article/details/130840504基础上继续搭建 1.创建mycat数据源,可以在navcat里直接执行 1.1读数据源m1 /* mycat:createDataSource{"dbType":"mysql","idleTimeout&qu…

Cisco Secure Web Appliance Virtual 15.0 发布 - 适用于网络安全的思科高级威胁防护

Cisco Secure Web Appliance Virtual, AsyncOS for WSA 15.0.0 LD 请访问原文链接:https://sysin.org/blog/cisco-secure-web-appliance-15/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.org Cisco Secure Web Appli…

Windows Server 2022 中文版、英文版下载 (updated May 2023)

Windows Server 2022 中文版、英文版下载 (updated May 2023) Windows Server 2022 正式版,2023 年 5 月更新 请访问原文链接:https://sysin.org/blog/windows-server-2022/,查看最新版。原创作品,转载请保留出处。 作者主页&a…

西南交通大学智能监测 培训课程练习1

2023.05.24培训 task1:ER的用法、对应数据库的设计 task2:熟悉数据库基本操作、使用Navicat 目录 一、E-R图是什么 二、E-R图的组成要素 三、E-R图绘制 四、E-R图实例 4.1E-R图转换到关系模式 4.2具体数据表设计 五、Mysql基础操作 5.1操作数据库…

燃料电池系统控制技术的演进

背景 随着环保意识的不断提高,对清洁能源的需求也日益增加。燃料电池作为一种高效、低污染的清洁能源,受到了广泛关注和重视。与传统燃烧发电相比,燃料电池具有高效、无污染、静音等优点,可以成为未来清洁能源的主要来源之一。 …

docker拉取镜像报错Error response from daemon: Get https://registry-1.docker.io/v2/:

阅读前请看一下:我是一个热衷于记录的人,每次写博客会反复研读,尽量不断提升博客质量。文章设置为仅粉丝可见,是因为写博客确实花了不少精力。希望互相进步谢谢!! 文章目录 阅读前请看一下:我是…

chatgpt赋能python:Pythonshutil.move:一个强大的文件移动工具

Python shutil.move:一个强大的文件移动工具 文件的移动和重命名是我们日常工作中经常会碰到的操作之一。Python的shutil模块提供了很多方便的工具,其中shutil.move是其中一个强大的工具,可以在文件系统中移动和重命名文件。在本文中&#x…

chatgpt赋能python:Python模型保存的完整指南:如何保存和加载模型

Python模型保存的完整指南:如何保存和加载模型 介绍 Python是一个十分强大的编程语言,它的简单易用的语法和丰富的库使其成为了机器学习开发者的首选。在机器学习领域,我们经常需要训练和保存模型,以便在实际应用中使用。本文将…

SpringCloud:(图书预约的操作)

借阅图书的操作主要是添加借阅记录: 在这张表上添加记录,并在t_book这张表上扣除库存的操作 下面我们开始进行操作: 首先我们创建t_borrow表的实体类在book项目中: package com.laosan.book.entity;import lombok.Data;import j…

成都PMP培训机构有推荐的吗?

PMP考试是不是必须要报班?自学可以考试吗?不需要报班的话,自学难度大吗?成都PMP培训机构有推荐的吗? 首先,PMP考试并非必须要报班,自学也可以考试。但是需要注意的是,PMP考试难度较大…

Centos7.9基于Kubeasz部署k8s 1.27.1高可用集群

一:kubeasz 的介绍 kubeasz 致力于提供快速部署高可用k8s集群的工具, 同时也努力成为k8s实践、使用的参考书; 基于二进制方式部署和利用ansible-playbook实现自动化;既提供一键安装脚本, 也可以根据安装指南分步执行安装各个组件。 kubea…

简单分享在微信上扫码点餐小程序怎么做

目前市场上有很多扫码点餐的小程序系统,制作方法有三种: 1、使用微信商家推出的扫码点餐小程序,上传营业执照和食品经营许可证就可以开通使用,然后上传自己的菜品信息就可以了,功能相对比较简单。 2、购买餐饮系统公司…

java链表——LinkList详解

LinkList详解 注:在学习LinkList之前可以先去看一下我的另一篇博客单链表的定义及其模拟实现——java https://editor.csdn.net/md/?articleId130642627,有助于本博客的理解 ArrayList和LinkedList的区别 LinkList模拟实现 定义三个类:MyL…

数据中台建设浪费,数据分析系统如何设计,听听 Gartner 怎么说

2023 年数据分析趋势:数据即业务、从平台到生态、以人为中心 作者 | 宋慧 出品 | CSDN 云计算 数据的价值被越来越多的行业用户看到。不过各种数据系统百花齐放,前几年关于数据中台的讨论仍然众说纷纭。国际研究机构 Gartner 持续对数据分析市场做了调研…

动态规划-数位DP

数字游戏 题目 链接:https://www.acwing.com/problem/content/1084/ 科协里最近很流行数字游戏。 某人命名了一种不降数,这种数字必须满足从左到右各位数字呈非下降关系,如 123 123 123, 446 446 446。 现在大家决定玩一个游…

杂记阅读(三)

目录 校徽和校训 权杖 学术竞赛与荣誉 香港罗德学人 中国大陆罗德学者 富布赖特项目 海外 学友 本土发展 海外扩展 史匹爾財富管理調查 880年,悉尼大学获得了一笔可观的捐赠。富商和慈善家约翰亨利查雷斯在遗嘱中将他的大部分遗产赠予悉尼大学。经过身为…

JavaScript WebAPI 实战

博主简介:想进大厂的打工人博主主页:xyk:所属专栏: JavaEE初阶 本篇文章为大家带来JavaScript WebAPI的使用,请各位过目~~ 目录 文章目录 一、什么是JavaScript WebAPI 1.1 什么是DOM 二、常用的DOM API 2.1 获取元素 2.2 什么是事件 2.2.1 …

Hive数据分层有哪些优点?具体每一层含义是什么?

为什么要分层? 作为一名数据的规划者,我们肯定希望自己的数据能够有秩序地流转,数据的整个生命周期能够清晰明确被设计者和使用者感知到。直观来讲就是如图这般层次清晰、依赖关系直观。 但是,大多数情况下,我们完成的数据体系却…

深度学习基础知识-误差反向传播+训练技巧(无代码仅用来理解)

参考书籍:(找不到资源可以后台私信我) 《深度学习入门:基于Python的理论与实现 (斋藤康毅)》 《Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow, 2nd Edition (Aurelien Geron [Gron, Aurlien])》 神经网络…

Delphi日薄西山?不仅用户300万,还大佬无数,转身就风靡全球

主人公名叫David Vacanti,是Delphi的长期开发人员,已有接近30年以上的编程经验。 我之所以注意到他,一是因为Delphi属实一波回忆杀。 二是不可思议:他在1983年,便开启了自己的副业。 开了一家叫做“Vacanti Yacht D…