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

SPI with multiple chip selects

As I understand the SPI HW driver documentation, it appears to be at least biased for using a single chip select (slave select in the API). I have an application where I have 5 devices on the SPI bus.

Should I create an SPI master configuration structure for each of the 5 devices? Only the SPI_Pin_SS would be different in each of them, and I think I would need to close and open SPI to switch between the devices.

Or can I use a single structure with spi configuration function to configure the SPI pins (CLK, MOSI, MISO) and manually configure the chip selects? And then when I want to change device I want, just change the SPI_Pin_SS?

Another way? After reading RK's answer, began to wonder if could use GPIOTE to handle the multiple chip selects. Would it work to assign SPI_Pin_SS to an "unused" pin, and cofigure GPIOTE task to toggle desired CS when SPI toggles _SS ? I haven't explored GPIOTE at all yet so this may be a naive idea.

  • Changing SPI_Pin_SS won't help you - that's copied when you open the SPI device and stored, so you can change it as often as you like, won't make any difference at all until you close and reopen the SPI.

    What I'd do myself in this case is set SPI_Pin_SS to UNCONNECTED so the SPI driver doesn't really use it at all, set up 5 GPIO output lines for your chip selects, setting them as outputs and default high (and note the code in the spi_master which sets the output high before configuring it as an output and has a note as to why). I'd then manually manage which device is selected by setting the pin low before sending/receiving with the standard spi code using standard GPIO calls.

  • Controlling the chip select pins outside of the SPI routines can work, and in fact I tried something like what you suggested (but setting _SS to an unused pin not to UNCONNECTED). But it isn't simple since would need to add a way to set the CS only after all bytes in buffer have been sent. The SPI functions already handle _SS correctly, would seem to be cleanest solution if the _SS could be changed in the config structure or the desired CS be passed as part of the _send_recv function.

  • Yes it would - however that's not the way it works if you look at the code. So either you do it manually or you open and close the device every time. If however you know when it's safe to open and to close the device, you probably already know when it's safe to change the chipselect.

    Or you derive off their source and write your own version I guess.

  • I discovered that the chip select can be changed in the spi_master_instance that is created when SPI is opened. I wrote this function to do it:

    void spi_master_change_cs(spi_master_hw_instance_t spi_master_hw_instance, uint32_t cs_pin)
    {		
      volatile spi_master_instance_t * p_spi_instance = spi_master_get_instance(spi_master_hw_instance);
      p_spi_instance->pin_slave_select = cs_pin;
      //A Slave select must be set as high before setting it as output,
      //because during connect it to the pin it causes glitches.
      nrf_gpio_pin_set(p_spi_instance->pin_slave_select);
      nrf_gpio_cfg_output(p_spi_instance->pin_slave_select);
      nrf_gpio_pin_set(p_spi_instance->pin_slave_select);
    }	
    

    and called like this

    spi_master_init(SPI_MASTER_0, spi_master_0_event_handler, true);
    
    for (;; )
    {
    		spi_send_recv(SPI_MASTER_0, m_tx_data_spi, m_rx_data_spi, TX_RX_MSG_LENGTH);
    		nrf_delay_ms(1);
    		spi_master_change_cs(SPI_MASTER_0,5);
    		spi_send_recv(SPI_MASTER_0, m_tx_data_spi, m_rx_data_spi, TX_RX_MSG_LENGTH);
    		nrf_delay_ms(1);
    		spi_master_change_cs(SPI_MASTER_0,6);
    		spi_send_recv(SPI_MASTER_0, m_tx_data_spi, m_rx_data_spi, TX_RX_MSG_LENGTH);
    		nrf_delay_ms(10);
    		spi_master_change_cs(SPI_MASTER_0,2);
    }
    

    image description

    Not perfect, still have to wait until SPI finishes previous transmission before changing hence the delays.

  • Hi

    Thank you for your feedback with your solution. By looking at the spi driver code, it seems to me that your solution is perfectly valid.

    To minimize the delay, you could call the spi_master_change_cs in the spi_master_event_handler. Then you will change the chip select pin as soon as the previous spi transaction finishes. Keep a single spi_send_recv function call in main, which should be called as soon as the spi_master_event_handler finishes execution.

Related