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

NRFX SPIM write and read

Hi team,

I want to make simple communication between NRF52840 dongle and mcp2517 CAN FD contoller using SPI. I do not understand, how to send and read values to and from registers. Same SPI communication works well between dongles.

I set the register value

// Defined register values for MCP
#define cINSTRUCTION_WRITE	     0x02

Set the SPI instance index and flag 

/**< SPI instance index. */
static const nrfx_spim_t spi = NRFX_SPIM_INSTANCE(SPI_INSTANCE);  /**< SPI instance. */

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

Created two buffers for transfer and receive and set transfer length

static uint8_t          m_tx_buf[5];
//static uint8_t        cINSTRUCTION_WRITE_m_rx_buf[5];  /**< RX buffer. */
static uint8_t          m_rx_buf[5];  /**< RX buffer. */
static const uint8_t    m_length = sizeof(m_tx_buf);        /**< Transfer length. */

SPIM event handler function 

void spim_event_handler(nrfx_spim_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));
    }
    */
}

In the main function I set function for starting the SPI data transfer with DCX control with SPI config

 nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(m_tx_buf, m_length, m_rx_buf, m_length);

nrfx_spim_config_t spi_config = NRFX_SPIM_DEFAULT_CONFIG;
    spi_config.frequency      = NRF_SPIM_FREQ_1M;
    spi_config.ss_pin         = NRFX_SPIM_SS_PIN;
    spi_config.miso_pin       = NRFX_SPIM_MISO_PIN;
    spi_config.mosi_pin       = NRFX_SPIM_MOSI_PIN;
    spi_config.sck_pin        = NRFX_SPIM_SCK_PIN;
    spi_config.dcx_pin        = NRFX_SPIM_DCX_PIN;
    spi_config.use_hw_ss      = true;
    spi_config.ss_active_high = false;
    APP_ERROR_CHECK(nrfx_spim_init(&spi, &spi_config, spim_event_handler, NULL));

And in the while loop I should start writing and reading registers.  Here I start to face with the problems. How I can  prepare tx_buf with values and how to start send values and read it from register? 

Sorry about huge misunderstanding, but I need to make things clear. 

  • Hi,

    I'm not sure I understand your questions. You can assign values to tx_buf as normal, for instance like this:

    m_tx_buf[0] = 0x01;
    m_tx_buf[1] = 0x02;
    m_tx_buf[2] = 0x03;
    m_tx_buf[3] = 0x04;
    m_tx_buf[4] = 0x05;

    In your code, you have already created a transfer description struck, xfer_desc, which points to the TX/RX buffers and their length. You can pass this to the function nrfx_spim_xfer/nrfx_spim_xfer_dcx to start the transfer. 

    Best regards,
    Jørgen

  • Thank you for your answer, Jorgen.

    I looked more closely to Nrfx spim and spi demo programs on the SDK, and I tried to switch to SPIM master example and I'm trying to create SPI communication on it. When I try to read register, I got the same value every time. Is the first byte transferred is the register address ? Or I'm wrong? How to add some value to send and how to read that value? According to MCP2517 API, for test purposes I need to write some data to CiFLTOBJ register (address - 0x1F0) and after that I need to read the same register.

    My current code (with USB CDC):

    
    #include "C:\Users\user1\Desktop\main.h"
    
    #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. */
    
    
    /* Buffer for MCP2517 config */
    //cREGADDR_CiFLTOBJ 
    static uint8_t          m_config_buf1[] = {((uint8_t)0x1F0)}; 
    
    
    /* send value buf */
    static uint8_t          m_tx_rx_buf1[] = {0x2};
    
    
    /* Rx buffer */
    static uint8_t          m_rx_buf1[sizeof(m_tx_rx_buf1) + 1];    
    
    
    /*Rx buf length*/
    static const uint8_t    m_length_rx = sizeof(m_tx_rx_buf1);       
    
    
    
    
    
    void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
                           void *                    p_context)
    {
        spi_xfer_done = true;
    }
    
    
    
    
    static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                        app_usbd_cdc_acm_user_event_t event);
    
    
    
    /**
     * @brief CDC_ACM class instance
     * */
    APP_USBD_CDC_ACM_GLOBAL_DEF(m_app_cdc_acm,
                                cdc_acm_user_ev_handler,
                                CDC_ACM_COMM_INTERFACE,
                                CDC_ACM_DATA_INTERFACE,
                                CDC_ACM_COMM_EPIN,
                                CDC_ACM_DATA_EPIN,
                                CDC_ACM_DATA_EPOUT,
                                APP_USBD_CDC_COMM_PROTOCOL_AT_V250
    );
    
    #define READ_SIZE 1
    
    static char m_rx_buffer[READ_SIZE];
    static char m_tx_buffer[NRF_DRV_USBD_EPSIZE];
    static char m_tx_buffer1[NRF_DRV_USBD_EPSIZE];
    static bool m_send_flag = 0;
    
    
    static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                        app_usbd_cdc_acm_user_event_t event)
    {
        app_usbd_cdc_acm_t const * p_cdc_acm = app_usbd_cdc_acm_class_get(p_inst);
    
        switch (event)
        {
            case APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN:
            {
                bsp_board_led_on(LED_CDC_ACM_OPEN);
    
                /*Setup first transfer*/
                ret_code_t ret = app_usbd_cdc_acm_read(&m_app_cdc_acm,
                                                       m_rx_buffer,
                                                       READ_SIZE);
                UNUSED_VARIABLE(ret);
                break;
            }
            case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE:
                bsp_board_led_off(LED_CDC_ACM_OPEN);
                break;
            case APP_USBD_CDC_ACM_USER_EVT_TX_DONE:
                bsp_board_led_invert(LED_CDC_ACM_TX);
                break;
            case APP_USBD_CDC_ACM_USER_EVT_RX_DONE:
            {
                ret_code_t ret;
    //            NRF_LOG_INFO("Bytes waiting: %d", app_usbd_cdc_acm_bytes_stored(p_cdc_acm));
                app_usbd_cdc_acm_bytes_stored(p_cdc_acm);
                do
                {
                    /*Get amount of data transfered*/
                    size_t size = app_usbd_cdc_acm_rx_size(p_cdc_acm);
    //                NRF_LOG_INFO("RX: size: %lu char: %c", size, m_rx_buffer[0]);
    
                    /* Fetch data until internal buffer is empty */
                    ret = app_usbd_cdc_acm_read(&m_app_cdc_acm,
                                                m_rx_buffer,
                                                READ_SIZE);
                } while (ret == NRF_SUCCESS);
    
                memcpy(m_tx_buffer,m_rx_buffer,READ_SIZE);
                app_usbd_cdc_acm_write(&m_app_cdc_acm, m_tx_buffer, READ_SIZE); //
    
                bsp_board_led_invert(LED_CDC_ACM_RX);
                break;
            }
            default:
                break;
        }
    }
    
    static void usbd_user_ev_handler(app_usbd_event_type_t event)
    {
        switch (event)
        {
            case APP_USBD_EVT_DRV_SUSPEND:
                bsp_board_led_off(LED_USB_RESUME);
                break;
            case APP_USBD_EVT_DRV_RESUME:
                bsp_board_led_on(LED_USB_RESUME);
                break;
            case APP_USBD_EVT_STARTED:
                break;
            case APP_USBD_EVT_STOPPED:
                app_usbd_disable();
                bsp_board_leds_off();
                break;
            case APP_USBD_EVT_POWER_DETECTED:
    //            NRF_LOG_INFO("USB power detected");
    
                if (!nrf_drv_usbd_is_enabled())
                {
                    app_usbd_enable();
                }
                break;
            case APP_USBD_EVT_POWER_REMOVED:
    //            NRF_LOG_INFO("USB power removed");
                app_usbd_stop();
                break;
            case APP_USBD_EVT_POWER_READY:
    //            NRF_LOG_INFO("USB ready");
                app_usbd_start();
                break;
            default:
                break;
        }
    }
    /// end for USB CDC
    
    
    int main(void)
    {
    
    
    ret_code_t ret;
      static const app_usbd_config_t usbd_config = {
          .ev_state_proc = usbd_user_ev_handler
      };
    
        bsp_board_init(BSP_INIT_LEDS);
        ///start clock set mandatory for USB CDC
        nrf_drv_clock_init(); // for HF 32MHz external X-tal
        nrf_drv_clock_lfclk_request(NULL); // for LF 32.768kHz external X-tal
        while(!nrf_drv_clock_lfclk_is_running()) {
          // Just waiting 
     }
        /// end clock set
        app_usbd_serial_num_generate();
        ret = app_usbd_init(&usbd_config);
        APP_ERROR_CHECK(ret);
    
    
        app_usbd_class_inst_t const * class_cdc_acm = app_usbd_cdc_acm_class_inst_get(&m_app_cdc_acm);
        ret = app_usbd_class_append(class_cdc_acm);
        APP_ERROR_CHECK(ret);
    
      if (USBD_POWER_DETECTION) {
          ret = app_usbd_power_events_enable();
          APP_ERROR_CHECK(ret);
      }
      else {
          app_usbd_enable();
          app_usbd_start();
      }
    
    
    
        nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
        //spi_config.ss_pin   = SPI_SS_PIN;
        spi_config.ss_pin   = NRF_DRV_SPI_PIN_NOT_USED;
        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));
    
    
        // Now enable a pin to work as the chip select
        nrf_gpio_cfg_output(SPI_SS_PIN);
        nrf_gpio_pin_set(SPI_SS_PIN);
    
    
        // Specify blocking operation
        APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
    
    
    
        while (1)
        {
    
        uint16_t address;
        uint16_t random_reiksme = rand();
        char tx[20];
        char rx[3];
        
        static int  frame_counter;               // USB CDC buferis
        static int  frame_counter1;              // USB CDC buferis
    
    
      // Reset rx buffer and transfer done flag
            memset(m_rx_buf1, 0, m_length_rx);
    
            spi_xfer_done = false;
    
            APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_config_buf1, sizeof(m_config_buf1), NULL, NULL));
            nrf_delay_ms(20);
            APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_tx_rx_buf1, m_length_rx, m_rx_buf1, m_length_rx));
    
        while (spi_xfer_done)
          {
             app_usbd_event_queue_process();
             bsp_board_led_invert(BSP_BOARD_LED_3);
    
        sprintf(tx,"%d",m_rx_buf1);
    
        size_t size = sprintf(m_tx_buffer,tx,frame_counter);
        size_t size1 = sprintf(m_tx_buffer,"\n\r",frame_counter1);
        ret = app_usbd_cdc_acm_write(&m_app_cdc_acm, m_tx_buffer, size);
    
        bsp_board_led_invert(BSP_BOARD_LED_0);
        nrf_delay_ms(100);
    
    
            nrf_gpio_pin_clear(SPI_SS_PIN);
            }
        }
    }
    

  • Typically, the first TX byte is the register address, yes. You seem to have set both tx length and rx length parameters to 1 byte. Note that the RX buffer will be filled when the TX byte is clocked out, not just afterwards. Since the SPI device typically does not know what to respond with before after it has received the entire register address in the TX-buffer, you need to set the RX length to the length of the TX-buffer + the expected response. You can ignore the first byte in the RX-buffer that was clocked out during the TX-part of the transfer.

  • Hello, our current system is using the NRF52840 with SDK 17.0.2. Our display is now working correctly with 

    nrfx_spim_xfer_dcx() as the call. However, for non-display SPI clients, should we use
    nrfx_spim_xfer() instead as we don't need to differentiate between command and data? 

  • Yes, if the SPI slave device does not support DCX, you can use nrfx_spim_xfer().

Related