NRF52832 - Send and receive up to 300 bytes via SPI

Hello,

I have 2x PCA10040 dev kits connected via SPI.

Device #1 is talking to a custom board MCU by UART

Device #2 is connected by BT to my android app

The task is to relay communication between the android app and the custom board. Communication can happen at any time in any direction. Maximum payload is 300 bytes.

I set Device #1 as SPIM, and I created a test payload of 300 bytes which is to be sent every 500ms to the slave device (Device #2).

Since the maximum SPI payload is around 256, I put together some code that should send 2 transfers in case the payload is larger.

#define SPI_PACKET_SIZE 250
static uint8_t m_spi_tx_dma_buf[SPI_PACKET_SIZE + 1];
static uint8_t m_spi_rx_dma_buf[SPI_PACKET_SIZE + 1];

const nrfx_spim_t radio_spi = NRFX_SPIM_INSTANCE(0);

void radio_spi_init()
{
    nrfx_spim_config_t spi_config = NRFX_SPIM_DEFAULT_CONFIG;
    spi_config.frequency      = NRF_SPIM_FREQ_8M;
    spi_config.ss_pin         = SPIRADIO_CS;
    spi_config.miso_pin       = SPIRADIO_MISO;
    spi_config.mosi_pin       = SPIRADIO_MOSI;
    spi_config.sck_pin        = SPIRADIO_SCK;
    spi_config.bit_order = NRF_SPI_BIT_ORDER_LSB_FIRST;
    spi_config.ss_active_high = false;
    APP_ERROR_CHECK(nrfx_spim_init(&radio_spi, &spi_config, radio_spi_handler, NULL));
}

uint32_t radio_spi_write(const uint8_t *data, uint16_t len)
{
  uint32_t err_code;
  uint32_t pos = 0;
  uint8_t spi_tx_one_byte = 0;

  while (len > 0) {
      radio_spi_transfer_finished = false;
      if (len < SPI_PACKET_SIZE) {
        spi_tx_one_byte = len;
        m_spi_tx_dma_buf[0] = spi_tx_one_byte;
        memcpy(m_spi_tx_dma_buf + 1, &data[pos], len);
        len = 0;
      } else {
        spi_tx_one_byte = SPI_PACKET_SIZE;
        m_spi_tx_dma_buf[0] = spi_tx_one_byte;
        memcpy(m_spi_tx_dma_buf + 1, &data[pos], SPI_PACKET_SIZE - 1);
        pos += (SPI_PACKET_SIZE - 1);
        len -= (SPI_PACKET_SIZE - 1);
      }
              
      uint8_t rx[1];
      nrf_delay_ms(1);
      nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(m_spi_tx_dma_buf, spi_tx_one_byte, rx, 0);
      
      if (NRFX_SUCCESS != nrfx_spim_xfer(&radio_spi, &xfer_desc, 0)) {
        NRF_LOG_INFO("SPI WRITE FAILED!");
        goto write_exit;
      } else {
        //NRF_LOG_INFO("Sent %i bytes!",spi_tx_one_byte);
      }
      while (!radio_spi_transfer_finished)
        ;
      
    }
write_exit:
    return 0;
}


//the 300 bytes test payload

uint8_t spi_test_payload[] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
                                0xBB, 0xBB, 0xBB, 0xBB, 0xBB,0xBB, 0xBB, 0xBB, 0xBB, 0xBB,0xBB, 0xBB, 0xBB, 0xBB, 0xBB,0xBB, 0xBB, 0xBB, 0xBB, 0xBB,0xBB, 0xBB, 0xBB, 0xBB, 0xBB,0xBB, 0xBB, 0xBB, 0xBB, 0xBB,0xBB, 0xBB, 0xBB, 0xBB, 0xBB,0xBB, 0xBB, 0xBB, 0xBB, 0xBB,0xBB, 0xBB, 0xBB, 0xBB, 0xBB,0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
                                0xCC, 0xCC, 0xCC, 0xCC, 0xCC,0xCC, 0xCC, 0xCC, 0xCC, 0xCC,0xCC, 0xCC, 0xCC, 0xCC, 0xCC,0xCC, 0xCC, 0xCC, 0xCC, 0xCC,0xCC, 0xCC, 0xCC, 0xCC, 0xCC,0xCC, 0xCC, 0xCC, 0xCC, 0xCC,0xCC, 0xCC, 0xCC, 0xCC, 0xCC,0xCC, 0xCC, 0xCC, 0xCC, 0xCC,0xCC, 0xCC, 0xCC, 0xCC, 0xCC,0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
                                0xDD, 0xDD, 0xDD, 0xDD, 0xDD,0xDD, 0xDD, 0xDD, 0xDD, 0xDD,0xDD, 0xDD, 0xDD, 0xDD, 0xDD,0xDD, 0xDD, 0xDD, 0xDD, 0xDD,0xDD, 0xDD, 0xDD, 0xDD, 0xDD,0xDD, 0xDD, 0xDD, 0xDD, 0xDD,0xDD, 0xDD, 0xDD, 0xDD, 0xDD,0xDD, 0xDD, 0xDD, 0xDD, 0xDD,0xDD, 0xDD, 0xDD, 0xDD, 0xDD,0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
                                0xEE, 0xEE, 0xEE, 0xEE, 0xEE,0xEE, 0xEE, 0xEE, 0xEE, 0xEE,0xEE, 0xEE, 0xEE, 0xEE, 0xEE,0xEE, 0xEE, 0xEE, 0xEE, 0xEE,0xEE, 0xEE, 0xEE, 0xEE, 0xEE,0xEE, 0xEE, 0xEE, 0xEE, 0xEE,0xEE, 0xEE, 0xEE, 0xEE, 0xEE,0xEE, 0xEE, 0xEE, 0xEE, 0xEE,0xEE, 0xEE, 0xEE, 0xEE, 0xEE,0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
                                0xFF, 0xFF, 0xFF, 0xFF, 0xFF,0xFF, 0xFF, 0xFF, 0xFF, 0xFF,0xFF, 0xFF, 0xFF, 0xFF, 0xFF,0xFF, 0xFF, 0xFF, 0xFF, 0xFF,0xFF, 0xFF, 0xFF, 0xFF, 0xFF,0xFF, 0xFF, 0xFF, 0xFF, 0xFF,0xFF, 0xFF, 0xFF, 0xFF, 0xFF,0xFF, 0xFF, 0xFF, 0xFF, 0xFF,0xFF, 0xFF, 0xFF, 0xFF, 0xFF,0xFF, 0xFF, 0xFF, 0xFF, 0xFF};


  //////////////////RADIO TEST/////////////////
  while(1){
    radio_spi_write(spi_test_payload, sizeof spi_test_payload);
    nrf_delay_ms(500);
  }

On the receiving side I have this code:

#define SPIS_INSTANCE 1                                                  /**< SPIS instance index. */
static const nrf_drv_spis_t spis = NRF_DRV_SPIS_INSTANCE(SPIS_INSTANCE); /**< SPIS instance. */

static uint8_t m_tx_buf[250];          /**< TX buffer. */
static uint8_t m_rx_buf[250]; /**< 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;
    send_data_ble(m_rx_buf);
  }
}




/**
 * @brief Send data via bluetooth.
 *
 * @param payload
 */

void send_data_ble(uint8_t *payload) {
  uint32_t err_code;
  do {
    uint16_t length = sizeof(payload);
    uint32_t err_code = ble_nus_data_send(&m_nus, payload, &length, m_conn_handle);
    if ((err_code != NRF_ERROR_INVALID_STATE) &&
        (err_code != NRF_ERROR_RESOURCES) &&
        (err_code != NRF_ERROR_NOT_FOUND)) {
      APP_ERROR_CHECK(err_code);
    }
  } while (err_code == NRF_ERROR_RESOURCES);
}





int main(void) {

  nrf_gpio_pin_dir_set(CUSTOM_SPI_IRQ_PIN, NRF_GPIO_PIN_DIR_OUTPUT);
  nrf_gpio_pin_clear(CUSTOM_SPI_IRQ_PIN);

  bool erase_bonds;

  // Initialize.
  log_init();
  timers_init();
  power_management_init();
  ble_stack_init();
  gap_params_init();
  gatt_init();
  services_init();
  advertising_init();
  conn_params_init();

  // 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;

  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;
  spis_config.bit_order = 1;

  APP_ERROR_CHECK(nrf_drv_spis_init(&spis, &spis_config, spis_event_handler));

  advertising_start();

  uint32_t err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, m_advertising.adv_handle, 4); //increase power to maximum
  APP_ERROR_CHECK(err_code); 

  // Enter main loop.
  static uint8_t test_ble[] = {0xAA,0xBB,0xCC};
  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();
      
    }
  }
}

I am just getting started in the embedded dev space and this is all new to me. My question is, how can I make my slave process those 2 transactions in a single buffer and send it via Bluetooth? Is this the right approach? Anything else I can do to my code to optimize it? Is that 1ms delay on the master side required or is the system fast enough to process everything without it?

Thanks in advance

Parents
  • Hi 

    When it comes to SPI the chip select line is used to signal the start and end of a transaction, so as long as you don't pull the chip select low for each transaction, but pull it low for the duration of both transactions, the slave should read it as one transaction. 

    With that being said, there is no 256 byte limit on the SPIM or SPIS interfaces in the nRF52840. 

    If you take a look at the TXD or RXD MAXCNT registers (like this one) you will see that they can be as large as 0xFFFF, or 65535 bytes. 

    Most likely you are thinking of the nRF52832, which was the first device in the nRF52 series, and was a bit more limited in terms of how large EasyDMA buffers you could use. 

    Best regards
    Torbjørn 

  • Thank you for the reply.

    Yes, as stated in the subject of my question, I am using nrf52832.

    So the trick with keeping the line low will work for the 52832 also?

    If not, what are my alternatives? Any example code?

    Please note, the communication must be bi-directional, and it can happen in either direction at any arbitrary time.

  • Hi

    This code shouldn't have to be much more complicated than the code you've already written, but I don't mind writing a small example for it, Christmas or not Wink

    I'll try to have it done by the end of the week. 

    Best regards
    Torbjørn

  • Thank you for that.

    Meanwhile, I have put together some code, and I would like to know how it can be optimized.

    Master sends to slave.

    Slave sends to master

    but after slave sends to master, master cannot send to slave anymore...

    SPI Slave Code:

    uint16_t total_data_length = 0;
    /**
     * @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) {
      uint16_t real_len;
        
      if (!spis_tx_mode)
        {
    			
    			if(!data_read_pending)		//if this is first/single transaction, 
    			{
    				total_data_length = bytes_to_u16(m_spi_rx_dma_buf[0],m_spi_rx_dma_buf[1]);		//get the payload size from the first 2 bytes
    			}
    			if(total_data_length > 250)		//payload size is greater then 250 
    			{
    				if(!data_read_pending)		//is this the first transaction?
    				{
    					//this is first transaction
    					memcpy(rx_data1, m_spi_rx_dma_buf, 252);
    					rx_data_len = total_data_length;
    					data_read_pending = true;
    				}
    				else
    				{
    					//this is the second transaction
    					uint16_t bytes_left = total_data_length - 250;
    					memcpy(rx_data1+252, m_spi_rx_dma_buf, bytes_left);
    					rx_data_len = total_data_length;
                                            data_read_pending = false;
                                            NRF_LOG_INFO("cnt: %d, received: %d, one: %X, last %X\n", rcv_cnt,rx_data_len, rx_data1[0], rx_data1[299]);
                                            rcv_cnt = rcv_cnt+1;
                                            //send_data_ble(rx_data); //here we need to ship this data by bluetooth (complete frame)
    				    total_data_length = 0;
    				}
    			}
    			else //payload size less than 250, this is a single transaction
    			{
    				
    				if(!data_read_pending) //we should never have this flag set to true, since this is a single transaction
    				{
    					memcpy(rx_data1+252, m_spi_rx_dma_buf, total_data_length);
    					rx_data_len = total_data_length;
                                            NRF_LOG_INFO("cnt: %d, received: %d, one: %X, last %X\n", rcv_cnt,rx_data_len, rx_data1[0], rx_data1[299]);
                                            rcv_cnt = rcv_cnt+1;
                                            //send_data_ble(rx_data); //here we need to ship this data by bluetooth (complete mini-frame)
    				    total_data_length = 0;
    				}
    				else
    				{
                                            NRF_LOG_INFO("CRITICAL FAILURE"); //data_read_pending should never be true for less than 250 bytes frames
    					
    				}
    			}			
    			
    			nrf_drv_spis_buffers_set(&spis, NULL, 0, m_spi_rx_dma_buf, sizeof m_spi_rx_dma_buf - 1); // problem here
        }
    
        spis_xfer_done = true;
        
      }
    }
    
    /**
     * @brief SPIS TX function.
     *
     * @param event
     */
    uint32_t spi_tx(uint8_t *data, uint32_t len) {
      uint32_t err_code;
      int i = 0;
      spis_tx_mode = 1;
      // check how many packets need to be sent if we send only 1 packet at a time with max size of 250 bytes
      uint8_t total_packets = 0; // total packets to send
      uint8_t packet_len = 0;
      if (len > 250) {
        total_packets = 2;
        packet_len = 250;
      } else {
        total_packets = 1;
        packet_len = len;
      }
    
      while (i < total_packets) {
        // split each packet and send
        uint8_t a = 0;
        uint8_t packet_to_send[255];
        if (i == 0) {
          // add the headers
          for (a = 0; a < packet_len; a++) {
            // keep space for headers in the first transaction
            packet_to_send[a + 2] = data[(i * 250) + a];
          }
        } else {
          for (a = 0; a < packet_len; a++) {
            // do not add any headers
            packet_to_send[a] = data[(i * 250) + a];
          }
        }
        i++;
        if (i != total_packets) {
          // add packet length in the first 2 bytes
          packet_to_send[0] = len >> 8;
          packet_to_send[1] = len;
        }
        // send the packet
        spis_xfer_done = false;
        if (i != total_packets) {
          // if its the first transaction, add 2 bytes count in the packet length
          err_code = nrf_drv_spis_buffers_set(&spis, packet_to_send, packet_len + 2, NULL, 0); // length byte + data
          VERIFY_SUCCESS(err_code);
        } else {
          err_code = nrf_drv_spis_buffers_set(&spis, packet_to_send, packet_len, NULL, 0); // length byte + data
          VERIFY_SUCCESS(err_code);
        }
        // NRF_LOG_INFO("Prepared %i bytes", packet_len);
        if (i == 1) {
          nrf_gpio_pin_set(CUSTOM_SPI_IRQ_PIN);
          nrf_delay_us(100);
          nrf_gpio_pin_clear(CUSTOM_SPI_IRQ_PIN);
        }
        while (!spis_xfer_done) {
          __WFE();
        }
        int d = 0;
        // calculate next data length
        if (i == total_packets) {
          // all data is sent
        } else if ((total_packets - i) == 1) {
          // only one packet left
          packet_len = len - 250;
        }
      }
      spis_tx_mode = 0;
    
      return NRF_SUCCESS;
    }
    
    /**
     * @brief Send data via bluetooth.
     *
     * @param payload
     */
    
    void send_data_ble(uint8_t *payload, uint8_t size) {
      uint32_t err_code;
      do {
        uint16_t length = size;
        uint32_t err_code = ble_nus_data_send(&m_nus, payload, &length, m_conn_handle);
        if ((err_code != NRF_ERROR_INVALID_STATE) &&
            (err_code != NRF_ERROR_RESOURCES) &&
            (err_code != NRF_ERROR_NOT_FOUND)) {
          APP_ERROR_CHECK(err_code);
        }
      } while (err_code == NRF_ERROR_RESOURCES);
    }
    
    /**@brief Application main function.
     */
    int main(void) {
    
      nrf_gpio_pin_dir_set(CUSTOM_SPI_IRQ_PIN, NRF_GPIO_PIN_DIR_OUTPUT);
      nrf_gpio_pin_clear(CUSTOM_SPI_IRQ_PIN);
    
      // Initialize.
      log_init();
      timers_init();
      power_management_init();
      ble_stack_init();
      gap_params_init();
      gatt_init();
      services_init();
      advertising_init();
      conn_params_init();
      // nrf_gpio_cfg_output(8);
    
      // 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;
    
      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;
      spis_config.orc = 0x55; // this one
      spis_config.bit_order = 1;
      uint32_t err_code;
      APP_ERROR_CHECK(nrf_drv_spis_init(&spis, &spis_config, spis_event_handler));
      err_code = nrf_drv_spis_buffers_set(&spis, NULL, 0, m_spi_rx_dma_buf, sizeof m_spi_rx_dma_buf - 1); // problem here
      VERIFY_SUCCESS(err_code);
    
      advertising_start();
    
      err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, m_advertising.adv_handle, 4); // increase power to maximum
      APP_ERROR_CHECK(err_code);
      uint8_t spi_test_payload[] = {0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
          0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
          0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
          0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
          0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
          0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44};
    
      // Enter main loop.
      uint8_t small_payload[14] = {0x2f, 0x38, 0xEF, 0xAB, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11};
    
      NRF_LOG_INFO("Slave SPI Started!");
      while (1) {
    
             if(!nrf_gpio_pin_read(BUTTON_1))
           {
                nrf_delay_ms(150);
                if(!nrf_gpio_pin_read(BUTTON_1))
                {
                   spi_tx(spi_test_payload, 300);
                }
           }
    
      }
    }

    SPI Master Code:

    static volatile uint8_t radio_spi_transfer_finished = 0;
    static void radio_spi_handler(nrfx_spi_evt_t const *p_event, void *p_context) {
      radio_spi_transfer_finished = 1;
    }
    
    uint32_t radio_is_ready_to_read() {
      nrf_gpio_cfg(SPIRADIO_IO1,
          NRF_GPIO_PIN_DIR_INPUT,
          NRF_GPIO_PIN_INPUT_CONNECT,
          NRF_GPIO_PIN_PULLDOWN,
          NRF_GPIO_PIN_S0S1,
          NRF_GPIO_PIN_NOSENSE);
    
      return nrf_gpio_pin_read(SPIRADIO_IO1);
    }
    
    uint32_t radio_spi_read(uint8_t *dst, uint32_t dstlen) {
      uint16_t rx_data_len = 0;
      memset(dst, 0xFF, dstlen); // sanitize the buffer
    
    
    
      /***********************FIRST READ*************************************/
      nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_RX(dst, 252);
    
      flash_spi_transfer_finished = 0;
      if (NRFX_SUCCESS != nrfx_spim_xfer(&radio_spi, &xfer_desc, 0)) {
        NRF_LOG_INFO("SPI READ FAILED!");
        return 0;
      }
    
      while (!radio_spi_transfer_finished)
        ;
      /*********************************************************************/
    
      nrf_delay_us(500);
      uint16_t total_data_length = bytes_to_u16(dst[0], dst[1]);
      if (total_data_length > 250) // need to read more data in case its more then 250 bytes?
      {
        uint16_t bytes_left = total_data_length - 250;
        nrf_delay_ms(1); // give some delay so that slave can easily prepare the next packet
      /***********************SECOND READ (OPTIONAL)*************************************/
      nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_RX(dst+252, total_data_length-252);
    
      flash_spi_transfer_finished = 0;
      if (NRFX_SUCCESS != nrfx_spim_xfer(&radio_spi, &xfer_desc, 0)) {
        NRF_LOG_INFO("SPI READ FAILED!");
        return 0;
      }
    
      while (!radio_spi_transfer_finished)
        ;
      /************************************************************************************/
        nrf_delay_us(500);
        rx_data_len = total_data_length;
      } else {
        rx_data_len = total_data_length;
      }
    
      NRF_LOG_INFO("received: %d, one: %X, last %X\n", rcv_cnt, rx_data_len, dst[0], dst[299]);
    
      return dstlen;
    }
    
    
    
    
    /***************************************  FUNCTION TO WRITE SPI DATA ******************************************/
    uint32_t radio_spi_write(uint8_t *data, uint32_t len) {
      uint32_t err_code;
      int i = 0;
      // check how many packets need to be sent if we send only 1 packet at a time with max size of 250 bytes
      uint8_t total_packets = 0; // total packets to send
      uint8_t packet_len = 0;
      if (len > 250) {
        total_packets = 2;
        packet_len = 250;
      } else {
        total_packets = 1;
        packet_len = len;
      }
    
      while (i < total_packets) {
        // split each packet and send
        uint8_t a = 0;
        uint8_t packet_to_send[255];
        if (i == 0) {
          // add the headers
          for (a = 0; a < packet_len; a++) {
            // keep space for headers in the first transaction
            packet_to_send[a + 2] = data[(i * 250) + a];
          }
        } else {
          for (a = 0; a < packet_len; a++) {
            // do not add any headers
            packet_to_send[a] = data[(i * 250) + a];
          }
        }
        i++;
        if (i != total_packets) {
          // add packet length in the first 2 bytes
          packet_to_send[0] = len >> 8;
          packet_to_send[1] = len;
        }
        // send the packet
        radio_spi_transfer_finished = false;
        if (i != total_packets) {
          // if its the first transaction, add 2 bytes count in the packet length
          nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(packet_to_send, packet_len + 2, NULL, 0);
    
          if (NRFX_SUCCESS != nrfx_spim_xfer(&radio_spi, &xfer_desc, 0)) {
            NRF_LOG_INFO("SPI WRITE FAILED!");
            return 1;
          }
        } else {
          nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(packet_to_send, packet_len, NULL, 0);
    
          if (NRFX_SUCCESS != nrfx_spim_xfer(&radio_spi, &xfer_desc, 0)) {
            NRF_LOG_INFO("SPI WRITE FAILED!");
            return 1;
          }
        }
    
        while (!radio_spi_transfer_finished) // wait for finish
          ;
        nrf_delay_ms(3);
    
        int d = 0;
        // calculate next data length
        if (i == total_packets) {
          // all data is sent
        } else if ((total_packets - i) == 1) {
          // only one packet left
          packet_len = len - 250;
        }
      }
    
      return NRF_SUCCESS;
    }
    
    //****************************************************************************************************//
    
    void radio_spi_init() {
      nrfx_spim_config_t spi_config = NRFX_SPIM_DEFAULT_CONFIG;
      spi_config.frequency = NRF_SPIM_FREQ_4M;
      spi_config.ss_pin = SPIRADIO_CS;
      spi_config.miso_pin = SPIRADIO_MISO;
      spi_config.mosi_pin = SPIRADIO_MOSI;
      spi_config.sck_pin = SPIRADIO_SCK;
      spi_config.bit_order = NRF_SPI_BIT_ORDER_LSB_FIRST;
      spi_config.ss_active_high = false;
      spi_config.orc = 0x5A;
      APP_ERROR_CHECK(nrfx_spim_init(&radio_spi, &spi_config, radio_spi_handler, NULL));
    }
    
    
    
    
      //////////////////MAIN LOOP/////////////////
      memset(spi_buffer, 0, sizeof(spi_buffer));
      nrf_gpio_cfg_input(BUTTON_1, NRF_GPIO_PIN_PULLUP);   //for devboard testing
      while(1){
    
           if(!nrf_gpio_pin_read(BUTTON_1))
           {
                nrf_delay_ms(150);
                if(!nrf_gpio_pin_read(BUTTON_1))
                {
                   radio_spi_write(spi_test_payload, sizeof spi_test_payload); 
                }
           }
    
                    
    if(radio_is_ready_to_read() == 1) 
    		{
                        uint8_t rx_data1[320];
                        radio_spi_read(rx_data1, 320);
    		}
    
    
      }

    Looking forward to your reply.

  • Ok, the problem was that I forgot the call the nrf_drv_spis_buffers_set at the end of the spi_tx in the slave. Now both sides send and receive OK. The question is how to make it more performant?
    I noticed that when i keep pressed both buttons only one of them sends, I would have expected that they both send intermittently.

  • Hi 

    You mean to say you are never able to get bi-directional communication, it will only send in one direction at a time? 

    It might be for the simple reason that both sides are too responsive. Since the button press on each side is unlikely to be processed at exactly the same time one side will start a transaction before the other one has a new packet ready?

    Alternatively the problem could be that you are not able to prepare a new transaction while the current one is ongoing. If you try to send a new packet on either side while it is currently in the middle of a transaction then the new packet should be buffered, so that as soon as the current transaction is complete you will immediately send the next one. Then you should be able to schedule the communication more efficiently. 

    Best regards
    Torbjørn

  • How would I go about implementing this buffered communication?

Reply Children
  • Hi 

    The first step would have to be to change your current SPI read/write functions to be non blocking rather than blocking, so that you are able to process button presses and prepare the next SPI packet while the current packet is being transmitted. 

    The normal way to do this is to implement some kind of state machine, and remove all the steps in your code where you are running a while loop to wait for some event to occur, like the line below:

    while (!radio_spi_transfer_finished) // wait for finish

    Then you can process and prepare a new buffer while the current one is being transmitted, and as soon as the ongoing transaction is complete you can immediately proceed with the next. 

    Best regards
    Torbjørn

Related