logo资料库

STM32F4 DMA双缓冲的正确打开方式.doc

第1页 / 共3页
第2页 / 共3页
第3页 / 共3页
资料共3页,全文预览结束
关于STM32 DMA双缓冲的话题
关于 STM32 DMA 双缓冲的话题 目前 STM32 家族中有些系列支持 DMA 的双缓冲模式,比如 STM32F2/STM32F4/STM32F7 等系列。尤其随着人们对 STM32F4/F7 系列应用不断拓宽和加深,在设计中运用到 DMA 双缓冲的场合也越来越多。STM32 芯片中的 DMA 又可分为两 大类,一类是通用 DMA,一类是专用 DMA,比如用于 USB,TFT LCD,ETHERNET 等外设应用上的 DMA。这里要谈的是基于通用 DMA 的话题,不妨以 STM32F4 系列芯片为例。 --------------------------------- 关于 STM32F4 的 DMA 双缓冲传输在 STM32F4 系列的参考手册里做了简单描述。因为它是基于介绍了单缓冲模式的 DMA 1. 2. 3. 介绍之后接着介绍的,稍显言简意赅。 相比单缓冲的数据流,双缓冲多了一个 DMA 存储区和相应的存储指针; 如果使能 DMA 双缓冲,硬件会自动使能 DMA 的循环传输模式; 每一批数据传输结束,或者说每次传输事务结束时通过交换存储指针实现更换存储区的目的。 4.DMA 双缓冲模式仅在外设与存储器间进行,不支持 memoryto Memory 间的传输。 基于 DMA 双缓冲模式的的特点,不难理解在应用中必须开辟两个存储区以及存放两 个存储区首地址的存储寄存器,DMA_SxM0AR 和 DMA_SxM1AR。 DMA_SxM0AR:指向存储区 0,单缓冲模式下默认使用该寄存器做存储区指针。 DMA_SxM1AR:指向存储区 1,仅在 DMA 双缓冲模式下才能使用。 DMA 正在访问的当前存储区由 CR@DMA_SxCR 表示 CT = 0:DMA 正在访问存储区 0,CPU 可以访问存储区 1 CT = 1:DMA 正在访问存储区 1,CPU 可以访问存储区 0 使用 DMA 双缓冲传输,既可以减少 CPU 的负荷,又能最大程度地实现 DMA 数据传输和 CPU 数据处理互不打扰又互不耽 搁,同时也给应用开发也带来方便。比如,假设你使用 DMA 单存储缓冲,有些情况下可能是等待 DMA 搬完了数据,CPU 才过来处理;有些情况下可能是 DMA 一边传输,CPU 也一边来访问,这时往往会使用到环形存放和读取,代码实现起来 稍显繁琐也容易出纰漏。如果改为 DMA 双缓冲模式,应用上实现起来也就简洁很多。再加上 DMA 双缓冲模式的循环特性, 使用它对存储区的空间容量要求也会大大降低。尤其在大批量数据传送时,你只需开辟两个合适大小的存储区,能满足 DMA 在切换存储区时的当前新存储区空出来就好,并不一定要开辟多大多深的存储空间。有过这方面应用经验的工程师 可能就有体会,单纯一味地加大双缓冲区的深度并不明显改善数据传输状况。关于这点不妨打个比方,某茶馆有俩芳名 分别为 CPU 和 DMA 的伺茶 MM,,每人手里有个同样茶壶。DMA 负责把她手里的茶壶装满茶水就好,CPU 就负责用从 DMA 手 里接过装满茶水的壶给客人倒茶,倒完了用空壶与 DMA 交换装满茶水的壶继续工作。显然,只要保证 CPU 妹妹茶壶里总 有茶水,至于那两个茶壶选多大容积并不是很重要。倒是那个茶壶进出口径对整个事情的效率有影响。 关于 DMA 双缓冲话题,我们也不妨看看一个具体的案例加深下印象。案例来自网络,为了尽量压缩篇幅,我省却了 部分配置代码,留下需要交流的关键语句。 &&&&&&&&&&&&&&&&& F407 DMA 的 double Buffer mode 上卡了好久了!大家看看配置哪里出问题了? uint8_tBuffer0[] = {0x11,0x22,0x33,0x44}; //无符号的 8 位整型数
uint8_tBuffer1[] = {0xaa,0xbb,0xcc,0xdd}; //无符号的 8 位整型数 voidUSART3_DMA1_Configuration(void) { ...... DMA_InitStructure.DMA_PeripheralBaseAddr= USART3_DR_Addr; //外设首地 DMA_InitStructure.DMA_Memory0BaseAddr= (uint32_t)Buffer0; //内存区首地址(1) DMA_InitStructure.DMA_DIR= DMA_DIR_MemoryToPeripheral; //内存->外设 DMA_InitStructure.DMA_BufferSize= 8; //*****传输数据个数为 8 *****(2) DMA_InitStructure.DMA_PeripheralInc= DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc= DMA_MemoryInc_Enable; // DMA_InitStructure.DMA_PeripheralDataSize= DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize= DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode= DMA_Mode_Circular; //循环传输 …… DMA_DoubleBufferModeConfig(DMA1_Stream3,(uint32_t)Buffer1, DMA_Memory_1);//(3) DMA_DoubleBufferModeCmd(DMA1_Stream3,ENABLE);// (4) enable double buffle DMA_Init(DMA1_Stream3,&DMA_InitStructure); DMA_Cmd(DMA1_Stream3,ENABLE); //使能 DMA1_Stream3 通道 DMA_ClearITPendingBit(DMA1_Stream3,DMA_IT_TCIF3); DMA_ITConfig(DMA1_Stream3,DMA_IT_TC, ENABLE); } &&&&&&&&&&&&&&&&& 发帖者述说,如果将蓝色语句(3)的 DMA_Memory_1 改成 DMA_Memory_0 的话,就能正常打印出 11 22 33 44 aa bb cc dd,如果换成 DMA_Memory_1 的话,现象就不对了!输出的结果却是 aa bb cc dd 15 00 08 52。请问是怎么回事? 显然发帖者使用 STM32F4 系列芯片 DMA 的双缓冲功能,应该只是做做实验而已。他开辟了两个长度均为 4 字节的 缓冲存储区 BUFFER0 和 BUFFER1。从基于 ST 固件库函数代码配置角度看,双缓冲模式相比单缓冲模式,就是多了(3)(4) 两句,其它都一样。这里我们特别留意下其中(1)(2)(3)句配置代码。 绿色语句(1)配置了存储区 0 指针指向的地址; 红色代码语句(2)处给出了 DMA 每轮的传输数据个数 8; 蓝色代码语句(3)处配置存储区 1 的地址和选择第一个当前存储区; 整体上看,该配置都配置了。结合我们上面的原理介绍,可以看出红色代码语句(2)配置每轮 DMA 传输个数为 8 有点问 题,传输的数据宽度为 BYTE,两个缓冲区各自空间大小为 4 BYTE。也就是说每传输 4 个 BYTE 数据就轮换存储区重开下 一轮传输,每轮 DMA 传输的数据个数应该是 4 而不是 8。 现在发帖者反馈的是调整语句(3)便会呈现不同的结果,当把第(3)句的当前存储区改为 Memory0 时就会呈现貌似 正确的结果。那是为什么呢? 其实这个貌似正确的结果是种巧合的假象。巧合的是在定义 BUFFER0 和 BUFFER1 时,因为二者紧邻在一起定义, 编译器刚好把二者安排在连续的 8 个字节存储单元。而发帖者又刚好将每轮 DMA 传输数据个数定义为 8 个缓冲单元,这 意味着每传输 8 个缓冲单元数据才切换缓冲区。当从 Memory0 即 BUFFER0 开始传输时,连续的 8 个数据在第一轮就读了 出来,也就是说这 8 个数据并未经过缓冲区的切换就读出来了。而当发帖者把第(3)句的第一次使用的当前存储区改为 Memory1 时就没那么幸运了。因为这次 DMA 从 BUFFER1 开始连续读取 8 个数据单元,读完 BUFFER1 内的 4 个单元后,后 面的 4 个缓存单元就是些不确定的数据,自然一眼就看出结果不对了。
实际上,当把上面红色代码语句(2)处的 DMA 传输数据个数调整为 4 时就结果正常了,至于第(3)句的起始当前缓 冲区的选择无关紧要。 有人在使用 DMA 双缓冲模式时,经常为这个传输个数纠结,尤其从单缓冲模式转为双缓冲模式时。其实,不管单缓 冲还是双缓冲模式,对于整体需要传输的数据个数是不会增减的,只是双缓冲模式由之前的单缓冲模式变成双缓冲循环。 一般来讲对于那些无需循环的小数量数据传输没必要使用 DMA 双缓冲模式。 相比单缓冲 DMA 传输,双缓冲模式在设置 DMA 传输数据个数时应更为灵活。比方之前单缓冲 DMA 传输时,每轮传输 数据个数假设为 1024。当改为双缓冲循环模式时,对应每个缓冲区的 DMA 传输数据个数并不一定要设置为 1024,可能 设置 50、100 就能满足要求,因为这里有两个存储区且是不停轮换的。不过,对于这个 DMA 传输数据个数的设置和使用 要注意几点: 该数据不要太小,因为 DMA 传输过程中往往伴随 DMA 传输完成中断,如果过小会导致中断频繁和切换频繁, 并非好事。 该数据也不必过大,上面也提过,一味加大缓冲容量对提升传输速度并无实质改善。同时也得考虑芯片内存 容量的限制与合理使用。 尽管 DMA 双缓冲模式基于循环传输,但实际应用中 DMA 传输请求总有中止或停止的时候。比如,一副图像数 据,完全可能不是刚好结束在事先设置的 DMA 传输数据个数的整数倍的位置点。那么,最后的这批缓冲数据因为未满而 不会发生缓冲交换请求或传输完成请求。此时如果不做适当的处理,这批缓冲数据就可能被无意中丢弃掉。所以,我们 在程序中需要设计些基于两次缓冲切换的超时机制,及时收取最后一批缓冲区的数据,以防因不能产生传输完成或缓冲 切换事件而导致数据丢失的现象。 1. 2. 3. 完结
分享到:
收藏