Hi,
I am having hard times to make SAADC working on DK-52, SDK13, S132. I need to perform a voltage measurement on command received from a connected device.
AIN pin is connected as in the following picture:
I also have a digital multimeter between AIN pin and ground.
I am facing two issues:
-
My voltage readings are always different from what I read on the multimeter:
a) 0V from multimeter - 0.17 from SAADC - Delta 0.17 b) 2.50 from multimeter - 2.539 from SAADC - Delta 0.039 c) 1.25 from multimeter - 1.277 from SAADC - Delta 0.027
(With a completely different multimeter, I get more or less the same deltas).
Difference appears to be relevant. Is it true?
-
To minimize the error at point 1, I thought I need to calibrate the SAADC. Because readings are not very frequent, I thought that I could start the calibration on device connection:
static void on_ble_evt(ble_evt_t *p_ble_evt) {
...
case BLE_GAP_EVT_CONNECTED: NRF_LOG_INFO("Connected.\r\n"); err_code = nrf_drv_saadc_calibrate_offset(); if (err_code == NRF_ERROR_BUSY) { NRF_LOG_INFO("\tSAADC BUSY\r\n"); } else { NRF_LOG_INFO("\tCalibration Started...\r\n"); } ... }
Unfortunately I always get NRF_ERROR_BUSY.
Where am I wrong?
Thanks.
UPDATE: I measured the powering voltage, changed the reference voltage to NRF_SAADC_REFERENCE_VDD4 and adjusted refVoltage in get_voltage accordingly. In this way I get much better results. Problem is that powering the device with a battery, reference voltage decreases over time.
This is my SAADC initialization function:
static void saadc_init() {
ret_code_t err_code;
nrf_drv_saadc_config_t saadc_config;
saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;
saadc_config.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;
saadc_config.interrupt_priority = 7;
saadc_config.low_power_mode = false;
err_code = nrf_drv_saadc_init(&saadc_config, &saddc_handler);
APP_ERROR_CHECK(err_code);
nrf_saadc_channel_config_t channel_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
channel_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED;
channel_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED;
channel_config.gain = NRF_SAADC_GAIN1_6;
channel_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
channel_config.acq_time = NRF_SAADC_ACQTIME_10US;
channel_config.mode = NRF_SAADC_MODE_SINGLE_ENDED;
channel_config.pin_p = NRF_SAADC_INPUT_AIN0;
channel_config.pin_n = NRF_SAADC_INPUT_DISABLED;
channel_config.burst = NRF_SAADC_BURST_DISABLED;
err_code = nrf_drv_saadc_channel_init(0, &channel_config);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0],SAADC_SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(err_code);
}
This is where I start the measurement:
static void on_char_value_received(ble_s1_service_t *p_s1, ble_gatts_evt_write_t *p_evt_write) {
uint32_t err_code;
if (p_evt_write->data[0]=='M') {
NRF_LOG_DEBUG("Start Measuring\r\n");
for(int i=0; i<SAADC_SAMPLES_IN_BUFFER; i++) {
err_code = nrf_drv_saadc_sample();
if (err_code == NRF_ERROR_INVALID_STATE)
NRF_LOG_DEBUG("\t SAADC Idle\r\n");
nrf_delay_ms(10);
}
}
}
This is SAADC event handler:
void saddc_handler(nrf_drv_saadc_evt_t const *p_event) {
ret_code_t err_code;
uint16_t adc_value;
if (p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE) {
NRF_LOG_DEBUG("Calibration Completed\r\n");
return;
}
if (p_event->type == NRF_DRV_SAADC_EVT_DONE) {
NRF_LOG_DEBUG("NRF_DRV_SAADC_EVT_DONE\r\n");
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAADC_SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEBUG("Buffer Converted\r\n");
adc_value = 0;
for (int i = 0; i < SAADC_SAMPLES_IN_BUFFER; i++) {
adc_value += p_event->data.done.p_buffer[i];
}
float v = adc_value/SAADC_SAMPLES_IN_BUFFER;
int intPart = (int) v;
int decimalPart = (v - intPart) * 1000;
NRF_LOG_DEBUG("Value %d.%d\r\n", intPart, decimalPart);
float voltage = get_voltage(v);
intPart = (int) voltage;
decimalPart = (voltage - intPart) * 1000;
NRF_LOG_DEBUG("Voltage %d.%d\r\n", intPart, decimalPart);
send_buffer(&m_pollen_service,(uint8_t *)&voltage,sizeof(float));
NRF_LOG_DEBUG("Finished\r\n");
}
}
This is the function that converts the ADC reading in voltage:
float get_voltage(float adc_value) {
float ret = 0;
float refVoltage = 0.6;
float range = 4096; // 2^12
float gain = 6;
ret = (adc_value * refVoltage)/(range/gain);
return ret;
}
UPDATE
Noise at ADC pin at about 0V
Noise at ADC pin at about VDDV