sakura.ioを用いたIoTサイコンを作るシリーズ第4弾です。
今回はSTM32とさくらの通信モジュールの通信方法について説明します。
Contents
通信インターフェース
さくらの通信モジュールでは通信方式として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できちんと実装できたらまた更新します。
ロボットと電子工作とプログラミング!
女の子は甘いもので出来てる?