cp2112_BME680

CP2112とは

  • 開発するためのパッケージを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
    11
    bme680.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
      10
      int8_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
        5
        user_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
        61
        int8_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
        11
        user_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
  • 最後に
    ここまで苦労して分かってみれば、案外hidapiからCP2112でI2C素子扱いもMM-CP2112A売り文句マイコンのプログラミング不要でI2C接続のセンサーやGPIO(8ピン)をPCアプリケーションから直接制御できます。を納得出来ない事はないかも、如何でしょう

  • Please check the source code below
    https://github.com/kujiranodanna/BME680_driver