If you are having issues with the exercises, please create a ticket on DevZone:
Click or drag files to this area to upload. You can upload up to 2 files.

Exercise 1 – Interfacing with ADC using Zephyr API

In this exercise, we will learn how to interact with an ADC (SAADC) on a Nordic device using the Zephyr ADC API.

Exercise steps

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 1.

Alternatively, in the GitHub repository for this course, go to the base code for this exercise, found in lesson6/inter_less6_exer1.

1. Enable the ADC API and driver in prj.conf

2. Define the ADC channel configration in devicetree

2.1 Create a devicetree overlay file for you board.

In the boards directory of the application, inter_less6_exer1/boards, rename the overlay file to the name of the build target for the board you are using, for example nrf52840dk_nrf52840.overlay.

2.2 Inside the devicetree overlay file, under the root node , create a zephyr,user node and set it’s io-channels to the ADC channel(s), you would like to use

In this exercise, we will only use one channel which is channel 0.

Add the following in your devicetree overlay file:

Note: you could specify multiple channels and have them separated by comma (Ex: io-channels = <&adc0 1>, <&adc0 3>;)

2.3 Configure the ADC channel(s)

Add the following in your devicetree overlay file (outside the root node):

The 0 after the @ in channel and reg signify that we are referencing channel 0 of the ADC.

We will populate the properties of the adc node which are described in here with all possible values for each property.

The gain zephyr,gain is set to ADC_GAIN_1_6, which means that the reading will be attenuated by (x 1/6).

The following gains are supported on the nRF SAADC: 1/6, 1/5, 1/4, 1/3, 1/2, 1, 2, 4. This is defined in the product specification.

For the reference voltage zephyr,reference, we will use the internal +0.6, which is specified by ADC_REF_INTERNAL .

For the acquisition time zephyr,acquisition-time , we will use the default value set in the hardware ADC_ACQ_TIME_DEFAULT, which equals 10us.

For the analog input zephyr,input-positive, we will use a single-ended input by not specifying an input-negative property, and we will set it to AIN0 (NRF_SAADC_AIN0). Since single-ended mode is just differential mode with the negative end internally connected to GND, noise can cause slightly negative measurements.

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. The mapping may differ from one nRF SoC to another, as demonstrated in the table below:



Do not confuse the Arduino shield analog input marking (A0-A5) printed on your DK PCB with the input of the SAADC (AIN0-AIN7), as these two are not the same entity.

Be aware that it is possible to use the SADDC to measure the internal voltage by specifying NRF_SAADC_VDD or NRF_SAADC_VDDHDIV5 in zephyr,input-positive .

For the resolution zephyr,resolution, we will select 12 bits.

For specifying multiple channels, see the ADC sample by Zephyr.

3. Retrieve the API-specific device structure for the ADC channel.

After we have defined the channel(s) we are interested in and it’s parameters (gain, reference, input mode, resolution, etc.) in the devicetree overlay, we can access it from the C code and set it up.

3.1 Include the header file of the Zephyr ADC API

#include <zephyr/drivers/adc.h>

3.2 Define a variable of type adc_dt_spec for each channel.

Since we are using only one channel in this exercise, we will use the ADC_DT_SPEC_GET() macro to get the io-channels defined at index 0.

static const struct adc_dt_spec adc_channel = ADC_DT_SPEC_GET(DT_PATH(zephyr_user));

3.3 We must validate that the ADC peripheral (SAADC) is ready before setting it up. This is done by calling adc_is_ready_dt().

if (!adc_is_ready_dt(&adc_channel)) {
	LOG_ERR("ADC controller devivce %s not ready",>name);
	return 0;

3.4 Setup the ADC channel by calling adc_channel_setup_dt(). The setup will be based on the configurations we have set in the devicetree overlay file.

err = adc_channel_setup_dt(&adc_channel);
if (err < 0) {
	LOG_ERR("Could not setup channel #%d (%d)", 0, err);
	return 0;

4. Define and initialize a sequence to store samples captured by the ADC.

4.1 Define a variable of type adc_sequence and a buffer of type int16_t to specify where the samples are to be written.

4.2 Initialize the ADC sequence

	err = adc_sequence_init_dt(&adc_channel, &sequence);
	if (err < 0) {
		LOG_ERR("Could not initalize sequnce");
		return 0;

5. Read a sample from the ADC by calling adc_read()

		err = adc_read(, &sequence);
		if (err < 0) {
			LOG_ERR("Could not read (%d)", err);

6. Convert raw value to mV by calling adc_raw_to_millivolts_dt() . This function relies on the parameters set in the devicetree overlay file.

err = adc_raw_to_millivolts_dt(&adc_channel, &val_mv);
/* conversion to mV may not be supported, skip if not */
if (err < 0) {
	        LOG_WRN(" (value in mV not available)\n");
	} else {
		LOG_INF(" = %d mV", val_mv);


7. Build and flash the application to your board.

8. Connect your analog input to a voltage source.

This could be a dedicated power supply, a PPK II kit, a battery, or you can simply connect a wire between the analog input (AIN0) and VDD as shown below.


As mentioned before, you don’t have to have external wiring to measure the VDD, you can simply set it in zephyr,input-positive = <NRF_SAADC_VDD>

9. On your serial terminal, you should see the measured voltage in mV.

You could try to connect the wire now to GND and see the measured voltage.

The small mV is due to noise. This is common for single-ended mode.

It’s worth noting that If you have a PPK II , you could use it as a variable voltage supply. Simply connect the VOUT of the PPK II to the analog input pin , connect the GND of the PPK II to your DK GND, Open the Power Profiler App in nRF Connect for Desktop, use the Source meter, and set the supply voltage to the desired value.


The analog input measured should not exceed the internal voltage of the SoC/SiP. If you need to measure an analog input higher than the internal voltage, you must have the necessary voltage step-down circuit.

Make sure that the measured analog input has the same ground as your DK.

The solution for this exercise can be found in the GitHub repository, lesson6/inter_less6_exer1_solution.

Register an account
Already have an account? Log in
(All fields are required unless specified optional)

  • 8 or more characters
  • Upper and lower case letters
  • At least one number or special character

Forgot your password?
Enter the email associated with your account, and we will send you a link to reset your password.