关于DMA的学习

来源:互联网 发布:斑马410网络打印设置 编辑:程序博客网 时间:2024/06/05 10:46

在之前做飞思卡尔智能车比赛时接触过K60的DMA,用DMA可以采集图像,也可以用DMA来测速。

概念相信大家都清楚:所谓 DMA就是直接内存取( Direct Memory Access ),是计算机科学中的一种内存访问技术。

书上说,DMA模块可以不占用CPU自行传输数据,能够减轻CPU的负担,然而具体原理是怎样的呢?为什么DMA能够不需要CPU的介入呢?我查阅了相关资料,大概地了解了些。

在STM32中,DMA单元和Cortex CPU之间对总线使用一种交叉存取的机制。DMA传输遵循相应的传输流程。其中,在数据从内存传输到内存的情况下,每传输一个字要消耗5个时钟周期:1个读周期,1个写周期,插入3个空闲周期供CPU使用。所以,每个DMA通道都只是在总线存取周期才会占用总线,即使传输大量数据,DMA单元最大也只会消耗40%的数据总线带宽。

所以说,STM32中的DMA与CPU对总线的使用方式是交叉式的。

在K60中,DMA是通过DMA控制器接管接管数据和地址总线。如果CPU正在执行指令,DMA控制利用空闲的地址和数据总线完成数据传送,某种程度上说,CPU运算和数据传送是在并行进行的。K60的DMA数据的传送分为主循环(major loop)和副循环(minor loop)。major loop循环一次,可能需要minor loop循环多次。每个minor loop循环都需要DMA源发来请求或者通过软件请求。每个minor loop传送完毕,对应的DMA通道就进入空闲模式,等待下一次DMA请求。当所有DMA传送完毕,即置DONE标志,并且可以通过设置选择传送完毕是否触发中断。此外,可以通过相关寄存器设置,使用Kinetis的DMA模块的主/副循环链接功能、散/聚模式、副循环映射。

对DMA模块的相应的寄存器进行初始化后,便可开启DMA功能。例如(K60例程):

以智能车摄像头组图采集应用为例。使GPIO口 D0~D7采集数字摄像头OV7620的 8位灰度输出,使用引脚 位灰度输出,使用引脚 A19输入像素同步脉冲pclk4分频后的信号,上升沿触发 分频后的信号,上升沿触发DMA 请求。 每行采集点数为 V,通过 DMA传送到到 video 数组 。一行采集完成后关闭 DMA 硬件请求,进 入 DMA中断,使用 DMA 通道 0:

void DMA0_Init(void){SIM_SCGC6|=SIM_SCGC6_DMAMUX_MASK;//打开DMA多路复用时钟SIM_SCGC7|=SIM_SCGC7_DMA_MASK;//打开DMA模块时钟DMAMUX_CHCFG0=DMAMUX_CHCFG_SOURCE(49);//DMA通道0对应49号DMA请求,即PORTADMA_TCD0_CITER_ELINKNO=DMA_CITER_ELINKNO_CITER(V);//当前主循环次数,采集点数DMA_TCD0_BITER_ELINKNO=DMA_BITER_ELINKNO_BITER(V);//起始主循环次数,采集点数DMA_TCD0_SADDR=(uint32)&GPIOD_PDIR;//设置源地址GPIO口,PORTDDMA_TCD0_SOFF=0;//每次传送源地址不变DMA_TCD0_NBYTES_MLNO=DMA_NBYTES_MLNO_NBYTES(1);//每次读取一字节DMA_TCD0_SLAST=0;//主循环结束后源地址不变DMA_TCD0_DLASTSGA=0;//主循环结束后目的地址不调整,自动指向下一行数组第一个元素DMA_TCD0_DADDR=(uint32)video;//设置目的地址,video数组第一个元素DMA_TCD0_DOFF=1;//每次写目的地址加1DMA_TCD0_ATTR=DMA_ATTR_SSIZE(0)+DMA_ATTR_DSIZE(0);//源数据8bit,目的数据8bitDMA_TCD0_CSR=DMA_CSR_DREQ_MASK;//通道0主循环结束后停止硬件请求DMA_TCD0_CSR|=DMA_CSR_INTMAJOR_MASK;//使能DMA0中断DMAMUX_CHCFG0|=DMAMUX_CHCFG_ENBL_MASK;//DMA通道0使能 }
DMA的中断服务函数中可登记采集的行数:

void DMA_CH0_ISR(void){DMA_INT|=DMA_INT_INT0_MASK;//清除通道0中断row_F[imagerow]=1;//采集完成标志imagerow++; }

顺便提一句,用DMA测速,就是把编码器或者光电马盘的脉冲当做DMA请求信号,在周期中断中读相应寄存器的值,再考虑上是否溢出,经过简单计算((DMA_MAX_NUM - DMA_BASE_PTR->TCD[DMA_CH0].CITER_ELINKNO)+ DMA_MAX_NUM*ch0_over_cnt),便可得出一定时间内的脉冲数,便可用于测速。

原创粉丝点击