[電子工作] ATxmega (E series)でFatFsをDMA対応する - rcvr_spi_milti関数 - [電子工作]
いよいよ本丸です。
いきなりコードから。
(E)DMAの使い方は、
1. 転送元アドレスを設定
2. 転送先アドレスを設定
3. 転送条件(開始トリガ)を設定
4. 完了を待つ
が基本のようです。
3.の転送条件について様々なペリフェラルの状態を設定できます。
今回はUSARTのSPIマスタモードの転送なので、それに合わせて設定することになります。
SPI転送には「送信処理」と「受信処理」の両方が必要になります。
DMAは片方向の転送処理なので、「送信DMA動作」と「受信DMA動作」の2つのchannelを使うことになります。
今回は Channel0 -> 受信動作, Channel2 -> 送信動作 に割り当てています。
1バイトの受信(転送)処理とDMA転送との対応は下記のようになります。
ここで一点、注意が。
ねむいさんのブログにも書かれていましたが、DMAを起動する際に受信側を先に起動する必要があるようです。
(合わせてDMAの優先度も受信側が優先度高にしたほうが良い?)
理由はよくわからないのですが、逆にするとDMA転送しなくなることがありました。
これ、本家Application Noteに書いといてほしいですね…
いきなりコードから。
/* Receive a data block fast */ static void rcvr_spi_multi ( BYTE *p, /* Data read buffer */ UINT cnt /* Size of data block */ ) { volatile uint8_t dmy = 0xff; /* DMA Chennel0 -> Transfer from USARTD0.Data to buffer */ EDMA.CH0.ADDRCTRL = EDMA_CH_RELOAD_NONE_gc | EDMA_CH_DIR_FIXED_gc; EDMA.CH0.DESTADDRCTRL = EDMA_CH_RELOAD_NONE_gc | EDMA_CH_DIR_INC_gc; EDMA.CH0.TRIGSRC = EDMA_CH_TRIGSRC_USARTD0_RXC_gc; EDMA.CH0.ADDRL = (uint16_t)(&USARTD0.DATA) & 0xff; EDMA.CH0.ADDRH = ((uint16_t)(&USARTD0.DATA) >> 8) & 0xff; EDMA.CH0.DESTADDRL = (uint16_t)p & 0xff; EDMA.CH0.DESTADDRH = ((uint16_t)p >> 8) & 0xff; EDMA.CH0.TRFCNTL = cnt & 0xff; EDMA.CH0.TRFCNTH = (cnt >> 8) & 0xff; /* DMA Chennel2 -> Transfer dummy 0xff to USARTD0.Data */ EDMA.CH2.ADDRCTRL = EDMA_CH_RELOAD_NONE_gc | EDMA_CH_DIR_FIXED_gc; EDMA.CH2.DESTADDRCTRL = EDMA_CH_RELOAD_NONE_gc | EDMA_CH_DIR_FIXED_gc; EDMA.CH2.TRIGSRC = EDMA_CH_TRIGSRC_USARTD0_DRE_gc; EDMA.CH2.ADDRL = (uint16_t)(&dmy) & 0xff; EDMA.CH2.ADDRH = ((uint16_t)(&dmy) >> 8) & 0xff; EDMA.CH2.DESTADDRL = (uint16_t)(&USARTD0.DATA) & 0xff; EDMA.CH2.DESTADDRH = ((uint16_t)(&USARTD0.DATA) >> 8) & 0xff; EDMA.CH2.TRFCNTL = cnt & 0xff; EDMA.CH2.TRFCNTH = (cnt >> 8) & 0xff; /* To make stable transmission, Rx must be enaled first ! */ EDMA.CH0.CTRLA = EDMA_CH_ENABLE_bm | EDMA_CH_SINGLE_bm; EDMA.CH2.CTRLA = EDMA_CH_ENABLE_bm | EDMA_CH_SINGLE_bm; while ( !(EDMA.INTFLAGS & EDMA_CH0TRNFIF_bm) ) {} EDMA.INTFLAGS |= EDMA_CH0TRNFIF_bm | EDMA_CH2TRNFIF_bm; }
(E)DMAの使い方は、
1. 転送元アドレスを設定
2. 転送先アドレスを設定
3. 転送条件(開始トリガ)を設定
4. 完了を待つ
が基本のようです。
3.の転送条件について様々なペリフェラルの状態を設定できます。
今回はUSARTのSPIマスタモードの転送なので、それに合わせて設定することになります。
SPI転送には「送信処理」と「受信処理」の両方が必要になります。
DMAは片方向の転送処理なので、「送信DMA動作」と「受信DMA動作」の2つのchannelを使うことになります。
今回は Channel0 -> 受信動作, Channel2 -> 送信動作 に割り当てています。
1バイトの受信(転送)処理とDMA転送との対応は下記のようになります。
/* Exchange a byte */ static BYTE xchg_spi ( /* Returns received data */ BYTE dat /* Data to be sent */ ) { /* 送信処理 => データ受信時は、ダミーで0xffを送る! => DMA Channel2に割り当て */ /* 送信可能を待つ => DMAでは「EDMA_CH_TRIGSRC_USARTD0_DRE_gc」条件 */ while ( !(USARTD0.STATUS & USART_DREIF_bm)); USARTD0.DATA = dat; /* データを送信 */ /* 受信処理 => DMA Channel0に割り当て*/ /* 受信完了を待つ => DMAでは「EDMA_CH_TRIGSRC_USARTD0_RXC_gc」条件 */ while ( !(USARTD0.STATUS & USART_RXCIF_bm)); return USARTD0.DATA; /* 受信データを読む */ }
ここで一点、注意が。
/* To make stable transmission, Rx must be enaled first ! */ EDMA.CH0.CTRLA = EDMA_CH_ENABLE_bm | EDMA_CH_SINGLE_bm; EDMA.CH2.CTRLA = EDMA_CH_ENABLE_bm | EDMA_CH_SINGLE_bm;
ねむいさんのブログにも書かれていましたが、DMAを起動する際に受信側を先に起動する必要があるようです。
(合わせてDMAの優先度も受信側が優先度高にしたほうが良い?)
理由はよくわからないのですが、逆にするとDMA転送しなくなることがありました。
これ、本家Application Noteに書いといてほしいですね…
コメント 0