Analog3 システムでは、モジュールの起動時にミッションコントロールとのハンドシェイクを行うので、立ち上がり時にメッセージが輻輳しがちです。特にミッションコントロールは後から起動した場合には ID 割り当てのメッセージを立て続けに送ります。これを STM32 FDCAN を使って実装されたモジュールは取りこぼしてしまい自分の ID がわからなくなる問題がありました。
問題の切り分けは少々面倒で、CAN モニタにもミッションコントロールから送られたメッセージが現れないのでミッションコントロールが「送った」と言いつつ送ってないのか受け側が取りこぼしているのか最初わかりませんでした。これは CAN トランシーバの一つにロジアナを取り付けて判明。ミッションコントロールはメッセージを送っているが STM32 が受け取れないようです。CAN モニタにも STM32 を使っているのでその時点で STM32 側を疑ってはいました。
ということで、デバッグは CAN 受信機能しかない CAN モニタがやりやすいのでそこで調べました。モニタから見たメッセージ取りこぼしの様子は以下のような感じです。
std[ 07 00 ]: 01
ext[ 18 51 f4 2d ]: 01
ext[ 02 02 01 a3 ]: 01
ext[ 1a 4c 5a 00 ]: 01
PlaintextID 0700 はミッションコントロールからのメッセージです。メッセージの意味するところは Analog3 の説明になってしまうので省きますが、ミッションコントロールはこの後 3 メッセージ送信しています。が、モニタはそれを取りこぼしました。
これは、RX FIFO のサイズが小さすぎるのじゃないかな?とあたりをつけて、コードを調べてみると FIFO サイズの設定など特に見当たらない。じゃあデフォルトはどうなってるの?と、使っているデバイス STM32C0 (STM32C092KB) のリファレンスマニュアルを当たってみたら 28.3.6 節に書いてありました。

受信に使っているのは Rx FIFO0 だけです。3メッセージ分の FIFO サイズ。これは臭いです。実際にどんな設定になっているか確認してみましょう。HAL のコードをのぞいてみたらハンドルから RAM 設定が取得できるようです。以下のようなコードでシリアルに出力してみました。
put_string("\r\nRAM configuration:\r\n");
{
char message[128];
FDCAN_MsgRamAddressTypeDef *msg_ram = &hfdcan1.msgRam;
uint32_t address;
uint16_t words;
address = msg_ram->StandardFilterSA;
words = (msg_ram->ExtendedFilterSA - address) / 4;
snprintf(message, sizeof(message), " 11-bit filter : 0x%08lx (%d)\r\n", address, words);
put_string(message);
address = msg_ram->ExtendedFilterSA;
words = (msg_ram->RxFIFO0SA - address) / 4;
snprintf(message, sizeof(message), " 29-bit filter : 0x%08lx (%d)\r\n", address, words);
put_string(message);
address = msg_ram->RxFIFO0SA;
words = (msg_ram->RxFIFO1SA - address) / 4;
snprintf(message, sizeof(message), " Rx FIFO 0 : 0x%08lx (%d)\r\n", address, words);
put_string(message);
address = msg_ram->RxFIFO1SA;
words = (msg_ram->TxEventFIFOSA - address) / 4;
snprintf(message, sizeof(message), " Rx FIFO 1 : 0x%08lx (%d)\r\n", address, words);
put_string(message);
address = msg_ram->TxEventFIFOSA;
words = (msg_ram->TxFIFOQSA - address) / 4;
snprintf(message, sizeof(message), " Tx event FIFO : 0x%08lx (%d)\r\n", address, words);
put_string(message);
address = msg_ram->TxFIFOQSA;
words = (msg_ram->StandardFilterSA + 212 * 4 - address) / 4;
snprintf(message, sizeof(message), " Tx buffers : 0x%08lx (%d)\r\n", address, words);
put_string(message);
}
C結果は以下の通りリファレンスマニュアル通りです。
RAM configuration:
11-bit filter : 0x4000b400 (28)
29-bit filter : 0x4000b470 (16)
Rx FIFO 0 : 0x4000b4b0 (54)
Rx FIFO 1 : 0x4000b588 (54)
Tx event FIFO : 0x4000b660 (6)
Tx buffers : 0x4000b678 (54)
Plaintextこれはメッセージ処理が追い付かず FIFO がいっぱいになった可能性が大きいです。では FIFO のサイズを増やすにはどうしたら良いでしょうか?リファレンスマニュアルを見ても FIFO サイズを設定できそうなレジスタが見つかりません。HAL の実装を見ても、FIFO サイズは #define 文で決め打ちしてあります。これはなんと、STM32C0 シリーズの FDCAN では FIFO サイズが変えられないということのようです。具体的には STM32C092KBT を使ってます。
これはやられた。作ってしまったモジュールもあるので当面はミッションコントロール側に救済コードを入れて逃げますが今後作るモジュール用には使えません。デバイスを変える必要があります。STM32、安いには安いんですが気を付けないと落とし穴だらけだなあ。