Nordic nRF54L15-DK Zephyr SAADC in Linux

Please refer to the previous article Nordic nRF54L15-DK Zephyr Linux Installation Steps to install the nRF54L15-DK Nordic Zephyr Dev installation needed to complete this. The goal of this article is to describe how to interface to the Nordic nRF54L15-DK SAADC. In order to do this please create the following files in a project foder, create the prj.conf

#
# Copyright (c) 2024 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

CONFIG_LOG=y
# STEP 1 - Enable the ADC API and driver
CONFIG_ADC=y

The main.c is the following,

/*
 * Copyright (c) 2024 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/logging/log.h>
/* STEP 3.1 - Include the header file of the Zephyr ADC API */
#include <zephyr/drivers/adc.h>

/* STEP 3.2 - Define a variable of type adc_dt_spec for each channel */
static const struct adc_dt_spec adc_channel = ADC_DT_SPEC_GET(DT_PATH(zephyr_user));

LOG_MODULE_REGISTER(Output, LOG_LEVEL_DBG);

int main(void)
{
	int err;
	uint32_t count = 0;

	/* STEP 4.1 - Define a variable of type adc_sequence and a buffer of type uint16_t */
	int16_t buf;
	struct adc_sequence sequence = {
		.buffer = &buf,
		/* buffer size in bytes, not number of samples */
		.buffer_size = sizeof(buf),
		// Optional
		//.calibrate = true,
	};

	/* STEP 3.3 - validate that the ADC peripheral (SAADC) is ready */
	if (!adc_is_ready_dt(&adc_channel)) {
		LOG_ERR("ADC controller devivce %s not ready", adc_channel.dev->name);
		return 0;
	}
	/* STEP 3.4 - Setup the ADC channel */
	err = adc_channel_setup_dt(&adc_channel);
	if (err < 0) {
		LOG_ERR("Could not setup channel #%d (%d)", 0, err);
		return 0;
	}
	/* STEP 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;
	}

	while (1) {
		int val_mv;

		/* STEP 5 - Read a sample from the ADC */
		err = adc_read(adc_channel.dev, &sequence);
		if (err < 0) {
			LOG_ERR("Could not read (%d)", err);
			continue;
		}

		val_mv = (int)buf;
		LOG_INF("ADC reading[%u]: %s, channel %d: Raw: %d", count++, adc_channel.dev->name,
			adc_channel.channel_id, val_mv);

		/* STEP 6 - Convert raw value to mV*/
		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);
		}

		k_sleep(K_MSEC(1000));
	}
	return 0;
}

Finally create the nrf54l15dk_nrf54l15.overlay overlay file is as follows,

/ {
	zephyr,user {
		io-channels = <&adc 0>;
	};
};

&adc {
	#address-cells = <1>;
	#size-cells = <0>;
	status = "okay";
	channel@0 {
		reg = <0>;
		zephyr,gain = "ADC_GAIN_1_4";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
		zephyr,input-positive = <NRF_SAADC_AIN4>; /* P1.11 for the nRF54L15 DK */
		zephyr,resolution = <14>;
	};
};

The resulting directory structure in your project folder is shown below,

├── CMakeLists.txt
├── nrf54l15dk_nrf54l15.overlay
├── prj.conf
└── src
    └── main.c

Now proceed to build this Zephyr RTOS project as follows in the python virtual environment,

digikey_coffee_cup (venv) $  west build -p always -b nrf54l15dk/nrf54l15/cpuapp -- -DEXTRA_DTC_OVERLAY_FILE=nrf54l15dk_nrf54l15.overlay

Finally connect the Nordic Development kit nRF54L15-DK via the USB interface to flash it,

digikey_coffee_cup (venv) $  west flash
-- west flash: rebuilding
ninja: no work to do.
-- west flash: using runner nrfutil
-- runners.nrfutil: reset after flashing requested
Using board 001057777221
-- runners.nrfutil: Flashing file: /home/engineer/Digikey/Projects/Workspace/zephyr/zephyrproject/zephyr/adc/build/zephyr/zephyr.hex
-- runners.nrfutil: Connecting to probe
-- runners.nrfutil: Programming image
-- runners.nrfutil: Verifying image
-- runners.nrfutil: Reset
-- runners.nrfutil: Board(s) with serial number(s) 1057777221 flashed successfully.

To see the results of this project open a minicom terminal as follows,

digikey_coffee_cup $ minicom -D /dev/ttyACM1

Welcome to minicom 2.9

OPTIONS: I18n 
Port /dev/ttyACM1, 19:28:23

Press CTRL-A Z for help on special keys

[00:04:54.044,706] <inf> Output: ADC reading[294]: adc@d5000, channel 0: Raw: 1056
[00:04:54.044,714] <inf> Output:  = 232 mV
[00:04:55.044,857] <inf> Output: ADC reading[295]: adc@d5000, channel 0: Raw: 1056
[00:04:55.044,869] <inf> Output:  = 232 mV
[00:04:56.044,994] <inf> Output: ADC reading[296]: adc@d5000, channel 0: Raw: 640
[00:04:56.045,002] <inf> Output:  = 140 mV
[00:04:57.045,145] <inf> Output: ADC reading[297]: adc@d5000, channel 0: Raw: 3840
[00:04:57.045,157] <inf> Output:  = 843 mV
[00:04:58.045,282] <inf> Output: ADC reading[298]: adc@d5000, channel 0: Raw: 4928
[00:04:58.045,290] <inf> Output:  = 1082 mV
[00:04:59.045,433] <inf> Output: ADC reading[299]: adc@d5000, channel 0: Raw: 8256
[00:04:59.045,445] <inf> Output:  = 1814 mV
[00:05:00.045,570] <inf> Output: ADC reading[300]: adc@d5000, channel 0: Raw: 8240
[00:05:00.045,578] <inf>Output:  = 1810 mV

The previous sequence shows that when the nRF54L15-DK SAADC was hooked up to VDIO is 1.8V, an open wire and the “ground” terminal, this shows is working for these conditions. The nRF54L15-DK available at DigiKey

image

is an excellent development platform for many demanding IoT applications.

Have a great day!

This article is also available in spanish here.

Este artículo esta disponible en español aquí.

1 Like