Arduino環境でWaveshare ESP32-S2-Picoを使う

コンピュータ、組み込み

WaveShareからRaspberry Pi Pico互換ボードでESP32-S2を搭載した基板を入手したので評価してみます。
Arduino環境でのセットアップ、トラブル事例と、基本的な入出力、通信機能(UART, I2C, SPI)を使ってみます。

紹介するもの

ESP32-S2-Pico

特徴

電源とGND、GPIOピン位置は互換であるが、ピン番号互換は無い。

ESP32-S2搭載であるため、処理速度は高い。
技適未対応です。
ライブラリはRP2040のものは使用できないので、ESPライブラリを使用することになります。

接続Type C
CPUESP32-S2 Xtensa LX7 (240MHz)
フラッシュMemory4MB
PSRAM8MB
GPIO26
PWM26 同時に8本まで使用可能 (8bit 255まで)
DAC1 (8bit 0 ~ 255, 3.3V)
ADC15 (13bit 0 ~ 8191, 3.3V)
UART2
I2C1
SPI2
ボタンRESET
LED1 赤 (GP9)
[2022/12/11修正 I2C数、PWM数、ADC数]
ピン配置
メーカー掲載のピン配置表

外観

400穴ブレッドボードでは、左右2列ずつ使用することができます。

Raspberry Pi Pico と比べても電源、GND、IOピン位置は同じです。
ピン番号と通信ピンが違うのでボードライブラリを変更しても互換にはなりません。

使ってみて

技適がないので電波は出せませんが、高性能マイコン基板としては十分使用できます。
Ali Expressでも\1,000強で、入手性も悪くありません。

Raspberry Pi Picoの互換製品としては電源とGND位置は共通ですが、ピン番号違い以外にもADCの位置や分解能の違いがありますが、ソフトウエアで吸収できる範囲だと思います。
無線を使用することができませんが、ピン設定はRP2040より自由度が高く処理性能も高いので、単純な置き換えとして以外にも高いパフォーマンスと使い勝手の良さは感じられます。

Waveshare ESP32-S3-Picoの記事

準備

ライブラリ

ボードライブラリ

Arduino IDEのボードマネージャからESP32用のライブラリのインストールとボードの選択をします。

ボードマネージャのURLhttps://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
検索ESP
ボードライブラリesp32 by Espressif Systems バージョン x.x.x※
選択するボードesp32 > ESP32S2 Dev Module
※ x.x.x 動作確認では2.0.4 を使用しました

モジュールライブラリ

機能やモジュールを使用しない場合インストールの必要はありません。

機能/モジュールライブラリ名検索確認時のバージョン
SSD1306Adafruit SSD1306 by AdafruitSSD13062.5.1
ST7735Adafruit ST7735 and ST7789
Library by Adafruit
ST77351.9.3
関連
SSD1306
ST7735
Adafruit GFX Library by AdafruitGFX1.11.3

スケッチの書き込み

一度スケッチを書きこむとCOMポートの認識をしなくなる。

スケッチを書き込むときは、次の2通りの方法で行う。
・BOOTボタンを押しながら電源を投入する。
・電源投入後にBOOTボタンを押しながらRESETボタンを押す。

COMポートが認識されるようになるので、ArduinoIDEからCOMポートを選択して書き込みを行う。

トラブル

現象(OLEDでの表示で意図した通りの動作をしない)

OLEDでの表示で、意図したとおりの描画ができず表示速度が遅い。

原因

詳しい原因はわかりません。
I2CのSDA, SCLのGPIO番号の指定先に GPIO6, GPIO7を指定したことによると推測。
公式のピン配置表では、I2C1で使用できることになっているがWireでもWire1を使用しても同結果。
また、SDA, SCLを反対に使用しても同結果。

すべての組み合わせを調査していないが、GPIO6, GPIO7, GPIO8を含むI2Cのピン設定を行うことで発生する。

対応

当該GPIOピン番号の使用を避ける。

そのほか情報

確認日 : 2022.11.13
ライブラリ : esp32 by Espressif Systems バージョン 2.0.5
ボード : ESP32S2 Dev Module

基本スケッチ

LEDチカ

説明

基板上のLEDを点滅させます。
ESP32-S2-PicoのビルトインLEDのGPIO番号はGP9です。

スケッチ
#define LED1 9       //BuiltINLED1 (赤)

void setup()
{
  pinMode(LED1, OUTPUT);  //ピン出力設定
}

void loop()
{
  digitalWrite(LED1, HIGH);
  delay(500);

  digitalWrite(LED1, LOW);
  delay(500);
}
結果

LEDが点滅しました。

PWM

説明

PWMを使ってLEDのフェード点灯(ゆっくり点灯)とフェード消灯(ゆっくり消灯)を行います。

スケッチ
//ESP32-S2 picoで使用できる PWMは
// 2, 3, 4, 5, 43, 44, 14, 15

#define LED 2     //

void setup()
{
  pinMode(LED , OUTPUT);

}

void loop()
{
  int i;

  for (i = 0; i < 256; i ++)
  {
    analogWrite(LED , i);
    delay(5);
  }

  for (i = 0; i < 256; i ++)
  {
    analogWrite(LED , 255 - i);
    delay(5);
  }

}
結果

動作結果の動画は用意していませんが、LEDがゆっくり点灯とゆっくり消灯を繰り返しました。

ADC

説明

スライド抵抗通過後の電圧値を読み取ります。
USB-シリアル変換アダプタとしてFT232RLを使用していますが、ESP32-S2-Picoはスケッチ書き込み後USBからのCOMが認識されなくなるので、UART0を使用して結果を読み取ります。

UART0はGP43(TX), 44(RX)ですので、FT232RLを配線しています。

配線
ESP32-S2-Pico配線スライド抵抗配線FT232RL
3.3V+
GND
GP6(Analog Input)S
GP43(TX)RX
GP44(RX)TX
スケッチ
#define PIN_ADC0 6      //ADC0
#define PIN_ADC1 7      //ADC1
#define PIN_ADC2 8      //ADC2
void setup()
{
  delay(1000);
  Serial.begin(115200);         //結果はCOMに出力
  //ESP32-S2 picoの場合、書き込み後にUSB のCOM出力が認識されなくなるので、UART0(GP43 TX, GP44 RX)から読み取りを行う。

  //ADCは3個読み取りで使用する
  pinMode(PIN_ADC0, INPUT);
  pinMode(PIN_ADC1, INPUT);
  pinMode(PIN_ADC2, INPUT);
}

void loop()
{
  //ESP32-S2 picoのADCは13bit (0 ~ 8191)
  long ADC0 = 0;
  long ADC1 = 0;
  long ADC2 = 0;

  ADC0 = analogRead(PIN_ADC0);
  ADC1 = analogRead(PIN_ADC1);
  ADC2 = analogRead(PIN_ADC2);

  //100msごとに読み取った値を表示する。
  Serial.printf("ADC0, ADC1, ADC2 = %4ld, %4ld, %4ld\r\n", ADC0, ADC1, ADC2);
  delay(100); 
}
結果

スライド抵抗の持ち合わせが一つしかないので、画像の結果はGP6(ADC0)に接続しているときのものです。

スライド抵抗のつまみを変化させることで、A/Dの読み取り値に変化がありました。
ADC1とADC2はオープン状態ですので何らかのノイズを拾っています。
スライド抵抗を接続すれば、正常なAD値を読み取ることはできます。

UART

説明

UARTは2系統ありますが、UART0はCOMになります。
ESP32-S2-Picoはスケッチ書き込み後にUSBのCOMは認識されなくなるので、UART0を使用するにはGP43(TX)とGP44(RX)を使用します。

UART1はそのままでは内部フラッシュに接続されているので、そのままでは使用できません。
スケッチでピン指定して使用しています。

UART0(COM)から読み取ったデータをUART1に送信します。
UART1から読み取ったデータをUART0(COM)に送信真します。

配線
ESP32-S2-Pico配線FT232RL(UART0側)配線FT232RL(UART1側)
GP2(UART1TX)RX
GP3(UART1RX)TX
GP43(UART0TX)RX
GP44(UART0TXTX
スケッチ
void setup()
{
  Serial.begin(115200);                         //SerialオブジェクトはUART0 (COM)
  Serial1.begin(115200, SERIAL_8N1, 2, 3);      //Serial1 オブジェクトは ピン指定で、RX = GP2, TX = GP3 で設定する
  
}

void loop()
{
  if(Serial1.available() != 0)          //UART1にデータがあれば、読み取った内容をUART0に送信
  {
      Serial.write(Serial1.read());
  }

  if(Serial.available() != 0)           //UART0にデータがあれば、読み取った内容をUART1に送信
  {
      Serial1.write(Serial.read());
  }
}
結果

結果はTeratermを起動して確認します。
Teratermから送信した文字は、エコーバックにより表示されます。

UART0にUSBのCOMが使用できないので、FT232RLを使って送受信を行います。
橙と紫の配線がUART0, 黄と緑の配線がUART1です。

今回UART1はCOM9、UART0はCOM151と認識されました。
UART1側から青の実線枠の入力を行うと、UART0側の青の点線枠のように入力した文字が出力されました。
UART0側から赤の実線枠の入力を行うと、UART1側の赤の点線枠のように入力した文字が出力されました。

I2C(I2C0 : SSD1306)

説明

I2Cを使ってSSD1306(OLED 0.96inch)モニタのサンプルを動作させます。
ESP32-S2-PicoにはI2Cが2系統あるので、まずI2C0を使ってみます。

使用するサンプルスケッチはAdafruit SSD1306のサンプルを使用しますが、ピン番号を設定するために、Wireオブジェクトのbegin()メソッドに、使用するピン番号を指定します。

サンプルスケッチ62行目
Wire.begin(16, 17, 1000000 );

配線
ESP32-S2-Pico配線SSD1306(0.96inch)
3.3VVCC
GNDGND
GP16SDA
GP17SCL

[2022.11.13追記]
GPIO6, GPIO7, GPIO8 を使用すると意図した通りの描画を行わない現象が確認されたのでピン指定しないでください。

スケッチ

Arduinoサンプルスケッチを掲載します。
ファイル(F) > スケッチ例 > Adafruit SSD1306 > ssd1306_128x64_i2c

変更箇所
使用しているSSD1306 OLEDのI2Cアドレスを変更して使用してください。
35行目 : 当方 0x3cで使用しているため変更しています。

Wireオブジェクトのbegin()メソッドに、使用するピン番号を指定します。
SDAをGP16, SCLをGP17, I2Cクロックを1000000(100kHz)
62行目 : Wire.begin(16, 17, 1000000 );

/**************************************************************************
 This is an example for our Monochrome OLEDs based on SSD1306 drivers

 Pick one up today in the adafruit shop!
 ------> http://www.adafruit.com/category/63_98

 This example is for a 128x64 pixel display using I2C to communicate
 3 pins are required to interface (two I2C and one reset).

 Adafruit invests time and resources providing this open
 source code, please support Adafruit and open-source
 hardware by purchasing products from Adafruit!

 Written by Limor Fried/Ladyada for Adafruit Industries,
 with contributions from the open source community.
 BSD license, check license.txt for more information
 All text above, and the splash screen below must be
 included in any redistribution.
 **************************************************************************/

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library. 
// On an arduino UNO:       A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO:   2(SDA),  3(SCL), ...
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define NUMFLAKES     10 // Number of snowflakes in the animation example

#define LOGO_HEIGHT   16
#define LOGO_WIDTH    16
static const unsigned char PROGMEM logo_bmp[] =
{ 0b00000000, 0b11000000,
  0b00000001, 0b11000000,
  0b00000001, 0b11000000,
  0b00000011, 0b11100000,
  0b11110011, 0b11100000,
  0b11111110, 0b11111000,
  0b01111110, 0b11111111,
  0b00110011, 0b10011111,
  0b00011111, 0b11111100,
  0b00001101, 0b01110000,
  0b00011011, 0b10100000,
  0b00111111, 0b11100000,
  0b00111111, 0b11110000,
  0b01111100, 0b11110000,
  0b01110000, 0b01110000,
  0b00000000, 0b00110000 };

void setup() {
  Serial.begin(115200);
  Wire.begin(16, 17, 1000000 );

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(2000); // Pause for 2 seconds

  // Clear the buffer
  display.clearDisplay();

  // Draw a single pixel in white
  display.drawPixel(10, 10, SSD1306_WHITE);

  // Show the display buffer on the screen. You MUST call display() after
  // drawing commands to make them visible on screen!
  display.display();
  delay(2000);
  // display.display() is NOT necessary after every single drawing command,
  // unless that's what you want...rather, you can batch up a bunch of
  // drawing operations and then update the screen all at once by calling
  // display.display(). These examples demonstrate both approaches...

  testdrawline();      // Draw many lines

  testdrawrect();      // Draw rectangles (outlines)

  testfillrect();      // Draw rectangles (filled)

  testdrawcircle();    // Draw circles (outlines)

  testfillcircle();    // Draw circles (filled)

  testdrawroundrect(); // Draw rounded rectangles (outlines)

  testfillroundrect(); // Draw rounded rectangles (filled)

  testdrawtriangle();  // Draw triangles (outlines)

  testfilltriangle();  // Draw triangles (filled)

  testdrawchar();      // Draw characters of the default font

  testdrawstyles();    // Draw 'stylized' characters

  testscrolltext();    // Draw scrolling text

  testdrawbitmap();    // Draw a small bitmap image

  // Invert and restore display, pausing in-between
  display.invertDisplay(true);
  delay(1000);
  display.invertDisplay(false);
  delay(1000);

  testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps
}

void loop() {
}

void testdrawline() {
  int16_t i;

  display.clearDisplay(); // Clear display buffer

  for(i=0; i<display.width(); i+=4) {
    display.drawLine(0, 0, i, display.height()-1, SSD1306_WHITE);
    display.display(); // Update screen with each newly-drawn line
    delay(1);
  }
  for(i=0; i<display.height(); i+=4) {
    display.drawLine(0, 0, display.width()-1, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=0; i<display.width(); i+=4) {
    display.drawLine(0, display.height()-1, i, 0, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=display.height()-1; i>=0; i-=4) {
    display.drawLine(0, display.height()-1, display.width()-1, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=display.width()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, i, 0, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=display.height()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, 0, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=0; i<display.height(); i+=4) {
    display.drawLine(display.width()-1, 0, 0, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=0; i<display.width(); i+=4) {
    display.drawLine(display.width()-1, 0, i, display.height()-1, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000); // Pause for 2 seconds
}

void testdrawrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2; i+=2) {
    display.drawRect(i, i, display.width()-2*i, display.height()-2*i, SSD1306_WHITE);
    display.display(); // Update screen with each newly-drawn rectangle
    delay(1);
  }

  delay(2000);
}

void testfillrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2; i+=3) {
    // The INVERSE color is used so rectangles alternate white/black
    display.fillRect(i, i, display.width()-i*2, display.height()-i*2, SSD1306_INVERSE);
    display.display(); // Update screen with each newly-drawn rectangle
    delay(1);
  }

  delay(2000);
}

void testdrawcircle(void) {
  display.clearDisplay();

  for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {
    display.drawCircle(display.width()/2, display.height()/2, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testfillcircle(void) {
  display.clearDisplay();

  for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) {
    // The INVERSE color is used so circles alternate white/black
    display.fillCircle(display.width() / 2, display.height() / 2, i, SSD1306_INVERSE);
    display.display(); // Update screen with each newly-drawn circle
    delay(1);
  }

  delay(2000);
}

void testdrawroundrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2-2; i+=2) {
    display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i,
      display.height()/4, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testfillroundrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2-2; i+=2) {
    // The INVERSE color is used so round-rects alternate white/black
    display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i,
      display.height()/4, SSD1306_INVERSE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testdrawtriangle(void) {
  display.clearDisplay();

  for(int16_t i=0; i<max(display.width(),display.height())/2; i+=5) {
    display.drawTriangle(
      display.width()/2  , display.height()/2-i,
      display.width()/2-i, display.height()/2+i,
      display.width()/2+i, display.height()/2+i, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testfilltriangle(void) {
  display.clearDisplay();

  for(int16_t i=max(display.width(),display.height())/2; i>0; i-=5) {
    // The INVERSE color is used so triangles alternate white/black
    display.fillTriangle(
      display.width()/2  , display.height()/2-i,
      display.width()/2-i, display.height()/2+i,
      display.width()/2+i, display.height()/2+i, SSD1306_INVERSE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testdrawchar(void) {
  display.clearDisplay();

  display.setTextSize(1);      // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE); // Draw white text
  display.setCursor(0, 0);     // Start at top-left corner
  display.cp437(true);         // Use full 256 char 'Code Page 437' font

  // Not all the characters will fit on the display. This is normal.
  // Library will draw what it can and the rest will be clipped.
  for(int16_t i=0; i<256; i++) {
    if(i == '\n') display.write(' ');
    else          display.write(i);
  }

  display.display();
  delay(2000);
}

void testdrawstyles(void) {
  display.clearDisplay();

  display.setTextSize(1);             // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE);        // Draw white text
  display.setCursor(0,0);             // Start at top-left corner
  display.println(F("Hello, world!"));

  display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
  display.println(3.141592);

  display.setTextSize(2);             // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.print(F("0x")); display.println(0xDEADBEEF, HEX);

  display.display();
  delay(2000);
}

void testscrolltext(void) {
  display.clearDisplay();

  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 0);
  display.println(F("scroll"));
  display.display();      // Show initial text
  delay(100);

  // Scroll in various directions, pausing in-between:
  display.startscrollright(0x00, 0x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);
  display.startscrollleft(0x00, 0x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);
  display.startscrolldiagright(0x00, 0x07);
  delay(2000);
  display.startscrolldiagleft(0x00, 0x07);
  delay(2000);
  display.stopscroll();
  delay(1000);
}

void testdrawbitmap(void) {
  display.clearDisplay();

  display.drawBitmap(
    (display.width()  - LOGO_WIDTH ) / 2,
    (display.height() - LOGO_HEIGHT) / 2,
    logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
  display.display();
  delay(1000);
}

#define XPOS   0 // Indexes into the 'icons' array in function below
#define YPOS   1
#define DELTAY 2

void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {
  int8_t f, icons[NUMFLAKES][3];

  // Initialize 'snowflake' positions
  for(f=0; f< NUMFLAKES; f++) {
    icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
    icons[f][YPOS]   = -LOGO_HEIGHT;
    icons[f][DELTAY] = random(1, 6);
    Serial.print(F("x: "));
    Serial.print(icons[f][XPOS], DEC);
    Serial.print(F(" y: "));
    Serial.print(icons[f][YPOS], DEC);
    Serial.print(F(" dy: "));
    Serial.println(icons[f][DELTAY], DEC);
  }

  for(;;) { // Loop forever...
    display.clearDisplay(); // Clear the display buffer

    // Draw each snowflake:
    for(f=0; f< NUMFLAKES; f++) {
      display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SSD1306_WHITE);
    }

    display.display(); // Show the display buffer on the screen
    delay(200);        // Pause for 1/10 second

    // Then update coordinates of each flake...
    for(f=0; f< NUMFLAKES; f++) {
      icons[f][YPOS] += icons[f][DELTAY];
      // If snowflake is off the bottom of the screen...
      if (icons[f][YPOS] >= display.height()) {
        // Reinitialize to a random position, just off the top
        icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
        icons[f][YPOS]   = -LOGO_HEIGHT;
        icons[f][DELTAY] = random(1, 6);
      }
    }
  }
}
結果

SSD1306のサンプルスケッチが動作しました。

ピン表ではI2C0で使用できるピンとI2C1で使用できるピンでは区別されていますが、サンプル62行目で設定する方法では、I2C1のピンを設定してもI2Cの割り当てのないピン(例 GP2, GP3)を設定しても動作してしまいます。
おそらくソフトウエアI2Cで動作しているものと思います。

H/W I2Cの設定方法が見つかっていないのでわかり次第更新します。

I2C(I2C0 & I2C1 : SSD1306)

説明

I2C0とI2C1 を使ってデュアルモニタで動作させます。

I2C(I2C0 : SSD1306)で紹介した通り、I2C0、I2C1ともにどのピンでも設定できてしまいますが、それでもピン表に従い、I2C0はGP16(SDA), GP17(SCL)を使用し、I2C1はGP14(SDA), GP15(SCL)を使用することとします。

配線
ESP32-S2-Pico配線I2C0側
SSD1306(0.96inch)
配線I2C1側
SSD1306(1.54inch)
3.3VVCCVCC
GNDGNDGND
GP16(I2C0 SDA)SDA
GP17(I2C0 SCL)SCL
GP14(I2C1 SDA)SDA
GP15(I2C1 SCL)SCL

SSD1306 0.96inchが手元に2個なかったので、SSD1306(1.54inch)を使用しています。
両方とも同じスケッチで動作します。
1.54inch にはReset端子ありますが接続しなくても動作するので配線していません。

[2022.11.13追記]
GPIO6, GPIO7, GPIO8 を使用すると意図した通りの描画を行わない現象が確認されたのでピン指定しないでください。

スケッチ

I2C(I2C0 : SSD1306) で紹介したサンプルスケッチを修正します。
重複箇所が多いので変更点のみを掲載します。

変更箇所
I2C1 を使用するためのWire1を追加。(37行目)

I2C1のピンを設定するための追加。(64行目)

I2C1 SSD1306を開始するための処理追加(72 から75行目)と、表示(80行目)

結果

SSD1306のサンプルスケッチが動作しました。
デュアルモニタの確認だけなので、I2C1側(1.54inch側)はAdafruitのタイトルだけですが、display1オブジェクトを使用することでI2C0側と独立して描画を行うことができます。

SPI(ST7735)

説明

SPIを使ってST7735(LCD 1.8inch)モニタのサンプルを動作させます。
ESP32-S2 picoではSPIを2系統使用できます。
1系統目(VSPI)はデフォルトのピン設定は、GP35(MOSI), GP36(CLK)です。
1系統目(VSPI)、2系統目(HSPI)ともに自由にピン設定ができる。

そのほかのグラフィックのサンプルはArduinoサンプルスケッチを参照してください。
ファイル(F) > スケッチ例 > Adafruit ST7735 and ST7789 Library > graphicstest

配線

デフォルトのH/W FSPI GP35(MOSI), GP36(CLK)を使用する配線。

ESP32-S2-pico配線ST7735(1.8inch)
3.3VVCC
3.3VLED
GNDGND
GP2CS
GP3RESET
GP4AO
GP35(FSPI0 MOSI)SDA
GP36(FSPI0 CLK)SCK

H/W FSPI を GP15(MOSI), GP16(CLK)に変更して使用する配線。

スケッチ
/**********************************************************************
【ライセンスについて】
Copyright(c) 2022 by tamanegi
Released under the MIT license
'http://tamanegi.digick.jp/about-licence/

【マイコン基板】
ESP32-S2-Pico で利用できます。

【スケッチの説明】
ST7735 LCDの表示制御します。
ESP32-PICO-KIT にはH/W SPIが2系統あります。(VSPI, HSPI)
H/W SPI(VSPI, HSPI)を使用するための方法についてサンプル記載します。


【ライブラリ】
esp32 > ESP32S2 Dev Module
Adafruit ST7735 and ST7789 Library
Adafruit GFX Library

【準備】
VSPIを使用する場合
ESP32-S2-Pico <-> ST7735
3V3           <-> VCC
GND           <-> GND
GPIO2(CS)     <-> CS
GPIO3(Reset)  <-> Reset
GPIO4(DC)     <-> AO
GPIO35(MOSI)  <-> SDA
GPIO36(SCK)   <-> SCK

HSPIを使用する場合
自由に設定できるが、例としてVSPIと同じピン番号を示す。
ESP32-S2-Pico <-> ST7735
3V3           <-> VCC
GND           <-> GND
GPIO2(CS)     <-> CS
GPIO3(Reset)  <-> Reset
GPIO4(DC)     <-> AO
GPIO35(MOSI)  <-> SDA
GPIO36(SCK)   <-> SCK

【バージョン情報】
2022/10/8 : 新規
2022/12/11 : hspi 制御追記

**********************************************************************/
#include <Adafruit_GFX.h> 
#include <Adafruit_ST7735.h>
#include <SPI.h>

//CS, RST, DCは任意のピンを指定可
//MOSI(35), SCK(36)はデフォルトで指定しなくても使用可能。
//ピン変更する場合は SPI.begin()を使用する。
//VSPI(SPIオブジェクト), HVSPI(hspiオブジェクト)ともにピン設定は自由
#define TFT_CS    2
#define TFT_RST   3
#define TFT_DC    4
#define TFT_MOSI  35
#define TFT_SCK   36

#define SPI0

#ifdef SPI0
  //デフォルトVSPIを使用する場合
  Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
#else
  //もう一系統のSPIを使用する場合
  SPIClass hspi(HSPI);
  Adafruit_ST7735 tft = Adafruit_ST7735(&hspi, TFT_CS, TFT_DC, TFT_RST);
#endif

//S/W SPIはこちら。遅いが任意のピンを使用できる。
//Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

void setup(void) 
{
//  SPI.begin(TFT_SCK, -1, TFT_MOSI, TFT_CS);       //VSPIピンを変更して使用する場合、この行のコメントを解除
//  hspi.begin(TFT_SCK, -1, TFT_MOSI, TFT_CS);       //HSPIピンを変更して使用する場合、この行のコメントを解除


  tft.initR(INITR_BLACKTAB);                  //Init ST7735S初期化
  
  tft.fillScreen(ST77XX_BLACK);               //背景の塗りつぶし

  //テキスト表示
  tft.setRotation(3);                         //画面回転
  tft.setTextSize(3);                         //サイズ

  tft.setCursor(0, 20);                      //カーソル位置                      
  tft.setTextColor(ST77XX_RED);              //赤
  tft.printf("TAMANEGI\n");

  tft.setTextColor(ST77XX_GREEN);            //緑
  tft.printf("TAMANEGI\n");

  tft.setTextColor(ST77XX_BLUE);             //青
  tft.printf("TAMANEGI\n");

  tft.setTextColor(ST77XX_YELLOW);           //黄
  tft.printf("TAMANEGI\n");
}

void loop()
{
    tft.setTextColor(ST77XX_YELLOW);           //黄
  tft.printf("TAMANEGI\n");

}
結果

ST7735のサンプルスケッチが動作しました。

コメント

タイトルとURLをコピーしました