cp2112_BME680
CP2112とは
- USB⇔I2C変換チップ(Silicon Laboratories社 Single-Chip HID USB to SMBus Master Bridg)でLinuxやWindowsのUSBからGPIO8本とI2Cを使うことが出来ます、ここではSunhayatoのMM-CP2112Aのモジュール基板を使用しています
- MM-CP2112A売り文句マイコンのプログラミング不要でI2C接続のセンサーやGPIO(8ピン)をPCアプリケーションから直接制御できます。とは簡単には行かず中々の難物で、プログラミングには以下HIDAPIとCP2112インターフェース仕様の理解が必要です。
- hidapi参考資料
- CP2112参考資料 AN495: CP2112 Interface Specification
- 今回動作検証したI2C接続デバイスBME680ハードウエア資料
- 筆者のlubuntu lts 18.04LinuxマシンのUSBに接続すると以下のようにドライバーがロードされHIDデバイスが認識されます
1
2
3
4
5# lsmod |grep cp2112
hid_cp2112 24576 0
hid 102400 3 hid_cp2112,hid_generic,usbhid
# ll /dev/hidraw-cp2112
lrwxrwxrwx 1 root root 7 4月 6 16:54 /dev/hidraw-cp2112 -> hidraw0 - ここからは前述の資料とかき集めた情報を元にGPIOとI2C接続の温湿度センサーAM2320と環境ガスセンサーBME680の動作検証を行なったpepocp2112ctl.cプログラムを元に説明します。
開発するためのパッケージを
libhidapi
インストール1
2# apt install libhidapi-dev
libhidapi-dev libhidapi-hidraw0 libhidapi-libusb0以下ファイルをゲット・コンパイルBoschSensortec/BME680_driverからFork
1
2
3
4
5
6
7
8
9
10
11bme680.c bme680.h bme680_defs.h pepocp2112ctl.c
# gcc -Wall -o pepocp2112ctl pepocp2112ctl.c bme680.c -lhidapi-hidraw
# ll
合計 144
drwxr-xr-x 2 root root 140 4月 6 17:39 ./
drwxr-xr-x 3 www-data www-data 840 4月 6 17:39 ../
-rwxr-xr-x 1 root root 44364 4月 13 2019 bme680.c*
-rwxr-xr-x 1 root root 8353 4月 13 2019 bme680.h*
-rwxr-xr-x 1 root root 17134 4月 13 2019 bme680_defs.h*
-rwxr-xr-x 1 root root 34644 4月 6 17:39 pepocp2112ctl*
-rw-r--r-- 1 root root 30626 3月 28 16:26 pepocp2112ctl.cプログラミング例、SMBus Devices操作のみソースから抜き出し重要な箇所だけ説明します、他はソースを確認して下さい。(経験上GPIOはマルチプロセスで動作させる必要があると思いセマフォ (semaphore)を使用し結構な量が有ります。)
- CP2112のI2C接続デバイスBME680はバッファーにI2Cアドレス、必要なコマンド、データ、バイト・カウンタを設定、hid_send_feature_reportで送信し必要に応じてコマンドに対するレスポンスを待ち処理をします。
- BME680ライトはコマンド(CP2112_DATA_WRITE)、レジスター・アドレス、データ、バイト数を設定しデバイスに書き込みます。
1
2
3
4
5
6
7
8
9
10int8_t user_i2c_write(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len)
{
.
reg[0] = CP2112_DATA_WRITE;
reg[1] = BME680_I2C_ADDR_PRIMARY<<1;
reg[2] = len+1;
reg[3] = reg_addr;
for (int8_t i=4; i<len+4; i++)
reg[i] = reg_data[i-4];
rslt = hid_send_feature_report(hd, reg, len+4);- デバック中のバッファ等ダンプ
1
2
3
4
5user_i2c_write() dev_id: ec reg_addr: e0 len: 1
user_i2c_write()1 rslt: 5 len: 1
reg dump start
0:14 1:ec 2:02 3:e0 4:b6
reg dump end
- デバック中のバッファ等ダンプ
- BME680リードは最初にライトコマンドでレジスターアドレスを送信します。
- 次にI2Cアドレスリード・リクエスト・コマンド(CP2112_DATA_READ_REQ)送信、
データが整う迄(CP2112_DATA_READ_FORCE_SEND)でPolling、ステータス確認、(CP2112_DATA_READ_RESPONS)を待ち、デバイスからデータを読み出します。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61int8_t user_i2c_read(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len)
{
.
reg[0] = CP2112_DATA_WRITE;
reg[1] = BME680_I2C_ADDR_PRIMARY<<1;
reg[2] = 1;
reg[3] = reg_addr;
rslt = hid_send_feature_report(hd, reg, 4);
.
user_delay_ms(HID_WAIT);
reg[0] = CP2112_DATA_READ_REQ;
reg[1] = BME680_I2C_ADDR_PRIMARY<<1;
reg[2] = 0x00; // reads length_high
reg[3] = len; // reads length_low
rslt = hid_send_feature_report(hd, reg, 4);
.
user_delay_ms(HID_WAIT);
while (retry_cnt < 3)
{
/* SMBus Polling */
reg[0] = CP2112_DATA_READ_FORCE_SEND;
reg[1] = 0x00; // reads length_high
reg[2] = len; // reads length_low
rslt = hid_send_feature_report(hd, reg, 3);
.
user_delay_ms(HID_WAIT);
rslt = hid_read_timeout(hd, buf_in, len+3, timeout);
.
/*
buf_in[0]:hid_report ID->0x13:CP2112_DATA_READ_RESPONS
buf_in[1]:hid_status 0x00->Idle ,x01->Busy ,x02->Complete
,buf_in[2]:hid_read_length
buf_in[3]-:Following_BME680_Read_Data
*/
if (buf_in[0] == CP2112_DATA_READ_RESPONS)
{
if (buf_in[1] == 0x00 || buf_in[1] == 0x02 || buf_in[1] == 0x03)
.
break;
retry_cnt++; // Busy or Idle or Other
user_delay_ms(HID_WAIT);
continue;
}
if (buf_in[0] == CP2112_TRANSFER_STATUS_RESP)
{
.
user_delay_ms(HID_WAIT);
retry_cnt++;
continue;
}
}
.
if(buf_in[2] > 0 && rslt > 3 && buf_in[2] < rslt)
{
int8_t j = buf_in[2];
for (int8_t i=0; i < j; i++)
{
reg_data[i] = buf_in[i+3];
}
return 0;
} - デバック中のバッファ等ダンプ
1
2
3
4
5
6
7
8
9
10
11user_i2c_read dev_id: ec reg_addr: d0 reg_data: 04 len: 1
user_i2c_read()hid_read_timeout rslt:4 retry_cnt:0
buf_in dump start
0:13 1:02 2:01 3:61
buf_in dump end
user_i2c_read dev_id: ec reg_addr: 89 reg_data: 04 len: 25
user_i2c_read()hid_read_timeout rslt:28 retry_cnt:0
buf_in dump start
0:13 1:02 2:19 3:40 4:a3 5:66 6:03 7:2f 8:80 9:8e 10:c9 11:d6 12:58 13:ff 14:de 15:19 16:cf 17:ff 18:2f 19:1e 20:00 21:00 22:1a 23:f3 24:e7 25:f6 26:1e 27:02
buf_in dump end
- 次にI2Cアドレスリード・リクエスト・コマンド(CP2112_DATA_READ_REQ)送信、
最後に
ここまで苦労して分かってみれば、案外hidapiからCP2112でI2C素子扱いもMM-CP2112A売り文句マイコンのプログラミング不要でI2C接続のセンサーやGPIO(8ピン)をPCアプリケーションから直接制御できます。を納得出来ない事はないかも、如何でしょうPlease check the source code below
https://github.com/kujiranodanna/BME680_driver