Additional examples for specific h/w by our interns (#171)
adc/microphone_adc - Read analog values from a microphone and plot the measured sound amplitude. i2c/bmp280_i2c - Read and convert temperature and pressure data from a BMP280 sensor, attached to an I2C bus. i2c/lis3dh_i2c - Read acceleration and temperature value from a LIS3DH sensor via I2C i2c/mcp9808_i2c - Read temperature, set limits and raise alerts when limits are surpassed. i2c/mma8451_i2c - Read acceleration from a MMA8451 accelerometer and set range and precision for the data. i2c/mpl3115a2_i2c - Interface with an MPL3115A2 altimeter, exploring interrupts and advanced board features, via I2C. i2c/oled_i2c - Convert and display a bitmap on a 128x32 SSD1306-driven OLED display i2c/pa1010d_i2c - Read GPS location data, parse and display data via I2C. i2c/pcf8523_i2c - Read time and date values from a real time clock. Set current time and alarms on it. uart/lcd_uart - Display text and symbols on a 16x02 RGB LCD display via UART
This commit is contained in:
12
i2c/bmp280_i2c/CMakeLists.txt
Normal file
12
i2c/bmp280_i2c/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
add_executable(bmp280_i2c
|
||||
bmp280_i2c.c
|
||||
)
|
||||
|
||||
# pull in common dependencies and additional i2c hardware support
|
||||
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)
|
||||
41
i2c/bmp280_i2c/README.adoc
Normal file
41
i2c/bmp280_i2c/README.adoc
Normal file
@@ -0,0 +1,41 @@
|
||||
= Attaching a BMP280 temp/pressure sensor via I2C
|
||||
|
||||
This example code shows how to interface the Raspberry Pi Pico with the popular BMP280 temperature and air pressure sensor manufactured by Bosch. A similar variant, the BME280, exists that can also measure humidity. There is another example that uses the BME280 device but talks to it via SPI as opposed to I2C.
|
||||
|
||||
The code reads data from the sensor's registers every 500 milliseconds and prints it via the onboard UART. This example operates the BMP280 in _normal_ mode, meaning that the device continuously cycles between a measurement period and a standby period at a regular interval we can set. This has the advantage that subsequent reads do not require configuration register writes and is the recommended mode of operation to filter out short-term disturbances.
|
||||
|
||||
[TIP]
|
||||
======
|
||||
The BMP280 is highly configurable with 3 modes of operation, various oversampling levels, and 5 filter settings. Find the datasheet online (https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf) to explore all of its capabilities beyond the simple example given here.
|
||||
======
|
||||
|
||||
== Wiring information
|
||||
|
||||
Wiring up the device requires 4 jumpers, to connect VCC (3.3v), GND, SDA and SCL. The example here uses the default I2C port 0, which is assigned to GPIO 4 (SDA) and 5 (SCL) in software. Power is supplied from the 3.3V pin from the Pico.
|
||||
|
||||
WARNING: The BMP280 has a maximum supply voltage rating of 3.6V. Most breakout boards have voltage regulators that will allow a range of input voltages of 2-6V, but make sure to check beforehand.
|
||||
|
||||
[[bmp280_i2c_wiring]]
|
||||
[pdfwidth=75%]
|
||||
.Wiring Diagram for BMP280 sensor via I2C.
|
||||
image::bmp280_i2c_bb.png[]
|
||||
|
||||
== List of Files
|
||||
|
||||
CMakeLists.txt:: CMake file to incorporate the example into the examples build tree.
|
||||
bmp280_i2c.c:: The example code.
|
||||
|
||||
== Bill of Materials
|
||||
|
||||
.A list of materials required for the example
|
||||
[[bmp280_i2c-bom-table]]
|
||||
[cols=3]
|
||||
|===
|
||||
| *Item* | *Quantity* | Details
|
||||
| Breadboard | 1 | generic part
|
||||
| Raspberry Pi Pico | 1 | https://www.raspberrypi.com/products/raspberry-pi-pico/
|
||||
| BMP280-based breakout board | 1 | https://shop.pimoroni.com/products/bmp280-breakout-temperature-pressure-altitude-sensor[from Pimoroni]
|
||||
| M/M Jumper wires | 4 | generic part
|
||||
|===
|
||||
|
||||
|
||||
252
i2c/bmp280_i2c/bmp280_i2c.c
Normal file
252
i2c/bmp280_i2c/bmp280_i2c.c
Normal file
@@ -0,0 +1,252 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
**/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "hardware/i2c.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
/* Example code to talk to a BMP280 temperature and pressure sensor
|
||||
|
||||
NOTE: Ensure the device is capable of being driven at 3.3v NOT 5v. The Pico
|
||||
GPIO (and therefore I2C) cannot be used at 5v.
|
||||
|
||||
You will need to use a level shifter on the I2C lines if you want to run the
|
||||
board at 5v.
|
||||
|
||||
Connections on Raspberry Pi Pico board, other boards may vary.
|
||||
|
||||
GPIO PICO_DEFAULT_I2C_SDA_PIN (on Pico this is GP4 (pin 6)) -> SDA on BMP280
|
||||
board
|
||||
GPIO PICO_DEFAULT_I2C_SCK_PIN (on Pico this is GP5 (pin 7)) -> SCL on
|
||||
BMP280 board
|
||||
3.3v (pin 36) -> VCC on BMP280 board
|
||||
GND (pin 38) -> GND on BMP280 board
|
||||
*/
|
||||
|
||||
// device has default bus address of 0x76
|
||||
#define ADDR _u(0x76)
|
||||
|
||||
// hardware registers
|
||||
#define REG_CONFIG _u(0xF5)
|
||||
#define REG_CTRL_MEAS _u(0xF4)
|
||||
#define REG_RESET _u(0xE0)
|
||||
|
||||
#define REG_TEMP_XLSB _u(0xFC)
|
||||
#define REG_TEMP_LSB _u(0xFB)
|
||||
#define REG_TEMP_MSB _u(0xFA)
|
||||
|
||||
#define REG_PRESSURE_XLSB _u(0xF9)
|
||||
#define REG_PRESSURE_LSB _u(0xF8)
|
||||
#define REG_PRESSURE_MSB _u(0xF7)
|
||||
|
||||
// calibration registers
|
||||
#define REG_DIG_T1_LSB _u(0x88)
|
||||
#define REG_DIG_T1_MSB _u(0x89)
|
||||
#define REG_DIG_T2_LSB _u(0x8A)
|
||||
#define REG_DIG_T2_MSB _u(0x8B)
|
||||
#define REG_DIG_T3_LSB _u(0x8C)
|
||||
#define REG_DIG_T3_MSB _u(0x8D)
|
||||
#define REG_DIG_P1_LSB _u(0x8E)
|
||||
#define REG_DIG_P1_MSB _u(0x8F)
|
||||
#define REG_DIG_P2_LSB _u(0x90)
|
||||
#define REG_DIG_P2_MSB _u(0x91)
|
||||
#define REG_DIG_P3_LSB _u(0x92)
|
||||
#define REG_DIG_P3_MSB _u(0x93)
|
||||
#define REG_DIG_P4_LSB _u(0x94)
|
||||
#define REG_DIG_P4_MSB _u(0x95)
|
||||
#define REG_DIG_P5_LSB _u(0x96)
|
||||
#define REG_DIG_P5_MSB _u(0x97)
|
||||
#define REG_DIG_P6_LSB _u(0x98)
|
||||
#define REG_DIG_P6_MSB _u(0x99)
|
||||
#define REG_DIG_P7_LSB _u(0x9A)
|
||||
#define REG_DIG_P7_MSB _u(0x9B)
|
||||
#define REG_DIG_P8_LSB _u(0x9C)
|
||||
#define REG_DIG_P8_MSB _u(0x9D)
|
||||
#define REG_DIG_P9_LSB _u(0x9E)
|
||||
#define REG_DIG_P9_MSB _u(0x9F)
|
||||
|
||||
// number of calibration registers to be read
|
||||
#define NUM_CALIB_PARAMS 24
|
||||
|
||||
struct bmp280_calib_param {
|
||||
// temperature params
|
||||
uint16_t dig_t1;
|
||||
int16_t dig_t2;
|
||||
int16_t dig_t3;
|
||||
|
||||
// pressure params
|
||||
uint16_t dig_p1;
|
||||
int16_t dig_p2;
|
||||
int16_t dig_p3;
|
||||
int16_t dig_p4;
|
||||
int16_t dig_p5;
|
||||
int16_t dig_p6;
|
||||
int16_t dig_p7;
|
||||
int16_t dig_p8;
|
||||
int16_t dig_p9;
|
||||
};
|
||||
|
||||
#ifdef i2c_default
|
||||
void bmp280_init() {
|
||||
// use the "handheld device dynamic" optimal setting (see datasheet)
|
||||
uint8_t buf[2];
|
||||
|
||||
// 500ms sampling time, x16 filter
|
||||
const uint8_t reg_config_val = ((0x04 << 5) | (0x05 << 2)) & 0xFC;
|
||||
|
||||
// send register number followed by its corresponding value
|
||||
buf[0] = REG_CONFIG;
|
||||
buf[1] = reg_config_val;
|
||||
i2c_write_blocking(i2c_default, ADDR, buf, 2, false);
|
||||
|
||||
// osrs_t x1, osrs_p x4, normal mode operation
|
||||
const uint8_t reg_ctrl_meas_val = (0x01 << 5) | (0x03 << 2) | (0x03);
|
||||
buf[0] = REG_CTRL_MEAS;
|
||||
buf[1] = reg_ctrl_meas_val;
|
||||
i2c_write_blocking(i2c_default, ADDR, buf, 2, false);
|
||||
}
|
||||
|
||||
void bmp280_read_raw(int32_t* temp, int32_t* pressure) {
|
||||
// BMP280 data registers are auto-incrementing and we have 3 temperature and
|
||||
// pressure registers each, so we start at 0xF7 and read 6 bytes to 0xFC
|
||||
// note: normal mode does not require further ctrl_meas and config register writes
|
||||
|
||||
uint8_t buf[6];
|
||||
i2c_write_blocking(i2c_default, ADDR, (uint8_t*)REG_PRESSURE_MSB, 1, true); // true to keep master control of bus
|
||||
i2c_read_blocking(i2c_default, ADDR, buf, 6, false); // false - finished with bus
|
||||
|
||||
// store the 20 bit read in a 32 bit signed integer for conversion
|
||||
*pressure = (buf[0] << 12) | (buf[1] << 4) | (buf[2] >> 4);
|
||||
*temp = (buf[3] << 12) | (buf[4] << 4) | (buf[5] >> 4);
|
||||
}
|
||||
|
||||
void bmp280_reset() {
|
||||
// reset the device with the power-on-reset procedure
|
||||
uint8_t buf[2] = { REG_RESET, 0xB6 };
|
||||
i2c_write_blocking(i2c_default, ADDR, buf, 2, false);
|
||||
}
|
||||
|
||||
// intermediate function that calculates the fine resolution temperature
|
||||
// used for both pressure and temperature conversions
|
||||
int32_t bmp280_convert(int32_t temp, struct bmp280_calib_param* params) {
|
||||
// use the 32-bit fixed point compensation implementation given in the
|
||||
// datasheet
|
||||
|
||||
int32_t var1, var2;
|
||||
var1 = ((((temp >> 3) - ((int32_t)params->dig_t1 << 1))) * ((int32_t)params->dig_t2)) >> 11;
|
||||
var2 = (((((temp >> 4) - ((int32_t)params->dig_t1)) * ((temp >> 4) - ((int32_t)params->dig_t1))) >> 12) * ((int32_t)params->dig_t3)) >> 14;
|
||||
return var1 + var2;
|
||||
}
|
||||
|
||||
int32_t bmp280_convert_temp(int32_t temp, struct bmp280_calib_param* params) {
|
||||
// uses the BMP280 calibration parameters to compensate the temperature value read from its registers
|
||||
int32_t t_fine = bmp280_convert(temp, params);
|
||||
return (t_fine * 5 + 128) >> 8;
|
||||
}
|
||||
|
||||
int32_t bmp280_convert_pressure(int32_t pressure, int32_t temp, struct bmp280_calib_param* params) {
|
||||
// uses the BMP280 calibration parameters to compensate the pressure value read from its registers
|
||||
|
||||
int32_t t_fine = bmp280_convert(temp, params);
|
||||
|
||||
int32_t var1, var2;
|
||||
uint32_t converted = 0.0;
|
||||
var1 = (((int32_t)t_fine) >> 1) - (int32_t)64000;
|
||||
var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * ((int32_t)params->dig_p6);
|
||||
var2 += ((var1 * ((int32_t)params->dig_p5)) << 1);
|
||||
var2 = (var2 >> 2) + (((int32_t)params->dig_p4) << 16);
|
||||
var1 = (((params->dig_p3 * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) + ((((int32_t)params->dig_p2) * var1) >> 1)) >> 18;
|
||||
var1 = ((((32768 + var1)) * ((int32_t)params->dig_p1)) >> 15);
|
||||
if (var1 == 0) {
|
||||
return 0; // avoid exception caused by division by zero
|
||||
}
|
||||
converted = (((uint32_t)(((int32_t)1048576) - pressure) - (var2 >> 12))) * 3125;
|
||||
if (converted < 0x80000000) {
|
||||
converted = (converted << 1) / ((uint32_t)var1);
|
||||
} else {
|
||||
converted = (converted / (uint32_t)var1) * 2;
|
||||
}
|
||||
var1 = (((int32_t)params->dig_p9) * ((int32_t)(((converted >> 3) * (converted >> 3)) >> 13))) >> 12;
|
||||
var2 = (((int32_t)(converted >> 2)) * ((int32_t)params->dig_p8)) >> 13;
|
||||
converted = (uint32_t)((int32_t)converted + ((var1 + var2 + params->dig_p7) >> 4));
|
||||
return converted;
|
||||
}
|
||||
|
||||
void bmp280_get_calib_params(struct bmp280_calib_param* params) {
|
||||
// raw temp and pressure values need to be calibrated according to
|
||||
// parameters generated during the manufacturing of the sensor
|
||||
// there are 3 temperature params, and 9 pressure params, each with a LSB
|
||||
// and MSB register, so we read from 24 registers
|
||||
|
||||
uint8_t buf[NUM_CALIB_PARAMS] = { 0 };
|
||||
i2c_write_blocking(i2c_default, ADDR, (uint8_t*)REG_DIG_T1_LSB, 1, true); // true to keep master control of bus
|
||||
// read in one go as register addresses auto-increment
|
||||
i2c_read_blocking(i2c_default, ADDR, buf, NUM_CALIB_PARAMS, false); // false, we're done reading
|
||||
|
||||
// store these in a struct for later use
|
||||
params->dig_t1 = (uint16_t)(buf[1] << 8) | buf[0];
|
||||
params->dig_t2 = (int16_t)(buf[3] << 8) | buf[2];
|
||||
params->dig_t3 = (int16_t)(buf[5] << 8) | buf[4];
|
||||
|
||||
params->dig_p1 = (uint16_t)(buf[7] << 8) | buf[6];
|
||||
params->dig_p2 = (int16_t)(buf[9] << 8) | buf[8];
|
||||
params->dig_p3 = (int16_t)(buf[11] << 8) | buf[10];
|
||||
params->dig_p4 = (int16_t)(buf[13] << 8) | buf[12];
|
||||
params->dig_p5 = (int16_t)(buf[15] << 8) | buf[14];
|
||||
params->dig_p6 = (int16_t)(buf[17] << 8) | buf[16];
|
||||
params->dig_p7 = (int16_t)(buf[19] << 8) | buf[18];
|
||||
params->dig_p8 = (int16_t)(buf[21] << 8) | buf[20];
|
||||
params->dig_p9 = (int16_t)(buf[23] << 8) | buf[22];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// useful information for picotool
|
||||
bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C));
|
||||
bi_decl(bi_program_description("BMP280 I2C example for the Raspberry Pi Pico"));
|
||||
|
||||
#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN)
|
||||
#warning i2c / bmp280_i2c example requires a board with I2C pins
|
||||
puts("Default I2C pins were not defined");
|
||||
#else
|
||||
printf("Hello, BMP280! Reading temperaure and pressure values from sensor...\n");
|
||||
|
||||
// I2C is "open drain", pull ups to keep signal high when no data is being sent
|
||||
i2c_init(i2c_default, 100 * 1000);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
|
||||
gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
|
||||
|
||||
// configure BMP280
|
||||
bmp280_init();
|
||||
|
||||
// retrieve fixed compensation params
|
||||
struct bmp280_calib_param params;
|
||||
bmp280_get_calib_params(¶ms);
|
||||
|
||||
int32_t raw_temperature;
|
||||
int32_t raw_pressure;
|
||||
|
||||
sleep_ms(250); // sleep so that data polling and register update don't collide
|
||||
while (1) {
|
||||
bmp280_read_raw(&raw_temperature, &raw_pressure);
|
||||
int32_t temperature = bmp280_convert_temp(raw_temperature, ¶ms);
|
||||
int32_t pressure = bmp280_convert_pressure(raw_pressure, raw_temperature, ¶ms);
|
||||
printf("Pressure = %.3f kPa\n", pressure / 1000.f);
|
||||
printf("Temp. = %.2f C\n", temperature / 100.f);
|
||||
// poll every 500ms
|
||||
sleep_ms(500);
|
||||
}
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
BIN
i2c/bmp280_i2c/bmp280_i2c.fzz
Normal file
BIN
i2c/bmp280_i2c/bmp280_i2c.fzz
Normal file
Binary file not shown.
BIN
i2c/bmp280_i2c/bmp280_i2c_bb.png
Normal file
BIN
i2c/bmp280_i2c/bmp280_i2c_bb.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 151 KiB |
Reference in New Issue
Block a user