概要
ESP32で実験を行っていたら、GPIOの理解不能な動作に遭遇した。
原因を調べたところ CPUのバグ(Eratta)であることが判明。
原因がわからないと、かなり厄介な現象なので報告する。
現象
ESP32で8bitパラレル接続のTFT-LCD(コントローラはST7781)に表示を行おうとするのだが、時々おかしな点が描画される。下の写真は、現象を観測しやすくするため、RGBの3色で縦線を3本描画している。上はErattaを回避後の正常動作の写真で、下は回避前の写真。
オシロスコープで信号を観測したところ、WR = Lowでデータ書き込み時に CS=Highになっている場合があることが判明した。
Eratta
問題のerattaは、Espressifの文書 ECO and Workarounds for Bugs in ESP32の「3.3. When the CPU accesses peripherals and writes one address repeatedly, random data loss occurs」。
タイトルからは、わかり難いが、対象に GPIOの主要レジスタが含まれている。GPIO以外は、UARTやI2SのFIFOなど。
対象となるチップのリビジョンは、 2016-09リリースのリビジョン0で、2017-02リリースのリビジョン1では修正されている模様。
回避方法
文書に書かれている回避方法は簡単で、別のアドレスでレジスタにアクセスしてくださいとのこと。
今回関係するレジスタだけ抜き出すと次のようになる。
Registers | Original addr | Changed addr |
---|---|---|
GPIO_OUT_W1TS_REG | 0x3ff44008 | 0x60004008 |
GPIO_OUT_W1TC_REG | 0x3ff4400c | 0x6000400c |
文書にW1TSの方は含まれていなかったが、これは記述抜けではないかと思う。
Original addrからのアクセスとChanged addrからのアクセスではタイミングがずれるようなので、変更するのであれば全部変更する方が好ましい。
esp-idf, Arduinoの状況
esp-idfやArduinoではGPIO操作をGPIOという名前の構造体を使って行っている。
この構造体のアドレスは tools/sdk/ld/esp32.peripherals.ldというファイルに定義されているが、値は0x3ff44000となっていた。これを0x60004000に変更すれば解決かと思われたが現象は変化しなかった。ライブラリの再生成などが必要なのかもしれない。 これを0x60004000に変更することで、回避することができた。 (2017/3/24 訂正)
/* PROVIDE ( GPIO = 0x3ff44000 ); */
PROVIDE ( GPIO = 0x60004000 );
動作確認
以下のように GPIO_OUT_W1TS,GPIO_OUT_W1TCレジスタと、各信号線の操作を定義したところ、プログラムはErrataを回避し、期待通りの動作をした。
#define GPIO_OUT_W1TS ESP_REG(0x60004008)
#define GPIO_OUT_W1TC ESP_REG(0x6000400C)
#define RD_ACTIVE GPIO_OUT_W1TC = _BV(LCD_RD)
#define RD_IDLE GPIO_OUT_W1TS = _BV(LCD_RD)
#define WR_ACTIVE GPIO_OUT_W1TC = _BV(LCD_WR)
#define WR_IDLE GPIO_OUT_W1TS = _BV(LCD_WR)
#define CD_COMMAND GPIO_OUT_W1TC = _BV(LCD_CD)
#define CD_DATA GPIO_OUT_W1TS = _BV(LCD_CD)
最後に
Errataは、一度目を通していたはずなのに、この問題の原因と気が付くまで、かなり時間がかかってしまった。まだまだ修行が足りない。
この文章が誰かのお役に立てれば幸いです。