---
# System prepended metadata

title: M4的DMA筆記
tags: [M4]

---

# 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;
```