BLDC制御における奇妙なソフトウェアバグ:「書き込み専用」レジスタにご注意
それはとても奇妙なバグでした。システムは期待通りに動作しているように見えたのに、テレメトリデータはそうではないことを示していました。モータの回転速度を決める要求入力が85%を示していましたが、ありがたいことにモータは回転していなかったのです。たいした事じゃない、あるいは、テレメトリの構文解析ソフトウェアの問題だなと、やり過ごしたいのはやまやまでしたが、きちんと追求してシステム周りを調べ、このバグの原因を突き止めるべき時期でした。
問題となったシステムでは、Allegro Microsystemsが提供するブラシレスDC(BLDC)モータドライバチップのA4964を使用していました。これは柔軟に使えるモータ駆動製品であり、CPUサイクルを食いつぶしかねない制御コードをすべてマイクロコントローラ(MCU)から専用のハードウェアチップに移すことができるため、私は特にこのチップを気に入っていました(図1)。
図1: A4964は、BLDCモータの制御をMCUから専用ハードウェアに移すことができて便利です。(画像提供: Allegro Microsystems)
このシステム構成では、BLDCモータの駆動および制御方法を決める32個の内蔵レジスタの設定を、SPI通信インターフェースを利用して行っていました。
このアプリケーションでは、起動時に初期化ルーチンでA4964を設定しますが、そのためにモータに必要な設定パラメータが記された構成テーブルを読み込みます。(例として)その疑似コードを以下に示します(リスト1)。
コピーfor(uint8_t WriteIndex = 0; WriteIndex < A4964MaxRegister; WriteIndex++)
{
// Write value stored in A4964Config at index WriteIndex to the chip
}
リスト1: モータの構成パラメータを読み取る初期化ルーチンを示します。(コード提供:ジェイコブ・ベニンゴ氏)
このコードは初期化処理であり、誤りの入り込む余地もそれほどないため、アプリケーション内で定期的に呼び出されるメインロジックをすぐに調べることにしました。このコードは、もう少し面白みがあります。本アプリケーションの使用場所は、RAMの格納値に悪影響を及ぼす可能性のある放射線の多い環境でした。初期化値は、起動時にA4964のRAMに書き込まれます。したがって、発生しうるビット反転の影響を即座に打ち消すため、A4964のメモリを定期的に読み取って、食い違いがあれば設定値を改めて書き込みます。疑似コードは次のようなものでした(リスト2)。
コピーfor(uint8_t Index = 0; Index < A4964MaxRegister; Index++)
{
// Read the A4964 configuration register at location Index
// If read value does not match expected value, write configuration value
}
リスト2:現地の放射線がRAMの格納値に悪影響を及ぼす恐れがあるため、ビット反転を打ち消すためにA4964のメモリを定期的に読み取って、食い違いがあれば設定値を改めて書き込みます。(コード提供:ジェイコブ・ベニンゴ氏)
これもごく単純なコードであり、誤りの入り込む余地はそうありませんが、それなのにどういうわけか、何かがチップに正しくない要求入力値を書き込んでいたのです。その値がテレメトリに表れた後、正しい値が表われ、その後また、正しくない値が表われるというように変化しているようでした。奇妙なことです。
ここでさらに興味深いのは、レジスタに85%を書き込むための設定値、すなわちアプリケーション上の値が存在しないことです。ではいったい、この85%という値はどこからきたのでしょうか。実は、10進数の85は、16進数に変換すると0x55になります。コードベースのどこかに0x55があったのでしょうか。もちろん、あります。0x55は、SPIバスの読み取り処理用にダミーの書き込み文字として使われています。しかし、どうして読み取り処理が書き込み処理に変わってしまっているのでしょうか。
実は、A4964のデータシートをよく見ると、その答えはかなり明白なことがわかります(図2)。
図2:問題点を示しているデータシートの部分:レジスタ30は書き込み専用です!(画像提供:Allegro Microsystems)
レジスタ30はモータの要求入力(DI)を制御しますが、これは書き込み専用レジスタです。このレジスタから読み取りをしようとすると、このレジスタにダミーバイトを書き込むことになってしまいます。単純な初期化とチップの再初期化を行う関数が、現在の設定を確認するために要求入力レジスタから読み取りをしようとして、意図せず新しい要求入力を書き込むことになっていたのです。システムが期待通りに動作し続けていたのは、書き込み専用レジスタの読み取りの結果として、必ずすぐ後に正しい値を書き込むからです。しかしその書き込みは、正しくない値がシステムのテレメトリにまで行かないほど速く行われるわけではありませんでした。
A4964を使う場合は、設定データを単純にメモリマップ全体にわたって書き込むことはできず、レジスタ29の後で止める必要があります。最後の2つのアドレス可能レジスタは、特別な書き込み専用レジスタと読み取り専用レジスタです。
ドライバの記述やソフトウェアの実装を正しく行うようにどんなに労力をつぎ込んでも、ときに「ああ、そういうことか」ということが必ず出てきます。奇妙なバグはしばしば、そのハードウェアについて新しいことを知る機会になり、その結果、新しいベストプラクティスが得られることがよくあります。今回の奇妙な小さいバグにより、私は自分の心得に「読み取り専用または書き込み専用のレジスタに細心の注意」を加え、それらのレジスタを注意して適切に扱うことにしました。さもないと、あの書き込み専用レジスタから読み取りをしようとして、システム内に壊滅的な事象を招くことになるかもしれません。
Have questions or comments? Continue the conversation on TechForum, Digi-Key's online community and technical resource.
Visit TechForum