ESP32 の SPI_MODE が修正。HSPI , VSPI , 複数SPIデバイス制御 , SPI高速化などについて

記事公開日:2017年5月10日


スポンサーリンク

修正された SPI_MODE の動作確認

では、最新の Arduino core for the ESP32 の SPI_MODE が修正されているか確認してみます。

以下のスケッチを入力して、コンパイル書き込みしてみてください。
10100110
というビットを送信した場合です。
これは SPI_MODE3 にしています。

/*
 * VSPI
 * SCLK #18
 * MOSI #23
 * MISO #19
 * CS(SS) #5
 */
#include <SPI.h>

byte b=0b10100110;

void setup() {
  SPI.begin();
  SPI.setFrequency(7000000); //SSD1331 のSPI Clock Cycle Time 最低150ns
  SPI.setDataMode(SPI_MODE3);
}

void loop() {
  SPI.write(b);
}

SCLK と MOSI ( Master Output Slave Input ) 信号をオシロスコープで確認してみるとこうなりました。

hspi_vspi_02.jpg


ちゃんと、本来の SPI MODE 3 に直っていましたね。
念のため、SPI_MODE2 にしてみます。

hspi_vspi_03.jpg


これもちゃんと SPI MODE 2 に修正されていますね。

これで、過去のソースコードを全て修正しなければならなくなりました。
ただ、誤ったMODE でもあまり違和感なく普通に動いているのが悩ましいところです。
電子工作系ブログ記事の場合、こういう修正がエライ大変ですね。
これからブログを立ち上げる方は電子工作系はやめておいた方が良いですよ~・・・。

VSPI や HSPI とは

そもそも、VSPI と HSPI とは何ぞや? ということですが、いろいろ調べてみて何となく分かったことがあります。
ESP32 ( ESP-WROOM-32 )には、Dual SPI や Quad SPI デバイスを動かすために、SPI インターフェースを4つ装備しているようです。

Quad SPI は4つの SPI を同時通信して、外部デバイスのSRAM や Flash を高速で読み書きするためのものだそうです。
ESP-WROOM-32 のデータシートをよ~く見てみると、Parallel QSPI ( Quad SPI ) と書いてあるところがそれです。

その Quad SPI 端子のうち、SHD/SD2 とか、SDO/SD0 のような SD カードっぽい GPIO 名称があります。
ESP32 – DevKitC では、下図の様な赤色囲いのところです。

hspi_vspi_10.jpg


GitHub の以下のページの下の方にもピンアサインがありますのでそれも参照してみてください。

https://github.com/espressif/arduino-esp32

これから、赤色のところの SDカードインターフェースは、HS ( Hi-Speed ) 通信用端子であることが分かりました。
つまり、以前の記事で説明した SDアソシエーション という団体にライセンス料を払って開発する高速通信モード用端子のようです。
この端子も通常のSPIモードで使えるかも知れませんが、まだ試していません。

ということで、現時点で Arduino core for the ESP32 で使える SPI 端子は、 VSPI と HSPI 端子ということのようです。

VSPI の ‘ V ‘  や HSPI の ‘ H ‘ の意味は良く分かりません。
Twitter で Vertical , Horizontal ではないかという意見もありました。
でも、機能的には通常の SPI と何ら変わりありませんでした。
Hがついているからといって、Hi-Speed ではありませんよ・・・。

HSPI で動かす

OLED SSD1331 を VSPI で動かす方法は以前の記事で説明しました。

では、これを HSPI で動かす場合を説明します。
HSPI 端子は以下のようになります。

GPIO #14  —– SCLK ( SPI Clock )
GPIO #13  —– MOSI ( Master Output Slave Input )
GPIO #12  —– MISO ( Master Input Slave Output )
GPIO #15  —– CS ( Chip Select )

まず、ESP32 – DevKitC に何も接続せずに、以下のスケッチを入力してみてください。

/*
 * HSPI
 * SCLK #14
 * MOSI #13
 * MISO #12
 * CS(SS) #15
 */
#include <SPI.h>

byte b=0b10100110;

SPIClass hspi(HSPI);

void setup() {
  pinMode(15, OUTPUT);
  digitalWrite(15, HIGH);
  
  hspi.begin(14, 12, 13, 15);
  hspi.setFrequency(7000000); //SSD1331 のSPI Clock Cycle Time 最低150ns
  hspi.setDataMode(SPI_MODE3);
}

void loop() {
  digitalWrite(15, LOW);
  hspi.write(b);
  digitalWrite(15, HIGH);
}

これで、HSPI 端子から信号が出力されていると思いますので、オシロスコープやロジックアナライザーで確認してみてください。

このスケッチでは、CS ( Chip Select )ピンを digitalWrite で HIGH-LOW を切り替えていますが、実はこれを使わなくても良い方法があります。
それは、 setHwCs 関数を使うことです。
以下のスケッチを入力してみてください。

/*
 * HSPI
 * SCLK #14
 * MOSI #13
 * MISO #12
 * CS(SS) #15
 */
#include <SPI.h>

byte b=0b10100110;

SPIClass hspi(HSPI);

void setup() {
  hspi.begin(14, 12, 13, 15);
  hspi.setFrequency(7000000); //SSD1331 のSPI Clock Cycle Time 最低150ns
  hspi.setDataMode(SPI_MODE3);
  hspi.setHwCs(true);
}

void loop() {
  hspi.write(b);
}

setHwCs 関数は、ハードウェアCSピンを使うか使わないか選択できる関数です。
begin関数で指定したCSピンがハードウェアCSピンです。

Arduino IDE 系の digitalWrite 関数は速度が遅いということで有名ですよね。
ロジックアナライザーを使ってdigitalWriteを使った様子を見てみたいと思います。
因みに、私のロジックアナライザーは ZEROPLUS LAP-C を使っています。

Amazon.co.jp

これはロジアナとしては安いのに 100MHz という優れものです。

では、CSピンを digitalWrite で High – Low 切り替えた場合の波形はこんな感じです。

hspi_vspi_12.jpg


ちょっと見にくくて申し訳ないのですが、SCK や MOSI 信号発信が無いところでも、しばらくCSピンがLOWになったままですね。

では、setHwCs 関数を使うとこうなります。

hspi_vspi_13.jpg


見事に SCK や MOSI 信号が発信されたところだけピンポイントで CS が LOW になっていますね。
ただ、これを使ったからと言って、バイト毎の送信間隔はあまり変わらないので、高速化は望めないようです。
でも、CS ( Chip Select )ピンをdigitalWriteで High – Low に切り替えなくて済むので、プログラムを節約できることが、この関数を使う最大の利点です。
これは有り難いですね。

では、実際に フルカラー OLED ( 有機EL ) SSD1331 を HSPI 接続して動作させてみましょう。

接続はこんな感じです。

hspi_vspi_11.jpg


VSPI 接続の以前の記事の、ブルー画面スケッチを HSPI 接続用に変更したものが以下のスケッチになります。

/*
 * HSPI
 * SCLK #14
 * MOSI #13
 * MISO #12
 * CS(SS) #15
 */
#include <SPI.h>

const uint8_t SCLK_OLED =  14; //SCLK (SPI Clock)
const uint8_t MOSI_OLED =  13; //MOSI (Master Output Slave Input)
const uint8_t MISO_OLED =  12; //SSD1331では実際は使わない。MISO (Master Input Slave Output)
const uint8_t CS_OLED = 15; //OLED ChipSelect
const uint8_t DC_OLED =  16; //OLED DC (Data/Command)
const uint8_t RST_OLED =  4; //OLED Reset

SPIClass hspi(HSPI);
 
void setup() {
  delay(1000);
  int i, j;
  uint8_t R, G, B, Dot1, Dot2;
   
  SSD1331_Init(SCLK_OLED, MISO_OLED, MOSI_OLED, CS_OLED, DC_OLED, RST_OLED);
 
  R = 7; G = 0; B = 0; //256 color : R (0-7), G (0-7), B (0-3) 
  Dot1 = (R << 5) | (G << 2) | B;
   
  R = 0; G = 0; B = 3; //256 color : R (0-7), G (0-7), B (0-3) 
  Dot2 = (R << 5) | (G << 2) | B;
   
  for(j=0; j<64; j++){
    for(i=0; i<96; i++){
      if(j<8 && i<16) {
        DataWrite(Dot1);
      }else{
        DataWrite(Dot2);
      }
    }
  }
}
 
void loop() {
   
}
 
//************ SSD1331 初期化 ****************************************
void SSD1331_Init(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t cs, uint8_t dc, uint8_t rst){  
  pinMode(rst, OUTPUT); //RES
  pinMode(dc, OUTPUT); //DC

  digitalWrite(rst, HIGH);
  digitalWrite(rst, LOW);
  delay(1);
  digitalWrite(rst, HIGH);

  digitalWrite(dc, HIGH);

  hspi.begin(sck, miso, mosi, cs);
  hspi.setFrequency(7000000); //SSD1331 のSPI Clock Cycle Time 最低150ns
  hspi.setDataMode(SPI_MODE3);
  hspi.setHwCs(true);

  CommandWrite(0xAE); //Set Display Off
  CommandWrite(0xA0); //Remap & Color Depth setting 
    CommandWrite(0b00110010); //A[7:6] = 00; 256 color. A[7:6] = 01; 65k color format
  CommandWrite(0xA1); //Set Display Start Line
    CommandWrite(0);
  CommandWrite(0xA2); //Set Display Offset
    CommandWrite(0);
  CommandWrite(0xA4); //Set Display Mode (Normal)
  CommandWrite(0xA8); //Set Multiplex Ratio
    CommandWrite(0b00111111); //15-63
  CommandWrite(0xAD); //Set Master Configration
    CommandWrite(0b10001110); //a[0]=0 Select external Vcc supply, a[0]=1 Reserved(reset)
  CommandWrite(0xB0); //Power Save Mode
    CommandWrite(0b00000000); //0x1A Enable power save mode
  CommandWrite(0xB1); //Phase 1 and 2 period adjustment
    CommandWrite(0x74);
  CommandWrite(0xB3); //Display Clock DIV
    CommandWrite(0xF0);
  CommandWrite(0x8A); //Pre Charge A
    CommandWrite(0x81);
  CommandWrite(0x8B); //Pre Charge B
    CommandWrite(0x82);
  CommandWrite(0x8C); //Pre Charge C
    CommandWrite(0x83);
  CommandWrite(0xBB); //Set Pre-charge level
    CommandWrite(0x3A);
  CommandWrite(0xBE); //Set VcomH
    CommandWrite(0x3E);
  CommandWrite(0x87); //Set Master Current Control
    CommandWrite(0x06);
  CommandWrite(0x15); //Set Column Address
    CommandWrite(0);
    CommandWrite(95);
  CommandWrite(0x75); //Set Row Address
    CommandWrite(0);
    CommandWrite(63);
  CommandWrite(0x81); //Set Contrast for Color A
    CommandWrite(255);
  CommandWrite(0x82); //Set Contrast for Color B
    CommandWrite(255);
  CommandWrite(0x83); //Set Contrast for Color C
    CommandWrite(255);

  for(int j=0; j<64; j++){ //Display Black OUT
    for(int i=0; i<96; i++){
      DataWrite(0x00);
      DataWrite(0x00);
      DataWrite(0x00);
    }
  }
  
  CommandWrite(0xAF); //Set Display On
  delay(110); //ディスプレイONコマンドの後は最低100ms 必要
}
//********** SPI コマンド出力 **************************
void CommandWrite(uint8_t b){
  digitalWrite(DC_OLED, LOW);
  hspi.write(b);
}
//********** SPI データ出力 ****************************
void DataWrite(uint8_t b){
  digitalWrite(DC_OLED, HIGH);
  hspi.write(b);
}

SSD1331 の場合は、データやコマンドを送る前に CS ( Chip Select )ピンを Low にして、DCピンを以下のようにします。

コマンド送信  DC — LOW
データ送信    DC — HIGH

今回は HSPI と VSPI に分けて、setHwCs 関数を使ったので、CSピンを切り替える必要は無く、digitalWrite を使うのは DC ( Data/Command )ピンだけで、とてもシンプルになりました。

では、これをコンパイル実行させてみてください。
こんな感じで表示できていればOKです。

hspi_vspi_18.jpg



スポンサーリンク


コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください