Task description
I would like to use RTC2's COMPARE_0-event to trigger SAADC's conversion task via PPI, continuously. The RTC needs to operate by itself without CPU intervention or interrupts. I've managed to trigger the required task on the SAADC side, and got out some readings after the conversion, which would suggest that at least the PPI connection and SAADC are working. I tried to look for answers on this forum (elsewhere online too, but this forum seemed most likely to provide an answer) but in most cases the provided help was a dead link or some old library that I don't have an access to.
The goal is a low-power, autonomous RTC-to-SAADC operation.
I've been working with both hal/nrf- and nrfx-libraries regarding all these drivers.
my_rtc.c
#include <hal/nrf_rtc.h> #include <nrfx_rtc.h> static nrfx_rtc_t my_rtc = NRFX_RTC_INSTANCE(2); static nrfx_rtc_config_t my_rtc_conf = { .prescaler = RTC_FREQ_TO_PRESCALER(8), .interrupt_priority = NRFX_RTC_DEFAULT_CONFIG_IRQ_PRIORITY, .tick_latency = 0, .reliable = false, }; static uint32_t p_mask; static void my_rtc_handler(nrfx_rtc_int_type_t int_type) { LOG_INF("my_rtc_handler"); } uint32_t my_rtc_address_event_comp0(void) { return nrfx_rtc_event_address_get(&my_rtc, NRF_RTC_EVENT_COMPARE_0); } uint32_t my_rtc_address_task_clear(void) { return nrfx_rtc_task_address_get(&my_rtc, NRF_RTC_TASK_CLEAR); } void my_rtc_init() { nrfx_err_t err; err = nrfx_rtc_init(&my_rtc, &my_rtc_conf, rtc_handler); nrfx_rtc_tick_disable(&my_rtc); nrfx_rtc_overflow_disable(&my_rtc); nrfx_rtc_int_disable(&my_rtc, &p_mask); err = nrfx_rtc_cc_set(&my_rtc, 0, 4, false); nrfx_rtc_enable(&my_rtc); }
my_ppi.c
#include <nrfx_ppi.h> static nrf_ppi_channel_t my_ppi_ch0; void my_ppi_set() { nrfx_ppi_channel_alloc(&my_ppi_ch0); nrfx_ppi_channel_assign(my_ppi_ch0, my_rtc_address_event_comp0(), my_saadc_address_task_sample()); nrfx_ppi_channel_enable(my_ppi_ch0); }
my_saadc.c
#include <hal/nrf_saadc.h> #include <nrfx_saadc.h> #define MM_SAADC_CH_CNT 2 static nrf_saadc_value_t samples[MY_SAADC_CH_CNT]; static nrfx_saadc_channel_t channels[MY_SAADC_CH_CNT] = { NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN0, 0), NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN1, 1), }; static nrfx_saadc_adv_config_t p_config = NRFX_SAADC_DEFAULT_ADV_CONFIG; uint32_t my_saadc_address_task_sample() { return nrf_saadc_task_address_get(NRF_SAADC, NRF_SAADC_TASK_SAMPLE); } static uint8_t next_free_buf_index(void) { static uint8_t buffer_index = -1; buffer_index = (buffer_index + 1) % MY_SAADC_CH_CNT; return buffer_index; } static void my_saadc_handler(nrfx_saadc_evt_t const * p_event) { LOG_INF("SAADC handler reached"); nrfx_err_t err; switch (p_event->type) { case NRFX_SAADC_EVT_DONE: LOG_INF("-------------> NRFX_SAADC_EVT_DONE"); // Buffer with data is available here: p_event->data.done.p_buffer for (int i = 0; i < p_event->data.done.size; i++) { LOG_INF("CH%d: %d", i, p_event->data.done.p_buffer[i]); } break; case NRFX_SAADC_EVT_BUF_REQ: LOG_INF("NRFX_SAADC_EVT_BUF_REQ"); err = nrfx_saadc_buffer_set(&samples[next_free_buf_index()], MM_SAADC_CH_CNT); break; default: LOG_INF("default: event type %x", p_event->type); break; } } void my_saadc_init(void) { nrfx_err_t err; IRQ_CONNECT(DT_IRQN(DT_NODELABEL(adc)), DT_IRQ(DT_NODELABEL(adc), priority), nrfx_isr, nrfx_saadc_irq_handler, 0); err = nrfx_saadc_init(NRFX_SAADC_DEFAULT_CONFIG_IRQ_PRIORITY); err = nrfx_saadc_channels_config(channels, MY_SAADC_CH_CNT); err = nrfx_saadc_advanced_mode_set((1<<0|1<<1), NRF_SAADC_RESOLUTION_12BIT, &p_config, my_saadc_handler); err = nrfx_saadc_buffer_set(&samples[next_free_buf_index()], MY_SAADC_CH_CNT); err = nrfx_saadc_mode_trigger(); }
main.c
void main(void) { my_saadc_init(); my_rtc_init(); my_ppi_set(); }
The problem
Once establishing the connection between RTC2 and SAADC via PPI, the RTC sends the event as expected, but does this one-shot. I need the RTC to continue running and providing events. The ideal situation would be that once the SAADC has completed the conversion, the CPU would be allowed to return to the sleep state, and the rest of the peripherals kept operating autonomously.
I tried to call a function from the SAADC handler, that would clear some of the registers and re-start the RTC. This was my drowning man's straw, since I know this doesn't support the key idea of CPU-independent peripheral. But even with this solution I was unable to re-start the RTC.
/* my_rtc.c */ void my_rtc_clear_all(void) { nrf_rtc_event_clear(NRF_RTC2, NRF_RTC_EVENT_COMPARE_0); nrf_rtc_task_trigger(NRF_RTC2, NRF_RTC_TASK_CLEAR); nrf_rtc_task_trigger(NRF_RTC2, NRF_RTC_TASK_START); } /* my_saadc.h */ ... ... switch (p_event->type) { case NRFX_SAADC_EVT_DONE: LOG_INF("-------------> NRFX_SAADC_EVT_DONE"); my_rtc_clear_all(); // <--- Attempting to restart RTC for (int i = 0; i < p_event->data.done.size; i++) { LOG_INF("CH%d: %d", i, p_event->data.done.p_buffer[i]); } break; case NRFX_SAADC_EVT_BUF_REQ: ... ...
Further development
Once I get the RTC to trigger SAADC conversion autonomously, my goal is to yet further to bind SAADC ready-type event to GPIOTE toggle-task. In my previous tests I managed to create RTC-to-GPIOTE connection via PPI with a successful pin toggle, but even then the RTC triggers only once. This is why I think I've got something wrong with either the RTC setup or usage.
Disclaimer
This is my very first post on any professional platform, so I apoligize for any bad form that might take in place with this ticket. Thank you for understanding and for your assistance.