How to use I2C devices in (Apache) NuttX: Adding support for an I2C device in your board
Previously in this EmbeddedRelated article, we saw how to find an I2C device connected to your board using the i2ctool that is very familiar for people with previous experience with embedded Linux. Today we will see how to add support to an I2C device (i.e. BMP280 sensor) in your board. So, lets to get started!
Quick Links
- Part 1: Getting Started with (Apache) NuttX RTOS - Part 1
- Part 2: Getting Started with (Apache) NuttX RTOS Part 2 - Looking Inside and Creating Your Customized Image
- Part 3: Getting Started with NuttX RTOS on Three Low Cost Boards
- Part 4: Using GPIO in (Apache) NuttX RTOS
- Part 5: Using (Apache) NuttX USERLED Subsystem
- Part 6: Using (Apache) NuttX Buttons Subsystem
- Part 7: How to use I2C devices in (Apache) NuttX: Scanning for Devices
- Part 8: How to use I2C devices in (Apache) NuttX: Adding support for an I2C device in your board
- Part 9: How to use SPI devices in NuttX RTOS
- Part 10: How to use analog input (ADC) on NuttX RTOS
NuttX uses a very simple approach to interface with devices connected to the board: each board has a board bringup() function that is used to initialize the I2C bus and register/initialize an I2C device (i.e. sensor, etc) to that I2C bus. Normally each I2C device to be initialize will have an initialization function inside a file with the chip family prefix followed by the device name (i.e.: stm32_bmp180.c). This file could be located at the boards common directory (when the chip family of the board already supports common drivers) like the boards/arm/stm32/common/src/stm32_bmp180.c or could be inside the board source directory (for boards that doesn't support yet the common drivers) like boards/arm/stm32l4/nucleo-l476rg/src/stm32_bmp180.c.
N.B.: The common driver directory is a solution to avoid code duplication for boards of same chip family. In the past all boards need to have their own device initialization. It means all boards using the same chip need to have their own stm32_bmp180.c, although the file was basically the same for all those boards.
In our case, we want to use the barometer BMP280 connected to I2C0 (as we saw in the previous article), but if we look at boards/arm/rp2040/common/src/ we don't find a rp2040_bmp280.c there, only a rp2040_bmp180.c. Fear not: today we will see how to add our board initialization for our BMP280!
Since the BMP180 driver (located at drivers/sensors/bmp180.c) and BMP280 driver (located at drivers/sensors/bmp280_uorb.c) has just a little different registering function signatures: bmp180_register(devpath, i2c) vs bmp280_register(devno, i2c), we can copy boards/arm/rp2040/common/src/rp2040_bmp180.c to boards/arm/rp2040/common/src/rp2040_bmp280.c and boards/arm/rp2040/common/include/rp2040_bmp180.h to boards/arm/rp2040/common/include/rp2040_bmp280.h.
$ cp boards/arm/rp2040/common/src/rp2040_bmp180.c boards/arm/rp2040/common/src/rp2040_bmp280.c $ cp boards/arm/rp2040/common/include/rp2040_bmp180.h boards/arm/rp2040/common/include/rp2040_bmp280.hThen all you need to do is edit both files (rp2040_bmp280.c and rp2040_bmp280.h) replacing bmp180 with bmp280. Since bmp280_register() doesn't use devpath, you can remove that "char devpath[12];" and that "snprintf(devpath, 12, "/dev/press%d", devno);" and just call bmp280_register() this way:
ret = bmp280_register(devno, i2c); if (ret < 0) { snerr("ERROR: Error registering BMP280 in I2C%d\n", busno); }
Because you added this rp2040_bmp280.c file inside boards/arm/rp2040/common/src/ you will need to edit the Make.defs inside this directory and add:
ifeq ($(CONFIG_SENSORS_BMP280),y) CSRCS += rp2040_bmp280.c endif
Also edit the bring-up file boards/arm/rp2040/common/src/rp2040_common_bringup.c adding the header files:
#ifdef CONFIG_SENSORS_BMP280 #include <nuttx/sensors/bmp280.h> #include "rp2040_bmp280.h" #endif
And the I2C initialization call for our BMP280 device inside rp2040_common_bringup() :
#ifdef CONFIG_SENSORS_BMP280 /* Try to register BMP280 device in I2C0 */ ret = board_bmp280_initialize(0); if (ret < 0) { syslog(LOG_ERR, "Failed to initialize BMP280 driver: %d\n", ret); } #endif
These are all the necessary modifications to get BMP280 called in your board bring-up process.
You need to wire the BMP280 sensor to your RaspberryPi Pico this way:
BMP28 | Raspberry Pi Pico | Physical Pin |
GND | GND | Pin 3 or 38 or ... |
VCC | 3V3 OUT | Pin 36 |
SDA | GP4 (I2C0 SDA) | Pin 6 |
SCL | GP5 (I2C0 SCL) | Pin 7 |
If you don't have a breadboard you can wire directly using Dupont Wires (Femea-Femea)
Now we just need to follow similar steps to what we did in the previous article to configure NuttX to use this BMP280 sensor.
Start clearing your previous configuration:
$ make distclean
We will use the NSH over USB to avoid connecting an external USB/Serial adapter to the serial pins, so run:
$ ./tools/configure.sh raspberrypi-pico:usbnsh
Now we can select the options to get the our BMP280 working on NuttX:
$ make menuconfigEnter inside "System Type --->" and select "I2C Master" and "I2C0"
You can "Exit" from "System Type" and enter inside "Device Drivers --->", as you can see "I2C Driver Support" is automatically selected (pay attention at "-*-" in front of it, instead of "[*]")
Navigate the bottom of Device Drivers and select [*] Sensor Device Support --->
Press ENTER to enter inside "[*] Sensor Device Support --->" and select Bosch BMP280 sensor:
You can "Exit" from "Sensor Device Support" and from "Device Driver" and move down to enter inside "Library Routines --->" and once you are there enter inside "Standard C I/O --->" and select "[*] Enable floating point in printf" :
Finally "Exit" from "Standard C I/O --->" and "Library Routines --->" and move down to enter inside "Application Configuration --->", "Examples --->" and select the BMP280 test application:
That is all! You can "Exit" from "Examples --->", "Application Configuration --->" and Exit and Save from menuconfig:
At this point we can compile:
$ make -j
If you see this message at the end:
PICO_SDK_PATH must be specified for flash boot
It means you forgot to include the SDK in your PATH, you need to include it, in my case:
$ export PICO_SDK_PATH=/home/alan/pico-sdk
Compile again and you need to see these lines at the end:
LD: nuttx
Generating: nuttx.uf2
tools/rp2040/elf2uf2 nuttx nuttx.uf2;
Done.
As you did in previous article, press and hold the Raspberry Pi Pico
BOOTSEL button and connect the USB cable. A "RPI-RP2" virtual disk
should appear on your file manager, the copy nuttx.uf2 to it.
After the file is flash the green LED will turn on, you can also run "sudo dmesg" to confirm that USB CDC/ACM was detected correctly.
Run minicom (or your preferred terminal console tool) and press "Enter" three times to let the "NSH>" appears. If you type "help" or "?" you will see that "i2c" tool application appeared:
That all folk!!! I hope you have enjoyed it.
Note: this tutorial is just a reference the step I took to add support to BMP280 for RaspberryPi Pico, you don't need to do it manually because I already submitted the PR (Pull Request) to add it into NuttX mainline:
https://github.com/apache/nuttx/pull/12420
So, if you want to test your BMP280 sensor on NuttX all you need to do it is:
$ ./tools/configure.sh raspberrypi-pico:bmp280 $ export PICO_SDK_PATH=/home/yourusername/pico-sdk $ make -j
And then copy the generated nuttx.uf2 to your board as explained above.
Anyway it is a reference that you can use to add support for other sensors or I2C devices supported by NuttX RTOS.
- Comments
- Write a Comment Select to add a comment
To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.
Please login (on the right) if you already have an account on this platform.
Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: