Hi,
I took a look a your battery.c file. However, I found it had too much overhead code that does not apply to me (I'm using the nRF52 DK nrf52832 and the nRF Connect SDK).
So, I came up with the following code:
battery.c
/** * @file battery.c * @author Ernesto Gonzales * @brief Battery Measurement Module (using nRF52 ADC) * @version 0.1 * @date 2023-02-16 * * @copyright Copyright (c) 2023 * */ #include <math.h> #include <stdio.h> #include <stdlib.h> #include <zephyr/kernel.h> #include <zephyr/init.h> #include <zephyr/drivers/gpio.h> #include <zephyr/drivers/adc.h> #include <zephyr/drivers/sensor.h> #include <zephyr/logging/log.h> #include <zephyr/device.h> #include <zephyr/devicetree.h> #include "battery.h" #define LOG_LEVEL CONFIG_BT_BAS_LOG_LEVEL LOG_MODULE_REGISTER(BATTERY,CONFIG_ADC_LOG_LEVEL); #define ZEPHYR_USER DT_PATH(zephyr_user) //Since the ADC was set with a devicetree overlay, we can call the settings with: //static const struct adc_channel_cfg ch4_cfg = ADC_CHANNEL_CFG_DT(DT_CHILD(DT_NODELABEL(adc),channel_4)); static const struct adc_dt_spec adc_ch4 = ADC_DT_SPEC_GET_BY_IDX(ZEPHYR_USER,0); /* Initializes the adc_ch4 to: { .dev = DEVICE_DT_GET(DT_NODELABEL(adc)), .channel_id = 4, .channel_cfg_dt_node_exists = true, .channel_cfg = { .channel_id = 4, .gain = ADC_GAIN_1_5, .reference = ADC_REF_INTERNAL, .acquisition_time = ADC_ACQ_TIME_DEFAULT, }, .vref_mv = 1200, .resolution = 12, .oversampling = 1, } */ bool battery_ok = false; struct battery_measurement_periphs { const struct adc_dt_spec *adc_ch4; struct adc_sequence *adc_seq; uint16_t raw_data; }; // Structure only for this file, not to be shared outside static struct battery_measurement_periphs batt_meas = { .adc_ch4 = &adc_ch4, .adc_seq = &(struct adc_sequence) { .channels = BIT(4), .buffer = &(batt_meas.raw_data), .buffer_size = sizeof(batt_meas.raw_data), .calibrate = true, }, }; static int battery_meas_setup(const struct device *arg){ const struct battery_measurement_periphs *batt_meas_setup = &batt_meas; int success; /* Check wether device is ready, first. */ if(!device_is_ready(batt_meas_setup->adc_ch4->dev)){ LOG_INF("ADC device is NOT ready!"); return -ENOENT; } // 0 = success success = adc_channel_setup_dt(batt_meas_setup->adc_ch4); if(success != 0){ LOG_INF("Could NOT setup the ADC channel!"); return -ENOENT; } battery_ok = true; LOG_INF("Battery measurement setup correctly!"); return success; } //System drivers: Use SYS_INIT() when you need to run a device's function at boot. SYS_INIT(battery_meas_setup,APPLICATION,CONFIG_APPLICATION_INIT_PRIORITY); int battery_sample(void){ int success = -ENOENT; int32_t val; if(battery_ok){ const struct battery_measurement_periphs *batt_setup = &batt_meas; struct adc_sequence *adc_seq = batt_setup->adc_seq; success = adc_read(batt_setup->adc_ch4->dev, adc_seq); adc_seq->calibrate = false; if (success == 0){ val = batt_setup->raw_data; adc_raw_to_millivolts_dt(batt_setup->adc_ch4, &val); LOG_INF("Battery read (mV) = %d",val); return (int)(val/(batt_setup->adc_ch4->vref_mv)); } else { LOG_INF("Error getting ADC reading."); return success; } } else { LOG_INF("Battery not ok!"); return success; } }
and battery.h only shows the battery_sample() function:
/** * @file battery.h * @author Ernesto Gonzales * @brief Battery Measurement Module (using nRF52 ADC) * @version 0.1 * @date 2023-02-16 * * @copyright Copyright (c) 2023 * */ #ifndef APPLICATION_BATTERY_H #define APPLICATION_BATTERY_H /** * @brief Takes sample measurement from the battery connected to the relevant GPIO * * @return an integer representing the battery capacity in percentage (0-100) */ int battery_sample(void); #endif
I also implemented a simple bas and in the main.c I'm using the following function in an infinite loop to measure battery:
static void bas_notify(void) { uint8_t batt_level = battery_sample(); if (batt_level > 100) { LOG_INF("Battery level measured is higher than 100 percent"); } bt_bas_set_battery_level(batt_level); }
As you can see from battery.c, I'm obtaining the ADC initialization structure from a Devicetree overlay as follows:
/ { zephyr,user { io-channels = <&adc 4>; }; }; &adc { #address-cells = <1>; #size-cells = <0>; channel@4{ reg = <4>; zephyr,gain = "ADC_GAIN_1_5"; zephyr,reference = "ADC_REF_INTERNAL"; zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20)>; zephyr,input-positive = <NRF_SAADC_AIN4>; zephyr,resolution = <12>; zephyr,vref-mv=<1200>; }; };
However, I'm getting a failure using the ADC read function, as it's not returning 0.
On top of that I get the following message: "adc_nrfx_saadc: set_resolution: ADC resolution value 0 is not valid"
How can I solve this? I thought the devicetree should've taken care of initializing with the right resolution.