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?
