次々と課題が見つかってなかなか収束しない VCA、ファームウェアも大まかに動くようになってきて、ラックに収めてみたところ、動作せず。おかしいと思ってモジュールを調べてみると、なんとフラッシュメモリが完全に消去されています。ユーザデータどころかファームウェアもなくなってしまいます。何回書き直しても同じで、これはモジュールの不具合、ということで調査しました。
電源を再投入するとファームウェアが消えるので、電源オン時に消えるのかオフ時に消えるのかまず切り分けました。データ破壊はモジュラーシステムのリニア電源をスイッチでオンオフしたときだけ起こります。モジュールの電源プラグをいきなり刺したり抜いたりした場合は起こりません。そういうわけで、リニア電源を入れてからプラグを抜いたりその逆をしたりで切り分けられます。結果電源オフ時に消えることがわかりました。電源オフ時にマイクロコントローラが誤動作する模様。まずはオフ時に電源とリセットの電圧の推移をオシロスコープで調べました。
以下青線が電源で赤線がリセットです。

電源を落とすと、電圧は鈍くふらふらっと降りてゆきます。2.0V ぐらいまで来るとリセットが急激に 0V に落ちます。これはコントローラ内部で自動的にリセットがかかっている模様。しかし、3.3V 電源から 2.0V まではコントローラが稼働しているらしく、この間にフラッシュメモリに対して何らかの誤動作を起こすようです。
こういう時には Brown Out Reset (BOR) を設定するのが常道です。ということで、設定してみました。STM32 では BOR が発動する電源電圧を何段階かに設定できます。電源降下時の設定 BORF (BOR Falling edge) に一番厳しい 2.8V を設定しました。以下が BOR を設定したときの電圧の推移です。

設定どおり、電源電圧 2.8V 程度でリセットがかかりますが、やはりフラッシュメモリは壊れます。そもそもなんでこんなに電源がふらふら降りるのかというと、リニア電源には平滑のために巨大容量が繋がっているから、スイッチは降圧トランスの一時側についていて電源を切っても容量は繋がったままだからです。そもそもリニア電源にリレーをかませるなどスパッと落ちる工夫をすべきとも思いますが、どのみち電源パタンでファームウェアが壊れる問題には対処しておく必要があります。
フラッシュメモリが壊れるのは設定やプログラムの問題の可能性が大きいと思うのですが、問題を見つけるには時間がかかるかもしれません。開発時に電源を落とすたびにファームウェアが壊れているのでは効率が悪くていけません。当面は回路で逃げることにしました。
BOR を使ってもファームウェアが壊れる。ではリセットボタンを押しながら電源を落とすとどうなるか試してみました。この場合にはファームウェアは無事です。マイクロコントローラの電源電圧が下がってくる前に強制的にリセットをかける回路を追加する方針を検討しました。
以下はモジュールに入ってくる 12V 電源とそれを降圧して得たマイクロコントローラの 3.3V 電源の推移を比較したものです。青線が 3.3V で赤線が 12V です。

おおもとの電源が 4V ぐらいまで落ちてきてはじめてマイクロコントローラ側に電圧の降下が現れるようです。8V 程度に落ちてくるまでにリセットがかけられれば十分間に合いそうです。
まずはシミュレータでリセット回路を検討します。VCC と 3.3V 電源を U1 で比較して10V 弱ぐらいまで降りてきたら U2 のスイッチを ON にする仕掛けです。最終的には 3.3V 電源の電圧も降りてきてしまうのでそれでもなおリセットスイッチが動作するか確認するため、シミュレータの電源に一工夫入れて実電源と同じようなパタンの降下をするようにしました。
以下、緑線が 12V 電源の降下の推移、青線が 3.3V 電源の電圧、赤線がリセット電圧です。

シミュレーションの結果は上々です。3.3V 電源が落ちてくる前にリセットがかかっています。
シミュレーションはうまくいったので実回路で実験してみます。いきなり基板上に部品を乗せるのは抵抗があるのでまずはデバッグポートから線を引っ張り出してきてブレッドボード上で試してみます。

結果うまく行きました。狙い通り電源電圧が降下する前にリセットがかかっています。ファームウェアの壊れもなくなりました。

あとは同じ回路を基板に載せます。といっても小さい基板でもう場所があまりない。今回はまじ修正だらけです。もうしょうがないから空中配線でいきましょう。

とりあえずブレッドボード版と同じように動作しました。

フラッシュメモリが壊れないことを確認。
ですがやっぱりこれが根本解決なのかどうか心もとないです。同じ電源に同じ STM32 コントローラを使った Rings が繋がっており、こちらはリセットに特別な仕掛けはありませんがファームウェアが壊れたりはしていません。やっぱりマイクロコントローラの設定かファームウェアに問題があるように思えます。
根本解決は後日ということにして、とりあえずファームウェアは消えないようになりました。
追記 8/7/2025
STM32CubeIDE (STM32CubeMx) の設定を確認したところプログラム領域から BOR を変更する設定になっていました。

これを以下のようにやめてみました。

今のところリセット回路を入れなくてもファームウェアが壊れていません。これが関係ある可能性が大きいです。
以下がコードの差分です。OB (Option Bytes) への書き込みがなくなっています。
diff --git a/firmware/Core/Src/main.c b/firmware/Core/Src/main.c
index 650ab10..48ebb47 100644
--- a/firmware/Core/Src/main.c
+++ b/firmware/Core/Src/main.c
@@ -391,8 +391,6 @@ static void MX_FLASH_Init(void)
/* USER CODE END FLASH_Init 0 */
- FLASH_OBProgramInitTypeDef pOBInit = {0};
-
/* USER CODE BEGIN FLASH_Init 1 */
/* USER CODE END FLASH_Init 1 */
@@ -400,33 +398,10 @@ static void MX_FLASH_Init(void)
{
Error_Handler();
}
-
- /* Option Bytes settings */
-
- if (HAL_FLASH_OB_Unlock() != HAL_OK)
- {
- Error_Handler();
- }
- pOBInit.OptionType = OPTIONBYTE_USER;
- pOBInit.USERType = OB_USER_BOR_EN|OB_USER_BOR_LEV;
- pOBInit.USERConfig = OB_BOR_ENABLE|OB_BOR_LEVEL_RISING_3
- |OB_BOR_LEVEL_FALLING_3;
- if (HAL_FLASHEx_OBProgram(&pOBInit) != HAL_OK)
- {
- Error_Handler();
- }
- if (HAL_FLASH_OB_Lock() != HAL_OK)
- {
- Error_Handler();
- }
if (HAL_FLASH_Lock() != HAL_OK)
{
Error_Handler();
}
-
- /* Launch Option Bytes Loading */
- /*HAL_FLASH_OB_Launch(); */
-
diff --git a/firmware/vca.ioc b/firmware/vca.ioc
index 4c3dfc1..be73cb5 100644
--- a/firmware/vca.ioc
+++ b/firmware/vca.ioc
@@ -47,7 +47,7 @@ FDCAN1.NominalSyncJumpWidth=2
FDCAN1.NominalTimeSeg1=30
FDCAN1.NominalTimeSeg2=17
FLASH.IPParameters=OB_BOR_Level_active,advanced_settings,OB_User_Configuration_active
-FLASH.OB_BOR_Level_active=true
+FLASH.OB_BOR_Level_active=false
FLASH.OB_User_Configuration_active=false
FLASH.advanced_settings=false
File.Version=6
Cリセット回路はあっても害になるものではないので試作 VCA にはつけたままにしておきたいと思います。次のモジュールに入れるかどうかは検討段階に実験して決めようと思います。