2015年10月22日木曜日

STM32F1の旧ライブラリでI2C通信

STM32F1の旧ライブラリ(StdPeriph_Driver)でI2C通信を行うための関数を作りなおした。

宣言
int I2CTR(I2C_TypeDef* I2Cx, uint8_t Addr, uint8_t*Buff, uint8_t TxLen, uint8_t RxLen)

I2Cx
通信に使用するポート

Addr
デバイスアドレスを使用する 7bit形式ではなく8bit形式で渡す

Buff
送受信のバッファを渡す

TxLen
送信データ数を渡す

RxLen
受信データ数を渡す

この関数では送信バッファと受信バッファを兼用している。そのためレジスタアドレスを指定する引数は取らない。レジスタアドレスを指定したい場合はBuff[0]にアドレスを指定し、TxLenを1にする。ROMのようなレジスタアドレスが複数バイトになる場合はBuffに必要なバイト数を設定し、TxLenにその長さを渡す。
送信だけで、受信する必要が無い場合はRxLenを0にすればいい。RxLenが0の場合は送信が終了した段階でStopを発行し関数を終了する。
手持ちのデバイスの中には「レジスタアドレスを指定せずに読みだす」という物がないため、TxLen==0の場合の動作確認はしていない。だけどたぶん失敗すると思う。

const int timeoutは無限ループに入らないようにしている。ただ0x7FFFFFFFでは明らかに長過ぎるから、もっと短い値に変更しないと意味が無い。例を挙げると72MHzで0xFFFFを指定すると67msecほどでタイムアウトする。大雑把な目安としてだいたい1マイクロ秒くらいの分解能になる。例えば10000を指定すると10msecくらいでタイムアウトするので、5000とか10000くらいがいいと思う。
タイムアウトは1回のループ毎に変数をtimeoutで初期化し、whileで比較、デクリメントを行っているだけ。そのためマイコンの動作クロックが変わると待ち時間も変化する。


/*
I2Cx:通信に使用するI2Cポート
Addr:デバイスアドレス
Buff:データバッファ
TxLen:送信バイト数
RxLen:受信バイト数
戻り値:成功した場合は0、失敗した場合は0位外
(戻り値で成功したかの判定は!0を使用すること 失敗時の1は今後変更の可能性がある)
*/
int I2CTR(I2C_TypeDef* I2Cx, uint8_t Addr, uint8_t*Buff, uint8_t TxLen, uint8_t RxLen) {
    uint8_t IsSuccess = 0;
    volatile int to;
    const int timeout = 0x7FFFFFFF;

    uint8_t*p1 = Buff, *p2 = Buff;

    do {
        // check busy 
        {
            to = timeout;
            while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY) && --to);
            if (!to) { break; }
        }

        // generate Start condition 
        {
            I2C_GenerateSTART(I2Cx, ENABLE);

            to = timeout;
            while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT) && --to);
            if (!to) { break; }
        }

        // send Slave address 
        {
            I2C_Send7bitAddress(I2Cx, Addr, I2C_Direction_Transmitter);

            to = timeout;
            while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) && --to);
            if (!to) { break; }
        }

        // send Register address / Transmitted mode 
        while (TxLen--) {
            I2C_SendData(I2Cx, *p1++);

            to = timeout;
            while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) && --to);
            if (!to) { break; }
        }
        if (!to) { break; }

        if (!RxLen) {
            I2C_GenerateSTOP(I2Cx, ENABLE);
            IsSuccess = 1;
            break;
        }

        // generate ReStart condition 
        {
            I2C_GenerateSTART(I2Cx, ENABLE);

            to = timeout;
            while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT) && --to);
            if (!to) { break; }
        }

        // send Slave address / Reciver mode 
        {
            I2C_Send7bitAddress(I2Cx, Addr, I2C_Direction_Receiver);

            to = timeout;
            while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) && --to);
            if (!to) { break; }
        }

        // check One Byte received. ACK Enabe 
        if (RxLen > 1) {
            I2C_AcknowledgeConfig(I2Cx, ENABLE);
        }

        while (RxLen--) {
            if (!RxLen) {
                I2C_AcknowledgeConfig(I2Cx, DISABLE);
                I2C_GenerateSTOP(I2Cx, ENABLE);
            }

            to = timeout;
            while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) && --to);
            if (!to) { break; }

            *p2++ = I2C_ReceiveData(I2Cx);
        }
        if (!to) { break; }

        IsSuccess = 1;
    } while (0);

    if (!IsSuccess) {
        I2C_GenerateSTOP(I2Cx, ENABLE);
        return(1);
    }

    return(0);
}

0 件のコメント:

コメントを投稿