ADC Battery Voltage Sample
Introduction
The ADC battery voltage sample reads the battery voltage through the on-board voltage divider using the ADC (Analog to Digital Converter) and prints the result in millivolts. This sample is compatible with the following boards:
In order to use this sample with the Icarus SoM DK, some additional steps must be taken. See the Icarus SoM DK datasheet for more information.
Download
ADC Battery VoltageNCS v3.2.1Project configuration
The application is configured in the prj.conf file. For this sample only the ADC driver is needed:
CONFIG_ADC=y
CONFIG_ADC enables the ADC driver, which is needed to read from the analog input pin.
The sysbuild.conf file enables MCUBoot so the sample can be flashed over USB:
SB_CONFIG_BOOTLOADER_MCUBOOT=y
Code explanation
ADC initialization
The ADC is accessed through its device tree node label. The init_adc function obtains the device reference and configures the channel:
#define ADC_NODE DT_NODELABEL(adc)
static const struct device *adc_dev;
static const struct adc_channel_cfg m_1st_channel_cfg = {
.gain = ADC_GAIN,
.reference = ADC_REFERENCE,
.acquisition_time = ADC_ACQUISITION_TIME,
.channel_id = ADC_1ST_CHANNEL_ID,
.input_positive = ADC_1ST_CHANNEL_INPUT,
};
static bool init_adc(void)
{
int err;
adc_dev = DEVICE_DT_GET(ADC_NODE);
if (!device_is_ready(adc_dev)) {
printk("Error: ADC device not ready\n");
return false;
}
err = adc_channel_setup(adc_dev, &m_1st_channel_cfg);
if (err) {
printk("Error in ADC setup: %d\n", err);
return false;
}
return true;
}
The ADC_REFERENCE is set to ADC_REF_INTERNAL (0.6 V) and ADC_GAIN is set to 1/6, giving an input voltage range of 0.6 / (1/6) = 3.6 V. The value is read from ADC_1ST_CHANNEL_INPUT which maps to analog input pin 0.
Reading battery voltage
The get_battery_voltage function reads a sample from the ADC and converts it to millivolts using the voltage divider resistor values:
#define BATVOLT_R1 4.7f /* MOhm */
#define BATVOLT_R2 10.0f /* MOhm */
#define INPUT_VOLT_RANGE 3.6f /* Volts */
#define VALUE_RANGE_10_BIT 1.023 /* (2^10 - 1) / 1000 */
static int get_battery_voltage(uint16_t *battery_voltage)
{
int err;
const struct adc_sequence sequence = {
.channels = BIT(ADC_1ST_CHANNEL_ID),
.buffer = m_sample_buffer,
.buffer_size = sizeof(m_sample_buffer),
.resolution = ADC_RESOLUTION,
};
if (!adc_dev) {
return -1;
}
err = adc_read(adc_dev, &sequence);
if (err) {
printk("ADC read err: %d\n", err);
return err;
}
float sample_value = 0;
for (int i = 0; i < BUFFER_SIZE; i++) {
sample_value += (float)m_sample_buffer[i];
}
sample_value /= BUFFER_SIZE;
*battery_voltage = (uint16_t)(sample_value *
(INPUT_VOLT_RANGE / VALUE_RANGE_10_BIT) *
((BATVOLT_R1 + BATVOLT_R2) / BATVOLT_R2));
return 0;
}
ADC_RESOLUTION is set to 10 bits (values from 0 to 1023). The raw sample is scaled by the voltage range and the voltage divider ratio to compute the actual battery voltage. For more details on the voltage divider, see Battery Voltage Measurement in the datasheet.
Main loop
The main loop reads and prints the battery voltage every 2 seconds:
if (!init_adc()) {
return -1;
}
while (1) {
uint16_t battery_voltage = 0;
get_battery_voltage(&battery_voltage);
printk("Battery voltage: %u mV\n", battery_voltage);
k_sleep(K_MSEC(2000));
}