Writing I2C driver for NRF52832 help

Introduction:

I am working on my first project based on NRF processor. It is also my first time working on Zephyr RTOS. I have spent a couple of days watching various videos to familiarize myself with how things are done but I still find difficult to understand the whole device tree principle.

I thought the fastest way to get going is to quickly create a simple i2c driver to poll temperature/humidity data from the SHT40 sensor that I have available at hand. As a reference, I use the example project for SHT3Xd that I found on the zephyr git: (https://github.com/zephyrproject-rtos/zephyr/blob/main/samples/sensor/sht3xd/src/main.c)

Information:

HW used: NRF52DK (https://www.nordicsemi.com/Products/Development-hardware/nrf52-dk)

IDE used: VS Code with nRF Connect for VS Code extension

NRF Connect SDK used: v2.4.0

In the C:\nrf\v2.4.0\zephyr\boards\arm\nrf52dk_nrf52832\nrf52dk_nrf52832.dts the i2c is defined:

arduino_i2c: &i2c0 {
compatible = "nordic,nrf-twi";
status = "okay";
pinctrl-0 = <&i2c0_default>;
pinctrl-1 = <&i2c0_sleep>;
pinctrl-names = "default", "sleep";
};

&i2c1 {
compatible = "nordic,nrf-twi";
/* Cannot be used together with spi1. */
/* status = "okay"; */
pinctrl-0 = <&i2c1_default>;
pinctrl-1 = <&i2c1_sleep>;
pinctrl-names = "default", "sleep";
};

From what I understand, in the my project overlay file I can then expand the i2c0 or i2c1 by adding:

&i2c1 {
sht3xd@44 {
compatible = "sensirion,sht3xd";
reg = <0x44>;
};
};

This tells the device tree that I am going to connect some external I2C device to the i2c1 bus. Is my understanding correct?

In my code, I call:

int main(void)
{
    const struct device *const dev = DEVICE_DT_GET_ONE(sensirion_sht3xd);
    int rc;

    if (!device_is_ready(dev)) {
        printf("Device %s is not ready\n", dev->name);
        return 0;
    }
    return 0;
}

The error I get: 
C:\nrf\v2.4.0\zephyr\include\zephyr\device.h:84:41: error: '__device_dts_ord_104' undeclared here (not in a function); did you mean '__device_dts_ord_14'?
What is the root cause of this error and how to solve this?
 
My additional questions:
  1. Where are the SCL/SDA pins defined? I cannot see the pins defined in the C:\nrf\v2.4.0\zephyr\boards\arm\nrf52dk_nrf52832\nrf52dk_nrf52832.dts. The i2c0 and i2c1 does not even have scl and sda property. How to define scl and sda pins for my particular application and how does the zephyr example project know which I2C pins to use?

  2. In the device tree compatible property sht3x zephyr example they use compatible = "sensirion,sht3xd";, Can I make up my own custom name for it? For example: compatible = "my_i2c_sensor,sht40";

  3. I can see that the Zephyr sht3x example is using functions such as sensor_attr_set, sensor_trigger_set, sensor_sample_fetch, sensor_channel_get and others to get the information from the i2c device. It seems to be unnecessary complex. For example, I have recently written STH40 driver for the ESP32 microcontroller and it looks something like:

esp_err_t Measure_temp_humidity(){
   uint8_t read_buffer[6];
   uint8_t write_command[1] = {0xFD}; // request temp_humid_high_precision
   i2c_master_write_to_device(I2C_MASTER_NUM, 0x44, &write_command, 1, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS); // send request
   vTaskDelay(20 / portTICK_PERIOD_MS); // wait for 20ms
   i2c_master_read_from_device(I2C_MASTER_NUM,0x44,&read_buffer,6,I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
}

Would someone be able to provide some pseudo code just to see what would be nrf52 zephyr alternative way to achieve the simmillar as above Measure_temp_humidity() function.

 

  • Hello

    First of all, if you're new to NCS and Zephyr, I would recommend going through our fundamentals course:

    https://academy.nordicsemi.com/courses/nrf-connect-sdk-fundamentals/

    What is the root cause of this error and how to solve this?

    This type of error means something is set up wrong in the devicetree.

    You seem to have the right idea, I suspect this might be caused by i2c1's status = "okay" being commented out. The status label is what enables the peripheral. You might also have to enable I2C in prj.conf if you haven't already.

    Where are the SCL/SDA pins defined?

    Pinctrl is used for this. You'll find SDA and SCL defined in a pinctrl node elsewhere in the devicetree.

    In the device tree compatible property sht3x zephyr example they use compatible = "sensirion,sht3xd";, Can I make up my own custom name for it?

    No. This property is a devicetree binding index. You can find a list of them here:

    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/build/dts/api/bindings.html

    If you at some point need to use a device that is not defined here, you would have to define your own devicetree binding, but changing the names of existing ones would only give you unnecessary problems.

    Would someone be able to provide some pseudo code just to see what would be nrf52 zephyr alternative way to achieve the simmillar as above Measure_temp_humidity() function.

    Not sure exactly what you're looking for here, you already have the pseudo code laid out. If you want to know how this Zephyr example does things just read through its code?

    For how to use I2C in Zephyr simply refer to Zephyr's I2C API:

    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/hardware/peripherals/i2c.html

    Best regards,

    Einar

  • Thank you very much for informative answer. Il go through reading material that you have suggested. and try to setup i2c. If I still have some issues il post here again.

  • Hello again. Please allow me to clarify a few things regarding communicating with various i2c/spi devices using nrf52 and zephyr. I am a little bit confused since I am not used to this at all so apologies if I ask dummy questions.

    I have previously had experience with STM32 and ESP32 microcontrollers where you literally have to write your own low level drivers for communicating with SPI and I2C sensors.

    I have shown an example code for the ESP32 where I have to use these low level functions such as I2C_write and I2C_read to write/read to and from required registers.

    From what I understood, thats not how things are done here. You have provided a document that contains bindings for various i2c/spi sensors:

    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/build/dts/api/bindings.html#dt-vendor-sensirion

    For example, if I want to use sensirion,sht4x, I do not have to worry about writing low level driver for it, in fact I dont even need to know anything about this sensor registers and how it communicates, this is already done somewhere deep, all i need to do is to add this to my device tree to tell compiler that I am going to be using this particular sensor.

    &i2c0 {
    	sht4x@44 {
    		repeatability = <0>;
    		compatible = "sensirion,sht4x";
    		reg = <0x44>;
    	};
    	status = "okay";
    };


    and when I refer to it in the code using:

    const struct device *const dev = DEVICE_DT_GET_ONE(sensirion_sht4x);

    it will already know what registers/and what commands to send to get the information from this sensor using 

    sensor_sample_fetch and 
    sensor_channel_get functions. Is my understanding correct?
    If that is the case, what if I want to use some kind of i2c sensor that is not currently supported? For example lets say sensirion recently released sht50 and I want to use it but its not currently available in:
  • zazas321 said:
    Is my understanding correct?

    Yes I wold say so. Though you don't have to use these high level drivers, you can do it the way you did with STM32/ESP32 if you want to.

    In the functions you are mentioning you are using the Zephyr Sensor library:

    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/hardware/peripherals/sensor.html

    But the Sensor library is still using the I2C API in the end anyway, so you can do that directly yourself if you'd like, it just depends on what level of abstraction you want.

    zazas321 said:
    what if I want to use some kind of i2c sensor that is not currently supported?

    Then if you want to use the Sensor library you'll have to define your own devicetree binding:

    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/build/dts/bindings.html#dt-bindings

    Or you can use the I2C API to communicate with the sensor directly like you did with STM32/ESP32.

Related