Based on the DevAcademy here is my following setup using a custom NRF54L15 chip with the NRF54L15-DK devicetree with following overlay:
/ {
zephyr,user {
io-channels = <&adc 1>;
};
chosen {
zephyr,console = &uart21;
zephyr,shell-uart = &uart21;
zephyr,uart-mcumgr = &uart21;
zephyr,bt-mon-uart = &uart21;
zephyr,bt-c2h-uart = &uart21;
};
aliases {
/delete-property/ led3;
/delete-property/ sw3;
/delete-property/ sw2;
/delete-property/ sw1;
/delete-property/ sw0;
};
};
/delete-node/ &button0;
/delete-node/ &button1;
/delete-node/ &button2;
/delete-node/ &button3;
/delete-node/ &{/buttons/};
/delete-node/ &led3;
&led0 {
gpios = <&gpio1 7 0>; // B
label = "BLUE LED 0";
};
&led1 {
gpios = <&gpio1 6 0>; // G
label = "GREEN LED 1";
};
&led2 {
gpios = <&gpio1 8 0>; // R
label = "RED LED 2";
};
&adc {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
channel@1 {
reg = <1>;
zephyr,gain = "ADC_GAIN_1_6";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40)>; // Increased to 40us for your 2M Ohm divider
zephyr,input-positive = <NRF_SAADC_AIN1>;
zephyr,resolution = <12>;
};
};
&spi20 {
status = "okay";
pinctrl-0 = <&spi20_default>;
pinctrl-1 = <&spi20_sleep>;
pinctrl-names = "default", "sleep";
cs-gpios = <&gpio1 13 0>;
};
&uart20 {
status = "disabled";
};
&uart21 {
status = "okay";
current-speed = <115200>;
pinctrl-0 = <&uart21_default>;
pinctrl-1 = <&uart21_sleep>;
pinctrl-names = "default", "sleep";
};
&pinctrl {
uart21_default: uart21_default {
group1 {
psels = <NRF_PSEL(UART_TX, 1, 4)>;
};
group2 {
psels = <NRF_PSEL(UART_RX, 1, 3)>;
bias-pull-up;
};
};
uart21_sleep: uart21_sleep {
group1 {
psels =
<NRF_PSEL(UART_TX, 1, 4)>,
<NRF_PSEL(UART_RX, 1, 3)>;
low-power-enable;
};
};
spi20_default: spi20_default {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 1, 11)>,
<NRF_PSEL(SPIM_MOSI, 1, 10)>,
<NRF_PSEL(SPIM_MISO, 1, 0)>;
};
};
spi20_sleep: spi20_sleep {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 1, 11)>;
};
};
};
prj.conf:
code:
static const struct adc_dt_spec adc_channel = ADC_DT_SPEC_GET(DT_PATH(zephyr_user));
/* Divider calculation remains in C as it's math-based:
* (2M + 510k) / 510k = 4.9215...
*/
#define BATTERY_DIVIDER_FACTOR 4.9215f
int16_t sample_buffer;
int read_battery_voltage(void)
{
int err;
/* 1. Check if the device is ready */
if (!adc_is_ready_dt(&adc_channel))
{
printk("ADC device not ready\n");
return -1;
}
/* 2. Setup the channel once (from DT) */
err = adc_channel_setup_dt(&adc_channel);
if (err < 0)
{
printk("Could not setup channel (%d)\n", err);
return -1;
}
/* 3. Prepare the sequence */
struct adc_sequence sequence = {
.buffer = &sample_buffer,
.buffer_size = sizeof(sample_buffer),
// All other fields (resolution, channels) are pulled from DT by macros below
};
adc_sequence_init_dt(&adc_channel, &sequence);
/* 4. Perform the read */
err = adc_read_dt(&adc_channel, &sequence);
if (err < 0)
{
printk("ADC read failed (%d)\n", err);
return -1;
}
int32_t mv_value = sample_buffer;
/* 5. Convert raw value to mV using DT-specific helper */
err = adc_raw_to_millivolts_dt(&adc_channel, &mv_value);
if (err < 0)
{
return -1;
}
/* 6. Scale back up to actual battery voltage */
return (int)(mv_value * BATTERY_DIVIDER_FACTOR);
}
ADC pin is 1.05. I also tried to use
io-channels = <&adc 0>; with channel@1 and also with ADC_ACQ_TIME_DEFAULT but no luck either. Always get Could not setup channel (-22)
Appreciate any help.