2016年9月12日月曜日

STM32F1のI2Cの初期化でハマった

STM32F1のI2Cは昔慣性ロガーを作った時にかなり苦労して嫌な思い出がある(その時は結局ソフトウェアI2Cで逃げた気がする)。

今回、I2Cで叩けるモータードライバを使ってみたくなり、ついでにペリフェラルのI2Cもやり直そうと考えた。

現状としてはwhileでイベントをポーリングして動かすというダメダメな感じだが、なんとかデバイスに値を送信することはできた。

で、タイトルの通り、初期化でかなりハマってしまった。


I2C_Initの後でレジスタをダンプして調べたところ、BUSYフラグが立っていることがわかった。BUSYはハードウェアで書き込まれ、ソフトウェアから書き込むことはできない。バス上で通信が行われている場合はビジーが1となりる。条件はSDA or SCLがLowの時にSetされ、STOPコンディションを検出してResetとなる。
しかしI2C_Init直後ではマイコンからI2Cを叩いているわけでもないし、バスにはプルアップ抵抗が付いているだけで他のデバイスも未接続なため、STMから動かさないかぎりはバスが動くことはない。

更に詳しく調べると、GPIOをAF_ODで初期化した時点でピンがLowとなり、I2C_InitのあたりでHighに戻ることがわかった。

解決策としてはI2C_Initを行った後にGPIO_Initを行う、という感じらしい。


いまいち動きが理解できないが、GPIO_InitとI2C_Initのタイミングを入れ替えれば動いたり動かなかったりするので、先にペリフェラル、後にGPIOの初期化を行えば正常に動作するようだ。

他のペリフェラル、USARTやSPIであればペリフェラル自体を有効にしなければ動作しないため初期化の順番の問題が表面化してこなかったが、I2Cはペリフェラルを有効にしなくても、クロックを供給した時点?でBUSYのサンプリングが始まるため、順番が厳密になっているのかも。


他にもいろいろ問題がありそう。昔使った時と同様、苦労することになりそうだ。

***
追記:2020/10/02
 だいぶ前のエントリだけど、気がついたので更新。
 これに関する話が、stmcu.jpの資料にある。
 概ね前述した内容はそう間違っていないが、本家の適切な初期化シーケンスが提案されているので、それに従うことをすすめる(資料の内容は会員登録した人向けなので、各自確認のこと)。

0 件のコメント:

コメントを投稿