• DMA ( Direct Memory Access )直接存储器存取• DMA 可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须 CPU 干预,节省了 CPU 的资源• 12 个独立可配置的通道: DMA1 ( 7 个通道), DMA2 ( 5 个通道)• 每个通道都支持软件触发和特定的硬件触发•• STM32F103C8T6 DMA 资源: DMA1 ( 7 个通道)
协助CPU,完成数据转运的工作
DMA可以直接访问STM32的内部存储器,包括运行内存SRAM、程序存储器Flash和寄存器等
外设的数据寄存器(DR,Data Register)
存储器指的是运行内存SRAM和程序存储器Flash,是我们存储变量数组和程序代码的地方
存储器到存储器之间需要软件触发
外设到存储器之间的数据转运需要硬件触发(需要一定时机)
ADC每个通道AD转换完成后,硬件触发一次DMA,之后DMA再转运,触发一次,转运一次
每个不同的DMA通道,它的硬件触发源是不一样的,你要使用某个外设的硬件触发源,就得使用它连接的那个通道,而不能任意选择通道
计算机系统的五大组成部分是运算器、控制器、存储器、输入设备和输出设备,运算器和控制器合在一起叫做CPU
ROM是只读存储器,是一种非易失性、掉电不丢失的存储器;RAM是随机存储器,是一种易失性、掉电丢失的存储器
每个字节都有独一无二的地址,实际上系统存储器和选项字节本质都是Flash,只不过我们一般说Flash指的是主闪存Flash,而不指这两块区域
BootLoader程序是芯片出厂自动写入的,一般也不允许我们修改
下载程序可以不刷新选项字节的内容,这样选项字节的配置就可以保持不变,选项字节里存的主要是Flash的读保护、写保护,还有看门狗等等的配置
内核外设就是NVIC和SysTick,因为内核外设和其他外设不是一个厂家设计的,所以他们的地址也是被分开了
程序从0开始运行,所以需要把我们想要执行的程序映射到0地址上,如果映射到Flash区,就是从Flash执行,如果映射到系统存储器区,就是从系统存储器运行BootLoader,如果映射到SRAM,就是从SRAM启动,怎么选择,由BOOT0和BOOT1两个引脚来决定
Cortex-M3内核包含了CPU和内核外设等,剩下所有都可以看成是存储器
Flash是主闪存,SRAM是运行内存,各个外设可以看成是寄存器,也是一种SRAM存储器
为了高效有条理地访问存储器,这里设计了一个总线矩阵,总线矩阵的左端是主动单元,也就是拥有寄存器的访问权;右边是被动单元,他们的存储器只能被左边的主动单元读写。主动单元这里,内核由DCode和系统总线,可以访问右边的存储器,其中DCode总线式专门访问Flash的,系统总线是访问其他东西的,另外,由于DMA要转运数据,所以DMA也必须要有访问的主动权。那主动单元,除了内核CPU,剩下的就是DMA总线了。
以太网外设自己私有DMA
仲裁器的存在是因为虽然多个通道可以独立转运数据,但是最终DMA总线只有1条,所以所有的通道都只能分时复用这一条DMA总线,如果产生了冲突,那就会由仲裁器,根据通道的优先级决定谁先用、谁后用。另外在总线矩阵也有个仲裁器,如果DMA和CPU都要访问同一个目标,那么DMA就会暂停CPU的访问,以防止冲突。不过总线仲裁器,仍然会保证CPU得到一半的总线带宽,使CPU也能正常的工作。
AHB从设备是DMA自身的寄存器,DMA作为外设,自己也会有相应的配置寄存器,这里连接在了总线右边的AHB总线上,所以DMA,即是总线矩阵的主动单元,可以读写各种存储器,也是AHB总线上的被动单元。CPU通过这条线路可以对DMA进行配置。
DMA请求,请求就是触发的意思,这条线路右边的触发源是各个外设,所以这个DMA请求就是DMA的硬件触发源
转运过程中,每转运一次,计数器的数就会减1,当传输计数器减到0之后,DMA就不会再进行数据转运了。另外,它减到0之后,之前自增的地址,也会恢复到起始地址的位置,以方便之后DMA开始新一轮的转运。自动重装器的作用就是,传输计数器减到0之后,是否要自动恢复到最初的值。 如果不重装就是单次模式,如果重装就是循环模式。
M2M:Memory to Memory,存储器到存储器
软件触发的执行逻辑是,以最快的速度,连续不断地触发DMA,争取早日把传输计数器清零,完成这一轮的转换,可以理解为连续触发。这个软件触发和循环模式,不能同时使用,因为软件触发就是想把传输计数器清零,循环模式是清零后自动重装,如果同时用的话DMA就停不下来了。软件触发一般适用于存储器到存储器的转运。硬件触发源可以选择ADC、串口、定时器等等,使用硬件触发的转运,一般都是与外设有关的转运。
开关控制 DMA_Cmd使能DMA,传输计数器必须大于0,必须要有触发信号。传输计数器为0之后,需要Disable,然后关闭DMA,再为传输计数器写入一个大于0的数,再DMA_Cmd,给Enable开启DMA。注意:写传输计数器时,必须要先关闭DMA,再进行,不能在DMA开启时,写传输计数器,这是手册的规定。
EN位是开关控制,EN=0时数据选择器不工作,EN=1时数据选择器工作,EN并不是数据选择器的控制位,而是决定这个数据选择器要不要工作 ;当M2M位=1时,选择软件触发
比如你要使用ADC1,那会有个库函数叫ADC_DMACmd,必须使用这个库函数开启ADC1的这一路输出,它才有效
默认优先级是通道号越小,优先级越高,当然也可以在程序中配置优先级
数据宽度与对齐(数据宽度不一样怎么处理):
位段区相当于是位寻址,它把外设寄存器和SRAM中所有的位都分配了地址,你操作这个新的地址,就相当于操作其中的一位,因为32位的地址有99%是空的,所以地址空间很充足,即使把每一位都单独编码,那也是毫无压力,所以就存在了这样一个位段,用于单独操作寄存器或SRAM的某一位。位段区是另找了一个地方,开辟了一段地址区域,其中SRAM位段区是2200开头的区域,外设寄存器的位段是4200开头的区域。
当读写一个保留的地址,将会产生DMA传输错误。
有传输过半、传输完成和传输错误的中断
--------------------------------------------------------------------------------------------
第一步,RCC开启DMA的时钟
第二步,直接调用DMA_Init,初始化参数,包括外设和存储器站点的起始地址、数据宽度、地址是否自增、方向、传输计数器、是否需要自动重装、选择触发源还有通道优先级。然后DMA_Cmd就使能DMA了。如果是硬件触发,不要忘了在对应的外设调用XXX_DMACmd,开启触发信号的输出。如果需要DMA的中断,那就调用DMA_ITConfig,开启中断输出,再在NVIC里,配置相应的中断通道,然后写中断函数就行了。如果传输计数器清零了,还想传输的话,就失能DMA、写传输计数器、DMA使能。
//恢复缺省配置
void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);
//初始化
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);
//结构体初始化
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);
//使能DMA
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
//中断输出使能
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);
//给传输计数器写数据的
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);
//返回传输计数器的值
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
//获取标志位状态、清除标志位、获取中断状态、清除中断挂起位
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);
void DMA_ClearFlag(uint32_t DMAy_FLAG);
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);
void DMA_ClearITPendingBit(uint32_t DMAy_IT);
DMA转运有三个条件:
第一个:传输计数器大于0
第二个:触发源有触发信号(软件触发一直都会有触发信号)
第三个:DMA使能
中断挂起:排队
中断标志:正在执行
DMA4个标志位:第一个是全局标志位,第二个是转运完成标志位,第三个是转运过半标志位,第四个是转运错误标志位