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

SAADC always busy, error 17, cannot calibrate

I've looked through and tried many examples on getting my SAADC to produce a temperature offset. About a month ago I had the SAADC working really well with a wheatstone bridge pressure sensor. It used a single channel differential input instance with oversampling. The output was filtered and looked very good. I could not get the temperature offset calibration to work because of error 17 (the ADC is busy). This got pushed to the back burner while I worked on some other features of the device I'm working on.

Now I need two more analog inputs. I need 3 channels and oversampling is no longer an option. I added two more single ended channels and now I only get garbage results from the ADC, including my original channel for monitoring the pressure sensor. I'm getting a large negative number. I figured that I should dive into the calibration and get that working first. I've gone from the simple blocking code to the 'nrf_drv_saadc_calibrate_offset()' function. Still the adc claims that it is busy, even though it claimed that the event was done. I do not know if the 'nrf_drv_saadc_buffer_convert' function is hanging the system up, but it is not allowing the calibration command to go through since it claims that the ADC is busy.

Below I'll include the event handler and initialization:

void saadc_event_handler(nrf_drv_saadc_evt_t const * p_event)
{
	uint32_t          err_code;
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
{	
			err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAADC_SAMPLES_IN_BUFFER);												
			if(err_code) set_err_led();
			APP_ERROR_CHECK(err_code);
		
			nrf_saadc_value_t adc_press;					
			adc_press 						= p_event->data.done.p_buffer[0];																				//Raw result from the ADC for pressure - its 2'somplement 16 bit number.
			uint8_t res 					= process_press(adc_press);																							//Hack the raw result down to a unsigned 8 bit number.
//				SEGGER_RTT_printf(0, "Pressure: %d\n", res);																									//If you got the RTT setup, you can display 'res' here to the J-link RTT viewer console as it comes in.
//				uint16_t motor_curr 	= p_event->data.done.p_buffer[1];																				//Raw result from the ADC for motor current - its 2'somplement 16 bit number.
//				uint16_t amb_temp			= p_event->data.done.p_buffer[2];																				//Raw result from the ADC for ambient tempurature - its 2'somplement 16 bit number.

			float press 							= res;																															//Put the data in a float for processing.
			sys_dat.press_filter_out 	= (uint8_t)L_Filter16(&sys_dat.press_filter, (uint8_t)press);				//Average the incoming filter data.
			sys_dat.actual_press 			= sys_dat.press_filter_out;      																		//Record pressure reading in the system data.
//				sys_dat.actual_press = 0;
		
			sys_dat.temperature 			= 25;//L_Filter16(&sys_dat.temp_filter, amb_temp);	//data_to_temp(amb_temp);
			sys_dat.motor_current			= 0;//L_Filter16(&sys_dat.curr_filter, motor_curr);	//data_to_current(motor_curr);
			sys_dat.adc_calib_ctr--;		
		
			if(sys_dat.adc_calib_ctr <= 0)
			{		
//						nrf_drv_saadc_abort();
					bool huh = nrf_drv_saadc_is_busy();
					sys_dat.adc_calibrating = true;
					sys_dat.adc_calib_ctr = CYCLES_BWTN_CALIB;
					set_err_led();
					err_code = nrf_drv_saadc_calibrate_offset();
					APP_ERROR_CHECK(err_code); 
			}				
}
	if(p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE)
	{
			sys_dat.adc_calibrating = false;
			clr_err_led();
	}
}

void saadc_init(void)
{
ret_code_t 									err_code;
nrf_drv_saadc_config_t 			saadc_config;
nrf_saadc_channel_config_t 	channel_config_0;		// Channel for air pressure reading.
	nrf_saadc_channel_config_t 	channel_config_1;		// Channel for motor current reading.
	nrf_saadc_channel_config_t 	channel_config_2;		// Channel for ambient temperature reading.

//Configure SAADC
saadc_config.resolution 				= NRF_SAADC_RESOLUTION_12BIT;                         //Set SAADC resolution to 12-bit. This will make the SAADC output values from 0 (when input voltage is 0V) to 2^12=2048 (when input voltage is 3.6V for channel gain setting of 1/6).
saadc_config.oversample 				=	NRF_SAADC_OVERSAMPLE_DISABLED;                      //No oversampling.
saadc_config.interrupt_priority = APP_IRQ_PRIORITY_LOW;                               //Set SAADC interrupt to low priority.
	saadc_config.low_power_mode     = SAADC_CONFIG_LP_MODE;

//Initialize SAADC
err_code = nrf_drv_saadc_init(&saadc_config, saadc_event_handler);										//Initialize the SAADC with configuration and callback function. The application must then implement the saadc_callback function, which will be called when SAADC interrupt is triggered
APP_ERROR_CHECK(err_code);
	
//Configure SAADC channel 0 - air pressure measurement.
	channel_config_0.reference 	= NRF_SAADC_REFERENCE_VDD4;																	//VDD/4 uses 1/4 of VDD for the reference voltage. We are going to use the 3.3 regulated voltage on the board for VDD so the reference voltage should be .825V.
	channel_config_0.gain 			= NRF_SAADC_GAIN1_2;																				//ADC result = Vdiff*(Gain/Vref)*(2^(bit_resolution - 1)) Gain of 1 should get a range of .1V
channel_config_0.acq_time 	= NRF_SAADC_ACQTIME_10US;                                   //Set acquisition time. Set low acquisition time to enable maximum sampling frequency of 200kHz. Set high acquisition time to allow maximum source resistance up to 800 kohm, see the SAADC electrical specification in the PS. 
	channel_config_0.mode 			= NRF_SAADC_MODE_DIFFERENTIAL;															//Use the ADC as a differrential ADC - compare pin_p to pin_n to get the input voltage.
channel_config_0.pin_p 			= NRF_SAADC_INPUT_AIN0;                                     //Positive input channel for differential input. AIN0 pin maps to physical pin P0.02.
	channel_config_0.pin_n 			= NRF_SAADC_INPUT_AIN1;																			//Negative input channel for differential input. AIN1 pin maps to physical pin P0.03.
channel_config_0.resistor_p = NRF_SAADC_RESISTOR_DISABLED;                              //Disable pullup resistor on the input pin.
channel_config_0.resistor_n = NRF_SAADC_RESISTOR_DISABLED;                              //Disable pulldown resistor on the input pin.
	
	//Initialize SAADC channel 0
err_code = nrf_drv_saadc_channel_init(0, &channel_config_0);                            //Initialize SAADC channel 0 with the channel configuration
if(err_code) set_err_led();
	APP_ERROR_CHECK(err_code);
	
	//Configure SAADC channel 1 - motor current measurement.
	channel_config_1.reference 	= NRF_SAADC_REFERENCE_VDD4;																	//VDD/4 uses 1/4 of VDD for the reference voltage. We are going to use the 3.3 regulated voltage on the board for VDD so the reference voltage should be .825V.
	channel_config_1.gain 			= NRF_SAADC_GAIN1_2;																				//ADC result = Vdiff*(Gain/Vref)*(2^(bit_resolution - 1)) Gain of 1 should get a range of .1V
channel_config_1.acq_time 	= NRF_SAADC_ACQTIME_40US;                                   //Set acquisition time. Set low acquisition time to enable maximum sampling frequency of 200kHz. Set high acquisition time to allow maximum source resistance up to 800 kohm, see the SAADC electrical specification in the PS. 
	channel_config_1.mode 			= NRF_SAADC_MODE_SINGLE_ENDED;															//Use the ADC as a differrential ADC - compare pin_p to pin_n to get the input voltage.
channel_config_1.pin_p 			= NRF_SAADC_INPUT_AIN6;                                              //Positive input channel for differential input. AIN6 pin maps to physical pin P0.30.
	channel_config_1.pin_n 			= NRF_SAADC_INPUT_DISABLED;																	//Negative input channel for differential input.
channel_config_1.resistor_p = NRF_SAADC_RESISTOR_DISABLED;                              //Disable pullup resistor on the input pin.
channel_config_1.resistor_n = NRF_SAADC_RESISTOR_DISABLED;                              //Disable pulldown resistor on the input pin.
	
	//Initialize SAADC channel 1
err_code = nrf_drv_saadc_channel_init(1, &channel_config_1);                            //Initialize SAADC channel 1 with the channel configuration
if(err_code) set_err_led();
	APP_ERROR_CHECK(err_code);
	
	//Configure SAADC channel 2 -  ambient temperature measurement.
	channel_config_2.reference 	= NRF_SAADC_REFERENCE_VDD4;																	//VDD/4 uses 1/4 of VDD for the reference voltage. We are going to use the 3.3 regulated voltage on the board for VDD so the reference voltage should be .825V.
	channel_config_2.gain 			= NRF_SAADC_GAIN1_2;																				//ADC result = Vdiff*(Gain/Vref)*(2^(bit_resolution - 1)) Gain of 1 should get a range of .1V
channel_config_2.acq_time 	= NRF_SAADC_ACQTIME_10US;                                   //Set acquisition time. Set low acquisition time to enable maximum sampling frequency of 200kHz. Set high acquisition time to allow maximum source resistance up to 800 kohm, see the SAADC electrical specification in the PS. 
	channel_config_2.mode 			= NRF_SAADC_MODE_SINGLE_ENDED;															//Use the ADC as a differrential ADC - compare pin_p to pin_n to get the input voltage.
channel_config_2.pin_p 			= NRF_SAADC_INPUT_AIN7;                                     //Positive input channel for differential input. AIN7 pin maps to physical pin P0.31.
	channel_config_2.pin_n 			= NRF_SAADC_INPUT_DISABLED;																	//Negative input channel for differential input.
channel_config_2.resistor_p = NRF_SAADC_RESISTOR_DISABLED;                              //Disable pullup resistor on the input pin.
channel_config_2.resistor_n = NRF_SAADC_RESISTOR_DISABLED;                              //Disable pulldown resistor on the input pin.

	//Initialize SAADC channel 2
err_code = nrf_drv_saadc_channel_init(2, &channel_config_2);                            //Initialize SAADC channel 1 with the channel configuration
if(err_code) set_err_led();
	APP_ERROR_CHECK(err_code);
	
                                            //Configure burst mode for channel 0. Burst is useful together with oversampling. When triggering the SAMPLE task in burst mode, the SAADC will sample "Oversample" number of times as fast as it can and then output a single averaged value to the RAM buffer. If burst mode is not enabled, the SAMPLE task needs to be triggered "Oversample" number of times to output a single averaged value to the RAM buffer.		
 //    }

err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0],SAADC_SAMPLES_IN_BUFFER);    //Set SAADC buffer 1. The SAADC will start to write to this buffer
if(err_code) set_err_led();
	APP_ERROR_CHECK(err_code);

err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1],SAADC_SAMPLES_IN_BUFFER);    //Set SAADC buffer 2. The SAADC will write to this buffer when buffer 1 is full. This will give the applicaiton time to process data in buffer 1.
if(err_code) set_err_led();
	APP_ERROR_CHECK(err_code);
 } 

I am using Keil uVision 5 using SDK 12.2.0 with the NRF52832.

Related