383 lines
11 KiB
C
383 lines
11 KiB
C
/**
|
|
* @file adc_sensor.c
|
|
* @brief Implementation of the ADC sensor library.
|
|
*
|
|
* 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 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>
|
|
|
|
LOG_MODULE_REGISTER(adc_sensor, LOG_LEVEL_INF);
|
|
|
|
// Simulated values
|
|
#define SIMULATED_VOLTAGE_MV 12000
|
|
#define SIMULATED_CURRENT_MA 45
|
|
|
|
// Devicetree node checks
|
|
#define VOLTAGE_SENSOR_NODE DT_NODELABEL(supply_voltage)
|
|
#define CURRENT_OPEN_SENSOR_NODE DT_NODELABEL(motor_current_open)
|
|
#define CURRENT_CLOSE_SENSOR_NODE DT_NODELABEL(motor_current_close)
|
|
#define VND7050AJ_NODE DT_NODELABEL(vnd7050aj)
|
|
|
|
#ifndef CONFIG_ADC_SENSOR_SIMULATED
|
|
// ADC device reference from voltage sensor node (all sensors use same ADC)
|
|
#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE)
|
|
#define ADC_NODE DT_PHANDLE(VOLTAGE_SENSOR_NODE, io_channels)
|
|
#define ADC_REFERENCE_MV DT_PROP(VOLTAGE_SENSOR_NODE, reference_mv)
|
|
#endif
|
|
|
|
#define ADC_CHANNEL 1 /* ADC1 channel 1 as defined in overlay */
|
|
|
|
// Sensor-specific properties
|
|
#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE)
|
|
#define VOLTAGE_DIVIDER_RATIO \
|
|
DT_PROP(VOLTAGE_SENSOR_NODE, voltage_divider_ratio)
|
|
#define VOLTAGE_DELAY_MS DT_PROP(VOLTAGE_SENSOR_NODE, measurement_delay_ms)
|
|
#endif
|
|
|
|
#if DT_NODE_EXISTS(CURRENT_OPEN_SENSOR_NODE)
|
|
#define CURRENT_OPEN_SENSE_RESISTOR_MOHM \
|
|
DT_PROP(CURRENT_OPEN_SENSOR_NODE, current_sense_resistor_mohm)
|
|
#define CURRENT_OPEN_K_FACTOR DT_PROP(CURRENT_OPEN_SENSOR_NODE, k_factor)
|
|
#define CURRENT_OPEN_DELAY_MS \
|
|
DT_PROP(CURRENT_OPEN_SENSOR_NODE, measurement_delay_ms)
|
|
#endif
|
|
|
|
#if DT_NODE_EXISTS(CURRENT_CLOSE_SENSOR_NODE)
|
|
#define CURRENT_CLOSE_SENSE_RESISTOR_MOHM \
|
|
DT_PROP(CURRENT_CLOSE_SENSOR_NODE, current_sense_resistor_mohm)
|
|
#define CURRENT_CLOSE_K_FACTOR DT_PROP(CURRENT_CLOSE_SENSOR_NODE, k_factor)
|
|
#define CURRENT_CLOSE_DELAY_MS \
|
|
DT_PROP(CURRENT_CLOSE_SENSOR_NODE, measurement_delay_ms)
|
|
#endif
|
|
|
|
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 = 12,
|
|
};
|
|
|
|
static uint16_t adc_buffer;
|
|
#endif
|
|
|
|
static bool initialized = false;
|
|
|
|
#ifndef CONFIG_ADC_SENSOR_SIMULATED
|
|
// GPIO specs from VND7050AJ node
|
|
#if DT_NODE_EXISTS(VND7050AJ_NODE)
|
|
static const struct gpio_dt_spec sen_gpio =
|
|
GPIO_DT_SPEC_GET(VND7050AJ_NODE, sen_gpios);
|
|
static const struct gpio_dt_spec s0_gpio =
|
|
GPIO_DT_SPEC_GET(VND7050AJ_NODE, s0_gpios);
|
|
static const struct gpio_dt_spec s1_gpio =
|
|
GPIO_DT_SPEC_GET(VND7050AJ_NODE, s1_gpios);
|
|
#endif
|
|
|
|
/**
|
|
* @brief Configure GPIO pins for ADC sensor multiplexer control
|
|
*/
|
|
static int configure_sensor_gpios(void) {
|
|
int ret = 0;
|
|
|
|
#if DT_NODE_EXISTS(VND7050AJ_NODE)
|
|
// Configure sensor multiplexer GPIOs
|
|
if (gpio_is_ready_dt(&sen_gpio)) {
|
|
ret = gpio_pin_configure_dt(&sen_gpio, GPIO_OUTPUT_INACTIVE);
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to configure SEN GPIO: %d", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (gpio_is_ready_dt(&s0_gpio)) {
|
|
ret = gpio_pin_configure_dt(&s0_gpio, GPIO_OUTPUT_INACTIVE);
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to configure S0 GPIO: %d", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (gpio_is_ready_dt(&s1_gpio)) {
|
|
ret = gpio_pin_configure_dt(&s1_gpio, GPIO_OUTPUT_INACTIVE);
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to configure S1 GPIO: %d", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Set multiplexer channel for sensor selection
|
|
* @param enable Enable/disable the sensor
|
|
* @param channel Multiplexer channel (0-3)
|
|
* @param delay_ms Delay after setting GPIOs
|
|
*/
|
|
static int set_mux_channel(bool enable, uint8_t channel, uint32_t delay_ms) {
|
|
#if DT_NODE_EXISTS(VND7050AJ_NODE)
|
|
if (gpio_is_ready_dt(&sen_gpio)) {
|
|
gpio_pin_set_dt(&sen_gpio, enable ? 1 : 0);
|
|
}
|
|
if (gpio_is_ready_dt(&s0_gpio)) {
|
|
gpio_pin_set_dt(&s0_gpio, (channel & 0x01) ? 1 : 0);
|
|
}
|
|
if (gpio_is_ready_dt(&s1_gpio)) {
|
|
gpio_pin_set_dt(&s1_gpio, (channel & 0x02) ? 1 : 0);
|
|
}
|
|
|
|
// Delay for GPIO settling
|
|
if (delay_ms > 0) {
|
|
k_msleep(delay_ms);
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif /* !CONFIG_ADC_SENSOR_SIMULATED */
|
|
|
|
#ifndef CONFIG_ADC_SENSOR_SIMULATED
|
|
/**
|
|
* @brief Read ADC value and convert to millivolts (for voltage sensor)
|
|
* @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 opening current
|
|
* @return ADC reading in milliamps, or 0 on error
|
|
*/
|
|
static uint16_t read_adc_current_open_ma(void) {
|
|
#if DT_NODE_EXISTS(CURRENT_OPEN_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;
|
|
|
|
// VND7050AJ current calculation: I = V_sense * K / R_sense
|
|
// Where: V_sense in mV, K is the current sense factor, R_sense in mΩ
|
|
// Result is in milliamps
|
|
uint32_t current_ma = (voltage_mv * CURRENT_OPEN_K_FACTOR * 1000) /
|
|
CURRENT_OPEN_SENSE_RESISTOR_MOHM;
|
|
|
|
LOG_DBG("Open current - ADC raw: %u, voltage: %u mV, current: %u mA",
|
|
adc_value, voltage_mv, (uint16_t)current_ma);
|
|
|
|
return (uint16_t)current_ma;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Read ADC value and convert to milliamps for closing current
|
|
* @return ADC reading in milliamps, or 0 on error
|
|
*/
|
|
static uint16_t read_adc_current_close_ma(void) {
|
|
#if DT_NODE_EXISTS(CURRENT_CLOSE_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;
|
|
|
|
// VND7050AJ current calculation: I = V_sense * K / R_sense
|
|
// Where: V_sense in mV, K is the current sense factor, R_sense in mΩ
|
|
// Result is in milliamps
|
|
uint32_t current_ma = (voltage_mv * CURRENT_CLOSE_K_FACTOR * 1000) /
|
|
CURRENT_CLOSE_SENSE_RESISTOR_MOHM;
|
|
|
|
LOG_DBG("Close current - ADC raw: %u, voltage: %u mV, current: %u mA",
|
|
adc_value, voltage_mv, (uint16_t)current_ma);
|
|
|
|
return (uint16_t)current_ma;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
int adc_sensor_init(void) {
|
|
if (initialized) {
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_ADC_SENSOR_SIMULATED
|
|
LOG_INF("ADC sensor initialized (simulated mode)");
|
|
LOG_INF("Simulated values: %dmV, %dmA", SIMULATED_VOLTAGE_MV,
|
|
SIMULATED_CURRENT_MA);
|
|
#else
|
|
// 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)");
|
|
|
|
#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE)
|
|
LOG_INF("Voltage sensor: divider ratio %d", VOLTAGE_DIVIDER_RATIO);
|
|
#endif
|
|
#if DT_NODE_EXISTS(CURRENT_OPEN_SENSOR_NODE)
|
|
LOG_INF("Open current sensor: K-factor %d, sense resistor %d mΩ",
|
|
CURRENT_OPEN_K_FACTOR, CURRENT_OPEN_SENSE_RESISTOR_MOHM);
|
|
#endif
|
|
#if DT_NODE_EXISTS(CURRENT_CLOSE_SENSOR_NODE)
|
|
LOG_INF("Close current sensor: K-factor %d, sense resistor %d mΩ",
|
|
CURRENT_CLOSE_K_FACTOR, CURRENT_CLOSE_SENSE_RESISTOR_MOHM);
|
|
#endif
|
|
#endif
|
|
|
|
initialized = true;
|
|
return 0;
|
|
}
|
|
|
|
uint16_t adc_sensor_get_voltage_mv(void) {
|
|
if (!initialized) {
|
|
LOG_WRN("ADC sensor not initialized, calling adc_sensor_init()");
|
|
adc_sensor_init();
|
|
}
|
|
|
|
#ifdef CONFIG_ADC_SENSOR_SIMULATED
|
|
return SIMULATED_VOLTAGE_MV;
|
|
#else
|
|
// Set multiplexer to voltage channel (channel 3: VCC sense)
|
|
#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE)
|
|
set_mux_channel(true, 3, VOLTAGE_DELAY_MS);
|
|
|
|
// Read real ADC value for voltage
|
|
uint16_t voltage = read_adc_voltage_mv();
|
|
|
|
// Disable sensor after measurement to save power
|
|
set_mux_channel(false, 0, 0);
|
|
|
|
return voltage;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
uint16_t adc_sensor_get_current_ma(void) {
|
|
// Legacy function - redirect to opening current for backward compatibility
|
|
return adc_sensor_get_current_open_ma();
|
|
}
|
|
|
|
uint16_t adc_sensor_get_current_open_ma(void) {
|
|
if (!initialized) {
|
|
LOG_WRN("ADC sensor not initialized, calling adc_sensor_init()");
|
|
adc_sensor_init();
|
|
}
|
|
|
|
#ifdef CONFIG_ADC_SENSOR_SIMULATED
|
|
return SIMULATED_CURRENT_MA;
|
|
#else
|
|
// Set multiplexer to IN0 current sense channel (channel 0)
|
|
#if DT_NODE_EXISTS(CURRENT_OPEN_SENSOR_NODE)
|
|
set_mux_channel(true, 0, CURRENT_OPEN_DELAY_MS);
|
|
|
|
// Read real ADC value for current
|
|
uint16_t current = read_adc_current_open_ma();
|
|
|
|
// Disable sensor after measurement to save power
|
|
set_mux_channel(false, 0, 0);
|
|
|
|
return current;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
uint16_t adc_sensor_get_current_close_ma(void) {
|
|
if (!initialized) {
|
|
LOG_WRN("ADC sensor not initialized, calling adc_sensor_init()");
|
|
adc_sensor_init();
|
|
}
|
|
|
|
#ifdef CONFIG_ADC_SENSOR_SIMULATED
|
|
return SIMULATED_CURRENT_MA;
|
|
#else
|
|
// Set multiplexer to IN1 current sense channel (channel 1)
|
|
#if DT_NODE_EXISTS(CURRENT_CLOSE_SENSOR_NODE)
|
|
set_mux_channel(true, 1, CURRENT_CLOSE_DELAY_MS);
|
|
|
|
// Read real ADC value for current
|
|
uint16_t current = read_adc_current_close_ma();
|
|
|
|
// Disable sensor after measurement to save power
|
|
set_mux_channel(false, 0, 0);
|
|
|
|
return current;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
#endif
|
|
}
|