ゆるく楽しむプログラミング

PICO LCD-1.8 (ST7735S)

Raspberry Pi Pico用 1.8インチ 液晶モジュール 160×128 (ST7735S) のArduinoIDEでの使用方法
RaspberryPi PICOと連結して使用する設計になっているため、SPIピン(SLCK/MOSI)がGP10/GP11に固定になっていて変更できません。

ArduinoIDEでは(2022.02)現状、SPIピン(SLCK/MOSI)はGP2/GP3に固定になっており、普通の方法では、このLCDは使用できません。
1) 変換コネクタを使ってSPIピン(SLCK/MOSI)をGP2/GP3に変更する。
2) SoftwareSPIを使用する
3) ArduinoIDE以外の方法(Pythonとか)でプログラミング
の、いずれかの方法になります。



使用するピン

コネクタは40ピン付いていますが、実際に使用されているのは▼の8本です。
RaspberryPi PICOと合体して使用する場合 
物理ピン番号 論理ピン名  機能 
 11 GP8 Data/Command
 12 GP9   Slave Selec
 14 GP10 CLK
 15 GP11 MOSI
 16 GP12 Reset
 17 GP13 BackLight
 39 VSYS 1.8~5.5V
 38 GND  GND

RaspberryPi PICO+ArduinoIDEで使用する場合 
物理ピン番号 論理ピン名  機能 
 11 GP8 Data/Command
 12 GP9   Slave Selec
 4 GP2 CLK
 5 GP3 MOSI
 16 GP12 Reset
 17 GP13 BackLight
 39 VSYS 1.8~5.5V
 38 GND  GND

GP10をGP2に、GP11をGP3に差し替えれば、ArduinoIDEで普通にSPIデバイスとして使用できます。
※ ▼はAdafruit_ST7735_and_ST7789_Libraryのサンプルスケッチを使用


ArduinoUNOでs使用する場合にはレベルシフターか抵抗を使って、信号レベルを3.3Vに落とした方が良い模様
ArduinoUNOで使用する場合 
論理ピン名  機能 
任意 Data/Command
D10 ※変更可   Slave Selec
D13 CLK
D11 MOSI
任意 Reset
任意 BackLight
5V 5.0V
GND  GND



ST7735Sのコマンド

コマンドは1バイトでパラメタは全てデータ扱いのため、コマンド送信時にはD/CをLOWに、パラメタ送信時にはD/CをHIGHにする。

コマンド送信手順 
SS(Slave Select)  LOWにする
DC(Data/Command)   LOWにする
コマンド送信 
DC(Data/Command)   HIGHにする
 パラメタ送信
SS(Slave Select)  HIGHにする

▼ 必要最低限のコマンド。他にも色調整関連のコマンドが多数あります。詳細はST7735Sのスペックシート参照。
コマンド パラメタ 動作
0x01 SOFTWARE RESET
0x11 Sleep Out
0x13 NomalDisplayMode
0x20 Display Inversion Off
0x28 Display OFF
0x29 Display ON
0x36 1Byte スキャン方向
0x3A 0x05 16Bit Pixel Mode
0xB4 0x00 表示色反転モード
0xB6 0x15,0x02 フレームレート設定
コマンド パラメタ 動作
0x2A 開始X上位バイト、開始X下位バイト、終了X上位バイト、終了X下位バイト
(0~159の範囲なので上位バイトは0固定で良い気がする)
Colmun Address
0x2B 開始Y上位バイト、開始Y下位バイト、終了Y上位バイト、終了Y下位バイト
(0~159の範囲なので上位バイトは0固定で良い気がする)
Row Address
0x2C データ BitmapData

描画モード設定コマンド Memory Data Access Control (0x36)

  D7 D6 D5 D4 D3 D2 D1 D0
コマンド  0 0 1 1 0 1 1 0
パラメタ  MY
(Y軸方向)
MX
(X軸方向)
MV
(XY軸入れ替え)
ML
(Y軸更新方向)
RGB
(色割り当て
 RGB or BGR)
MH
(X軸更新方向)
- -

ML/RGB/MHは変更する必要はあまりないと思われる。
MY/MX/MVの組み合わせによる表示方向は▼のようになる



Dot指定方法設定コマンド Interface Pixel Format(0x3A)

1Dot(ピクセル)の色を指定する方法(フォーマット)を指定する
この指定で、描画コマンド Column Address Set (0x2A) のパラメタの意味が変わる

  D7 D6 D5 D4 D3 D2 D1 D0
コマンド  0 0 1 1 1 0 1 0
パラメタ  - - - - - IFPF2 IFPF1 IFPF0

IFPF[2:0] HEX Color Format
011 3 12-bit/pixel
101 5 16-bit/pixel
110 6 18-bit/pixel
111 7 No used



描画手順

描画範囲を指定し、指定範囲を埋めるBitmapデータを送信する
描画範囲 COLの指定  0X2A  0,XS,0,XE
描画範囲 ROWの指定   0x2B  0,YS,0xYE
描画データ送信   0x2C  Bitmapデータ





ST7753-PICO.zip (RaspberryPi PICO GP2/GP3使用)
ST7753-UNO.zip (ArduinoUNO)
ST7753-SoftSPI.zip (RaspberryPi PICO SoftwareSPI GP10/GP11使用)
※ <>を全角<>に置き換えています。コピペの際は注意してください(ZIPファイルの解凍を推奨)。
#include <SPI.h>
#include "image2.h"
#include "font8x16.h"

#define TFT_CS  9    // Data/Command
#define TFT_DC  8    // Data/Command
#define TFT_RST 12   // RESET
#define TFT_BL  13   // BACK LIGHT

#define TFT_WIDTH 160
#define TFT_HIGH  128
uint8_t SPIBuf[256] ;   // SPI転送用バッファ
uint8_t fontColorF[2] ; // TEXT FOR GROND COLOR
uint8_t fontColorB[2] ; // TEXT BACK GROND COLOR

char str[64] ;

SPISettings settings = SPISettings(32000000, MSBFIRST, SPI_MODE0);

// TFTにコマンドを送信
void tftSendCommand(uint8_t command) {
  digitalWrite(TFT_CS, LOW); // 
  digitalWrite(TFT_DC, LOW); // Command mode
  SPI.transfer(command);
  digitalWrite(TFT_CS, HIGH);  //
}

// TFTにコマンド+1バイトデータを送信
void tftSendCommand1(uint8_t command, uint8_t data1) {
  digitalWrite(TFT_CS, LOW); // 
  digitalWrite(TFT_DC, LOW); // Command mode
  SPI.transfer(command);
  digitalWrite(TFT_DC, HIGH); // Command mode
  SPI.transfer(data1);
  digitalWrite(TFT_CS, HIGH);  //
}

// TFTにコマンド+2バイトデータを送信
void tftSendCommand2(uint8_t command, uint8_t data1, uint8_t data2) {
  digitalWrite(TFT_CS, LOW); // 
  digitalWrite(TFT_DC, LOW); // Command mode
  SPI.transfer(command);
  digitalWrite(TFT_DC, HIGH); // Command mode
  SPI.transfer(data1);
  SPI.transfer(data2);
  digitalWrite(TFT_CS, HIGH);  // 
}

// TFTにコマンド+3バイトデータを送信
void tftSendCommand3(uint8_t command, uint8_t data1, uint8_t data2, uint8_t data3) {
  digitalWrite(TFT_CS, LOW); // 
  digitalWrite(TFT_DC, LOW); // Command mode
  SPI.transfer(command);
  digitalWrite(TFT_DC, HIGH); // Command mode
  SPI.transfer(data1);
  SPI.transfer(data2);
  SPI.transfer(data3);
  digitalWrite(TFT_CS, HIGH);  //
}

// TFTにコマンド+4バイトデータを送信
void tftSendCommand4(uint8_t command, uint8_t data1, uint8_t data2, uint8_t data3, uint8_t data4) {
  digitalWrite(TFT_CS, LOW); // 
  digitalWrite(TFT_DC, LOW); // Command mode
  SPI.transfer(command);
  digitalWrite(TFT_DC, HIGH); // Command mode
  SPI.transfer(data1);
  SPI.transfer(data2);
  SPI.transfer(data3);
  SPI.transfer(data4);
  digitalWrite(TFT_CS, HIGH);  //
}

// 画面クリア
void cls(bool rot) {
  if (rot) {
    tftSendCommand4(0x2A,0,0,0,131) ; // Colmun Address
    tftSendCommand4(0x2B,0,0,0,161) ; // Row Address
  } else {
    tftSendCommand4(0x2A,0,0,0,160) ; // Colmun Address
    tftSendCommand4(0x2B,0,0,0,131) ; // Row Address
  }

  digitalWrite(TFT_CS, LOW);
  digitalWrite(TFT_DC, LOW); // Command mode
  SPI.transfer(0x2C);
  digitalWrite(TFT_DC, HIGH); // Data mode
  for (int i=0;i<160;i++) {
    for (int j=0;j<131;j++) {
      SPI.transfer(B00000000);
      SPI.transfer(B00000000);
    }
  }
  digitalWrite(TFT_DC, LOW); // Command mode
  digitalWrite(TFT_CS, HIGH);  //
}

// 塗りつぶし
void fill(int xs,int ys , int xe,int ye , uint8_t c1 , uint8_t c2) {
  tftSendCommand4(0x2A,0,xs,0,xe) ; // Colmun Address
  tftSendCommand4(0x2B,0,ys,0,ye) ; // Row Address

  digitalWrite(TFT_CS, LOW);
  digitalWrite(TFT_DC, LOW); // Command mode
  SPI.transfer(0x2C);
  digitalWrite(TFT_DC, HIGH); // Data mode
  for (int i=0;i<xe-xs+1;i++) {
    for (int j=0;j<ye-ys+1;j++) {
      uint8_t dw = c1 ;
      SPI.transfer(dw);
      dw = c2 ;
      SPI.transfer(dw);
    }
  }
  digitalWrite(TFT_DC, LOW); // Command mode
  digitalWrite(TFT_CS, HIGH);  //
}

// BITマップ表示
void drowBitMap(int offsetX , int offsetY , int width, int hight, uint8_t bitMap[]) {
  uint8_t endX = offsetX + width - 1;
  uint8_t endY = offsetY + hight - 1;
  tftSendCommand4(0x2A,0,offsetX,0,endX) ; // Colmun Address
  tftSendCommand4(0x2B,0,offsetY,0,endY) ; // Row Address
  delayMicroseconds(500);

  int pos = 0 ;
  digitalWrite(TFT_CS, LOW);
  digitalWrite(TFT_DC, LOW); // Command mode
  SPI.transfer(0x2C);
  digitalWrite(TFT_DC, HIGH); // Data mode
  for (int row = 0 ; row < hight ; row ++) {
    // 表示データ転送
    // Arduinoだとメモリが足りなくて1画面分を一気に転送する事ができない
    // なので、1Lineずつ送信している
    for (int col = 0 ; col < width * 2 ; col ++ ) {
      SPIBuf[col] = bitMap[pos];
      //      SPIBuf[col] = 0xF8 ;
      pos ++ ;
    }
    SPI.transfer(SPIBuf, width * 2);
  }
  digitalWrite(TFT_DC, LOW); // Command mode
  digitalWrite(TFT_CS, HIGH);  //
}

void DrowFont(uint8_t code , uint8_t x, uint8_t y) {
  int pos = (code - 0x20) * 16 ;
  memset(SPIBuf,0,8*16*2) ;
  for (int i=0;i<16;i++) {
    uint8_t data = ssd1306xled_font8x16[pos + i];
    uint8_t bitMast = 0x01 ; 
    for (int j=0;j<8;j++) {
      int fpos = 0 ;
      if (i<8) {
        fpos = (j*8+i)*2 ;
      } else {
        fpos = ((j+8)*8+(i-8))*2 ;
      }
      if ((data & bitMast) == 0) {
        SPIBuf[fpos] = fontColorB[0] ;
        SPIBuf[fpos+1] = fontColorB[1] ;
      } else {
        SPIBuf[fpos] = fontColorF[0] ;
        SPIBuf[fpos+1] = fontColorF[1] ;
      }
      bitMast <<= 1 ;
    }
  }

  uint8_t endX = x + 7 ;
  uint8_t endY = y + 15;

  tftSendCommand4(0x2A,0,x,0,endX) ; // Colmun Address
  tftSendCommand4(0x2B,0,y,0,endY) ; // Row Address
  digitalWrite(TFT_CS, LOW);
  digitalWrite(TFT_DC, LOW); // Command mode
  SPI.transfer(0x2C);
  digitalWrite(TFT_DC, HIGH); // Data mode
  SPI.transfer(SPIBuf, 8*16*2);
  digitalWrite(TFT_DC, LOW); // Command mode
  digitalWrite(TFT_CS, HIGH);  // 
  delayMicroseconds(500);
}

void DrowStr(char str[] , uint8_t x , uint8_t y) {
  for (int i=0;str[i] != 0 ; i++) {
      DrowFont(str[i],x,y) ;
      x += 8 ;
      if (x+8>=96) {
        x = 0 ;
        y += 16 ;
        if (y + 16 >= 64) {
          y = 0 ; 
        }
      }
  }
}

void setup() {
  Serial.begin(9600);
  Serial.print(F("Pico LCD-1.8 (ST3375)Test"));

  SPI.begin();  //SPIを初期化、SCK、MOSI、SSの各ピンの動作は出力、SCK、MOSIはLOW、SSはHIGH
  SPI.beginTransaction(settings);

  // -----  ST3375 INITIAL -----
  pinMode(TFT_CS, OUTPUT);
  pinMode(TFT_DC, OUTPUT);
  pinMode(TFT_BL, OUTPUT);
  pinMode(TFT_RST, OUTPUT);

  // BACK LIGHT ON
  digitalWrite(TFT_BL, HIGH);

  // --- HARD Ware Reset
  digitalWrite(TFT_RST, HIGH);
  delay(500);                  // VDD goes high at start, pause for 500 ms
  digitalWrite(TFT_RST, LOW);  // Bring reset low
  delay(100);                  // Wait 100 ms
  digitalWrite(TFT_RST, HIGH); // Bring out of reset
  delay(500);                  // Wait 500 ms, more then 120 ms
  // --- SOFT Ware Reset
  tftSendCommand(0x01) ;       // SOFTWARE RESET
  delay(50);

  // --- Initial Comands
  bool r = false ;
  tftSendCommand(0x28) ;            // Display OFF : 0x29 - Display ON
  delay(500);

  tftSendCommand(0x11) ;            // Sleep Out
  delay(500);
  tftSendCommand1(0x3A,05) ;        // 16Bit Pixel Mode
  delay(10);
  tftSendCommand1(0x36,B10100000) ; r = false; // MX MY MV ML RGB MH x x:横向き1
//  tftSendCommand1(0x36,B01100000) ; r = false;  // MX MY MV ML RGB MH x x:横向き2
//  tftSendCommand1(0x36,B00000000) ; r = true;  // MX MY MV ML RGB MH x x:縦向き1
//  tftSendCommand1(0x36,B11000000) ; r = true;  // MX MY MV ML RGB MH x x:縦向き2(多分これがスタンダード)
  tftSendCommand2(0xB6,0x15,0x02) ; // Display settings #5
  tftSendCommand1(0xB4,0x00) ;      // Display inversion control

  tftSendCommand(0x13) ;            // NomalDisplayMode
  tftSendCommand(0x20) ;            // Display Inversion Off

  // ---
  cls(r) ;
  delay(500);
  tftSendCommand(0x29) ;            // Display ON : 0x28 - Display OFF
  delay(500);
}

// the loop function runs over and over again forever
void loop() {
  bool r = false ;
  fontColorB[0] = 0x00 ; fontColorB[1] = 0x00 ; // TEXT BACK GROND COLOR
  fontColorF[0] = 0xFF ; fontColorF[1] = 0xFF ; // TEXT FOR GROND COLOR - WHITE
  for (int i=0;i<4;i++) {
    switch(i) {
      case  0:
        sprintf(str,"B10100000");
        tftSendCommand1(0x36,B10100000) ;// MX MY MV ML RGB MH x x:横向き1
        r = false; 
        break ;
      case  1:
        sprintf(str,"B01100000");
        tftSendCommand1(0x36,B01100000) ; // MX MY MV ML RGB MH x x:横向き2
        r = false; 
        break ;
      case  2:
        sprintf(str,"B00000000");
        tftSendCommand1(0x36,B00000000) ; // MX MY MV ML RGB MH x x:縦向き1
        r = true; 
        break ;
      case  3:
        sprintf(str,"B11000000");
        tftSendCommand1(0x36,B11000000) ; // MX MY MV ML RGB MH x x:縦向き2(多分これがスタンダード)
        r = true; 
        break ;
    }
    cls(r) ;
    DrowStr(str,10,0) ;
    drowBitMap(10,20,96,64,bitmapData2) ;
    fill(10, 90,106, 99,B11111000,B00000000) ;
    fill(10,102,106,111,B00000111,B11100000) ;
    fill(10,113,106,122,B00000000,B00011111) ;
    delay(5000);
 }
}

BITMAPデータの作成


Windows用の画像データをLCD用のBITMAP定義データに変換するツール LCD Image Converter を利用します。

96x64にリサイズしたImageファイルを読み込みます。

トリミングと拡大は読み込み後にも操作できます。
Optionsを開いて作成するイメージの BlockSizeを8Bitに設定
コンバートを実行 
  イメージデータの定義ファイルが出来るので
スケッチに貼付けて使用できます。

解説動画