Hi!
I am trying to use the SAADC driver on SDK13, following some examples from the playground repo. I am using the app timer to trigger a measurement for 4 channels.
1 - When I read the output buffer, the readings appear to be out of order i.e. they are not AIN0,1,2,3 but AIN3,0, 1, 2. I am not sure why this may be the case, and how do I know what the order is supposed to be? If I comment out my adv_update function, this order changes. This is kind of puzzling to me. Hope you offer an explanation.
2 - When the ADC buffer is printed, the values just before the calibration procedure are way off (prints 1000, 8000!). And the buffer just after the calibration has mostly zeros. If this has to do with the calibration procedure, how can I ignore these values.
Here is my code:
void saadc_init(void)
{
ret_code_t err_code;
nrf_drv_saadc_config_t saadc_config;
nrf_saadc_channel_config_t channel_config;
//Configure SAADC
saadc_config.resolution = NRF_SAADC_RESOLUTION_10BIT;
saadc_config.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;
saadc_config.interrupt_priority = APP_IRQ_PRIORITY_LOW; //Set SAADC interrupt to low priority.
//Initialize SAADC
err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback); //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
channel_config.reference = NRF_SAADC_REFERENCE_INTERNAL; //Set internal reference of fixed 0.6 volts
channel_config.gain = NRF_SAADC_GAIN1_6; //Set input gain to 1/6. The maximum SAADC input voltage is then 0.6V/(1/6)=3.6V. The single ended input range is then 0V-3.6V
channel_config.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.mode = NRF_SAADC_MODE_SINGLE_ENDED; //Set SAADC as single ended. This means it will only have the positive pin as input, and the negative pin is shorted to ground (0V) internally.
channel_config.pin_n = NRF_SAADC_INPUT_DISABLED; //Since the SAADC is single ended, the negative pin is disabled. The negative pin is shorted to ground internally.
channel_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED; //Disable pullup resistor on the input pin
channel_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED; //Disable pulldown resistor on the input pin
//Initialize SAADC channel
channel_config.pin_p = NRF_SAADC_INPUT_AIN0; //Select the input pin for the channel. AIN0 pin maps to physical pin P0.02.
err_code = nrf_drv_saadc_channel_init(0, &channel_config); //Initialize SAADC channel 0 with the channel configuration
APP_ERROR_CHECK(err_code);
channel_config.pin_p = NRF_SAADC_INPUT_AIN1; //Select the input pin for the channel. AIN1 pin maps to physical pin P0.03.
err_code = nrf_drv_saadc_channel_init(1, &channel_config); //Initialize SAADC channel 1 with the channel configuration
APP_ERROR_CHECK(err_code);
channel_config.pin_p = NRF_SAADC_INPUT_AIN2; //Select the input pin for the channel. AIN2 pin maps to physical pin P0.04.
err_code = nrf_drv_saadc_channel_init(2, &channel_config); //Initialize SAADC channel 2 with the channel configuration
APP_ERROR_CHECK(err_code);
channel_config.pin_p = NRF_SAADC_INPUT_AIN3; //Select the input pin for the channel. AIN3 pin maps to physical pin P0.05.
err_code = nrf_drv_saadc_channel_init(3, &channel_config); //Initialize SAADC channel 3 with the channel configuration
APP_ERROR_CHECK(err_code);
if(SAADC_BURST_MODE) {
NRF_SAADC->CH[0].CONFIG |= 0x01000000; //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.
NRF_SAADC->CH[1].CONFIG |= 0x01000000;
NRF_SAADC->CH[2].CONFIG |= 0x01000000;
NRF_SAADC->CH[3].CONFIG |= 0x01000000;
}
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
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.
APP_ERROR_CHECK(err_code);
}
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
ret_code_t err_code;
//Capture offset calibration complete event
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
{
//Evaluate if offset calibration should be performed. Configure the SAADC_CALIBRATION_INTERVAL constant to change the calibration frequency
if((m_adc_evt_counter % SAADC_CALIBRATION_INTERVAL) == 0)
{
nrf_drv_saadc_abort(); // Abort all ongoing conversions. Calibration cannot be run if SAADC is busy
m_saadc_calibrate = true; // Set flag to trigger calibration in main context when SAADC is stopped
}
NRF_LOG_INFO("ADC event number: %d",(int)m_adc_evt_counter);
for (int i = 0; i < p_event->data.done.size; i++)
{
NRF_LOG_INFO("%d", p_event->data.done.p_buffer[i]);
}
if(m_saadc_calibrate == false)
{
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAADC_SAMPLES_IN_BUFFER); //Set buffer so the SAADC can write to it again.
APP_ERROR_CHECK(err_code);
}
m_adc_evt_counter++;
}
else if (p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE)
{
err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAADC_SAMPLES_IN_BUFFER); //Set buffer so the SAADC can write to it again.
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAADC_SAMPLES_IN_BUFFER); //Need to setup both buffers, as they were both removed with the call to nrf_drv_saadc_abort before calibration.
APP_ERROR_CHECK(err_code);
NRF_LOG_INFO("SAADC calibration complete!"); //Print on UART
}
}
My timer code/other inits:
static void saadc_handler(void * p_context)
{
nrf_drv_saadc_sample();
}
static void adv_handler(void * p_context)
{
// volatile variable
adv_update = true;
}
int main(void)
{
err_code = app_timer_create(&saadc_timer,
APP_TIMER_MODE_REPEATED,
saadc_handler);
err_code = app_timer_create(&adv_timer,
APP_TIMER_MODE_REPEATED,
adv_handler);
err_code = app_timer_start(saadc_timer, SAADC_INTERVAL, NULL);
err_code = app_timer_start(adv_timer, ADV_INTERVAL, NULL);
APP_ERROR_CHECK(err_code);
saadc_init();
for (;;) {
if (adv_update == true) {
update_adv_data();
adv_update = false;
}
if(m_saadc_calibrate == true) {
NRF_LOG_INFO("SAADC calibration starting");
while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS); //Trigger calibration task
m_saadc_calibrate = false;
}
}
}