STM32のI2Cコントローラを使用してI2C EEPROMを使おうとしたが、どうもあやしげなので、I2Cコントローラの使用は敬遠して、自前ソフト駆動でやることにした。
I2Cコントローラ自体は悪くはない、ような気がする、のだが、STのライブラリ stm32f10x_i2c.c が怖い。
サンプルコードを見ると、 I2C_CheckEvent() で「I2Cバスがフリーになった」「スレーブアドレスを送信してACKが返ってきた」等々のイベント待ちをして処理をするようになっているが、I2C_CheckEvent() は、I2Cコントローラのステータスレジスタ SR1,SR2 が「指定した定数値になった」ことがわかるだけの単純な関数。複数イベントをチェックすることもできないし、何か例外的なフラグが立ったらもうそれで希望した定数値には合致しない。
なのに、サンプルコードでは、イベント待ちは
while(CheckEvent(I2C_EE, I2C_EVENT_HOGEHOGE)) ;
と、無邪気に書いてある。テストしてみたら、こういうイベント待ちでしばしば永久ループになってしまった。
EEPROMのライト完了待ちでは通常ACKポーリングを行うが、サンプルソースではこんなコードになっていた。
void I2C_EE_WaitEepromStandbyState(void)
{
do
{
/* Send START condition */
I2C_GenerateSTART(I2C_EE, ENABLE);
/* Read I2C_EE SR1 register to clear pending flags */
SR1_Tmp = I2C_ReadRegister(I2C_EE, I2C_Register_SR1);
/* Send EEPROM address for write */
I2C_Send7bitAddress(I2C_EE, EEPROM_ADDRESS, I2C_Direction_Transmitter);
} while(!(I2C_ReadRegister(I2C_EE, I2C_Register_SR1) & 0x0002));
// SR1 bit1(ADDR) == 1 アドレス送信後、ACKがきた
/* Clear AF flag */
I2C_ClearFlag(I2C_EE, I2C_FLAG_AF);
/* STOP condition */
I2C_GenerateSTOP(I2C_EE, ENABLE);
}
どうも挙動がおかしいので、このACKポーリング中にI2C信号線をロジアナで見てみたところ、送っているスレーブアドレス(EEPROM_ADDRESS)が、しばしば、化けていた。
このループ、イベント待ちをせずにスレーブアドレスを送りまくる感じだけど、だからオーバーランしているのか?
例外処理が弱すぎる気がする。ていうか全く無い。いくらサンプルコードでもあんまりだ。
うーむ…。普通はちゃんと使えているんだろうか?これ?
2012/05/29 追記。スレーブアドレスが化けないACKポーリング関数を書いてみた。
自作の1us単位でカウントアップする関数を使ってタイムアウト処理をしているのと、これでもまだ例外処理が足りないのでその辺は利用される場合は追加してください。
void I2C_EE_WaitEepromStandbyState(void)
register uint16_t SR1_Temp;
while (1) {
IWDG_ReloadCounter(); /* WDT リロード */
/* I2C バスビジーだったら待つ */
while(I2C_GetFlagStatus(I2C_EE, I2C_FLAG_BUSY)) {
IWDG_ReloadCounter(); /* WDT リロード */
}
/* Send START condition */
I2C_GenerateSTART(I2C_EE, ENABLE);
/* Test on EV5 and clear it (スレーブアドレス送信可能になるまで待つ) */
while(!I2C_CheckEvent(I2C_EE, I2C_EVENT_MASTER_MODE_SELECT)) {
IWDG_ReloadCounter(); /* WDT リロード */
}
/* EEPROMのスレーブアドレスを送信 */
I2C_Send7bitAddress(I2C_EE, EEPROM_ADDRESS,, I2C_Direction_Transmitter);
/* ACKまたはNAKが確認できるまで待つ
ADDR, AF だけのチェックでは引っかけられないので、
とりあえず1msでタイムアウトとする */
set_1us_counter(0);
while (1) {
IWDG_ReloadCounter(); /* WDT リロード */
SR1_Temp = I2C_ReadRegister(I2C_EE, I2C_Register_SR1);
// ACK
// if (I2C_CheckEvent(I2C_EE, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {
// I2C_CheckEvent()でなく、 SR1の ADDR ビットのみを判断材料とする
/* SR1 bit1(ADDR: アドレス送信完了、ACKが来た) */
if (SR1_Temp & 0x0002) {
/* STOPコンディション送信 */
I2C_GenerateSTOP(I2C_EE, ENABLE);
return;
}
/* NAK (SR1 bit10(AF): ACKが来なかった) or タイムアウト */
if ((SR1_Temp & 0x0400) || get_1us_counter() > 1000) {
/* Clear AF flag (AF: 送信後、ACKが来ない) */
I2C_ClearFlag(I2C_EE, I2C_FLAG_AF);
/* STOPコンディション送信 */
I2C_GenerateSTOP(I2C_EE, ENABLE);
break;
}
}
}
}

コメント
管理者様
突然のコメントにて失礼致します。
当方、STM32マイコンをテーマにしたホームページ(http://miqn.net)を制作・運営いたしております川内と申します。
このホームページははSTM32マイコンを題材に、開発環境の構築方法やRTOSの利用方法を解説しているホームページです。もともと書籍として刊行する予定で執筆しておりました原稿の一部をホームページで無償公開しております。また関連する動画も掲載中です。
インターネットで検索を致しておりましたら、貴ホームページにSTM32に関する記事が掲載されているのを拝見いたしました。ご迷惑でなければ相互リンクをさせていただきたく、このメールを差し上げた次第です。
勝手ながらこちらのリンクページには、先だって貴ホームページへのリンクを掲載させていただきました。ご迷惑であれば削除させていただきますのでお知らせください。
http://miqn.net/node/38
リンクを掲載いただけます場合には、「STM32マイコン徹底入門」というホームページ名をアンカーテキストとして、「http://miqn.net」にリンクを設定いただけますと幸いです。
勝手を申しまして恐縮ではございますが、是非ともリンクをお願いいたしたく、よろしくお願いいたします。
miqn.net管理人
川内康雄
川内さん、こんにちは。
わたしは相互リンクというのに興味がなく、またリンクに承諾が必要とも思っていません。
ですので、こちらに対してリンクを張ることはどうぞご自由になさってください。