初めてのRaspberry Pi Pico ③ C言語で気圧センサBMP280（I2Cバス)　ver.2

　pico-examplesには気圧センサBME280（SPIバス）のコードが入っています。BMP280はその前のモデルに当たり、温度と気圧が測れます。内部のレジスタはBME280と同じで、湿度関連が省略されています。したがって、BME280のコードから湿度関連を削除し、SPIバスのアクセスをI2Cバス用に書き換えます。

　開発環境はラズパイ4です。入れ忘れているかもしれないのでToolchainをインストールします。

```
sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential
```

###標準出力を用意

　最初に、printfの出力先を用意します。
　　https://datasheets.raspberrypi.org/pico/getting-started-with-pico.pdf
の「Chapter 4. Saying "Hello World" in C」を実行できるようにします。すでにbiuldされたコードがありました。通信用ソフトminicomもインストールされていました。raspi-configで行うUARTの変更もすんでいます。
　次のようにUARTを接続します。


|Pico UART0名称 |Pico 物理的ピン番号 |Pico GPIOピン番号|ラズパイの物理ピン番号|
|:-----------|:-----------:|:-----------:|:-----------:|
|GND |3 |GND|6(GND)
|UART0_TX |1 |GP0|10(Rx)
|UART0_RX |2 |GP1|8(Tx)

　ターミナルを一つ立ち上げて、次のコマンドで通信ソフトを動かします。

```shell
minicom -b 115200 -o -D /dev/serial0
```
　BOOTSELボタンを押したままUSBケーブルをつなげます。つながったら、BOOTSELから手を放します。RPI-RP2ドライブがマウントされます。
　pico\pico-examples\build\hello_world\serialに入っているhello_serial.uf2を、デスクトップに表示されているRPI-RP2ドライブにドラッグします。
　minicomの動いているターミナルに、文字が表示されたことを確認します。このままターミナルで通信ソフトは動かしたままにします。

<img width="400" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/cddb9fd7-a1fa-b76e-8844-87887b00cb88.png">





###BMP280の接続
　AdafruitのSTEMMA QTのBMP280を使います。I2Cで使うようにコネクタがついています。ほかのボードでもかまいません。ジャンパなどがあればI2Cバスを有効にします。このボードはスレーブ・アドレスが0x77に設定されています。入手したボードが0x76の場合は、該当部分を変更してください。

<img width="300" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/64e67df0-e1d6-36c3-5985-dc4c77daafd0.png">


　ケーブルの色は自作なので、白色の代わりに黄色になっています。ほかのピンは純正（Adafruit）と同じ色です。

|STEMMA QTコネクタ |物理的ピン番号 |GPIOピン番号|
|:-----------|------------:|:------------:|
|SCL 緑 |12 |GP9|
|SDA 黄|11 |GP8|
|Vcc 赤|ラズパイの3.3Vへ |-|
|GND 黒|ラズパイのGNDへ |-|

　pico/pico-examples/i2c/bus_scanに入っているbus_scan.cのピン番号を変更します。

```cpp
int main() {
    // Enable UART so we can print status output
    stdio_init_all();

    // This example will use I2C0 on GPIO4 (SDA) and GPIO5 (SCL)
    i2c_init(i2c0, 100 * 1000);
    gpio_set_function(8, GPIO_FUNC_I2C);
    gpio_set_function(9, GPIO_FUNC_I2C);
    gpio_pull_up(8);
    gpio_pull_up(9);
```
　makefileはすでにできているので、cmakeは動かしません。
　/home/pi/pico/pico-examples/build/i2c/bus_scanに移動して、

```
make -j4
```

します。
　BOOTSELボタンを押したままUSBケーブルをつなげます（抜き差し）。
　pico\pico-examples\build\i2c\bus_scanに入っているi2c_bus_scan.uf2を、デスクトップに表示されているRPI-RP2ドライブにドラッグします。
　minicomの動いているターミナルに、スレーブ・アドレスの0x77の位置に＠が表示されたことを確認します。

<img width="450" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/f3872abd-4e78-72a4-5cd1-2c251190af72.png">



###BMP280のプログラム
　I2Cバスなので、ソースは、/home/pi/pico/pico-examples/i2c/の中にbmp280_i2cフォルダを作り、bmp280_i2c.cのソースを入れました。隣のbus_scanのCMakeLists.txtをコピペし、内容を次のように変更し、保存します。つまり、このフォルダには、ソースのbmp280_i2c.c（最後に掲載）とCMakeLists.txtが存在します。

```cpp
add_executable(bmp280_i2c
        bmp280_i2c.c
        )

# Pull in our (to be renamed) simple get you started dependencies
target_link_libraries(bmp280_i2c pico_stdlib hardware_i2c)

# create map/bin/hex file etc.
pico_add_extra_outputs(bmp280_i2c)

# add url via pico_set_program_url
example_auto_set_url(bmp280_i2c)
```


　/home/pi/pico/pico-examples/build/i2cディレクトリで、cmake_install.cmakeのファイルを開き、endifの前に1行追加します。

```
if(NOT CMAKE_INSTALL_LOCAL_ONLY)
  # Include the install script for each subdirectory.
  include("/home/pi/pico/pico-examples/build/i2c/bus_scan/cmake_install.cmake")
  include("/home/pi/pico/pico-examples/build/i2c/lcd_1602_i2c/cmake_install.cmake")
  include("/home/pi/pico/pico-examples/build/i2c/mpu6050_i2c/cmake_install.cmake")
  include("/home/pi/pico/pico-examples/build/i2c/lcd_1602_i2c/cmake_install.cmake")
  include("/home/pi/pico/pico-examples/build/i2c/bmp280_i2c/cmake_install.cmake")
endif()
```

　/home/pi/pico/pico-examples/buildで、

```
export PICO_SDK_PATH=../../pico-sdk
cmake ..
```

　/home/pi/pico/pico-examples/build/i2c/bmp280_i2cの中にmakeファイルが作られます。
/home/pi/pico/pico-examples/build/i2c/bmp280_i2cディレクトリに移動して、

```
make -j4
```
を実行しビルドします。
　BOOTSELボタンを押したままUSBケーブルをつなげます（抜き差し）。
　pico\pico-examples\build\i2c\bmp280_i2cに入っているbmp280.uf2を、デスクトップに表示されているRPI-RP2ドライブにドラッグします。

<img width="450" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/98322696-3c8b-7b7b-028b-0afdef15c137.png">



　ソースです。ベースはBME280（SPIバス用）です。

```cpp
/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"

/* Example code to talk to a bmp280 temperature/pressure sensor.

   NOTE: Ensure the device is capable of being driven at 3.3v NOT 5v. The Pico
   GPIO (and therefor SPI) cannot be used at 5v.

   GPIO 8 (pin 11)-> SDA on bmp280 board
   GPIO 9 (pin 12)-> SCL on bmp280 board
   3.3v (pin 3;6) -> VCC on bmp280 board
   GND (pin 38)  -> GND on bmp280 board

*/

static int addr = 0x77;

#define I2C_PORT i2c0
#define ID_register 0xd0
#define READ_BIT 0x80

int32_t t_fine;

uint16_t dig_T1;
int16_t dig_T2, dig_T3;
uint16_t dig_P1;
int16_t dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9;


/* The following compensation functions are required to convert from the raw ADC
data from the chip to something usable. Each chip has a different set of
compensation parameters stored on the chip at point of manufacture, which are
read from the chip at startup and used inthese routines.
*/
int32_t compensate_temp(int32_t adc_T) {
    int32_t var1, var2, T;
    var1 = ((((adc_T >> 3) - ((int32_t) dig_T1 << 1))) * ((int32_t) dig_T2)) >> 11;
    var2 = (((((adc_T >> 4) - ((int32_t) dig_T1)) * ((adc_T >> 4) - ((int32_t) dig_T1))) >> 12) * ((int32_t) dig_T3))
            >> 14;

    t_fine = var1 + var2;
    T = (t_fine * 5 + 128) >> 8;
    return T;
}

uint32_t compensate_pressure(int32_t adc_P) {
    int32_t var1, var2;
    uint32_t p;
    var1 = (((int32_t) t_fine) >> 1) - (int32_t) 64000;
    var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * ((int32_t) dig_P6);
    var2 = var2 + ((var1 * ((int32_t) dig_P5)) << 1);
    var2 = (var2 >> 2) + (((int32_t) dig_P4) << 16);
    var1 = (((dig_P3 * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) + ((((int32_t) dig_P2) * var1) >> 1)) >> 18;
    var1 = ((((32768 + var1)) * ((int32_t) dig_P1)) >> 15);
    if (var1 == 0)
        return 0;

    p = (((uint32_t) (((int32_t) 1048576) - adc_P) - (var2 >> 12))) * 3125;
    if (p < 0x80000000)
        p = (p << 1) / ((uint32_t) var1);
    else
        p = (p / (uint32_t) var1) * 2;

    var1 = (((int32_t) dig_P9) * ((int32_t) (((p >> 3) * (p >> 3)) >> 13))) >> 12;
    var2 = (((int32_t) (p >> 2)) * ((int32_t) dig_P8)) >> 13;
    p = (uint32_t) ((int32_t) p + ((var1 + var2 + dig_P7) >> 4));

    return p;
}


static void write_register(uint8_t reg, uint8_t data) {
    uint8_t buf[2];
    //buf[0] = reg & 0x7f;  // remove read bit as this is a write
    buf[0] = reg;
    buf[1] = data;
    i2c_write_blocking(I2C_PORT, addr, buf, 2, true);
}

static void read_registers(uint8_t reg, uint8_t *buf, uint16_t len) {
    // For this particular device, we send the device the register we want to read
    // first, then subsequently read from the device. The register is auto incrementing
    // so we don't need to keep sending the register we want, just the first.
    reg = reg | READ_BIT;
    i2c_write_blocking(I2C_PORT, addr, &reg, 1, true); 
    i2c_read_blocking(I2C_PORT, addr, buf, len, false);
}

/* This function reads the manufacturing assigned compensation parameters from the device */
void read_compensation_parameters() {
    uint8_t buffer[26];

    read_registers(0x88, buffer, 24);

    dig_T1 = buffer[0] | (buffer[1] << 8);
    dig_T2 = buffer[2] | (buffer[3] << 8);
    dig_T3 = buffer[4] | (buffer[5] << 8);

    dig_P1 = buffer[6] | (buffer[7] << 8);
    dig_P2 = buffer[8] | (buffer[9] << 8);
    dig_P3 = buffer[10] | (buffer[11] << 8);
    dig_P4 = buffer[12] | (buffer[13] << 8);
    dig_P5 = buffer[14] | (buffer[15] << 8);
    dig_P6 = buffer[16] | (buffer[17] << 8);
    dig_P7 = buffer[18] | (buffer[19] << 8);
    dig_P8 = buffer[20] | (buffer[21] << 8);
    dig_P9 = buffer[22] | (buffer[23] << 8);

}

static void bmp280_read_raw(int32_t *pressure, int32_t *temperature) {
    uint8_t buffer[6];
    read_registers(0xF7, buffer, 6);
//    printf("\nraw PressureData = %d %d\n", buffer[0],buffer[1]);
    *pressure = ((uint32_t) buffer[0] << 12) | ((uint32_t) buffer[1] << 4) | (buffer[2] >> 4);
    *temperature = ((uint32_t) buffer[3] << 12) | ((uint32_t) buffer[4] << 4) | (buffer[5] >> 4);
//        printf("\nraw Pressure = %d\n", pressure);
//        printf("raw Temp. = %d\n", temperature);
}

int main() {
    stdio_init_all();

    printf("Hello, bmp280! Reading raw data from registers via i2c...\n");

    // This example will use I2C0 on GPIO8 (SDA) and GPIO9 (SCL) running at 100kHz.
    i2c_init(I2C_PORT, 100 * 1000);
    gpio_set_function(8, GPIO_FUNC_I2C);
    gpio_set_function(9, GPIO_FUNC_I2C);
    gpio_pull_up(8);
    gpio_pull_up(9);

    // interrograte the device for its I2C ID number, should be 0x58
    uint8_t id;
    read_registers(ID_register, &id, 1);
    printf("\nChip ID is 'maybe 0x58' 0x%x\n", id);

    read_compensation_parameters();

    write_register(0xF5, 0x20);
    write_register(0xF4, 0x27);// Set rest of oversampling modes and run mode to normal

    int32_t pressure, temperature;

    while (1) {
        bmp280_read_raw(&pressure, &temperature);


        // These are the raw numbers from the chip, so we need to run through the
        // compensations to get human understandable numbers
        pressure = compensate_pressure(pressure);
        temperature = compensate_temp(temperature);

        printf("Pressure = %.1fhPa\n", pressure / 100.0);
        printf("Temp. = %.2fC\n", temperature / 100.0);

        sleep_ms(5000);
    }

    return 0;
}
```