Hi,
I'm porting a working SPI camera demo from nRF52840 to nRF54L15 and I'm stuck with an issue where the SPIS RX amount is correct, but the received buffer is all zeros.
Hardware / setup:
- New platform: nRF54L15 DK, CPUAPP
- Old reference project: nRF52840 (custom board) BLE image transfer demo using BF30A2 (GC032A) camera module (Nordic nRF5 SDK)
- Camera module: BF3901 / BF30A2 (GC032A-compatible)
- SPI slave instance:
- nRF52840: SPIS1
- nRF54L15: SPIS21
- Pins on nRF54L15:
- SCK = P2.6
- MOSI = P2.0
- MISO = P2.8
- CSN = P2.10
- A GPIO P2.3 is shorted to P2.10 on the board, and I drive P2.3 in software as the camera CS. So effectively camera CSN and SPIS CSN are the same line (this is the same topology we use on the original nRF52840 board, where P0.6 and P0.27 are shorted).
- SCCB / I2C init sequence for the camera is identical to the working nRF52840 project.
Devicetree for SPIS21 (nRF54L15):
&pinctrl {
spis21_default: spis21_default {
group1 {
psels = <NRF_PSEL(SPIS_SCK, 2, 6)>,
<NRF_PSEL(SPIS_MOSI, 2, 0)>,
<NRF_PSEL(SPIS_MISO, 2, 8)>,
<NRF_PSEL(SPIS_CSN, 2, 10)>;
};
};
spis21_sleep: spis21_sleep {
group1 {
psels = <NRF_PSEL(SPIS_SCK, 2, 6)>,
<NRF_PSEL(SPIS_MOSI, 2, 0)>,
<NRF_PSEL(SPIS_MISO, 2, 8)>,
<NRF_PSEL(SPIS_CSN, 2, 10)>;
low-power-enable;
};
};
};
&spi21 {
compatible = "nordic,nrf-spis";
status = "okay";
pinctrl-0 = <&spis21_default>;
pinctrl-1 = <&spis21_sleep>;
pinctrl-names = "default", "sleep";
def-char = <0x00>;
/delete-property/rx-delay-supported;
/delete-property/rx-delay;
};
Software (nRF Connect SDK v3.0.0 / Zephyr 4.0.99):
1) I first tried using nrfx_spis directly (nrfx_spis_init + nrfx_spis_buffers_set). The event handler reported RX amount = 42413 bytes (expected frame size), but my RX buffer (static uint8_t m_rx_buf[65536]) was all zeros.
2) To avoid any DMA / memory-region / cache issues on nRF54, I switched to using the Zephyr SPI slave driver spi_nrfx_spis:
- Device:
static const struct device *spis_dev = DEVICE_DT_GET(DT_NODELABEL(spi21));
- Config (I tried all four modes; original nRF52840 code uses MODE_2):
static const struct spi_config spis_cfg = {
.operation = SPI_OP_MODE_SLAVE | SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_WORD_SET(8),
.frequency = 0,
.slave = 0,
.cs = NULL,
};
- Transfer code (simplified):
spis_xfer_done = false;
last_rx_amount = 0;
struct spi_buf tx_buf = { .buf = m_tx_buf, .len = totol_size };
struct spi_buf rx_buf = { .buf = m_rx_buf, .len = totol_size };
struct spi_buf_set tx_set = { .buffers = &tx_buf, .count = 1 };
struct spi_buf_set rx_set = { .buffers = &rx_buf, .count = 1 };
int ret = spi_transceive_cb(spis_dev, &spis_cfg, &tx_set, &rx_set,
spis_transfer_cb, NULL);
// in callback:
// if (result >= 0) last_rx_amount = (size_t)result;
// spis_xfer_done = true;
// software-driven CS window (P2.3 shorted to CSN P2.10):
gpio_pin_set_dt(&csn_pin, 0); // CS low
NRFX_DELAY_US(180 * 1000); // 180 ms window for camera to output frame
gpio_pin_set_dt(&csn_pin, 1); // CS high
// wait for callback
while (!spis_xfer_done && timeout--) {
k_busy_wait(10);
}
if (spis_xfer_done && last_rx_amount > 0) {
sys_cache_data_invd_range(m_rx_buf, last_rx_amount);
// print first bytes
}
Observations:
- spi_transceive_cb ret = 0
- Callback is called, xfer_done = 1
- last_rx_amount = 42413 (which matches totol_size)
- But m_rx_buf[0..last_rx_amount-1] are all 0x00, sum = 0.
Logic analyzer:
- Probes on the nRF54L15 DK pins directly:
- SCLK (P2.6) shows valid clock only while CS (P2.3/P2.10) is low.
- MOSI (P2.0) shows valid camera data in that CS-low window (not all zeros), matching what works on the nRF52840 design.
- CS line (P2.3 shorted to P2.10) is low for ~180ms then goes high, as expected.
Other info:
- I tried SPI modes 0/1/2/3 on nRF54L15; RX amount is always correct, but RX buffer content is always 0x00.
- On the original nRF52840 project, with essentially identical timing and camera register setup, the same BF30A2/BF3901 module streams valid image data over SPI slave.
- SCCB (I2C) ID read of the sensor returns the expected ID on both boards.
- NCS version: nRF Connect SDK v3.0.0 (Zephyr v4.0.99).
- prj.conf has CONFIG_SPI=y, CONFIG_SPI_SLAVE=y, CONFIG_SPI_ASYNC=y and CONFIG_NRFX_SPIS21=y.
Questions:
1. Is there anything special about SPIS21 on nRF54 (SERIAL subsystem, SPU, memory-regions, etc.) that could cause "RX amount correct but RX buffer all zeros" in SPI slave mode, even when using the Zephyr spi_nrfx_spis driver?
2. Are there known restrictions on which memory regions can be used for SPI slave RX buffers on nRF54L15 when using spi_nrfx_spis? (I assumed the driver + DMM would handle this.)
3. Is it valid to use a software-driven CS window (P2.3) shorted to SPIS CSN (P2.10) in slave mode, similar to how we did on nRF52840, or does SPIS21 require a different CS timing or configuration?
4. Could SPU or some default security configuration be forcing the SPIS21 MISO/MOSI inputs low or blocking the data path, while still allowing the transfer to complete and report a non-zero RX amount?
Any guidance on how to debug this further on nRF54L15 (e.g., registers to inspect for SPIS21, SPU settings, or recommended way to capture continuous SPI camera data in slave mode) would be greatly appreciated.
Thanks!