179 lines
6.6 KiB
C
179 lines
6.6 KiB
C
#include <zephyr/kernel.h>
|
|
#include <zephyr/settings/settings.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/drivers/gpio.h>
|
|
#include <zephyr/drivers/adc.h>
|
|
#include <lib/valve.h>
|
|
|
|
LOG_MODULE_REGISTER(valve, LOG_LEVEL_DBG);
|
|
|
|
// ADC configuration for MULTISENSE (PA0)
|
|
static const struct device *adc_dev = DEVICE_DT_GET(DT_NODELABEL(adc1));
|
|
static const struct adc_channel_cfg adc_channel_cfg = {
|
|
.gain = ADC_GAIN_1,
|
|
.reference = ADC_REF_INTERNAL,
|
|
.acquisition_time = ADC_ACQ_TIME_DEFAULT,
|
|
.channel_id = 1, // ADC1_IN1 (PA0)
|
|
};
|
|
|
|
static const struct valve_gpios valve_gpios = {
|
|
.in0 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), in0_gpios),
|
|
.in1 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), in1_gpios),
|
|
.rst = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), rst_gpios),
|
|
.sen = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), sen_gpios),
|
|
.s0 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), s0_gpios),
|
|
.s1 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), s1_gpios),
|
|
};
|
|
|
|
static enum valve_state current_state = VALVE_STATE_CLOSED;
|
|
static enum valve_movement current_movement = VALVE_MOVEMENT_IDLE;
|
|
static uint16_t max_opening_time_s = 60;
|
|
static uint16_t max_closing_time_s = 60;
|
|
static struct k_work_delayable valve_work;
|
|
|
|
static void valve_work_handler(struct k_work *work)
|
|
{
|
|
gpio_pin_set_dt(&valve_gpios.in0, 0);
|
|
gpio_pin_set_dt(&valve_gpios.in1, 0);
|
|
gpio_pin_set_dt(&valve_gpios.rst, 0);
|
|
|
|
if (current_movement == VALVE_MOVEMENT_OPENING) {
|
|
LOG_INF("Valve finished opening");
|
|
} else if (current_movement == VALVE_MOVEMENT_CLOSING) {
|
|
current_state = VALVE_STATE_CLOSED;
|
|
LOG_INF("Valve finished closing");
|
|
}
|
|
current_movement = VALVE_MOVEMENT_IDLE;
|
|
}
|
|
|
|
void valve_init(void)
|
|
{
|
|
k_work_init_delayable(&valve_work, valve_work_handler);
|
|
settings_load_one("valve/max_open_time", &max_opening_time_s, sizeof(max_opening_time_s));
|
|
settings_load_one("valve/max_close_time", &max_closing_time_s, sizeof(max_closing_time_s));
|
|
|
|
// Initialize ADC for MULTISENSE
|
|
if (!device_is_ready(adc_dev)) {
|
|
LOG_ERR("ADC device not ready");
|
|
return;
|
|
}
|
|
|
|
int ret = adc_channel_setup(adc_dev, &adc_channel_cfg);
|
|
if (ret < 0) {
|
|
LOG_ERR("Could not setup ADC channel (%d)", ret);
|
|
return;
|
|
}
|
|
|
|
gpio_pin_configure_dt(&valve_gpios.in0, GPIO_OUTPUT_INACTIVE); // IN0 control pin - output, deactivate
|
|
gpio_pin_configure_dt(&valve_gpios.in1, GPIO_OUTPUT_INACTIVE); // IN1 control pin - output, deactivate
|
|
gpio_pin_configure_dt(&valve_gpios.rst, GPIO_OUTPUT_INACTIVE); // Keep VND7050AJ in reset
|
|
gpio_pin_configure_dt(&valve_gpios.sen, GPIO_OUTPUT_INACTIVE); // Sensor enable pin - output, inactive
|
|
// S0 and S1 pins are used for selecting the valve state, they are initially inactive
|
|
// and will be set to active when the valve is opened or closed.
|
|
gpio_pin_configure_dt(&valve_gpios.s0, GPIO_OUTPUT_INACTIVE); // S0 select pin - output
|
|
gpio_pin_configure_dt(&valve_gpios.s1, GPIO_OUTPUT_INACTIVE); // S1 select pin - output
|
|
|
|
LOG_INF("Valve initialized: max_open=%us, max_close=%us", max_opening_time_s, max_closing_time_s);
|
|
}
|
|
|
|
void valve_open(void)
|
|
{
|
|
if (current_state == VALVE_STATE_CLOSED) {
|
|
gpio_pin_set_dt(&valve_gpios.rst, 1);
|
|
gpio_pin_set_dt(&valve_gpios.in1, 0);
|
|
gpio_pin_set_dt(&valve_gpios.in0, 1);
|
|
current_state = VALVE_STATE_OPEN;
|
|
current_movement = VALVE_MOVEMENT_OPENING;
|
|
k_work_schedule(&valve_work, K_MSEC(max_opening_time_s * 1000 * 0.9));
|
|
}
|
|
}
|
|
|
|
void valve_close(void)
|
|
{
|
|
if (current_state == VALVE_STATE_OPEN) {
|
|
gpio_pin_set_dt(&valve_gpios.rst, 1);
|
|
gpio_pin_set_dt(&valve_gpios.in0, 0);
|
|
gpio_pin_set_dt(&valve_gpios.in1, 1);
|
|
current_movement = VALVE_MOVEMENT_CLOSING;
|
|
k_work_schedule(&valve_work, K_MSEC(max_closing_time_s * 1000 * 0.9));
|
|
}
|
|
}
|
|
|
|
void valve_stop(void)
|
|
{
|
|
k_work_cancel_delayable(&valve_work);
|
|
current_movement = VALVE_MOVEMENT_IDLE;
|
|
}
|
|
|
|
enum valve_state valve_get_state(void) { return current_state; }
|
|
enum valve_movement valve_get_movement(void) { return current_movement; }
|
|
uint16_t valve_get_motor_current(void) { return (current_movement != VALVE_MOVEMENT_IDLE) ? 150 : 10; }
|
|
|
|
uint16_t valve_get_supply_voltage(void)
|
|
{
|
|
LOG_DBG("Starting supply voltage measurement");
|
|
|
|
// Ensure VND7050AJ is enabled (RST=HIGH)
|
|
LOG_DBG("Enabling VND7050AJ (RST=1)");
|
|
gpio_pin_set_dt(&valve_gpios.rst, 1);
|
|
|
|
// Wait for VND7050AJ to power up and stabilize
|
|
k_msleep(50);
|
|
|
|
int16_t buf;
|
|
struct adc_sequence sequence = {
|
|
.buffer = &buf,
|
|
.buffer_size = sizeof(buf),
|
|
.channels = BIT(adc_channel_cfg.channel_id),
|
|
.resolution = 12,
|
|
};
|
|
|
|
// Configure VND7050AJ to output supply voltage on MULTISENSE
|
|
// According to VND7050AJ datasheet page 20:
|
|
// S0=1, S1=1: Supply voltage sensing mode
|
|
LOG_DBG("Setting S0=1, S1=1 for supply voltage sensing");
|
|
gpio_pin_set_dt(&valve_gpios.s0, 1);
|
|
gpio_pin_set_dt(&valve_gpios.s1, 1);
|
|
|
|
// Enable sensing
|
|
LOG_DBG("Enabling MULTISENSE (SEN=1)");
|
|
gpio_pin_set_dt(&valve_gpios.sen, 1);
|
|
|
|
// Wait for voltage to stabilize
|
|
k_msleep(10);
|
|
|
|
// Read ADC value
|
|
LOG_DBG("Reading ADC channel %d", adc_channel_cfg.channel_id);
|
|
int ret = adc_read(adc_dev, &sequence);
|
|
if (ret < 0) {
|
|
LOG_ERR("Could not read ADC (%d)", ret);
|
|
gpio_pin_set_dt(&valve_gpios.sen, 0);
|
|
return 0;
|
|
}
|
|
|
|
// Disable sensing to save power
|
|
LOG_DBG("Disabling MULTISENSE (SEN=0)");
|
|
gpio_pin_set_dt(&valve_gpios.sen, 0);
|
|
|
|
// Convert ADC value to millivolts
|
|
// VDD = 3.3V, ADC resolution = 12-bit (4096 steps)
|
|
// ADC voltage = (buf / 4096) * 3300 mV
|
|
int32_t val_mv = ((int32_t)buf * 3300) / 4096;
|
|
|
|
// VND7050AJ MULTISENSE voltage divider:
|
|
// According to datasheet page 35, MULTISENSE = VCC / 8 (8:1 voltage divider)
|
|
// So actual supply voltage = MULTISENSE * 8
|
|
uint16_t supply_voltage_mv = (uint16_t)(val_mv * 8);
|
|
|
|
LOG_INF("Supply voltage: %u mV (ADC raw: %d, ADC mV: %d)",
|
|
supply_voltage_mv, buf, (int)val_mv);
|
|
|
|
return supply_voltage_mv;
|
|
}
|
|
|
|
void valve_set_max_open_time(uint16_t seconds) { max_opening_time_s = seconds; settings_save_one("valve/max_open_time", &max_opening_time_s, sizeof(max_opening_time_s)); }
|
|
void valve_set_max_close_time(uint16_t seconds) { max_closing_time_s = seconds; settings_save_one("valve/max_close_time", &max_closing_time_s, sizeof(max_closing_time_s)); }
|
|
uint16_t valve_get_max_open_time(void) { return max_opening_time_s; }
|
|
uint16_t valve_get_max_close_time(void) { return max_closing_time_s; }
|