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

ADC_IRQHandler specific calling

Hi, I am new to nordic n51822, i have implemented adc and timer independently. Now, what i want to do is read the adc values on a specific time when the timer overflows for 500ms. I am little confused that void ADC_IRQHandler(void) continually polls and reads the value and displays the value on UART. Can anyone guide me through the steps on how to read adc with specific time, like when enabling timing interrupt timer starts and reads value and every time when the value is read , the interrupt is stopped and values are processed then again the interrupt is activated and timer is started. What i want is to create a specific sample rate in which it reads adc,process values,print and repeat.

    #define UART_TX_BUF_SIZE 256 /**< UART TX buffer size. */

   #define UART_RX_BUF_SIZE 1 /**< UART RX buffer size. */ 
  #define GPIO_TOGGLE_PIN 4 /*!< gpio pin to toggle after delay. */ 
 #ifndef NRF_APP_PRIORITY_HIGH #define NRF_APP_PRIORITY_HIGH 1
  #endif

/** @TIMER2
* 
*
*/

void start_timer(void)
{		
  NRF_TIMER2->MODE = TIMER_MODE_MODE_Timer;  // Set the timer in Counter Mode
  NRF_TIMER2->TASKS_CLEAR = 1;               // clear the task first to be usable for later
	NRF_TIMER2->PRESCALER = 8;                             //Set prescaler. Higher number gives slower timer. Prescaler = 0 gives 16MHz timer
	NRF_TIMER2->BITMODE = TIMER_BITMODE_BITMODE_16Bit;		 //Set counter to 16 bit resolution
	NRF_TIMER2->CC[0] = 31250;                             //Set value for TIMER2 compare register 0
		
  // Enable interrupt on Timer 2, both for CC[0] and CC[1] compare match events
	NRF_TIMER2->INTENSET = (TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos)	;
	//| (TIMER_INTENSET_COMPARE1_Enabled << TIMER_INTENSET_COMPARE1_Pos);
  NVIC_EnableIRQ(TIMER2_IRQn);
	NRF_TIMER2->SHORTS = (TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos) & TIMER_SHORTS_COMPARE0_CLEAR_Msk;	
  NRF_TIMER2->TASKS_START = 1;               // Start TIMER2
}
		
/** TIMTER2 peripheral interrupt handler. This interrupt handler is called whenever there it a TIMER2 interrupt
 */
void TIMER2_IRQHandler(void)
{
	if(NRF_TIMER2->EVENTS_COMPARE[0])
    {
				NRF_TIMER2->EVENTS_COMPARE[0] = 0;           //Clear compare register 0 event	
	    	nrf_gpio_pin_toggle(GPIO_TOGGLE_PIN);
 
    }
}


/**
 * @brief UART events handler.
 */
void uart_events_handler(app_uart_evt_t * p_event)
{
    switch (p_event->evt_type)
    {
        case APP_UART_COMMUNICATION_ERROR: APP_ERROR_HANDLER(p_event->data.error_communication);
            break;

        case APP_UART_FIFO_ERROR:          APP_ERROR_HANDLER(p_event->data.error_code);
            break;

        case APP_UART_TX_EMPTY:            printf("RESULT= %1.1f\r\n", (float)adc_sample); // out ADC result
            break;

        default: break;
    }
}


/**
 * @brief ADC interrupt handler.
 */
void ADC_IRQHandler(void)
{
    nrf_adc_conversion_event_clean();

    adc_sample = nrf_adc_result_get();

    // trigger next ADC conversion
    nrf_adc_start();
}


/**
 * @brief UART initialization.
 */
void uart_config(void)
{
    uint32_t                     err_code;
    const app_uart_comm_params_t comm_params =
    {
        RX_PIN_NUMBER,
        TX_PIN_NUMBER,
        RTS_PIN_NUMBER,
        CTS_PIN_NUMBER,
        APP_UART_FLOW_CONTROL_DISABLED,
        false,
        UART_BAUDRATE_BAUDRATE_Baud38400
    };

    APP_UART_FIFO_INIT(&comm_params,
                       UART_RX_BUF_SIZE,
                       UART_TX_BUF_SIZE,
                       uart_events_handler,
                       APP_IRQ_PRIORITY_LOW,
                       err_code);

    APP_ERROR_CHECK(err_code);
}


/**
 * @brief ADC initialization.
 
 */
void adc_config(void)
{
    const nrf_adc_config_t nrf_adc_config = NRF_ADC_CONFIG_DEFAULT;

    // Initialize and configure ADC
    nrf_adc_configure( (nrf_adc_config_t *)&nrf_adc_config);
    nrf_adc_input_select(NRF_ADC_CONFIG_INPUT_7);
    nrf_adc_int_enable(ADC_INTENSET_END_Enabled << ADC_INTENSET_END_Pos);
		//ADC INTERRUPT ENABLE
    NVIC_SetPriority(ADC_IRQn, NRF_APP_PRIORITY_LOW);
    NVIC_EnableIRQ(ADC_IRQn);
}

/*void adc_init(void)
{
 const nrf_adc_config_t nrf_adc_config = NRF_ADC_CONFIG_DEFAULT;

	// Enable interrupt on ADC sample ready event	
	NRF_ADC->INTENSET = ADC_INTENSET_END_Msk;   
	sd_nvic_SetPriority(ADC_IRQn, NRF_APP_PRIORITY_LOW);  
	sd_nvic_EnableIRQ(ADC_IRQn);
	

}
*/

/**
 * @brief Function for main application entry.
 */
int main(void)
{
    adc_config();
		//adc_init();
    uart_config();

    printf("\n\rADC HAL simple example\r\n");

    printf("Current sample value:\r\n");

    nrf_adc_start();

    while (true)
    {
        // enter into sleep mode
        __WFI();	//WAIT FOR INTERRUPT
			  //__SEV();
        //__WFE();
        //__WFE();
    }
}
  • Hi

    Welcome to Nordic !

    I suspect that you are following the code examples from the SDK 8.1.0. There are additional ADC code examples available on Nordic's Github. The examples are currently all for SDK 6, but you can still copy and paste the code snippets that you need from the examples in order to make it work with SDK 8.1.0.

    The Github ADC examples generally use a PPI channel in order to connect a timer event with the ADC start task. A PPI channel connects the timer and ADC hardware together, so there will be close to no latency. If you would start the ADC start task in a timer interrupt handler, it will generally take a few microseconds for the ADC to start after the timer generates an interrupt. Also, other higher priority tasks (e.g. BLE protocol events) could delay the timer interrupt handler execution even further. PPI is therefore the preferred method to connect timer and ADC, it is quicker, deterministic and lower power.

    In the Github ADC examples it is chosen to use RTC peripheral instead of TIMER. The reason is that it is lower power. TIMER consumes typically ~1mA while the RTC consumes typically ~1uA. The RTC uses 32kHz low frequency clock that has to be started manually before starting the RTC. I would suspect the RTC to be adequate in your case since your sampling frequency is relatively low.

    Another deciding factor is to use either internal 16MHz external crystal or the internal 16MHz RC as a clock source for the ADC. It is a trade-off between accuracy and low power. If you use the crystal, you will have maximum accuracy. If you use the RC it is lower power. If you use the 16MHz crystal, you will have to explicitly start it before start sampling with the ADC. Otherwise, RC will be used by default. The rtc0_triggering_ADC_sample_from_GPIO_pin example shows how to manually start the external 16MHz crystal oscillator.

    If you look for example at the rtc0-triggering-adc-example, the procedure is as follows:

    • Configure the 32kHz clock, RTC and ADC.
    • Configure PPI channel to connect the RTC TICK event with the ADC start task
    • Start the RTC in order to get periodic events and periodic ADC samples
    • The RTC, PPI and ADC operations are pure hardware operations, so the CPU will sleep while all this is happening.
    • When the ADC is finished sampling, the ADC interrupt handler will execute which wakes up the CPU.
    • In the ADC interrupt handler, you can print the result out on UART.

    There is usually explanation on how the examples work at the top of the main file in each example.

    The adc_example_with_softdevice_and_UART shows how to set up the UART and print the result. This example also sends the ADC result over BLE with the NUS service. (NUS = Nordice UART service).

  • Thanks for the detailed explanation Stefan. It has cleared many confusions. Currently, I am using internal 16MHz RC as a clock source, so can I use it with enabling PPI channel? I have tried the following

    static void ppi_init(void)
    

    { // Configure PPI channel 0 to start ADC task NRF_PPI->CH[0].EEP = (uint32_t)NRF_TIMER2->EVENTS_COMPARE[0]; NRF_PPI->CH[0].TEP = (uint32_t)&NRF_ADC->TASKS_START;

    // Enable PPI channel 0 NRF_PPI->CHEN = (PPI_CHEN_CH0_Enabled << PPI_CHEN_CH0_Pos); }

    HERE IS THE TIMER2 CONFIGURATION**********

    void start_timer(void)
    

    { NRF_TIMER2->MODE = TIMER_MODE_MODE_Timer; // Set the timer in Counter Mode NRF_TIMER2->TASKS_CLEAR = 1; // clear the task first to be usable for later NRF_TIMER2->PRESCALER = 8; //Set prescaler. Higher number gives slower timer. Prescaler = 0 gives 16MHz timer NRF_TIMER2->BITMODE = TIMER_BITMODE_BITMODE_16Bit; //Set counter to 16 bit resolution NRF_TIMER2->CC[0] = 31250; //Set value for TIMER2 compare register 0

    // Enable interrupt on Timer 2, both for CC[0] and CC[1] compare match events NRF_TIMER2->INTENSET = (TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos) ; //| (TIMER_INTENSET_COMPARE1_Enabled << TIMER_INTENSET_COMPARE1_Pos); NVIC_EnableIRQ(TIMER2_IRQn); //NRF_TIMER2->SHORTS = (TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos) & TIMER_SHORTS_COMPARE0_CLEAR_Msk; NRF_TIMER2->TASKS_START = 1; // Start TIMER2 }

    Have I enabled the PPI correctly ? As it doesn't seem to work not printing anything on UART

Related