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

SAADC IRQ Handler not working

Hi

I am working on a project that has two timers, TIMER3 and TIMER4 (TIMER3 as timer and theTIMER4 as a counter). TIMER3 resets after counting up to 4. I am using PPI to increment TIMER4 every time TIMER3 resets. TIMER4 also resets after counting up to 128. I have also a PPI event that compares the event when TIMER4 resets to trigger SAADC->TASKS_SAMPLE.  I have a global variable, adc_counter, that increments every time ADC sample i ready. 

I have configured my SAADC to take just one sample every time the task TASKS_SAMPLE is triggered from the PPI. I wanted to have a customized SAADC_IRQHandler that increments the global variable, the adc_counter, do the conversion, and put that in a buffer. The following is my code. I don't where I have the problem, but just noticed that the adc_counter never increments.

#include "nrf.h"
#include <stdbool.h>
#include <stdint.h>
//#include "bsp.h"
#include "nrf_gpio.h"
#include "nrf_delay.h"
#include "math.h"

//==================================
//PPI channels
#define TIMER4_PPI_CH_A						0 // TIMER3 reloads and TIMER4 increments
#define TIMER4_PPI_CH_B						1 // TIMER4 reloads, one ADC sample is taken
//===================================

//===================================
// TIMER CC registers
#define TIMER3_RELOAD_CC_NUM				5
#define TIMER4_RELOAD_CC_NUM 				5
//===================================

// TIMER3 reload value. The PWM frequency equals '16000000 / TIMER_RELOAD_VALUE'
#define TIMER3_RELOAD                   4 		
#define TIMER4_RELOAD					128		// TIMER4 counts upto 128. Count is incremented every timer TIMER3 RELOADs
#define SAMPLES_IN_BUFFER				32 	// Total number of ADC samples

static int adc_counter = 0; // counter for the ADC samples
volatile int16_t adc_value = 0;
volatile float adc_exact_value = 0;
static uint32_t adc_buffer[SAMPLES_IN_BUFFER];

//timer init
void timers_init(void)
{
    NRF_TIMER3->BITMODE                 		= TIMER_BITMODE_BITMODE_08Bit << TIMER_BITMODE_BITMODE_Pos;
    NRF_TIMER3->PRESCALER               		= 0;
    NRF_TIMER3->SHORTS                  		= TIMER_SHORTS_COMPARE0_CLEAR_Msk << TIMER3_RELOAD_CC_NUM;
    NRF_TIMER3->MODE                    		= TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos;
    NRF_TIMER3->CC[TIMER3_RELOAD_CC_NUM] 		= TIMER3_RELOAD;
	
		NRF_TIMER4->BITMODE                 	= TIMER_BITMODE_BITMODE_08Bit << TIMER_BITMODE_BITMODE_Pos;
    NRF_TIMER4->PRESCALER               		= 0;
    NRF_TIMER4->SHORTS                  		= TIMER_SHORTS_COMPARE0_CLEAR_Msk << TIMER4_RELOAD_CC_NUM;
    NRF_TIMER4->MODE                    		= TIMER_MODE_MODE_Counter << TIMER_MODE_MODE_Pos;
    NRF_TIMER4->CC[TIMER4_RELOAD_CC_NUM] 		= TIMER4_RELOAD;
}

void timers_start(void)
{
    NRF_TIMER3->TASKS_START = 1;		
	NRF_TIMER4->TASKS_START = 1;
}
void SAADC_IRQHandler(void)
{
  // Clear dataready event
	NRF_SAADC-> EVENTS_END = 0;
	adc_exact_value =(((float)adc_value/4096)*3.6f*1000)*5.545f;
	adc_buffer[adc_counter] = adc_exact_value;	
	adc_counter++;
}

void adc_init(void)

	// Enable interrupt on ADC sample ready
	NRF_SAADC->INTENSET = SAADC_INTENSET_END_Msk;
	NVIC_EnableIRQ(SAADC_IRQn); 
	
	// configure ADC
	NRF_SAADC->CH[0].CONFIG = (SAADC_CH_CONFIG_GAIN_Gain1_6    << SAADC_CH_CONFIG_GAIN_Pos) |
                            (SAADC_CH_CONFIG_MODE_SE         << SAADC_CH_CONFIG_MODE_Pos) |
                            (SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
                            (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESN_Pos) |
                            (SAADC_CH_CONFIG_RESP_Bypass     << SAADC_CH_CONFIG_RESP_Pos) |
                            (SAADC_CH_CONFIG_TACQ_3us        << SAADC_CH_CONFIG_TACQ_Pos);

  // Configure the SAADC channel with AIN2 as positive input, no negative input(single ended). // change it to AIN5 in the final version
  NRF_SAADC->CH[0].PSELP = SAADC_CH_PSELP_PSELP_AnalogInput2 << SAADC_CH_PSELP_PSELP_Pos;
  NRF_SAADC->CH[0].PSELN = SAADC_CH_PSELN_PSELN_NC << SAADC_CH_PSELN_PSELN_Pos; // Will be ignored if MODE is SE
	
  // Configure the SAADC resolution.
  NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL_12bit << SAADC_RESOLUTION_VAL_Pos;
	
  // Configure result to be put in RAM at the location of "adc_value" variable.
  // Number of sample counts
  NRF_SAADC->RESULT.MAXCNT = 1;
  NRF_SAADC->RESULT.PTR = (uint32_t)&adc_value;
	
  // Enable ADC
  NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos;
	
  //Start the ADC 
  NRF_SAADC->TASKS_START = 1;
  while(NRF_SAADC->EVENTS_STARTED ==0);
  NRF_SAADC->EVENTS_STARTED = 0;
  
}

void timer4_adc_ppi(void)
{
	// Create PPI event when TIMER3 reloads that triggers an increment counting task in TIMER4
	NRF_PPI->CH[TIMER4_PPI_CH_A].EEP 		= (uint32_t)&NRF_TIMER3->EVENTS_COMPARE[TIMER3_RELOAD_CC_NUM];
	NRF_PPI->CH[TIMER4_PPI_CH_A].TEP 		= (uint32_t)&NRF_TIMER4->TASKS_COUNT;
	
	
	// Create PPI event on TIMER4 when it reaches TIMER4_RELOAD, that triggers ADC sampling task
	NRF_PPI->CH[TIMER4_PPI_CH_B].EEP 		= (uint32_t)&NRF_TIMER4->EVENTS_COMPARE[TIMER4_RELOAD_CC_NUM];
	NRF_PPI->CH[TIMER4_PPI_CH_B].TEP 		= (uint32_t)&NRF_SAADC->TASKS_SAMPLE;	
	
	NRF_PPI->CHENSET 							= ( 1 << TIMER4_PPI_CH_A) | ( 1 << TIMER4_PPI_CH_B);
}

int main(void)
{
   //Empty adc_buffer
    for(int n = 0; n < sizeof(adc_buffer)/sizeof(adc_buffer[0]); n++)
	{
		adc_buffer[n] = 0;
	}
	
	// init adc
    adc_init();
    
    // init timers
    timers_init();
    
    // start ppi
    timer4_adc_ppi();
    
    // start timers
    timers_start();
    
    
    while(adc_counter < SAMPLES_IN_BUFFER)
    {
        //do nothing until all adc samples are collected
    }
    
    // calculate the average of the samples in the buffer
    float sample_sum =0;
	float sample_avg=0;
	
	for (int j = 0; j < sizeof(adc_buffer)/sizeof(adc_buffer[0]); j++)
	{
	    sample_sum += adc_buffer[j];
	}
	sample_avg = sample_sum/sizeof(adc_buffer)/sizeof(adc_buffer[0]);
	sample_avg = (uint16_t)sample_avg;
	// UART print sample_avg (to be implemented!)
	
	//Empty adc_buffer for next measurement
    for(int n = 0; n < sizeof(adc_buffer)/sizeof(adc_buffer[0]); n++)
	{
		adc_buffer[n] = 0;
	}
    
}

Is there  something I am missing in my code? My goal is to take 32 samples, calculate the average and print that over UART. I didn't want to use the nrf_drv_saadc_buffer_convert() API function, as the register interface is more readable and understandable to me. Any comments, or improved code is appreciated.

  • From earlier:
    "I checked the SAADC registers while debugging. I just noticed that TASKs_SAMPLE in PPI enables (sets to 1) EVENTS_DONE, EVENTS_END and EVENTS_RESULTDONE  registers. I tried to disable them in my IRQ handler, but only EVENTS_END was able to clear to zero. "

    You only need EVENTS_END to be cleared, it's the only interrupt that we care about. Does you SAADS IRQ not fire at the end of the 32 samples? 

  • " Does you SAADS IRQ not fire at the end of the 32 samples? "

    MY SAADC_IRQ does not fire at the end of the 32 samples. After the the 32th sample, the mcu exits the  while(adc_counter < SAMPLES_IN_BUFFER) loop in the main function, continue executing the remaining lines of the main function. At the end of the main function, the IRQ_SAADC fires again. And when it leaves the IRQ, the MCU jumps back to line 164 in my code above (nrf_saadc_disable()) and continues executing the remaining lines of the main function before it finally halts. 

  • You need to encase your state machine in a while(1), otherwise the CPU will halt after the first execution:

    int main(void)
    {
       //Empty adc_buffer
        for(int n = 0; n < sizeof(adc_buffer)/sizeof(adc_buffer[0]); n++)
    	{
    		adc_buffer[n] = 0;
    	}
    	
    	// init adc
        adc_init();
        
        // init timers
        timers_init();
        
        // start ppi
        timer4_adc_ppi();
        
        // start timers
        timers_start();
        
        while(1)
        {
            while(adc_counter < SAMPLES_IN_BUFFER)
            {
                //do nothing until all adc samples are collected
            }
            
            // calculate the average of the samples in the buffer
            float sample_sum =0;
        	float sample_avg=0;
        	
        	for (int j = 0; j < sizeof(adc_buffer)/sizeof(adc_buffer[0]); j++)
        	{
        	    sample_sum += adc_buffer[j];
        	}
        	sample_avg = sample_sum/sizeof(adc_buffer)/sizeof(adc_buffer[0]);
        	sample_avg = (uint16_t)sample_avg;
        	// UART print sample_avg (to be implemented!)
        	
        	//Empty adc_buffer for next measurement
            for(int n = 0; n < sizeof(adc_buffer)/sizeof(adc_buffer[0]); n++)
        	{
        		adc_buffer[n] = 0;
        	}
        }    
    }

Related