首页 > 其他分享 >stm32f103c8t6学习笔记(学习B站up江科大自化协)-DMA

stm32f103c8t6学习笔记(学习B站up江科大自化协)-DMA

时间:2024-03-28 23:59:27浏览次数:30  
标签:DMA 触发 存储器 up 地址 自化协 转运 外设

DMA简介

        ·DMA主要用于协助CPU完成数据转运的工作

        ·DMA,英文全称Direct Memory Access,DMA这个外设是可以直接访问STM32内部存储器的,包括运行内存SRAM,程序存储器flash和寄存器等等,DMA都有权限访问,所以DMA能完成数据转运的工作

        ·第二行的外设指的是外设寄存器,一般是外设的数据寄存器DR(Data Register),比如ADC的数据寄存器和串口的数据寄存器等等,存储器指的是运行内存sram和程序存储器flash,是存储变量数组和程序代码的地方。

        ·可配置的通道,指的是数据转运的路径,从一个地方转运到另一个地方需要占用一个通道,如果有多个通道进行转运,可以各转各的,互不干扰,

        ·如果DMA进行的是存储器到存储器的数据转运,比如想把flash里的数据转运到sram里去,那就需要软件触发,DMA会一股脑将数据以最快速度转运完成。

        如果DMA进行的是外设到存储器的数据转运,就不能一股脑转运。外设的数据转运是有时机的,需要使用硬件触发,比如转运ADC的数据,得等ADC每个通道AD转换完成之后,硬件触发一次DMA,之后DMA再进行转运,触发一次转运一次,数据才是正确的。

        存储器到存储器的转运一般使用软件触发,外设到存储器的转运一般使用硬件触发。特定的硬件触发意味着每个DMA的硬件触发源是不一样的,要使用某个外设的硬件触发源,就得使用他连接的那个通道,而不能任意选择通道

        ·c8t6这个芯片只有7个通道,没有DMA2

存储器映像

        ·ROM,只读存储器,是一种非易失性、掉电不丢失的存储器

ROM分为三块:
        第一块是程序存储器flash,也就是主闪存,用途是存储c语言编译后的代码,也就是下载程序的位置。运行程序一般是从主闪存里面开始运行。这一块存储器STM32分配的地址是0x0800 0000,起始地址,也就是第一个字节的地址0800,然后剩余字节的地址依次增长,每个字节都分配一个独一无二的地址,之后程序才能精准的访问这个寄存器。终止地址取决于他的容量,编到哪里,哪里就是终止地址。如果在软件里看到,某个数据的地址是0800开头的,一般可以确定为主闪存的数据。
        第二块和第三块也是掉电不丢失,不难看出这两块存储器的位置是在ROM区的最后面,实际上存储介质也是flash,只不过一般flash指的是主闪存flash,而不是指这两个区域。对于这两块区域的地址都是1FFF开头的。系统存储器的用途是存储bootloader,用于串口下载。bootloader一般是芯片出厂自动写入的,一般不允许修改。选项字节的存储器一般用于存储一些独立于程序代码的配置参数,位置是在ROM的最后面,下载程序可以不刷新选项字节的内容,这样选项字节的配置就可以保持不变,选项字节里主要存储的是flash的读保护和写保护,还有看门狗等等的配置。

        ·RAM,随机存储器,是一种    易失性,掉电    丢失的存储器
        2000开头的是ram区域,首先是运行内存sram,分配地址是0x2000 0000,用途是用于存储运行过程的临时变量,也就是在程序中定义变量、数组、结构体的地方,类比电脑的话,运行内存就是内存条。
        外设寄存器,地址是0x4000 0000,用途是存储各个外设的配置参数,也就是初始化各个外设最终读写的东西。外设寄存器也是存储器的一种,存储介质其实也是sram,不过一般习惯把运行内存叫做sram,外设寄存器直接叫做寄存器。
        内核外设寄存器,地址是0xE000 0000,用途是存储内核各个外设的配置参数,内核外设就是NVIC和systick,因为内核外设和其他外设不是一个厂家设计的,所以地址被分开。

DMA基本结构

        ·图中的外设寄存器和存储器(flash、sram)这两个部分是数据转运的两大站点,在STM32里,一般特指的是flash和sram,不包含外设寄存器。外设寄存器一般直接称作外设,寄存器也是存储器的一种,DMA的数据转运可以从外设到存储器,也可以从存储器到外设,具体转运方向有一个方向的参数可以进行控制。还有一种转运方式是从存储器到存储器,比如flash到sram或sram到sram这两种方式。由于flash是只读的,所以DMA不可以进行从sram到flash或flash到flash的转运操作。

        ·左右两边分别是外设和存储器的三个参数,要进行数据转运首先要指定从哪到哪,以及怎么转。所以外设和存储器都有三个参数
                第一个是起始地址,有外设端的起始地址和存储器端的起始地址,这两个参数决定了数据是从何来到何去,
                第二个参数是数据宽度,这个参数的作用是指定一次转运要按多大的数据宽度来进行,可以选择字节byte、半字halfword和字word。字节是八位,也就是一次转运一个uint8_t大小的数据,半字是uint16_t大小,字是uint32_t大小
                第三个参数是地址是否自增,这个参数的作用是指定一次转运完成之后,下一次转运是否要把地址移动到下一个地址去,相当于指针p++。比如ADC扫描模式,用DMA进行数据转运,外设地址是ADC_DR寄存器,寄存器这边显示地址是不需要自增的,原因是自增的话下次转运就跑到别的寄存器去了,但是存储器这边地址需要自增,每转运一次数据后都往后挪个坑,否则下次在转运的时候就会把上次的数据覆盖,这就是地址是否自增的作用。

        ·如果要进行存储器到存储器的数据转运,就需要把其中一个存储器的地址放在外设的站点。只要在外设起始地址里写入flash或sram的地址,那就会去flash和sram里面找数据。虽然这个站点叫做外设寄存器,仅是名字而已,并不代表这个地址只能写寄存器的地址,如果写flash就会去flash里面找,写sram就会去sram里面找,没有限制,甚至可以在外设站点写存储器的地址,存储器站点写外设的地址,然后方向参数反过来即可。

        ·下面有个传输计数器,用来指定总共需要转运几次,这个传输计数器是一个递减计数器,比如给他写一个5,那么DMA就只能进行5次数据转运,转运过程中,每转运一次计数器的数就会减1,当传输技术器减到0之后就不会进行数据转运,同时减到0之后,前边自增的地址也会恢复到起始的位置,以方便后边DMA开始新一轮的转运。

        ·在传输计数器的右边有一个自动重装器,作用是传输计数器减到0之后是否要恢复到最初的值,比如传输计数器初值给5,不使用自动重装器,那转运5次之后DMA就结束了,如果使用自动重装器,那重装5次计数器减到0之后就会立刻重装到初始值5。自动重装器决定了转运的模式,如果不重装那就是正常的单次模式,如果重装就是循环模式,如果想转运一个数组一般就是单次模式,转运一轮就结束;如果是ADC扫描+连续转换,为了配合ADC,DMA也需要使用循环模式。这个循环模式和ADC的连续模式相似,都用于指定一轮工作之后是否立刻开启下一轮工作。

        ·最下边是DMA的触发控制,触发决定了DMA是在什么时候进行转运。触发源有硬件触发和软件触发,具体选择哪个由M2M(Memory to Memory,由于2和two同音,意思是存储器到存储器)这个参数来决定。

        当给M2M时DMA会选择软件触发,这个软件触发并不是调用某一个函数触发一次,这个软件触发的逻辑是:以最快的速度不断地触发DMA,争取快速把传输寄存器清零,完成本轮转换。这里的软件触发和之前外部中断、ADC的软件触发不太一样,可以理解成连续触发,但是软件触发和循环模式不能同时使用,因为软件触发是为了将传输计数器清零,而循环模式是将其清零后自动重装,同时使用的话DMA停不下来。软件触发一般用于存储器到存储器之间的转运,是软件启动,不需要时机。

        当M2M位给0那就是使用硬件触发,硬件触发源可以选择ADC、串口、定时器等等,使用硬件触发的转运一般是和外设有关的转运,这些转运需要一定的时机,比如ADC转换完成、串口发送数据、定时时间到等等,当硬件达到这些时机时,传一个信号来触发DMA进行转运。

        ·开关控制,也就是DMA_CMD函数,当给DMA使能之后,DMA准备就绪,可以进行转运。DMA进行转运有几个条件:

        第一就是开关控制,DMA_Cmd必须使能

        第二就是传输计数器必须大于0

        第三就是触发源必须有触发信号,触发一次转运一次,传输计数器自减一次。当传输计数器等于0且没有自动重装时,此时无论是否触发DMA都不会进行转运,此时需要将DMA_Cmd给disable关闭DMA,再为传输计数器写一个大于0的数,在DMA_Cmd给enable开启DMA,DMA才能继续工作。

        注意:手册规定,写传输计数器时必须先关闭DMA再进行,不能在DMA开启时写传输计数器

DMA请求

        这张图是DMA1的请求映像,下面是DMA的7个通道,每个通道都有一个数据选择器,可以选择硬件触发或软甲触发

        ·图的左侧列外设请求信号,每个通道的硬件触发源都是不同的,如果需要使用ADC1来触发的话就需要选择通道1,如果需要定时器2来更新事件的话那就需要选择通道2。由于每个通道的硬件触发源不同,如果想使用某个硬件触发源的话,就必须使用其所在的通道,这是关于使用硬件触发的注意事项;如果使用软件触发的话通道可以任意选择,因为每个通道的软件触发都是一样的。
        ·图中通道1的硬件触发是ADC1,定时器2的通道3和定时器4的通道1,具体选择哪个触发源是根据对应的外设是否开启了DMA输出来决定的。比如要使用ADC1,那就会有个库函数叫ADC_DMACmd,必须使用这个库函数开启ADC1的这一路输出才有效;如果想使用定时器2的通道3,会有个TIM_DMACmd的函数进行DMA输出控制。关于这三个触发源使用哪个,取决于把哪个外设的DMA输出开启了,如果都开启了,那就变成了一个或门,三个硬件都可以进行触发,一般情况下只开启一个。
        ·最终7个触发源进入到仲裁器,进行优先级判断,最终产生内部的DMA1请求,优先级的判断类似于中断优先级。默认优先级是通道号越小优先级越高,可在程序中进行配置。
 

        这里的意思应该是,EN并不是数据选择器的控制位,而是选择数据寄存器是否工作,EN=0数据寄存器不工作,EN=1数据寄存器工作。软件触发后面跟着M2M位的意思是当M2M=1时选择软件触发。

数据宽度与对齐

        数据宽度与对齐,在前面的数据转运的两个站点都有一个数据宽度的参数,如果数据宽度都一样那就是一个一个正常的转运,如果数据宽度不一样,需要参考操作表格。表格中第一列是源端宽度,第二列是目标宽度,第三列是传输数目,当源端和目标都是8位时,转运第一步在源端的0位置读数据B0,在目标的0位置写数据B0,就是把这个B0从表格左边移到右边,接着就是把B1移动到右边,接着就是B2B3。源端是8位目标是16位,这个的操作是源端读B0,目标写入00B0,读B1写入00B1,如果目标的数据宽度比源端的数据宽度大,那就在目标前面多出来的空位补0。在8位转到32位同理,将前边空出来的都补0。当目标数据宽度比源端数据宽度小的时候,比如16位转8位,现象就是读B1和B0,只写入B0,读B3B2,只写入B2,写就是把多出来的高位舍弃,后边的也是类似操作。总结就是如果把小的数据转到大的里边就会补0,如果把大的数据转到小的里面高位就会舍弃。
 

DMA数据转运

        .使用DMA,进行存储器到存储器的数据转运,也就是把一个数组里的数据复制到另一个数组里。

        ·数据转运+DMA,将sram里面的数组DataA转运到另一个数组DataB中的配置方式如下,首先是外设站点和存储器站点的起始地址、数据宽度、地址是否自增这三个参数。在这个任务里外设数组(发送)应该填写DataA的数组的首地址,存储器地址(接收)给DataB数组的地址,数据宽度两个数组类型都是uint8_t,所以数据宽度都是按8位的字节传输,由图可见将数组A0转到数组B0,然后A1到B1等等,所以两个站点的地址都需要自增,都移动到下一个数据的位置,才能继续进行转运。如果左边自增右边补自增,那么最后只会有B0接收到A8的数据,如果左边不自增右边自增,最后B0到B8都是A0的数据,如果都不自增那么只有B0有A0的数据。由于将外设站点转运到存储器站点,方向参数需正向。数组中有7个数据,所以传输计数器给7,自动重装暂不需要。触发部分要使用软件触发,因为这是存储器到存储器的数据转运,无需等待时机,最后调用ADC_Cmd给DMA使能。这里的转运是复制转运,转运完成之后DataA的数据不会消失,仅是把DataA的数据复制到DataB的位置
 

DMA+数据多通道

        ·ADC扫描模式+DMA,左边是ADC扫描模式的执行流程,有七个通道,触发一次之后7个通道依次进行AD转换,然后转换结果放到ADC_DR寄存器里面。在每个单独的通道转换完成之后,进行一个DMA数据转运,并且目的的地址进行自增,防止数据被覆盖。外设地址写入ADC_DR这个寄存器的地址,存储器的地址可以在sram中定义一个数组ADValue,然后把ADValue的地址当做存储器的地址,之后数据宽度由于ADC_DR和sram数组都是uint16_t的数据,所以数据宽度都是16位的半字传输。在图中可见外设地址不自增存储器地址自增,传输方向是外设站点到存储器站点。传输通道有7个所以传输计数器设置为7。ADC如果是单次扫描,那么ADC的传输计数器可以不自动重装,转换一轮即停,如果ADC是连续扫描,可以使用自动重装,在ADC启动下一轮转换的时候,DMA也启动下一轮转运,ADC和DMA同步工作。ADC_DR的值是在ADC单个转换完成之后才有效的,所以DMA转运的时机,需要和ADC单个通道转换完成同步,所以ADC的触发要选择硬件触发。

        ·DMA最常用的用途就是配合ADC的扫描模式,因为ADC扫描模式有数据覆盖的特征,ADC对于DMA的需求非常强烈,其他的外设使用DMA将锦上添花,ADC没有的话功能会受到极大的限制。

接线图

        

        这个部分的数据转运是在STM32内部进行的,无需其他外加模块,首先印证一下定义的数据是否储存在相应的地址区间里。

        在主函数写入如下内容,编译烧录后可见OLED显示屏上边显示第一行66是aa的内容,第二行是2000 0000是aa被存储的地址,对照存储器映像的表就知道aa存储的位置是sram区。在sram区的地址必定为20开头,但是具体地址是由编译器决定的。由于现在sram没有其他变量,所以sram就把aa放在sram的第一个位置。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"

uint16_t aa = 0x66;

int main()
{
	OLED_Init();
	
	OLED_ShowHexNum(1,1,aa,3);
	OLED_ShowHexNum(2,1,(uint32_t)&aa,8);	//长度为8 是8个16进制数 表示的是32位
	//显示地址一般使用16进制 对于一个变量取地址之后应该存放在一个指针变量里
	//如果想当做指针变量进行显示的话 需要在前边加上强制类型转换 如果不加强制类型转换 就是指针跨级赋值 会报错
	
	while(1)
	{

	}
}

        在代码中加入const关键字,const uint16_t aa = 0x66;,将aa表示为只能读不能写的常量,由于flash里边的数据也是只能读不能写,因此也对应起来,stm32中用const定义的变量(实际上变成了常量)是存储在flash里边的。编译烧录后发现第二行变成了0800 0808,对照存储器映像表可以发现aa被储存在了flash里边。flash里存储的是程序代码和常量数据。

        0800 0808地址尾部有偏移,没有和前边sram一样安排在第一个位置,这是因为flash里边还有程序代码在前边,所以编译器给这个常量安排的地址相对靠后。

        程序出现大量数据是无需修改时可以用const定义,节省sram空间,比如查找表或字库数据等。

外设寄存器地址

        对于变量或常量来说,地址是由编译器决定的,不同的程序地址可能不一样,非固定。对于外设寄存器地址来说是固定的,可以在手册查询,也可以在程序中直接访问寄存器,比如访问ADC1的DR寄存器,写ADC1->DR即可,代码如下

int main()
{
	OLED_Init();
	
	OLED_ShowHexNum(2,1,(uint32_t)&ADC1->DR,8);	//长度为8 是8个16进制数 表示的是32位
	
	while(1)
	{

	}
}

        烧录编译后发现,显示的内容为4001 244C,对应寄存器映像表,是属于外设寄存器的地址,这个地址是一个固定的,查询数据手册可以查到ADC1的起始地址是4001 2400,DR的偏移地址是4C,所以ADC1的DR地址就是4001 244C,与显示结果相同。

        寄存器的实际地址 =  起始地址 + 偏移地址

标签:DMA,触发,存储器,up,地址,自化协,转运,外设
From: https://blog.csdn.net/weixin_74859061/article/details/136987999

相关文章

  • Camera sensor bringup
    CameraSensorModule配置信息:CameraModuleConfiguration的信息包含在:<cameraModuleData>…</cameraModuleData>以vendor/qcom/proprietary/chi-cdk/oem/qcom/module/xxx_sunny_s5khm2_wide_module.xml 为例:各配置参数含义如下:参数名说明cameraId......
  • Popup
    给我解释<PopupName="myPopup"IsOpen="{BindingIsChecked,ElementName=checkbox}"PlacementTarget="{BindingElementName=checkbox}"StaysOpen="True"><......
  • fatal: 无法访问 ‘https://github.com/pupupupupi/mysql_8.0.31.git/‘:Failed to con
    解决方法:1.更新系统:sudoaptupdatesudoaptupgrade2.安装git:sudoaptinstallgit3.验证git是否成功安装git--version//以上步骤如果已操作可以忽略此时我们发现可以clone了:如果仍不能clone,检查连接的网络状态,例如把校园网切换为手机热点或者是其他网速好的网络!!!本......
  • Oracle中的for update 和 for update nowait
    在Oracle数据库中,FORUPDATE和FORUPDATENOWAIT是两种用于行级锁定的SQL子句,它们通常用在SELECT语句中以确保数据的一致性和隔离性。这里是它们的基本区别和用法:FORUPDATEFORUPDATE子句用于锁定SELECT语句检索到的行,以便于进行更新操作。当使用FORUPDATE时,如果所选行已经......
  • 2024MathorCup数学建模思路A题B题C题D题思路汇总 妈妈杯建模思路分享
    文章目录1赛题思路2比赛日期和时间3组织机构4建模常见问题类型4.1分类问题4.2优化问题4.3预测问题4.4评价问题5建模资料1赛题思路(赛题出来以后第一时间在CSDN分享)https://blog.csdn.net/dc_sinor?type=blog2比赛日期和时间报名截止时间:2024年4月11......
  • 2024妈妈杯数学建模思路ABCD题思路汇总分析 MathorCup建模思路分享
    文章目录1赛题思路2比赛日期和时间3组织机构4建模常见问题类型4.1分类问题4.2优化问题4.3预测问题4.4评价问题5建模资料1赛题思路(赛题出来以后第一时间在CSDN分享)https://blog.csdn.net/dc_sinor?type=blog2比赛日期和时间报名截止时间:2024年4月11......
  • P7137 [THUPC2021 初赛] 切切糕 题解
    题目传送门前置知识博弈论解法由于本题是CF1628D1GameonSum(EasyVersion)的扩展,故先从CF1628D1GameonSum(EasyVersion)讲解。CF1628D1GameonSum(EasyVersion)设\(x_{i}\)表示第\(i\)轮时Alice选择的数。设\(f_{i,j}\)表示已经进行了\(i\)轮,且......
  • 一个基于Nodejs环境的小工具,使用Puppeter对网页进行全屏截图
    screenshotsAsmalltoolbasedonNodejsenvironment,usingPuppetertotakefullscreenscreenshotsofanywebpage1.简介 一个基于Nodejs环境的小工具,使用Puppeter对任何网页进行全屏截图 2.特点 如果页面有滚动条会自动滚屏截取全部可见内容如果页面使用......
  • superset安装完整过程
    1.安装superset所需python依赖包wtforms_json、flask_compress、celery、flask_migrate、flask_talisman、flask_caching、sqlparse、bleach、markdown、numpy、pandas、parsedatetime、pathlib2、simplejson,humanize,python-geohash,polyline,geopy,cryptography,backoff,msgpack,pyarr......
  • Elasticsearch 8.x以上实现初始化用户密码,elasticsearch-setup-passwords interactive
    Elasticsearch8.x以上,默认自动开启x-pack验证,在首次启动时,会设置密码,当再次执行elasticsearch-setup-passwordsinteractive就会报错,提示使用elasticsearch-reset-passwords,但是用户太多,还是想要能像8.x以下一直敲回车,设置密码。今天偶然Elasticsearch报错,发现一个方法可以使用,......