STM32--DMA

文章目录

  • DMA简介
    • DMA特性
  • DMA框图
  • DMA基本结构
  • DMA请求
  • 数据宽度对齐
  • DMA数据转运工程
  • DMA+ADC多通道

DMA简介

直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。

DMA特性

拥有12个独立可配置通道:DMA1(7个通道),DMA2(5个通道)

STM32F103C8T6 DMA资源:DMA1(7个通道)

在这里插入图片描述

每个通道都直接连接专用的硬件DMA请求,也就是硬件触发,也支持软件触发

对于有多个请求的同时,可以利用软件编程设置优先级的先后顺序,优先级相等的情况下由硬件决定

对于DMA来说,需要传输的源头和传输的目的地,传输的大小称为传输宽度,一般有字节(8bit)、半字(16bit)、字(32bit)。

在这里插入图片描述

每个通道都有3个事件标志(DMA半传输、 DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。
每个DMA通道都可以在DMA传输过半、传输完成和传输错误时产生中断。为应用的灵活性考虑,通过设置寄存器的不同位来打开这些中断。
在这里插入图片描述

DMA框图

在这里插入图片描述
先看左上角,DMA与Cortex™-M3核心共享系统数据总线,通过总数据库,可以直接执行存储器的数据传输(Flash、SRAM、外设寄存器);当DMA与Cortex™-M3同时访问相同的目标时,会通过一个仲裁器,来给它们两个循环调度访问目标,而CPU最终会得到至少一半的带宽,也就访问权限。

接着往下看,AHB从设备连接着AHB总线,可以控制总裁器和通道的选择。相当于AHB放长线,放出一个设备来管理DMA。

总裁器是可以根据通道请求的优先级来启动外设/存储器的访问。
可以通过软件编程设置4个不同的等级:最高优先级、高优先级、中等优先级、低优先级。
相同等级的情况下,则较低编号的通道比较高编号的通道有较高的优
先权。举个例子,通道2优先于通道4。

右边都是一些存储器和外设寄存器,通过DMA的请求,就可以直接对数据进行传输了。
在这里插入图片描述
这是存储器对应的起始地址和作用。

DMA基本结构

在这里插入图片描述

DMA的处理:在发生一个事件后,外设向DMA控制器发送一个请求信号。 DMA控制器根据通道的优先权处理请求。当DMA控制器开始访问发出请求的外设时, DMA控制器立即发送给它一个应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求, DMA控制器同时撤销应答信号。如果有更多的请求时,外设可以启动下一个周期。

我们首先会从要传输的外设寄存器或者是储存器获取它的起始地址和传输的数据宽度,然后再存数据到外设寄存器或者存储器指示的存储器地址,并确定数据宽度;

首先这里要明确DMA的数据转运是用复制去传输,也就是原位置的数据是不会改变的。地址自增是考虑到一般给出的地址只是首元素地址或者说是起始地址,要对下一个数据进行传输,就需要对地址进行增加,才能获取到下一个数据

传输计数器是来统计要传输多少个数据宽度的,一开始先赋予传输计数器一个初始值
在没有启动自动重装器的情况下,这个初始值会逐渐递减,当计数器的值变为0时,就停止传输。
如果启动了自动重装器,当初始值逐渐递减为0时,会重新恢复到初始值,而对应的传输地址,也会恢复到初始地址的状态;

M2M是决定使用软件触发或者硬件触发的寄存器

DMA请求

在这里插入图片描述
这是DMA1的请求映像,对于DMA请求来说,不同的外设是有要求不同对应通道的,没有由我们所决定哪个硬件外设对应哪条通道;而软件触发的则每条通道都允许。

数据宽度对齐

在这里插入图片描述

大小端讲解连接入口

这里是小端存储。
对以上的总结:
当两端宽度相等时:那么传输数据不变;
当源端宽度小于目标宽度时:目标宽度高位补0;
当源端宽度大于目标宽度时:对源端宽度进行截断,保留低位的数据

DMA数据转运工程

OLED代码连接入口

连接方式:
在这里插入图片描述
在这里插入图片描述
通过DMA的数据搬运,将一个数组中的内容搬运到另一个数组中,并且原数组会随时间不断增加,DMA也不断的进行数据搬运;

DMA.c

#include "stm32f10x.h"                  // Device headeruint16_t MyDMA_Size;
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)
{MyDMA_Size=Size;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//DMA初始化DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_BufferSize=Size; //指定通道缓冲区大小DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC; //指定外设是源或者目标DMA_InitStructure.DMA_M2M=DMA_M2M_Enable; //指定是否为软件触发运送DMA_InitStructure.DMA_MemoryBaseAddr=AddrB; //指定内存基地址DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte; //内存数据宽度DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable; //指定内存地址是否自增DMA_InitStructure.DMA_Mode=DMA_Mode_Normal; //指定DMA通道工作模式DMA_InitStructure.DMA_PeripheralBaseAddr=AddrA; //指定外设基地址DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte; //外设数据宽度DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Enable; //指定内存地址是否自增DMA_InitStructure.DMA_Priority=DMA_Priority_Medium; //指定DMA通道优先级DMA_Init(DMA1_Channel1,&DMA_InitStructure);//DMA是否启动DMA_Cmd(DMA1_Channel1,DISABLE);
}
//数据转运函数
void MyDMA_Transfer()
{DMA_Cmd(DMA1_Channel1,DISABLE);DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size); //设计DMA通道数据单元数DMA_Cmd(DMA1_Channel1,ENABLE);//检查DMA通道上的标志位,运送完成标志while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET); //需要手动清除标志位DMA_ClearFlag(DMA1_FLAG_TC1);
}

DMA.h

#ifndef __DMA_H__
#define __DMA_H__void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size);
void MyDMA_Transfer();#endif

size表示传输的个数,大小由源端数据宽度决定,地址自增将会使数组下标进行移动;
规定软件触发不能启动自动重装器,也就是DMA的模式
数据转运函数:由于需要不断的数据转运,而没有启动自动重装器,所以需要我们自己编程来进行重装,也就是将Size恢复为初始值,地址会随着Size的变化进行变化。
手动将Size重装之前,需要先对运行控制禁用,全部操作设置完才可以启用运行控制

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Buzzer.h"
#include "DMA.h"
#include "OLED.h"uint8_t DataA[]={0x01,0x02,0x03,0x04};
uint8_t DataB[]={0,0,0,0};
int main()
{OLED_Init();MyDMA_Init((uint32_t)DataA,(uint32_t)DataB,4);OLED_ShowString(1,1,"DataA");OLED_ShowString(3,1,"DataB");OLED_ShowHexNum(1,8,(uint32_t)DataA,8);OLED_ShowHexNum(3,8,(uint32_t)DataB,8);while(1){DataA[0]++;DataA[1]++;DataA[2]++;DataA[3]++;OLED_ShowHexNum(1,8,(uint32_t)DataA,8);OLED_ShowHexNum(2,1,DataA[0],2);OLED_ShowHexNum(2,4,DataA[1],2);OLED_ShowHexNum(2,7,DataA[2],2);OLED_ShowHexNum(2,10,DataA[3],2);OLED_ShowHexNum(4,1,DataB[0],2);OLED_ShowHexNum(4,4,DataB[1],2);OLED_ShowHexNum(4,7,DataB[2],2);OLED_ShowHexNum(4,10,DataB[3],2);Delay_ms(1000);MyDMA_Transfer();OLED_ShowHexNum(2,1,DataA[0],2);OLED_ShowHexNum(2,4,DataA[1],2);OLED_ShowHexNum(2,7,DataA[2],2);OLED_ShowHexNum(2,10,DataA[3],2);OLED_ShowHexNum(4,1,DataB[0],2);OLED_ShowHexNum(4,4,DataB[1],2);OLED_ShowHexNum(4,7,DataB[2],2);OLED_ShowHexNum(4,10,DataB[3],2);Delay_ms(1000);}
}

DMA+ADC多通道

接线方式:
在这里插入图片描述
在这里插入图片描述
ADC的规则通道会将多通道结果放在规则通道寄存器上,每当扫描到一个通道,结果就放在ADC_DR上,结果会覆盖掉上一个储存的数据,DMA就会将结果转运到DMA存储器地址上。所以对于ADC_DR不用实现地址自增,而DMA储存器需要实现地址自增。

AD.c

#include "stm32f10x.h"                  // Device headeruint16_t AD_Value[4];void AD_Init()
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//配置ADC时钟RCC_ADCCLKConfig(RCC_PCLK2_Div6);  // 72M/6=12MHzGPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; //模拟输入GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);//为所选ADC规则通道配置其序列器对应等级和采样时间ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5);//ADC结构体成员ADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_ContinuousConvMode=ENABLE; //指定通道模式为连续转换或者单转换ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐方式ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None; //启动规则通道模拟电压到数字转换的外部触发器ADC_InitStructure.ADC_Mode=ADC_Mode_Independent; //配置ADC为独立模式或者双模式ADC_InitStructure.ADC_NbrOfChannel=4;ADC_InitStructure.ADC_ScanConvMode=ENABLE; //选择是否为扫描模式ADC_Init(ADC1,&ADC_InitStructure);//DMA结构体成员DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;DMA_InitStructure.DMA_BufferSize = 4;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;DMA_Init(DMA1_Channel1, &DMA_InitStructure);//ADC运行控制DMA_Cmd(DMA1_Channel1,ENABLE);ADC_DMACmd(ADC1,ENABLE);ADC_Cmd(ADC1,ENABLE);//重置所选ADC校准寄存器ADC_ResetCalibration(ADC1);//获取ADC复位状态,复位后为0while(ADC_GetResetCalibrationStatus(ADC1));//开始校准ADC_StartCalibration(ADC1);//获取ADC所选标准位状态,校准需要时间,校准好后置0while(ADC_GetCalibrationStatus(ADC1));//启动ADC软件转换ADC_SoftwareStartConvCmd(ADC1,ENABLE);}

AD.h

#ifndef __AD_H__
#define __AD_H__extern uint16_t AD_Value[4];void AD_Init();#endif

这里采用的是连续转换+扫描模式。这样就可以直接实现数据转换的自动化。
这里需要加上ADC_DMACmd函数,因为上面DMA请求中通道一的硬件触发有3个选择,这里表示选择ADC1.
ADC为软件触发方式,DMA为硬件触发方式。

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"int main()
{OLED_Init();AD_Init();OLED_ShowString(1,1,"AD0:");OLED_ShowString(2,1,"AD1:");OLED_ShowString(3,1,"AD2:");OLED_ShowString(4,1,"AD3:");while(1){OLED_ShowNum(1,5,AD_Value[0],4);OLED_ShowNum(2,5,AD_Value[1],4);OLED_ShowNum(3,5,AD_Value[2],4);OLED_ShowNum(4,5,AD_Value[3],4);}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STL——stack和queue

一、stack和queue stl中提供了栈和队列配接器供我们使用,以后就可以直接使用了。不需要我们自己造轮子。 使用细节参考文档就可以,与之学过的容器并无二致。栈和队列的特性我们再学习数据结构时已经了解了。这里就不在赘述了。 stack - C Reference (…

vite+vue3项目配置cdn引入在线依赖

采用ejs的方式 安装语法依赖 npm install vite-plugin-ejs -D配置暴露数据 vite.config.js文件: import { fileURLToPath, URL } from node:url import { defineConfig, loadEnv } from vite import vue from vitejs/plugin-vue import vueJsx from vitejs/plug…

分布式锁实现方式

分布式锁 1 分布式锁介绍 1.1 什么是分布式 一个大型的系统往往被分为几个子系统来做,一个子系统可以部署在一台机器的多个 JVM(java虚拟机) 上,也可以部署在多台机器上。但是每一个系统不是独立的,不是完全独立的。需要相互通信&#xff…

vue 关闭prettier警告warn

这个就是我们创建vue cli的时候 把这个给默认上了 关闭这个只需在.eslintrc.js页面里边添加一行代码"prettier/prettier": "off"

34.Netty源码之Netty如何处理网络请求

highlight: arduino-light 通过前面两节源码课程的学习,我们知道 Netty 在服务端启动时会为创建 NioServerSocketChannel,当客户端新连接接入时又会创建 NioSocketChannel,不管是服务端还是客户端 Channel,在创建时都会初始化自己…

线程池原理

一、线程池的定义 线程池,按照配置参数(核心线程数、最大线程数等)创建并管理若干线程对象,没有任务的时候,这些线程都处于等待空闲状态。如果有新的线程任务,就分配一个空闲线程执行。如果所有线程都处于…

【NX】NX二次开发BlockUI集列表的详细使用步骤

最近使用NX二次开发,需要用到集列表,也就是SetList这个控件,然而网上相关的资料和范例实在是太少,有幸找到《NX二次开发-BlockUI集列表的使用技巧》和《UG(NX)二次开发 BlockUI 集列表使用方法》&#xff0…

Dubbo使用

<!--dubbo--><dependency><groupId>com.alibaba.spring.boot</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.0.0</version></dependency>接口 public interface IUseService {public String…

设计模式之状态模式(State)的C++实现

1、状态模式的提出 在组件功能开发过程中&#xff0c;某些对象的状态经常面临变化&#xff0c;不同的状态&#xff0c;其对象的操作行为不同。比如根据状态写的if else条件情况&#xff0c;且这种条件变化是经常变化的&#xff0c;这样的代码不易维护。可以使用状态模式解决这…

安全学习DAY16_信息打点-CDN绕过

信息打点-CDN绕过 文章目录 信息打点-CDN绕过本节思维导图相关链接&工具站&项目工具前置知识&#xff1a;CDN配置&#xff1a;配置1&#xff1a;加速域名-需要启用加速的域名配置2&#xff1a;加速区域-需要启用加速的地区配置3&#xff1a;加速类型-需要启用加速的资源…

VGG分类实战:猫狗分类

关于数据集 数据集选择的是Kaggle上的Cat and Dog&#xff0c;猫狗图片数量上达到了上万张。你可以通过这里进入Kaggle下载数据集Cat and Dog | Kaggle。 在我的Github仓库当中也放了猫狗图片各666张。 VGG网络 VGG的主要特点是使用了一系列具有相同尺寸 3x3 大小的卷积核进…

vue3生命周期

原理 vue3也提供了Composition API形式的生命周期钩子&#xff0c;与vue2.x中钩子对应关系如下&#xff1a; beforeCreate setup&#xff08;&#xff09; created setup&#xff08;&#xff09; beforeMountonBeforeMount mountedonMounted beforeUpdateonBeforeUpdate updat…

【C++】做一个飞机空战小游戏(十)——子弹击落炮弹、炮弹与飞机相撞

[导读]本系列博文内容链接如下&#xff1a; 【C】做一个飞机空战小游戏(一)——使用getch()函数获得键盘码值 【C】做一个飞机空战小游戏(二)——利用getch()函数实现键盘控制单个字符移动【C】做一个飞机空战小游戏(三)——getch()函数控制任意造型飞机图标移动 【C】做一个飞…

Unsafe upfileupload

文章目录 client checkMIME Typegetimagesize 文件上传功能在web应用系统很常见&#xff0c;比如很多网站注册的时候需要上传头像、上传附件等等。当用户点击上传按钮后&#xff0c;后台会对上传的文件进行判断 比如是否是指定的类型、后缀名、大小等等&#xff0c;然后将其按…

HTML中的字符串转义

为什么要转义&#xff1f; 转义可以防止 xss 攻击。接下来&#xff0c;我们来看一下如何转义。 HTML Sanitizer API Sanitizer 是浏览器自带的转义方法&#xff0c;在2021年初被提出&#xff0c;兼容性问题很大。 列举几个常用的 API&#xff1a; const $div document.qu…

【广州华锐视点】VR线上教学资源平台提供定制化虚拟现实学习内容

虚拟现实&#xff08;VR&#xff09;技术的出现为我们提供了一种全新的在线教学方式。由广州华锐视点开发的VR线上教学资源平台&#xff0c;作为一个综合性的学习工具&#xff0c;正在教育领域迅速发展&#xff0c;并被越来越多的教育机构和学生所接受。那么&#xff0c;VR线上…

redis实战-缓存数据解决缓存与数据库数据一致性

缓存的定义 缓存(Cache),就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码。防止过高的数据访问猛冲系统,导致其操作线程无法及时处理信息而瘫痪&#xff0c;这在实际开发中对企业讲,对产品口碑,用户评价都是致命的;所以企业非常重视缓存…

ReactNative进阶(三十四):ipa Archive 阶段报错error: Multiple commands produce问题修复及思考

文章目录 一、前言二、问题描述三、问题解决四、拓展阅读五、拓展阅读 一、前言 在应用RN开发跨平台APP阶段&#xff0c;从git中拉取项目&#xff0c;应用Jenkins进行组包时&#xff0c;发现最终生成的ipa安装包版本号始终与项目中设置的版本号不一致。 二、问题描述 经过仔…

图数据库_Neo4j和SpringBoot整合使用_实战创建明星关系图谱---Neo4j图数据库工作笔记0010

然后我们再来看一下这个明星关系图谱 可以看到这里 这个是原来的startRelation 我们可以写CQL去查询对应的关系 可以看到,首先查询出来以后,然后就可以去创建 我们可以把写的创建明星关系的CQL,拿到 springboot中去执行 可以看到,这里我们先写一个StarRelationRepository,然…

【100天精通python】Day42:python网络爬虫开发_HTTP请求库requests 常用语法与实战

目录 1 HTTP协议 2 HTTP与HTTPS 3 HTTP请求过程 3.1 HTTP请求过程 3.2 GET请求与POST请求 3.3 常用请求报头 3.4 HTTP响应 4 HTTP请求库requests 常用语法 4.1 发送GET请求 4.2 发送POST请求 4.3 请求参数和头部 4.4 编码格式 4.5 requests高级操作-文件上传 4.6 …