[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);

Parents
  • 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.

  • Okay, I'll try that. I'll get back to you as soon as possible.

  • Hello. I followed your advice and coded accordingly. Below are the modified sections, while the rest remains the same as in the previous code

    static struct fifo_data_t {
       void *fifo_reserved; // This must be the first element for FIFO
       uint8_t data[122][2]; // Buffer to hold the data
       uint16_t len; // Length of the data 
    };
    
    K_FIFO_DEFINE(nus_fifo);
    
    struct fifo_data_t *spim_buf = NULL; 
    static volatile uint16_t buffer_index = 0;
    
    //////////////////////////////////////////////////
    
    void spim1_handler(nrfx_spim_evt_t const * p_event, void * p_context) { 
    
       if (spim_buf == NULL) {
          spim_buf = k_malloc(sizeof(struct fifo_data_t));
          if (spim_buf == NULL) {
             LOG_ERR("Failed to allocate memory for FIFO buffer:%d", buffer_index);
             return;
          }
          spim_buf->len = 0;  
       }
    
       memcpy(spim_buf->data[buffer_index], spim_rx_buf_A[buffer_index], 2);   
       buffer_index++;
       spim_buf->len++;
    
       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;
       }
    
       if (buffer_index >= 120){    //if (buffer_index >= 100 && repeat_counter % 18 == 0){
          k_fifo_put(&nus_fifo, spim_buf); 
          
          spim1_inst.p_reg->RXD.PTR = (uint32_t)spim_rx_buf_A[0];
          buffer_index = 0;
    
          spim_buf = NULL;
       }
    
       repeat_counter++;
       if (repeat_counter % 18 == 0) {
          spim1_inst.p_reg->TXD.PTR = (uint32_t)spim_tx_buf_repeat[0];
          repeat_counter = 0;
       }
    }
    
    void ble_write_thread(void){
       int ret;
       struct fifo_data_t *nus_buf;
    
       k_sem_take(&nus_start, K_FOREVER);
       LOG_WRN("Starting BLE Write Thread");
       while (1) {
          nus_buf = k_fifo_get(&nus_fifo, K_FOREVER);
          ret = bt_nus_send(current_conn, (uint8_t *)nus_buf->data, 2*(nus_buf->len));
    
          k_free(nus_buf);
    
          if (k_sem_take(&disconnect_sem, K_NO_WAIT) == 0){
             LOG_WRN("Disconnected, exiting BLE Write Thread");
             return;
          }
       }
    }
    
    K_THREAD_DEFINE(ble_write_thread_id, 8192, ble_write_thread, NULL, NULL, NULL, PRIORITY, 0, 0);
    



    There are a few issues. The SPIM result for the spim_tx_buf_initial command is missing, and only the result for spim_tx_buf_repeat is printed to NUS. To address this, I added a few variables for debugging as shown below.(The commented-out section is the part that has been modified.)
    // #define MAX_SPI_COUNT 400
    // static K_SEM_DEFINE(debugging, 0, 1);
    // uint32_t recording[MAX_SPI_COUNT];
    // int spim_count = 0;
    ///////////////////////////////////////////////////
    
    // void spi_disable(NRF_SPIM_Type *p_spim_instance){
    //     nrfy_spim_disable(p_spim_instance);
    //     nrf_spim_int_disable(p_spim_instance, 0xFFFFFFFF);
    // }
    
    // void timer_disable(nrfx_timer_t *p_timer_instance){
    //     nrfx_timer_disable(p_timer_instance);
    //     nrfx_timer_uninit(p_timer_instance);
    // }  
    
    void spim1_handler(nrfx_spim_evt_t const * p_event, void * p_context) { 
       //LOG_INF("spim1_handler- buffer_index: %d, spim_buf->len: %d ",buffer_index, spim_buf->len);
       //LOG_INF("spim1_handler- initialization_counter: %d",initialization_counter);
       //LOG_INF("spim1_handler- 0x%02x%02x",spim_rx_buf_A[buffer_index][0], spim_rx_buf_A[buffer_index][1]);
       // LOG_INF("spim1_handler- %d >> RXD.PTR: 0x%08x", buffer_index, (uint32_t)spim1_inst.p_reg->RXD.PTR);
       // LOG_INF("spim1_handler- %d >> TXD.PTR: 0x%08x", buffer_index, (uint32_t)spim1_inst.p_reg->TXD.PTR);
       // if(spim_count >= MAX_SPI_COUNT){                
       //    timer_disable(&timer1_inst);
       //    spi_disable(spim1_inst.p_reg);
       //    k_sem_give(&debugging);
       //    LOG_INF("<<<< SPIM+TIMER END  >>>>");
       //    return;
       // } 
       //recording[spim_count] = (uint32_t)spim1_inst.p_reg->RXD.PTR;
       //recording[spim_count] = (uint32_t)initialization_counter;
       //recording[spim_count] = (uint32_t)initialization_stop;
       //recording[spim_count] = (uint32_t)repeat_counter;
       // spim_count++;
    
       if (spim_buf == NULL) {
          spim_buf = k_malloc(sizeof(struct fifo_data_t));
          if (spim_buf == NULL) {
             LOG_ERR("Failed to allocate memory for FIFO buffer:%d", buffer_index);
             return;
          }
          spim_buf->len = 0;  // Initialize the length of the data
       }
    
       memcpy(spim_buf->data[buffer_index], spim_rx_buf_A[buffer_index], 2);    //void *memcpy(void *dest, const void *src, size_t n); 
       //recording[spim_count] = (uint32_t)buffer_index;
    
       buffer_index++;
       spim_buf->len++;
    
       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;
       }
    
       if (buffer_index >= 120){    //if (buffer_index >= 100 && repeat_counter % 18 == 0){
    
          k_fifo_put(&nus_fifo, spim_buf); 
          
          spim1_inst.p_reg->RXD.PTR = (uint32_t)spim_rx_buf_A[0];
          buffer_index = 0;
    
          spim_buf = NULL;
       }
    
       repeat_counter++;
       if (repeat_counter % 18 == 0) {
          spim1_inst.p_reg->TXD.PTR = (uint32_t)spim_tx_buf_repeat[0];
          repeat_counter = 0;
       }
    }
    
    int main(void){
       nrfx_err_t status;
       (void)status;
       int err;
    
       LOG_INF(" -----------      revision      ----------- ");
    
       /************ BLE ***********/ 
       bt_conn_cb_register(&conn_callbacks); 
       err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks); 
       //LOG_INF("main - bt_conn_auth_info_cb_register: %d",err);
       err = bt_enable(NULL);
       //LOG_INF("main - bt_enable: %d",err);
       if (IS_ENABLED(CONFIG_SETTINGS)) {
          settings_load();
       }
       err = bt_nus_init(&nus_cb);
       //LOG_INF("main - bt_nus_init(&nus_cb): %d",err);
       err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
       //LOG_INF("main - bt_le_adv_start: %d",err);   
       k_sem_take(&ble_setup_sem1, K_FOREVER);
       k_sem_take(&ble_setup_sem2, K_FOREVER);
       k_sem_take(&ble_setup_sem3, K_FOREVER);
    
       /************ SPIM ************/ 
       initialization_command();
       #if defined(__ZEPHYR__)
          IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_SPIM_INST_GET(SPIM_INST_IDX)), IRQ_PRIO_LOWEST,     
                                  NRFX_SPIM_INST_HANDLER_GET(SPIM_INST_IDX), 0, 0);
          IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_TIMER_INST_GET(TIMER_INST_IDX)), IRQ_PRIO_LOWEST, 
                                  NRFX_TIMER_INST_HANDLER_GET(TIMER_INST_IDX), 0, 0);
       #endif
       LOG_INF("main - TIME_TO_WAIT_US: %d [us],  total_sampling_rate: %ld [Hz],  per_ch_SAMPLE_RATE: %ld [Hz]", TIME_TO_WAIT_US, total_sampling_rate, per_ch_SAMPLE_RATE);
       status = nrfx_gpiote_init(&gpiote_inst, NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY);
       //LOG_INF("main - nrfx_gpiote_init: %d", status);
       status = nrfx_gpiote_channel_alloc(&gpiote_inst, &out_channel);
       //LOG_INF("main - nrfx_gpiote_channel_alloc: %d", status);
       LOG_INF("---------------------------------------------------------------------");
       LOG_INF("main - spim_tx_buf_initial address[0]: %p", (void*)&spim_tx_buf_initial[0][0]);
       LOG_INF("main - spim_tx_buf_initial address[29]: %p", (void*)&spim_tx_buf_initial[29][0]);
       LOG_INF("main - spim_tx_buf_repeat address[0]: %p", (void*)&spim_tx_buf_repeat[0][0]);
       LOG_INF("main - spim_tx_buf_repeat address[17]: %p", (void*)&spim_tx_buf_repeat[17][0]);
       LOG_INF("main - spim_rx_buf_A address[0]: %p", (void*)&spim_rx_buf_A[0][0]);
       LOG_INF("main - spim_rx_buf_A address[243]: %p", (void*)&spim_rx_buf_A[243][0]);
    
       LOG_INF("---------------------------------------------------------------------");
    /*    for(int y=0 ; y < 30; y++){
          LOG_INF("main - spim_tx_buf_initial address: %p", (void*)&spim_tx_buf_initial[y][0]);
       }
          LOG_INF("---------------------------------------------------------------------");
       for(int y=0 ; y < 18; y++){
          LOG_INF("main - spim_tx_buf_repeat address: %p", (void*)&spim_tx_buf_repeat[y][0]);
       }
       LOG_INF("---------------------------------------------------------------------"); */
       peripheral_setup();
       nrfx_gpiote_out_task_enable(&gpiote_inst, SS_PIN_MASTER);
       nrfx_gppi_channels_enable(BIT(ppi_channel_spi_start));
       nrfx_gppi_channels_enable(BIT(ppi_channel_spi_end));
       nrfx_timer_enable(&timer1_inst);
       k_sem_give(&nus_start);
       //k_sem_take(&debugging, K_FOREVER);
       //for(int i=0; i<400; i++){
          //LOG_INF("spim1_handler- %d >> RXD.PTR: 0x%08x", i, recording[i]);
          //LOG_INF("spim1_handler- %d >> buffer_index: %d", i, recording[i]);
          //LOG_INF("spim1_handler- %d >> initialization_counter: %d", i, recording[i]);
          //LOG_INF("spim1_handler- %d >> repeat_counter: %d", i, recording[i]);
       //}
    
       return 0;
    }
    


    However, after adding these variables, the SPIM result for the spim_tx_buf_initial command is no longer missing. What could be the issue?

  • Hi Seongmin, 

    It's getting quite hard to follow here. 
    My suggestion is to try to isolate the problem and simplify the application so you can spot where the issue is. 
    As I mentioned, please try to simply printout on the log the fifo buffer instead of sending it over BLE. This way we can take BLE out of the picture and focus the debugging on SPI and the FIFO buffer. 

    Please show your test result with some dummy data so I can get to understand what your problem is. Please provide the log.

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

    < Test Results for SPIM and NUS >
    1. SPIM (Executed Individually)
       - SPIM communication occurs consistently according to the 'TIME_TO_WAIT_US' variable.
       - It was confirmed that SPIM works correctly when TIME_TO_WAIT_US is above a certain threshold.
       - 'result for spim_tx_buf_repeat' was printed successfully. 'result for spim_tx_buf_initial' was printed successfully.

    2. NUS (Executed Individually)
      - NUS functions well when executed individually.

    3. SPIM + NUS Simultaneous Execution
       - When SPIM and NUS are executed simultaneously, the issue previously mentioned occurs again.

    There are a few issues. The SPIM result for the spim_tx_buf_initial command is missing, and only the result for spim_tx_buf_repeat is printed to NUS. To address this, I added a few variables for debugging as shown below.(The commented-out section is the part that has been modified.)
  • Please show your test result with some dummy data so I can get to understand what your problem is. Please provide the log.

    okay.I'll get back to you as soon as possible.

Reply Children
No Data
Related