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

Configuring SAADC multiple channel scanning

Hello

I am using nrf52840 chip. I am having trouble configuring SAADC to read analogue input on two channels (one signal is VDD of the chip). I have already searched the forum and came across the thread in the description. I had no problems using SAADC while i was reading one channel, however I need to expand my code to read two channels. When i tried to upgrade my code, the program began to freeze. I do not know where the problem is, so i hope someone can help me to spot the error. I have tried to upgrade my code similar to the code in the thread, but ended up failing. In the description are the header file and the cpp file containing the class I have made. I need to make functions get_analogue_input() and get_VDD() to work.

Thank you for your time,

Best regards

thread: https://devzone.nordicsemi.com/f/nordic-q-a/14938/nrf52-reading-ain0-to-ain4-ain4-fails

/*************************************************************************************************//**
**
**	@file EODRV_Saadc.h
**	Header file for class CEODRV_Saadc
**	
**	@addtogroup DriverObjects 
**	CEODRV_Saadc
**
*****************************************************************************************************/

#ifndef _EODRV_SAADC_H
#define _EODRV_SAADC_H

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include "EODRV.h"
#include "nrf.h"
#include "nrf_drv_saadc.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"
#include "boards.h"
#include "app_error.h"
#include "nrf_delay.h"
#include "app_util_platform.h"
#include "nrf_pwr_mgmt.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "SEGGER_RTT.h"
#include "bsp.h"

//analogue input pins on the board:

#define ANALOGUE_PIN_0 								0   				 
#define ANALOGUE_PIN_1 								1    			  
#define ANALOGUE_PIN_2 								2   				   
#define ANALOGUE_PIN_3 								3    			
#define ANALOGUE_PIN_4 								4   				 
#define ANALOGUE_PIN_5 								5    			  
#define ANALOGUE_PIN_6 								6   				   

//the channel used for the analogue to digital conversion is channel 0

#define SAADC_CHANNEL_0 							0
#define SAADC_CHANNEL_1 							1

//number of analogue pins that are being sampled

#define NUMBER_OF_SAMPLED_CHANNELS 		            2

/*

The DEVIDE_CONSTANT depends on V(p), V(n), gain, reference and resolution
and its value must be changed if any of the listed parameters are changed.

*/

#define DEVIDE_CONSTANT 							4551.1f

//other macros

#define START 										1
#define STOP 										0
#define IS_NOT_STARTED 								0
#define STOP_TASK_EVENT 							1
#define IS_NOT_STOPEED 								0
#define TASK_EVENT_IS_STOPPED 				        0

#define VDD_INDEX 									1

/**
 *  @brief Class for object CEODRV_Saadc, from Driver objects group
 */
 
class CEODRV_Saadc : public CEODRV {
	
private:
	
	//atributes

  volatile unsigned short result[NUMBER_OF_SAMPLED_CHANNELS]; //a buffer in which will be stored the
															 //values of SAADC conversions before being 
														     //devided by the formula

  float voltage_on_the_saadc_pin[NUMBER_OF_SAMPLED_CHANNELS]; //this is the input voltage of the analog pin, that is the
															  //result of SAADC conversion

public:

	//constructior

	CEODRV_Saadc();

	//methods

	void			Init														(void);

	void 			activate_saadc_high_frequency_clock							(void); //Start HFCLK (HIGH FREQUENCY CLOCK) from crystal oscillator,
																						//this will give the SAADC high accuracy

	void 			configure_saadc_channel										(unsigned long channel,
																				 unsigned long gain,               
																			   	 unsigned long mode,
																				 unsigned long refsel,
																				 unsigned long resn,
    																			 unsigned long resp,
																				 unsigned long tacq,
																				 unsigned long pselp,
																				 unsigned long pseln,
																				 unsigned long pselp_position);
														 
	void 			configure_saadc_resolution						    		(unsigned long resolution);

	void 			configure_saadc_14_bit_resolution					    	(void);
														
	void 			configre_saadc_result_buffer								(void);
														 
	void 			configure_saadc_sampling_mode								(unsigned long sampling_mode);
														 
	void 			configure_saadc_sampling_mode_as_task_sampling_mode	    	(void);
														 
	void 			enable_saadc												(void);
	
	void 			disable_saadc												(void);
															
	void 			calibrate_saadc												(void);

	void 			start_saadc_task_event										(void);
														 
	void 			make_a_sample_and_put_it_in_the_result_buffer				(void);

	float*		    calculate_saadc_input_voltage						    	(void);

	void 			stop_saadc_task_event										(void);

	float 		    get_voltage_on_the_saadc_pin								(uint8_t index);

	void 			make_saadc_sample											(void);
	
	float           make_saadc_sample_and_get_the_value_of_volts_on_the_saadc_pin   (uint8_t index);
	
	float           get_analogue_input											(void);
	
	float           get_VDD														(void);

	// Overloaded virtual functions -------------------------------
	// ------------------------------------------------------------
};
#endif // #ifndef _EODRV_SAADC_H
/*************************************************************************************************//**
**
**	@file EODRV_Saadc.cpp
**	Implementation file for class CEODRV_Saadc
**
*****************************************************************************************************/

//#include "EmbeddedStd.h"
#include "EODRV_Saadc.h"

/*************************************************************************************************//**
**	
**	@brief Constructor for CEODRV_Saadc class
**
*****************************************************************************************************/

volatile unsigned short result[NUMBER_OF_SAMPLED_CHANNELS]; 
float voltage_on_the_saadc_pin[NUMBER_OF_SAMPLED_CHANNELS];

CEODRV_Saadc::CEODRV_Saadc() : CEODRV() {
	
	for(int i = 0; i < NUMBER_OF_SAMPLED_CHANNELS; i++){
	
      result[i] = 0;
	  voltage_on_the_saadc_pin[i] = 0;
	}
}

void CEODRV_Saadc::Init(void){

	activate_saadc_high_frequency_clock();

	configure_saadc_channel(SAADC_CHANNEL_0,
							SAADC_CH_CONFIG_GAIN_Gain1_6,               
							SAADC_CH_CONFIG_MODE_SE,
							SAADC_CH_CONFIG_REFSEL_Internal,
							SAADC_CH_CONFIG_RESN_Bypass,
							SAADC_CH_CONFIG_RESP_Bypass,
							SAADC_CH_CONFIG_TACQ_3us,
							SAADC_CH_PSELP_PSELP_AnalogInput3,
							SAADC_CH_PSELN_PSELN_NC,
							SAADC_CH_PSELP_PSELP_Pos);
	
	configure_saadc_channel(SAADC_CHANNEL_1,
							SAADC_CH_CONFIG_GAIN_Gain1_6,               
							SAADC_CH_CONFIG_MODE_SE,
						    SAADC_CH_CONFIG_REFSEL_Internal,
							SAADC_CH_CONFIG_RESN_Bypass,
							SAADC_CH_CONFIG_RESP_Bypass,
							SAADC_CH_CONFIG_TACQ_3us,
							SAADC_CH_PSELP_PSELP_AnalogInput4,
							SAADC_CH_PSELN_PSELN_NC,
						    SAADC_CH_PSELP_PSELP_VDD);
 
    configure_saadc_14_bit_resolution();  
	
	configre_saadc_result_buffer();
	
	configure_saadc_sampling_mode_as_task_sampling_mode();
	
	enable_saadc();

}

void CEODRV_Saadc::activate_saadc_high_frequency_clock(void){  								

	NRF_CLOCK->TASKS_HFCLKSTART = START;						  
    while (NRF_CLOCK->EVENTS_HFCLKSTARTED == IS_NOT_STARTED);
    NRF_CLOCK->EVENTS_HFCLKSTARTED = STOP;

}  

void CEODRV_Saadc::configure_saadc_channel(unsigned long channel,
										   unsigned long gain,               
										   unsigned long mode,
										   unsigned long refsel,
										   unsigned long resn,
								      	   unsigned long resp,
										   unsigned long tacq,
										   unsigned long pselp,
										   unsigned long pseln,
										   unsigned long pselp_position){ 

	NRF_SAADC->CH[channel].CONFIG = (gain   << SAADC_CH_CONFIG_GAIN_Pos) |
									(mode   << SAADC_CH_CONFIG_MODE_Pos) |
									(refsel << SAADC_CH_CONFIG_REFSEL_Pos) |
									(resn   << SAADC_CH_CONFIG_RESN_Pos) |
									(resp   << SAADC_CH_CONFIG_RESP_Pos) |
									(tacq   << SAADC_CH_CONFIG_TACQ_Pos);

    NRF_SAADC->CH[channel].PSELP = pselp << pselp_position;
    NRF_SAADC->CH[channel].PSELN = pseln << SAADC_CH_PSELN_PSELN_Pos;
	
}
														 
void CEODRV_Saadc::configure_saadc_resolution(unsigned long resolution){

	NRF_SAADC->RESOLUTION = resolution << SAADC_RESOLUTION_VAL_Pos;
}

void CEODRV_Saadc::configure_saadc_14_bit_resolution(void){

	configure_saadc_resolution(SAADC_RESOLUTION_VAL_14bit);
}

void CEODRV_Saadc::configre_saadc_result_buffer(void){

	NRF_SAADC->RESULT.MAXCNT = NUMBER_OF_SAMPLED_CHANNELS;
    NRF_SAADC->RESULT.PTR = (uint32_t)&result[0];
	
}

void CEODRV_Saadc::configure_saadc_sampling_mode(unsigned long sampling_mode){

	NRF_SAADC->SAMPLERATE = sampling_mode << SAADC_SAMPLERATE_MODE_Pos;
}

void CEODRV_Saadc::configure_saadc_sampling_mode_as_task_sampling_mode(void){

	configure_saadc_sampling_mode(SAADC_SAMPLERATE_MODE_Task);
}

void CEODRV_Saadc::enable_saadc(void){

	NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos;
}
	
void CEODRV_Saadc::disable_saadc(void){

	NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Disabled << SAADC_ENABLE_ENABLE_Pos;
}

void CEODRV_Saadc::calibrate_saadc(void){

	NRF_SAADC->TASKS_CALIBRATEOFFSET = START;
	while (NRF_SAADC->EVENTS_CALIBRATEDONE == IS_NOT_STARTED);
	NRF_SAADC->EVENTS_CALIBRATEDONE = STOP;
	while (NRF_SAADC->STATUS == (SAADC_STATUS_STATUS_Busy <<SAADC_STATUS_STATUS_Pos));
}

void CEODRV_Saadc::start_saadc_task_event(void){

	NRF_SAADC->TASKS_START = START;
	while (NRF_SAADC->EVENTS_STARTED == IS_NOT_STARTED);
	NRF_SAADC->EVENTS_STARTED = STOP;
}

void CEODRV_Saadc::make_a_sample_and_put_it_in_the_result_buffer(void){

	NRF_SAADC->TASKS_SAMPLE = START;
	while (NRF_SAADC->EVENTS_END == IS_NOT_STARTED);
	NRF_SAADC->EVENTS_END = STOP;
}

float* CEODRV_Saadc::calculate_saadc_input_voltage(void){

		    //Convert the result to voltage
			// Result = [V(p) - V(n)] * GAIN/REFERENCE * 2^(RESOLUTION)
			// Result = (VDD - 0) * ((1/6) / 0.6) * 2^14
			// VDD = Result / 4551.1
	
    for(int i=0;i<NUMBER_OF_SAMPLED_CHANNELS;i++){
				
		voltage_on_the_saadc_pin[i] =(float)result[i]/DEVIDE_CONSTANT;
    }

    return &voltage_on_the_saadc_pin[0];
}


void CEODRV_Saadc::stop_saadc_task_event(void){

    NRF_SAADC->TASKS_STOP = STOP_TASK_EVENT;
	while (NRF_SAADC->EVENTS_STOPPED == IS_NOT_STOPEED){};
    NRF_SAADC->EVENTS_STOPPED = TASK_EVENT_IS_STOPPED;
}

float CEODRV_Saadc::get_voltage_on_the_saadc_pin(uint8_t index){

	return voltage_on_the_saadc_pin[index];
}

void CEODRV_Saadc::make_saadc_sample(void){

	calibrate_saadc();

	// Start the SAADC and wait for the started event.
	start_saadc_task_event();

	// Do a SAADC sample, will put the result in the configured RAM buffer.
	make_a_sample_and_put_it_in_the_result_buffer();

	calculate_saadc_input_voltage();

	stop_saadc_task_event();
}

float CEODRV_Saadc::make_saadc_sample_and_get_the_value_of_volts_on_the_saadc_pin(uint8_t index){

	make_saadc_sample();                    
		
	return get_voltage_on_the_saadc_pin(index);
	
}

float CEODRV_Saadc::get_analogue_input(index){

    return make_saadc_sample_and_get_the_value_of_volts_on_the_saadc_pin(0);
}

float CEODRV_Saadc::get_VDD(void){

	return make_saadc_sample_and_get_the_value_of_volts_on_the_saadc_pin(VDD_INDEX);
}




Related