SSブログ

[電子工作] ATxmega (E series)でFatFsをDMA対応する - rcvr_spi_milti関数 - [電子工作]

いよいよ本丸です。

いきなりコードから。

/* 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 -> 送信動作 に割り当てています。

USART-SPI on DMA.jpg

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に書いといてほしいですね…
nice!(0)  コメント(0)  トラックバック(0) 
共通テーマ:パソコン・インターネット

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。