zoukankan      html  css  js  c++  java
  • SAM4E单片机之旅——20、DMAC之使用Multi-buffer进行内存拷贝

    这次使用这个DMAC的Multi-buffer传输功能,将两个缓冲区的内容拷贝至一个连续的缓冲区中。

    一、 DMAC

    在M4中,DMA控制器(DMAC)比外设DMA控制器(PDC)要复杂,但是功能更加强大。

    为适应不同的传输要求,DMAC 可以进行灵活的自定义配置,甚至配备了一个FIFO缓存。比如可以为源设备和目标设备分别设定传输时,地址的变动方式(递增、递减或固定);以及一次传输的数据量(字节、半字或字)。

    DMAC有4个通道,每个通道可以进行一个传输任务。进行传输的设备可分为“内存”及“非内存”:内存表示随时可以对该设备进行访问,而非内存表示需要一个信号(握手接口)来触发或控制对设备的访问。握手接口可以选择硬件或软件的,并且可以在传输的过程中动态配置。

    另外,比起PDC只能设置下一次传输的参数(传输地址,数据量大小等),DMAC可以先在内存中保存好若干次传输的参数,然后自动进行多次传输(Multi-buffer传输)。

    二、 Multi-buffer传输的实现机制

    每个通道有若干个寄存器。其中:源地址和目的地址寄存器(SADDR和DADDR),描述符地址寄存器(DSCR),控制器存器(CTRLA和CTRLB)这几个寄存器可以根据需要进行自动修改。在内存中有一块区域(LLI),连续地储存着这几个寄存器的目标设置。然后就像一个链表一样,DSCR表示下一个区域的地址:

    image

    在启用通道时,如果DSCR为0,则表示只需进行一次传输,在传输完成后就关闭通道。

    如果DSCR不为0,则表示进行多次传输,而这几个寄存器的更新过程如下:

    1. 获取DSCR指向的LLI的内容。如果DSCR为0,则任务结束。

    2. 根据当前CTRLB寄存器的内容,判断是否需要根据该LLI更新SADDR及DADDR。然后根据该LLI更新其余寄存器(CTRLA,CTRLB,DSCR)。

    3. 根据新的寄存器内容进行传输。

    4. 传输完成后,将CTRLA的内容回写至内存中(传输中仅有该寄存器的BTSIZE和DONE字段会发生该变)。

    5. 根据通道CFG寄存器的Stop On Done(SOD)字段判断是否需要重新执行以上过程。

    所以,在启用通道前,除了要设置好CFG寄存器外,也需要设置好CTRLB

    三、 实现思路

    重申一下目标:将两个缓冲区的内容拷贝至一个连续的缓冲区中。

    由于源缓冲区有两个,所以我们将使用两个LLI。其中每个LLI的SADDR指向每个源缓冲区的首地址,并且在每次获取LLI时,更新SADDR。而由于目标缓冲区是连续的,所以不需要更新DADDR。

    然后在启用通道前,设置好DADDR。同时,设置CTRLB,该通道不从LLI中更新DADDR地址;设置好DSCR,使其指向第一个LLI。

    四、 使用LLI

    1. 定义LLI结构体。

      LLI的内存布局不复杂,但是使用结构体来进行操作也很有助于简化工作。而且由于布局简单,也不用太关注内存对齐的细节。(另外,在使用LLI时,需要它的地址是字对齐的。)

      typedef struct _lli{
      	uint32_t SADDR;
      	uint32_t DADDR;
      	uint32_t CTRLA;
      	uint32_t CTRLB;
      	uint32_t DSCR;
      }LLI;
    2. LLI的初始化。

      由于两个LLI的设置有许多相同的部分,所以将共同的部分抽象出来。

      // lli:		需要初始化的LLI的地址
      // saddr:		源地址
      // btsize:	传输次数
      // next_lli:	下一个LLI的地址。如果是最后一个LLI,该参数为NULL即可
      void InitLLI(LLI* lli, void* saddr, uint16_t btsize, LLI* next_lli)
      {
      	lli->SADDR = (uint32_t)saddr;
      	lli->DADDR = 0;		// DADDR 不会被使用,初始化为即可
      	lli->DSCR = DMAC_DSCR_DSCR_Msk & (uint32_t)next_lli;
      	lli->CTRLA = 
      		  DMAC_CTRLA_BTSIZE(btsize)		// 传输次数
      		| DMAC_CTRLA_SRC_WIDTH_WORD		// 源设备一次传输一个字
      		| DMAC_CTRLA_DST_WIDTH_WORD		// 目标设备一次传输一个字
      		;
      	lli->CTRLB = 
      		  DMAC_CTRLB_SRC_DSCR_FETCH_FROM_MEM	// 从LLI中更新SRC地址
      		| DMAC_CTRLB_DST_DSCR_FETCH_DISABLE	// 不更新DST地址
      		| DMAC_CTRLB_FC_MEM2MEM_DMA_FC		// 设备类型:内存至内存
      		| DMAC_CTRLB_SRC_INCR_INCREMENTING	// 传输时,源地址递增
      		| DMAC_CTRLB_DST_INCR_INCREMENTING	// 传输时,目标地址递增
      		;
      }

    五、 实现过程

    1. 缓冲区。

      // 源缓冲区
      uint32_t src1[2];
      uint32_t src2[3];
      // 目标缓冲区
      uint32_t dst[5];
      // 向源缓冲区时填充内容
      src1[0] = 50; src1[1] = 51;
      src2[0] = 52; src2[1] = 53; src2[2] = 54;
    2. 设置LLI。

      注意,要确保LLI的实例在整个程序的运行过程中都是有效的。比如如果LLI是储存在函数的栈中的话,那么函数退出后,该LLI即无效了。所以可以选择在堆中分配LLI实例的空间,或是将其定义为全局变量,也可以在main函数中定义实例。

      LLI first_lli, last_lli;
      InitLLI(&first_lli, (void*)src1, 2, &last_lli);
      InitLLI(&last_lli, (void*)src2, 3, 0);
    3. 启用DMAC。

      // PMC
      PMC->PMC_PCER0 = 1 << ID_DMAC;
      DMAC->DMAC_GCFG =
      	 DMAC_GCFG_ARB_CFG_ROUND_ROBIN;	// 轮转优先级
      DMAC->DMAC_EN = DMAC_EN_ENABLE;
    4. 配置通道。

      // 使用的通道为通道0
      #define DMAC_CH 0
      // 使DSCR指向first_lli
      DMAC->DMAC_CH_NUM[DMAC_CH].DMAC_DSCR = 
      		(uint32_t)(void*)(&first_lli);
      // 设置目标地址
      DMAC->DMAC_CH_NUM[DMAC_CH].DMAC_DADDR = 
      (uint32_t)(void*) dst;
      // 设置CTRLB,使通道从LLI中更新源地址
      DMAC->DMAC_CH_NUM[DMAC_CH].DMAC_CTRLB = 
      		  DMAC_CTRLB_SRC_DSCR_FETCH_FROM_MEM 
      		| DMAC_CTRLB_DST_DSCR_FETCH_DISABLE;
      // 配置CFG寄存器
      DMAC->DMAC_CH_NUM[DMAC_CH].DMAC_CFG = 
      DMAC_CFG_SOD_DISABLE
      		| DMAC_CFG_FIFOCFG_ALAP_CFG
      		;
    5. 启用通道。

      DMAC->DMAC_CHER = DMAC_CHER_ENA0 << DMAC_CH;
    6. 等待通道关闭,即传输完成。

      const uint32_t check_bit = DMAC_CHSR_ENA0 << DMAC_CH;
      while( (DMAC->DMAC_CHSR & check_bit) != 0);
  • 相关阅读:
    项目检出JRE问题(Unbound classpath container: 'JRE System Library [JavaSE-1.7]' in project 'idweb')
    OpenCV 4 Python高级配置—安装setuptools,matplotlib,six,dateutil,pyparsing 完整过程
    OpenCV249 for python278 最简配置方案
    纯Python综合图像处理小工具(4)自定义像素级处理(剪纸滤镜)
    纯Python综合图像处理小工具(3)10种滤镜算法
    纯Python综合图像处理小工具(2)图像增强
    纯Python综合图像处理小工具(1)分通道直方图
    树莓派2代干货帖(第一天)按图索骥的搭建
    jQuery慢慢啃之特效(八)
    jQuery慢慢啃之事件(七)
  • 原文地址:https://www.cnblogs.com/h46incon/p/3505385.html
Copyright © 2011-2022 走看看