Description: I’m working on an nRF52 project where I need the SAADC to sample at a specified interval (5000 ms in this example) using a timer and PPI to trigger sampling. However, I’m observing that the sampling does not follow the timer’s rate, the initial trigger works as expected, but subsequent samples do not follow the timer rate, even though I set the sampling rate as 5000ms the samples are continuously being printed. I suspect the issue may lie with the PPI setup between the SAADC END
and START
events, but I haven’t been able to pinpoint the exact cause.
Setup:
-
Timer Configuration: Set up with
nrfx_timer_extended_compare
to trigger every 5000ms( example) -
PPI Channels: Configured two PPI channels:
m_saadc_sample_ppi_channel
connects the timer compare event to theNRF_SAADC_TASK_SAMPLE
.m_saadc_start_ppi_channel
connects the SAADCEND
event to the SAADCSTART
task, as shown in this example by nordic - ncs-inter/lesson6/inter_less6_exer3_solution at main · NordicDeveloperAcademy/ncs-inter · GitHub- .
-
Issue: The SAADC samples does not follow the 5000 ms interval set by the timer.
The code:-
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(ADC_CONSOLE,LOG_LEVEL_DBG);
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/uart.h>
#include <nrfx_saadc.h>
#include <nrfx_timer.h>
#include <helpers/nrfx_gppi.h>
#include <nrfx_ppi.h>
s
#define SAADC_SAMPLE_INTERVAL_MS 5000
#define TIMER_INST_IDX 0
#define SAADC_BUFFER_SIZE 2
#define MESSAGE_QEUEE_BUFFER_SIZE SAADC_BUFFER_SIZE * 20
#define STACKSIZE 1024
#define CONSOLE_THREAD_PRIORITY 7
typedef struct data{
int16_t raw_data;
int16_t data;
} data_t;
const static nrfx_timer_t timer_instance = NRFX_TIMER_INSTANCE(TIMER_INST_IDX);
static uint8_t rx_buf[1];
static uint8_t tx_buf[20];
volatile static bool start_transmission=false;
const struct device *uart = DEVICE_DT_GET(DT_NODELABEL(uart0));
const struct uart_config uart_cfg={
.parity=UART_CFG_PARITY_NONE,
.stop_bits=UART_CFG_STOP_BITS_1,
.data_bits=UART_CFG_DATA_BITS_8,
// .flow_ctrl=UART_CFG_FLOW_CTRL_NONE,
};
static nrf_saadc_value_t saadc_sample_buffer[2][SAADC_BUFFER_SIZE];
volatile static uint32_t saadc_current_buffer = 0;
static uint8_t m_saadc_sample_ppi_channel;
static uint8_t m_saadc_start_ppi_channel;
K_MSGQ_DEFINE(device_message_queue,sizeof(data_t),MESSAGE_QEUEE_BUFFER_SIZE,4);
K_SEM_DEFINE(uart_tx_done, 1, 1);
static void configure_timer(void)
{
nrfx_err_t err;
nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG(1000000);
err = nrfx_timer_init(&timer_instance, &timer_config, NULL);
if (err != NRFX_SUCCESS) {
LOG_ERR("nrfx_timer_init error: %08x", err);
return;
}
uint32_t timer_ticks = nrfx_timer_ms_to_ticks(&timer_instance, SAADC_SAMPLE_INTERVAL_MS);
nrfx_timer_extended_compare(&timer_instance, NRF_TIMER_CC_CHANNEL0, timer_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
}
static void saadc_event_handler(nrfx_saadc_evt_t const * p_event)
{
nrfx_err_t err;
switch (p_event->type)
{
case NRFX_SAADC_EVT_READY:
// nrfx_timer_enable(&timer_instance);
break;
case NRFX_SAADC_EVT_BUF_REQ:
err = nrfx_saadc_buffer_set(saadc_sample_buffer[(saadc_current_buffer++)%2], SAADC_BUFFER_SIZE);
if (err != NRFX_SUCCESS) {
LOG_ERR("nrfx_saadc_buffer_set error: %08x", err);
return;
}
break;
case NRFX_SAADC_EVT_DONE:
data_t sample;
int ret;
for(int i=0; i < p_event->data.done.size; i++){
sample.raw_data = p_event->data.done.p_buffer[i];
sample.data = ((600*6) * p_event->data.done.p_buffer[i]) / ((1<<12));
while((ret = k_msgq_put(&device_message_queue,&sample, K_NO_WAIT )) != 0){
LOG_WRN("Message queue purged");
k_msgq_purge(&device_message_queue);
}
if (ret) {
LOG_ERR("Return value from k_msgq_put = %d", ret);
}
}
LOG_DBG("SAADC buffer at 0x%x filled with %d samples", (uint32_t)p_event->data.done.p_buffer, p_event->data.done.size);
break;
default:
LOG_INF("Unhandled SAADC evt %d", p_event->type);
break;
}
}
static void configure_saadc(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(DT_IRQ(DT_NODELABEL(adc), priority));
if (err != NRFX_SUCCESS) {
LOG_ERR("nrfx_saadc_init error: %08x", err);
return;
}
nrfx_saadc_channel_t channel = NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN1, 0);
channel.channel_config.gain = NRF_SAADC_GAIN1_6;
err = nrfx_saadc_channels_config(&channel, 1);
if (err != NRFX_SUCCESS) {
LOG_ERR("nrfx_saadc_channels_config error: %08x", err);
return;
}
nrfx_saadc_adv_config_t saadc_adv_config = NRFX_SAADC_DEFAULT_ADV_CONFIG;
err = nrfx_saadc_advanced_mode_set(BIT(0),
NRF_SAADC_RESOLUTION_12BIT,
&saadc_adv_config,
saadc_event_handler);
if (err != NRFX_SUCCESS) {
LOG_ERR("nrfx_saadc_advanced_mode_set error: %08x", err);
return;
}
err = nrfx_saadc_buffer_set(saadc_sample_buffer[0], SAADC_BUFFER_SIZE);
if (err != NRFX_SUCCESS) {
LOG_ERR("nrfx_saadc_buffer_set error: %08x", err);
return;
}
err = nrfx_saadc_buffer_set(saadc_sample_buffer[1], SAADC_BUFFER_SIZE);
if (err != NRFX_SUCCESS) {
LOG_ERR("nrfx_saadc_buffer_set error: %08x", err);
return;
}
err = nrfx_saadc_mode_trigger();
if (err != NRFX_SUCCESS) {
LOG_ERR("nrfx_saadc_mode_trigger error: %08x", err);
return;
}
}
static void configure_ppi(void)
{
nrfx_err_t err;
err = nrfx_gppi_channel_alloc(&m_saadc_sample_ppi_channel);
if (err != NRFX_SUCCESS) {
LOG_ERR("nrfx_gppi_channel_alloc error: %08x", err);
return;
}
err = nrfx_gppi_channel_alloc(&m_saadc_start_ppi_channel);
if (err != NRFX_SUCCESS) {
LOG_ERR("nrfx_gppi_channel_alloc error: %08x", err);
return;
}
nrfx_gppi_channel_endpoints_setup(m_saadc_sample_ppi_channel,
nrfx_timer_compare_event_address_get(&timer_instance, NRF_TIMER_CC_CHANNEL0),
nrf_saadc_task_address_get(NRF_SAADC, NRF_SAADC_TASK_SAMPLE));
nrfx_gppi_channel_endpoints_setup(m_saadc_start_ppi_channel,
nrf_saadc_event_address_get(NRF_SAADC, NRF_SAADC_EVENT_END),
nrf_saadc_task_address_get(NRF_SAADC, NRF_SAADC_TASK_START));
}
void disable_ppi(void){
nrfx_gppi_channels_disable(BIT(m_saadc_sample_ppi_channel));
nrfx_gppi_channels_disable(BIT(m_saadc_start_ppi_channel));
nrfx_timer_disable(&timer_instance);
LOG_INF("PPI channels disabled\n");
}
void enable_ppi(void){
nrfx_gppi_channels_enable(BIT(m_saadc_sample_ppi_channel));
nrfx_gppi_channels_enable(BIT(m_saadc_start_ppi_channel));
nrfx_timer_enable(&timer_instance);
LOG_INF("PPI channels enabled\n");
}
void uart_rx_callback(const struct device *dev, struct uart_event *evt, void *user_data) {
switch(evt->type)
{
case UART_RX_RDY:
LOG_INF("Received %c",evt->data.rx.buf[evt->data.rx.offset]);
if((evt->data.rx.len)==1){
if (evt->data.rx.buf[evt->data.rx.offset]=='s'){
enable_ppi();
}
if (evt->data.rx.buf[evt->data.rx.offset]=='b'){
disable_ppi();
}
}
break;
case UART_RX_DISABLED:
uart_rx_enable(uart,rx_buf,sizeof(rx_buf),100);
break;
case UART_TX_DONE:
k_sem_give(&uart_tx_done);
break;
default:
break;
}
}
void thread0(void)
{
while (1) {
data_t temp;
int ret;
ret = k_msgq_get(&device_message_queue, &temp, K_FOREVER);
if (ret) {
LOG_ERR("Return value from k_msgq_get = %d", ret);
}
// uint8_t tx_buf_length = sprintf(tx_buf,"%d\r\n", temp.raw_data);
uint8_t tx_buf_length = sprintf(tx_buf,"data: %hd\r\n",temp.data); // send any one value: raw data or data
// k_usleep(500);
ret = uart_tx(uart,tx_buf,tx_buf_length,SYS_FOREVER_US);
// printk("data :%s\r\n",tx_buf);
if (ret) {
LOG_ERR("Return value from uart_tx = %d", ret);
}
k_sem_take(&uart_tx_done, K_FOREVER);
}
}
int main(void)
{
if(!device_is_ready(uart)){
return 0;
}
int err= uart_configure(uart,&uart_cfg);
if (err == -ENOSYS){
return -ENOSYS;
}
uart_callback_set(uart, uart_rx_callback, NULL);
uart_rx_enable(uart,rx_buf,sizeof(rx_buf),100);
configure_timer();
configure_saadc();
configure_ppi();
return 0;
}
K_THREAD_DEFINE(thread0_id, STACKSIZE, thread0, NULL, NULL, NULL,
CONSOLE_THREAD_PRIORITY, 0, 0);
The prj.conf:-
CONFIG_LOG=n
# CONFIG_LOG_DEFAULT_LEVEL=3
# CONFIG_LOG_MAX_LEVEL=2
# CONFIG_LOG_MODE_DEFERRED=n
# CONFIG_LOG_BACKEND_UART=y
CONFIG_PRINTK=y
CONFIG_GPIO=y
# enable uart driver
CONFIG_SERIAL=y
# enable console
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_UART_ASYNC_API=y
# CONFIG_DEBUG_THREAD_INFO=y
# CONFIG_DEBUG_OPTIMIZATIONS=y
CONFIG_NRFX_SAADC=y
CONFIG_NRFX_PPI=y
CONFIG_NRFX_TIMER0=y
Project Details:
DK- nrf52832
SDK - nrf connect sdk for vs code (v2.5.2) on ubuntu