nRF52840: read SPI fast using nrfx_spim

Hello! Slight smile

We have a project where we are using the nRF52840 with the ADS7052 (an ADC) on the SPI bus. We are using Zephyr 4.1.0, but I guess its same same with nRF Connect SDK.

We need to read the ADC samples very fast, therefore I am using nrfx_spim directly.

The ADS7052 ADC sample readout works (simplified) as follows:

- falling edge of CS Pin, then read 3 bytes.

for another sample, we have to drive cs pin high and then go low again etc.

I tried to test that on the nRF52840DK. Read 10 samples, wait 2 seconds, repeat:

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <nrfx_spim.h>
#include <nrf_sys_event.h>
static nrfx_spim_t spim = NRFX_SPIM_INSTANCE(3);

static uint8_t rx_buf[3];                        // buffer to store the received data
static uint8_t tx_dummy[3] = {0xFF, 0xFF, 0xFF}; // 


int main(void)
{

	int ret;
	nrfx_err_t err_code;

	nrfx_spim_config_t spim_config = {
		.sck_pin = NRF_GPIO_PIN_MAP(1, 9),   // SCLK
		.miso_pin = NRF_GPIO_PIN_MAP(0, 12), // Master In Slave Out pin
		.ss_pin = NRF_GPIO_PIN_MAP(1, 0),
		.irq_priority = NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY,
		.orc = 0xFF,             // Over-run character
		.frequency = 16000000,   
		.mode = NRF_SPIM_MODE_3, 
		.bit_order = NRF_SPIM_BIT_ORDER_MSB_FIRST,
		.ss_active_high = false 
	};

	err_code = nrfx_spim_init(&spim, &spim_config, NULL, NULL);
	if (err_code != NRFX_SUCCESS) {
		LOG_ERR("spi init failed: %d", err_code);
	}

	int count = 5;
	nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(tx_dummy, 3, rx_buf, 3);
	while (1) {
		for (size_t i = 0; i < count; i++) {
			err_code = nrfx_spim_xfer(&spim, &xfer_desc, NRFX_SPIM_FLAG_REPEATED_XFER);
			if (err_code != NRFX_SUCCESS) {
				LOG_ERR("error reading SPI");
			}
		}
		LOG_INF("sleep..");
		k_sleep(K_SECONDS(2));
	}

}

on the oscilloscope, in green the cs pin, and in violet the SCLK.

It takes quiet a few us (4us) for the SCLK to start after the falling edge of CS. Also it takes very long (5.7us) for the cs pin to go high, when SCLK stops.

it takes about 6.6us for CS to go low again.

Reading the 10 samples is very slow, as we need to be faster. Are there any improvements I can make?

Thanks in Advance,

Visu

edit: I am aware that the current code does not read something properly, but currently I just want to look at the timing on the oscilloscope :)

Parents
  • I have adapted the nrfx sample so the spi task starts as soon button0 is pressed:

    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
    
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <nrfx_spim.h>
    #include <nrfx_gpiote.h>
    #include <helpers/nrfx_gppi.h>
    #include <zephyr/irq.h>
    
    static nrfx_spim_t spim = NRFX_SPIM_INSTANCE(3);
    
    static uint8_t rx_buf[3];                        // buffer to store the received data
    static uint8_t tx_dummy[3] = {0xFF, 0xFF, 0xFF}; // 
    
    #define INPUT_PIN	NRF_GPIO_PIN_MAP(0,11)
    
    int main(void)
    {
    
    	int ret;
    	nrfx_err_t err;
    
    	nrfx_spim_config_t spim_config = {
    		.sck_pin = NRF_GPIO_PIN_MAP(1, 9),   // SCLK
    		.miso_pin = NRF_GPIO_PIN_MAP(0, 12), // Master In Slave Out pin
    		.irq_priority = NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY,
    		.orc = 0xFF,             // Over-run character
    		.frequency = 16000000,   
    		.mode = NRF_SPIM_MODE_3, 
    		.bit_order = NRF_SPIM_BIT_ORDER_MSB_FIRST,
    		.ss_active_high = false,
    		.ss_duration = 1,
    		.rx_delay = 1
    	};
    
    	err = nrfx_spim_init(&spim, &spim_config, NULL, NULL);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("spi init failed: %d", err);
    	}
    
    	int count = 5;
    	nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(tx_dummy, 3, rx_buf, 3);
    
    	nrfx_spim_xfer(&spim, &xfer_desc, NRFX_SPIM_FLAG_HOLD_XFER);
    
    	uint8_t in_channel, out_channel;
    	uint8_t ppi_channel;
    	const nrfx_gpiote_t gpiote = NRFX_GPIOTE_INSTANCE(0);
    
    	err = nrfx_gpiote_init(&gpiote, 0);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_gpiote_init error: 0x%08X", err);
    		// return 0;
    	}
    
    	err = nrfx_gpiote_channel_alloc(&gpiote, &in_channel);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("Failed to allocate in_channel, error: 0x%08X", err);
    		return 0;
    	}
    
    	err = nrfx_gpiote_channel_alloc(&gpiote, &out_channel);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("Failed to allocate out_channel, error: 0x%08X", err);
    		return 0;
    	}
    
    	/* Initialize input pin to generate event on high to low transition
    	 * (falling edge) and call button_handler()
    	 */
    	static const nrf_gpio_pin_pull_t pull_config = NRF_GPIO_PIN_PULLUP;
    	nrfx_gpiote_trigger_config_t trigger_config = {
    		.trigger = NRFX_GPIOTE_TRIGGER_HITOLO,
    		.p_in_channel = &in_channel,
    	};
    
    	nrfx_gpiote_input_pin_config_t input_config = {
    		.p_pull_config = &pull_config,
    		.p_trigger_config = &trigger_config,
    		.p_handler_config = NULL
    	};
    
    	err = nrfx_gpiote_input_configure(&gpiote, INPUT_PIN, &input_config);
    
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_gpiote_input_configure error: 0x%08X", err);
    		return 0;
    	}
    
    	nrfx_gpiote_trigger_enable(&gpiote, INPUT_PIN, true);
    
    	LOG_INF("nrfx_gpiote initialized");
    
    	/* Allocate a (D)PPI channel. */
    	err = nrfx_gppi_channel_alloc(&ppi_channel);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_gppi_channel_alloc error: 0x%08X", err);
    		return 0;
    	}
    
    
    	nrfx_gppi_channel_endpoints_setup(ppi_channel,
    		nrfx_gpiote_in_event_address_get(&gpiote, INPUT_PIN),
    		nrfx_spim_start_task_address_get(&spim));
    
    	/* Enable the channel. */
    	nrfx_gppi_channels_enable(BIT(ppi_channel));
    
    	LOG_INF("(D)PPI configured, leaving main()");
    
    
    }

    From falling edge of the button until SCLK starts, it is now 0.6us (600ns).

    So maybe control the cs-pin manually and then trigger spi is the way to go? I am not sure on how i should do this.
    How can i drive a GPIO pin high/low AND trigger a event?

Reply
  • I have adapted the nrfx sample so the spi task starts as soon button0 is pressed:

    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
    
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <nrfx_spim.h>
    #include <nrfx_gpiote.h>
    #include <helpers/nrfx_gppi.h>
    #include <zephyr/irq.h>
    
    static nrfx_spim_t spim = NRFX_SPIM_INSTANCE(3);
    
    static uint8_t rx_buf[3];                        // buffer to store the received data
    static uint8_t tx_dummy[3] = {0xFF, 0xFF, 0xFF}; // 
    
    #define INPUT_PIN	NRF_GPIO_PIN_MAP(0,11)
    
    int main(void)
    {
    
    	int ret;
    	nrfx_err_t err;
    
    	nrfx_spim_config_t spim_config = {
    		.sck_pin = NRF_GPIO_PIN_MAP(1, 9),   // SCLK
    		.miso_pin = NRF_GPIO_PIN_MAP(0, 12), // Master In Slave Out pin
    		.irq_priority = NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY,
    		.orc = 0xFF,             // Over-run character
    		.frequency = 16000000,   
    		.mode = NRF_SPIM_MODE_3, 
    		.bit_order = NRF_SPIM_BIT_ORDER_MSB_FIRST,
    		.ss_active_high = false,
    		.ss_duration = 1,
    		.rx_delay = 1
    	};
    
    	err = nrfx_spim_init(&spim, &spim_config, NULL, NULL);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("spi init failed: %d", err);
    	}
    
    	int count = 5;
    	nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(tx_dummy, 3, rx_buf, 3);
    
    	nrfx_spim_xfer(&spim, &xfer_desc, NRFX_SPIM_FLAG_HOLD_XFER);
    
    	uint8_t in_channel, out_channel;
    	uint8_t ppi_channel;
    	const nrfx_gpiote_t gpiote = NRFX_GPIOTE_INSTANCE(0);
    
    	err = nrfx_gpiote_init(&gpiote, 0);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_gpiote_init error: 0x%08X", err);
    		// return 0;
    	}
    
    	err = nrfx_gpiote_channel_alloc(&gpiote, &in_channel);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("Failed to allocate in_channel, error: 0x%08X", err);
    		return 0;
    	}
    
    	err = nrfx_gpiote_channel_alloc(&gpiote, &out_channel);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("Failed to allocate out_channel, error: 0x%08X", err);
    		return 0;
    	}
    
    	/* Initialize input pin to generate event on high to low transition
    	 * (falling edge) and call button_handler()
    	 */
    	static const nrf_gpio_pin_pull_t pull_config = NRF_GPIO_PIN_PULLUP;
    	nrfx_gpiote_trigger_config_t trigger_config = {
    		.trigger = NRFX_GPIOTE_TRIGGER_HITOLO,
    		.p_in_channel = &in_channel,
    	};
    
    	nrfx_gpiote_input_pin_config_t input_config = {
    		.p_pull_config = &pull_config,
    		.p_trigger_config = &trigger_config,
    		.p_handler_config = NULL
    	};
    
    	err = nrfx_gpiote_input_configure(&gpiote, INPUT_PIN, &input_config);
    
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_gpiote_input_configure error: 0x%08X", err);
    		return 0;
    	}
    
    	nrfx_gpiote_trigger_enable(&gpiote, INPUT_PIN, true);
    
    	LOG_INF("nrfx_gpiote initialized");
    
    	/* Allocate a (D)PPI channel. */
    	err = nrfx_gppi_channel_alloc(&ppi_channel);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_gppi_channel_alloc error: 0x%08X", err);
    		return 0;
    	}
    
    
    	nrfx_gppi_channel_endpoints_setup(ppi_channel,
    		nrfx_gpiote_in_event_address_get(&gpiote, INPUT_PIN),
    		nrfx_spim_start_task_address_get(&spim));
    
    	/* Enable the channel. */
    	nrfx_gppi_channels_enable(BIT(ppi_channel));
    
    	LOG_INF("(D)PPI configured, leaving main()");
    
    
    }

    From falling edge of the button until SCLK starts, it is now 0.6us (600ns).

    So maybe control the cs-pin manually and then trigger spi is the way to go? I am not sure on how i should do this.
    How can i drive a GPIO pin high/low AND trigger a event?

Children
No Data
Related