ArduinoでaitendoのTFT液晶(M-Z18SPI-2PB)を制御するライブラリを作成するための調査(7)

投稿者: | ↻ : 2019年1月28日

はじめに

液晶の制御チップの初期設定に必要なコマンドが分かったので、1Pixelを描画する手順を調べます。

Adafruit_ST7735Sクラス

調べる関数はdrawPixelです。


writePixelでは?

Adafruit-GFX-LibrarydrawPixelを純粋仮想関数として、それを隠蔽するようにwritePixelからdrawPixelを呼び出すようにしているのに、付属のサンプルスケッチではdrawPixelを直接呼び出してしまっているので、writePixelの意味が無いです。

各描画処理で、描画前、描画後に呼ばれる処理としてstartWriteendWriteを用意して、必要ならば「サブクラスでオーバーライドしてstartWriteendWriteの実装と、各描画処理にstartWriteendWriteの呼び出しを追加して」とのコメントがあり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
}

処理の概要

  1. 描画座標のチェック
    範囲外ならば描画しない。_width_heightは回転処理(setRotation)で縦横が再設定される。
  2. 描画範囲を設定する。
  3. SPI通信の設定する。
    SPIライブラリにより、それまでの設定は退避され排他処理が開始される。
  4. データを転送する指定を行う。
  5. 液晶モジュールとのSPI通信を有効にする。
  6. データを送信する。
  7. 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ビットを取得するマクロが、highBytelowByteとして定義されています。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ビットカラーへの対応も行えるようにしたいです。

やっと、コードが書けます( ≧ω≦ )و