This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Clarification about SPIM - SS pin management for nrf9160

Hello,

i'm trying to understand how the management of the SS pin is (or has to be) performed when using nrf9160 as SPI master.

In this post Håkon said that:

"Most driver implementations has its own property for SS pin, as this is essentially a GPIO output that is set/cleared (SPIM doesn't have a dedicated hardware CSN/SS pin). The nrfx_spim driver itself supports handling the SS pin, but this is disabled for the zephyr SPI port layer: https://github.com/NordicPlayground/fw-nrfconnect-zephyr/blob/master/drivers/spi/spi_nrfx_spim.c#L411" "

So i have to implement my own SS-pin management and that's generally fine. It's here that i become confused. In the SPI API provided is defined this:

struct spi_config {
	u32_t		frequency;
	u16_t		operation;
	u16_t		slave;

	const struct spi_cs_control *cs;
};

where:

struct spi_cs_control {
	struct device	*gpio_dev;
	u32_t		gpio_pin;
	u32_t		delay;
};

Have the struct elements "operation" (it has cs_active_high option) and "spi_cs_control" have any effect on the behaviour of the ss-pin? Does it effectively pull the pin high/low? Or i can leave these struct empty because this part is disabled?

In the case these struct elements are ineffective, i suppose this is the summarized way to proceed:

  //NOT REAL CODE, JUST TO SUMMARIZE OPERATIONS
  
  void spi_dev_write(spi_dev, spi_cfg, gpio_ss_dev, ss_pin, ) {
    gpio_pin_write(gpio_ss_dev, ss_pin, 0); //PULL CS LOW
    err = spi_write(spi_dev, spi_cfg, const struct spi_buf_set tx); //WRITE
    gpio_pin_write(gpio_ss_dev, ss_pin, 1); //PULL CS HIGH
  }

Am i correct?

 

Thanks,

frax

  • Have the struct elements "operation" (it has cs_active_high option) and "spi_cs_control" have any effect on the behaviour of the ss-pin?

    No, likely this could be used if the hardware supported it, but it doesn't.

    So you end up with the spi_dev_write() you show.

  • Hi Kenneth,

    ty for the answer. Did you test what you said? Because, from Zephyr documentation, i read:

    structspi_cs_control
    #include <spi.h>
    SPI Chip Select control structure.
    
    This can be used to control a CS line via a GPIO line, instead of using the controller inner CS logic.

    It says this is an alternative to "controller inner CS logic". I understood that "controller inner CS logic" is the one that is not supported in this case, while i thought that spi_cs_control structure had some kind of effect. Unfortunately due to COVID i have not my scope to test the behaviour of the CS line with or without using this structure, but maybe you already tested it

    Thanks,

    Frax

  • I was thinking the CS logic was handled in hardware, but by experimenting with it I can see it's handled in software by the driver. So it is possible to use the CS logic in the driver, I made an example below.

    Best regards,
    Kenneth

    #include <zephyr.h>
    #include <drivers/spi.h>
    #include <drivers/gpio.h>
    #include <logging/log.h>
    
    LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
    
    #define GPIO_DRV_PORT	DT_NORDIC_NRF_GPIO_GPIO_0_LABEL
    #define CSN_PIN         DT_GPIO_LEDS_LED_0_GPIOS_PIN
    #define SPI_DRV_NAME    DT_NORDIC_NRF_SPI_SPI_1_LABEL
    
    #define BUF_SIZE 12
    
    u8_t buffer_tx[BUF_SIZE] = "Hello World";
    u8_t buffer_rx[BUF_SIZE];
    
    struct device *spi_dev;
    struct device *gpio_dev;
    
    struct spi_cs_control cs_cfg = {
            .gpio_dev = NULL,
    	.gpio_pin = CSN_PIN,
    	.delay = 100,
    };
    
    const struct spi_config spi_cfg_slow = {
    	.frequency = 1000000,
    	.operation = SPI_OP_MODE_MASTER |  SPI_WORD_SET(8) | SPI_LINES_SINGLE,
    	.slave = 0,
    	.cs = &cs_cfg,
    };
    
    const struct spi_buf tx_bufs[] = {
            {
                    .buf = buffer_tx,
                    .len = BUF_SIZE,
            },
    };
    const struct spi_buf rx_bufs[] = {
            {
                    .buf = buffer_rx,
                    .len = BUF_SIZE,
            },
    };
    const struct spi_buf_set tx = {
            .buffers = tx_bufs,
            .count = ARRAY_SIZE(tx_bufs)
    };
    const struct spi_buf_set rx = {
            .buffers = rx_bufs,
            .count = ARRAY_SIZE(rx_bufs)
    };
    
    void main(void)
    {
    	int ret;
    
          	gpio_dev = device_get_binding(GPIO_DRV_PORT);
            if (!gpio_dev) {
                    LOG_INF("gpio port was not found %s!\n", GPIO_DRV_PORT);     
                    return;
    	}
            LOG_INF("gpio port initialized");  
    
    
            spi_dev = device_get_binding(SPI_DRV_NAME);
    	if (!spi_dev) {
    		LOG_ERR("spi was not found %s!\n", SPI_DRV_NAME);		
    		return;
    	}
            LOG_INF("spi initialized");  
    
            cs_cfg.gpio_dev = gpio_dev;
    
            while(1)
            {
                    //gpio_pin_configure(gpio_dev, CSN_PIN,0);
                    //gpio_pin_write(gpio_dev, CSN_PIN, 0);
                    ret = spi_transceive(spi_dev, &spi_cfg_slow, &tx, &rx);
                    //gpio_pin_write(gpio_dev, CSN_PIN, 1);
                    LOG_HEXDUMP_INF(&rx, sizeof(rx), "received");
                    k_sleep(1000);
            }
    }
    

  • So the software actually pulls CS line LOW and HIGH automatically, without direct gpio write. Nice, this allow to write more compact and error-proof code.

  • Hi Kenneth,

    What does your prj.conf look for this example? When I try to instantiate a spi_cs_control struct, I get a "storage size of cs_pin isn't known" error.

    Thanks,

    -Kyle

Related