写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做!
本文写于:2025.04.07
前言
本次笔记是用来记录我的学习过程,同时把我需要的困难和思考记下来,有助于我的学习,同时也作为一种习惯,可以督促我学习,是一个激励自己的过程,让我们开始32单片机的学习之路。
欢迎大家给我提意见,能给我的嵌入式之旅提供方向和路线,现在作为小白,我就先学习32单片机了,就跟着B站上的江协科技开始学习了.
在这里会记录下江协科技32单片机开发板的配套视频教程所作的实验和学习笔记内容,因为我之前有一个开发板,我大概率会用我的板子模仿着来做.让我们一起加油!
另外为了增强我的学习效果:每次笔记把我不知道或者问题在后面提出来,再下一篇开头作为解答!
开发板说明
本人采用的是慧净的开发板,因为这个板子是我N年前就买的板子,索性就拿来用了。另外我也购买了江科大的学习套间。
原理图如下
1、开发板原理图
2、STM32F103C6和51对比
3、STM32F103C6核心板
视频中的都用这个开发板来实现,如果有资源就利用起来。另外也计划实现江协科技的套件。
下图是实物图
引用
【STM32入门教程-2023版 细致讲解 中文字幕】
还参考了下图中的书籍:
STM32库开发实战指南:基于STM32F103(第2版)
数据手册
解答和科普
一、DMA
这里外设,指的就是外设的寄存器,一般是外设的数据寄存器,DR,比如ADC的数据寄存器,串口的数据寄存器,这里存储器,值得就是运行内存SRAM和程序存储器Flash,是我们存储变量数组和程序代码的地方。无需CPU的干预,节省了CPU的资源。
如果DMA进行的是存储器到存储器的数据转运,比如我们想把Flash里的一批数据,转运到SRAM里去,那就需要软件触发了, 使用软件触发之后。DMA就会一股脑地, 把这批数据。以最快的速度。全部转运完成.
那如果DMA进行的是外设到存储器的数据转运, 就不能一股脑地转运了, 因为外设的数据是有一定时机的, 所以这时我们就需要用硬件触发。比如转运ADC的数据。那就得ADC每个通道AD转换完成后,硬件触发一次DMA,之后DMA再转运,触发一次,转运一次。所以存储器到存储器的数据转运。我们一般使用软件触发, 外设到存储器的数据转运,我们一般使用硬件触发。
特定的硬件触发:意思就是每个DMA的通道,它的硬件触发源是不一样的,你要使用某个外设的硬件触发源,就得使用它连接的那个通道,而不能任意选择通道。
STM32存储器映像:
计算机的五大组成部分:运算器、控制器、存储器、输入设备和输出设备,其中运算器和控制器,一般会合在一起,叫做CPU,所以计算机的核心关键部分就是CPU和存储器,存储器又有两个重要的知识点,一个是存储器的内容,另一个是存储器的地址,这些都是存储器的一种,包括外设寄存器,实际上也是存储器,本质上都是存储器之间的数据转运。
ROM:只读存储器,是一种非易失性,掉电不丢失的存储器。
RAM:随意存储器,是一种易失性、掉电丢失的存储器
程序存储器Flash:主闪存,存储C语言编译后的程序代码,也就是我们下载程序的位置;运行程序一般也是从主闪存里面开始运行的。其实地址为0x0800 0000;
系统存储器和选项字节也是Flash,在ROM靠后的位置;选项字节存的主要是Flash的读保护、写保护,还有看门狗;
RAM:运行内存SRAM,分配的地址是0x2000 0000,用途是存储运行过程中的临时变量,也就是我们在程序定义变量、数组、结构体的地方;
外设寄存器:0x4000 0000,也是SRAM;
内核外设寄存器:存储内核外设的配置参数;
程序是从0开始运行的,所以这里需要把我们想要执行的程序,映射到0地址来,如果映射在Flash区,就是从Flash执行,如果映射在Flash区,就是从Flash执行,如果映射在系统存储器区,就是从系统存储器运行BootLoade,如果映射到SRAM,就是从SRAM启动,怎么选择,由BOOT0和BOOT1两个引脚来决定,这就是0地址里的别名区。
0800开始的Flash区,用于存储程序代码,1FFF开始的系统存储器和选项字节,是在ROM区的最后面。
2000开始的,是SRAM区,4000开始的,是外设寄存器区:具体到每个外设,又有它们自己的起始地址,比如TIM2的地址是4000 0000,TIM3是4000 0400,然后外设地址里面又可以具体细分到每个寄存器的地址、寄存器里每个字节的地址,最终,所有字节的地址,就都可以算出来了.
最后上面这里,E000开始的区域,存放的就是内核里面的外设寄存器了。
CPU和存储器两个东西,Flash是主闪存,SRAM是运行内存,各个外设,都可以看成是寄存器,也是一种SRAM存储器,寄存器是一种特殊的存储器:一方面,CPU可以对寄存器进行读写,就像读写运行内存一样,另一方面,寄存器的每一位背后,都接了一根导线,这些导线可以用于控制外设电路的状态,比如置引脚的高低电平、导通和断开开关、切换数据选择器,或者多位结合起来,当作计数器\数据寄存器,所以寄存器是连接软件和硬件的桥梁。软件读写寄存器,就相当于在控制硬件的执行,既然外设就是寄存器,寄存器就是存储器,那使用DMA进行数据转运,就可以归为一类问题了,就是从某个地址取内容,再放到另一个地址去.
为了高效有条理地访问存储器,这里设计了一个总线矩阵,总线矩阵的左端,是主动单元,也就是拥有存储器的访问权,右边这些,是被动单元,它们的存储器只能被左边的主动单元读写,主动单元这里,内核有Dcode和系统总线,可以访问右边的存储器,其中Dcode是专门访问Flash的,系统总线是访问其他东西的,另外,由于DMA要转运数据,所以DMA也必须要有访问的主动权,那主动单元,除了内核CPU,剩下的就是DMA总线了,这里有DMA1一条总线,DMA2也有一条总线,下面这还有一条DMA总线,这是以太网外设自己私有的DMA,这个可以不用管的,在DMA1和DMA2里面,可以看到DMA1有7个通道,DMA2有5个通道,各个通道可以分别设置它们转运数据的源地址和目的地址,这样它们就可以各自独立地工作了,接着下面有个仲裁器:虽然多个通道可以独立转运数据,但是最终DMA总线只有一条,所以所有的通道都只能复用这一条DMA总线,如果产生了冲突,那就会由仲裁器,根据通道的优先级,来决定谁先用,谁后用,另外在总线矩阵这里,也会有个仲裁器,如果DMA和CPU要访问同一个目标,那么DMA就会暂停CPU的访问,以防止冲突,不过总线仲裁器,仍然会保证CPU得到一半的总线带宽,使CPU正常工作。
AHB从设备也就是DMA自身的寄存器,因为DMA作为一个外设,他自己也会有相应的配置寄存器,这里连接在了总线右边的AHB总线上,所以DMA,即是总线矩阵的主动单元,可以读写各种存储器,也是AHB总线上的被动单元。
DMA请求就是DMA的硬件触发源, 比如ADC转换完成、串口接收到数据,需要触发DMA转运数据的时候, 就会通过这条线路。向DMA发出硬件触发信号。之后DMA就可以执行数据转运的工作了。这就是DMA请求的作用。
这里的Flash,它是ROM只读存储器的一种,如果通过总线直接访问的话,无论是CPU,还是DMA,都是只读的,只能读取数据,而不能写入,如果你DMA的目的地址,填了Flash的区域,转运时,就会出错。
当然Flash也不是绝对的不可写入,我们可以配置这个Flash接口控制器,对Flash进行写入,这个流程就比较麻烦了,要先对Flash按页进行擦除,再写入数据,不过这是另一个课题了,这里就不在讨论。
总之就是CPU或者DMA直接访问Fash的话。是只可以读而不可以写的。
然后SRAM是运行内存。可以意读写。没有问题, 外设寄存器的话。得看参考手册里面的描述, 有的寄存器是只读的。有的寄存器是只写的, 不过我们主要用的是数据寄存器。数据寄存器都是可以正常读写的。
左边是外设寄存器站点,右边是存储器站点,包括Flash和SRAM,在STM32手册里,他所说的存储器,一般是特指Flash和SRAM,不包含外设寄存器,外设寄存器,他一般直接称作外设,所以就是外设到存储器,存储器到存储器,这样来描述,虽然我们刚才说了,寄存器也是存储器的一种,但是STM32还是使用了外设和存储器来作为区分,这个注意一下描述方法的不同;
在这里可以看到,DMA的数据转运,可以是从外设到存储器,也是可以从存储器到外设,具体是向左还是向右,有一个方向的参数进行控制,另外还有一种转运方式,就是存储器到存储器,比如Flash到SRAM或者SRAM到SRAM,这两种方式,由于Flash是只读的,所以DMA不可以进行SRAM到Flash,或者Flash到Flash的转运操作,然后看两边的参数,既然要进行数据转运,那肯定就要指定从哪里转到哪里,具体怎么转了,所以外设和存储器两个站点,就都有3个参数:
第一个是起始地址,有外设端的起始地址,和存储器端的起始地址,这两个参数决定了数据是从哪里来,到哪里去的,之后第二个参数就是数据宽度,这个参数的作用是,指定一次要转运要按多大的数据宽度来进行,它可以选择字节Byte(8),半字HalfWord(16) 和字Word(32)。
第三个参数,是地址是否自增,这个参数的作用是,指定一次转运完成后,下一次转运,是不是要把地址移动到下一个位置去,外设不用,存储器这里就需要, 每转运一个数据后。就往后挪个坑, 要不然下次再转就把上次的覆盖掉了,这就是地址是否自增的作用,就是指定,是不是要转运一次挪那个坑,这就是外设站点,和存储器站点各自的3个参数了, 如果要进行存储器到存储器的数据转运,那我们就需要把其中一个存储器的地址。放在外设的这个站点, 这样就能进行存储器到存储器的转运了, 只要你在外设起始地址里写Fash或者SRAM的地址,这个站点虽然叫外设寄存器。但是它就只是个名字而已, 并不是说这个地址只能写寄存器的地址,你如果写Flash的地址。那它就会去Fash里找, 写SRAM。它就会去SRAM里找。这个没有限制, 甚至你回以在外设站点写存储器的地址, 存储器站点写外设的地址, 然后方向参数给反过来,这样也是可以的,只是ST公司给它起了这样的名字而已,也可以叫它站点A站点B,不必拘泥于他写的外设站点,存储器站点这个名字的.
传输寄存器,这个东西是用来指定,我总共需要转运几次的,这个传输寄存器是一个自减计数器,每转运一次,计数器减1,当传输寄存器减到0后,DMA就不会再进行数据转运了,另外,它减到0之后,之前自增的地址,也会恢复到起始地址的位置,以方便之后DMA开始新一轮的转运,在传输寄存器的右边,有一个自动重装器,
这个自动重装器的作用就是,传输计数器减到0之后,是否要自动回复到最初处的值,如果不使用自动重装器,计数器转运5次后,DMA就结束了,如果使用自动重装器,那转运5次,计数器减到0后,就会立即重装到初始值5,这个就是自动重装器,它决定了转运的模式,如果不重装,就是正常的单次模式,如果重装,就是循环模式, 比如如果你想转运一个数组。那一般就是单次模式, 转运一轮。就结束了; 如果是ADC扫描模式+连续转换, 那为了配合ADC。DMA也需要使用循环模式, 所以这个循环模式和ADC的连续模式差不多, 都是指定一轮工作完成后,是不是立即开始下一轮工作.
DMA的触发控制:触发。就是决定DMA需要在什么时机进行转运的, 触发源,有硬件触发。和软件触发,具体哪个参数由M2M决定,M2M就是 Menory to Memory,存储器到存储器的意思,M2M为1时,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的基本结构。我就介绍完了。
EN=0,数据选择器不工作,EN=1,数据选择器工作,M2M位=1时,选择软件触发。
每个通道的硬件触发源都是不同的,如果你需要用ADC来触发的话,那就必须选择通道1;
如果需要定时器2的更新事件来触发的话,那就必须选择通道2;这就是特定的硬件触发;
所以如果你想使用某个硬件触发源的话,就必须使用它所在的通道,如果使用软件触发的话,那通道就可以任意选择了。所以是每个通道都支持软件触发和特定的硬件触发。
到底选择哪个触发源呢:这个是对应的外设是否开启了DMA输出来决定的,比如你要使用ADC1,那就会有个库函数叫ADC_DMACmd,必须使用这个库函数开启ADC1的这一路输出,它才有效. 这三个使用哪一个,取决于你把哪个外设的DMA输出开启了,经过一个或门,理论上3个硬件都可以触发,不过一般情况下,我们都是开启其中一个。
之后,这7个触发源,进入仲裁器,进行优先级判断,最终产生内部的DMA1请求,这个优先级的判断,类似于中断的优先级,默认优先级是通道号越小,优先级越高,当然也可以在程序中配置优先级。
如果数据宽度一样,那就是正常的一个个转运,如果数据宽度不一样,那会怎么处理呢?
将SRAM数组中的数组DataA,转运到另一个数组DataB中,基本结构参数如何配置:
在这个任务中,外设地址显然应该填DataA数组的首地址,存储器地址,给DataB数组的首地址;数据宽度,两个数组的类型都是uint_8,所以数据宽度都是按8位的字节传输,之后数据地址是否自增,显示一一对应,俩个地址都应该自增,方向参数,显示是外设站点转运到存储器站点,可以方向看需求;显然要转运7次,所以传输计数器给7,自动重装暂时不需要,之后触发选择部分,这里使用的是软件触发,是不需要等待硬件时机的,尽快转运完成就行了,最后调用DMA_Cmd,给DMA使能,这样数据就会从DataA转运到DataB了。转运7次之后,传输计数器自减到0,DMA停止,转运完成,这里的数据转运是一种复制转运,转运完成后DataA的数据并不会消失。存储器到存储器的数据转运。
左边是ADC扫描模式的执行流程,在这里有7个通道,触发一次后,7个通道依次进行AD转换,然后转换结构放到ADC_DR数据寄存器里面,在每个单独的通道转换完成后,进行一个DMA数据转运,并且目的地址进行自增,这样数据就不会被覆盖了。
在这里DMA配置就是:外设地址写入ADC_DR这个寄存器的地址,存储器的地址可以在SRAM中定义一个数组ADValue,然后把ADValue的地址当做存储器的地址,数据宽度都是16位,外设地址不自增,存储器地址自增,方向是从外设站点到存储器站点,传输计数器,这里通道有7个,所以计数7次,计数器是否自动重装,这里可以看ADC的配置,ADC如果是单次扫描,那么DMA的传输计数器可以不自动重装,转换一轮就停止,如果ADC是连续扫描,那DMA就可以使用自动重装,在ADC启动下一轮转换的时候,DMA也启动下一轮的转运,ADC和DMA同步工作,最后是触发选择,这里ADC_DR的值是在ADC单个通道完成转换后才会有效,所以DMA转运的时机,需要ADC单个通道转换完成同步,所以DMA的触发要选择ADC的硬件触发;
ADC扫描模式。在每个单独的通道完成后,没有任何标志位,也不会触发中断,虽然不产生标志位和中断,但是它应该是会产生DMA请求,去触发DMA转运,手册没有详细说明,但是单个通道的DMA请求肯定是有的。
ADC扫描模式和DMC配合使用,ADC扫描模式有数据覆盖的缺陷,对DMA非常必要,功能都会受到限制。
问题
总结
本节课主要是介绍了DMA直接存储器存取,是数据转运小帮手,不需要CPU的参与,了解了存储器的ROM和RAM等,知道了DMA转运的流程,以及如何配置完成DMA完成转运的操作。