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

NRF52832 SPI and SoftDevice (S132) issue

Hello,

I am facing an issue with SPI when the SoftDevice S132 is enabled and i cannot figure out what the problem is.

The following test works perfectly when SoftDevice is not included, but fails as soon as the SoftDevice is included (regardless if it is initialized of not).

I have tried everything i could think of, changing IRQ priority in sdk_config.h, changing  APP_IRQ_PRIORITY in the SPI init, adding  CRITICAL_REGION_ENTER() /  CRITICAL_REGION_EXIT() in the SPI calls, but the result is always the same, there is no error returned by any call however the returned datas are totally wrong, and even inconsistent between resets.

In this test i simply read a register of the SPI peripheral which has a known value of 43445 (dec), when i run the test with with no soft device it works perfectly fine, and when i run it with SoftDevice S132 include in the project it always fails and returns 7811 or 7819 or even 7883, in a random fashion.

I am not sure what can cause this issue, the SPI implementation is as per SDK example and again it works perfectly when the SoftDevice is not here, we even used it in production on a previous design on NRF52840 (but with no SoftDevice). Right now it is on NRF52832, it still works perfectly when there is no SoftDevice included in the project but fails just as perfectly when SoftDevice is include, be it initialized or not.

If someone at Nordic could check into the issue it would be nice because after few hours of tests i still have no idea what could be changed to make it work as it should.

#define SPI_INSTANCE 0
static const nrf_drv_spi_t spi_ = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);
static volatile bool spi_xfer_done_;

void WaitOnbsy_(void){
   while (nrf_gpio_pin_input_get(BSY) == 1) {
      __WFI(); 
      __WFE(); 
   }
}
void setNSS_(int state) {

  if (state) nrf_gpio_pin_set(NSS);
  else nrf_gpio_pin_clear(NSS);
}
void setRST_(int state) {

  if (state) nrf_gpio_pin_set(RST);
  else nrf_gpio_pin_clear(RST);
}
void Reset_(void) {

  nrf_delay_ms(20);
  setRST_(0);
  nrf_delay_ms(50);
  setRST_(1);
  nrf_delay_ms(20);
}
void spi_event_handler_(nrf_drv_spi_evt_t const *p_event, void *p_context) {

  spi_xfer_done_ = true;
}
void spi_init_(void) {

  nrf_gpio_pin_dir_set(DIO1, NRF_GPIO_PIN_DIR_INPUT); //11
  nrf_gpio_pin_dir_set(RST, NRF_GPIO_PIN_DIR_OUTPUT); //17
  nrf_gpio_pin_dir_set(NSS, NRF_GPIO_PIN_DIR_OUTPUT); //13
  nrf_gpio_pin_dir_set(BSY, NRF_GPIO_PIN_DIR_INPUT);  //12

  nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
  spi_config.ss_pin = NRF_SPI_PIN_NOT_CONNECTED;      //0xFFFFFFFF;
  spi_config.miso_pin = MISO;                         //16
  spi_config.mosi_pin = MOSI;                         //15
  spi_config.sck_pin = SCK;                           //14
  spi_config.mode = NRF_DRV_SPI_MODE_0;
  spi_config.frequency = NRF_DRV_SPI_FREQ_8M;
  spi_config.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST;
  //spi_config.irq_priority = APP_IRQ_PRIORITY_HIGH;
  APP_ERROR_CHECK(nrf_drv_spi_init(&spi_, &spi_config, spi_event_handler_, NULL));
}
uint8_t spi_write_(uint8_t dat) {

  spi_xfer_done_ = false;
  uint8_t tx=dat, rx=0;
  //CRITICAL_REGION_ENTER();//doesnt hang, but doesnt help either
  APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi_, &tx, 1, &rx, 1));
  //CRITICAL_REGION_EXIT();
  while (!spi_xfer_done_){__WFE();}
  return rx;
}
void ReadRegister_(uint16_t address, uint8_t *buffer, uint16_t size) {

  WaitOnbsy_();
  //CRITICAL_REGION_ENTER(); //doesnt work, hangs here
  setNSS_(0);
  spi_write_(RADIO_READ_REGISTER);
  spi_write_((address & 0xFF00) >> 8);
  spi_write_(address & 0x00FF);
  spi_write_(0);
  for (uint16_t i = 0; i < size; i++) {
    buffer[i] = spi_write_(0);
  }
  setNSS_(1);
  //CRITICAL_REGION_EXIT();
}
uint8_t ReadRegisterS_(uint16_t address) {

  uint8_t data;
  ReadRegister_(address, &data, 1);
  UARTOUT_INFO("0x%02x\r", data);
  return data;
}
void WriteRegister_(uint16_t address, uint8_t *buffer, uint16_t size) {

  WaitOnbsy_();
  //CRITICAL_REGION_ENTER(); //doesnt work, hangs here
  setNSS_(0);
  spi_write_(RADIO_WRITE_REGISTER);
  spi_write_((address & 0xFF00) >> 8);
  spi_write_(address & 0x00FF);
  for (uint16_t i = 0; i < size; i++) {
    spi_write_(buffer[i]);
  }
  setNSS_(1);
  //CRITICAL_REGION_EXIT();
}
void WriteRegisterS_(uint16_t address, uint8_t value) {

  WriteRegister_(address, &value, 1);
}
void rst_(void) {

  nrf_delay_ms(20);
  setRST_(0);
  nrf_delay_ms(50);
  setRST_(1);
  nrf_delay_ms(20);
}
void Wakeup_(void) {

  setNSS_(0);
  spi_write_(RADIO_GET_STATUS);
  spi_write_(0x00);
  setNSS_(1);
  WaitOnbsy_();
}
uint16_t GetFirmwareVersion_(void) {

  return (((ReadRegisterS_(REG_LR_FIRMWARE_VERSION_MSB)) << 8) | 
           (ReadRegisterS_(REG_LR_FIRMWARE_VERSION_MSB + 1)));
}
int main(void) {

  APP_ERROR_CHECK(app_timer_init());
  uart_init();
  UARTOUT_INFO("SPI init\r");
  spi_init_();
  rst_();
  Wakeup_();
  uint16_t fw_rev = GetFirmwareVersion_(); 
  UARTOUT_INFO("FW REV : %d\r", fw_rev); // should be 43445

  for (;;) {
    app_sched_execute();
    __WFI();
  }
}

Console output for several resets:

Next day :I tried to use SPIM instead of the legacy SPI driver, exactly same result

#define SPI_INSTANCE 0
static const nrfx_spim_t spi_ = NRFX_SPIM_INSTANCE(SPI_INSTANCE);
static volatile bool spi_xfer_done_;

void spi_event_handler_(nrf_drv_spi_evt_t const *p_event, void *p_context) {
  spi_xfer_done_ = true;
}
void spi_init_(void) {

  nrf_gpio_pin_dir_set(DIO1, NRF_GPIO_PIN_DIR_INPUT); //11
  nrf_gpio_pin_dir_set(RST, NRF_GPIO_PIN_DIR_OUTPUT); //17
  nrf_gpio_pin_dir_set(NSS, NRF_GPIO_PIN_DIR_OUTPUT); //13
  nrf_gpio_pin_dir_set(BSY, NRF_GPIO_PIN_DIR_INPUT);  //12

  nrfx_spim_config_t spi_cf = NRFX_SPIM_DEFAULT_CONFIG; 
  spi_cf.sck_pin = SCK; 
  spi_cf.mosi_pin = MOSI; 
  spi_cf.miso_pin = MISO; 
  spi_cf.ss_pin = NRF_SPIM_PIN_NOT_CONNECTED; 
  spi_cf.ss_active_high = false;
  spi_cf.irq_priority = NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY;
  spi_cf.orc = 0xFF;
  spi_cf.frequency = NRF_SPIM_FREQ_1M;
  spi_cf.mode = NRF_SPIM_MODE_0;
  spi_cf.bit_order = NRF_SPIM_BIT_ORDER_MSB_FIRST;
  /*APP_ERROR_CHECK*/(nrfx_spim_init(&spi_, &spi_cf, spi_event_handler_, NULL));
}
uint8_t spi_write_(uint8_t dat) {

  spi_xfer_done_ = false;
  uint8_t tx[10], rx[10];
  tx[0]=dat;
  nrfx_spim_xfer_desc_t xfer; 
  xfer.p_tx_buffer = tx;
  xfer.tx_length = 1;
  xfer.p_rx_buffer = rx;
  xfer.rx_length= 1;
  APP_ERROR_CHECK(nrfx_spim_xfer(&spi_,&xfer, 0));
  while (!spi_xfer_done_){__WFE();}
  return rx[0];
}

After few more hours of tests i am getting somewhere. As one may notice this driver is using two consecutive ReadRegister calls , increasing the register address for the second call, to get MSB and LSB of the data.

uint16_t GetFirmwareVersion_(void) {

  return (((ReadRegisterS_(REG_LR_FIRMWARE_VERSION_MSB)) << 8) | 
           (ReadRegisterS_(REG_LR_FIRMWARE_VERSION_MSB + 1)));
}

So i wondered if the problem could not come from here and rewrote this function to get the two bytes in one single call, instead of 1 byte per call.

And it worked, this time i got 0xa9 and 0xb5, which gives 43445 in decimal, the expected return value.

void ReadRegister_(uint16_t address, uint8_t *buffer, uint16_t size) {

  WaitOnbsy_();
  setNSS_(0);
  uint8_t tx[10], rx[10];
  nrfx_spim_xfer_desc_t xfer; 
  xfer.p_tx_buffer = tx;
  xfer.tx_length = 4+size;
  xfer.p_rx_buffer = rx;
  xfer.rx_length= 5+size;
  tx[0]=RADIO_READ_REGISTER;
  tx[1]=(address & 0xFF00) >> 8;
  tx[2]=address & 0x00FF;
  tx[3]=0x00;
  for (uint16_t i=0; i<size; i++) {tx[4+i] = 0x00;}
  APP_ERROR_CHECK(nrfx_spim_xfer(&spi_,&xfer,0));
  while (!spi_xfer_done_){__WFE();}
  setNSS_(1);
  memcpy(buffer,&rx[4],size);
}
uint16_t GetFirmwareVersion_(void) {

  uint8_t bfr[2];
  ReadRegister_(REG_LR_FIRMWARE_VERSION_MSB,&bfr,2);
  return (bfr[0]<<8) | bfr[1];
}

Now i have at least a clue, however this does not solve the problem nor explain how the exact same implementation can work fine without softdevice and break once softdevice is added. Of course i can finally get the data, but it relies on the fact that this SPI peripheral allows to retrieve the two registers values in a single call, which is not allways the case, and anyway i see no good reason why the SPI would break when two consecutive calls are issued. After all the register address is increased by 1 so it is in effect two different register addresses.

Related