# 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
## 硬體暫存器


...略
## 支援運作模式
- 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

>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];
```
:::

## 名詞解釋
### 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;
```