-
Notifications
You must be signed in to change notification settings - Fork 910
Add example for MPL3115A2 altimeter #136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
aallan
merged 11 commits into
raspberrypi:merge-intern-examples
from
matiasilva:mpl3115a2-altimeter
Oct 14, 2021
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
6b0b5b6
Bootstrap example
374e394
Add Fritzing schematic
9e82afa
Add example to general README
fc76e10
Add sensor initialization
ba7b1d3
Flesh out documentation
aaaa5cb
Fix typo
965a1e3
Push latest changes
90289fe
Improve signed integer calculations
19ec70f
Polish sensor value conversions
b9c0322
Fix typo
2199a83
Remove unused comment
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
if (NOT PICO_NO_HARDWARE) | ||
add_subdirectory(bus_scan) | ||
add_subdirectory(lcd_1602_i2c) | ||
add_subdirectory(mpl3115a2_i2c) | ||
add_subdirectory(mpu6050_i2c) | ||
endif () |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
add_executable(mpl3115a2_i2c | ||
mpl3115a2_i2c.c | ||
) | ||
|
||
# pull in common dependencies and additional i2c hardware support | ||
target_link_libraries(mpl3115a2_i2c pico_stdlib hardware_i2c) | ||
|
||
# create map/bin/hex file etc. | ||
pico_add_extra_outputs(mpl3115a2_i2c) | ||
|
||
# add url via pico_set_program_url | ||
example_auto_set_url(mpl3115a2_i2c) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
= Attaching an MPL3115A2 altimeter via I2C | ||
|
||
This example code shows how to interface the Raspberry Pi Pico to an MPL3115A2 altimeter via I2C. The MPL3115A2 has onboard pressure and temperature sensors which are used to estimate the altitude. In comparison to the BMP- family of pressure and temperature sensors, the MPL3115A2 has two interrupt pins for ultra low power operation and takes care of the sensor reading compensation on the board! It also has multiple modes of operation and impressive operating conditions. | ||
|
||
The board used in this example https://www.adafruit.com/product/1893[comes from Adafruit], but any MPL3115A2 breakouts should work similarly. | ||
|
||
The MPL3115A2 makes available two ways of reading its temperature and pressure data. The first is known as polling, where the Pico will continuously read data out of a set of auto-incrementing registers which are refreshed with new data every so often. The second, which this example will demonstrate, uses a 160-byte first-in-first-out (FIFO) queue and configurable interrupts to tell the Pico when to read data. More information regarding when the interrupts can be triggered available https://www.nxp.com/docs/en/data-sheet/MPL3115A2.pdf[in the datasheet]. This example waits for the 32 sample FIFO to overflow, detects this via an interrupt pin, and then averages the 32 samples taken. The sensor is configured to take a sample every second. | ||
|
||
Bit math is used to convert the temperature and altitude data from the raw bits collected in the registers. Take the temperature calculation as an example: it is a 12-bit signed number with 8 integer bits and 4 fractional bits. First, we read the 2 8-bit registers and store them in a buffer. Then, we concatenate them into one unsigned 16-bit integer starting with the OUT_T_MSB register, thus making sure that the last bit of this register is aligned with the MSB in our 16 bit unsigned integer so it is correctly interpreted as the signed bit when we later cast this to a signed 16-bit integer. Finally, the entire number is converted to a float implicitly when we multiply it by 1/2^8 to shift it 8 bits to the right of the decimal point. Though only the last 4 bits of the OUT_T_LSB register hold data, this does not matter as the remaining 4 are held at zero and "disappear" when we shift the decimal point left by 8. Similar logic is applied to the altitude calculation. | ||
|
||
TIP: Choosing the right sensor for your project among so many choices can be hard! There are multiple factors you may have to consider in addition to any constraints imposed on you. Cost, operating temperature, sensor resolution, power consumption, ease of use, communication protocols and supply voltage are all but a few factors that can play a role in sensor choice. For most hobbyist purposes though, the majority of sensors out there will do just fine! | ||
|
||
== Wiring information | ||
|
||
Wiring up the device requires 5 jumpers, to connect VCC (3.3v), GND, INT1, SDA and SCL. The example here uses I2C port 0, which is assigned to GPIO 4 (SDA) and GPIO 5 (SCL) by default. Power is supplied from the 3.3V pin. | ||
|
||
NOTE: The MPL3115A2 has a 1.6-3.6V voltage supply range. This means it can work with the Pico's 3.3v pins out of the box but our Adafruit breakout has an onboard voltage regulator for good measure. This may not always be true of other sensors, though. | ||
|
||
[[mpl3115a2_i2c_wiring]] | ||
[pdfwidth=75%] | ||
.Wiring Diagram for MPL3115A2 altimeter. | ||
image::mpl3115a2_i2c_bb.png[] | ||
|
||
== List of Files | ||
|
||
CMakeLists.txt:: CMake file to incorporate the example in to the examples build tree. | ||
mpl3115a2_i2c.c:: The example code. | ||
|
||
== Bill of Materials | ||
|
||
.A list of materials required for the example | ||
[[mpl3115a2-i2c-bom-table]] | ||
[cols=3] | ||
|=== | ||
| *Item* | *Quantity* | Details | ||
| Breadboard | 1 | generic part | ||
| Raspberry Pi Pico | 1 | http://raspberrypi.org/ | ||
| MPL3115A2 altimeter | 1 | https://www.adafruit.com/product/1893[Adafruit] | ||
| M/M Jumper wires | 5 | generic part | ||
|=== |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
/** | ||
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd. | ||
* | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
|
||
#include <stdio.h> | ||
#include "pico/stdlib.h" | ||
#include "pico/binary_info.h" | ||
#include "hardware/gpio.h" | ||
#include "hardware/i2c.h" | ||
|
||
/* Example code to talk to an MPL3115A2 altimeter sensor via I2C | ||
|
||
See accompanying documentation in README.adoc or the C++ SDK booklet. | ||
|
||
Connections on Raspberry Pi Pico board, other boards may vary. | ||
|
||
GPIO PICO_DEFAULT_I2C_SDA_PIN (On Pico this is 4 (pin 6)) -> SDA on MPL3115A2 board | ||
GPIO PICO_DEFAULT_I2C_SCK_PIN (On Pico this is 5 (pin 7)) -> SCL on MPL3115A2 board | ||
GPIO 16 -> INT1 on MPL3115A2 board | ||
3.3v (pin 36) -> VCC on MPL3115A2 board | ||
GND (pin 38) -> GND on MPL3115A2 board | ||
*/ | ||
|
||
// 7-bit address | ||
#define ADDR 0x60 | ||
#define INT1_PIN _u(16) | ||
|
||
// following definitions only valid for F_MODE > 0 (ie. if FIFO enabled) | ||
#define MPL3115A2_F_DATA _u(0x01) | ||
#define MPL3115A2_F_STATUS _u(0x00) | ||
#define MPL3115A2_F_SETUP _u(0x0F) | ||
#define MPL3115A2_INT_SOURCE _u(0x12) | ||
#define MPL3115A2_CTRLREG1 _u(0x26) | ||
#define MPL3115A2_CTRLREG2 _u(0x27) | ||
#define MPL3115A2_CTRLREG3 _u(0x28) | ||
#define MPL3115A2_CTRLREG4 _u(0x29) | ||
#define MPL3115A2_CTRLREG5 _u(0x2A) | ||
#define MPL3115A2_PT_DATA_CFG _u(0x13) | ||
#define MPL3115A2_OFF_P _u(0x2B) | ||
#define MPL3115A2_OFF_T _u(0x2C) | ||
#define MPL3115A2_OFF_H _u(0x2D) | ||
|
||
#define MPL3115A2_FIFO_DISABLED _u(0x00) | ||
#define MPL3115A2_FIFO_STOP_ON_OVERFLOW _u(0x80) | ||
#define MPL3115A2_FIFO_SIZE 32 | ||
#define MPL3115A2_DATA_BATCH_SIZE 5 | ||
#define MPL3115A2_ALTITUDE_NUM_REGS 3 | ||
#define MPL3115A2_ALTITUDE_INT_SIZE 20 | ||
#define MPL3115A2_TEMPERATURE_INT_SIZE 12 | ||
#define MPL3115A2_NUM_FRAC_BITS 4 | ||
|
||
#define PARAM_ASSERTIONS_ENABLE_I2C 1 | ||
|
||
volatile uint8_t fifo_data[MPL3115A2_FIFO_SIZE * MPL3115A2_DATA_BATCH_SIZE]; | ||
volatile bool has_new_data = false; | ||
|
||
struct mpl3115a2_data_t { | ||
// Q8.4 fixed point | ||
float temperature; | ||
// Q16.4 fixed-point | ||
float altitude; | ||
}; | ||
|
||
void copy_to_vbuf(uint8_t buf1[], volatile uint8_t buf2[], int buflen) { | ||
for (size_t i = 0; i < buflen; i++) { | ||
buf2[i] = buf1[i]; | ||
} | ||
} | ||
|
||
#ifdef i2c_default | ||
void mpl3115a2_read_fifo(volatile uint8_t fifo_buf[]) { | ||
// drains the 160 byte FIFO | ||
uint8_t reg = MPL3115A2_F_DATA; | ||
uint8_t buf[MPL3115A2_FIFO_SIZE * MPL3115A2_DATA_BATCH_SIZE]; | ||
i2c_write_blocking(i2c_default, ADDR, ®, 1, true); | ||
// burst read 160 bytes from fifo | ||
i2c_read_blocking(i2c_default, ADDR, buf, MPL3115A2_FIFO_SIZE * MPL3115A2_DATA_BATCH_SIZE, false); | ||
copy_to_vbuf(buf, fifo_buf, MPL3115A2_FIFO_SIZE * MPL3115A2_DATA_BATCH_SIZE); | ||
} | ||
|
||
uint8_t mpl3115a2_read_reg(uint8_t reg) { | ||
uint8_t read; | ||
i2c_write_blocking(i2c_default, ADDR, ®, 1, true); // keep control of bus | ||
i2c_read_blocking(i2c_default, ADDR, &read, 1, false); | ||
return read; | ||
} | ||
|
||
void mpl3115a2_init() { | ||
// set as altimeter with oversampling ratio of 128 | ||
uint8_t buf[] = { MPL3115A2_CTRLREG1, 0xB8 }; | ||
i2c_write_blocking(i2c_default, ADDR, buf, 2, false); | ||
|
||
// set data refresh every 2 seconds, 0 next bits as we're not using those interrupts | ||
buf[0] = MPL3115A2_CTRLREG2, buf[1] = 0x00; | ||
i2c_write_blocking(i2c_default, ADDR, buf, 2, false); | ||
|
||
// set both interrupts pins to active low and enable internal pullups | ||
buf[0] = MPL3115A2_CTRLREG3, buf[1] = 0x01; | ||
i2c_write_blocking(i2c_default, ADDR, buf, 2, false); | ||
|
||
// enable FIFO interrupt | ||
buf[0] = MPL3115A2_CTRLREG4, buf[1] = 0x40; | ||
i2c_write_blocking(i2c_default, ADDR, buf, 2, false); | ||
|
||
// tie FIFO interrupt to pin INT1 | ||
buf[0] = MPL3115A2_CTRLREG5, buf[1] = 0x40; | ||
i2c_write_blocking(i2c_default, ADDR, buf, 2, false); | ||
|
||
// set p, t and h offsets here if needed | ||
// eg. 2's complement number: 0xFF subtracts 1 meter | ||
//buf[0] = MPL3115A2_OFF_H, buf[1] = 0xFF; | ||
//i2c_write_blocking(i2c_default, ADDR, buf, 2, false); | ||
|
||
// do not accept more data on FIFO overflow | ||
buf[0] = MPL3115A2_F_SETUP, buf[1] = MPL3115A2_FIFO_STOP_ON_OVERFLOW; | ||
i2c_write_blocking(i2c_default, ADDR, buf, 2, false); | ||
|
||
// set device active | ||
buf[0] = MPL3115A2_CTRLREG1, buf[1] = 0xB9; | ||
i2c_write_blocking(i2c_default, ADDR, buf, 2, false); | ||
} | ||
|
||
void gpio_callback(uint gpio, uint32_t events) { | ||
// if we had enabled more than 2 interrupts on same pin, then we should read | ||
// INT_SOURCE reg to find out which interrupt triggered | ||
|
||
// we can filter by which GPIO was triggered | ||
if (gpio == INT1_PIN) { | ||
// FIFO overflow interrupt | ||
// watermark bits set to 0 in F_SETUP reg, so only possible event is an overflow | ||
// otherwise, we would read F_STATUS to confirm it was an overflow | ||
printf("FIFO overflow!\n"); | ||
// drain the fifo | ||
mpl3115a2_read_fifo(fifo_data); | ||
// read status register to clear interrupt bit | ||
mpl3115a2_read_reg(MPL3115A2_F_STATUS); | ||
has_new_data = true; | ||
} | ||
} | ||
#endif | ||
|
||
void mpl3115a2_convert_fifo_batch(uint8_t start, volatile uint8_t buf[], struct mpl3115a2_data_t* data) { | ||
// convert a batch of fifo data into temperature and altitude data | ||
|
||
// 3 altitude registers: MSB (8 bits), CSB (8 bits) and LSB (4 bits, starting from MSB) | ||
// first two are integer bits (2's complement) and LSB is fractional bits -> makes 20 bit signed integer | ||
int32_t h = (int32_t)((uint32_t)buf[start] << 24 | buf[start + 1] << 16 | buf[start + 2] << 8); | ||
data->altitude = h * 1.f / 65536; | ||
|
||
// 2 temperature registers: MSB (8 bits) and LSB (4 bits, starting from MSB) | ||
// first 8 are integer bits with sign and LSB is fractional bits -> 12 bit signed integer | ||
int16_t t = (int16_t)(((uint16_t)buf[start + 3]) << 8 | buf[start + 4]); | ||
data->temperature = t * 1.f / 256; | ||
} | ||
|
||
int main() { | ||
stdio_init_all(); | ||
#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN) | ||
#warning i2c / mpl3115a2_i2c example requires a board with I2C pins | ||
puts("Default I2C pins were not defined"); | ||
#else | ||
printf("Hello, MPL3115A2. Waiting for something to interrupt me!...\n"); | ||
|
||
// use default I2C0 at 400kHz, I2C is active low | ||
i2c_init(i2c_default, 400 * 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); | ||
|
||
gpio_init(INT1_PIN); | ||
gpio_pull_up(INT1_PIN); // pull it up even more! | ||
|
||
// add program information for picotool | ||
bi_decl(bi_program_name("Example in the pico-examples library for the MPL3115A2 altimeter")); | ||
bi_decl(bi_1pin_with_name(16, "Interrupt pin 1")); | ||
bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C)); | ||
|
||
mpl3115a2_init(); | ||
|
||
gpio_set_irq_enabled_with_callback(INT1_PIN, GPIO_IRQ_LEVEL_LOW, true, &gpio_callback); | ||
|
||
while (1) { | ||
// as interrupt data comes in, let's print the 32 sample average | ||
if (has_new_data) { | ||
float tsum = 0, hsum = 0; | ||
struct mpl3115a2_data_t data; | ||
for (int i = 0; i < MPL3115A2_FIFO_SIZE; i++) { | ||
mpl3115a2_convert_fifo_batch(i * MPL3115A2_DATA_BATCH_SIZE, fifo_data, &data); | ||
tsum += data.temperature; | ||
hsum += data.altitude; | ||
} | ||
printf("%d sample average -> t: %.4f C, h: %.4f m\n", MPL3115A2_FIFO_SIZE, tsum / MPL3115A2_FIFO_SIZE, hsum / MPL3115A2_FIFO_SIZE); | ||
has_new_data = false; | ||
} | ||
sleep_ms(10); | ||
}; | ||
|
||
#endif | ||
return 0; | ||
} |
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no mutex protection on the has_new_data variable and it is set in the interrupt routine and tested here. However, its likely OK in this simple case.