In this exercise, we will explore the simple mode of the nrfx SAADC driver to measure battery voltage on the chip supply (VDD) at a regular interval, using a software timer.
Open the code base of the exercise by navigating to Create a new application in the nRF Connect for VS Code extension, select Copy a sample, and search for Lesson 6 – Exercise 2.
Alternatively, in the GitHub repository for this course, go to the base code for this exercise, found in lesson6/inter_less6_exer2.
1. Let’s enable the SAADC driver by adding the following line into the application configuration file prj.conf
.
CONFIG_NRFX_SAADC=y
Kconfig2. Add SAADC related header file by including the following line at the top (include section) of the main.c
#include <nrfx_saadc.h>
C3. We need to declare some objects that will be used later in the initialization process.
3.1 Declare the struct to hold the configuration for the SAADC channel used to sample the battery voltage. The macro will assign default configuration parameters for a single-ended input. The configuration can be changed later. Add this line close to the top of the main.c
:
static nrfx_saadc_channel_t channel = NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN0, 0);
CConnect a battery between GND and analog input 0 (AIN0). Check the Hardware and Layout ->Pin assignment chapter in the Product specification to know which Pin is connected to the analog inputs on your choice of SoC/SiP. You can also connect a jumper wire between analog input 0 and VDD if you do not have a battery available.
3.2 Declare the buffer where the SAADC sample value will be stored during sampling. Add this line below code from the last point:
static int16_t sample;
CMake sure that the voltage applied to the analog input does not exceed VDD. If you have a battery with a higher voltage level than VDD, you need to use a voltage divider between the battery and the input.
If you want to measure battery voltage directly from VDD, you can replace NRF_SAADC_INPUT_AIN0
with NRF_SAADC_INPUT_VDD
.
4. We will use a software timer (k_timer
) to trigger the sampling.
4.1 Define the sample interval by adding this line close to the top of main.c
:
#define BATTERY_SAMPLE_INTERVAL_MS 2000
C4.2 Define the timer instance that will be used for sampling:
K_TIMER_DEFINE(battery_sample_timer, battery_sample_timer_handler, NULL);
C4.3 Add forward declaration of timer callback handler right before the previous timer definition:
static void battery_sample_timer_handler(struct k_timer * timer);
C5. Configure the SAADC driver.
5.1 We will reference the ADC defined in the Zephyr devicetree, to make the code more portable. To connect the SAADC interrupt to SAADC interrupt handler, add these lines:
IRQ_CONNECT(DT_IRQN(DT_NODELABEL(adc)),
DT_IRQ(DT_NODELABEL(adc), priority),
nrfx_isr, nrfx_saadc_irq_handler, 0);
CBy default, the ADC is enabled in the board DTS file for all DKs supported by this course, but for custom boards you may have to enable it in your DTS or overlay file using the following code snippet:
&adc {
status = "okay";
};
Devicetree5.2 Before using the nrfx SAADC driver, the driver instance must be initialized. We will again refer to the devicetree to get the configured priority of the ADC node and use this for the driver:
nrfx_err_t err = nrfx_saadc_init(DT_IRQ(DT_NODELABEL(adc), priority));
if (err != NRFX_SUCCESS) {
printk("nrfx_saadc_mode_trigger error: %08x", err);
return;
}
C5.3 Configure the SAADC channel using the previously defined channel configuration structure. The default configuration uses GAIN=1, which is too high to support supply voltage measurements. We need to change the gain config before configuring the channel:
channel.channel_config.gain = NRF_SAADC_GAIN1_6;
err = nrfx_saadc_channels_config(&channel, 1);
if (err != NRFX_SUCCESS) {
printk("nrfx_saadc_channels_config error: %08x", err);
return;
}
C5.4 Configure SAADC driver in simple mode on channel 0. Passing NULL to the last argument will make the driver operate in blocking mode:
err = nrfx_saadc_simple_mode_set(BIT(0),
NRF_SAADC_RESOLUTION_12BIT,
NRF_SAADC_OVERSAMPLE_DISABLED,
NULL);
if (err != NRFX_SUCCESS) {
printk("nrfx_saadc_simple_mode_set error: %08x", err);
return;
}
C5.5 Set buffer where the sample will be stored. Since the sample interval is quite long and we only sample one channel, a buffer of one sample is sufficient:
err = nrfx_saadc_buffer_set(&sample, 1);
if (err != NRFX_SUCCESS) {
printk("nrfx_saadc_buffer_set error: %08x", err);
return;
}
C6. Start the periodic timer for battery sampling at the interval given by BATTERY_SAMPLE_INTERVAL_MS
.
Add the following line to the end of the function configure_saadc()
:
k_timer_start(&battery_sample_timer, K_NO_WAIT, K_MSEC(BATTERY_SAMPLE_INTERVAL_MS));
C7. We will now implement the timer callback handler where sampling is triggered and results are printed.
7.1 Add the empty function structure to the main.c
file
void battery_sample_timer_handler(struct k_timer *timer)
{
/* Step 7.2 - Trigger the sampling */
/* STEP 7.3 - Calculate and print voltage */
}
C7.2 Trigger the sampling. The SAADC driver was previously configured in blocking mode, so the sample will be ready when the function returns:
nrfx_err_t err = nrfx_saadc_mode_trigger();
if (err != NRFX_SUCCESS) {
printk("nrfx_saadc_mode_trigger error: %08x", err);
return;
}
C7.3 Calculate the battery voltage from the sample and print it on console. The formula is based on the Digital output formula in nRF52/nRF53/nRF91 SAADC peripheral chapter and converted to millivolts:
int battery_voltage = ((600*6) * sample) / ((1<<12));
printk("SAADC sample: %d\n", sample);
printk("Battery Voltage: %d mV\n", battery_voltage);
CTesting
8. Build the application and flash it to your board.
9. Connect your analog input to a voltage source, just as you did in exercise 1.
This could be a dedicated power supply, a PPK2, a battery, or you can simply connect a wire between the analog input (AIN0) and VDD as shown below.
10. Using a serial terminal, you should see the below output:
*** Booting nRF Connect SDK 2.6.1-3758bcbfa5cd ***
SAADC sample: 3245
Battery Voltage: 2852 mV
SAADC sample: 3237
Battery Voltage: 2845 mV
SAADC sample: 3252
Battery Voltage: 2858 mV
TerminalThe solution for this exercise can be found in the course repository, lesson6/inter_less6_exer2_solution
.