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

NRF_ERROR_BUSY at nrf_drv_twi_tx on sleep_mode_enter

I am using SDK15.2 and I am using ble_app_hrs example to build a device that has multiple sensors and a display.

I am constantly reading sensors data using blocking TWI and everything works great, but before the system goes to sleep I need to send a TWI commands that will turn off the sensors and the display, this is where I get the NRF_ERROR_BUSY at the nrf_drv_twi_tx call...

If I remove the "APP_ERROR_CHECK(err_code);" lines from my custom sensors and display libraries I dont get the error but the sensors and the display dont receive the command to turn off...

I tired doing a while loop until err_code becomes NRF_SUCCESS and also I tried doing the "do while(0)" loop as provided in the twi_master_with_twis_slave example but it didnt fix my problem.

I think the problem is that the constant reading keeps the TWI busy non stop and once the APP_ADV_DURATION times out and prepares the system to go to sleep (where i also try to send my TWI commands for turning off the sensors and display) is where I get the conflict...

Any suggestion how should i fix this properly?

Thanks in advance.

Parents
  • Hi,

    It is not clear to me weather you stop the constant calls to nrf_drv_twi_tx() before you do the final call before going to sleep? With the behavior you are describing I wonder if you are doing the regular polling in continuously thread mode, and then suddenly try to do the last write in an interrupt handler priority? (in a timer event handler or BLE event handler or similar)? You need to let the last "normal" transaction finish, then move on to the last transaction where you send the command to turn off sensors and display.

  • Thanks for your reply.
    Actually after reading everything that I could find about blocking TWI I cant figure out how to stop sending calls before I send the last "turn off" sensors and display command before the system goes to sleep...

    I read the sensors and refresh the display in the main loop (function idle_state_handle) and then in the function sleep_mode_enter() i try to send the commands that turn off the sensors and the display.

    Here is the "idle_state_handle" function:

    static void idle_state_handle(void)
    {
        ret_code_t err_code;
        err_code = nrf_ble_lesc_request_handler();
        APP_ERROR_CHECK(err_code);
    
        readHrSpo();
        OLED_SD1306_Driver_refresh();    
    
    
        if (NRF_LOG_PROCESS() == false)
        {
            nrf_pwr_mgmt_run();
        }
    }


    And here is the "sleep_mode_enter" function:

    static void sleep_mode_enter(void)
    {
        ret_code_t err_code;
        MAX30105_shutDown();
        OLED_SD1306_Driver_off();
        nrf_drv_twi_disable(&m_twi);
    
        err_code = bsp_indication_set(BSP_INDICATE_IDLE);
        APP_ERROR_CHECK(err_code);
    
        // Prepare wakeup buttons.
        err_code = bsp_btn_ble_sleep_mode_prepare();
        APP_ERROR_CHECK(err_code);
    
        // Go to system-off mode (this function will not return; wakeup will cause a reset).
        err_code = sd_power_system_off();
        APP_ERROR_CHECK(err_code);    
    }

    I get the error in the MAX30105_shutDown() function where I send the command for shut down using nrf_drv_twi_tx.

  • Hi,

    I see. Then the error you are seeing is expected and appropriate. You are trying to do two transactions at the same time. The proper way to handle this is to let the current transaction complete, then do the last transaction where you send the command for the sensor to go to sleep.

  • yes, thats what I think too, but where can i find info how to do that? i read everything in the sdk documentation about twi and checked all the twi examples and i cant figure out how to do that waiting for the transaction to complete, also tried to wait in while loop until the nrf_drv_twi_tx response becomes NRF_SUCCESS but then i end up in an endless loop for some reason.

    from what i understand from the documentation about blocking TWI, code continues after nrf_drv_twi_tx only when the transaction completes, but as i said its not the case in my code...

  • I am having some problems making sense of this. Can you upload your code and describe in more detail? As you write, the blocking call will return whenever it is finished. Then you simply just have to not call the function again.

  • sorry but i cant share that part of the code right now...

    here is where the error is thrown:

      do{
        err_code = nrf_drv_twi_tx(&m_twi, address, reg_addr, sizeof(reg_addr), false);
        APP_ERROR_CHECK(err_code);
      } while (err_code != 0);

    if i remove the line "APP_ERROR_CHECK(err_code);" instead throwing the error the board gets stuck... same thing happens sometimes when i click the button 1 to make the board go to sleep... i use this same function to read the sensor and to send commands (example to make it turn off)...

    here is how i initialize the twi:

    void twi_init() {
      ret_code_t err_code;
    
      const nrf_drv_twi_config_t twi_config = {
         .scl                = ARDUINO_SCL_PIN,
         .sda                = ARDUINO_SDA_PIN,
         .frequency          = NRF_DRV_TWI_FREQ_100K,
         .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
         .clear_bus_init     = false
      };
    
      err_code = nrf_drv_twi_init(&m_twi, &twi_config, NULL, NULL);
      APP_ERROR_CHECK(err_code);
    
      nrf_drv_twi_enable(&m_twi);
    }


    is there any other better way to debug my application instead using serial monitor?

    i must add that its not only on one sensor, but this error happens on all sensors and on the display twi communication too... it doesnt happen always but eventually it does happen...

Reply
  • sorry but i cant share that part of the code right now...

    here is where the error is thrown:

      do{
        err_code = nrf_drv_twi_tx(&m_twi, address, reg_addr, sizeof(reg_addr), false);
        APP_ERROR_CHECK(err_code);
      } while (err_code != 0);

    if i remove the line "APP_ERROR_CHECK(err_code);" instead throwing the error the board gets stuck... same thing happens sometimes when i click the button 1 to make the board go to sleep... i use this same function to read the sensor and to send commands (example to make it turn off)...

    here is how i initialize the twi:

    void twi_init() {
      ret_code_t err_code;
    
      const nrf_drv_twi_config_t twi_config = {
         .scl                = ARDUINO_SCL_PIN,
         .sda                = ARDUINO_SDA_PIN,
         .frequency          = NRF_DRV_TWI_FREQ_100K,
         .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
         .clear_bus_init     = false
      };
    
      err_code = nrf_drv_twi_init(&m_twi, &twi_config, NULL, NULL);
      APP_ERROR_CHECK(err_code);
    
      nrf_drv_twi_enable(&m_twi);
    }


    is there any other better way to debug my application instead using serial monitor?

    i must add that its not only on one sensor, but this error happens on all sensors and on the display twi communication too... it doesnt happen always but eventually it does happen...

Children
  • Hi,

    Ignoring the return value is never a good idea and based on what you have written earlier it seems that the problem is that you have written your code so that you risk starting a second TWI transaction before the first has finished. Have you fixed that?

    The alternative approach you are using, trying again as long as there is an error is also viable, though you should probably check the error message explicitly. Doing it this way will mask away any errors. Also, if you do this in a higher interrupt priority than the other TWI write, you will end up in a deadlock (as the other code never runs, which might be happening since you write "the board gets stuck". Doing a busy wait like this is often not a good idea, as it tends to lead to exactly these kinds of problems.

    I don't see any need for using serial monitor here. Normal debugging techniques should be enough. I suggest just fixing what we have discussed first.

  • I get what you are saying, but i cant understand how can I send multiple TWI transactions when blocking TWI is blocking all transactions and code progression until the current transaction finishes... Thats why I decided to use blocking TWI instead non-blocking TWI in the first place.

    As i said before having the while loop or not doesnt change the outcome, yes i am checking the error code explicitly since the APP_ERROR_CHECK is inside the while loop, the only difference is that if i have the APP_ERROR_CHECK inside the while loop i get NRF_ERROR_BUSY, if i remove the APP_ERROR_CHECK the board gets stuck, if i remove the while loop the sensors and the display doesnt get the shut down command when the board goes to sleep...

    I forgot to explain this but here is the basic logic behind the whole app I am building. There are two sensors and one display in my setup, all those work using TWI. I wrote custom drivers for all of them which are based on code parts that i found here on the devzone and also followed the logic of their Arduino libraries. In the main.c file i am initializing TWI instance that i am then sharing with the sensors and display drivers.

    Here is how i do that. In the main.c i am defining this:

    nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);

    then also in main.c i am initializing the twi using the "twi_init" function that i posted in my previous comment and then in each of the driver i have this

    extern nrf_drv_twi_t m_twi;

    which is enabling me to use the same TWI instance in all custom drivers that i wrote.

    As for the normal debugging techniques, i am new with NRF and this is my first project using NRF (before i was working with ATMega and STM microcontrollers), i am really having a hard time understanding most of the normal ways of doing things with NRF since i found the SDK documentation quite messy and providing way to little info for a beginner like me, also most of the results that i find when i search for documentation are for the old SDK's and most of the questions and answers that i find here in the devzone are years old and mostly also for older SDK's and using Kali instead Segger studio. At the moment for debugging i am using Putty serial communication.

    Can you please point me to any tutorial or explanation what are the normal debugging techniques that should be used with Segger studio and SDK15.2?

  • Hi,

    The way you have described the problem indicates to me that you have different parts of your code trying to use the same TWI peripheral simultaneously (one in a interrupt handler, I assume), which would explain the problem you are seeing. However seeing just tiny bits of your code doesn't show how it interconnects so I cannot pinpoint it any closer than what I have written earlier.

    With "normal" debugging techniques I meant simple things like:

    • Understanding and using error checks properly. I recommend reading An introduction to error handling in nRF5 projects.
    • Logging
    • Using the debugger to inspect and step through the code (note that you cannot step through the code when using a SoftDevice, as the SoftDevice will assert when it loses timing).
  • yes, i think you are right that interrupt handler is making a mess and calling the TWI while its being used in other part of the code, but i really cant think of any other way to do what i need to do in certain situations...

    i found this function "nrfx_twi_is_busy()" which returns bool if the TWI is busy, but i cant find how to cancel all ongoing TWI transactions if the TWI is busy when i try to make a new transaction...

    can i use "PRIVATE Tech Support" option to share the complete code with you?

  • Hi,

    Yes, you can open a private case to upload your code and refer to this thread.

    Just let me outline a simple way you can achieve what I think you want with some pseudo code. It is just a way to try to convey how I think you could handle this. The point here is just to show how you can get the TWI transactions to complete before you call the last TWI transaction by using a synchronization variable.

    static volatile bool shutdown = false;
    
    // Normal operation, always doing TWI transactions
    void operation()
    {
        while (!shotdowm)
        {
            twi_transaction();
        }
    
        // Write shutdown TWI message
        
    }
    
    // Interrupt routine called when you should show down (based on timer or something else)
    void isr()
    {
        shutdown = true;
    }
    
    
    // shutdown function
    void shutdown_procedure()
    {
        twi_transaction();
        sleep_mode_prepare();
        sd_power_system_off();
    }
    
    
    // Main application entry
    main()
    {
        while(true)
        {
            // Operation will only return when it is time to shut down
            operation();
            shutdown_procedure();
        }
    }
    

Related