Configuring GPIO pins that are used for SPI under Zephyr

I'm prototyping with a nRF9160 DK using nRF Connect SDK v2.4.0 and the Visual Code extension. I'm using an ADXL372z breakout board that I want to communicate with via SPI. When I've done this in the past using an nRF52840DK, the same ADXL372 breakout board and SDK 17, I have had to configure the GPIO pins used for SCLK and MOSI with NRF_GPIO_PIN_H0H1. I've also had to keep the clock frequency below 1MHz. This is because the jumper wire lengths between the DK and the breakout board make you need a bit more current than normal. Also without using twisted pair wiring with proper grounding, you have to keep line frequencies low.

short ADXL372_spi_master_init(nrf_drv_spi_t m_spi0_master,
                              nrf_drv_spi_evt_handler_t spi_event_handler,
                              bool *xferdone)
{
    uint32_t              err_code = 0;

    m_ad_spi_master = m_spi0_master;

    m_config.ss_pin       = NRF_DRV_SPI_PIN_NOT_USED;
    m_config.irq_priority = APP_IRQ_PRIORITY_LOW;
    m_config.orc          = 0xFF;  // Most devices expect ones as filler/dummy bytes.
    m_config.frequency    = NRF_SPI_FREQ_8M;
    m_config.mode         = NRF_DRV_SPI_MODE_0;  // Corresponds to the datasheet specification CPHA = CPOL = 0
    m_config.bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST;  // Put it out the way you put it in

    m_config.sck_pin  = SPI_SCK_PIN;
    m_config.mosi_pin = SPI_MOSI_PIN;
    m_config.miso_pin = SPI_MISO_PIN;
    
    err_code = nrf_drv_spi_init(&m_ad_spi_master, &m_config, spi_event_handler, NULL);    
    APP_ERROR_CHECK(err_code);

    #if (USING_DEVELOPMENT_KIT == 1)
    nrf_gpio_cfg(SPI_SCK_PIN,
                 NRF_GPIO_PIN_DIR_OUTPUT,
                 NRF_GPIO_PIN_INPUT_DISCONNECT,
                 NRF_GPIO_PIN_NOPULL,
                 NRF_GPIO_PIN_H0H1,
                 NRF_GPIO_PIN_NOSENSE);

    nrf_gpio_cfg(SPI_MOSI_PIN,
                 NRF_GPIO_PIN_DIR_OUTPUT,
                 NRF_GPIO_PIN_INPUT_DISCONNECT,
                 NRF_GPIO_PIN_NOPULL,
                 NRF_GPIO_PIN_H0H1,
                 NRF_GPIO_PIN_NOSENSE);
    #endif // (USING_DEVELOPMENT_KIT == 1)

    spixferdone = xferdone;
    m_spi_event_handler = spi_event_handler;

    return (0);
    
}

 This works for prototyping under SDK 17.

I need to do the same using nRF Connect SDK. I can't figure out where to use NRF_GPIO_DRIVE_H0H1 because I don't know how to configure individual GPIO pins under Zephyr. This is my current overlay file:

// To get started, press Ctrl+Space to bring up the completion menu and view the available nodes.
// For more help, browse the DeviceTree documentation at https: //docs.zephyrproject.org/latest/guides/dts/index.html
//
//


&pinctrl
{

    spi3_default: spi3_default
	{
	    group1
		{
		    psels = <NRF_PSEL(SPIM_SCK, 0, 0)>,
				    <NRF_PSEL(SPIM_MOSI, 0, 1)>,
				    <NRF_PSEL(SPIM_MISO, 0, 2)>;

	    };
    };

    spi3_sleep: spi3_sleep
	{
	    group2
		{
		    psels = <NRF_PSEL(SPIM_SCK, 0, 0)>,
				    <NRF_PSEL(SPIM_MOSI, 0, 1)>,
				    <NRF_PSEL(SPIM_MISO, 0, 2)>;
		    low-power-enable;
	    };
	};
};


/*** link SPI pin configurations with a device ***/
my_spi3: &spi3
{
	compatible = "nordic,nrf-spim";
	status = "okay";
	pinctrl-0 = <&spi3_default>;
	pinctrl-1 = <&spi3_sleep>;
	pinctrl-names = "default", "Sleep";
	cs-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>, <&gpio0 29 GPIO_ACTIVE_LOW>;

	winbondflash: flash@0
	{
		compatible = "winbond,w25n01", "jedec,spi-nor";
		size = <1073741824>;
		reg = <0>;
		label = "1GBIT_FLASH_DEVICE";
		spi-max-frequency = <1000000>;
		jedec-id = [ EF AA 21  ];	//UNIQUE TO SPECIFIC FLASH CHIP BEING USED W25N01G
	};

	adxl372: spi-dev-adxl372@1
	{
		compatible = "adi,adxl372";
		spi-max-frequency = <1000000>;
		reg = <1>;
		int1-gpios = <&gpio0 19 0>;
	};
	

};

This is my initialization and use

#include "spi3.h"
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/pwm.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/logging/log.h>

struct spi_cs_control adxl372_spi_cs = 
{
	.gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(adxl372)),
	.delay = 0,
};


static const struct spi_config adxl372_spi3_cfg = 
{
	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
				 SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA,
	.frequency = 400000,
	.slave = 0,
	.cs = &adxl372_spi_cs,
};

struct spi_cs_control winbond1g_spi_cs = 
{
	.gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(winbondflash)),
	.delay = 0,
};


static const struct spi_config winbond1g_spi3_cfg = 
{
	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
				 SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA,
	.frequency = 400000,
	.slave = 0,
	.cs = &winbond1g_spi_cs,
};

static const struct device *spi3_dev;

uint8_t init_spi3(void)
{
	int      err;

	spi3_dev = DEVICE_DT_GET(DT_NODELABEL(spi3));

    if (spi3_dev == NULL)
    {
		printk("Could not get %s device\n", spi3_dev->name);
        return 1;
    }
	printk("SPI Device: %s\n", spi3_dev->name);

    return 0;
}


int adxl372_id(void)
{
	static uint8_t tx_buffer[2];
	static uint8_t rx_buffer[2];

	const struct spi_buf tx_buf =
	{
		.buf = tx_buffer,
		.len = sizeof(tx_buffer)
	};
	const struct spi_buf_set tx =
	{
		.buffers = &tx_buf,
		.count = 1
	};

	struct spi_buf rx_buf =
	{
		.buf = rx_buffer,
		.len = sizeof(rx_buffer),
	};
	const struct spi_buf_set rx =
	{
		.buffers = &rx_buf,
		.count = 1
	};


	// last byte of rx contains the response from the accelerometer
    // Shift the register's address to the left by one, inorder to
    // clear bit 0, and set bit 0 high per the adxl372 data sheet's
    // specification for the read command structure of the device's SPI Protocal.

	tx_buffer[0] = (0x02 << 1) + 1;

	int error = spi_transceive(spi3_dev, &adxl372_spi3_cfg, &tx, &rx);
	if (error != 0)
	{
		printk("SPI transceive error: %i\n", error);
		return error;
	}

	if(rx_buffer[1] == 0xfa)
	{
		printk("Successfull ACC communication\n");
	}
	else
	{
		printk("Faild ACC communication\n");
		printk("SPI Tx bytes: ");
		for (int i = 0; i < sizeof(tx_buffer); i++)
		{
			printk("%x ", tx_buffer[i]);
		}
		printk(" SPI Rx bytes: ");
		for (int i = 0; i < sizeof(rx_buffer); i++)
		{
			printk("%x ", rx_buffer[i]);
		}
		printk("\n");
	}

	return 0;
}

 This is a screen shot of the adxl372_id function in action (

How do I configure the GPIO pins I'm using for SCLK and MOSI to be NRF_GPIO_PIN_H0H1 ? Should I do that GPIO configuration in my Overlay file and how would I do that?

Why is my clock line normally high ?

Greatly appreciate any help and advice. Thank you.

Parents
  • Hello,

    Thank you for contacting DevZone at NordicSemi.

    You can use gpio_pin_configure() function provided in gpio.h to configure a single gpio pin.

    The function takes pointer to device (e.g. gpio0), which can be obtained by using DEVICE_DT_GET() macro (provided by zephyr device.h). It also takes the pin number and flags.

    Pleas see the details here

    Please include required headers (device.h, gpio.h, and nrf_gpio.h) that defines the functions and flags.

    BR,

    Naeem

  • Thank you Naeem, Shortening the wires and implementing GPIO configuration as suggested is getting the right results from spi_transceive for both of my devices, (W25N01GV and the ADXL372), using the spi3 device.

    But as you can see in the following screen shot of my logic analyzer's output

    The clock line is normally high. Shouldn't the clock be normally low, (like MOSI and MISO)? Doesn't the SPI peripheral automatically configure it that way or is that something I have to do? You can see the analyzer doesn't like it.

  • Hi,

    Apologies for delayed response.

    I can look into it if you could send your minimal project to produce same results and experiment with it.

    BR,
    Naeem

  • Got hung-up on something else for a bit.

    I misconfigured the spi_config structure in my code. I had SPI_MODE_CPOL defined as part of the .operation member. This changes CPOL to 1 from the default value of zero so naturally the clock line was set high since I was telling the SPI device to set the clock polarity to 1.

    Also, in addition to keeping the bread board wiring as short as possible, Wes Cherry, (a  Nordic Field Application Engineer), tipped me off to adding the following to my overlay file:

    /* Enable high drive mode for the SPI3 pins to get a square signal at 8 MHz */
    &spi3_default {
        group1 {
            nordic,drive-mode = <NRF_DRIVE_H0H1>;
        };
    };

    Everything works as expected. Thank you.

Reply
  • Got hung-up on something else for a bit.

    I misconfigured the spi_config structure in my code. I had SPI_MODE_CPOL defined as part of the .operation member. This changes CPOL to 1 from the default value of zero so naturally the clock line was set high since I was telling the SPI device to set the clock polarity to 1.

    Also, in addition to keeping the bread board wiring as short as possible, Wes Cherry, (a  Nordic Field Application Engineer), tipped me off to adding the following to my overlay file:

    /* Enable high drive mode for the SPI3 pins to get a square signal at 8 MHz */
    &spi3_default {
        group1 {
            nordic,drive-mode = <NRF_DRIVE_H0H1>;
        };
    };

    Everything works as expected. Thank you.

Children
No Data
Related