Compare commits

...

2 Commits

Author SHA1 Message Date
Eduard Iten a9a0626913 Implement real ADC readings with VND7050AJ sensor multiplexing
- Switch from simulated to real ADC readings in adc_sensor library
- Add GPIO control for VND7050AJ sensor selection (sen, s0, s1 pins)
- Implement proper ADC device and channel setup for voltage/current measurements
- Enable ADC driver in prj.conf (CONFIG_ADC=y)
- Disable simulation mode (CONFIG_ADC_SENSOR_SIMULATED=n)
- Add devicetree bindings for custom supply voltage and motor current sensors
- Update overlay with adc_sensors nodes using PB4, PB5, PB6 pins
- Integrate real ADC readings into Modbus server registers
- Support HSE/HSI clock source toggling in overlay configuration
2025-07-08 16:50:27 +02:00
Eduard Iten b11f844415 feat: Add ADC sensor device tree bindings and configuration
Introduces device tree bindings for custom ADC voltage and current sensors,
allowing for flexible configuration of sensor inputs and associated GPIOs.
This enables proper hardware abstraction for ADC measurements.

The example overlay file
has been removed as its content is now integrated or superseded by the new
binding definitions.
2025-07-08 16:43:27 +02:00
7 changed files with 442 additions and 12 deletions

View File

@ -9,4 +9,7 @@
"app_version.h": "c"
},
"C_Cpp.clang_format_style": "file",
"nrf-connect.applications": [
"${workspaceFolder}/apps/slave_node"
],
}

View File

@ -11,6 +11,40 @@
s0-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>; // S0 (PB6) - Status/Select 0 output from VND7050AJ
s1-gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; // S1 (PB5) - Status/Select 1 output from VND7050AJ
};
adc_sensors {
compatible = "adc-sensors";
supply_voltage: supply-voltage {
compatible = "custom,supply-voltage";
io-channels = <&adc1 1>; /* ADC1 channel 1 (PA0) */
io-channel-names = "voltage";
reference-mv = <3300>;
voltage-divider-ratio = <4>; /* Adjust based on your voltage divider */
/* GPIO control pins using VND7050AJ pins */
sen-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; /* SEN (PB4) - enable sensor */
s0-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>; /* S0 (PB6) - mux select bit 0 */
s1-gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; /* S1 (PB5) - mux select bit 1 */
measurement-delay-ms = <5>; /* 5ms delay after GPIO setup */
};
motor_current: motor-current {
compatible = "custom,motor-current";
io-channels = <&adc1 1>; /* Same ADC channel, different mux setting */
io-channel-names = "current";
reference-mv = <3300>;
current-sense-resistor-mohm = <100>; /* 100mΩ sense resistor */
/* GPIO control pins using VND7050AJ pins */
sen-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; /* SEN (PB4) - enable sensor */
s0-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>; /* S0 (PB6) - mux select bit 0 */
s1-gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; /* S1 (PB5) - mux select bit 1 */
measurement-delay-ms = <10>; /* 10ms delay for current settling */
};
};
};
// Clock configuration: Uncomment the following section to use calibrated HSI instead of HSE

View File

@ -0,0 +1,48 @@
description: Custom motor current measurement with GPIO control
compatible: "custom,motor-current"
properties:
io-channels:
type: phandle-array
required: true
description: ADC channel for current measurement
io-channel-names:
type: string-array
description: Names for the ADC channels
current-sense-resistor-mohm:
type: int
required: true
description: Current sense resistor value in milliohms
amplifier-gain:
type: int
default: 1
description: Current sense amplifier gain
reference-mv:
type: int
default: 3300
description: ADC reference voltage in millivolts
sen-gpios:
type: phandle-array
required: true
description: GPIO to enable/disable the current measurement sensor
s0-gpios:
type: phandle-array
required: true
description: GPIO for multiplexer control bit 0
s1-gpios:
type: phandle-array
required: true
description: GPIO for multiplexer control bit 1
measurement-delay-ms:
type: int
default: 10
description: Delay in milliseconds after setting GPIOs before ADC measurement

View File

@ -0,0 +1,63 @@
description: Custom supply voltage measurement with GPIO control
compatible: "custom,supply-voltage"
properties:
io-channels:
type: phandle-array
required: true
description: ADC channel for voltage measurement
io-channel-names:
type: string-array
description: Names for the ADC channels
voltage-divider-ratio:
type: int
required: true
description: Voltage divider ratio for scaling
reference-mv:
type: int
default: 3300
description: ADC reference voltage in millivolts
sen-gpios:
type: phandle-array
required: true
description: GPIO to enable/disable the voltage measurement sensor
s0-gpios:
type: phandle-array
required: true
description: GPIO for multiplexer control bit 0
s1-gpios:
type: phandle-array
required: true
description: GPIO for multiplexer control bit 1
measurement-delay-ms:
type: int
default: 10
description: Delay in milliseconds after setting GPIOs before ADC measurement
sen-gpios:
type: phandle-array
required: true
description: GPIO for SEN (Sense Enable) pin
s0-gpios:
type: phandle-array
required: true
description: GPIO for S0 (Select 0) pin
s1-gpios:
type: phandle-array
required: true
description: GPIO for S1 (Select 1) pin
measurement-delay-ms:
type: int
default: 10
description: Delay in milliseconds after setting control pins before ADC reading

View File

@ -20,3 +20,7 @@ CONFIG_MODBUS=y
CONFIG_MODBUS_ROLE_SERVER=y
CONFIG_MODBUS_BUFFER_SIZE=256
# ADC Sensor Configuration - Use real ADC readings
CONFIG_ADC_SENSOR_SIMULATED=n
CONFIG_ADC=y

View File

@ -8,7 +8,7 @@ if ADC_SENSOR
config ADC_SENSOR_SIMULATED
bool "Use simulated ADC readings"
default y
default n
help
Use simulated values instead of real ADC readings.
Voltage: 12000mV, Current: 45mA

View File

@ -4,10 +4,13 @@
*
* This file contains the implementation for initializing and reading from ADC
* sensors. It currently provides simulated values for voltage and current, with
* placeholders for real hardware ADC implementation.
* placeholders for real hardware ADC implementation including GPIO control.
*/
#include <lib/adc_sensor.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
@ -17,8 +20,237 @@ LOG_MODULE_REGISTER(adc_sensor, LOG_LEVEL_INF);
#define SIMULATED_VOLTAGE_MV 12000
#define SIMULATED_CURRENT_MA 45
static bool initialized =
false; // Flag to indicate if the ADC sensor is initialized
// Devicetree node checks
#define VOLTAGE_SENSOR_NODE DT_NODELABEL(supply_voltage)
#define CURRENT_SENSOR_NODE DT_NODELABEL(motor_current)
#ifndef CONFIG_ADC_SENSOR_SIMULATED
// ADC device reference
#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE)
#define ADC_NODE DT_PHANDLE(VOLTAGE_SENSOR_NODE, io_channels)
#define ADC_CHANNEL DT_PHA(VOLTAGE_SENSOR_NODE, io_channels, input)
#define ADC_RESOLUTION 12
#define ADC_REFERENCE_MV DT_PROP(VOLTAGE_SENSOR_NODE, reference_mv)
#define VOLTAGE_DIVIDER_RATIO \
DT_PROP(VOLTAGE_SENSOR_NODE, voltage_divider_ratio)
static const struct device *adc_dev;
static struct adc_channel_cfg adc_channel_cfg = {
.gain = ADC_GAIN_1,
.reference = ADC_REF_INTERNAL,
.acquisition_time = ADC_ACQ_TIME_DEFAULT,
.channel_id = ADC_CHANNEL,
.differential = 0};
static struct adc_sequence adc_sequence = {
.channels = BIT(ADC_CHANNEL),
.buffer_size = sizeof(uint16_t),
.resolution = ADC_RESOLUTION,
};
static uint16_t adc_buffer;
#endif
#endif
static bool initialized = false;
#ifndef CONFIG_ADC_SENSOR_SIMULATED
// GPIO specs for voltage sensor (if devicetree nodes exist)
#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE)
static const struct gpio_dt_spec voltage_sen_gpio =
GPIO_DT_SPEC_GET(VOLTAGE_SENSOR_NODE, sen_gpios);
static const struct gpio_dt_spec voltage_s0_gpio =
GPIO_DT_SPEC_GET(VOLTAGE_SENSOR_NODE, s0_gpios);
static const struct gpio_dt_spec voltage_s1_gpio =
GPIO_DT_SPEC_GET(VOLTAGE_SENSOR_NODE, s1_gpios);
#endif
// GPIO specs for current sensor (if devicetree nodes exist)
#if DT_NODE_EXISTS(CURRENT_SENSOR_NODE)
static const struct gpio_dt_spec current_sen_gpio =
GPIO_DT_SPEC_GET(CURRENT_SENSOR_NODE, sen_gpios);
static const struct gpio_dt_spec current_s0_gpio =
GPIO_DT_SPEC_GET(CURRENT_SENSOR_NODE, s0_gpios);
static const struct gpio_dt_spec current_s1_gpio =
GPIO_DT_SPEC_GET(CURRENT_SENSOR_NODE, s1_gpios);
#endif
/**
* @brief Configure GPIO pins for ADC sensor control
*/
static int configure_sensor_gpios(void) {
int ret = 0;
#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE)
// Configure voltage sensor GPIOs
if (gpio_is_ready_dt(&voltage_sen_gpio)) {
ret = gpio_pin_configure_dt(&voltage_sen_gpio, GPIO_OUTPUT_INACTIVE);
if (ret < 0) {
LOG_ERR("Failed to configure voltage sen GPIO: %d", ret);
return ret;
}
}
if (gpio_is_ready_dt(&voltage_s0_gpio)) {
ret = gpio_pin_configure_dt(&voltage_s0_gpio, GPIO_OUTPUT_INACTIVE);
if (ret < 0) {
LOG_ERR("Failed to configure voltage s0 GPIO: %d", ret);
return ret;
}
}
if (gpio_is_ready_dt(&voltage_s1_gpio)) {
ret = gpio_pin_configure_dt(&voltage_s1_gpio, GPIO_OUTPUT_INACTIVE);
if (ret < 0) {
LOG_ERR("Failed to configure voltage s1 GPIO: %d", ret);
return ret;
}
}
#endif
#if DT_NODE_EXISTS(CURRENT_SENSOR_NODE)
// Configure current sensor GPIOs
if (gpio_is_ready_dt(&current_sen_gpio)) {
ret = gpio_pin_configure_dt(&current_sen_gpio, GPIO_OUTPUT_INACTIVE);
if (ret < 0) {
LOG_ERR("Failed to configure current sen GPIO: %d", ret);
return ret;
}
}
if (gpio_is_ready_dt(&current_s0_gpio)) {
ret = gpio_pin_configure_dt(&current_s0_gpio, GPIO_OUTPUT_INACTIVE);
if (ret < 0) {
LOG_ERR("Failed to configure current s0 GPIO: %d", ret);
return ret;
}
}
if (gpio_is_ready_dt(&current_s1_gpio)) {
ret = gpio_pin_configure_dt(&current_s1_gpio, GPIO_OUTPUT_INACTIVE);
if (ret < 0) {
LOG_ERR("Failed to configure current s1 GPIO: %d", ret);
return ret;
}
}
#endif
return 0;
}
/**
* @brief Set GPIO pins for voltage measurement
* @param s0_state State for S0 pin (multiplexer bit 0)
* @param s1_state State for S1 pin (multiplexer bit 1)
*/
static int set_voltage_sensor_gpios(bool enable, bool s0_state, bool s1_state) {
#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE)
if (gpio_is_ready_dt(&voltage_sen_gpio)) {
gpio_pin_set_dt(&voltage_sen_gpio, enable ? 1 : 0);
}
if (gpio_is_ready_dt(&voltage_s0_gpio)) {
gpio_pin_set_dt(&voltage_s0_gpio, s0_state ? 1 : 0);
}
if (gpio_is_ready_dt(&voltage_s1_gpio)) {
gpio_pin_set_dt(&voltage_s1_gpio, s1_state ? 1 : 0);
}
// Delay for GPIO settling (from devicetree or default)
#if DT_NODE_HAS_PROP(VOLTAGE_SENSOR_NODE, measurement_delay_ms)
k_msleep(DT_PROP(VOLTAGE_SENSOR_NODE, measurement_delay_ms));
#else
k_msleep(5); // Default 5ms delay
#endif
#endif
return 0;
}
/**
* @brief Set GPIO pins for current measurement
* @param s0_state State for S0 pin (multiplexer bit 0)
* @param s1_state State for S1 pin (multiplexer bit 1)
*/
static int set_current_sensor_gpios(bool enable, bool s0_state, bool s1_state) {
#if DT_NODE_EXISTS(CURRENT_SENSOR_NODE)
if (gpio_is_ready_dt(&current_sen_gpio)) {
gpio_pin_set_dt(&current_sen_gpio, enable ? 1 : 0);
}
if (gpio_is_ready_dt(&current_s0_gpio)) {
gpio_pin_set_dt(&current_s0_gpio, s0_state ? 1 : 0);
}
if (gpio_is_ready_dt(&current_s1_gpio)) {
gpio_pin_set_dt(&current_s1_gpio, s1_state ? 1 : 0);
}
// Delay for GPIO settling (from devicetree or default)
#if DT_NODE_HAS_PROP(CURRENT_SENSOR_NODE, measurement_delay_ms)
k_msleep(DT_PROP(CURRENT_SENSOR_NODE, measurement_delay_ms));
#else
k_msleep(10); // Default 10ms delay
#endif
#endif
return 0;
}
#endif /* !CONFIG_ADC_SENSOR_SIMULATED */
#ifndef CONFIG_ADC_SENSOR_SIMULATED
/**
* @brief Read ADC value and convert to millivolts
* @return ADC reading in millivolts, or 0 on error
*/
static uint16_t read_adc_voltage_mv(void) {
#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE)
int ret = adc_read(adc_dev, &adc_sequence);
if (ret < 0) {
LOG_ERR("ADC read failed: %d", ret);
return 0;
}
// Convert ADC reading to millivolts
// ADC reading is 12-bit (0-4095) representing 0 to ADC_REFERENCE_MV
uint32_t adc_value = adc_buffer;
uint32_t voltage_mv = (adc_value * ADC_REFERENCE_MV) / 4095;
// Apply voltage divider scaling
voltage_mv *= VOLTAGE_DIVIDER_RATIO;
LOG_DBG("ADC raw: %u, voltage: %u mV", adc_value, (uint16_t)voltage_mv);
return (uint16_t)voltage_mv;
#else
return 0;
#endif
}
/**
* @brief Read ADC value and convert to milliamps (for current sensor)
* @return ADC reading in milliamps, or 0 on error
*/
static uint16_t read_adc_current_ma(void) {
#if DT_NODE_EXISTS(CURRENT_SENSOR_NODE)
int ret = adc_read(adc_dev, &adc_sequence);
if (ret < 0) {
LOG_ERR("ADC read failed: %d", ret);
return 0;
}
// Convert ADC reading to millivolts first
uint32_t adc_value = adc_buffer;
uint32_t voltage_mv = (adc_value * ADC_REFERENCE_MV) / 4095;
// Convert voltage to current based on current sensor characteristics
// Assuming a linear current sensor with specific mV/mA ratio
// This will need to be calibrated for your specific current sensor
uint32_t current_ma = voltage_mv / 10; // Example: 10mV per mA
LOG_DBG("ADC raw: %u, current: %u mA", adc_value, (uint16_t)current_ma);
return (uint16_t)current_ma;
#else
return 0;
#endif
}
#endif
int adc_sensor_init(void) {
if (initialized) {
@ -30,8 +262,40 @@ int adc_sensor_init(void) {
LOG_INF("Simulated values: %dmV, %dmA", SIMULATED_VOLTAGE_MV,
SIMULATED_CURRENT_MA);
#else
// TODO: Initialize real ADC hardware
LOG_INF("ADC sensor initialized (real ADC mode - not yet implemented)");
// Initialize GPIO pins for sensor control
int ret = configure_sensor_gpios();
if (ret < 0) {
LOG_ERR("Failed to configure sensor GPIOs: %d", ret);
return ret;
}
// Initialize ADC hardware
#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE)
adc_dev = DEVICE_DT_GET(ADC_NODE);
if (!device_is_ready(adc_dev)) {
LOG_ERR("ADC device not ready");
return -ENODEV;
}
adc_sequence.buffer = &adc_buffer;
ret = adc_channel_setup(adc_dev, &adc_channel_cfg);
if (ret < 0) {
LOG_ERR("Failed to setup ADC channel: %d", ret);
return ret;
}
LOG_INF("ADC device ready: %s", adc_dev->name);
#endif
LOG_INF("ADC sensor initialized (real ADC mode with GPIO control)");
#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE)
LOG_INF("Voltage sensor found in devicetree");
#endif
#if DT_NODE_EXISTS(CURRENT_SENSOR_NODE)
LOG_INF("Current sensor found in devicetree");
#endif
#endif
initialized = true;
@ -47,9 +311,16 @@ uint16_t adc_sensor_get_voltage_mv(void) {
#ifdef CONFIG_ADC_SENSOR_SIMULATED
return SIMULATED_VOLTAGE_MV;
#else
// TODO: Read real ADC value for voltage
// For now return simulated value
return SIMULATED_VOLTAGE_MV;
// Set GPIOs for voltage measurement (example: s0=0, s1=0 for channel 0)
set_voltage_sensor_gpios(true, false, false);
// Read real ADC value for voltage
uint16_t voltage = read_adc_voltage_mv();
// Disable sensor after measurement to save power
set_voltage_sensor_gpios(false, false, false);
return voltage;
#endif
}
@ -62,8 +333,15 @@ uint16_t adc_sensor_get_current_ma(void) {
#ifdef CONFIG_ADC_SENSOR_SIMULATED
return SIMULATED_CURRENT_MA;
#else
// TODO: Read real ADC value for current
// For now return simulated value
return SIMULATED_CURRENT_MA;
// Set GPIOs for current measurement (example: s0=1, s1=0 for channel 1)
set_current_sensor_gpios(true, true, false);
// Read real ADC value for current
uint16_t current = read_adc_current_ma();
// Disable sensor after measurement to save power
set_current_sensor_gpios(false, false, false);
return current;
#endif
}