ロガーのSDカードI/O部分の書き出し部分をDMAを使うようにした。(今迄は全力
でFIFOに書き込んでいた。これではSD/MMCコントローラの書き出しスピードを
最低にまで落とさないと(クロックを最低に設定)、FIFO under runでSD/MMCコ
ントローラが止まってしまう。
LPC2388のGPDMAはメモリ側にUSB RAMと外部拡張メモリしかアクセスできないの で、今迄内部RAMにとっていたバッファ領域をUSB RAMに変更。スピードメータ、 あるいはP-LAPからの割り込みが入ったら、その時間を直接USB RAMに書き出す。
バッファは512Bのダブルバッファなので、フルになったらそのバンクをDMAで SD/MMCコントローラに送る。フローコントロールはSD/MMC側として、調停はお まかせ。
終了の検出はポーリング、DMAのターミナルカウントの割り込みまで寝るの二種 類を試してみたところ、ポーリングで250ms、割り込みで50msだったので、割り 込みに。このあたりは転送速度を上げていくとどうなるかわからない。 寝るお膳立てをしてる間にDMAが終了するようになったらポーリングにしよう。
とりあえずこの仕様でピストンナラシのついでにテスト。
LPC2388のGPDMAはメモリ側にUSB RAMと外部拡張メモリしかアクセスできないの で、今迄内部RAMにとっていたバッファ領域をUSB RAMに変更。スピードメータ、 あるいはP-LAPからの割り込みが入ったら、その時間を直接USB RAMに書き出す。
バッファは512Bのダブルバッファなので、フルになったらそのバンクをDMAで SD/MMCコントローラに送る。フローコントロールはSD/MMC側として、調停はお まかせ。
終了の検出はポーリング、DMAのターミナルカウントの割り込みまで寝るの二種 類を試してみたところ、ポーリングで250ms、割り込みで50msだったので、割り 込みに。このあたりは転送速度を上げていくとどうなるかわからない。 寝るお膳立てをしてる間にDMAが終了するようになったらポーリングにしよう。
とりあえずこの仕様でピストンナラシのついでにテスト。
STATIC thread_t sleeping_thread;
void
dma_init ()
{
// Power on 起動時にはDMAの電源は入っていない。
mcu_peripheral_power (PCONP_PCGPDMA, TRUE);
// Src, Dstともにエンディアンの設定ができる。
*DMACConfig = DMACConfigEnable; // Little-endian
// *DMACConfig = 0;
}
void
dma_fini ()
{
*DMACConfig = 0;
mcu_peripheral_power (PCONP_PCGPDMA, FALSE);
}
void
dma_ch0_mci_start (uint32_t src_addr)
{
// Clear pending interrupt.
*DMACIntTCClear = IntTCClear0;
*DMACIntErrClr = IntErrClr0;
// Source address.
*DMACC0SrcAddr = src_addr;
// Destination address.
*DMACC0DstAddr = MCI_FIFO_ADDR;
// not Linked List Item.
// 512BドカDMAなのでLLIは使わない。
*DMACC0LLI = 0;
// Disable and reset configuration parameter of channel 0.
*DMACC0Config &= ConfigReservedMask;
*DMACC0Control = (512 & TransferSizeMask) | //512B送ります。
(BurstSize32 << SBSizeShift) | // バーストサイズはKeilのサンプルを
(BurstSize8 << DBSizeShift) | // 参考にした。考える必要あり。
(WidthSize32bit << SWidthShift) |
(WidthSize32bit << DWidthShift) |
DMASrcIncr; // 出所(USB RAM)だけインクリメント。
//目的地(MCI_FIFO_ADDR)はインクリメントしない。
//目的地SD/MMCの指定と、フローコントローラをDMAかSD/MMC側かを指定。
*DMACC0Config |= (PeripheralSDMMC << DstPeripheralShift) |
(FlowCntrlM2P_P << FlowCntrlShift);// Peripheral Flow Control.
#define DMA_INTR
#ifdef DMA_INTR
// 50ms
// マイOSではIRQしか再スケジューリングをしないので、IRQじゃないとだめ。
*VICIntSelect &= ~VIC_GPDMA; // IRQ
*VICIntEnable |= VIC_GPDMA;
sleeping_thread = current_thread;
// カウント終了で割り込みを発生させるように。
*DMACC0Control |= TerminalCountIntr;
*DMACC0Config |= ConfigTerminalCount | ConfigIntrError;
#else
// 250ms
#endif
// iprintf ("src=%x dst=%x\n", *DMACC0SrcAddr, *DMACC0DstAddr);
// Kick DMA. DMA channel is automatically enabled.
*DMACC0Config |= ChannelEnable;
#ifdef DMA_INTR
// DMAカウント終了の割り込みで起こされるまで寝ます。この間にサンプリング
//の割り込みがあれば、その後のデータ処理スレッドが動く。それらの間にはリ
//ングバッファがあるので、バッファが溢れない限りポーリングでも構わない。
// コントローラの7セグLEDの更新は止まるけれど、それはどうでもいい。
thread_sleep (current_thread);
#endif
}
void
gpdma_intr ()
{
*DMACIntTCClear = *DMACIntTCStatus;//割り込み要因をクリアして
*VICIntEnable &= ~VIC_GPDMA;//次のDMAまで割り込みを使いません。
thread_wakeup (sleeping_thread);
}
まずは書き込みだけDMA化。ロガーが読み込みをするのは起動時のみなので遅くとも
まったく問題ない。
bool
mmc_write (void *self, uint8_t *buf, daddr_t lba)
{
#define MCI_USE_DMA
struct cpsm_command *cmd = cpsm_command + MMC_CMD24;
self = self;
mci_tran_state ();
*MCI_DATATIMER = ~0;
*MCI_DATALENGTH = 512;
#ifdef MCI_USE_DMA
// DMAを使うことを指定することで自動的にDMAと調停してくれる。
*MCI_DATACTRL = DATACTRL_ENABLE | DATACTRL_DMAENABLE |
((9 & DATACTRL_BLOCKSIZE_MASK) << DATACTRL_BLOCKSIZE_SHIFT);
#else
*MCI_DATACTRL = DATACTRL_ENABLE |
((9 & DATACTRL_BLOCKSIZE_MASK) << DATACTRL_BLOCKSIZE_SHIFT);
#endif
*MCI_CLEAR = CLEAR_ALL;
cmd->arg = card_info.block_address ? lba : lba * 512;
if (!mci_cpsm_command (&card_info, cmd))
return FALSE;
md_timer_counter_set (cmd->timeout);
while ((*MCI_STATUS & STATUS_CMDACTIVE) && md_timer_counter () != 0)
;
if (md_timer_counter () == 0)
{
printf ("timeout 1\n");
return FALSE;
}
#ifdef MCI_USE_DMA
dma_ch0_mci_start ((uint32_t)buf);
// md_timer_counter_set (1000);
//DMA側が転送し終ってもまだSD/MMCコントローラ側は仕事してるかもしれないので。
while (*MCI_STATUS & STATUS_TXACTIVE) //XXXtimeout
;
// printf ("DMA %d\n", md_timer_counter ());
#else
uint32_t *fifobuf = (uint32_t *)buf;
int i;
md_timer_counter_set (cmd->timeout);
for (i = 0; i < 512 / 4; i++)
{
while (*MCI_STATUS & STATUS_TXFIFOFULL && md_timer_counter () != 0)
;
*MCI_FIFO_START = fifobuf[i];
}
// Transmit in prgress.
while (*MCI_STATUS & STATUS_TXACTIVE) //XXXtimeout
;
#endif
// Stop DPSM
*MCI_DATACTRL = 0;
return TRUE;
}
