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.

  • So basically creating a global 300 bytes buffer and a bool called "complex_transaction".
    When IRQ triggers, if first byte of the payload is 254, complex_transaction will be set to true and the next transaction will be automatically appended to the buffer after the first one.

    Does that sound right to you? How do you see it?

  • Hi 

    That was more or less my original idea, but I think I might have come up with a better one:

    Rather than adding header bytes or similar to your data it is possible to simply use the length of the packet as an indication whether or not there is another packet coming. 

    Essentially if you receive a packet that is 255 bytes long then you assume that the real message is longer, and that the next packet will have to be appended to the first. 

    The advantage of this method is that you don't need to manually append the data from the two packets, since you can just set the EasyDMA pointer of the SPI slave to the right place in the buffer for the second transaction. 

    The only special case here is if you ever need to send a packet that is exactly 255 bytes long, since that would cause the SPI slave to assume another transaction was coming. 

    To solve this issue you either need to send a 1 byte dummy packet after the 255 byte transaction, to wrap it up, or you need to include a length field or something at the start of the first packet so the SPI slave can tell whether or not the second packet is needed (similar to the first suggestion, but if you only include the header on the first packet you can still append the two packets seamlessly without requiring a memcpy). 

    Best regards
    Torbjørn

  • will this work for bi-directional transfers? master send to slave and slave send to master (using irq pin)

  • Hi 

    That is a bit less straight forward. It is the master that decides how many bytes to clock out, which the slave would have to tell it somehow. 

    This is another argument for including the packet length as the first bytes of the message. 

    Then you could have a basic approach like this:

    1) IRQ pin is asserted by the slave

    2) The master sets the chip select pin low and starts a 2 byte transaction to read the length

    3) Based on the length the master sets up the second transaction, to pick up the data (for the slave this looks like a continuation of the first transaction, since chip select was kept low)

    4) The master sets the chip select pin high

    5) If the data length was > 253 bytes, the spi master sends another command to pick up the remaining bytes

    Best regards
    Torbjørn

  • Ok, this is way above my coding capabilities. Is there any way someone your team can write some some sample code for this? I am sure it will benefit many users who want to use SPI on any device other than nrf52840.

    Time to get into the Christmas spirit and give the gift of code to the community.

Reply Children
  • 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?

Related