YTM32的DMA控制器要点简析

电子说

1.3w人已加入

描述

引言

考虑到DMA是一个AHB Master设备,可以同处理器内核一样,主动向总线发起传输请求,将“小脏手”伸向各总线上挂载的各外设模块,因此,我更愿意把DMA看做是处理器核心服务的一部分,甚至把它当成一个小核或者协处理器都不为过。

DMA可以在CPU之外,捕获到触发信号后,自行搬运数据从指定地址到另一个指定地址,并且还可以根据预先的配置自动计算下一次搬运的地址。使用DMA可以有效地节约CPU处理海量数据传输的负载。可以想见,如果使用中断方式处理通过外设发送或者接收数据,CPU将会在频繁切换中断服务之间花费大量的时间。

另外,DMA从DMAMUX获取的触发源,能够实现的自动读写的操作,若是某些读操作或者写操作能够产生额外的触发事件,还可以传递触发,形成触发链,最终可实现一些完全不需要硬件干预的自动化任务。总之,DMA真心是一个功能丰富的模块。

本文将介绍YTM32平台上DMA的工作机制,对关键概念展开讲解。

简介

YTM32(以YTM32B1ME05为例)微控制器上集成的DMA控制器可以支持16个通道,并且搭配了一个多达128个选项的DMA MUX模块,可以对接任何一个通道。其中多个通道是复用同一个DMA控制器的,并且共用同一个数据搬运引擎。DMA MUX管理了可以触发DMA通道的硬件事件,每款芯片可能都不一样(YTM32B1MD14中的DMAMUX只有64个选项),在具体使用时,需要从具体的芯片手册中查表。

还需要特别注意的是,目前YTM32平台上的DMA尚未支持异步时钟模式,它使用core clock驱动,仅能在普通模式下工作,在休眠模式、深度休眠以及更低功耗的休眠模式下,均停止工作。

本DMA控制器通过多个通道的传输任务描述符(CTS)管理搬运数据的过程,并且还支持链接模式,即将多个传输任务描述符连链接在一起,形成传输任务链。

原理与机制

DMA控制器是一个AHB总线主机,但仍同普通的外设一样,作为一个APB总线从机,被配置成合适的工作模式。DMA控制器通过DMAMUX,可以直接收集来自片上其他外设模块发出的触发信号,进而触发DMA在地址空间搬运数据的过程。如图x所示。

DMA控制器

图x DMA控制器的系统框图

需要注意的是,DMA的搬运过程是在地址空间内操作的,可以是从内存到内存,从外设到外设,在内存与外设之间等,对于DMA而言,只是搬数,至于数据映射到物理设备是外设还是内存,均由总线负责落实。

相对于有的DMA控制器将触发信号和搬运数据源头地址或目标地址绑定的设计,YTM32的DMA控制器将触发信号和搬运任务所使用的地址相互独立,例如,当某个定时器模块产生的触发信号触发了DMA的一个搬运数据的任务(通道),这个任务可以将ADC转换结果的数据搬运到内存中。这其中,定时器和ADC是没有直接关联的。

DMA通道的传输任务描述符

YTM32的DMA的各个通道,可以看做是各自独立的传输任务,每个任务都有自己的触发条件、对触发条件的响应方式、搬运数据的源地址和目的地址、搬运数据的带宽、搬运数据的数量、每次搬运完成后对搬运过程进行调整的策略等,如此看来,DMA控制器就是这些独立任务的调度器,当多个任务被同时触发时,以一定的调度策略安排他们依次运行。

DMA通道对应的这些独立的搬运任务,在DMA引擎的建模中,被称为CTS(DMA Channel Transfer Structure),其结构如图x所示。

DMA控制器

图x DMA通道的传输任务描述符

这个结构的内容并不是以指针的方式存放在SRAM中,而是直接做在寄存器结构里,可以在DMA的寄存器清单中找到与之一一对映的寄存器。如图x所示。

DMA控制器

图x CTS对应的每个通道各自下辖的一组寄存器

但CTS也可以存放在RAM中,若配置了DMA_CTS_CSR[RLDEN]=1,则在大循环完成后,直接从寄存器DMA_CTS_DTO(原来存放的是地址偏移量)存放的指针进行索引,搬运整个CTS结构体的内存覆写到CTS对应的寄存器中。

除此之外,只是借鉴CTS结构中相关的寄存器,去配置DMA传输任务的参数即可,不必受限于CTS的抽象数据结构。

DMA的触发信号

YTM32的DMA控制器为每个DMA搬运任务设计了两种触发方式:软件触发和硬件触发。其中,软件触发可由CPU直接向DMA控制器的寄存器写数(DMA_CTSn_CSR[START]),主动启动传输过程;硬件触发使用预设的硬件触发信号,当来自外设的硬件触发信号通过DMAMUX到来之时,自动启动DMA搬运任务开始搬数。DMA的每次触发,执行一次小循环的搬运过程,一个大循环可以包含多个小循环的,因此一个大循环的搬运任务可能会需要多次触发才能完成。(关于大小循环的概念,可见楼下)

  1. 软件触发

DMA的软件触发是通过软件写各通道的寄存器位DMA_CTSn_CSR[START],或者寄存器DMA_START中对应通道的控制位实现的,每写1次就发出一个触发信号。每次触发,执行一次小循环(one trigger loop)的搬运过程,TCNT寄存器中的计数器减1。

特别注意,软件触发是直接作用于DMA控制器的,不必配置DMAMUX的那个always_on的选项。但由此也可知,哪怕有可用的硬件触发通过DMAMUX输入到DMA控制器,软件触发也可以生效。相当于是,软件触发和DMAMUX导入的硬件触发信号相或,然后统一输入到DMA控制器。

  1. 硬件触发

DMA的硬件触发信号来自于DMAMUX,而DMAMUX则可以从众多触发信号的源中选择其中一个适用于某个指定的通道(寄存器DMA_CHMUXn)。具体选项可在芯片手册中查阅,如图x所示。

DMA控制器

图x 从手册中查阅DMAMUX选项

DMAMUX选中的触发信号,还需要经过一个REQEN的门控开关(寄存器DMA_REQEN中对应通道的控制位),才能顺利进入DMA引擎。因此,每次使用DMA开始传输之前,如果要使用外部的硬件触发源,必须确保打开这个门控开关。另外,每个DMA通道的传输描述符中的寄存器位DMA_CTS_CSR[DREQ]=1还可以控制在每个大循环传输完成之后,自动关闭这个门控开关。如果DMA_CTS_CSR[DREQ]=0,则这个门控开关在大循环传输完毕后仍会保持打开。

这里提到的硬件触发信号,是直接来自于外设的DMA触发信号,通常会伴随着这些外设的某些事件的发生,大多同时也可以触发中断。以LINFlexD为例,有对应的DMA触发信号的开关,如图x所示。

DMA控制器

图x SPI外设模块的使能DMA请求控制位

DMA的大循环和小循环

一个最完整的DMA传输,可以包含多次触发,而每次触发,会引起连续地搬运一块数据(可以是连续的多个字节)。以此,完整的DMA搬运有大循环(Major Loop)小循环(Minor Loop)的概念,大循环包含小循环。

YTM32的手册中使用了Transfer LoopTrigger Loop的名字:

  • Transfer loop means data transferred after one DMA channel trigger.
  • Trigger loop means DMA channel could accept how many DMA channel triggers(include software and hardware trigger).

从手册的描述中可以获知,Transfer Loop描述的是一次触发(one trigger)执行的包含若干个transfer的搬运过程,而Trigger Loop可以包含多个触发(many triggers),对应大循环和小循环。如图x所示。

DMA控制器

图x DMA搬数过程中的大循环和小循环

小循环搬运的字节数,由各DMA通道的BCNT寄存器指定,它本身也是一个递减计数器,每传输一个字节就减1,减到0时就停止搬运。

大循环的包含的小循环的次数(不是字节数,是对触发信号的计数),由各DMA通道的TCNT_KDDIS[TCNT]寄存器字段指定,它本身也是一个递减计数器,每执行一次小循环(触发)就减1,减到0时就停止。特别注意,此处的大循环管理的仅仅是触发,而不是传输内存块,如果使用多个传输任务描述符链接起来的传输任务描述链表,则每个任务描述符(可能在不同的地址块和传输模式搬运数据)都对应属于各自的触发次数(同一个通道的触发源仍为同一个)。

大循环执行一半和完毕时都有对应的标志位(DMA_CHTLHDIFDMA_CHTLDIF),这里有个特别的设计,只有启用DMA传输通道的大循环半完成和全完成的中断时(DMA_CTS_CSR[THDINT]=1DMA_CTS_CSR[TDINT]=1),这两个标志位才会置位,否则哪怕对应的事件到来,也不会被置位。但另一个传输完成标志位(DMA_DONEDMA_CTS_CSR[DONE]),无论是否开启对应的中断(DMA_CTS_CSR[LOOPINT]),都能置位。这里就有一点小纠结了,如果同时启动了DMA_CTS_CSR[LOOPINT]DMA_CTS_CSR[TDINT]=1DMA_DONEDMA_CHTLDIF所对应的行为将完全一样,那么在一个大循环完成后产生中断,其中的服务程序就需要同时清零这两个标志位。(这里的设计似乎有点冗余,有似乎缺了点什么。。。)

DMA搬运任务的地址更新策略

DMA外设设计了非常灵活的搬运地址更新策略,可以覆盖最大范围的应用场景。但需要整理清楚其中的概念和更新时机,才能玩转DMA,否则,一不小心产生了错误的参数配置状态,DMA也将会停止工作并报错(DMA_ERS)。

重申一次DMA搬数中的操作单元:

  • 一次指定带宽的总线传输被称为一个搬运Transfer
  • 一次触发可以发起一个或多个连续的Transfer,也可被称为Transfer Loop或者a loop of transfers,这也对应文中描述的小循环Minor Loop
  • 一组触发可以包含一个或者多个连续的Minor Loop,也可被称为Trigger Loop 或者a loop of triggers,这对应文中描述的大循环 Major Loop

以数据源地址指针为例(数据目标地址指针相同):

  • 最初的数据地址存放在寄存器DMA_CSR_SADDR中。这个寄存器中的值也会随着DMA搬运过程的执行变化,始终指向即将要搬运数据的地址。
  • 预先配置DMA_CTS_TCNT寄存器的值大于等于1,表示本次DMA传输任务至少包含1次触发产生的小循环。
  • 一次触发将启动搬数过程。先从小循环走起。
  • 每次总线传输搬运的数据长度(宽度),由寄存器DMA_CSR_TCR[SSIZE]配置,可以选择1 Byte、2 Byte、4 Byte,以及16 Byte和32 Byte,这也代表了DMA使用数据总线的数据带宽。每次搬运都是从当前的数据地址开始搬运带宽指定数量的字节数。搬运过后,不对当前搬运数据地址产生影响,指针保持不变。
  • 每次搬运执行后,可以由软件指定一个地址偏移量,由寄存器DMA_CSR_SOFF配置,可以是正整数,也可以是负整数(地址向前跳)。这个偏移量是作用于当前搬运数据地址指针寄存器DMA_CSR_SADDR的,当搬运执行后,当前地址指针将会加上这个偏移量,更新成新的地址指针。
  • 如果有多次传输,则多次传输会连续执行。DMA_CTS_BCNT寄存器预置了本次小循环的需要数据的总长度(以字节为单位),而不是地址范围(切记,地址有可能是不连续地跳跃)。DMA控制器内部会自动递减DMA_CTS_BCNT寄存器的数,但不会覆写到DMA_CTS_BCNT寄存器中,因为整个小循环的搬运过程是连续执行的,用户看不到中间状态。
  • 当小循环完成后,可以有一个对源数据地址指针的偏移。然而,这里并没有设计。
  • DMA_CTS_TCNT寄存器的值减1,并覆写到DMA_CTS_TCNT寄存器中。如果值仍大于0,则说明当前的大循环任务还没执行完,继续等下一个触发启动一次小循环。如果值被减到0,说明大循环任务完成,此时,DMA_DONEDMA_CHTLDIF(若开放中断)都会置位,同时,DMA控制器还会更新两个计数值:
  • 源数据指针在加上了最后一次搬运的偏移量(由寄存器DMA_CSR_SOFF配置)之后,还会立即继续叠加一个大循环完成地址偏移,由寄存器DMA_CSR_STO配置,这也是一个可正可负的整数。计算的结果会覆写到寄存器DMA_CSR_SADDR中。
  • DMA_CTS_TCNTRV寄存器中预存的重载值覆写到DMA_CTS_TCNT寄存器中,以便于启动下次任务时无需重新配置这些计数器和地址指针。

说起来,个人觉得,如果设计小循环结束后有一个地址偏移,比实现大循环结束后的地址偏移更加直观一些。大循环专门管理触发(管理触发的递减和循环),小循环管理指针(地址的递减和递增等),分工相对更明确些。这里实现的大循环的一次性偏移,也可以等价实现为等分到每次小循环之后的地址偏移。

读者可以自行进行实验,观察DMA寄存器中各计数器的变化。

DMA控制器

图x 利用Keil的寄存器调试界面调试

DMA控制器还支持Scatter Gather模式,将多个DMA传输任务串联在一起,可以实现地址不规则的连续传输。在地址增长模式上,还有个回环递增的模式可以用。可以在具体用到的时候再深究。手册上的描述比较简略,届时仍需要用户发挥主观能动性,大胆猜想多做尝试。

应用要点(软件)

  • YTMicro SDK提供了dma外设的驱动程序,以及配合其他外设使用的样例工程,例如adc_dmaspi_slave_dmaspi_master_dmasent_dma等。
  • 在开源的arm-mcu-sdk仓库中,也收纳了ytm_dma驱动程序,以及对应的样例工程dma_basic
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分