I need to connect a device to an nRF5340 SPI port without creating a custom driver. I'm tempted to use the nrfx_spim driver directly, but I'm trying to do it with Zephyr and the device tree. I've looked for examples of how to use the Zephyr SPI driver directly (without having to extend it to a custom device driver like the BME280 example), but I could only find the following examples:
- The SPI FRAM Module Sample (spi_fujitsu_fram) from the zephyr/sample/drivers sample apps - This seems incomplete since it doesn't show to get the CS pin working, and it is missing any overlay files
- https://github.com/sigurdnev/ncs-playground/tree/master/samples/spi_test is out of date
I've tried to combine the information from these and have code that compiles and runs, but the pins don't move (viewed by a logic analyzer). The SCK, MOSI and MISO lines stay low, and the CS# line seems to be floating since it is just noise. Can you tell me what is missing from my code?
Here is the edited file (fpga_spi_init is called by the main() in main.c, and the LOG information is seen on the console):
#include <zephyr/kernel.h> #include <zephyr/logging/log.h> #include <zephyr/device.h> #include <zephyr/devicetree.h> #include <zephyr/drivers/gpio.h> #include <zephyr/drivers/spi.h> LOG_MODULE_REGISTER(fpga_spi, LOG_LEVEL_DBG); // Register/command addresses. Even = read (status/readback), odd = write (command/control) #define VERSION_CMD 0x00 // ...rest redacted const struct device *spi32_dev = DEVICE_DT_GET(DT_ALIAS(scope_fpga_spi32)); static struct spi_config spi32_cfg = { .operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB, .frequency = 32000000, }; struct { uint8_t chip_version[2]; } scope_data; static int fpga_reg_read(uint8_t reg, uint8_t *data, uint8_t size) { int err; uint8_t tx_buffer = reg; struct spi_buf tx_spi_buf = {.buf = (void *)&tx_buffer, .len = 1}; struct spi_buf_set tx_spi_buf_set = {.buffers = &tx_spi_buf, .count = 1}; struct spi_buf rx_spi_buf = {.buf = data, .len = size}; struct spi_buf_set rx_spi_buf_set = {.buffers = &rx_spi_buf, .count = 1}; err = spi_transceive(spi32_dev, &spi32_cfg, &tx_spi_buf_set, &rx_spi_buf_set); if (err < 0) { LOG_ERR("spi_transceive() failed, err: %d", err); return err; } return 0; } int fpga_spi_init(void) { int err; if (!device_is_ready(spi32_dev)) { LOG_ERR("SPI32 device %s is not ready", spi32_dev->name); return -ENODEV; } err = fpga_reg_read(VERSION_CMD, scope_data.chip_version, sizeof(scope_data.chip_version)); if (err < 0) { LOG_DBG("FPGA version read failed: %d", err); return err; } // Validate the version if (scope_data.chip_version[0] == 1 && scope_data.chip_version[1] == 2) { LOG_INF("FPGA version OK"); } else { LOG_ERR("Bad FPGA version id %d.%d", scope_data.chip_version[0], scope_data.chip_version[1]); return -ENOTSUP; } return 0; }
The device_is_ready call returns true, and the read returns, but with zero data, and no movement on the pins. Note that this uses SPI4 which is capable of 32 MHz, and lowering the frequency to 8 MHz doesn't work either.
Here is the nrf7002dk_nrf5340_cpuapp.overlay file which removes the nRF7002DK's mx25R6435f Flash chip support and sets the alias. The board has been modified to cut power to the Flash chip and solder jump the SPI4 pins to the headers as described in the nRF7002DK documentation and board.
// Override the 32 Mbps SPI to use for the main FPGA connection // The nRF7002DK's onboard serial flash chip on this port will be disabled by solder cuts and jumps &spi4 { /delete-node/ mx25r6435f@0; }; / { aliases { // scope-fpga-spi8 = &spi1; scope-fpga-spi32 = &spi4; }; }; &clock { status = "okay"; };
I checked the build/zephyr/zephyr.dts file to confirm that the device tree is compiling the way I intend it, and it looks ok to me, with `status = "okay"`, the cs-gpios set to pin 0.11, etc. Here are the relevant sections:
aliases { led0 = &led0; led1 = &led1; pwm-led0 = &pwm_led0; sw0 = &button0; sw1 = &button1; bootloader-led0 = &led0; mcuboot-button0 = &button0; mcuboot-led0 = &led0; scope-fpga-spi32 = &spi4; }; ... spi4: spi@a000 { compatible = "nordic,nrf-spim"; #address-cells = < 0x1 >; #size-cells = < 0x0 >; reg = < 0xa000 0x1000 >; interrupts = < 0xa 0x1 >; max-frequency = < 0x1e84800 >; easydma-maxcnt-bits = < 0x10 >; rx-delay-supported; rx-delay = < 0x2 >; status = "okay"; pinctrl-0 = < &spi4_default >; pinctrl-1 = < &spi4_sleep >; pinctrl-names = "default", "sleep"; cs-gpios = < &gpio0 0xb 0x1 >; }; ... spi4_default: spi4_default { phandle = < 0x8 >; group1 { psels = < 0x40008 >, < 0x6000a >, < 0x50009 >; }; }; spi4_sleep: spi4_sleep { phandle = < 0x9 >; group1 { psels = < 0x40008 >, < 0x6000a >, < 0x50009 >; low-power-enable; }; };
What am I missing to get the SPI pins to actually work?