# M4的DMA筆記 ###### tags: `M4` ## 參考資料 > - [SAM D5x/E5x Family Data Sheet](https://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5x_E5x_Family_Data_Sheet_DS60001507G.pdf) > Chapter 22. DMAC ## 硬體暫存器 ![](https://i.imgur.com/F4hvJwK.png) ![](https://i.imgur.com/gO472C2.png) ...略 ## 支援運作模式 - SRAM to SRAM - SRMA to peripheral register - peripheral register to SRAM - peripheral register to peripheral register ## Descriptor介紹 Descriptor格式 ```c= typedef struct _dma_descriptor{ uint16_t BTCTRL; uint16_t BTCNT; uint32_t SRCADDR; uint32_t DSTADDR; uint32_t DESCADDR; }DmacDescriptor __attribute__((__packed__), __aligned__(16)); ``` - SRCADDR 為DMA source address (存取) 若無開啟source address累加功能直接填寫source位置即可 否則source address應填寫為 若stepsize應用於SRC: $SRCADDR = SRCADDR_{START} + BTCNT * (BEATSIZE + 1) * 2 ^{STEPSIIZ}$ 若用於DST: $SRCADDR = SRCADDR_{START} + BTCNT * (BEATSIZE + 1)$ - DSTADDR 為DMA destination address (寫入) 若無開啟destination address累加功能直接填寫destination位置即可 否則destination address 應填寫為 若stepsize應用於DST: $DSTADDR = DSTADDR_{START} + BTCNT * (BEATSIZE + 1) * 2 ^{STEPSIIZ}$ 若用於DST: $DSTADDR = DSTADDR_{START} + BTCNT * (BEATSIZE + 1)$ - DESCADDR 為下一個descriptor的位址 (linked list) - BTCNT 為DMA source中 object個數 - BTCTRL ![](https://i.imgur.com/NeEaed3.png) >STEPSIZE設定每次觸發DMA要將index往後幾個beat size >BEATSIZE可設定為BYTE(8-bit)、HWORD(16-bit)、WORD(32-bit) >VALID需為1此descriptor才有作用 :::info descriptor需要開空間於SRAM中(static, malloc, global) 且head必須為連續空間 假設使用到32個通道 應宣告成 ```c= volatile DmacDescriptor dmac_channel[32]; ``` ::: ![](https://i.imgur.com/aDjz8Wl.png) ## 名詞解釋 ### BLOCK、BRUST、TRANSACTION #### 出現區域:Channel Control A(CHCTRLA)->TRIGACT 每次觸發DMA存取後,資料要往後多少以存取下一個。 - BURST: 可由同暫存器的BURSTLEN旗標定義為多少長度的BEAT,BEAT的長度在descriptor中定義。 - BLOCK: 每次傳送一整個descriptor挾帶的資料 - TRANSACTION: 每次傳送一整條descriptor linked list的資料 ## DMAC設定 - CTRL暫存器主要為禁致能硬體使用 因LVLEN0為DMA通道預設優先序LVLEN0應連同ENABLE一起致能 禁致能應於全部硬體設定完畢後再致能 - BASEADDR暫存器需寫入DmacDescriptor第一個位置 - WRBADDR暫存器需寫入write back Descriptor第一個位置 write back Descriptor格式同DmacDescriptor 可以使用同一群descriptor省空間 - 各Channel中的CHCTRLA設定每次觸發要取多少資料及觸發源 ## 測試 此硬體提供DMAC可以軟體觸發 使用暫存器SWTRIGCTRL 寫入特定bit即軟體觸發該通道 搭配檢查BUSYCH是否set即可知道是否轉換完畢 ```c= DMAC->SWTRIGCTRL.bit.SWTRIG0 = 1; while (DMAC->BUSYCH.bit.BUSYCH0); ``` ## 範例 :::info SRC為全域變數七節管碼 DST為PORT輸出暫存器 使用軟體觸發 ::: ```c= uint32_t *addr = 0; uint8_t const seven_seg_num[16] = {0x40, 0xf9, 0x24, 0xb0, 0x99, 0x92, 0x82, 0xd8, 0x00, 0x98, 0x08, 0x03, 0x46, 0x21, 0x06, 0x0e}; volatile DmacDescriptor dmach[1] = {0}; __attribute__((__aligned__(16))) DmacDescriptor writeback[1] = {0}; __attribute__((__aligned__(16))) DMAC->CTRL.bit.DMAENABLE = 0; DMAC->CTRL.bit.SWRST = 1; while(DMAC->CTRL.bit.SWRST); DMAC->Channel[0].CHCTRLA.bit.SWRST = 1; while(DMAC->Channel[0].CHCTRLA.bit.SWRST); dmach[0].BTCTRL.bit.STEPSEL = 1; dmach[0].BTCTRL.bit.SRCINC = 1; dmach[0].BTCTRL.bit.VALID = 1; dmach[0].BTCNT.reg = 16; addr = seven_seg_num; dmach[0].SRCADDR.reg = (uint32_t)addr + dmach[0].BTCNT.reg * (dmach[0].BTCTRL.bit.BEATSIZE + 1) * (1 << dmach[0].BTCTRL.bit.STEPSIZE); addr = &(PORT->Group[2].OUT.reg); dmach[0].DSTADDR.reg = (uint32_t)addr; dmach[0].DESCADDR.reg = (uint32_t)(dmach); addr = dmach; DMAC->BASEADDR.reg = (uint32_t)addr; addr = writeback; DMAC->WRBADDR.reg = (uint32_t)addr; DMAC->Channel[0].CHCTRLA.bit.TRIGACT = 2; DMAC->Channel[0].CHCTRLA.bit.RUNSTDBY = 1; DMAC->Channel[0].CHCTRLA.bit.ENABLE = 1;//enable channel0, burst transfer DMAC->CTRL.bit.LVLEN0 = 1; DMAC->CTRL.bit.DMAENABLE = 1; ```