您的位置 首页 新品

SAM4E单片机之旅——20、DMAC之运用Multi-buffer进行内存复制

这次使用这个DMAC的Multi-buffer传输功能,将两个缓冲区的内容拷贝至一个连续的缓冲区中。一、DMAC在M4中,DMA控制器(DMAC)比外设DMA控制器(PDC)要复杂,但是功

  这次运用这个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,则表明进行屡次传输,而这几个寄存器的更新进程如下:

  获取DSCR指向的LLI的内容。假如DSCR为0,则使命完毕。

  依据当时CTRLB寄存器的内容,判别是否需求依据该LLI更新SADDR及DADDR。然后依据该LLI更新其他寄存器(CTRLA,CTRLB,DSCR)。

  依据新的寄存器内容进行传输。

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

  依据通道CFG寄存器的Stop On Done(SOD)字段判别是否需求从头履行以上进程。

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

  三、 完结思路

  重申一下方针:将两个缓冲区的内容复制至一个接连的缓冲区中。

  因为源缓冲区有两个,所以咱们将运用两个LLI。其间每个LLI的SADDR指向每个源缓冲区的首地址,并且在每次获取LLI时,更新SADDR。而因为方针缓冲区是接连的,所以不需求更新DADDR。

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

  四、 运用LLI

  界说LLI结构体。

  LLI的内存布局不杂乱,可是运用结构体来进行操作也很有助于简化作业。并且因为布局简略,也不必太重视内存对齐的细节。(别的,在运用LLI时,需求它的地址是字对齐的。)

  typedef struct _lli{

  uint32_t SADDR;

  uint32_t DADDR;

  uint32_t CTRLA;

  uint32_t CTRLB;

  uint32_t DSCR;

  }LLI;

  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 // 传输时,方针地址递加

  ;

  }

  五、 完结进程

  缓冲区。

  // 源缓冲区

  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;

  设置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);

  启用DMAC。

  // PMC

  PMC->PMC_PCER0 = 1 << ID_DMAC;

  DMAC->DMAC_GCFG =

  DMAC_GCFG_ARB_CFG_ROUND_ROBIN; // 轮转优先级

  DMAC->DMAC_EN = DMAC_EN_ENABLE;

  装备通道。

  // 运用的通道为通道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

  ;

  启用通道。

  DMAC->DMAC_CHER = DMAC_CHER_ENA0 << DMAC_CH;

  等候通道封闭,即传输完结。

  const uint32_t check_bit = DMAC_CHSR_ENA0 << DMAC_CH;

  while( (DMAC->DMAC_CHSR & check_bit) != 0);

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/xinpin/203762.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部