2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

アドレス計算をさせずに処理時間短縮を実現〜肌感覚は意外に正しい

Last updated at Posted at 2025-10-24

背景

SSD1306というコントローラを使ったOLEDを使う仕事がありました。
実際に使うディスプレイは一般人では買いづらいのですが、秋月電子にSSD1306を使った小さなOLEDが売っていました。
0.96インチ 128×64ドット有機ELディスプレイ(OLED) 白色

ちょうど表示画素数も同じだったので、これを使って事前にドライバの開発などを始めます。
ディスプレイのドット構成はこんな感じ。

image.png

このコントローラを使ったOLEDは、縦方向が8ページに分かれています。
データの転送はページ毎に行います。
横書きの文字列を表示するのに適しています。

このコントローラには文字キャラクタが内蔵されていないので、マイコン内のメモリにスクリーンバッファを設けて、その中に文字や絵を描き、コントローラにスクリーンバッファの内容をページ毎に分けて転送します。

横方向は128ドットあり、好きな位置から好きな範囲を転送できます。
特定の位置の表示だけを更新するときに便利です。

このOLEDの一つのページは高さが8ドットなので、HD44780などの液晶コントローラで使われていた5ドットx7ドットのキャラクタを表示するには便利です。

横方向全てを更新するには128バイト転送しなくてはいけません。
全ての画素を更新するには1024バイト転送しなくてはならず、シリアル系ではビットに分解して送りますから、単純計算で8096ビットをシリアルで転送します。

しかし、I2Cなどのシリアル系のインターフェースは割り込みを使って転送することで、他の処理とは並行させて動かせるので、転送時間が気になるようなことはあまりないでしょう。

転送時に意外に時間がかかっている!?

特に動作に問題はなく、文字の表示ができるようになりました。

IMG_1547.jpeg

いつも通りmainループがどのくらいのスピードで回っているかを測定していました。
その際、転送時にmainループが2msec近く遅くなっていることがわかりました。
全画面を更新するわけでなく、一部の文字を表示更新が必要なときに、必要な箇所だけをI2Cで送る作りにしています。

I2C自体は割り込みで転送しているので、mainループに影響を与えません。

調べていくと、スクリーンバッファに文字キャラクタデータを書き込む時に時間がかかっていることがわかりました。
しかし、組み込み機器の開発を長年やっている肌感覚で、「この程度のデータ転送で2msecはかからないのでは?」と感じていました。

しかし、測定すると実際には1.4msecほどかかっています。

スクリーンバッファの構成と文字キャラクタの構成

今回、このような予想以上の時間がかかっているのは8ドットx8ドットの文字ではなく、

  • 2倍のサイズの文字
  • 3倍のサイズの文字
  • 4倍のサイズの文字

で発生していました。
今回は4倍サイズの文字で測定をしてみました。

8x8の1倍サイズの文字は、今までもこのようなディスプレイを扱ったことがあったので、自作のキャラジェネを使ったりしていました。
2〜4倍サイズの文字はデータを持っていないので、GitHubで公開されている方がいたので、それを利用させてもらいました。
LCD-fonts〜GitHub

OLEDのデータの送信方法はページごとに左から右へ送ります。
image.png

GitHubで公開されているフォントデータは、上から下、左から右という並びになっています。
image.png

このような関係から、フォントデータを順番に送っていく方式を採用する場合は、ページ指定を変えながらスクリーンバッファへデータを埋めていきます。

スクリーンバッファは下記のような構造でメモリ上にあります。

    unsigned char screenBuffer[8][128];    //8 page x 128 columns(8bit x 128column) x 

I2Cで転送する際、データはページに分かれてた方がいいので、このような構成でメモリ上に配置しています。
必要に応じて、ページに分けずベタで持たせたりしたいときは、キャストするなどして利用します。

4倍サイズの文字を例に、実際にスクリーンバッファへ描画する方法を検討します。

素直にページ毎に転送する

1.4msecの転送時間がかかっていたときは、このようにしていました。

1文字をスクリーンバッファに描画する関数の処理
    int width = 22;    //4倍文字の横幅(dot, 高さは32dot=4ページ)
    int row = 0;       //表示を開始するページ
    int pix = 0;       //表示を開始する横の位置
    int cg = CHAR_4X;  //4倍文字のフォントデータ

    for(int i = 0; i < width; i++) {
        for(int page = 0; page < 4; page++) {    //page
            screenBuffer[row + page][pix] = *cg++;
        }
        pix++;
    }
    

フォントデータは上から下の順にデータが並んでいるので、スクリーンバッファへはページを変えながら転送していきます。
上記のプログラムでは、同じ横方向の位置(pix)でページ(row + page)を4回変えて転送し、1ドット(pix)ずつ右へアドレスをずらしていきます。
フォントデータ(cg)はアドレスをインクリメントし続け、順番に送っていきます。

この方法は見た目にわかりやすいです。
ページ(row + page)を4回変え、終わったら横方向アドレス(pix)を変えていくというのがわかります。
このソースは可読性に優れていますよね。

しかし、個人的にはあまりこのような方法は使っていませんでした。
理由は、この方法だと転送のたびにアドレス計算をしているからです。
ここに、予想以上に時間がかかっていました!!

その測定結果がこちら。
4文字の4倍サイズの文字を送るのに1.41msecかかっています
RigolDS32.png

アドレスの計算をさせない方法

いつもやる通り、アドレス計算をさせず、ポインタを使ってスクリーンバッファへ文字を書いていきます。

ポインタを使ってスクリーンバッファへ描画する
    int width = 22;    //4倍文字の横幅(dot, 高さは32dot=4ページ)
    int row = 0;       //表示を開始するページ
    int pix = 0;       //表示を開始する横の位置
    int cg = CHAR_4X;  //4倍文字のフォントデータ
    
    unsigned char* page0 = &(screenBuffer[row][pix]);
    unsigned char* page1 = &(screenBuffer[row + 1][pix]);
    unsigned char* page2 = &(screenBuffer[row + 2][pix]);
    unsigned char* page3 = &(screenBuffer[row + 3][pix]);

    for(int i = 0; i < width; i++) { 
        *page0++ = *cg++;
        *page1++ = *cg++;
        *page2++ = *cg++;
        *page3++ = *cg++;
    }

アドレスは前出の方式の時とは違って、加算処理だけで算出されます。
転送時間は770usecまで高速化され、およそ半分の時間まで速くなりました。

RigolDS34.png

長年培った肌感覚...意外に正しい

個人的には、メモリ転送はいつも

for(int i = 0; i < width; i++) {
    *dst++ = *src++;
}

という感じでやっています。
やはりアドレス計算って意外に掛け算が出てくることがあるので、可能な限り加算だけで計算したいのです。

今回はページを変えながら...という処理が必要だったので、まずは素直に可読性のいいプログラムにしようと考えました。

処理速度を測定した時、「なんとなく時間がかかりすぎてないか?」と感じたことは、正しかったわけです。
よくよく調べていくと、可読性のいいプログラムがそのままにされていて、ポインタ処理に書き換えてなかったんです。

アプリケーションでこれが問題にならなければ、ポインタに置き換えなくてもいいのかなぁとは思います。
ただ、ポインタ処理に変えても、今回はそれほど可読性が悪くない気はしました。

お客様の実際のアプリケーションがわからないので、今回はポインタに置き換えておけば安心でしょう。

アセンブラで動きを確認するのは大変なので、測定でお茶を濁しました(笑)

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?