[SPIM + NUS] conflict between the SPIM and BLE thread.

I am programming with the nRF52840DKs. [Toolchain Manager: v1.3.0, IDE: Visual Studio Code (VSCode), SDK: ncs v2.6.0, window11 pro]

I am currently working on integrating 'NUS_peripheral' and 'SPIM'.

My goal: communicate with the ADC via SPIM and transmit the collected data via Bluetooth.

To briefly explain my code, the SPIM turns on and off at regular intervals using a timer. The NUS (peripheral) sends data through a while loop.

However, it seems that there's a conflict between the SPIM and the while loop in the BLE thread.

Below are the main code:

#define MTU_SIZE 244
#define PRIORITY 7

nrfx_spim_t spim1_inst = NRFX_SPIM_INSTANCE(SPIM_INST_IDX);
nrfx_timer_t timer1_inst = NRFX_TIMER_INSTANCE(TIMER_INST_IDX);
nrfx_gpiote_t gpiote_inst = NRFX_GPIOTE_INSTANCE(GPIOTE_INST_IDX);
nrf_ppi_channel_t ppi_channel_spi_start;
nrf_ppi_channel_t ppi_channel_spi_end;

volatile uint16_t initialization_counter = 0;
volatile uint16_t repeat_counter = 0;
volatile uint16_t NUS_send_counter = 0;
volatile bool initialization_stop = false;

#define TIME_TO_WAIT_US 50

uint8_t spim_tx_buf_initial[30][2];
uint8_t spim_tx_buf_repeat[18][2];

#define rx_data_size 4096
uint8_t spim_rx_buf_A[rx_data_size][2];
uint8_t spim_rx_buf_B[rx_data_size][2];
uint8_t (*spim_buffer)[2];
uint8_t (*nus_buffer)[2];
uint8_t out_channel;

void spim1_handler(nrfx_spim_evt_t const * p_event, void * p_context) {   
   NUS_send_counter++;
    
   if(!initialization_stop){
         initialization_counter++;
         if(initialization_counter == 30){
            initialization_stop = true;
            spim1_inst.p_reg->TXD.PTR = (uint32_t)spim_tx_buf_repeat[0];
         }
      return ;
   }
   repeat_counter++;
   if ( repeat_counter % 18 == 0 ) {
      spim1_inst.p_reg->TXD.PTR = (uint32_t)spim_tx_buf_repeat[0];
   } 
}

void timer0_handler(nrf_timer_event_t event_type, void * p_context){   

} 

void peripheral_setup(void){
    nrfx_err_t error;
    (void)error;
    
    /*  GPIOTE Setting */
    static const nrfx_gpiote_output_config_t output_config = {
        .drive = NRF_GPIO_PIN_S0S1,
        .input_connect = NRF_GPIO_PIN_INPUT_DISCONNECT,
        .pull = NRF_GPIO_PIN_NOPULL,
    };
    const nrfx_gpiote_task_config_t task_config = {
        .task_ch = out_channel,
        .polarity = NRF_GPIOTE_POLARITY_TOGGLE,
        .init_val = NRF_GPIOTE_INITIAL_VALUE_HIGH,
    };

    error = nrfx_gpiote_output_configure(&gpiote_inst, SS_PIN_MASTER, &output_config, &task_config);
    nrfx_gpiote_out_task_enable(&gpiote_inst, SS_PIN_MASTER);

    /*  SPIM 1 Setting */
    nrfx_spim_config_t spim1_config = NRFX_SPIM_DEFAULT_CONFIG(SCK_PIN_MASTER,
                                                            MOSI_PIN_MASTER,
                                                            MISO_PIN_MASTER,
                                                            NRF_SPIM_PIN_NOT_CONNECTED);
    spim1_config.frequency      = NRFX_MHZ_TO_HZ(8);
    error = nrfx_spim_init(&spim1_inst, &spim1_config, spim1_handler,0);

    nrfx_spim_xfer_desc_t spim1_xfer_desc = NRFX_SPIM_XFER_TRX((uint8_t*)spim_tx_buf_initial, 2, (uint8_t*)spim_rx_buf_A, 2);
    uint32_t spim1_flags = NRFX_SPIM_FLAG_HOLD_XFER | NRFX_SPIM_FLAG_REPEATED_XFER; 
    error = nrfx_spim_xfer(&spim1_inst, &spim1_xfer_desc, spim1_flags);
    spim1_inst.p_reg->TXD.PTR = (uint32_t)spim_tx_buf_initial[0];
    spim1_inst.p_reg->TXD.MAXCNT = 2;
    spim1_inst.p_reg->TXD.LIST =SPIM_TXD_LIST_LIST_ArrayList << SPIM_TXD_LIST_LIST_Pos;
    spim1_inst.p_reg->RXD.PTR = (uint32_t)spim_rx_buf_A[0];
    spim1_inst.p_reg->RXD.MAXCNT = 2;
    spim1_inst.p_reg->RXD.LIST = SPIM_RXD_LIST_LIST_ArrayList << SPIM_RXD_LIST_LIST_Pos;

    /*  TIMER 0 Setting */
    nrfx_timer_config_t timer0_config = NRFX_TIMER_DEFAULT_CONFIG(16000000);
    timer0_config.bit_width          = NRF_TIMER_BIT_WIDTH_32,
    error = nrfx_timer_init(&timer1_inst, &timer0_config, timer0_handler);
    //error = nrfx_timer_init(&timer1_inst, &timer0_config, NULL);
    nrfx_timer_clear(&timer1_inst);
    k_sleep(K_MSEC(2000));


    uint32_t desired_ticks = nrfx_timer_us_to_ticks(&timer1_inst, TIME_TO_WAIT_US);
    nrfx_timer_extended_compare(&timer1_inst, NRF_TIMER_CC_CHANNEL0, desired_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);

    /*   PPI  Setting  */
    uint32_t gpiote_task_addr = nrfx_gpiote_out_task_address_get(&gpiote_inst ,SS_PIN_MASTER);    
    uint32_t timer_start_compare_event_addr = nrfx_timer_compare_event_address_get(&timer1_inst, NRF_TIMER_CC_CHANNEL0);
    uint32_t spi_start_task_addr   = nrfx_spim_start_task_address_get(&spim1_inst);
    uint32_t spi_end_evt_addr = nrfx_spim_end_event_address_get(&spim1_inst);

    // Timer reaches the desired tick -> GPIOTE toggle(off), SPI start
    error = nrfx_gppi_channel_alloc(&ppi_channel_spi_start);
    nrfx_gppi_channel_endpoints_setup(ppi_channel_spi_start, timer_start_compare_event_addr, gpiote_task_addr);
    nrfx_gppi_fork_endpoint_setup(ppi_channel_spi_start, spi_start_task_addr);
    // SPI tx-rx transmission ends -> GPIOTE toggle(on)
    error = nrfx_gppi_channel_alloc(&ppi_channel_spi_end);
    nrfx_gppi_channel_endpoints_setup(ppi_channel_spi_end, spi_end_evt_addr, gpiote_task_addr);

}

void ble_write_thread(void){
   int ret;
   uint16_t temp;
   uint8_t (*temp_buffer)[2];
    
   k_sem_take(&nus_start, K_FOREVER);
   LOG_INF("Starting BLE Write Thread");

   while (1) {

      temp = NUS_send_counter;
      NUS_send_counter = 0;
      
      temp_buffer = spim_buffer;  
      spim_buffer = nus_buffer;
      nus_buffer = temp_buffer;

      spim1_inst.p_reg->RXD.PTR = (uint32_t)spim_buffer;

      while(temp > 0){
         if(temp < MTU_SIZE){
            ret = bt_nus_send(current_conn, (uint8_t *)nus_buffer, temp);
            temp = 0;
            break;
         }else{
            ret = bt_nus_send(current_conn, (uint8_t *)nus_buffer, MTU_SIZE);
            temp -= MTU_SIZE;
         }
      }
      if (k_sem_take(&disconnect_sem, K_NO_WAIT) == 0){
         LOG_INF("Disconnected, exiting BLE Write Thread");
         return;
      }
   }
}

K_THREAD_DEFINE(ble_write_thread_id, 8192, ble_write_thread, NULL, NULL, NULL, PRIORITY, 0, 0);

  • Please show the full code. I don't see how spim_buffer assigned. 


    I don't see where you assign the SPI buffer spim_rx_buf_A to. 

  • MOSI: command, MISO: result

    First of all, the data received from the sensor is the result (MISO) of the SPIM.

    While debugging the data received via BLE, there is an issue where the pattern of data received from the sensor is not being output as expected.

    After the result of the initial transmission (spim_tx_buf_initial) is successfully sent via BLE, only the result of the repeated transmission (spim_tx_buf_repeat) should be transmitted. However, even after the initial transmission is complete, some of the 'result of spim_tx_buf_initial' continues to be output. As a result, only the 'result of spim_tx_buf_repeat' should be repeated, but this is not happening as expected.

  • Hi Seongmin, 
    We currently don't have NUS and SPIM sample.

    My suggestion is to try instead of sending over BLE, you can print out on the log the data should be sent by BLE. This way you can monitor if there is any problem with the SPI buffer. 


    Please correct me if I'm wrong, you are trying to test using a loopback ? The MOSI is connected to MISO, correct? 

    Please try to do a memcpy to copy the SPI buffer to another buffer before sending as the BLE stack will not buffer it until it's sending. So if the content of the buffer is changed/updated before the BLE packet is sent you will send the wrong data.

  • First of all, thank you so much for your response.

    When using SPIM alone, there were no issues, and all debugging has been completed. Additionally, even when NUS and SPIM used separate buffers, the tests were conducted and everything worked fine in that case as well. (Additionally, I measured SS, SCK, MOSI, and MISO with an oscilloscope to confirm this.)

    The problem arises when the buffer is shared between the SPIM handler and the NUS thread. SPIM itself operates correctly, as confirmed by the oscilloscope, but sharing the buffer became unavoidable when I tried to send the data received by SPIM to NUS.

    To address this, I implemented double buffering. However, I'm facing difficulties in writing the code due to the limitation that I cannot wait for or block the handler(in zephyr OS).

    Please try to do a memcpy to copy the SPI buffer to another buffer before sending as the BLE stack will not buffer it until it's sending. So if the content of the buffer is changed/updated before the BLE packet is sent you will send the wrong data.

    As you mentioned, are you referring to storing NUS_send_counter in temp like in my code(nus_thread)? I think it's a good idea. However, I'm concerned that memcpy might result in high CPU load.

  • Hi Seongmin, 

    I think you may want to look at how we handle UART buffer in NUS sample of using k_fifo. Notice how the buffer is initialized at each UART_RX_BUF_REQUEST event. And then free after it's sent by bt_nus_send()

    As I mentioned if you simply share the same buffer between BLE and SPI you will run into the issue that SPI may change the data before it's sent via BLE. 

Related