DMA(Data Memory Access)直接存储器访问
DMA(Data Memory Access)直接存储器访问
DMA是不经过CPU控制,直接把数据从一个设备传给另一个设备。
DMA有两个控制器,DMA1,和DMA2。
DMA1支持:外设到存储器,存储器到外设
DMA2:外设到存储器,存储器到外设,存储器到存储器。
每个数据流都与一个 DMA 请求相关联,此 DMA 请求可以从 8 个可能的通道请求中选出。
此选择由 DMA_SxCR 寄存器中的 CHSEL[2:0] 位控制。
DMA1请求映射
DMA2请求映射
FIFO——源和目标之间的一个数据中转站。(first in, first out)
1—每个数据流有4字(16字节)FIFO,阈值级别有1/4、1/2、3/4、4/4(满),可以通过 DMA数据流
xFIFO 控制寄存器 DMA_SxFCR 的 FTH[1:0]位来控制 FIFO 的阈值,如果数据存储量达到阈值级别时,FIFO 内容将传输到目标中。
2—DMA传输具有 FIFO模式和直接模式。在开启FIFO的时候,直接模式要禁止。
直接模式在每个外设请求都立即启动对存储器传输。在直接模式下,如果 DMA配置为存储器到外设传输,那 DMA会见一个数据存放在 FIFO 内,如果外设启动 DMA传输请求就可以马上将数据传输过去。
FIFO 用于在源数据传输到目标地址之前临时存放这些数据。如果数据存储量达到阈值级别时,FIFO 内容将传输到目标中。
FIFO 对于要求源地址和目标地址数据宽度不同时非常有用,比如源数据是源源不断的字节数据,而目标地址要求输出字宽度的数据,即在实现数据传输时同时把原来 4 个 8 位字节的数据拼凑成一个 32 位字数据。此时使用 FIFO功能先把数据缓存起来,分别根据需要输出数据。
FIFO的阈值配置
1 - FIFO大小:4个字,16个字节,半字即2个字节,字即4个字节。
2 - 节拍:即MSIZE的单位。
两个DMA控制器系统实现
编程时需要用到的固件库函数
DMA的实验设计
1- M to M:FLASH to SRAM,把内部FLASH的数据传输到内部的SRAM。
2 - M to P: SRAM to 串口,同时LED等闪烁,演示DMA传数据不需要占用CPU。
M to M 编程要点
1 - 在FLASH中定义好要传输的数据,在SRAM中定义好用来接收的FLASH数据的变量。
2 - 确定使用DMA2,哪个数据流,哪个通道?然后定义宏,方便修改。
3 - 初始化DMA,主要是配置DMA初始化结构体。
参考《STM32F4XX参考手册》9.3.17流的配置过程
4 - 编写数据比较函数。
5 - 编写main函数。
编程注意:
通过const定义一个数组,这样的数组存放在常量区中,(常量存放在内部flash中),再定义一个全局数组不加const的,存放在全局区,(变量存放在SRAM中)。
先开时钟(时钟就是心脏),DAM初始化需要先复位,而且要等待复位完成!
/* DMA数据流通道选择 */ DMA_InitStructure.DMA_Channel = DMA_CHANNEL; /* 源数据地址 */ DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer; /* 目标地址 */ DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)aDST_Buffer; /* 存储器到存储器模式 */ DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory; /* 数据数目 */ DMA_InitStructure.DMA_BufferSize = (uint32_t)BUFFER_SIZE; /* 使能自动递增功能 */ DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; /* 使能自动递增功能 */ DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; /* 源数据是字大小(32位) */ DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; /* 目标数据也是字大小(32位) */ DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; /* 一次传输模式,存储器到存储器模式不能使用循环传输 */ DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; /* DMA数据流优先级为高 */ DMA_InitStructure.DMA_Priority = DMA_Priority_High; /* 禁用FIFO模式 */ 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数据流参数配置 */ DMA_Init(DMA_STREAM, &DMA_InitStructure);
/* 清除DMA数据流传输完成标志位 */ DMA_ClearFlag(DMA_STREAM,DMA_FLAG_TCIF);
/* 使能DMA数据流,开始DMA数据传输 */
DMA_Cmd(DMA_STREAM, ENABLE);
所以上面的最后两句需要:
/* 清除DMA数据流传输完成标志位 */
DMA_ClearFlag(DMA_STREAM,DMA_FLAG_TCIF);
/* 使能DMA数据流,开始DMA数据传输 */
DMA_Cmd(DMA_STREAM, ENABLE);
这个软件写1的寄存器是LIFCR,对LIFCR寄存器相应的位写1,就可以吧TCFIx寄存器相应的位清零。
M to P 编程要点
1 - 初始化串口(从现有的例程移植过来)
2 - 配置DMA初始化结构体
3 - 编写主函数(开启串口发送DMA请求)。
这个和M TO M有一点不同,需要我们注意,我们通过 M TO P,外设选择串口,内存选择数组需要注意以下几点:
1.和 M TO M不同,DMA初始化之后,需要外设发送DMA请求,才会发生DMA。
int main(void) { uint16_t i; /* 初始化USART */ Debug_USART_Config(); /* 配置使用DMA模式 */ USART_DMA_Config(); /* 配置RGB彩色灯 */ LED_GPIO_Config(); printf("\r\n USART1 DMA TX 测试 \r\n"); /*填充将要发送的数据*/ for(i=0;i<SENDBUFF_SIZE;i++) { SendBuff[i] = 'A'; } /*为演示DMA持续运行而CPU还能处理其它事情,持续使用DMA发送数据,量非常大, *长时间运行可能会导致电脑端串口调试助手会卡死,鼠标乱飞的情况, *或把DMA配置中的循环模式改为单次模式*/ /* USART1 向 DMA发出TX请求 */ USART_DMACmd(DEBUG_USART, USART_DMAReq_Tx, ENABLE); /* 此时CPU是空闲的,可以干其他的事情 */ //例如同时控制LED while(1) { LED1_TOGGLE Delay(0xFFFFF); } }
注意主函数中的红色部分,初始化DMA完毕,需要外设发起DMA请求,我们想要通过串口发送,所以是发送请求,虽然数据是从内存传到串口数据寄存器再传到串口调试助手的,但我们的目的是串口DMA发送,不占用CPU资源,所以是发送请求。
2.为了演示DMA传输时不占用CPU,我们把DMA模式设置成为
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;循环模式,一直发送,但是可能串口调试助手会有卡屏现象,不过这样的演示效果最好。
/*usart1 tx对应dma2,通道4,数据流7*/ DMA_InitStructure.DMA_Channel = DEBUG_USART_DMA_CHANNEL; /*设置DMA源:串口数据寄存器地址*/ DMA_InitStructure.DMA_PeripheralBaseAddr = DEBUG_USART_DR_BASE; /*内存地址(要传输的变量的指针)*/ DMA_InitStructure.DMA_Memory0BaseAddr = (u32)SendBuff; /*方向:从内存到外设*/ DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
内存是SendBuff数组(源地址),外设地址是串口的DR寄存器地址(目标地址),方向是内存到外设。
其余一些FIFO,或者突发模式,根据以后的业务需要做调整。
欢迎加入作者的小圈子
扫描下方左边二维码加入QQ交流群,扫描下方右边二维码关注个人微信公众号并,获取更多隐藏干货,QQ交流群:859800032 微信公众号:Crystal软件学堂
作者:Liu_Jing bilibili视频教程地址:https://space.bilibili.com/5782182 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在转载文章页面给出原文连接。 如果你觉得文章对你有所帮助,烦请点个推荐,你的支持是我更文的动力。 文中若有错误,请您务必指出,感谢给予我建议并让我提高的你。 |