SSブログ
English Version

CH32V203で遊ぶ(その4)DMAで高速描画 [CH32V]

 「CH32V203で遊ぶ(その1)SPIでLCD」の記事の末尾に追記したように SPI インターフェースを使って 2.8インチTFT液晶 MSP2807(320 x 240 dot)に描画できるようになりましたが、画面クリアに時間が掛かることから DMA を使って高速化したのでメモっておきたいと思います。


1.今回の目標
 2.8インチTFT液晶 MSP2807(320 x 240 dot)への連続したデータ書き込み処理部分を DMA 機能を使って高速化します。


2.方針
 下記の変更/追加を行います。
  1. SPI のクロックアップ
     SPI のクロックは従来 2MHz でしたが上限の 4MHz にアップします

  2. 連続データ書き込み処理の DMA 化
     LCD への連続したデータ書き込みが発生する塗りつぶし処理(画面クリア含む)を DMA 機能で実現します

  3. 塗りつぶし機能追加
     円と四角形の塗りつぶし機能を実装します


3.対処内容
 以下に対処した内容について記載します。DMA を利用すると言っても DMA の設定の追加と LCD へのデータ書き込み部分のコードを若干変更するだけなので本記事の内容がスカスカになってしまうのを防ぐためにデータシートからの引用等を含め、CH32V203 の DMA 機能自体の説明もある程度記載します。


 3-1.SPI のクロックアップ
 SPI の初期化の時のプリスケーラの設定を変更するだけです。簡単ですね。


SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; // 16:500kHz



 3-2.連続データ書き込み処理の DMA 化
 今回使用している CPU(CH32V203)のブロック図を下図に示します。DMA 機能は左上の部分にあり、8チャンネル構成で FLASH, SRAM 及びペリフェラルのバスに接続されています。機能としてはメモリ to メモリ、メモリ to ペリフェラル及び ペリフェラル to メモリの高速なデータ転送を実現します。

CPU(CH32V203)のブロック図


 ペリフェラルと言ってもそれを制御するペリフェラル用のレジスタは下図のようにメモリにマッピングされています。

CPU(CH32V203)のメモリマップ


 周辺機器とのインターフェースにはそれぞれ固有の機能があるので DMA 側も各機能に合わせてカスタマイズされている関係上、DMA のチャンネルとペリフェラルは下図のように対応付けされています。

DMA リクエストマッピング


 この対応付けを表にしたものが下表になります。今回は LCD 通信の送信(SPI1_TX)を DMA 化するので DMA の チェンネル3を使用することになります。

DMA チャンネル vs ペリフェラル対応表


 一つの DMA チャンネルで複数のリクエストに対応している関係上、下記の様にリクエスト要求側でリクエストを有効にする設定が必要になります。


SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);



 DMA の制御レジスタのビット構成を下図に示します。あとはサンプルソースを参照して設定することで容易に動作しました。

DMA 制御レジスタ


 唯一悩ましかったのが次の点でした。
 今回は LCD の画面消去等の塗りつぶし(2バイトデータの連続書込み)が DMA 対象であり、この場合のペリフェラルサイズ(PSIZE)とメモリサイズ(MSIZE)の設定で悩みました。SPI 側のデータサイズはバイトに設定していて、書き込みデータはメモリ上に2バイトデータで存在するので
  • PSIZE : DMA_PeripheralDataSize_Byte
  • MSIZE : DMA_MemoryDataSize_HalfWord
と設定したところ、メモリ上のデータの1バイト目だけしか転送されませんでした。最終的には PSIZE を HalfWord に変更し、かつ DMA 使用時は SPI のデータサイズも 16bit に変更することでうまく動作するようになりました。

 参考として DMA 設定部分のソースを以下に貼っておきます。ppadr と memadr の型が uint32_t になっていて、これは void * の方が妥当だと思いますがサンプルのままで弄っていません。

DMA 初期化部分のソース
// init Dma void DMA_Tx_Init(DMA_Channel_TypeDef *DMA_CHx, uint32_t ppadr, uint32_t memadr, uint16_t bufsize) { DMA_InitTypeDef DMA_InitStructure = {0}; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA_CHx); DMA_InitStructure.DMA_PeripheralBaseAddr = ppadr; DMA_InitStructure.DMA_MemoryBaseAddr = memadr; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = bufsize; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; // nenory not incriment DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA_CHx, &DMA_InitStructure); }


 連続書込み部分のソースは下記になります。DMA での1回の転送データ数は 0xffff 個が上限(0x0000 設定時は転送しない)なので転送数が 0xffff より大きい場合は分割して転送するようにしています。ループ内に if 文を入れれば一つのループにできますが速度優先で二つのループにしています(実質的な速度は殆ど変わらないとは思いますが)。

DMA での連続書込み部分のソース
// write same data // data <- write data // len <- how many data void LcdWrFill( uint16_t data, uint32_t len ) { LcdWrDotCmd(); // send mem write command(on SpiCs) #ifndef USE_DMA // not USE DMA while( len-- ) { LcdWrWord( data ); } LcdCsOff(); #else SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; // 16bit mode msbfirst SPI_Init( LCD_SPI, &SPI_InitStructure ); while( LCD_SPIWRSTS() == RESET ) {} // wait until can write PrevSort = DATA; while( LCD_SPIWRBSY() == SET ) {} // wait until write complete LCD_BSET( LCD_CMD ); while( len > 0xffff ) { DMA_Tx_Init( DMA1_Channel3, (u32)&SPI1->DATAR, (uint32_t)&data, 0xffff ); DMA_Cmd(DMA1_Channel3, ENABLE); len -= 0xffff; while(!DMA_GetFlagStatus(DMA1_FLAG_TC3)) {}; } if( len ) { DMA_Tx_Init( DMA1_Channel3, (u32)&SPI1->DATAR, (uint32_t)&data, (uint16_t)len ); DMA_Cmd(DMA1_Channel3, ENABLE); while(!DMA_GetFlagStatus(DMA1_FLAG_TC3)) {}; } LcdCsOff(); SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8bit mode msbfirst SPI_Init( LCD_SPI, &SPI_InitStructure ); #endif }


 下図は DMA 化前の画面クリア時(0x0000の連続書込み)のロジアナ波形です。SPI 出力間に隙間時間がかなりあります。

DMA 対応前の画面クリア時のロジアナ波形


 下図は DMA 化後のロジアナ波形です。隙間なく SPI 出力されています。

DMA 対応後の画面クリア時のロジアナ波形



 3-3.塗りつぶし機能追加
 上記の DMA 化した連続書込み処理を利用して、円と四角形の塗りつぶし描画処理を追加しました。


4.まとめ
 SPI のクロックが MHz レベルになるとマイコンのソフト処理では流石に転送と転送の間に隙間時間が発生してしまいます。このため多くのマイコンが DMA 機能を有しており、効率よくテータ転送ができるようになっています。
 今回の LCD 表示での塗りつぶし処理も DMA 機能を使用することで隙間時間なく転送できるようになり、処理時間を短縮出来ました。
 更に LCD コントローラの書き込み対象の四角形範囲を指定できる機能により、四角形の塗りつぶしはかなり高速化しました。
 また、今回は使用していませんが、DMA の転送中間時点と転送終了時点で割込みを発生できるので、LCD 書込みと並行して別の処理をすることもソフトウェア次第で可能になります。

 最後に DMA 化後のデモ画面を貼っておきます。

DMA 描画デモ画面例


 X(旧Twitter)のメッセージに添付した動画も貼っておきます。



[TOP] [ 前へ ] 連載記事一覧 [ 次へ ]
nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー

nice! 0

コメント 0

コメントを書く

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