Incorrect SPI reads and inconsistent behavior on nRF54L15-DK when communicating with external sensor (ADXL372)

I'm using NCS v2.9.0 and attempting to communicate with an ADXL372 IMU over SPI. Occasionally, the MCU successfully reads from the IMU and begins printing values. However, most of the time it either fails during initialization with a "failed to read device ID" error or stalls while fetching data.

Initially, I suspected a clock polarity/phase mismatch or a breadboarding issue, but after troubleshooting these seem unlikely. The problem appears to be specific to the nRF54L15-DK when used with this sensor. I’ve experimented with various settings, including clock speed, high-drive mode, polling/trigger modes, pull-up/pull-down resistors, and soft/hard resets. Regardless of the configuration, the program remains "consistently inconsistent." I've also tried:

  • Testing communication with the ADXL372 IMU using different pins on the nRF54L15-DK (same issue)
  • Testing communication with the ADXL372 IMU using the same pins on a backup nRF54L15-DK (same issue)
  • Testing communication with a different IMU using the same pins on the nRF54L15-DK (works)
  • Testing communication with the ADXL372 IMU on an nRF52840-DK (works)

The IMU does correctly send data. Its expected IDs are 0xAD and 0xFA, the logic analyzer output reads 0xAD and 0xFA, but the Zephyr driver errors because the values it reads are 0x80 and 0xF8:

Does anyone know what the source of this issue could be? I've included my overlay, prj.conf, and main below.

/delete-node/ &mx25r64;

&spi00 {
	status = "disabled";
};

&spi21 {
	status = "okay";
	pinctrl-0 = <&spi21_default>;
	pinctrl-names = "default";
	cs-gpios = <&gpio2 10 (GPIO_ACTIVE_LOW | (1 << 8))>;
	adxl372@0 {
		compatible = "adi,adxl372";
		reg = <0x0>;
		spi-max-frequency = <DT_FREQ_M(8)>;
		int1-gpios = <&gpio0 0 0>;
		odr = <4>;
		bw = <4>;
		hpf = <0>;
		zephyr,deferred-init;
	};
};

&pinctrl {
	spi21_default: spi21_default {

		group2 {
			psels = <NRF_PSEL(SPIM_MISO, 1, 11)>;
			bias-pull-up;
		};

		group1 {
			psels = <NRF_PSEL(SPIM_SCK, 1, 8)>, <NRF_PSEL(SPIM_MOSI, 1, 12)>;
			nordic,drive-mode = <NRF_DRIVE_H0S1>;
		};
	};
};

CONFIG_STDOUT_CONSOLE=y
CONFIG_LOG=y
CONFIG_LOG_BACKEND_UART=y
CONFIG_LOG_MODE_IMMEDIATE=n
CONFIG_SENSOR_LOG_LEVEL_DBG=y
CONFIG_CBPRINTF_FP_SUPPORT=y
CONFIG_LOG_PRINTK=y

CONFIG_SPI=y
CONFIG_SENSOR=y
CONFIG_ADXL372=y
CONFIG_ADXL372_TRIGGER_GLOBAL_THREAD=n
CONFIG_ADXL372_MEASUREMENT_MODE=y
CONFIG_CBPRINTF_FP_SUPPORT=y

#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>

static void fetch_and_display(const struct device *sensor)
{
	printf("fetch_and_display\n");
    int rc = sensor_sample_fetch(sensor);
    printf("rc: %d\n", rc);
}

#ifdef CONFIG_ADXL372_TRIGGER
static void trigger_handler(const struct device *dev,
			    const struct sensor_trigger *trig)
{
	fetch_and_display(dev);
}
#endif

int main(void)
{
	const struct device *const sensor = DEVICE_DT_GET_ANY(adi_adxl372);
	k_sleep(K_MSEC(500));
	device_init(sensor);

	if (sensor == NULL) {
		printf("No device found\n");
		return 0;
	}
	if (!device_is_ready(sensor)) {
		printf("Device %s is not ready\n", sensor->name);
		return 0;
	}

#if CONFIG_ADXL372_TRIGGER
	{
		struct sensor_trigger trig;
		int rc;

		trig.type = SENSOR_TRIG_DATA_READY;
		trig.chan = SENSOR_CHAN_ACCEL_XYZ;

		rc = sensor_trigger_set(sensor, &trig, trigger_handler);
		if (rc != 0) {
			printf("Failed to set trigger: %d\n", rc);
			return 0;
		}

		printf("Waiting for triggers\n");
		while (true) {
			k_sleep(K_MSEC(2000));
		}
	}
#else /* CONFIG_ADXL372_TRIGGER */
	printf("Polling at 2 Hz\n");
	while (true) {
		fetch_and_display(sensor);
		k_sleep(K_MSEC(500));
	}
#endif /* CONFIG_ADXL372_TRIGGER */
}

Related