ADC On Nrf5340DK, Modifying the Battery Voltage Measurement

Hi, I have been trying to do a simple analogRead using the nrf5340 using the adc, the closest working example I found was Battery Voltage Measurement working with SDK 2.4 and Zephyr 3.2.

The example is working perfectly fine, but I am trying to modify it so I can read the voltage of an outside battery, a Classic AnalogRead on a pin. 

I am sure it's a simple modification, but I couldn't figure it out Sweat smile, I would appreciate if you could help me figure out which configuration I need to modify to not read the power supply voltage but the one coming into the Pin from an outside source  Thank you very much.

.overlay: 

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


main.c
/*
 */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <zephyr/init.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/kernel.h>


#define ZEPHYR_USER DT_PATH(zephyr_user)
#define BATTERY_ADC_GAIN ADC_GAIN_1_6

struct io_channel_config {
	uint8_t channel;
};


struct divider_config {
	struct io_channel_config io_channel;
	struct gpio_dt_spec power_gpios;
	/* output_ohm is used as a flag value: if it is nonzero then
	 * the battery is measured through a voltage divider;
	 * otherwise it is assumed to be directly connected to Vdd.
	 */
	uint32_t output_ohm;
	uint32_t full_ohm;
};
static const struct divider_config divider_config = {.io_channel = {
		DT_IO_CHANNELS_INPUT(ZEPHYR_USER),
	},
};


struct divider_data {
	const struct device *adc;
	struct adc_channel_cfg adc_cfg;
	struct adc_sequence adc_seq;
	int16_t raw;
};


static struct divider_data divider_data = {
.adc = DEVICE_DT_GET(DT_IO_CHANNELS_CTLR(ZEPHYR_USER)),
};


static bool battery_ok;


static int divider_setup(void)
{
	const struct divider_config *cfg = &divider_config;
	const struct io_channel_config *iocp = &cfg->io_channel;
	const struct gpio_dt_spec *gcp = &cfg->power_gpios;
	struct divider_data *ddp = &divider_data;
	struct adc_sequence *asp = &ddp->adc_seq;
	struct adc_channel_cfg *accp = &ddp->adc_cfg;
	int rc;

	if (!device_is_ready(ddp->adc)) {
		printk("ADC device is not ready %s", ddp->adc->name);
		return -ENOENT;
	}

	if (gcp->port) {
		if (!device_is_ready(gcp->port)) {
			printk("%s: device not ready", gcp->port->name);
			return -ENOENT;
		}
		rc = gpio_pin_configure_dt(gcp, GPIO_OUTPUT_INACTIVE);
		if (rc != 0) {
			printk("Failed to control feed %s.%u: %d",
				gcp->port->name, gcp->pin, rc);
			return rc;
		}
	}

	*asp = (struct adc_sequence){
		.channels = BIT(0),
		.buffer = &ddp->raw,
		.buffer_size = sizeof(ddp->raw),
		.oversampling = 4,
		.calibrate = true,
	};

#ifdef CONFIG_ADC_NRFX_SAADC
	*accp = (struct adc_channel_cfg){
		.gain = BATTERY_ADC_GAIN,
		.reference = ADC_REF_INTERNAL,
		.acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40),
	};

	if (cfg->output_ohm != 0) {
		accp->input_positive = SAADC_CH_PSELP_PSELP_AnalogInput0
			+ iocp->channel;
			printk("OUTPUT != 0\n");
			//this should not be activated in my situation
	} else {
		accp->input_positive = SAADC_CH_PSELP_PSELP_VDD;
		printk("OUTPUT == 0, else\n");
	}
	asp->resolution = 14;
#else /* CONFIG_ADC_var */
#error Unsupported ADC
#endif /* CONFIG_ADC_var */

	rc = adc_channel_setup(ddp->adc, accp);
	printk("Setup AIN%u got %d \n", iocp->channel, rc);

	return rc;
}


static int battery_setup(void)
{
	int rc = divider_setup();

	battery_ok = (rc == 0);
	printk("Battery setup: %d %d \n", rc, battery_ok);
	return rc;
}

SYS_INIT(battery_setup, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); //when system start this initialise

int main(void)
{
		const struct gpio_dt_spec *gcp = &divider_config.power_gpios;
		if (gcp->port) {
		gpio_pin_set_dt(gcp, true);
		}

	while (true) {
		struct divider_data *ddp = &divider_data;
		const struct divider_config *dcp = &divider_config;
		struct adc_sequence *sp = &ddp->adc_seq;

		int batt_mV = adc_read(ddp->adc, sp);
			//printk("%d adc read;\n",batt_mV);

		sp->calibrate = false;

		if (batt_mV == 0) {
			int32_t val = ddp->raw;
			adc_raw_to_millivolts(adc_ref_internal(ddp->adc),ddp->adc_cfg.gain,sp->resolution,&val);
				batt_mV = val;
		}
		printk("%d mV;\n",batt_mV);
		k_busy_wait( ((1000U) * (1000U)));
	}
	return 0;
}

Parents
  • It's been a while but for anybody wondering the things to change was the input positive : 

    #ifdef CONFIG_ADC_NRFX_SAADC
    	*accp = (struct adc_channel_cfg){
    		.gain = ADC_GAIN_1_6,
    		.reference = ADC_REF_INTERNAL, //+-6V ,so gains should be 1/6, so that 3600mv/6 = 600mV -> 500mV = 3V
    		//https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf5340%2Fsaadc.html&anchor=concept_qh3_spp_qr
    		.acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40),
    	};
    
    		//accp->input_positive = SAADC_CH_PSELP_PSELP_VDD; //this thing connect ot the mcu power supply
    		//for our use we just want to read a pin
    		accp->input_positive = SAADC_CH_PSELP_PSELP_AnalogInput1;
    		printk("Setup as analog input\n");
    
    	//asp->resolution = 14; //for 14 its oversampling, we dont need
    	asp->resolution = 12;
    #else /* CONFIG_ADC_var */
    #error Unsupported ADC
    #endif /* CONFIG_ADC_var */
    
    	rc = adc_channel_setup(ddp->adc, accp);
    	printk("Setup AIN%u got %d", iocp->channel, rc);
    
    	return rc;
    }

Reply
  • It's been a while but for anybody wondering the things to change was the input positive : 

    #ifdef CONFIG_ADC_NRFX_SAADC
    	*accp = (struct adc_channel_cfg){
    		.gain = ADC_GAIN_1_6,
    		.reference = ADC_REF_INTERNAL, //+-6V ,so gains should be 1/6, so that 3600mv/6 = 600mV -> 500mV = 3V
    		//https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf5340%2Fsaadc.html&anchor=concept_qh3_spp_qr
    		.acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40),
    	};
    
    		//accp->input_positive = SAADC_CH_PSELP_PSELP_VDD; //this thing connect ot the mcu power supply
    		//for our use we just want to read a pin
    		accp->input_positive = SAADC_CH_PSELP_PSELP_AnalogInput1;
    		printk("Setup as analog input\n");
    
    	//asp->resolution = 14; //for 14 its oversampling, we dont need
    	asp->resolution = 12;
    #else /* CONFIG_ADC_var */
    #error Unsupported ADC
    #endif /* CONFIG_ADC_var */
    
    	rc = adc_channel_setup(ddp->adc, accp);
    	printk("Setup AIN%u got %d", iocp->channel, rc);
    
    	return rc;
    }

Children
No Data
Related