Using I2C on nRF52840 with 2 sensor devices

I want to get sensor data from the Bosch BNO055 and the NXP MPR121 to the nRF52840 (DK, for now) using I2C. I completed the Nordic Connect SDK Fundamentals course, and am trying to adapt Lesson 6 Exercise 1 to be for two sensors; however, if I try to set them up through one port (i2c0), Zephyr doesn't have the appropriate .yaml files for my sensors, and I also don't have an example main script that works with sensors this way. What I'm now trying to do is put one sensor on the i2c0 port, and one on the i2c1 port, using the same formatting as in the Exercise overlay:

&i2c0 {  

    MPR: MPR@5a{
        compatible = "i2c-device";
        status = "okay";
        reg = < 0x5a >;
        label = "MPR";
    };
};

&i2c1 {  

    BNO: BNO@29{
        compatible = "i2c-device";
        status = "okay";
        reg = < 0x29 >;
        label = "BNO";
    };
};

But, when I continue onto my main script, I'm having trouble writing it for both ports. Even starting out with this (see Image), it gives me errors because I can't alter the name of the struct (at least, I think that's what the error means). 

  1. What am I doing wrong?
  2. Am I going about this the right way?
  3. Is there an example code that could be created or that I could follow?
  4. How should I alter the Exercise files to work with 2 i2c devices so I can get sensor data from both at the same time?

Thank you so much! Apologies--I'm just starting out still. 

Parents
  • Hello,

    I would recommend adding the Kconfig "CONFIG_SHELL=y" and "CONFIG_I2C_SHELL=y" for your project especially when starting out. It will allow you to directly interact with I2C devices (i.e. scan to make sure addresses are correct, read ID registers).

    blog.golioth.io/.../

    For your devicetree configuration, try something like the following:

    &i2c0 {
        compatible = "nordic,nrf-twi";
        status = "okay";
        pinctrl-0 = <&i2c0_default>;
        pinctrl-1 = <&i2c0_sleep>;
        pinctrl-names = "default", "sleep";
        i2c_device_0: i2cdev0@<FILL_IN_DEVICE_ADDR> {
            status = "okay";
            compatible = "vnd,i2c-device";
            reg = <FILL_IN_DEVICE_ADDR>;
        };
        i2c_device_1: i2cdev1@<FILL_IN_DEVICE_ADDR> {
            status = "okay";
            compatible = "vnd,i2c-device";
            reg = <FILL_IN_DEVICE_ADDR>;
        };
    };

    Then, to access the devices, you can use:

    static const struct i2c_dt_spec0 dev0_i2c = I2C_DT_SPEC_GET(DT_NODELABEL(i2c_device_0));

    I think the reason you were getting errors was because you weren't grabbing the node label.

    I checked through Zephyr and the two devices you are using don't have in-tree Zephyr drivers. Not to worry! Here is a guide from a Nordic partner, Golioth, on creating generic SPI device drivers. Of course you are using I2C here but the general concepts are the same.

    https://blog.golioth.io/how-to-use-generic-spi-devices-with-zephyr/

    Also check the Zephyr I2C docs. https://docs.zephyrproject.org/latest/hardware/peripherals/i2c.html

    Be sure to skim through them. There are a lot of nice helper functions like "i2c_reg_write_byte_dt" that do a lot of the setup for you depending on the operation you're trying to do.

    Finally, you can refer to the Zephyr examples for I2C device drivers. This can make it a bit easier to see what setup steps you may need to take for your I2C device. For instance: https://github.com/zephyrproject-rtos/zephyr/tree/main/drivers/sensor/bme680

    Thanks,

    Helmut Lord

Reply
  • Hello,

    I would recommend adding the Kconfig "CONFIG_SHELL=y" and "CONFIG_I2C_SHELL=y" for your project especially when starting out. It will allow you to directly interact with I2C devices (i.e. scan to make sure addresses are correct, read ID registers).

    blog.golioth.io/.../

    For your devicetree configuration, try something like the following:

    &i2c0 {
        compatible = "nordic,nrf-twi";
        status = "okay";
        pinctrl-0 = <&i2c0_default>;
        pinctrl-1 = <&i2c0_sleep>;
        pinctrl-names = "default", "sleep";
        i2c_device_0: i2cdev0@<FILL_IN_DEVICE_ADDR> {
            status = "okay";
            compatible = "vnd,i2c-device";
            reg = <FILL_IN_DEVICE_ADDR>;
        };
        i2c_device_1: i2cdev1@<FILL_IN_DEVICE_ADDR> {
            status = "okay";
            compatible = "vnd,i2c-device";
            reg = <FILL_IN_DEVICE_ADDR>;
        };
    };

    Then, to access the devices, you can use:

    static const struct i2c_dt_spec0 dev0_i2c = I2C_DT_SPEC_GET(DT_NODELABEL(i2c_device_0));

    I think the reason you were getting errors was because you weren't grabbing the node label.

    I checked through Zephyr and the two devices you are using don't have in-tree Zephyr drivers. Not to worry! Here is a guide from a Nordic partner, Golioth, on creating generic SPI device drivers. Of course you are using I2C here but the general concepts are the same.

    https://blog.golioth.io/how-to-use-generic-spi-devices-with-zephyr/

    Also check the Zephyr I2C docs. https://docs.zephyrproject.org/latest/hardware/peripherals/i2c.html

    Be sure to skim through them. There are a lot of nice helper functions like "i2c_reg_write_byte_dt" that do a lot of the setup for you depending on the operation you're trying to do.

    Finally, you can refer to the Zephyr examples for I2C device drivers. This can make it a bit easier to see what setup steps you may need to take for your I2C device. For instance: https://github.com/zephyrproject-rtos/zephyr/tree/main/drivers/sensor/bme680

    Thanks,

    Helmut Lord

Children
  • I'll look into the shell, thank you!

    As far as device configuration: 1) can the name "12c_device_0" be anything? 2) Does compatible = "vnd,i2c-device"; have to change? Where did we get vnd? Right now, it is showing up as an "unknown type", though it seems this is used for generic drivers. 

    Adding the DT_NODELABEL call didn't help my errors--I had called it earlier in the script. Still getting the same errors. Do you know what might be causing them? Is it just the missing driver files? 

    Is it Section 4 from https://blog.golioth.io/how-to-use-generic-spi-devices-with-zephyr/ that is outlining what I need to do to get the right driver so my device will be recognized? I can't tell what they're creating and where they're putting it. Could you offer any more advice? Once I make that file and put it somewhere, I'll let you know if the errors go away. 
  • 1) Yes, "I2c_device_0" over here is the name of the node and you can choose it as you like

    2) compatible="vnd,i2-device" means that the specific i2c-device is from the specific vendor. Below, see a compatible property for BOSCH BME680 sensor (located at /zephyr/dts/bindings/sensor/)

    So when we want to add BOSCH BME680 sensor to the i2c bus, then we add this compatible entry in the overlay

    For your sensor, please see if it is available in the zephyr bindings or search online for help related to those sensors.

    3) We can get the node-label using the DT_NODELABEL() macro if everything is good/correct in the overlay, otherwise it should produce error as in your case.

  • My sensors are not in the Zephyr bindings, and no info online that I could find--where do I go from there? 

  • Hello,

    You are correct. The orthodox way to add a device driver to Zephyr is to create an out-of-tree driver and fill in all of the boilerplate (kconfigs, etc). For early stages of development it is simpler to use the generic I2C device to at least get communicating. You can continue to use the generic device or you could create device tree bindings, relevant Kconfigs, and a driver implementation and upstream it into Zephyr itself.

    Could you share your build log when you run a pristine build? Sometimes errors will persist in devicetree files until a pristine build.

    Also, the data type you use should be "i2c_dt_spec" right now you are treating it as though it were a variable name. There is a typedef for the struct "i2c_dt_spec" so your lines should be:

    static const struct i2c_dt_spec dev0_i2c...

    instead of

    static const struct i2c_dt_specdev0_i2c...

    Thanks,

    Helmut Lord

  • Thank you all! It worked, some weird quirky things I figured out on my end.

Related