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できちんと実装できたらまた更新します。

ロボットと電子工作とプログラミング!
女の子は甘いもので出来てる?