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

how to drive spi CS line low for 32 bits.

Hi,

Can you please direct me to the document that explains about generating more than 8 bits of clock in SPI and keeping CS line low for that entire period.

Also, if i have to send two 16bit data, would i have to send them in lots of 8bit ? or i can send them as 16bits using nrf_drv_spi_transfer API?

I am modifying the SPI example found under SDK15/example/peripheral/spi.

And i have to achieve the sequence attached in the image. 

Thank you  

Parents
  • Hi,

    I recommend you use the SPI master driver, which will handle everything for you. The SPI hardware peripheral and SPI driver count 8 bit words but that does not have any practical implication for what happens on the SPI line. How you think of the data is up to you, whether you think of it as a single 16 bit word or two 8 bit words has no implication on the physical data on the SPI bus.

  • I realized that first i should understand the SPI examples provided in the SDK thoroughly before customizing them.

    I am using nrf52832 DK as Master and flashing SPI example to it which is supposed to send a string " NORDIC " and then i am flashing SPIS example to my nrf52840 DK which should act as the slave then.  Now, though the transactions are getting completed in both the kits and LED's get toggled but i am not receiving the string "NORDIC" from my slave . What do i have to modify in the slave example to receive the string ?

    #include "sdk_config.h"
    #include "nrf_drv_spis.h"
    #include "nrf_gpio.h"
    #include "boards.h"
    #include "app_error.h"
    #include <string.h>
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #define SPIS_INSTANCE 1 /**< SPIS instance index. */
    static const nrf_drv_spis_t spis = NRF_DRV_SPIS_INSTANCE(SPIS_INSTANCE);/**< SPIS instance. */
    
    #define TEST_STRING "Nordic"
    static uint8_t       m_tx_buf[] = TEST_STRING;           /**< TX buffer. */
    static uint8_t       m_rx_buf[sizeof(TEST_STRING) + 1];    /**< RX buffer. */
    static const uint8_t m_length = sizeof(m_tx_buf);        /**< Transfer length. */
    
    static volatile bool spis_xfer_done; /**< Flag used to indicate that SPIS instance completed the transfer. */
    
    /**
     * @brief SPIS user event handler.
     *
     * @param event
     */
    void spis_event_handler(nrf_drv_spis_event_t event)
    {
        if (event.evt_type == NRF_DRV_SPIS_XFER_DONE)
        {
            spis_xfer_done = true;
            NRF_LOG_INFO(" Transfer completed. Received: %s",(uint32_t)m_rx_buf);
            printf("recieved : %s", m_rx_buf );
        }
    }
    
    int main(void)
    {
        // Enable the constant latency sub power mode to minimize the time it takes
        // for the SPIS peripheral to become active after the CSN line is asserted
        // (when the CPU is in sleep mode).
        NRF_POWER->TASKS_CONSTLAT = 1;
    
        bsp_board_init(BSP_INIT_LEDS);
    
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        NRF_LOG_INFO("SPIS example");
    
        nrf_drv_spis_config_t spis_config = NRF_DRV_SPIS_DEFAULT_CONFIG;
        spis_config.csn_pin               = APP_SPIS_CS_PIN;
        spis_config.miso_pin              = APP_SPIS_MISO_PIN;
        spis_config.mosi_pin              = APP_SPIS_MOSI_PIN;
        spis_config.sck_pin               = APP_SPIS_SCK_PIN;
    
        APP_ERROR_CHECK(nrf_drv_spis_init(&spis, &spis_config, spis_event_handler));
    
        while (1)
        {
            memset(m_rx_buf, 0, m_length);
            spis_xfer_done = false;
    
            APP_ERROR_CHECK(nrf_drv_spis_buffers_set(&spis, m_tx_buf, m_length, m_rx_buf, m_length));
    
            while (!spis_xfer_done)
            {
                __WFE();
            }
    
            NRF_LOG_FLUSH();
    
            bsp_board_led_invert(BSP_BOARD_LED_0);
        }
    }
    

  • Also, in the code attached, if my m_length = 4. Will an SPI transaction end after every 8bit transfer and CS line will go high again or will the transaction end after all the 4 bytes are sent ???? 

     while (1)
        {
            // Reset rx buffer and transfer done flag
            memset(m_rx_buf, 0, m_length);
            spi_xfer_done = false;
    
            APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &start_write, m_length, m_rx_buf, m_length));
            APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &config_addr, m_length, m_rx_buf, m_length));
            APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &config_lowvalue, m_length, m_rx_buf, m_length));
            APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &config_highvalue, m_length, m_rx_buf, m_length));
    
            while (!spi_xfer_done)
            {
                __WFE();
            }
    
            NRF_LOG_FLUSH();
    
            bsp_board_led_invert(BSP_BOARD_LED_0);
            nrf_delay_ms(200)

  • Hi.

    The SPI Master and SPI Slave example in the SDK can be used without any modification and demonstrates sending the string "Nordic" in both directions. The only thing you do is to flash both boards with the example and hook them up as specified in the SPI Slave Example documentation.

    The length parameters defines the length of a transaction, yes. So if you set 4 for both Rx and Tx the transaction will be 4 bytes.

  • Thanks Einar, I did not realize it will be so difficult to understand an SPI implementation. Can you help me with this very specific short example : I want to write 0X0030 and then 0X5678 to slave.  In the attached code snippet, i am experiencing a very weird behavior. I have set m_length and rx_buffer to 2 as i am making a 16bit transaction. And i am receiving 0X0030 multiple times before i receive 0X5678. I will appreciate a lot if you could help me with this example .

    Thanks

    #define SPI_INSTANCE  0 /**< SPI instance index. */
    static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);  /**< SPI instance. */
    static volatile bool spi_xfer_done;  /**< Flag used to indicate that SPI instance completed the transfer. */
    
    #define TEST_STRING "ashu"
    static uint16_t       m_tx_buf = 0b0011000000000000;           /**< TX buffer. */
    static uint16_t       m_tx_buf1 =0b0111100001010110;           /**< TX buffer. */
    static uint16_t       m_rx_buf[2];    /**< RX buffer. */
    static const uint8_t m_length = 2;        /**< Transfer length. */
    
    /**
     * @brief SPI user event handler.
     * @param event
     */
    void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
                           void *                    p_context)
    {
        spi_xfer_done = true;
        NRF_LOG_INFO("Transfer completed.");
        if (m_rx_buf[0] != 0)
        {
            NRF_LOG_INFO(" Received:");
            NRF_LOG_HEXDUMP_INFO(m_rx_buf, strlen((const char *)m_rx_buf));
        }
    }
    
    int main(void)
    {
        bsp_board_init(BSP_INIT_LEDS);
    
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
        spi_config.ss_pin   = SPI_SS_PIN;
        spi_config.miso_pin = SPI_MISO_PIN;
        spi_config.mosi_pin = SPI_MOSI_PIN;
        spi_config.sck_pin  = SPI_SCK_PIN;
        APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
    
        NRF_LOG_INFO("SPI example started.");
    
        while (1)
        {
            // Reset rx buffer and transfer done flag
            memset(m_rx_buf, 0, m_length);
            spi_xfer_done = false;
    
            APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &m_tx_buf, m_length, m_rx_buf, m_length));
    
            while (!spi_xfer_done)
            {
                __WFE();
            }
    
            NRF_LOG_FLUSH();
    
             nrf_gpio_pin_set(BSP_BOARD_LED_0);
            nrf_delay_ms(200);
          
    
    
    
              memset(m_rx_buf, 0, m_length);
            spi_xfer_done = false;
    
            APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &m_tx_buf1, m_length, m_rx_buf, m_length));
    
            while (!spi_xfer_done)
            {
                __WFE();
            }
    
            NRF_LOG_FLUSH();
    
            bsp_board_led_invert(BSP_BOARD_LED_1);
            nrf_delay_ms(200);
       
        }
    }
    

Reply
  • Thanks Einar, I did not realize it will be so difficult to understand an SPI implementation. Can you help me with this very specific short example : I want to write 0X0030 and then 0X5678 to slave.  In the attached code snippet, i am experiencing a very weird behavior. I have set m_length and rx_buffer to 2 as i am making a 16bit transaction. And i am receiving 0X0030 multiple times before i receive 0X5678. I will appreciate a lot if you could help me with this example .

    Thanks

    #define SPI_INSTANCE  0 /**< SPI instance index. */
    static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);  /**< SPI instance. */
    static volatile bool spi_xfer_done;  /**< Flag used to indicate that SPI instance completed the transfer. */
    
    #define TEST_STRING "ashu"
    static uint16_t       m_tx_buf = 0b0011000000000000;           /**< TX buffer. */
    static uint16_t       m_tx_buf1 =0b0111100001010110;           /**< TX buffer. */
    static uint16_t       m_rx_buf[2];    /**< RX buffer. */
    static const uint8_t m_length = 2;        /**< Transfer length. */
    
    /**
     * @brief SPI user event handler.
     * @param event
     */
    void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
                           void *                    p_context)
    {
        spi_xfer_done = true;
        NRF_LOG_INFO("Transfer completed.");
        if (m_rx_buf[0] != 0)
        {
            NRF_LOG_INFO(" Received:");
            NRF_LOG_HEXDUMP_INFO(m_rx_buf, strlen((const char *)m_rx_buf));
        }
    }
    
    int main(void)
    {
        bsp_board_init(BSP_INIT_LEDS);
    
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
        spi_config.ss_pin   = SPI_SS_PIN;
        spi_config.miso_pin = SPI_MISO_PIN;
        spi_config.mosi_pin = SPI_MOSI_PIN;
        spi_config.sck_pin  = SPI_SCK_PIN;
        APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
    
        NRF_LOG_INFO("SPI example started.");
    
        while (1)
        {
            // Reset rx buffer and transfer done flag
            memset(m_rx_buf, 0, m_length);
            spi_xfer_done = false;
    
            APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &m_tx_buf, m_length, m_rx_buf, m_length));
    
            while (!spi_xfer_done)
            {
                __WFE();
            }
    
            NRF_LOG_FLUSH();
    
             nrf_gpio_pin_set(BSP_BOARD_LED_0);
            nrf_delay_ms(200);
          
    
    
    
              memset(m_rx_buf, 0, m_length);
            spi_xfer_done = false;
    
            APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &m_tx_buf1, m_length, m_rx_buf, m_length));
    
            while (!spi_xfer_done)
            {
                __WFE();
            }
    
            NRF_LOG_FLUSH();
    
            bsp_board_led_invert(BSP_BOARD_LED_1);
            nrf_delay_ms(200);
       
        }
    }
    

Children
  • Hi,

    I do not see anything in your code that should explain repeating the first transaction. The only thing is that you do not explicitly wait for the one transaction to finish before you start another one, but the 200 ms delay should take care of it. If this was a problem (which I do not think) and you have not defined DEBUG for your project, then the NRF_ERROR_BUSY returned from nrf_drv_spi_transfer() would trigger a system reset, which in turn would lead to sending the initial data repeatedly.

    • Have you verified that the device is not resetting when you see this?
    • Do you use a project where DEBUG is defined (just select Debug in the Build Configuration dropdown if you are using SES)?
    • Can you upload a logic analyzer trace so that we can see what is happening on the SPI lines?
  • Thanks, Einar,

    Surprisingly, it worked after removing the explicit wait. But my peripheral is not responding to any command. I am supposed to send a write command like in this attached sequence .  

    I am sending a 32 bit tx_buffer with m_length = 1 . Do you

    static uint32_t       m_tx_buf = 0b00000000001100000101011001111000;           /**< TX buffer. */
    
    
    
    static uint32_t       m_rx_buf[1];    /**< RX buffer. */
    static const uint8_t m_length = 4;        /**< Transfer length. */
    
    /**
     * @brief SPI user event handler.
     * @param event
     */
    void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
                           void *                    p_context)
    {
        spi_xfer_done = true;
        NRF_LOG_INFO("Transfer completed.");
        if (m_rx_buf[0] != 0)
        {
            NRF_LOG_INFO(" Received:");
            NRF_LOG_HEXDUMP_INFO(m_rx_buf, strlen((const char *)m_rx_buf));
        }
    }
    
    int main(void)
    {
        bsp_board_init(BSP_INIT_LEDS);
    
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
        spi_config.ss_pin   = SPI_SS_PIN;
        spi_config.miso_pin = SPI_MISO_PIN;
        spi_config.mosi_pin = SPI_MOSI_PIN;
        spi_config.sck_pin  = SPI_SCK_PIN;
        spi_config.mode = NRF_DRV_SPI_MODE_2;
        APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
    
        NRF_LOG_INFO("SPI example started.");
    
            // send 30H
            memset(m_rx_buf, 0, m_length);
            spi_xfer_done = false;
            APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &m_tx_buf, m_length, m_rx_buf, m_length));
            NRF_LOG_FLUSH();
            nrf_delay_ms(200);
     
    }
    
    find any issue in my code snippet before I talk to the IC manufacturer ?

  • Hi,

    I do not see any problems with this code. I think we need two things in order to understand more:

    • Datasheet of the SPI slave (what is it?) so that we can check if you have set the correct SPI mode.
    • Logic analyzer trace of the SPI lines so that we can see what is actually going on.
  • Hi Einar,

    Thanks for helping me with this. PFA the datasheet and an image where you can read about the modes. I assume, it says the data will be shifted on SDI using SPI mode 2 and on SDO using SPI mode 3. isn't that right? So once I write the data on SDI line, would i have to uninitialize the SPI first to configure it to mode 3 or can i directly change the mode ? 

    Also, find attached the trace of SCK and MOSI SPI lines.

    Can you figure out whats going wrong?

    Appreciate it!!

    Atmel-46004-SE-M90E36A-Datasheet.pdf

  • Hi Einar,

    One more trace of when I send 0X00305678 / 0b00000000001100000101011001111000

Related