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?

  • 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

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

    As per your advice, I moved the code including nrfx_saadc_abort() to the main function, and nus_data_handler() now only used to stop the PPI and set a flag.

    Now I can stop and re-init the SAADC! I tested the functionality with nrf connect application and there was no problem at all–no reboot due to an error.

    Thanks, Karl!

  • As per your advice, I moved the code including nrfx_saadc_abort() to the main function, and nus_data_handler() now only used to stop the PPI and set a flag.

    Now I can stop and re-init the SAADC! I tested the functionality with nrf connect application and there was no problem at all–no reboot due to an error.

    That is great, I am glad to hear that it is now working as intended.
    Thank you for updating me on your progress here!

    Thanks, Karl!

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

    Please do not hesitate to open another ticket on the forum if you encounter any other issues or questions during your development :) 

    Good luck with your development!

    Best regards,
    Karl

Related