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

ADC interupt based on SAADC limit monitoring or LPCOMP

Greetings,

We are using nrf-sdk v1.3.0.

We would like to monitor an adc channel using either limit event monitoring or maybe LPCOMP.

I was able to find "low level" documentation, describing the registers and such, but no actual example code using zephyr.
https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf52840%2Flpcomp.html&cp=4_0_0_5_11
https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf52840%2Fsaadc.html&cp=4_0_0_5_22_6&anchor=saadc_limits

What we would like to achieve is the following:
1. On device startup, sample the adc channel 1000x -> calculate average baseline value. This value would then used as the trigger threshold.
2. go to sleep
3. when the sample value on the channel is bellow 70% of threshold or above 130% of threshold, trigger an event handler in code (waking the system).

NOTE:
we know how to do #1.
The percentages in #3 are just an example, we would like to be able to change them in the code every so often.
We need to consume as little power as possible.
The comparison should be "continious", if possible. Or, if ADC must be sampled, once per second should be OK

Reading this thread:
https://devzone.nordicsemi.com/f/nordic-q-a/20895/saadc-low-power-scan-mode

I can see that this can be done, but there are not enough code samples to be able to produce something working. 
Must I set the registers directly? is there no wrapper API for this?

I would be very happy to see a working code example.

Regards,
Tjaž

Parents Reply Children
  • Yes, sorry.

    We are trying to do this using nrf9160.

    The docs are these:
    https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf9160%2Fsaadc.html&cp=2_0_0_5_11_8&anchor=saadc_limits

    Regardless of the wrong links, the question remains the same: how to register a handler to run when adc sample is "out of range", and to consume as little power as possible?

    If this can not be done through zephyrs APIs, is it possible in some other way?

  •  Note that this is a 4 year old answer which is not related to nRF Connect SDK.

     

    Tjaz said:
    Regardless of the wrong links, the question remains the same: how to register a handler to run when adc sample is "out of range", and to consume as little power as possible?

     There is currently no API for this in Zephyr, but you can use the nrfx saadc driver directly (modules\hal\nordic\nrfx\drivers\include\nrfx_saadc.h)

    /**
     * @brief Function for setting the SAADC channel limits.
     *
     * When limits are enabled and the conversion result exceeds the defined bounds,
     * the handler function is called with the corresponding event as parameter.
     *
     * @note Before the limits are set, the driver operation mode (simple or advanced) has
     *       to be configured. Only non-blocking conversions can be monitored.
     *
     * @note Changing of the driver operation mode disables all configured limits.
     *
     * @param[in] channel    Channel index.
     * @param[in] limit_low  Limit low value to generate interrupt. Use @c INT16_MIN
     *                       to disable interrupt generation.
     * @param[in] limit_high Limit high value to generate interrupt. Use @c INT16_MAX
     *                       to disable interrupt generation.
     *
     * @retval NRFX_SUCCESS             Requested channel limits were set.
     * @retval NRFX_ERROR_INVALID_PARAM Attempt to activate the limits on disabled channel.
     * @retval NRFX_ERROR_FORBIDDEN     Attempt to activate the limits for blocking conversions.
     * @retval NRFX_ERROR_INVALID_STATE Attempt to activate the limits without configured mode.
     */
    nrfx_err_t nrfx_saadc_limits_set(uint8_t channel, int16_t limit_low, int16_t limit_high);

  • Thank you for the link.

    I have some further questions:

    1. Is there a driver for setting RTC to trigger SAADC sampling on an interval?

    2. Can I use this driver you linked in addition to my existing code, that uses zephyrs driver (#include <drivers/adc.h>)? Or will using nrfx_saadc.h directly mess up things that I configure with adc.h?

    Regards,
    Tjaž

  • Tjaz said:
    1. Is there a driver for setting RTC to trigger SAADC sampling on an interval?

     Yes, I believe the DPPI can be used for this purpose. Have a look here. Just change TIMER0 to RTC.

    One-to-one connection

    This example shows how to create a one-to-one connection between TIMER compare register and SAADC start task.

    The channel configuration is set up first. TIMER0 will publish its COMPARE0 event on channel 0, and SAADC will subscribe its START task to events on the same channel. After that, the channel is enabled through the DPPIC.

    
    NRF_TIMER0->PUBLISH_COMPARE0 = (DPPI_PUB_CHIDX_Ch0) | 
                                   (DPPI_PUB_EN_Msk);
    NRF_SAADC->SUBSCRIBE_START   = (DPPI_SUB_CHIDX_Ch0) | 
                                   (DPPI_SUB_EN_Msk);
      
    NRF_DPPIC->CHENSET = (DPPI_CHENSET_CH0_Set << DPPI_CHENSET_CH0_Pos);

     

    Tjaz said:
    2. Can I use this driver you linked in addition to my existing code, that uses zephyrs driver (#include <drivers/adc.h>)? Or will using nrfx_saadc.h directly mess up things that I configure with adc.h?
    The Zephyr ADC (zephyr\include\drivers\adc.h-->zephyr\drivers\adc\adc_nrfx_saadc.c) uses the functions from hal/nrf_saadc.h. If you configure stuff on the lowest level first (nrf_saadc.h.) the Zephyr layer will not know about it. I would recommend sticking to one layer. But if you know what you're doing, I think it should be possible to mix them
  • Before configuring this "link" via DPPI, I have to get the rtc to work first tho...

    Using nrfx_rtc.h and writing code as such:

    prj.conf

    CONFIG_NRFX_RTC0=y
    CONFIG_NRFX_DPPI=y

    main.c

    #include <nrfx_saadc.h>
    #include <nrfx_rtc.h>
    
    #include <logging/log.h>
    LOG_MODULE_REGISTER(main);
    
    void rtc_handler(nrfx_rtc_int_type_t int_type)
    {
        LOG_INF("RTC HANDLER");
    }
    
    
    void main(void)
    { 
        LOG_INF("INIT NRFX RTC");
        nrfx_rtc_t rtc_dev = NRFX_RTC_INSTANCE(0);
        // copy of default config
        nrfx_rtc_config_t rtc_conf = {
            .prescaler = RTC_FREQ_TO_PRESCALER(32768),
            .interrupt_priority = NRFX_RTC_DEFAULT_CONFIG_IRQ_PRIORITY,
            .tick_latency = NRFX_RTC_US_TO_TICKS(2000, 32768),
            .reliable = false,
        };
        nrfx_err_t nerr = nrfx_rtc_init(&rtc_dev, &rtc_conf, rtc_handler);
        if (nerr != NRFX_SUCCESS)
        {
            LOG_ERR("err: %d", nerr);
        }
    
        nrfx_rtc_cc_set(&rtc_dev,
                        0,
                        1000000,
                        true);
    
        nrfx_rtc_enable(&rtc_dev);
        
        LOG_INF("AFTER INIT NRFX RTC");
    
        // main goes to sleep;
        while (1)
        {
            k_sleep(K_SECONDS(3600));
        }
    }

    I get the following runtime error when the interrupt should happen (there are no compile errors or warnings):

    <err> os: >>> ZEPHYR FATAL ERROR 1: Unhandled interrupt on CPU 0
    <err> os: Current thread: 0x200238c8 (unknown)
    <err> os: Halting system
    

    How do I make zephyr aware of my interupt?

    In the final set up, i will probably have to pass false as the last param to nrfx_rtc_cc_setsince I only want ADC to trigger an interupt. But If the adc interupt is configured only using the nrfx layer, wont a similar error occur?

    Finaly, lets say I add this to the code, as you suggested, to set up DPPI:

        NRF_RTC0->PUBLISH_COMPARE[0] = (DPPI_PUB_CHIDX_Ch0) |
                                       (DPPI_PUB_EN_Msk);
        NRF_SAADC->SUBSCRIBE_START = (DPPI_SUB_CHIDX_Ch0) |
                                     (DPPI_SUB_EN_Msk);
    
        NRF_DPPIC->CHENSET = (DPPI_CHENSET_CH0_Set << DPPI_CHENSET_CH0_Pos);

    This does not compile, with the following errors:

    ../src/main.c: In function 'main':
    ../src/main.c:311:37: error: 'DPPI_PUB_CHIDX_Ch0' undeclared (first use in this function)
         NRF_RTC0->PUBLISH_COMPARE[0] = (DPPI_PUB_CHIDX_Ch0) |
                                         ^~~~~~~~~~~~~~~~~~
    ../src/main.c:311:37: note: each undeclared identifier is reported only once for each function it appears in
    ../src/main.c:312:37: error: 'DPPI_PUB_EN_Msk' undeclared (first use in this function); did you mean 'DPPIC_CHG_CH2_Msk'?
                                        (DPPI_PUB_EN_Msk);
                                         ^~~~~~~~~~~~~~~
                                         DPPIC_CHG_CH2_Msk
    ../src/main.c:313:35: error: 'DPPI_SUB_CHIDX_Ch0' undeclared (first use in this function); did you mean 'DPPI_PUB_CHIDX_Ch0'?
         NRF_SAADC->SUBSCRIBE_START = (DPPI_SUB_CHIDX_Ch0) |
                                       ^~~~~~~~~~~~~~~~~~
                                       DPPI_PUB_CHIDX_Ch0
    ../src/main.c:314:35: error: 'DPPI_SUB_EN_Msk' undeclared (first use in this function); did you mean 'DPPI_PUB_EN_Msk'?
                                      (DPPI_SUB_EN_Msk);
                                       ^~~~~~~~~~~~~~~
                                       DPPI_PUB_EN_Msk
    ../src/main.c:316:27: error: 'DPPI_CHENSET_CH0_Set' undeclared (first use in this function); did you mean 'DPPIC_CHENSET_CH0_Set'?
         NRF_DPPIC->CHENSET = (DPPI_CHENSET_CH0_Set << DPPI_CHENSET_CH0_Pos);
                               ^~~~~~~~~~~~~~~~~~~~
                               DPPIC_CHENSET_CH0_Set
    ../src/main.c:316:51: error: 'DPPI_CHENSET_CH0_Pos' undeclared (first use in this function); did you mean 'DPPIC_CHENSET_CH0_Pos'?
         NRF_DPPIC->CHENSET = (DPPI_CHENSET_CH0_Set << DPPI_CHENSET_CH0_Pos);
                                                       ^~~~~~~~~~~~~~~~~~~~
                                                       DPPIC_CHENSET_CH0_Pos
    ninja: build stopped: subcommand failed.
    FATAL ERROR: command exited with status 1: /home/comemaster/.local/bin/cmake --build /home/comemaster/Synology-Drive/IRNAS-Users/tjaz/Nordic/ncs_v1.3.0/nrf/applications/saadc-test/build
    

    Looking at nrf9160_bitfields.h I can see that some of these identifiers have changed (like  DPPI_CHENSET_CH0_Set -> DPPIC_CHENSET_CH0_Set), while others I can not find at all (like DPPI_PUB_CHIDX_Ch0 or DPPI_PUB_EN_Msk).

    It would be nice, as I have asked before, to get a working example of at lease each element on its own - so rtc configuration, adc configuration and dppi configuration to link them together

    Regards,
    Tjaž

Related