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?

  • 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?

  • 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

Related