Triyng to use a MPRLS 0015PA0000SAB sensor with nrf5340dk

Hello, I've been developing on a Nrf5340 board and I am trying to gather pressure sensor data from both a adafruit mprls pressure sensor breakout board on i2c and then a Honeywell mprls0015PA0000SAB sensor that is running on the SPI bus. I've been able to get the adafruit sensor board and read data from that board but I have been having many troubles trying to configure the honeywell board and get my project to actually build. 

This is my main.c file:

/*
 * Copyright (c) 2024 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/printk.h>

/* Configuration Macros */
#define SLEEP_TIME_MS 1000
#define ADC_SAMPLE_INTERVAL_MS 50
#define I2C_CONFIG_REG 0x8C
#define PRESSURE_SENSOR_ADDR DT_NODELABEL(mprls)
#define SPIOP      SPI_WORD_SET(8) | SPI_TRANSFER_MSB

/* ADC Specification */
static const struct adc_dt_spec adc_channel = ADC_DT_SPEC_GET(DT_PATH(zephyr_user));

/* Logging Module */
LOG_MODULE_REGISTER(ImprovedSensorApp, LOG_LEVEL_DBG);

/* Function Prototypes */
static int initialize_i2c_sensor(const struct i2c_dt_spec *dev_i2c);
static int initialize_adc(const struct adc_dt_spec *adc_channel, struct adc_sequence *sequence, int16_t *buf);
static int initialize_spi_sensor(const struct spi_dt_spec *dev_spit);
static int read_adc_value(const struct adc_dt_spec *adc_channel, struct adc_sequence *sequence, int16_t *buf);
static float calculate_pressure(uint8_t *data_buffer);
static float calculate_spi_pressure(uint8_t *spi_data);

int main(void) {
    /* I2C Initialization */
    uint8_t Sensor_I2C[3];
    Sensor_I2C[0] = 0xAA;      //Output Measurement
    Sensor_I2C[1] = 0x00;     
    Sensor_I2C[2] = 0x00; 
    struct adc_sequence sequence;
    int16_t adc_buf;
    if (initialize_adc(&adc_channel, &sequence, &adc_buf) < 0) {
        return -1;
    }
    static const struct i2c_dt_spec dev_i2c = I2C_DT_SPEC_GET(PRESSURE_SENSOR_ADDR);
    if (initialize_i2c_sensor(&dev_i2c) < 0) {
        return -1;
    }

    static const struct spi_dt_spec dev_spi = SPI_DT_SPEC_GET(DT_NODELABEL(mprls_spi), SPIOP, 0);


        if (initialize_spi_sensor(&dev_spi) < 0) {
            return -1;
        }


    printk("Start reading Sensor\n");
    while (1) {
        /* ADC Reading */
        int adc_mv = read_adc_value(&adc_channel, &sequence, &adc_buf);
        if (adc_mv >= 0) {
            printk(">");
			printk("var1:");
		    printk("%d mV", adc_mv);
			printk("\r\n");
        }

        /* I2C Sensor Reading */
        uint8_t i2c_rx_buffer[4];
        uint8_t config[2] = {0xAA, 0x8C};  // Replace this with the correct command for your sensor
        int ret = i2c_write_dt(&dev_i2c, config, sizeof(config));

        // Assuming 3 bytes for pressure data
        ret = i2c_read_dt(&dev_i2c, i2c_rx_buffer, sizeof(i2c_rx_buffer));
        printk("I2C raw data: %02x %02x %02x %02x\n", i2c_rx_buffer[0], i2c_rx_buffer[1], i2c_rx_buffer[2], i2c_rx_buffer[3]);

        if (ret != 0) {
            printk("Failed to read from sensor (%d)\n", ret);
        } else {
            float pressure = calculate_pressure(i2c_rx_buffer);
            printk(">");
			printk("var2:");
			printk("%.2f Psi", pressure);
			printk("\r\n");
            printk("Pressure: %.2f kPa\n", pressure);
        }
        uint8_t spi_tx_buffer[1] = {0x00}; // Command to request pressure data (adjust as needed)
        uint8_t spi_rx_buffer[3] = {0}; // Buffer for received pressure data
        struct spi_buf tx_buf = {
            .buf = spi_tx_buffer,
            .len = sizeof(spi_tx_buffer),
        };
        struct spi_buf rx_buf = {
            .buf = spi_rx_buffer,
            .len = sizeof(spi_rx_buffer),
        };
        struct spi_buf_set tx_set = {
            .buffers = &tx_buf,
            .count = 1,
        };
        struct spi_buf_set rx_set = {
            .buffers = &rx_buf,
            .count = 1,
        };

        ret = spi_transceive_dt(&dev_spi, &tx_set, &rx_set);

        if (ret != 0) {
            printk("Failed to read from SPI sensor (%d)\n", ret);
        } else {
            float pressure = calculate_spi_pressure(spi_rx_buffer);
            printk(">SPI Pressure: %.2f kPa\n", pressure);
        }

        /* Sleep */
        k_sleep(K_MSEC(ADC_SAMPLE_INTERVAL_MS));
    }

    return 0;
}

/* Initialize the I2C Sensor */
static int initialize_i2c_sensor(const struct i2c_dt_spec *dev_i2c) {
    if (!device_is_ready(dev_i2c->bus)) {
        printk("I2C bus %s is not ready!\n", dev_i2c->bus->name);
        return -1;
    }
    uint8_t config[2] = {I2C_CONFIG_REG, 0x8C};
    int ret = i2c_write_dt(dev_i2c, config, sizeof(config));
    if (ret != 0) {
        printk("Failed to configure I2C sensor (addr: 0x%x, reg: 0x%x)\n", dev_i2c->addr, config[0]);
        return -1;
    }
    printk("I2C Sensor initialized successfully.\n");
    return 0;
}

static int initialize_spi_sensor(const struct spi_dt_spec *dev_spi) {
    const struct device *mprls = DEVICE_DT_GET(DT_NODELABEL(mprls_spi));
    if (!device_is_ready(mprls)) {
        printk("MPRLS device not ready\n");
        return -ENODEV;
    }

    if (!spi_is_ready_dt(dev_spi)) {
        printk("SPI device not ready\n");
        return -1;
    }

    printk("SPI Sensor initialized successfully.\n");
    return 0;
}


/* Initialize the ADC */
static int initialize_adc(const struct adc_dt_spec *adc_channel, struct adc_sequence *sequence, int16_t *buf) {
    if (!adc_is_ready_dt(adc_channel)) {
        LOG_ERR("ADC controller device %s is not ready", adc_channel->dev->name);
        return -1;
    }
    int err = adc_channel_setup_dt(adc_channel);
    if (err < 0) {
        LOG_ERR("Failed to setup ADC channel (%d)", err);
        return -1;
    }

    /* Configure ADC sequence */
        sequence->options = NULL;
        sequence->channels = BIT(adc_channel->channel_id);
        sequence->buffer = buf;
        sequence->buffer_size = sizeof(int16_t); // Single sample
        sequence->resolution = adc_channel->resolution;
        sequence->oversampling = 0;
    printk("ADC initialized successfully.\n");
    return 0;
}

/* Read ADC Value */
static int read_adc_value(const struct adc_dt_spec *adc_channel, struct adc_sequence *sequence, int16_t *buf) {
    int err = adc_read(adc_channel->dev, sequence);
    if (err < 0) {
        LOG_ERR("ADC read failed (%d)", err);
        return err;
    }
    int val_mv = buf[0];
    err = adc_raw_to_millivolts_dt(adc_channel, &val_mv);
    if (err < 0) {
        LOG_WRN("Raw ADC value: %d (conversion to mV not available)", buf[0]);
    }
    return val_mv;
}

/* Calculate Pressure from Sensor Data */
static float calculate_pressure(uint8_t *data_buffer) {
    int raw_value = (data_buffer[1] << 16) | data_buffer[2] << 8 | data_buffer[3]; // Assuming 16-bit raw data
    float double_pressure = (float)raw_value;
    float calculated_pressure = (((double_pressure - 1677722)*25)/13421772)+(0);
    return calculated_pressure;
}

static float calculate_spi_pressure(uint8_t *spi_data) {
    int raw_value = (spi_data[1] << 16) | (spi_data[2] << 8) | spi_data[2];
    float double_pressure = (float)raw_value;
    float calculated_pressure = (((double_pressure - 1677722)*25)/13421772)+(0);
    return calculated_pressure;
}


And this is the nrf5340dk_nrf5340_cpuadd_ns.overlay file
/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * Copyright (c) 2022 Nordic Semiconductor ASA
 *
 
	Includes code to activate the ADC and the I2C drivers. Overlay details are typically mirrored in the prj.conf file
 
 */
&uart1 {status = "disabled";};
 / {
	zephyr,user {
		io-channels = <&adc 0>;			// We're using channel 0, but we can specify more
	};
};

&adc {
	#address-cells = <1>;		// 
	#size-cells = <0>;			//
	status = "okay";			//
	channel@0 {					// We're using channel 0
		reg = <0>;
		zephyr,gain = "ADC_GAIN_1";
		zephyr,reference = "ADC_REF_INTERNAL"; //Use an intenral reference, there are several to choose from. 
		zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;	// 10us. fSAMPLE < 1/(tACQ + tconv), where tconv depends on resolution
		zephyr,input-positive = <NRF_SAADC_AIN0>; /* P0.04 for nRF5340 */
		zephyr,resolution = <12>; //can also be 8/10/12
	};
};

// Device tree shows this as i2c1 node for the nRF53DK

&i2c1 {
	mprls: mprls@18{
		compatible = "i2c-device";
		reg = < 0x18 >; //This must be a 7 bit address, not a 8 bit one. If comm is an issue, this could be it
		label = "mprls";
		pinctrl-0 = <&i2c1_default>;
		pinctrl-1 = <&i2c1_sleep>;
		int_gpios = <&gpio0 27 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
		status = "okay";
		zephyr,concat-buf-size = <128>; //fixes an old issue in which burst read/write runs out of memory 
	};
};

&spi0 {
    compatible = "nordic,nrf-spim";
    status = "okay";
    pinctrl-0 = <&spi0_default>;
    pinctrl-1 = <&spi0_sleep>;
    pinctrl-names = "default", "sleep";
    cs-gpios = <&gpio0 25 GPIO_ACTIVE_LOW>;

    mprls_spi: mprls_spi@0 {
        compatible = "honeywell,mprls_spi";
        reg = <0>;                          // Chip-select index
        spi-max-frequency = <1000000>;      // Maximum SPI clock frequency (1 MHz)
        duplex = <0>;                    // Ensure duplex is explicitly defined
    };
};

&pinctrl {
    spi0_default: spi0_default {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 8)>,					 
                    <NRF_PSEL(SPIM_MOSI, 0, 9)>,
                    <NRF_PSEL(SPIM_MISO, 0, 10)>;
        };
    };
    spi0_sleep: spi0_sleep {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 8)>,
                    <NRF_PSEL(SPIM_MOSI, 0, 9)>,
                    <NRF_PSEL(SPIM_MISO, 0, 10)>;
            low-power-enable;
        };
    };
};


I've managed to get the project to build pretty far to the point where it's generating build files but near the end I will get an error that says "undefined reference to '__device_dts_ord_146'

Im pretty stuck at this point and not sure what my next steps are to figuring this out. In order to get mrpls_spi to work I did go into the zephyr project folders and added a yaml specifically for that board and made the file honeywell,mprls_spi.yaml there was already a honeywell,mpr.yaml file but it had specified that it only worked on the i2c bus and not the spi one which was my reason for doing that. Im not sure if I have to create a new driver for the sensor or if there's just something wrong im doing with the configuration?

  • Hello,

    How does your prj.conf file like? Have you checked if any other pins of other peripherals are overlapped with the SPI pins which connected to Honeywell sensors?

    Can you check the zephyr.dts file from build folder if the SPI pins are defined properly?

    Thanks.

    BR
    Kazi

  • This is my prj.conf file

    #
    # Copyright (c) 2024 Nordic Semiconductor
    #
    # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
    #
    
    CONFIG_LOG=y
    CONFIG_SERIAL=n
    # Enable the ADC API and driver
    CONFIG_ADC=y
    CONFIG_SPI=y
    # Add the I2C Drivers and Show floating points w/digits
    CONFIG_I2C=y
    CONFIG_PINCTRL=y 
    CONFIG_CBPRINTF_FP_SUPPORT=y
    CONFIG_GPIO=y
    CONFIG_SENSOR=y
    


    In my zephyr.dts file this is the definition that is generated for spi0

    			spi0: spi@8000 {
    				compatible = "nordic,nrf-spim";
    				#address-cells = < 0x1 >;
    				#size-cells = < 0x0 >;
    				reg = < 0x8000 0x1000 >;
    				interrupts = < 0x8 0x1 >;
    				max-frequency = < 0x7a1200 >;
    				easydma-maxcnt-bits = < 0x10 >;
    				status = "okay";
    				pinctrl-0 = < &spi0_default >;
    				pinctrl-1 = < &spi0_sleep >;
    				pinctrl-names = "default", "sleep";
    				cs-gpios = < &gpio0 0x19 0x1 >;
    				mprls_spi: mprls_spi@0 {
    					compatible = "honeywell,mprls_spi";
    					reg = < 0x0 >;
    					spi-max-frequency = < 0xf4240 >;
    					duplex = < 0x0 >;
    				};
    			};


    And I also wanted to the include the I2c1 that is generated since that is my other sensor

    			i2c1: arduino_i2c: i2c@9000 {
    				compatible = "nordic,nrf-twim";
    				#address-cells = < 0x1 >;
    				#size-cells = < 0x0 >;
    				reg = < 0x9000 0x1000 >;
    				clock-frequency = < 0x186a0 >;
    				interrupts = < 0x9 0x1 >;
    				status = "okay";
    				pinctrl-0 = < &i2c1_default >;
    				pinctrl-1 = < &i2c1_sleep >;
    				pinctrl-names = "default", "sleep";
    				mprls: mprls@18 {
    					compatible = "i2c-device";
    					reg = < 0x18 >;
    					label = "mprls";
    					pinctrl-0 = < &i2c1_default >;
    					pinctrl-1 = < &i2c1_sleep >;
    					int_gpios = < &gpio0 0x1b 0x11 >;
    					status = "okay";
    					zephyr,concat-buf-size = < 0x80 >;
    				};
    			};



    And the ADC sensor that is generated aswell

    			adc: adc@e000 {
    				compatible = "nordic,nrf-saadc";
    				reg = < 0xe000 0x1000 >;
    				interrupts = < 0xe 0x1 >;
    				status = "okay";
    				#io-channel-cells = < 0x1 >;
    				#address-cells = < 0x1 >;
    				#size-cells = < 0x0 >;
    				phandle = < 0x17 >;
    				channel@0 {
    					reg = < 0x0 >;
    					zephyr,gain = "ADC_GAIN_1";
    					zephyr,reference = "ADC_REF_INTERNAL";
    					zephyr,acquisition-time = < 0x0 >;
    					zephyr,input-positive = < 0x1 >;
    					zephyr,resolution = < 0xc >;
    				};
    			};

    Only other pins that I have connected to the nrf controller are those for the honeywell mpr pressure sensor that uses I2C, those pins are only connected to the I2C datalines (P103 and P102) so it shouldnt be overlapping with the SPI datalines.

    Let me know if this is enough information to see the problem or if there is something else that I should be checking out on, Thanks!

  • Hello,

    ''Only other pins that I have connected to the nrf controller are those for the honeywell mpr pressure sensor that uses I2C, those pins are only connected to the I2C datalines (P103 and P102) so it shouldnt be overlapping with the SPI datalines.''

    Could you please elaborate this? 

    The SPI shares registers and other resources with other peripherals that have the same ID as the SPI. Therefore, the user must disable all peripherals that have the same ID as the SPI before the SPI can be configured and used. So, it is not about i2c have dedicated pins but also you need to chcek if ay other peripherals share the same pins as SPI.

    Disabling a peripheral that has the same ID as the SPI will not reset any of the registers that are shared with the SPI. It is therefore important to configure all relevant SPI registers explicitly to secure that it operates correctly.

    Which Pins are used for SPI0 peripheral? Can you show me the pincontrol  file?

    This is the default pin selection of nRF5340 (nrf5340dk_nrf5340_cpunet-pinctrl.dtsi)

    This is the documentation of pin assignments of nRF5340 https://docs.nordicsemi.com/bundle/ps_nrf5340/page/chapters/pin.html

Related