コンテンツにスキップ

CAN・IMU・OLEDの取得と診断

2026-10-08のユーザー指定により、CANにはOrion_CM4相当のダミー、IMUには旧Orionと同じ機器、I2CにはSH1106の128x64 OLEDが接続されているものとして実装した。旧参照ソースはC:/Users/hiroyuki/STM32CubeIDE/workspace_1.17.0/G474_Orion_main/Core/Src/can_ibis.cとicm20602_spi.c。実装と実機検証結果を区別し、結果は開発手順へ記録する。

CAN受信

IDの用途・バイト配置・型・単位の正本はOrion CAN契約。以下は本FWのデコーダー対応範囲と値の保持方法を示す。4WSの接続先・輪番号・操舵軸への割当は共通の未決事項として管理する。

Protocol/orion_telemetryがHALに依存せず共通契約の配置を復号し、Device/orion_canがバス別・ID別の最新値、受信時刻、受信回数を保持する。app_process()が電源受信処理と並行して渡す。既知IDは8 byte限定で、NaN/Infは拒否。未知IDも転送層では受信するが、この解析層には格納しない。

対応ID デコーダーの分類
0x000–001 ORION_ERROR
0x200–204 ORION_ENCODER
0x210–216 ORION_VOLTAGE(負値も拒否)
0x220–223 ORION_MOTOR_TEMP
0x224 ORION_POWER_TEMP
0x230–234 ORION_CURRENT
0x240 ORION_BALL
0x241 ORION_MOUSE
0x500–503 ORION_PARAMETER

旧コードの機体補正であるエンコーダー角度の符号反転は行わず、パケットの値を維持する。4WSの輪・基板へのID割当は別途確定する。orion_can_read(bus, id, now, max_age_ms, &sample)は最新値が指定年齢以内ならtrue。falseの場合も、有効な引数ならreceivedで未受信と古い値を区別できる。受信状態はmain専用で、制御IRQへ直接渡していない。タイムアウト時の機体停止判断は今後実装する。アクチュエーター指令は自動送信しない。

ICM-20602

Board/board_imu_spiがSPI1(PA⅚/7)とPA4のSPI_IMU_CSを使用する。旧OrionのSPI1 Mode 0・CS Low選択を採用し、WHO_AM_Iの0x12を確認してから設定する。旧コードのコメントにある0x68は期待値として使用しない。TDKのデータシートを参照。

  • imu_init()は状態を初期化し、imu_process()がmainで識別、リセット、100 ms待機、設定と読戻し、100 ms待機、取得を進める。
  • 加速度±2 g、角速度±1000 dps。DLPF設定1、SMPLRT_DIV=1でセンサーの指定出力周期は500 Hz。mainでは2 ms以上の間隔でdata-readyを確認し、0x3Bから14 byteを一括取得する。
  • 加速度はm/s²、角速度はdeg/s、温度はraw / 326.8 + 25 °C。センサー座標系の値で、軸の機体対応、校正、フィルター、姿勢積分は含めない。
  • 最終取得から100 ms超はimu_get_status()のvalidをfalseにする。識別・転送・設定読戻し失敗は無効化し、1秒後に再試行。data-readyが1秒超停止した場合も再初期化する。
  • SPI転送は2 msの有限タイムアウト、main専用。main遅延時は最新値を取得し、FIFOによる全500サンプルの保存は行わない。実際の取得頻度はOLED・CAN負荷に依存する。
  • 現行FSYNCは入力として維持し、旧ソースのGPIO出力操作は移植しない。IMUのINT端子やFSYNCをIRQ入力として使用しない。

.iocと生成SPI1初期値の16分周(10.625 MHz)は変更せず、Boardが動作開始時に32分周(5.3125 MHz)へ再設定する。ICM-20602のSPI上限10 MHzを超えないための実行時設定である。生成コードは変更していない。CubeMX再生成後もApplication/、ルートCMakeの登録、USER CODE内のApp呼出しを保持すれば適用される。

SH1106表示

Board/board_oled_i2cがI2C1(PB7 SDA / PA15 SCL)を使用。SCLはユーザー提示の回路図・基板のJ18-3に合わせてPA15へ修正した。7 bitアドレス0x3C、0x3Dの順でACKを確認し、応答したアドレスにSH1106初期化を行う。ACKだけではコントローラー型番は識別できないため、SH1106であることはユーザー指定を前提とする。SH1106データシートに従い、ページ位置指定・内部DC-DC設定を使用する。

  • 128x64、8ページ、列オフセット2、5x7英大文字・数字・記号による8行×21文字の診断表示。列オフセットと向きはモジュール依存で、実表示で確認する。
  • oled_begin()で画面を消去、oled_text(row, column, text)で描画、oled_commit()で送信開始。送信中は次のbeginを拒否し、バッファを固定する。
  • oled_process()は1回につきページ位置指定または8 byteのデータ転送を1回だけ行う。1画面は位置指定8回+データ128回+表示ON1回。転送の間にIMU取得とCAN回収を挟む。
  • 最初の画面送信完了まで表示OFF。2026-10-08の追加指定により、外部プルアップ未実装の基板を内部プルアップで疎通確認する。BoardがPB7/PA15をAF4・open-drain・内部プルアップへ設定し、2026-10-10の段階試験後はI2C1のTIMINGRを0x60400D28(PCLK1=170 MHz専用、400 kHz設定)へ変更する。転送タイムアウト25 ms、ACK確認3 ms。IRQからの転送を拒否する。
  • 失敗時はready/busyを解除してエラー計数、Appが1秒ごとに再検出する。非接続でもCAN・制御入口・UARTの起動を継続する。

Appは約100 msごとに画面生成を試みる。送信中は更新を省略するため、実際の更新頻度はI2C速度とmain負荷に依存する。表示はCAN受信数、IMU識別と有効性、加速度Z・角速度Zの生値、DIP 0123・追加SW 012・5方向BTN LTRBCの1/0状態。スイッチ表示の順序と意味はハードウェアを参照。温度・Orion解析受理数・ADC生値は診断RAMに保持し、ADC生値はUART4でも確認できる。

内部プルアップは疎通確認用で、波形・立上り時間・通常運用の仕様適合は未検証。50/100/200/400 kHz設定で転送エラー0を確認し、400 kHz設定では60秒間で597画面、約10回/秒の更新を確認した。画面生成の100 ms周期は維持する。実際のスイッチ操作から表示までの遅延は未測定。

タイミング値はCubeMX 6.15の計算処理でPCLK1=170 MHz、アナログフィルター有効、DNF=0、立上り100 ns・立下り10 nsを仮定して算出した。仮定は実測値ではない。.ioc・生成コードとBoardのTIMINGR=0x60400D28は一致させた。生成GPIOはGPIO_NOPULLのままで、board_oled_i2c_init()が内部プルアップを適用する。CubeMX再生成後はTIMINGR、PCLK1、フィルター、Boardのpull設定を照合する。

SCLのPA15への変更は.iocのピン一覧・I2C1_SCL割当と、生成Core/Src/i2c.cのGPIOAクロック・初期化・解除へ同期して反映した。CubeMX本体による再生成は未実施。再生成後はPB7/PA15のAF4設定とBoardの内部プルアップ設定を照合する。

CANはユーザーによるVIO修正後、2026-10-09にダミーから両バスの受信と解析を確認した。CAN2は20フレームのソフトウェアRXキュー満杯による破棄が継続しており、取りこぼし対策は未実施。OLEDの同期転送中もIRQ受信は継続するが、mainのキュー回収が遅れる構成であり、負荷との関係を改善時に検証する。実機の0x215/216はDLC=4で、現行の8 byte限定解析では電圧状態へ反映されない。0x244も解析対象外。これらの通信形式対応は残課題とする。

OLED描画・転送時間の計測

board_cycles()は制御タイマー起動時に有効化したDWTを読む。oled_begin()の画面消去開始からoled_commit()までを描画時間として測り、文字列整形・スイッチ入力取得を含める。送信はBoard内でHAL_I2C_Master_Transmit()の前後を測る。oled_process()の実行時間合計にはHAL送信待機を含む。commitから137トランザクション完了までの経過時間は、その間のmainの別処理も含む。

起動後の成功64画面をoled_timing_samplesに保存し、oled_timing_count=64で保存を止める。エラー画面は保存しない。最初の画面の転送は制御タイマー起動後に始まるため、DWTが有効な状態で測れる。OLED再初期化では記録を消去しない。再計測にはMCUをリセットする。従来の144 byte診断構造体は変更していない。サンプルRAMは1,536 byte。

Script/read_oled_timing.pyはELFのシンボルから記録とSystemCoreClockを読み、msへ換算して最小・平均・最大と生サイクル値をJSONへ保存する。64画面保存後に短時間CPUを停止して読み、終了時は再開する。測定には読み取り時のCPU停止時間が混入しない。

2026-10-10のDebug FW、400 kHz設定、128x64全画面、2回目の64画面の実測は、描画平均0.305 ms、HAL同期送信合計31.041 ms、OLED転送処理合計31.358 ms、描画開始から全画面転送完了まで37.599 ms。通信以外の描画・送信処理は差分で約0.623 ms、mainの別処理と計測記帳などの間隔は約5.935 ms。2回の起動で近い値を確認した。画面更新間隔100 ms自体はこれらの計測に含めない。

DWTは割込みを無効にせず読むため、各値には区間中の割込み時間と計測の影響が含まれる。HAL同期送信時間はHAL処理と完了待機を含み、純粋なSCL波形の時間やLCDの内部走査・画素反映時間は測定していない。これらを厳密なCPU専有時間とは扱わない。詳細な条件と再読取りコマンドは検証記録を参照。

SWDからの診断読取り

App/app_monitorのapp_monitor_statusを約100 msごとに更新する。magic=0x34575331、sequenceは更新中が奇数・完成が偶数。CAN受信・破棄・FIFO喪失・bus-off、IMU生値、OLEDフレーム数、SW、制御タイマーの計数を収録する。last_idは受信の都度更新するため、診断読取りは短時間CPUを停止して行う。

書き込んだELFと同じファイルを指定する。例のシリアル番号は実際のST-Linkへ置き換える。

python ./Script/read_monitor.py --serial 002D00373033510635393935 --elf build/Debug/4WS_MainFW.elf --output build/hardware/monitor.json

PythonはArm nmでシンボル位置を求め、CubeProgrammerのHOTPLUG・1 MHz SWD接続でCPU停止、RAM保存、再開を行う。JSONと同名の.binを保存する。--nm、--programmerでツール位置を指定可能。診断構造体の版・サイズとsequenceを確認するが、FW全体の一致を保証する検証ではない。CPU停止は周期に影響するので、ジッターや周期精度の測定には使用しない。USB通信が切れた場合はCPU再開も保証できない。