次はSTM32ボードを積極的に使ていきたい(26) グラフィック液晶(7)
フォント描画処理の改善
SPI接続のグラフィック液晶モジュール(コントローラー ILI9341)利用の調査を継続中です。
利用しているAdafruit_ILI9341_STMライブラリのフォント表示、ビットマップ表示の
描画処理の遅さが気になり何とかならないかと試行錯誤してみました。
特に横向きに利用した場合、ハードウェア的にスクロール機能が無いため、
表示内容を上にスクロールするには、全画面再表示をする必要があり、
描画速度が遅いのは致命的です。
遅さの原因を調べるためライブラリのソースを見ると、
文字表示、ビットマップ表示は1点毎にdrawPixel()関数で描画しているようです。
これはかなり効率の悪い処理です。1点毎にグラフィック液晶モジュールに
コマンドとデータの送信を行っています。
そこで処理を前回の画像表示で高速化に貢献したDMAを利用する方式を適用してみました。
なるべくコマンド送信回数を減らす作戦です。
実装する画面構成
改善を図ります。
この処理が高速化されれば、スクロール表示処理も当然改善されます。
前半が改善前、後半が改善後です。
かなり改善出来た雰囲気が伝わると思います。
数値的には、53x30文字表示全表示に かかる時間は、
方式的には、1ライン(320ドット)用のバッファを用意し、1ライン毎にDMA転送を行いまいた。
1280バイト(320ドットx16ビットx2)ほどバッファ用のメモリを消費しますが、効果は絶大です。
さて、理論的に最大何処まで改善可能なのか見積もってみると、
今回のSPIの転送速度はクロックが36MHzなので、データ送信能力は
36,000,000ビット/秒
です。
一方、グラフィック画面の1画面のデータは、
320x240x16ビット= 1,228,800 ビット
です。
1画面の更新に必要とする時間は、
1,228,800 ビット ÷ 36,000,000ビット/秒 = 0.034133333 秒 = 34.13 ms
理論的な最大パフォーマンスに近い値が出せていることが分かります。
検証用スケッチ
// // BluePill(Arduino STM32) グラフィック液晶(ILI9341) フォント表示改善デモ // 2018/07/09 by たま吉さん #include <Adafruit_GFX_AS.h> // Core graphics library #include <Adafruit_ILI9341_STM.h> // Hardware-specific library #include <font6x8tt.h> // 6x8ドットフォント // TFT制御用ピン #define TFT_CS PA0 #define TFT_RST PA1 #define TFT_DC PA2 // TFT制御用 Adafruit_ILI9341_STM tft = Adafruit_ILI9341_STM(TFT_CS, TFT_DC, TFT_RST); // フォント管理用 uint8_t* fontTop; #define CH_W 6 // フォント横サイズ #define CH_H 8 // フォント縦サイズ // スクリーン管理用 #define SC_W 53 // キャラクタスクリーン横サイズ #define SC_H 30 // キャラクタスクリーン縦サイズ #define SCSIZE (SC_W*SC_H) // スクリーンサイズ uint8_t screen[SCSIZE]; // スクリーンバッファ // 指定座標に文字の表示 void drawChar(uint16_t x, uint16_t y, uint8_t c) { uint8_t* ptr = &fontTop[c*8]; // フォントデータ先頭アドレス for (uint8_t i=0; i <8; i++) { for (uint8_t j=0; j < 6; j++) { if ( (*ptr) & (0x80>>j)) { tft.drawPixel(x+j,y+i,ILI9341_WHITE); } else { tft.drawPixel(x+j,y+i,ILI9341_BLACK); } } ptr++; } } // 従来の画面更新表示 void sc_updateOld() { for (uint16_t y=0; y < SC_H; y++) { for (uint16_t x=0; x < SC_W; x++) { uint8_t c = screen[SC_W*y+x]; // キャラクタの取得 drawChar(x*CH_W, y*CH_H, c); } } } // 指定位置の文字の更新表示 void sc_updateChar(uint16_t x, uint16_t y) { uint8_t c = screen[SC_W*y+x]; // キャラクタの取得 uint8_t* ptr = &fontTop[c*8]; // フォントデータ先頭アドレス tft.setAddrWindow(x*CH_W, y*CH_H, x*CH_W+CH_W-1, y*CH_H+CH_H-1); for (uint8_t i=0; i <8; i++) { for (uint8_t j=0; j < 6; j++) { if ( (*ptr) & (0x80>>j)) { tft.pushColor(ILI9341_WHITE); //SPI.write(ILI9341_WHITE); } else { tft.pushColor(ILI9341_BLACK); //SPI.write(ILI9341_BLACK); } } ptr++; } } // 指定行をTFT画面に反映 // 引数 // ln:行番号(0~29) void sc_updateLine(uint16_t ln) { uint8_t c; uint8_t dt; uint16_t buf[2][SC_W*CH_W]; uint16_t index; for (uint16_t i=0; i < CH_H; i++) { // 1文字高さ分ループ index = 0; for (uint16_t clm = 0; clm < SC_W; clm++) { // 横文字数分ループ c = screen[SC_W*ln+clm]; // キャラクタの取得 dt = fontTop[c*8+i]; // 文字内i行データの取得 for (uint16_t j=0; j < 6; j++) { if ( dt & (0x80>>j)) { buf[i&1][index] = ILI9341_WHITE; } else { buf[i&1][index] = ILI9341_BLACK; } index++; } } tft.pushColors(buf[i&1], SC_W*CH_W, 1); } } void setup() { Serial.begin(115200); delay(1000); // TFTの初期化 tft.begin(); tft.setRotation(3); tft.fillScreen(ILI9341_BLACK); fontTop = (uint8_t*)font6x8tt+3; memset(screen, 0, SCSIZE); // ダミーデータのセット for (uint16_t i=0; i < SCSIZE; i++) screen[i] = i % 256; } void loop() { Serial.print("Hit any key to start"); while(Serial.read() <0); Serial.println(); // 従来方式 uint32_t t1, t2; uint8_t c = 0; for (uint16_t i=0; i < 15; i++) { t1 = micros(); sc_updateOld(); t2 = micros(); Serial.print("update Time ="); Serial.println(t2-t1,DEC); memmove(&screen[0], &screen[SC_W*1], SCSIZE-SC_W); for(uint8_t j=0;j < SC_W; j++) screen[SC_W*29+j] = c++; //delay(10); } delay(3000); // 高速対応 for (uint16_t i=0; i < 300; i++) { t1 = micros(); tft.setAddrWindow(0, 0, SC_W*CH_W-1, 29*CH_H+CH_H-1); for (uint8_t y=0; y < 30; y++) { sc_updateLine(y); } t2 = micros(); Serial.print("update Time ="); Serial.println(t2-t1,DEC); memmove(&screen[0], &screen[SC_W*1], SCSIZE-SC_W); for(uint8_t j=0;j < SC_W; j++) screen[SC_W*29+j] = c++; //delay(10); } }
pushColors()関数の利用が今回の改善の立役者です。
内部的にDMA転送をバックグランドで行ってくれます。
setAddrWindow()、pushColors()、pushColor()関数は使い方によってかなり描画処理の
改善を行うことが出来ます。
いずれは、豊四季Tiny BASIC for Arduino STM32にこのテキスト描画を採用しようと思います。
« 次はSTM32ボードを積極的に使ていきたい(25) グラフィック液晶(6) | トップページ | Arduino用漢字フォントライブラリ SDカード版のSdfat対応 »
「arduino」カテゴリの記事
- Arduino IDE+Arduino STM32環境で指定と異なるgccが使われてしまう(2025.01.23)
- Zorin OSでArduino Uno互換機(CH340)が認識しない(2025.01.19)
- Arduino IDE 2.3.4でArduino STM32を利用する(2025.01.12)
- Arduino用 SKK日本語変換ライブラリの開発 その1(2024.12.28)
- NeoPixel(WS2812B)の制御 その5(2024.09.15)
「表示器制御関連」カテゴリの記事
- NeoPixel(WS2812B)の制御 その5(2024.09.15)
- Arduino用 美咲フォントライブラリを更新しました(2024.03.21)
- Raspberry Pi Pico(MicroPython)でLEDドットマトリックスを使ってみる(2024.03.14)
- Raspberry Pi Pico MicroPython用のマルチフォントライブラリ(2023.02.09)
- MicroPython(Raspberry Pi pico)で8x8ドットNeoPixcel文字表示(2023.02.08)
「ARM」カテゴリの記事
- Arduino IDE+Arduino STM32環境で指定と異なるgccが使われてしまう(2025.01.23)
- Arduino IDE 2.3.4でArduino STM32を利用する(2025.01.12)
- PocketGoで遊んでみる(1)(2020.03.24)
- Arduino用 MML文演奏ライブラリの作成 その1(2019.04.01)
- BluePillボードで4桁7セグLEDの制御(2019.03.21)
「STM32」カテゴリの記事
- Arduino IDE+Arduino STM32環境で指定と異なるgccが使われてしまう(2025.01.23)
- Arduino IDE 2.3.4でArduino STM32を利用する(2025.01.12)
- 「Arduino STM32 リファレンス 日本語版」が2万アクセス突破!(2021.03.26)
- SPI接続フラッシュメモリモジュールを入手しました(2020.05.13)
- Arduino STM32でキャラクタ液晶ディスプレイを使う(2019.06.01)
« 次はSTM32ボードを積極的に使ていきたい(25) グラフィック液晶(6) | トップページ | Arduino用漢字フォントライブラリ SDカード版のSdfat対応 »
コメント