前回はDACをSPI接続。今回はI2C接続に戻ります。前々回はI2Cでデータをたれ流し、読み取りが入ってなかったためです。接続するデバイスはAHT21B温湿度センサです。すでに何度か使ってみてはいるものの、MbedOS環境でSTM32マイコンには接続してなかったです。まあ「淡々と」移植作業をすれば動くものは動く、と。
※「モダンOSのお砂場」投稿順Indexはこちら
Nucleo F401RE、I2Cバスの引き出し
今回もターゲットはST Microelectronics社のSTM32F401REマイコン搭載、Nucleo F401REボードです。NUCLEOボードはArduino互換配列のピンソケットを搭載しているのでそこからI2Cバス(3.3V)を引き出して使用しています。以下の前々回の記事でI2Cバスの先にAQM1602、LCDパネルを接続しているのでそれをそのまま流用です(すでに前々回からAHT21Bは横にいた。)
モダンOSのお砂場(50) Mbed OS2->OS6お引越し、I2CでAQM1602編
AHT21Bの制御
AHT21B、温湿度センサは、使いやすい温湿度センサじゃないかと思います。すでに何度か接続実験を行っています。
Raspberry Pi 3 model B+から引き出したI2Cバスに接続し、Raspberry Pi OS上のMicroPythonから制御してみた記事が以下に。
部品屋根性(77) AHT21B、CRC8でのデータ検査を追加。ラズパイPython
また、「最恐のZ80後継機」Rabbit4000に接続してみた記事が以下にあります。
うさちゃんと一緒(17) Rabbit4000にAHT21B温湿度センサをI2C接続
今回はRabbit4000で使用した Dynamic C版のCのソースコードをベースにMbed OS6用に書き換えました。
なお、Mbed OS6のI2Cバスの制御については、Mbed OS v6.15 APIの以下のページを参照しています。
実験に使用したソースコード
例によって、センサ用に「クラスもどき」を定義して、main関数からはそのクラスのインスタンスを使用するスタイルにしてあります。しかしハードは一つなので、複数個のインスタンスができないように、とか、不要な内部は見せないようにとか一切やってないです。手抜きです。Dynamic Cの「なんでもあり」の昔のCのスタイルをほぼ踏襲。
まずはクラスヘッダ。バッファまでpublicにしているのは、実験なのでmain関数内でバッファ内容まで「吟味」しとるためです。
#ifndef AHT21B_H #define AHT21B_H #include "mbed.h" #define AHT21B_ADDR (0x70) #define BUFSZ (7) class AHT21B { private: I2C& i2cRef; public: uint8_t buf[BUFSZ]; AHT21B(I2C& ch); uint8_t calcCRC8(); int32_t triggerAHT21B(); int32_t readMeasurementResult(); uint32_t calcHumidity(); int32_t calcTemp(); void clearBUF(); }; #endif //AHT21B_H
次にクラスメソッド。メソッドというほどのもんじゃありません。ほぼほぼDynamic C用に書いたコードそのまま。
#include "AHT21B.h" uint8_t buf[BUFSZ]; void AHT21B::clearBUF() { for (int i=0; i< BUFSZ; i++) { buf[i]=0; } } AHT21B::AHT21B(I2C& ch) : i2cRef(ch) { clearBUF(); } uint8_t AHT21B::calcCRC8() { int ack, cnt, idx; uint8_t poly=0x31; uint8_t crc=0xFF; for (cnt = 0; cnt < (BUFSZ - 1); ++cnt) { crc ^= buf[cnt]; for (idx=0; idx < 8; ++idx) { if (crc & 0x80) { crc = (crc << 1) ^ poly; } else { crc = crc << 1; } } } return crc; } int32_t AHT21B::triggerAHT21B() { ThisThread::sleep_for(10ms); buf[0]=0xAC; buf[1]=0x33; buf[2]=0x0; return i2cRef.write(AHT21B_ADDR, (const char*)buf, 3, false); } int32_t AHT21B::readMeasurementResult() { int ack, cnt; clearBUF(); ThisThread::sleep_for(80ms); return i2cRef.read(AHT21B_ADDR, (char*)buf, BUFSZ, false); } uint32_t AHT21B::calcHumidity() { uint32_t humInt; humInt = (uint32_t)buf[1]; humInt <<= 8; humInt += (uint32_t)buf[2]; humInt <<= 4; humInt += (uint32_t)((buf[3] & 0xF0) >>4); humInt *= 100; humInt >>= 20; return humInt; } int32_t AHT21B::calcTemp() { int32_t tempInt; tempInt = (int32_t)(buf[3] & 0x0F); tempInt <<=8; tempInt += (int32_t)buf[4]; tempInt <<=8; tempInt += (int32_t)buf[5]; tempInt *= 200; tempInt >>=20; tempInt -=50; return tempInt; }
最後に main関数です。
#include "mbed.h" #include "AHT21B.h" DigitalOut myled(LED1); I2C i2c(I2C_SDA, I2C_SCL); int main() { AHT21B aht21(i2c); int32_t status; printf("AHT21B Read test\r\n"); while (1) { status = aht21.triggerAHT21B(); status = aht21.readMeasurementResult(); printf(" Status: 0x%02x\r\n", aht21.buf[0]); printf(" Data: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\r\n", aht21.buf[1], aht21.buf[2], aht21.buf[3], aht21.buf[4], aht21.buf[5]); printf(" Humidity [RH%%]: %d\r\n", aht21.calcHumidity()); printf(" Temperature[C]: %d\r\n", aht21.calcTemp()); if (aht21.buf[6] == aht21.calcCRC8()) { printf(" CRC: OK\r\n"); } else { printf(" CRC: NG\r\n"); } ThisThread::sleep_for(500ms); myled = !myled; } }
ビルドして実行
ビルドはいつものように、Arm社のオンライン開発環境、Keil Studio Cloudを使わせていただいております。Mbed OS6.15.1です。Mbed Online complierからこちらへ移れ、との思し召しで移ってきたときはどうなるかと思いましたが、まあ慣れたかな。ほんとか?
ビルド結果をターゲットに書き込み、USBシリアルにたれ流されている結果を表示(毎度お世話になっておりますTeraTerm Pro利用です)したものが以下に。
動いているソフトを移植するのはお楽。