2024年08月04日

CH32V203で遊ぶ(その6)クロックアップ

 前回の記事で CH32V203 に TFT 液晶 MSP2807(320 x 240 dot)を接続した環境でのライン描画の高速化から端を発して新たな高速ライン描画法を提案しました。
 今回はレジスタ直書込みによる高速化と CH32V203 のクロックアップによる変貌ぶりについて記録して置きたいと思います。


1.はじめに
 以前から認識はしていたのですが、CH32V203 は今回使用している開発環境(MounRiver Studio)のディフォルト設定では外部クロックを使用する設定になっていて、今回のように外部クロックを接続していない状態では内部クロックを使って 8MHz で動作します。
 言わばこの大リングボール養成ギブスを付けた状態で今までグラフィック描画の高速化を試みてきました。今回はレジスタの直たたきによる高速化を行った後に、クロックアップにより羊の皮を脱ぎ捨てた CH32C203 の真の実力について記載します。


2.レジスタ直接制御による高速化
 サンプルプログラムの記述に従ってC言語で開発するとペリフェラルのステータス確認の度にサブルーチンコールし、サブルーチンの中では対応ビットをANDチェック後の状態(ゼロか否か)により、リターン値を設定するようなまどろっこしい処理になってしまいます。特にライン描画のような繰り返し回数が多い処理ではなるべくステップ数の少ない実装にしたいものです。
 そこでステータスの確認とデータの書き込みをレジスタ直アクセスにしてみました。レジスタのアドレスやステータスのビットの定義についてはディバッガでトレースすれば容易に確認でき、これらは ch32v20x.h や ch32v20x_spi.h のファイル内で定義されています。
 参考として SPI1 関連のレジスタとステータスレジスタのビット構成を下表に示します。

SPI1 関連レジスタ
SPI1 ステータス

 下図がレジスタ直制御対応前と対応後のロジアナ波形です。SPI のデータ出力の間隔が短くなり、高速化できていることが判ります。末尾の「4.まとめ」に記載しているようにレジスタを直接制御することで前回の記事に掲載した 512 個の線分を描画するテストプログラムの実行時間は従来の処理から 16% 程速くなりました。

レジスタ直制御対応前のSPI波形
レジスタ直制御対応後のSPI波形


3.クロックアップ
 下図は CH32V203 のクロック系の構成図で、これを見ると HSI(内部クロック)の 8MHz を PLL で18倍まで逓倍したものを SYSCLK にすることができます。

CH32V203 のクロック構成

 今まではディフォルト設定で SYSCLK は 8MHz で、言わばこの大リーグボール養成ギブスを付けた状態で色々高速化を試みてきました。高速化の対処も一通りやった感があるので、この辺で CH32V203 の羊の皮を取り去り本来の実力を見てみたいと思います。
 クロックの設定は system_ch32v20x.c の下記の部分の define 設定を変更することで簡単にできました。

system_ch32v20x.c
/* 
* Uncomment the line corresponding to the desired System clock (SYSCLK) frequency (after 
* reset the HSI is used as SYSCLK source).
* If none of the define below is enabled, the HSI is used as System clock source. 
*/
//#define SYSCLK_FREQ_HSE    HSE_VALUE
//#define SYSCLK_FREQ_48MHz_HSE  48000000
//#define SYSCLK_FREQ_56MHz_HSE  56000000
//#define SYSCLK_FREQ_72MHz_HSE  72000000
/////#define SYSCLK_FREQ_96MHz_HSE  96000000
//#define SYSCLK_FREQ_120MHz_HSE  120000000
//#define SYSCLK_FREQ_144MHz_HSE  144000000
//#define SYSCLK_FREQ_HSI    HSI_VALUE
//#define SYSCLK_FREQ_48MHz_HSI  48000000
//#define SYSCLK_FREQ_56MHz_HSI  56000000
//#define SYSCLK_FREQ_72MHz_HSI  72000000
//#define SYSCLK_FREQ_96MHz_HSI  96000000
//#define SYSCLK_FREQ_120MHz_HSI  120000000
#define SYSCLK_FREQ_144MHz_HSI  144000000

 sysclk が従来の 8MHz から一挙に 144MHz にアップし、SPI のプリスケーラーは2に設定しているので SPI のクロックも 4MHz から 72MHz に変わることになります。流石に SPI のプリスケーラーの設定は変更が必要と思っていましたが、なんとそのままで動作しました。
 こうなると私の安いロジアナ(サンプリングの上限が100MSa/s)では波形を確認できません。最高サンプリング 1GSa/s のオシロで観測した SPI の CLK(水色)とデータ(黄色)が下図です。プローブが安物のせいか波形が綺麗には見えませんが、カーソルで示しているように確かに 72MHz 程度のクロックが出ているようです。ブレッドボード環境でこの周波数で動作していることもすごいですね。

SPI CLK(水色) と MOSI(黄色)


4.まとめ
 数回に渡り、高速化検討をしてきましたが、高速化を一通りやった感があるので今回はクロックを 8MHZ から一挙に上限の 144MHz にアップし、CH32V203 の羊の皮を脱がせて真の実力を見てみたところグラフィック描画はとんでもない速さになりました^^
 120円の CPU でこの速さはとてつもないと思います。接続した TFT 液晶 MSP2807 もすごいですね。

 最後に今回の高速化対応での線分描画時間の測定結果を下表に示します。

Nomethodtime[s]speed ratio
1Before13.1091.00
2Register direct11.3041.16
3Clockup(144MHz)0.62620.94

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



[TOP] [ 前へ ] CH32V203で遊ぶ [ 次へ ]

posted by skyriver at 13:50| Comment(0) | CH32V | このブログの読者になる | 更新情報をチェックする

2024年08月01日

CH32V203で遊ぶ(その5)高速なライン描画法

 前回の記事では DMA での高速転送機能によって TFT液晶 MSP2807(320 x 240 dot)での塗りつぶし処理を高速化したことについて書きました。この時のデモ表示を見ているとラインの描画が目で見えるほど遅いので何とか高速化できないものか考えてみました。
 ディバッガで追ってみると速度のネックはライン描画アルゴリズムよりもドット描画自体にあり、SPI や PIO での I/O 制御部がC言語での標準な制御方法ではまどろっこしい程の処理なのでレジスタ直書きやアセンブラ化すればかなり速くなりそうです。
 今回はライン描画処理の高速化を検討する中で、上記のような I/O 制御自体の高速化では無く、ライン描画アルゴリズム自体の高速化を思い付いた(以降、傾き予測法(Slope prediction method)と記す)ので記録しておきたいと思います。
 尚、後で気が付きましたが「ポケコン(G850)高速ライン描画」の記事でも引用していた「ラインルーチンの高速化の手法」のページにライン描画の高速化について判り易くまとめられていて、ここにダブルステップの手法が書かれていますが評価結果として高速化はできなかったとの結論でした。今回提案する傾き予測法は本ページの末尾にまとめたように実際に高速化への効果が確かめられた描画法です。


1.今回のお品書き
 ライン描画手法について下記の内容を記載します。
  1. 標準的なライン描画処理
  2. 両端からの同時描画
  3. 今回提案の傾き予測法
最後にまとめとして上記手法の評価結果を記載します。


2.高速ライン描画手法
 以降にライン描画の各手法について記載します。


 2-1.標準的なライン描画処理
 高速化のために整数だけの処理でラインを描画する手法でブレゼンハムのアルゴリズムとして広く知られています。
 下記のソースはネット上のソースを参照せずに独自に起こしたものですが、ブレゼンハムのアルゴリズムと同等の処理になっています。
 高速化の観点からループ内での条件分岐を最小限にするために傾きの大きさにより場合分けし、二つのループ処理に分割しています。PSET は1ピクセルを描画する外部処理です。

標準的なライン描画処理(C言語)
void LineStd( int16_t xst, int16_t yst, int16_t xen, int16_t yen ) { int16_t x,y,difx,dify; int16_t err, xtail, ytail; LcdXPos = xen; LcdYPos = yen; difx = abs( xen - xst ) << 1; dify = abs( yen - yst ) << 1; if( ( (difx >= dify) && (xen > xst) ) || ( (difx < dify) && (yen > yst) ) ) { x = xst; y = yst; xtail = xen; ytail = yen; } else { x = xen; y = yen; xtail = xst; ytail = yst; } if( difx >= dify ) { int16_t dy; dy = ytail > y ? 1 : -1; err = difx >> 1; for( ; x <= xtail; x++ ) { PSET( x, y ); err += dify; if( err >= difx ) { err -= difx; y += dy; } } } else { int16_t dx; dx = xtail > x ? 1 : -1; err = dify >> 1; for( ; y <= ytail; y++ ) { PSET( x, y ); err += difx; if( err >= dify ) { err -= dify; x += dx; } } } }
・2024/08/16 中心部のポイントが乱れる場合がある問題に対処


 2-2.両端からの同時描画
 上記の改良版でラインが中心点から見て点対称なので始点側と同時に終点側も描画することでループ回数を1/2にして高速化しています。ループ終了後の PSET は始点~終点間の距離が偶数の場合、中心のポイントの描画が欠落してしまうことに対する対処です。

両端からの同時描画(C言語)
void LineBoth( int16_t xst, int16_t yst, int16_t xen, int16_t yen ) { int16_t x,y,difx,dify; int16_t err, xtail, ytail; LcdXPos = xen; LcdYPos = yen; difx = abs( xen - xst ) << 1; dify = abs( yen - yst ) << 1; if( ( (difx >= dify) && (xen > xst) ) || ( (difx < dify) && (yen > yst) ) ) { x = xst; y = yst; xtail = xen; ytail = yen; } else { x = xen; y = yen; xtail = xst; ytail = yst; } if( difx >= dify ) { int16_t dy; dy = ytail > y ? 1 : -1; err = difx >> 1; for( ; x < xtail; x++, xtail-- ) { PSET( x, y ); PSET( xtail, ytail ); err += dify; if( err >= difx ) { err -= difx; y += dy; ytail -= dy; } } if( x == xtail ) { PSET( x, y ); } } else { int16_t dx; dx = xtail > x ? 1 : -1; err = dify >> 1; for( ; y < ytail; y++, ytail-- ) { PSET( x, y ); PSET( xtail, ytail ); err += difx; if( err >= dify ) { err -= dify; x += dx; xtail -= dx; } } if( y == ytail ) { PSET( x, y ); } } }
・2024/08/16 中心部のポイントが乱れる場合がある問題に対処


 2-3.今回提案の傾き予測法
 上記の描画では傾きが1(45°)以下の場合と1より大きい場合の二通りに場合分けして処理しましたが、傾き予測法では傾きの大きさによりループ処理を更に2分割します。
 下図に示すように傾きの大きさと移動方向の条件により、次の移動方向を決定できるケースがあり、これを利用して高速化しています。前述の両端からの描画手法も併用します。

  • 傾きが 0 ~ 0.5 の場合
     縦移動/横移動 は1/2以下なので下図の上側の緑色の■のように縦移動直後は必ず横移動になります。

  • 傾きが 0.5 ~ 1.0 の場合
     縦移動/横移動 は1/2以上なので下図の下側の緑色の■のように横移動直後は必ず縦移動になります。
傾き予測法での予測方法

 例えば、上記の「標準的なライン描画処理」において単純にループ内で2ドット分の進みを処理すれば、ループ回数が 1/2 になるので for ループのための条件ジャンプの比率が1/2になり効果は少ないですが若干高速化します(ループ処理の展開による高速化)。逆に言うと1回のループ処理で多くのドットを処理してもそれに比例して条件分岐が増えれば高速化できません(当然処理量が比例して多くなっても高速化できません)。従って高速化するためには上図のようにループ内の条件ジャンプの増加を抑えつつ、より多くのドットを処理するようにする必要があります。

傾き予測法によるライン描画処理(C言語)
// draw line Ver 0.02b void LcdLine( int16_t xst, int16_t yst, int16_t xen, int16_t yen ) { int16_t x,y,difx,dify,until; int16_t err, xtail, ytail; LcdXPos = xen; LcdYPos = yen; difx = abs( xen - xst ) << 1; dify = abs( yen - yst ) << 1; if( ( (difx >= dify) && (xen > xst) ) || ( (difx < dify) && (yen > yst) ) ) { x = xst; y = yst; xtail = xen; ytail = yen; } else { x = xen; y = yen; xtail = xst; ytail = yst; } if( difx >= dify ) { int16_t dy; dy = ytail > y ? 1 : -1; err = difx >> 1; until = x + (difx>>1) + ( (difx&1) ? 0 : -1); if( difx >= (dify<<1) ) { // slope:[0,0.5] for( ; x < until; x++, xtail-- ) { PSET( x, y ); PSET( xtail, ytail ); err += dify; if( err >= difx ) { err += dify - difx; x++; xtail--; y += dy; ytail -= dy; PSET( x, y ); PSET( xtail, ytail ); } } } else { // slope:(0.5,1] for( ; x < until; x++, xtail-- ) { PSET( x, y ); PSET( xtail, ytail ); err += dify; if( err >= difx ) { err -= difx; y += dy; ytail -= dy; } else { x++; xtail--; PSET( x, y ); PSET( xtail, ytail ); err += dify - difx; y += dy; ytail -= dy; } } } for( ; x <= xtail; x++ ) { PSET( x, y ); err += dify; if( err >= difx ) { err -= difx; y += dy; } } } else { int16_t dx; dx = xtail > x ? 1 : -1; err = dify >> 1; until = y + (dify>>1) + ( (dify&1) ? 0 : -1); if( dify >= (difx<<1) ) { for( ; y < until; y++, ytail-- ) { PSET( x, y ); PSET( xtail, ytail ); err += difx; if( err >= dify ) { err += difx - dify; x += dx; xtail -= dx; y++; ytail--; PSET( x, y ); PSET( xtail, ytail ); } } } else { for( ; y < until; y++, ytail-- ) { PSET( x, y ); PSET( xtail, ytail ); err += difx; if( err >= dify ) { err -= dify; x += dx; xtail -= dx; } else { y++; ytail--; PSET( x, y ); PSET( xtail, ytail ); err += difx - dify; x += dx; xtail -= dx; } } } for( ; y <= ytail; y++ ) { PSET( x, y ); err += difx; if( err >= dify ) { err -= dify; x += dx; } } } }
・Ver 0.02 2024/08/03 中心部のポイントが描画されない場合がある問題に対処し、下記の実行時間も再測定
・Ver 0.02a 2024/08/16 中心部のポイントが乱れる場合がある問題に対処
・Ver 0.02b 2024/09/04 傾きが0.5以上で線が太くなる問題に対処

★追記 2024/08/06 {
 上記は速度優先で同様な処理が2カ所にありましたが、速度低下を最小限にしてコードを短くしたバージョンを追記します。

傾き予測法によるライン描画処理[shorter code ver](C言語)
// draw line Ver 0.03b void LcdLine( int16_t xst, int16_t yst, int16_t xen, int16_t yen ) { int16_t x,y,difx,dify,until; int16_t err, xtail, ytail,dy; int16_t *px,*py,*pxtail, *pytail; LcdXPos = xen; LcdYPos = yen; difx = abs( xen - xst ) << 1; dify = abs( yen - yst ) << 1; if( ( (difx >= dify) && (xen > xst) ) || ( (difx < dify) && (yen > yst) ) ) { x = xst; y = yst; xtail = xen; ytail = yen; } else { x = xen; y = yen; xtail = xst; ytail = yst; } if( difx >= dify ) { px = &x; py = &y; pxtail = &xtail; pytail = &ytail; } else { int16_t tmp; px = &y; py = &x; pxtail = &ytail; pytail = &xtail; tmp = difx; difx = dify; dify = tmp; } dy = *pytail > *py ? 1 : -1; err = difx >> 1; until = *px + (difx>>1) + ( (difx&1) ? 0 : -1); if( difx >= (dify<<1) ) { // slope:[0,0.5] for( ; *px < until; (*px)++, (*pxtail)-- ) { PSET( x, y ); PSET( xtail, ytail ); err += dify; if( err >= difx ) { err += dify - difx; (*px)++; (*pxtail)--; *py += dy; *pytail -= dy; PSET( x, y ); PSET( xtail, ytail ); } } } else { // slope:(0.5,1] for( ; *px < until; (*px)++, (*pxtail)-- ) { PSET( x, y ); PSET( xtail, ytail ); err += dify; if( err >= difx ) { err -= difx; *py += dy; *pytail -= dy; } else { (*px)++; (*pxtail)--; PSET( x, y ); PSET( xtail, ytail ); err += dify - difx; *py += dy; *pytail -= dy; } } } for( ; *px <= *pxtail; (*px)++ ) { PSET( x, y ); err += dify; if( err >= difx ) { err -= difx; *py += dy; } } }
・Ver 0.03a 2024/08/16 中心部のポイントが乱れる場合がある問題に対処
・Ver 0.03b 2024/09/04 傾きが0.5以上で線が太くなる問題に対処
}

3.各ライン描画法の評価
 上述のライン描画法の評価環境として、前回の記事でも使用した CH32V203(8MHz動作)に TFT液晶 MSP2807(320 x 240 dot)を接続した環境を使いました。評価方法としては事前に乱数で生成した 512 本のラインデータに従ってライン描画処理を実行した際の時間を計測しました。時間計測は systick タイマの内部カウンタだけでも計測できそうでしたが、今回は systick を使った 1ms 毎の割込み処理でインクリメントするカウンタ値で時間を計測しています。コンパイル時のオプティマイズはディフォルトのままの -Os(サイズ優先)です。

 下図は評価時の LCD 表示です。

ライン描画法評価時の LCD 画面

 計測結果を下表に示します。

No.methodtime[s]
1標準的なライン描画13.185
2両端からの同時描画13.121
3今回提案の傾き予測法13.109

 上記の表では殆ど時間差が無いように見えますが、冒頭で書いたようにドット描画処理(PSET)が重いので PSET を何もしないダミー関数にし、ライン描画アルゴリズムの処理自体の時間を計測し直した結果が下表です。

No.methodtime[s]speed ratio
1標準的なライン描画0.2001.00
2両端からの同時描画0.1241.61
3今回提案の傾き予測法0.1121.79

 期待通り、今回提案の傾き予測法は標準的なライン描画より約 1.8 倍速いという結果になりました。


[TOP] [ 前へ ] CH32V203で遊ぶ [ 次へ ]
posted by skyriver at 15:27| Comment(0) | CH32V | このブログの読者になる | 更新情報をチェックする

2024年07月26日

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

 「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 * の方が妥当だと思いますがサンzプルのままで弄っていません。

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] [ 前へ ] CH32V203で遊ぶ [ 次へ ]
posted by skyriver at 23:31| Comment(0) | CH32V | このブログの読者になる | 更新情報をチェックする