はじめに
液晶の制御チップの初期設定に必要なコマンドが分かったので、1Pixelを描画する手順を調べます。
Adafruit_ST7735Sクラス
調べる関数はdrawPixel
です。
writePixelでは?
Adafruit-GFX-Library
でdrawPixel
を純粋仮想関数として、それを隠蔽するようにwritePixel
からdrawPixel
を呼び出すようにしているのに、付属のサンプルスケッチではdrawPixel
を直接呼び出してしまっているので、writePixel
の意味が無いです。
各描画処理で、描画前、描画後に呼ばれる処理としてstartWrite
、endWrite
を用意して、必要ならば「サブクラスでオーバーライドしてstartWrite
、endWrite
の実装と、各描画処理にstartWrite
、endWrite
の呼び出しを追加して」とのコメントがあり1Pixel描画の場合、その場所がwritePixel
です。なので実際に使って欲しいのはwritePixel
だったのかと思います。
1Pixelを描画する処理はサブクラスでdrawPixel
実装する作りなので、drawPixel
を調査します。
描画の手順
対象のコード
Adafruit_ST7735.cpp
からの抜粋
void Adafruit_ST7735::drawPixel(int16_t x, int16_t y, uint16_t color) {
if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return;
setAddrWindow(x,y,x+1,y+1);
#if defined (SPI_HAS_TRANSACTION)
if (hwSPI) SPI.beginTransaction(mySPISettings);
#endif
DC_HIGH();
CS_LOW();
spiwrite(color >> 8);
spiwrite(color);
CS_HIGH();
#if defined (SPI_HAS_TRANSACTION)
if (hwSPI) SPI.endTransaction();
#endif
}
処理の概要
- 描画座標のチェック
範囲外ならば描画しない。_width
と_height
は回転処理(setRotation
)で縦横が再設定される。 - 描画範囲を設定する。
- SPI通信の設定する。
SPIライブラリにより、それまでの設定は退避され排他処理が開始される。 - データを転送する指定を行う。
- 液晶モジュールとのSPI通信を有効にする。
- データを送信する。
- SPI通信の設定を戻す。
SPIライブラリにより、退避された設定に戻し排他処理が解除される。
色データ送信について
Arduino UNO R3が採用しているATmega328pの仕様で、SPI通信は8ビットごとに送信されます。そのため16ビットカラーの場合は2回の送信になります。
Arduino(ATmega328P)のバイトオーダーはリトル・エンディアン(Little – endian)です。なので下位8ビット、上位8ビットの順に送ることで、uint16_t
で渡された値を最上位ビットから16ビット送ることになります。
(NOTE:ESP32もリトル・エンディアンです)
spiwrite
spiwrite
は引数にuint8_t
(符合無し8ビット)を受け取りますが、drawPixel
ではuint16_t
(符合無し16ビット)の値を渡しています。結果は、実装依存ですが下位8ビットが渡るので処理として希望の動作になります。
inline void Adafruit_ST7735::spiwrite(uint8_t c) {
if (hwSPI) {
#if defined (SPI_HAS_TRANSACTION)
SPI.transfer(c);
#elif defined (__AVR__) || defined(CORE_TEENSY)
// ...省略
#elif defined (__arm__)
// ...省略
#endif
} else {
// ...省略 ソフトウェアSPI処理
}
}
Arduino UNO R3を含めハードウェアSPI対応のボードに限定するならば、SPIライブラリにSPI.transfer16
として実装されているので、その処理に任せれば済みます。このspiwrite
のメインはソフトウェアSPIを行うことです。
Github – Arduino/hardware/arduino/avr/libraries/SPI/src/SPI.hからの抜粋
inline static uint16_t transfer16(uint16_t data) {
union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } in, out;
in.val = data; /// 共用体により上位と下位の8ビットを取り分ける
if (!(SPCR & _BV(DORD))) { /// ビットオーダーにより送る順番を切り替えている
SPDR = in.msb; /// 上位ビットから送信
asm volatile("nop"); // See transfer(uint8_t) function
while (!(SPSR & _BV(SPIF))) ;
out.msb = SPDR;
SPDR = in.lsb;
asm volatile("nop");
while (!(SPSR & _BV(SPIF))) ;
out.lsb = SPDR;
} else {
SPDR = in.lsb; /// 下位ビットから送信
asm volatile("nop");
while (!(SPSR & _BV(SPIF))) ;
out.lsb = SPDR;
SPDR = in.msb;
asm volatile("nop");
while (!(SPSR & _BV(SPIF))) ;
out.msb = SPDR;
}
return out.val;
}
また、上位8ビットと下位8ビットを取得するマクロが、highByte
、lowByte
として定義されています。uint8_t
の範囲内になるように変換してからキャストしているので、実装依存の影響を減らせます。
Github – Arduino/hardware/arduino/avr/cores/arduino/Arduino.hからの抜粋
#define lowByte(w) ((uint8_t) ((w) & 0xff))
#define highByte(w) ((uint8_t) ((w) >> 8))
まとめ
ST7735
シリーズの複数の型番、Arduinoの複数のボードへ対応するためにコードが複雑になっているので、自作ライブラリを作成する際に、この辺の処理は独自に考える必要がありそうです。
また、16ビットカラーのみの対応(一部は12ビットカラーでも動作する)のようなので、18ビットカラーへの対応も行えるようにしたいです。
やっと、コードが書けます( ≧ω≦ )و