nRF5340: Debugging SPI + Implementing SPI + PPI + FORK

Hello,

I have a question regarding 1. debugging SPI and 2. implementing SPI + PPI + FORK. It would be really appreciated if anyone could help me with this. 

Debugging SPI

In order to connect SPI and PPI, I need to use nrfx library. I have a working example that is using zephyr library. There is a sample from devZone that is using nrfx library. I made an adjustment (SCK, MISO, MOSI, CS pins) but seems like it's not working.

-- In zephyr +overlay version, I could set those pins to "gpio1" and pin 4,5,6,7 in overlay. But in nrfx version, I could only set pin numbers to 4,5,6,7. Is there any way I could set it to gpio1 specifically?

-- Since I would like to do "read" operation, I am checking "m_rx_buf" in nrfx version. But the buffer is always filled with 0. With zephyr version, I can read the number I expect.

Please let me know if there is anything I am missing. 

 

Below are overlay file, zephyr and nrfx code. 

1. nRF Connect SDK and toolchain version:  v.2.3.0

2. Build configuration: 

- Board: nrf5340dk_nrf5340_cpuapp

- Overlay:

&spi4 {
    status = "okay";
    cs-gpios = <&gpio1 0x7 GPIO_ACTIVE_LOW>;
    pinctrl-0 = <&spi4_default>;
    pinctrl-1 = <&spi4_sleep>;
    pinctrl-names = "default", "sleep";

    ads8866: [email protected] {
            compatible = "vnd,spi-device";
            reg = <0>;
            spi-max-frequency = <16000000>;
            label = "ads8866_convst";
    };
};


&pinctrl {
    spi4_default: spi4_default {
		group1 {
			psels = <NRF_PSEL(SPIM_SCK, 1, 5)>,
				<NRF_PSEL(SPIM_MISO, 1, 6)>,
				<NRF_PSEL(SPIM_MOSI, 1, 4)>;
		};
	};

	spi4_sleep: spi4_sleep {
		group1 {
			psels = <NRF_PSEL(SPIM_SCK, 1, 5)>,
				<NRF_PSEL(SPIM_MISO, 1, 6)>,
				<NRF_PSEL(SPIM_MOSI, 1, 4)>;
			low-power-enable;
		};
	};

};

3. Code

// Working version using zephyr library
#define SPI_OP  SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8) | SPI_FULL_DUPLEX | SPI_FRAME_FORMAT_MOTOROLA
const struct spi_dt_spec ads8866_dev = SPI_DT_SPEC_GET(DT_NODELABEL(ads8866), SPI_OP, 0);
static const struct gpio_dt_spec ads8866_cs = SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(ads8866));

void ADS8866_get_data(char* filename)
{
	int ret;

	gpio_pin_set_dt(&ads8866_cs, 1);	
	gpio_pin_set_dt(&ads8866_cs, 0);	

	uint8_t ads_buffer[2] = {0};
	struct spi_buf ads_spi_buffer[1];
	ads_spi_buffer[0].buf = ads_buffer;
	ads_spi_buffer[0].len = 2;
	const struct spi_buf_set rx_buff = { ads_spi_buffer, 1 };

	if (spi_is_ready_dt(&ads8866_dev))
	{
		printk("ADS8866 device ready\n");
	}

	ret = spi_read_dt(&ads8866_dev, &rx_buff);
	if (ret == 0) 
	{
		uint16_t ads_data = ads_buffer[0] << 8 | ads_buffer[1];
		// printk("spi_read status: %d\n",  ads_data); 
	}

}

//main on SPI master
#include <zephyr/kernel.h>
#include "nrf.h"
#include <nrfx_spim.h>
#include <nrfx_gpiote.h>
#include <drivers/nrfx_errors.h>
#include <zephyr/sys/printk.h>
#include <string.h>

#define SPI_INSTANCE 1
static const nrfx_spim_t spim = NRFX_SPIM_INSTANCE(SPI_INSTANCE);

#define APP_SPIM_CS_PIN (0x07)
#define APP_SPIM_SCK_PIN (0x05)
#define APP_SPIM_MISO_PIN (0x06)
#define APP_SPIM_MOSI_PIN (0x04)

#define NRFX_CUSTOM_ERROR_CODES 0 //used in nrfx_errors.h

static nrfx_spim_config_t spim_config =
	NRFX_SPIM_DEFAULT_CONFIG(APP_SPIM_SCK_PIN, APP_SPIM_MOSI_PIN, APP_SPIM_MISO_PIN, APP_SPIM_CS_PIN);

#define TEST_STRING "1234567890"
static uint8_t m_tx_buf[] = TEST_STRING;
static uint8_t m_rx_buf[sizeof(TEST_STRING) + 1];
static const uint8_t m_length = sizeof(m_tx_buf);

static volatile bool
	spim_xfer_done; /**< Flag used to indicate that SPIM instance completed the transfer. */

static void manual_isr_setup()
{
#ifdef NRF9160_XXAA
	IRQ_DIRECT_CONNECT(UARTE1_SPIM1_SPIS1_TWIM1_TWIS1_IRQn, 0,
			   nrfx_spim_1_irq_handler, 0);
	irq_enable(UARTE1_SPIM1_SPIS1_TWIM1_TWIS1_IRQn);

#else	
	// IRQ_CONNECT( DT_IRQN( DT_NODELABEL( spi1 ) ), DT_IRQ( DT_NODELABEL( spi1 ), priority ), nrfx_isr, nrfx_spim_1_irq_handler, 0 );

	// irq_enable(DT_IRQN( DT_NODELABEL( spi1 ) ));

	IRQ_DIRECT_CONNECT(SPIM1_SPIS1_TWIM1_TWIS1_UARTE1_IRQn, 0,
			   nrfx_spim_1_irq_handler, 0);
	irq_enable(SPIM1_SPIS1_TWIM1_TWIS1_UARTE1_IRQn);
#endif
}

void spim_event_handler(nrfx_spim_evt_t const *p_event, void *p_context)
{
	printk("IRQ: %d\n", p_event->type);
	if (p_event->type == NRFX_SPIM_EVENT_DONE) {
		spim_xfer_done = true;
		printk("Transfer completed\n");
	}
    if (m_rx_buf[0] != 0)
    {
        printk("Received: ");
        for(int i = 0; i < sizeof(m_rx_buf); i++) {
			printk("%x ", m_rx_buf[i]);
		}
		printk("\n");
    }	
}

void main(void)
{
	printk("SPIM example\n");
	nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(NULL, m_length, m_rx_buf, m_length);

	if (NRFX_SUCCESS !=
	    nrfx_spim_init(&spim, &spim_config, spim_event_handler, NULL)) {
		printk("Init Failed\n");
		return;
	}

	manual_isr_setup();
	while(1) {
		memset(m_rx_buf, 0, m_length);
		spim_xfer_done = false;

		nrfx_err_t err_code = nrfx_spim_xfer(&spim, &xfer_desc, 0);

		if (err_code == NRFX_ERROR_BUSY) {
			printk("SPI busy\n");
		}

		else if (err_code != NRFX_SUCCESS){
			printk("Error code = %d\n", err_code);
		}

		while (!spim_xfer_done) {
			__WFE();
		}
		m_tx_buf[0]++;
		if (m_tx_buf[0] > '9') {
			m_tx_buf[0] = '0';
		}
		k_sleep(K_MSEC(1000));
	}
}

Implementing SPI + PPI + FORK

I found in order to connect SPI and PPI, I need to set NRFX_SPIM_FLAG_HOLD_XFER flag for SPI and configure CS to  NRFX_SPIM_PIN_NOT_USED and manage it outside the driver. This suggests using fork. Is there any example code I can follow for this? 

Thank you,

  • Hi,

    There is a sample from devZone that is using nrfx library.

    Can you post a link to the sample for reference?

    -- In zephyr +overlay version, I could set those pins to "gpio1" and pin 4,5,6,7 in overlay. But in nrfx version, I could only set pin numbers to 4,5,6,7. Is there any way I could set it to gpio1 specifically?

    The port is set as the 5th bit in the PSEL registers of the peripheral, which corresponds to adding 32 to the pin number to assign a pin on port1. You can use the macro NRF_GPIO_PIN_MAP(portpin) when defining the pins in your application.

    -- Since I would like to do "read" operation, I am checking "m_rx_buf" in nrfx version. But the buffer is always filled with 0. With zephyr version, I can read the number I expect.

    It is possible that some of the pins you have assigned to SPIM peripheral is also assigned to another peripheral in the application, preventing the slave to output the correct data. Have you checked the SPIM pins with a logic analyzer/scope, to see if there is activity on the lines as expected?

    I found in order to connect SPI and PPI, I need to set NRFX_SPIM_FLAG_HOLD_XFER flag for SPI and configure CS to  NRFX_SPIM_PIN_NOT_USED and manage it outside the driver. This suggests using fork. Is there any example code I can follow for this? 

    We do not have any example code showing how to do automatic control of SPI CS pin through PPI and TIMER. Have you tried to implement this yourself? Do you have any specific issues?

    Best regards,
    Jørgen

  • Can you post a link to the sample for reference?

    --I cannot recall where I found, but here is the original zip file: 6136.nrfx_spi_master.zip

    It is possible that some of the pins you have assigned to SPIM peripheral is also assigned to another peripheral in the application, preventing the slave to output the correct data. Have you checked the SPIM pins with a logic analyzer/scope, to see if there is activity on the lines as expected?

    -- Yes. I tested and SCK, MOSI, MISO, CS pins are called correctly. But I still don't see m_rx_buf getting filled with correct data. Do you have any idea in this case?

    Here is another question,

    Q: Is it not possible to use nrfx's SPIM and zephyr's I2C in the same project? It seems like once I call "nrfx_spim_init()", call for "i2c_burst_read()" always returns an error. 

    Thank you,

    Skim.

Related