問題
ESP32でCAN通信を扱う際、Arduino IDE で開発している場合は、arduino-CAN というライブラリを使うことが多いと思います。この arduino-CAN ライブラリ を使って ESP32 でCAN通信を行うと、なぜか通信速度が指定値の半分になる という問題に遭遇することがあります。
問題が発生しているハードウェア
ESP32でCAN通信をする を参考に結線しています。ただし、私の環境では都合によりRX
を32
、TX
を33
としています。
問題が発生しているコード
arduino-CAN のサンプルコード examples/CANSender
そのものなので省略します。
原因
以下のページに、この問題に関する議論がありました。
https://github.com/sandeepmistry/arduino-CAN/issues/62
チップバージョンがRev2以上のESP32では、CAN通信に関係する、とあるレジスタビットの役割が「通信速度を半分にする」という意味に変更されたそうです。変更前の仕様に従って書かれている arduino-CAN ライブラリは、このビット値を1
にセットしてしまうため、通信速度がCAN.begin(baudrate)
で指定したbaudrate
の半分になってしまうのです。
解決策
単純に、送りたい速度の2倍を指定してbegin
すれば、送ることはできます。
しかし、1 Mbps で通信したい場合や、根本的な解決を求める場合、setup()
内で CAN.begin(baudrate)
の後に以下のように追記します。
// start the CAN bus at 500 kbps
if (!CAN.begin(500E3)) {
Serial.println("Starting CAN failed!");
while (1);
}
// 以下2行を追記
volatile uint32_t* pREG_IER = (volatile uint32_t*)0x3ff6b010;
*pREG_IER &= ~(uint8_t)0x10;
(詳しい人向け)
CAN割込み関連のレジスタ REG_IER
(ESP32 の公式マニュアルでいう TWAI_INT_ENA_REG
0x3ff6b010
) の bit 4 の役割が、"Wake-up interrupt" から "divide BRP by 2" に変更されたそうです。CAN.begin(baudrate)
のソースコードでこのレジスタに0xff
を書き込んでいるため、当該bitが1
となり、速度が半分になります。そこで、begin
した後に当該ビットに0を書き込んでいます。
おわりに
複数のプロジェクトで立て続けにこの問題に遭遇し頭を抱えておりました。しかも、ESP32 Technical Reference Manual にもこの仕様変更が反映されていない(執筆時点での最新版v4.5, 2021/7/21)ため原因特定に時間がかかりました。かなりニッチな話題ですが、1度ハマると迷宮入りしそうなポイントに見えます。