電子ペーパー を ESP32 と Arduino開発環境で使う その3


「電子ペーパー を ESP32 と Arduino開発環境で使う」
https://qiita.com/nanbuwks/items/14257cf5f9edd192b8ec
「電子ペーパー を ESP32 と Arduino開発環境で使う その2」
https://qiita.com/nanbuwks/items/266bf61726b005d8e221

ではそれぞれGxEPD 、 GxEPD2 ライブラリを使いましたが、今回はライブラリを使わない方法を試してみます。

# 今回の実行結果

フォント情報などを使わないデモのため、代わりにチェッカー模様を表示しています。

![IMG_20200909_220632109 (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/151c3400-12da-a73f-9b90-0a17aba163f1.jpeg)

# 目的

複雑な e-Paper の制御を e-Paper へのコマンドレベルで理解するため、e-Paper 外部ライブラリにお任せではなく SPI を直接制御して動かしてみます。
また、図形描画や文字描画などのためのグラフィックバッファーの操作については省略し、単に黒と白を描画するために最低限必要な＜動く＞コードを作成します。

# 環境

今までの実験と同様です

- WaveShareの電子ペーパーモジュール。
https://www.waveshare.com/wiki/2.9inch_e-Paper_Module_(B)
- Arduino 1.8.10
- ESP32

ESP32 モジュール は WEMOS LOLIN32 を使いました。
「WEMOS LOLIN32 の調査」
https://qiita.com/nanbuwks/items/111ee8cd69f3390d866f

# 配線

今までの実験と同様です

![IMG_20200824_071232602.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/0dbdd676-2e5f-4225-e305-bf7401a02598.jpeg)
![IMG_20200824_071422605.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/14de0924-ada6-e1d8-0557-dd080630de18.jpeg)

|ESP32 PIN|ESP32 信号名|WAVESHARE e-Paper 信号名|ケーブル色|
|:---:|:---:|:---:|:---:|
|4 |4 |BUSY|紫| 
|16 |16 |RST|白| 
|17 |17 |DC|緑|
|5 | SS/5/LED|CS |橙| 
|18 |SCK/18| CLK |黄| 
|23 |MOSI | DIN |青| 
|GND |GND|GND |黒| 
|3V3 |3.3V |VCC |赤| 
|19 |MISO/19|接続せず|接続せず|


# 元となったコード

waveshare の e-Paper レポジトリがあります。
このうちの、「epd2in9d」を元に作成しました。
https://github.com/waveshare/e-Paper/tree/master/Arduino/epd2in9d
このレポジトリはMITライセンスぽいです。
https://github.com/waveshare/e-Paper/issues/23

なお、今回使うのは2.9inch_e-Paper_Module_(B)なので本来は epd2in9bc を使うはずなのですが、いろんな経緯からこちらを使用しています。
「epd2in9d」はこれ用かな？ (あまり調査していません)
https://www.waveshare.net/wiki/2.9inch_e-Paper_HAT_(D)






# コード

利用しているのはSPI.hのみインクルードしています。
あとは以下の1ファイルのみで動作します。




```

#include <SPI.h>

#define RST_PIN         16
#define DC_PIN          17
#define CS_PIN          5
#define BUSY_PIN        4

#define EPD_WIDTH       128
#define EPD_HEIGHT      296

const unsigned char EPD_2IN9D_lut_vcomDC[] = {
    0x00,0x08,0x00,0x00,0x00,0x02,0x60,0x28,0x28,0x00,0x00,0x01,0x00,0x14,0x00,0x00,0x00,0x01,0x00,0x12,
    0x12,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,
};
const unsigned char EPD_2IN9D_lut_ww[] = {
    0x40,0x08,0x00,0x00,0x00,0x02,0x90,0x28,0x28,0x00,0x00,0x01,0x40,0x14,0x00,0x00,0x00,0x01,0xA0,0x12,
    0x12,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,
};
const unsigned char EPD_2IN9D_lut_bw[] = {
    0x40,0x17,0x00,0x00,0x00,0x02,0x90,0x0F,0x0F,0x00,0x00,0x03,0x40,0x0A,0x01,0x00,0x00,0x01,0xA0,0x0E,
    0x0E,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,
};
const unsigned char EPD_2IN9D_lut_wb[] = {
    0x80,0x08,0x00,0x00,0x00,0x02,0x90,0x28,0x28,0x00,0x00,0x01,0x80,0x14,0x00,0x00,0x00,0x01,0x50,0x12,
    0x12,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,
};
const unsigned char EPD_2IN9D_lut_bb[] = {
    0x80,0x08,0x00,0x00,0x00,0x02,0x90,0x28,0x28,0x00,0x00,0x01,0x80,0x14,0x00,0x00,0x00,0x01,0x50,0x12,
    0x12,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,
};

void SendCommand(unsigned char command)
{
    digitalWrite(DC_PIN, LOW);
    SPI.transfer(command);
}
void SendData(unsigned char data)
{
    digitalWrite(DC_PIN, HIGH);
    SPI.transfer(data);
}
void WaitUntilIdle(void)
{
    char busy;
    do {
        SendCommand(0x71);
        busy = digitalRead(BUSY_PIN);
        busy =!(busy & 0x01);
    } while(busy);
    delay(200);
}
void SetFullReg(void)
{
    SendCommand(0X50);      //VCOM AND DATA INTERVAL SETTING
    SendData(0xb7);   //WBmode:VBDF 17|D7 VBDW 97 VBDB 57   WBRmode:VBDF F7 VBDW 77 VBDB 37  VBDR B7

    unsigned int count;
    SendCommand(0x20);
    for(count=0; count<44; count++) {
        SendData(EPD_2IN9D_lut_vcomDC[count]);
    }

    SendCommand(0x21);
    for(count=0; count<42; count++) {
        SendData(EPD_2IN9D_lut_ww[count]);
    }

    SendCommand(0x22);
    for(count=0; count<42; count++) {
        SendData(EPD_2IN9D_lut_bw[count]);
    }

    SendCommand(0x23);
    for(count=0; count<42; count++) {
        SendData(EPD_2IN9D_lut_wb[count]);
    }

    SendCommand(0x24);
    for(count=0; count<42; count++) {
        SendData(EPD_2IN9D_lut_bb[count]);
    }
}
void TurnOnDisplay(void)
{
    SendCommand(0x12);     //DISPLAY REFRESH
    delay(10);     //!!!The delay here is necessary, 200uS at least!!!
    WaitUntilIdle();
}
void setup()
{
    // put your setup code here, to run once:
    Serial.begin(115200);
    pinMode(CS_PIN, OUTPUT);
    pinMode(RST_PIN, OUTPUT);
    pinMode(DC_PIN, OUTPUT);
    pinMode(BUSY_PIN, INPUT); 
    SPI.begin();
    SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));

    digitalWrite(RST_PIN, HIGH);
    delay(200);
    digitalWrite(RST_PIN, LOW);    // module reset
    delay(10);
    digitalWrite(RST_PIN, HIGH);
    delay(200);
    digitalWrite(CS_PIN, LOW);
    
    //POWER SETTING
    SendCommand(0x01);
    SendData(0x03);
    SendData(0x00);
    SendData(0x2b);
    SendData(0x2b);
    SendData(0x03);

    SendCommand(0x06);  //boost soft start
    SendData(0x17);     //A
    SendData(0x17);     //B
    SendData(0x17);     //C

    SendCommand(0x04);
    WaitUntilIdle();

    SendCommand(0x00);  //panel setting
    SendData(0xbf);     //LUT from OTP，128x296
    SendData(0x0e);     //VCOM to 0V fast

    SendCommand(0x30);  //PLL setting
    SendData(0x3a);     // 3a 100HZ   29 150Hz 39 200HZ 31 171HZ

    SendCommand(0x61);  //resolution setting
    SendData(EPD_WIDTH);
    SendData((EPD_HEIGHT >> 8) & 0xff);
    SendData(EPD_HEIGHT & 0xff);

    SendCommand(0x82);  //vcom_DC setting
    SendData(0x28);

    Serial.println("e-Paper initialized.");
    int w, h;
    w = (EPD_WIDTH % 8 == 0)? (EPD_WIDTH / 8 ): (EPD_WIDTH / 8 + 1);
    h = EPD_HEIGHT;
    SendCommand(0x10);
    for (int j = 0; j < h; j++) {
        for (int i = 0; i < w; i++) {
            SendData(0x00);
        }
    }
//    SendCommand(0x13);
//    for (int j = 0; j < h; j++) {
//        for (int i = 0; i < w; i++) {
//            SendData(0xFF);
//        }
//    }
    SendCommand(0x13);
    for (int j = 0; j < h; j++) {
        for (int i = 0; i < w; i++) {
            if ( 0 == ( j / 8 ) % 2 ){
               SendData(0x00);
               SendData(0xFF);
            } else {
               SendData(0xFF);
               SendData(0x00);
            }
        }
        j++;
    }
    Serial.println("e-Paper cleared");
    SetFullReg();
    TurnOnDisplay();
 
    SendCommand(0X50);
    SendData(0xf7);
    SendCommand(0X02);    //power off
    WaitUntilIdle();
    SendCommand(0X07);    //deep sleep
    SendData(0xA5);

    digitalWrite(RST_PIN, LOW);
}

void loop()
{

}
```


# ハイライト

何点かに絞ってコードを読み込んでいきます。

## 該当初期化ルーチン

waveshare のデータシートから。左は2.9inch e-Paper Module (B)のもの。右は2.9inch e-Paper HAT (D)のもの。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/cf23c608-97e6-25b1-a5a1-bdadc4aae040.png)

右側のタイトルが「GDEW029I6F V1.1 Specification」となってますね。
GDEW029I6F は GooDisplay の  2.9 inch flexible 4 grayscale e ink display module e paper display
で、
http://www.e-paper-display.com/products_detail/productId=411.html
から IC Driver のリンクを辿ると
IL0373 だそうです。


## SPIの設定

Waveshare のインターフェースボードにBSジャンパーがある。

![IMG_20200910_202613539.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/6b54af38-edb3-ae24-7652-f9e4c3e3c6b7.jpeg)


デフォルトでは 4-line SPI に設定されている。
4-line SPI とは、データシート
http://www.good-display.com/public/html/pdfjs/viewer/viewernew.html?file=https://v4.cecdn.yun300.cn/100001_1909185148/GDEH029Z13.pdf
を読むと DC 信号線を使ってCommandとDataのバイト境界、ないしCommandとデータリードの境界を示す方法。

3-line SPI は、SPIでDC信号線の代わりに
Command Startbit として 0, 
Data Startbit ないしデータリードの Startbit として 1 となる DC bit を付加する方法らしい。

したがって、 Command/Data送出時において 4-line SPI は 8bit データ単位、3-line SPI は 9bit データ単位となることになる。

コードは 4-line SPI なので例えばコマンド送出するときは以下のようにしてDCラインをLOWにして送出している

```

void SendCommand(unsigned char command)
{
    digitalWrite(DC_PIN, LOW);
    SPI.transfer(command);
}
```

## BUSY チェック

初期化フロー中に BUSY ピンのチェックがあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/9e5bb6f1-f9de-71bb-5804-9768fea2e262.png)

該当コードはこれです

```

void WaitUntilIdle(void)
{
    char busy;
    do {
        SendCommand(0x71);
        busy = digitalRead(BUSY_PIN);
        busy =!(busy & 0x01);
    } while(busy);
    delay(200);
}
```