Sampling ADC with PPI and Timer on nrf connect sdk

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:

  1. Timer Configuration: Set up with nrfx_timer_extended_compare to trigger every 5000ms( example)

  2. PPI Channels: Configured two PPI channels:

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

Parents Reply Children
  • Hi

    Thank you for replying.

    I did get the logs:-

    *** Booting nRF Connect SDK v2.5.2 ***
    [00:00:00.535,430] <inf> NRFX_TIMER: Function: nrfx_timer_init, error code: NRFX_SUCCESS.
    [00:00.[00:00:00.535,491] <inf> NRFX_TIMER: Timer id: 1, capture value set: 5000000, channel: 0.
    [00:00:00.535,522] <inf> NRFX_SAADC: Function: nrfx_saadc_init, error code: NRFX_SUCCESS.
    [00:00:00.535,583] <inf> NRFX_PPI: Assigned channel: 18, event end point: 40009140, task end point: 40007004.
    [00:00:00.535,644] <inf> NRFX_PPI: Function: nrfx_ppi_channel_assign, error code: NRFX_SUCCESS.
    [00:00:00.535,675] <inf> NRFX_PPI: Function: nrfx_ppi_channel_enable, error code: NRFX_SUCCESS.
    [00:00:00.535,705] <inf> NRFX_TIMER: Enabled instance: 1.
    [00:00:00.535,705] <inf> ADC_CONSOLE: PPI channels enabled
    
    [00:00:00.573,883] <dbg> ADC_CONSOLE: saadc_event_handler: SAADC buffer at 0x20000874 filled with 2 samples
    data: 141
    data: 145
    [00:00:00.612,030] <dbg> ADC_CONSOLE: saadc_event_handler: SAADC buffer at 0x20000878 filled with 2 samples
    data: 150
    data: 158
    [00:00:00.650,299] <dbg> ADC_CONSOLE: saadc_event_handler: SAADC buffer at 0x20000874 filled with 2 samples
    data: 162
    data: 159
    [00:00:00.688,446] <dbg> ADC_CONSOLE: saadc_event_handler: SAADC buffer at 0x20000878 filled with 2 samples
    data: 168
    data: 173s
    ata: 177
    [00:00:00.764,801] <dbg> ADC_CONSOLE: saadc_event_handler: SAADC buffer at 0x20000878 filled with 2 samples
    data: 174
    data: 181
    [00:00:00.802,947] <dbg> ADC_CONSOLE: saadc_event_handler: SAADC buffer at 0x20000874 filled with 2 samples
    data: 179
    data: 183

    In the logs I can see the buffer getting filled at around 100ms, even though the timer is set for 5000ms. And also when I changed the timer frequency from 1Mhz to 16Mhz, the samples were getting even faster. I suspect this bug is related to timer and not ppi and adc.

  • Figured it out, a silly mistake on my part, the default timer width is 16 bit, adding this line solved the problem.

    timer_config.bit_width = NRF_TIMER_BIT_WIDTH_32;

Related