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

Change SAADC channel while the code is running

Hello.

I'm using nrf52840 to sample the voltage from the wireless board and then send the data to the PC via Bluetooth.

With the help of DevZone, it was able to construct a fundamental system, so I'm trying to add some additional features.

I tested whether I can transmit some messages while the 20 kHz sampled data are transmitted through Bluetooth. And the result seems it works which means the PC  can control the wireless board with commands.

For now, I want to control the SAADC channel with a simple command. To be more specific, I want to change which SAADC channel to sample. The current version of the code samples pre-determined single channel and I have to re-upload the code to change the channel.

My idea is that if I send '7' from the PC, then the AIN5 channel is sampled continuously. Similarly, '9' for the AIN7 and '4' to stop sampling.

To do so, I modified a nus_data_handler function that is used to handle BLE data in the ble_peripheral\ble_app_uart example. Following is the modified code.

static void nus_data_handler(ble_nus_evt_t * p_evt)
{

    if (p_evt->type == BLE_NUS_EVT_RX_DATA)
    {
        uint32_t err_code;

        switch (p_evt->params.rx_data.p_data[0])
        {
            case '4':
                // Stop recording
                err_code = nrf_drv_saadc_channel_uninit(0);
                APP_ERROR_CHECK(err_code);
                break;
            case '7':
                // Record channel 1
                err_code = nrf_drv_saadc_channel_uninit(0);
                APP_ERROR_CHECK(err_code);
                
                nrf_saadc_channel_config_t channel_config7 = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN5); // Pin 10
                //channel_config.acq_time = NRF_SAADC_ACQTIME_10US; // Default is 10us
        
                err_code = nrf_drv_saadc_channel_init(0, &channel_config7);
                APP_ERROR_CHECK(err_code);
                break;

            case '9':
                // Record channel 2
                err_code = nrf_drv_saadc_channel_uninit(0);
                APP_ERROR_CHECK(err_code);
                
                nrf_saadc_channel_config_t channel_config9 = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN7); // Pin 12

                err_code = nrf_drv_saadc_channel_init(0, &channel_config9);
                APP_ERROR_CHECK(err_code);
                break;
        }
    }
}

I thought the nrf_drv_saadc_channel_uninit function stops sampling and I can change the channel by initiating the SAADC with a changed config.

But when I run this code and send a command from the PC, it stops.

If I send '1' or 'a', the board gets '1' or 'a' and the code keeps running. But when I send '4', '7', '8', the code is rebooted.

I'm not sure why this happens and how can I get what I deserve. Is there any way to do that?

Parents
  • Hello,

    To do so, I modified a nus_data_handler function that is used to handle BLE data in the ble_peripheral\ble_app_uart example. Following is the modified code.
    If I send '1' or 'a', the board gets '1' or 'a' and the code keeps running. But when I send '4', '7', '8', the code is rebooted.

    The ble_app_uart example by default uses RTT logging. Have you changed this, or are you able to see the logger outputs in either the SES debug terminal or the Segger RTT Viewer application?
    I suspect that your call to nrf_drv_saadc_channel_uninit might be NRF_ERROR_BUSY instead of NRF_SUCCESS, which would trigger a reset when the != NRF_SUCCESS error code is passed to the APP_ERROR_CHECK. Resetting the device is the default error handling, if you have not implemented specific error handling for this error yourself.

    How are you conducting / initiating your sampling - are you using PPI and the SAMPLESTART_TASK like in the SAADC example?
    If so, this could be why your SAADC is busy most of the time. Could you also tell me which acquisition time you are using in your SAADC configuration?

    Could you also confirm for me that you have defined DEBUG in your preprocessor defines, like shown in the included image?

    This will print out a detailed error message to your logger module when a != NRF_SUCCESS error code is passed to the APP_ERROR_CHECK function.

    Please do this, and let me know what this error message says.

    Looking forward to resolving this issue together!

    Best regards,
    Karl

  • Thanks for the reply!

    How are you conducting / initiating your sampling - are you using PPI and the SAMPLESTART_TASK like in the SAADC example?
    If so, this could be why your SAADC is busy most of the time. Could you also tell me which acquisition time you are using in your SAADC configuration?

     I'm using PPI for continuous recording. An acquisition time of the SAADC is 10 us, and I'm calling sample task get every 50 us.

    I suspect that your call to nrf_drv_saadc_channel_uninit might be NRF_ERROR_BUSY instead of NRF_SUCCESS, which would trigger a reset when the != NRF_SUCCESS error code is passed to the APP_ERROR_CHECK. Resetting the device is the default error handling, if you have not implemented specific error handling for this error yourself.
    Please do this, and let me know what this error message says.

    I checked the error message and it says,

    <warning> SAADC: Function: nrfx_saadc_channel_uninit, error code: NRF_ERROR_BUSY.
    <error> app: ERROR 17 [NRF_ERROR_BUSY] at C:\Code\BLE\nRF5_SDK_16.0.0_98a08e2_new\examples\ble_peripheral\ble_app_Ver.2021_dbg\main.c:263
    PC at: 0x000313

    It seems like NRF_ERROR_BUSY is passed to the APP_ERROR_CHECK function as you said.

    I thought the saadc_channel_uninit function stops the recording, but if it is blocked when the SAADC is busy, then how can I pause the sampling and change the ADC channel?

Reply
  • Thanks for the reply!

    How are you conducting / initiating your sampling - are you using PPI and the SAMPLESTART_TASK like in the SAADC example?
    If so, this could be why your SAADC is busy most of the time. Could you also tell me which acquisition time you are using in your SAADC configuration?

     I'm using PPI for continuous recording. An acquisition time of the SAADC is 10 us, and I'm calling sample task get every 50 us.

    I suspect that your call to nrf_drv_saadc_channel_uninit might be NRF_ERROR_BUSY instead of NRF_SUCCESS, which would trigger a reset when the != NRF_SUCCESS error code is passed to the APP_ERROR_CHECK. Resetting the device is the default error handling, if you have not implemented specific error handling for this error yourself.
    Please do this, and let me know what this error message says.

    I checked the error message and it says,

    <warning> SAADC: Function: nrfx_saadc_channel_uninit, error code: NRF_ERROR_BUSY.
    <error> app: ERROR 17 [NRF_ERROR_BUSY] at C:\Code\BLE\nRF5_SDK_16.0.0_98a08e2_new\examples\ble_peripheral\ble_app_Ver.2021_dbg\main.c:263
    PC at: 0x000313

    It seems like NRF_ERROR_BUSY is passed to the APP_ERROR_CHECK function as you said.

    I thought the saadc_channel_uninit function stops the recording, but if it is blocked when the SAADC is busy, then how can I pause the sampling and change the ADC channel?

Children
  • DL_November said:
    Thanks for the reply!

    It is no problem at all, I am happy to help!

    DL_November said:

     I'm using PPI for continuous recording. An acquisition time of the SAADC is 10 us, and I'm calling sample task get every 50 us.

    Thank you for clarifying. 

    DL_November said:

    It seems like NRF_ERROR_BUSY is passed to the APP_ERROR_CHECK function as you said.

    I thought the saadc_channel_uninit function stops the recording, but if it is blocked when the SAADC is busy, then how can I pause the sampling and change the ADC channel?

    Great! This is very helpful.
    You are correct that it is blocked when the SAADC is actively using the channel, and therefore you will either have to time the reconfiguration to happen in-between samples(if it is critical that you do not miss a single sample), or you could just disable the PPI channel, wait for the SAADC to be free again, then do the reconfiguration.
    For the latter approach you could use the nrfx_saadc_is_busy function to determine when to proceed with the reconfiguration.

    Let me know if this achieves the functionality you were after! :) 

    Best regards,
    Karl

  • For the latter approach you could use the nrfx_saadc_is_busy function to determine when to proceed with the reconfiguration.

    First, I wrote code that stops the PPI then wait until the SAADC is not busy like the following.

    case '4':
        // Stop ppi
        err_code = nrf_drv_ppi_channel_disable(m_ppi_channel);
        APP_ERROR_CHECK(err_code);
        
        while(nrfx_saadc_is_busy())
        {
            //Wait until SAADC is free
        }
        
        err_code = nrf_drv_saadc_channel_uninit(0);
        APP_ERROR_CHECK(err_code);
        break;

    But in this case, I was locked in a while loop due to the SAADC is not freed.

    So I replaced the while loop with nrfx_saadc_abort() function.

    Unfortunately, when I use the abort function, NRFX_ASSERT() in the nrfx_saadc_abort() throws ERROR 3735928559.

    How can I make the SAADC finish or abort the ongoing work?

  • DL_November said:
    But in this case, I was locked in a while loop due to the SAADC is not freed.

    What do you mean when you say this - did you never leave the while(nrfx_saadc_is_busy()) loop after disabling the PPI channel?
    I would seldom advise to use an empty while loop for timing/waiting purposes, but its alright now while debugging.

    DL_November said:
    So I replaced the while loop with nrfx_saadc_abort() function.
    DL_November said:
    How can I make the SAADC finish or abort the ongoing work?

    Great, aborting the ongoing conversion before uninitilizing the channel is a clean way to do this, and should work just as well.
    Please be advised that the nrfx_saadc_abort function can not be called from the context of the SAADC event handler, nor any interrupt with a priority equal to or higher than the interrupt priority of the SAADC.
    What is the priority of the nus data handler, and the SAADC?

    Best regards,
    Karl

  • I would seldom advise to use an empty while loop for timing/waiting purposes, but its alright now while debugging.

    Okay, I'll avoid using an empty loop later.

    What is the priority of the nus data handler, and the SAADC?

    In the sdk_config.h file, BLE_NUS_BLE_OBSERVER_PRIO is set to 2, and NRFX_SAADC_CONFIG_IRQ_PRIORITY is set to 6.

    As the NRF52840 is my first experience of the embedded system, I wonder whether this means that when the codes in nus_data_handler() are in progress, SAADC pauses and stays in the busy state until the higher priority interrupt finishes.

    Is this what 'nrfx_saadc_abort function can not be called from the context of the SAADC event handle' means?

  • DL_November said:
    Okay, I'll avoid using an empty loop later.

    Great!
    It is a common thing to do - so it is not a critical misstep - but it wastes both power and CPU cycles, and thus should be avoided when possible.

    DL_November said:

    In the sdk_config.h file, BLE_NUS_BLE_OBSERVER_PRIO is set to 2, and NRFX_SAADC_CONFIG_IRQ_PRIORITY is set to 6.

    As the NRF52840 is my first experience of the embedded system, I wonder whether this means that when the codes in nus_data_handler() are in progress, SAADC pauses and stays in the busy state until the higher priority interrupt finishes.

    Is this what 'nrfx_saadc_abort function can not be called from the context of the SAADC event handle' means?

    Yes, you are absolutely correct - the abort function will generate a NRFX_SAADC_EVT_DONE event if called when there is an ongoing conversion. This event will have a priority 6, which is much lower than the priority of the interrupt that called the abort function. Thus, the EVT_DONE generated by the abort function will not be handled until the nus_evt_handler is finished - but the nus_evt_handler never finishes because it is waiting for the SAADC event handler to finish handling the EVT_DONE.
    The same goes for the case if you had called the abort function from the SAADC event handler itself. Say that you wanted to reconfigure the channels after every EVT_DONE - if you had called the abort function and then waited in the SAADC handler for the SAADC handler to process the event, it would create the same deadlock, because an interrupt of equal priority does not get to interrupt the SAADC handler.

    Mixing up the priorities like this is quite a common occurrence, and why the warning is outlined explicitly in its API Reference that it must not be called from a priority with equal or higher priority.

    You could instead try to move the chanel reconfiguration to the main context, and rather set a flag as part of your nus_evt_handler, so that when you return to the main context, it knows to change the configuration.

    Try this, and let me know if it performs as expected - reconfiguring the SAADC channels on command, and abort the ongoing conversion successfully to do so.

    DL_November said:
    As the NRF52840 is my first experience of the embedded system,

    This is good for me to be aware of, thank you for letting me know!
    Please do not hesitate to ask if any part of my answer is unclear, so I may try to elaborate and clear out any confusion.

    Please do not hesitate to open new tickets on the forum if you encounter any other issues or questions during your development - it's why the forum is here after all!

    Looking forward to resolving this issue together,

    Best regards,
    Karl

Related