IoTサイコンを作る(4) ~STM32とsakura.ioの通信~

投稿者: | 2017年10月1日

sakura.ioを用いたIoTサイコンを作るシリーズ第4弾です。
今回はSTM32とさくらの通信モジュールの通信方法について説明します。

通信インターフェース

さくらの通信モジュールでは通信方式としてI2CとSPIのどちらかを選択できます。
今回はI2Cを使用します。

コマンド体型

I2C、SPI共にコマンド体型は以下のようになっています。

  • 要求シンタックス
要求種別 引数データ長 引数データ パリティ
Q N A(0) A(1) A(N-1) P

 

  • 応答シンタックス
実行結果 応答データ長 応答データ パリティ
S M D(0) D(1) D(N-1) P

このように、送信データ・応答データ共に可変長な仕様となっています。

公式Arduinoライブラリの構造

ArduinoライブラリはC++で記載されています。

SakuraIOという基底クラスから派生したSakuraIO_I2C・SakuraIO_SPIの2種類のクラスが存在し、この2つを選択することによって通信インターフェースを気にせずに実装できるように設計されています。

通信インターフェースに依存する内容はSakuraIO::executeCommandメソッドに集約されており、このメソッドを移植することによってArduino以外でもライブラリの再利用が可能となっています。

実際には、I2C・SPIクラスそれぞれで以下のメソッドがオーバーライドされることで各インターフェースでの通信を実現しています。

  • void SakuraIO::begin();
  • void SakuraIO::end();
  • void SakuraIO::sendByte(uint8_t data);
  • uint8_t SakuraIO::startReceive(uint8_t length);

しかし、STM32のHALにあるI2Cライブラリでは1バイトずつを送受信する機能がないため、今回はexecuteCommandメソッド全体を移植する形で実装します。
(将来的にはLL APIを使用してレジスタレベルでなんとかしようと思います)

executeCommandメソッドの移植

ということで、今回実装したexecuteCommandメソッドです。
やっつけ仕事なので以下の制約があります

  • SAKURAIO_BUFFERSIZEで指定したバッファサイズ分、毎回必ず受信する。
  • SAKURAIO_BUFFERSIZEで指定したバイト数以上のデータが返ってきたら正常に処理できない。

これらの制約により、データの一括送信・ファイル送受信などの機能には対応できていません。
そのあたりをちゃんとしたいならNucleoをmbedライブラリ・LL APIの使用、あるいはソフトウェアI2Cでの実装をおすすめします。

HAL_StatusTypeDef SakuraIO_ExecuteCommand(uint8_t cmd,uint8_t requestLength, uint8_t *request, uint8_t *responseLength, uint8_t *response){
    uint8_t parity = 0x00;

    sakuraio_buffer[0] = cmd;
    sakuraio_buffer[1] = requestLength;

    //Put the parity byte into the end of the request buffer
    parity = cmd ^ requestLength;
    for(int16_t i=0; i<requestLength; i++){
        parity ^= request[i];
        sakuraio_buffer[i+2] = request[i];
    }
    sakuraio_buffer[requestLength+2] = parity;

    //cmd byte, requestLength byte and parity byte
    requestLength += 3;
    if(HAL_I2C_Master_Sequential_Transmit_IT(sakuraio_i2c, (uint16_t)SAKURAIO_SLAVE_ADDR, (uint8_t *)sakuraio_buffer, requestLength, I2C_LAST_FRAME)!= HAL_OK)
    {
        return CMD_ERROR_RUNTIME;
    }

    /*## Wait for the end of the transfer #################################*/
    while (HAL_I2C_GetState(sakuraio_i2c) != HAL_I2C_STATE_READY)
    {
    }

    /* When Acknowledge failure occurs (Slave don't acknowledge it's address) Master restarts communication */
    if( HAL_I2C_GetError(sakuraio_i2c) == HAL_I2C_ERROR_AF ){
        return CMD_ERROR_RUNTIME;
    }

    if(HAL_I2C_Master_Sequential_Receive_IT(sakuraio_i2c, (uint16_t)SAKURAIO_SLAVE_ADDR, (uint8_t *)sakuraio_buffer, SAKURAIO_BUFFERSIZE, I2C_LAST_FRAME) != HAL_OK)
    {
        return CMD_ERROR_RUNTIME;
    }

    /*## Wait for the end of the transfer #################################*/
    while (HAL_I2C_GetState(sakuraio_i2c) != HAL_I2C_STATE_READY)
    {
    }

    /* When Acknowledge failure occurs (Slave don't acknowledge it's address)
           Master restarts communication */
    if(HAL_I2C_GetError(sakuraio_i2c) == HAL_I2C_ERROR_AF){
        return CMD_ERROR_RUNTIME;
    }

    //Check command status
    if( sakuraio_buffer[0] != CMD_ERROR_NONE ){
        return sakuraio_buffer[0];
    }

    *responseLength = sakuraio_buffer[1];

    //Check parity
    parity = 0x00;
    for(int i=0; i<(*responseLength + 2); i++){
        parity ^= sakuraio_buffer[i];
    }
    if( parity != sakuraio_buffer[*responseLength + 2] ){
        return CMD_ERROR_PARITY;
    }

    return CMD_ERROR_NONE;
}

LL APIできちんと実装できたらまた更新します。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です