1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-01-30

 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)

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

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の動いているターミナルに、文字が表示されたことを確認します。このままターミナルで通信ソフトは動かしたままにします。

ファイル名

BMP280の接続

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

ファイル名

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

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

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

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の位置に@が表示されたことを確認します。

ファイル名

BMP280のプログラム

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

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ドライブにドラッグします。

ファイル名

 ソースです。ベースはBME280(SPIバス用)です。

/**
 * 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;
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?