接上一章,继续更。
通道配置
■ 源/目的数据宽度
■ 源/目的地址增量
■ 总线重新仲裁之前传输的数目(仲裁数目)
■ 待传输的数据单元总数
■ 采用猝发传输标志
■ 传输模式
关于控制字及其各个位域的详细介绍,请参阅“μDMA通道控制结构体”在359页。μDMA控制器在传输执行期间自
动更新待传输大小位域以及传输模式位域。当传输结束后,待传输数目将为0,传输模式将变为“已停止”。由于
控制字是由 μDMA 控制器自动修改的,因此在每次新建传输之前必须手动配置。源末指针和目的末指针不会被
自动修改,所以只要源地址或目的地址不变,就无需再进行配置。在启动传输之前,必须将DMA 通道启用置位
(DMAENASET)寄存器中的相应标志位置位,启用μDMA 通道。当需要禁用某个通道时,应将DMA通道启用清除
(DMAENACLR)寄存器中的相应标志位置位。当某个μDMA传输结束后,控制器会自动禁用该通道。
传输模式
停止模式
停止模式虽然是控制字中传输模式位域的有效值,但实际上这并不是一种真正的传输模式。当控制字中的传输模式是停止模式时,μDMA控制器并不会对此通道进行任何传输,并且一旦该通道启用,μDMA控制器还会自动禁
用该通道。在任何μDMA传输结束后,μDMA控制器都会自动将通道控制字的传输模式位域改写为停止模式。
基本模式
则不得采用基本模式。举例来说,如果将某个通道设为基本模式,并且采用软件启动,则启动时只会创建一个
瞬时请求;此时传输的数目等于DMA通道控制字(μDMA)(DMACHCTL)中ARBSIZE位域所指定的数目(即仲裁数
目),即使还有更多数据需要传输也将停止。
在基本模式下,当所有数据单元传输完成后,μDMA控制器自动将该通道置为停止模式。
自动模式
自动模式与基本模式类似,区别在于:每当收到一个传输请求后,传输过程会一直持续到整个传输结束,即使μDMA请求已经消失(瞬时请求)也会持续完成。这种模式非常适用于软件触发的传输过程。一般来说外设都
不使用自动模式。在自动模式下,当所有数据单元传输完成后,μDMA 控制器自动将该通道置为停止模式。
乒乓模式
乒乓模式用于实现内存与外设之间连续不断的数据流。要使用乒乓模式,必须同时配置主数据结构体和副数据结构体。两个结构体均用于实现存储器与外设之间的数据传输,均由处理器建立。传输过程首先从主控制结构
体开始。当主控制结构体所配置的传输过程结束后,μDMA控制器自动载入副控制结构体并按其配置继续传。
每当这时都会产生一个中断,处理器可以对刚刚结束传输过程的数据结构体进行重新配置。于是乎,主/副控制
结构体交替在缓冲区与外设之间搬运数据,周而复始,川流不息。
限于篇幅原因,具体乒乓模式工作原理可以到数据手册DMA模块查看。
聚散模式
由于此模式工作原理较复杂,在此就不讲了,本人研究的也不是很深入。聚散模式是一种很重要的工作模式。外设接口
注意: 当使用μDMA与外设进行数据通信时,外设必须禁用所有到NVIC的中断。当μDMA传输结束后,μDMA控
制器产生一个中断,详见“中断及错误”在351页。关于某种外设与μDMA控制器如何相互配合工作的详细信息,
请参阅该类型外设DMA操作的相关章节。
待传输数目及增量
表8-5列出了从某个支持8位数据的外设进行读操作时的配置。
中断及错误
采用μDMA传输数据,并且启用了该外设中断,那么中断处理函数中必须包含对μDMA传输结束中断的相关处理。
假如传输过程使用了软件μDMA通道,那么结束中断将在专用的软件μDMA中断向量上产生(见352页)。
当启用某外设的μDMA后,μDMA控制器将禁止该外设的普通传输中断传递到中断控制器,不过这些中断的状态仍
然能在外设的中断寄存器中查询到。因此,当采用μDMA传输大量数据时,中断控制器并不会随着数据流从外设频
繁收到中断,而是只在数据传输过程结束后收到一个中断。请注意,未屏蔽的外设错误中断仍会正常发送到中断
控制器。若μDMA控制器在尝试进行数据传输时遇到了总线错误或存储器保护错误,将会自动关闭出错的μDMA通
道,并且在μDMA错误中断向量处产生中断。处理器可以通过读取DMA总线错误清除(DMAERRCLR) 寄存器来确
定是否产生了错误中断。一旦产生错误则ERRCLR标志位将置位。向ERRCLR位写1即可清除错误状态。
初始化及配置
模块初始化
在使用μDMA控制器之前,必须先在系统控制模块中将其启用,并且在外设中启用μDMA 功能。此外,还应当先
设置好通道控制结构体的位置。
系统初始化期间应执行一遍下面的步骤:
b) 通过将DMA配置(DMACFG)中的MASTEREN位置位,启用μDMA控制器。
c) 向DMA通道控制基指针(DMACTLBASE)寄存器写入通道控制表的基地址。基地址必须按照1024字节对齐。
配置通道属性
首先我们应当配置通道属性:
1) 对DMA通道优先置位(DMAPRIOSET)或DMA通道优先清除(DMAPRIOCLR)寄存器的第30位编程,将通道的优先2) 将DMA通道主副清除(DMAALTCLR)寄存器中的第30位置位,为此次传输选择主通道控制结构体。
3) 将DMA通道采用猝发清除(DMAUSEBURSTCLR)寄存器中的第30位置位,以允许μDMA 控制器既能响应单次请
4) 将DMA通道请求屏蔽清零。
配置通道结构体
下面来配置通道控制结构体。
本示例需要实现的功能是:从某个内存缓冲区向另一缓冲区传输256个字。采用第30号通道进行软件启动传输,其启动传输过程
完成通道配置后,即可启动传输过程:
1) 将DMA通道启用置位(DMAENASET)寄存器中的第30位置位以启用该通道。2) 将DMA通道软件请求(DMASWREQ)中的第30位置位,以发送传输请求。
随后就会开始μDMA传输。倘若同时开启了相关中断,那么当传输过程全部结束后还会产生中断事件通知处理器。
如果需要,还需通过读取DMAENASET寄存器中的第30位来检查状态。当传输完成后,此位自动清零。此外也可
通过通道控制字(偏移量0x1E8)的XFERMODE 位域来检查传输状态。当传输完成后,此位自动清零。
EPI + DMA 工作原理
深度为4个字,阻塞时CPU等待当前操作完成,如此便会占用CPU大量时间,为了减少CPU在EPI传输数据使用的
时间,因此使用DMA。使用DMA的好处在于:当一次写大量数据时,CPU不会发生阻塞,因为DMA后台执行,几
乎不占用CPU,而CPU可以有足够的时间处理其他任务。
DMA传输数据需要猝发条件,这个条件是由EPI产生。由数据手册知,当WFIFO的空闲单元数大于等于DMA的仲
裁数目(关于仲裁数目原理见前文)时,自动触发DMA,如果初始化并使能DMA模块,就将开始数据传输。WFIFO
中的数据在通过地址映射到片外,完成数据传输过程。例如:WFIFO空闲单元数为2个(最大4个),DMA仲裁数目
为2个,那么EPI将触发DMA,DMA向WFIFO写数据,由于WFIFO内部已经有两个数据,所以在DMA向WFIFO写
数据过程中,WFIFO的数据也会写到外设,腾出空闲单元,如果此时WFIFO空闲单元数为1,显然触发不了DMA,
于是DMA不向WFIFO写数据,直到满足触发条件。可假设为一个水池,WFIFO就是水池,DMA负责向水池注水,
外设负责放掉水池里的水。如果注入快,输出慢,就会溢出,DMA会等待,然后继续放。如果注入慢,输出快,
水池则永远装不满,当然这种情况在单片机里不太可能出现。
常见陷阱
本人由于当初对EPI不是很了解(第一次听说呀),所以走了不少弯路,在此加以说明,希望用到的朋友不会犯这种
错误。
- 对于片外无地址设备HB8/HB16模式,DMA初始化基址应该是0xC000 0000或0xA000 0000
- 冲裁数目最大只能为4,比较合适的值是2
- 当使用DMA与外设进行数据通信时,必须禁用所有到NVIC的中断(血淋淋的教训啊)
- DMA的EPI请求信号只有猝发请求信号,没有单次请求信号
- EPI配置时,没有必要开传输中断,否则会一直进入中断
- EPI最大数据传输速度为50M/s
代码区
void DMA_Init()
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UDMA);
IntEnable(INT_UDMAERR);
uDMAEnable();
IntEnable(INT_UDMA);
uDMAControlBaseSet(ucControlTable);
uDMAChannelSelectSecondary(UDMA_DEF_TMR1B_SEC_EPI0TX );//| UDMA_DEF_TMR1A_SEC_EPI0RX
uDMAChannelAttributeDisable(UDMA_SEC_CHANNEL_EPI0TX, UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK);
// uDMAChannelAttributeDisable(UDMA_SEC_CHANNEL_EPI0RX, UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK);
uDMAChannelAttributeEnable(UDMA_SEC_CHANNEL_EPI0TX, UDMA_ATTR_USEBURST);
// uDMAChannelAttributeEnable(UDMA_SEC_CHANNEL_EPI0RX, UDMA_ATTR_USEBURST);
uDMAChannelControlSet(UDMA_SEC_CHANNEL_EPI0TX | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_2);
// uDMAChannelControlSet(UDMA_SEC_CHANNEL_EPI0RX | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_4);
}
OVER ^_^