ルモーリン

I2Cを割込制御で使えるようになった

投稿:2012-03-06

I2Cのサンプルがポーリングのだったので、割込制御での使い方が分かっていなかったというのもあって、 最初にI2Cを使った時も失敗してポーリング制御で完成させてました
NXPの人には申し訳ないが、割り込みを舐めてないか? つぎのコードはNXP謹製のサンプルコードで、この処理を使ったサンプルは未提供(笑)。 lpc17xx_i2c.cの割り込みルーチンI2C_MasterHandler()がセットしたフラグを アプリケーション側から呼ばれたI2C_MasterTransferComplete()が刈り取る仕掛けです。
//I2C_MasterHandler()の末尾
end_stage:
    // Disable interrupt
    I2C_IntCmd(I2Cx, 0);
    // Send stop
    I2C_Stop(I2Cx);
    I2C_MasterComplete[tmp] = TRUE; //①フラグをセット

uint32_t I2C_MasterTransferComplete(LPC_I2C_TypeDef *I2Cx)
{
    uint32_t retval, tmp;
    tmp = I2C_getNum(I2Cx);
    retval = I2C_MasterComplete[tmp]; //②フラグをコピー
    I2C_MasterComplete[tmp] = FALSE;  //③クリア
    return retval;
}
処理順序が①②③ですんなり進んでくれると無事終了なのですけれど、②①③の順に処理されると①の終了がアプリケーション側に伝わらない。 ②と③の間で①が処理されると困る訳ですよ。 I2Cの通信が1回こっきりで、それを②①③で処理して完了を把握できないケースがありますね。 実はこれ、②でコピーしたフラグが立っていれば、通信完了で割り込み処理が終わっているのは確実(割り込みルーチンのソース見て確認済)ですから、 フラグが立っている場合に限り③のクリアを行なうと無事に終了できます。 このサンプル使ってトラブル起きてないのかな?
uint32_t I2C_MasterTransferComplete(LPC_I2C_TypeDef *I2Cx)
{
    uint32_t retval, tmp;
    tmp = I2C_getNum(I2Cx);
    retval = I2C_MasterComplete[tmp]; //②フラグをコピー
    if (retval)
    {
        I2C_MasterComplete[tmp] = FALSE;  //③クリア
    }
    return retval;
}
もっと厳しい状況で使われるフラグであれば②のフラグを見てから③のクリアでもダメなケースがあって、 CPUによっては(HITACとか)専用命令を持ってたりします。 テスト&クリア命令とか言って「1個の命令で」フラグの状態を把握し立っていたらクリアできるようになっています。 1個の命令なのでテストとクリアの間に割り込みルーチンがフラグを立てる可能性がない訳です。 将来、テスト&クリアをするほどの厳しい状況になったらどうしよう、うーん。

現行のフラグ刈り取り方式では、アプリケーション側がウエイト5ms入れつつループして完了待ち(恥)なんてことをしてるので 送信要求よこしたタスクを止めてI2Cの処理完了時に再開するつもりです。 既にFreeRTOSを使ってるので、処理停止/再開なんて朝飯前です。のはずです。だといいなあ。
やっぱりマルチタスクは機能の追加が楽ですね。 前回のたこルカ4人表示のうち、右上の1人に休んでもらって時計表示を付けました。 コーディングは時計表示の事だけ考えればいいので特にチョンボもありませんでした。 特に、前回やったメイン処理からの呼び出しコーディングには、機能追加に伴う変更がありません。 あたらしく追加したCファイルだけの操作で機能追加できるのが何か嬉しい。


さて、今回の鉄ゲタコーナーは…
前回のweak属性を使った作戦はほぼ成功を収めましたが、追加する機能が別プロジェクト(?)のライブラリに収まっていて それを使ったアプリケーションをリンクするとオブジェクトを探してくれない。 つまり、ライブラリからそのオブジェクトを持って来ない。 結局、機能が追加されません(苦笑)。 アプリケーション側のプロジェクトに入れると目論見どおりに追加されますが、こっちとしては意地でもライブラリに入れておきたい。 もう一つの上手く行った方法は、リンカのオプションで機能のオブジェクトファイルを直接指定してインポート。 なんか負ける気がするので、これもボツ。 で、どうしたか?アプリケーション側のプロジェクトに例の配列だけの定義を入れました。 呼び出し関数はグローバルな定義ですから、リンカが普通にライブラリから持ってくるのでした。

まあ、どうせ話が通じないと思うので、あとはソース見てください(^^;
yrntrlmnmnt20120213.zip