Compare commits
2 Commits
45d011952f
...
2cc258e8e2
| Author | SHA1 | Date |
|---|---|---|
|
|
2cc258e8e2 | |
|
|
a77298b3a6 |
|
|
@ -0,0 +1,31 @@
|
||||||
|
/ {
|
||||||
|
zephyr,user {
|
||||||
|
io-channels = <&adc1 1>;
|
||||||
|
io-channel-names = "multisense";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
&adc1 {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
status = "okay";
|
||||||
|
st,adc-clock-source = "SYNC";
|
||||||
|
st,adc-prescaler = <4>;
|
||||||
|
pinctrl-0 = <&adc1_in1_pa0>;
|
||||||
|
pinctrl-names = "default";
|
||||||
|
|
||||||
|
channel@1 {
|
||||||
|
reg = <1>;
|
||||||
|
zephyr,gain = "ADC_GAIN_1";
|
||||||
|
zephyr,reference = "ADC_REF_INTERNAL";
|
||||||
|
zephyr,acquisition-time = <ADC_ACQ_TIME_MAX>;
|
||||||
|
zephyr,resolution = <12>;
|
||||||
|
zephyr,vref-mv = <3300>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
&pinctrl {
|
||||||
|
adc1_in1_pa0: adc1_in1_pa0 {
|
||||||
|
pinmux = <STM32_PINMUX('A', 0, ANALOG)>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
CONFIG_ADC=y
|
||||||
|
CONFIG_ADC_STM32=y
|
||||||
|
CONFIG_LOG=y
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Your Name
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/drivers/adc.h>
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(adc_test, LOG_LEVEL_DBG);
|
||||||
|
|
||||||
|
#if !DT_NODE_EXISTS(DT_PATH(zephyr_user))
|
||||||
|
#error "zephyr,user node not found"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct adc_dt_spec adc_channel = ADC_DT_SPEC_GET_BY_NAME(DT_PATH(zephyr_user), multisense);
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!device_is_ready(adc_channel.dev)) {
|
||||||
|
LOG_ERR("ADC device not found: %s", adc_channel.dev->name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = adc_channel_setup_dt(&adc_channel);
|
||||||
|
if (err < 0) {
|
||||||
|
LOG_ERR("Could not setup channel #%d, error %d", adc_channel.channel_id, err);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int16_t buffer[1];
|
||||||
|
struct adc_sequence sequence = {
|
||||||
|
.channels = BIT(adc_channel.channel_id),
|
||||||
|
.buffer = buffer,
|
||||||
|
.buffer_size = sizeof(buffer),
|
||||||
|
.resolution = adc_channel.resolution,
|
||||||
|
.calibrate = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
err = adc_read(adc_channel.dev, &sequence);
|
||||||
|
if (err < 0) {
|
||||||
|
LOG_ERR("Could not read ADC, error %d", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t millivolts = buffer[0];
|
||||||
|
err = adc_raw_to_millivolts_dt(&adc_channel, &millivolts);
|
||||||
|
if (err < 0) {
|
||||||
|
LOG_ERR("Could not convert to millivolts (%d)", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INF("ADC raw: %d, mV: %d", buffer[0], millivolts);
|
||||||
|
|
||||||
|
k_msleep(500);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -23,22 +23,22 @@
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
};
|
};
|
||||||
|
|
||||||
&adc1 { // ADC1 wird für PA0 verwendet
|
&adc1 {
|
||||||
status = "okay"; // ADC1 aktivieren
|
status = "okay";
|
||||||
pinctrl-0 = <&adc1_in1_pa0>; // Pinmux für PA0 als ADC1_IN1
|
pinctrl-0 = <&adc1_in1_pa0>;
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
st,adc-clock-source = "SYNC";
|
st,adc-clock-source = "SYNC";
|
||||||
st,adc-prescaler = <4>;
|
st,adc-prescaler = <4>;
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
|
|
||||||
// Definition des ADC-Kanals für MULTISENSE (PA0)
|
channel@1 {
|
||||||
channel@1 { // ADC1_IN1 ist Kanal 1
|
reg = <1>;
|
||||||
reg = <1>; // Kanalnummer
|
|
||||||
zephyr,gain = "ADC_GAIN_1";
|
zephyr,gain = "ADC_GAIN_1";
|
||||||
zephyr,reference = "ADC_REF_INTERNAL";
|
zephyr,reference = "ADC_REF_INTERNAL";
|
||||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||||
zephyr,resolution = <12>;
|
zephyr,resolution = <12>;
|
||||||
|
zephyr,vref-mv = <3300>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,10 @@ properties:
|
||||||
|
|
||||||
s0-gpios:
|
s0-gpios:
|
||||||
type: phandle-array
|
type: phandle-array
|
||||||
description: GPIO for status/select 0 pin
|
description: GPIO for select 0 pin
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
s1-gpios:
|
s1-gpios:
|
||||||
type: phandle-array
|
type: phandle-array
|
||||||
description: GPIO for status/select 1 pin
|
description: GPIO for select 1 pin
|
||||||
required: true
|
required: true
|
||||||
|
|
|
||||||
|
|
@ -21,3 +21,7 @@ CONFIG_MODBUS=y
|
||||||
CONFIG_MODBUS_ROLE_SERVER=y
|
CONFIG_MODBUS_ROLE_SERVER=y
|
||||||
CONFIG_MODBUS_BUFFER_SIZE=256
|
CONFIG_MODBUS_BUFFER_SIZE=256
|
||||||
|
|
||||||
|
# Enable ADC driver
|
||||||
|
CONFIG_ADC=y
|
||||||
|
CONFIG_ADC_STM32=y
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,10 @@ int main(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test supply voltage reading
|
||||||
|
uint16_t supply_voltage = valve_get_supply_voltage();
|
||||||
|
LOG_INF("Supply voltage: %u mV", supply_voltage);
|
||||||
|
|
||||||
LOG_INF("Irrigation System Slave Node started successfully");
|
LOG_INF("Irrigation System Slave Node started successfully");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ void valve_stop(void);
|
||||||
enum valve_state valve_get_state(void);
|
enum valve_state valve_get_state(void);
|
||||||
enum valve_movement valve_get_movement(void);
|
enum valve_movement valve_get_movement(void);
|
||||||
uint16_t valve_get_motor_current(void);
|
uint16_t valve_get_motor_current(void);
|
||||||
|
uint16_t valve_get_supply_voltage(void);
|
||||||
|
|
||||||
void valve_set_max_open_time(uint16_t seconds);
|
void valve_set_max_open_time(uint16_t seconds);
|
||||||
void valve_set_max_close_time(uint16_t seconds);
|
void valve_set_max_close_time(uint16_t seconds);
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,19 @@
|
||||||
#include <zephyr/logging/log.h>
|
#include <zephyr/logging/log.h>
|
||||||
#include <zephyr/device.h>
|
#include <zephyr/device.h>
|
||||||
#include <zephyr/drivers/gpio.h>
|
#include <zephyr/drivers/gpio.h>
|
||||||
|
#include <zephyr/drivers/adc.h>
|
||||||
#include <lib/valve.h>
|
#include <lib/valve.h>
|
||||||
|
|
||||||
LOG_MODULE_REGISTER(valve, LOG_LEVEL_INF);
|
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 = {
|
static const struct valve_gpios valve_gpios = {
|
||||||
.in0 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), in0_gpios),
|
.in0 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), in0_gpios),
|
||||||
|
|
@ -43,16 +53,29 @@ void valve_init(void)
|
||||||
settings_load_one("valve/max_open_time", &max_opening_time_s, sizeof(max_opening_time_s));
|
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));
|
settings_load_one("valve/max_close_time", &max_closing_time_s, sizeof(max_closing_time_s));
|
||||||
|
|
||||||
gpio_pin_configure_dt(&valve_gpios.in0, GPIO_OUTPUT_INACTIVE);
|
// Initialize ADC for MULTISENSE
|
||||||
gpio_pin_configure_dt(&valve_gpios.in1, GPIO_OUTPUT_INACTIVE);
|
if (!device_is_ready(adc_dev)) {
|
||||||
gpio_pin_configure_dt(&valve_gpios.rst, GPIO_OUTPUT_ACTIVE); // Keep VND7050AJ out of reset
|
LOG_ERR("ADC device not ready");
|
||||||
gpio_pin_configure_dt(&valve_gpios.sen, GPIO_OUTPUT_INACTIVE);
|
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.s0, GPIO_OUTPUT_INACTIVE); // S0 select pin - output
|
||||||
gpio_pin_configure_dt(&valve_gpios.s1, GPIO_OUTPUT_INACTIVE); // S1 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);
|
LOG_INF("Valve initialized: max_open=%us, max_close=%us", max_opening_time_s, max_closing_time_s);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void valve_open(void)
|
void valve_open(void)
|
||||||
{
|
{
|
||||||
|
|
@ -87,6 +110,68 @@ enum valve_state valve_get_state(void) { return current_state; }
|
||||||
enum valve_movement valve_get_movement(void) { return current_movement; }
|
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_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_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)); }
|
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_open_time(void) { return max_opening_time_s; }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import serial
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def monitor_serial():
|
||||||
|
try:
|
||||||
|
# Open serial connection
|
||||||
|
ser = serial.Serial('/dev/ttyACM3', 115200, timeout=1)
|
||||||
|
print("Connected to /dev/ttyACM3")
|
||||||
|
|
||||||
|
# Send reset command
|
||||||
|
ser.write(b'reset\n')
|
||||||
|
print("Sent reset command")
|
||||||
|
|
||||||
|
# Wait a bit and then read output
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# Read output for 10 seconds
|
||||||
|
start_time = time.time()
|
||||||
|
while time.time() - start_time < 10:
|
||||||
|
if ser.in_waiting > 0:
|
||||||
|
data = ser.read(ser.in_waiting)
|
||||||
|
try:
|
||||||
|
text = data.decode('utf-8', errors='ignore')
|
||||||
|
print(text, end='')
|
||||||
|
except:
|
||||||
|
print(f"Raw bytes: {data}")
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
ser.close()
|
||||||
|
print("\nSerial monitor closed")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
monitor_serial()
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import serial
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def monitor_serial_with_reset():
|
||||||
|
try:
|
||||||
|
# Open serial port
|
||||||
|
ser = serial.Serial('/dev/ttyACM3', 115200, timeout=1)
|
||||||
|
print("Serial port opened successfully")
|
||||||
|
|
||||||
|
# Clear any existing data
|
||||||
|
ser.flushInput()
|
||||||
|
ser.flushOutput()
|
||||||
|
|
||||||
|
# Send reset command
|
||||||
|
print("Sending reset command...")
|
||||||
|
ser.write(b"reset\n")
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
# Read output for 10 seconds
|
||||||
|
print("Reading serial output...")
|
||||||
|
start_time = time.time()
|
||||||
|
output_lines = []
|
||||||
|
|
||||||
|
while time.time() - start_time < 10:
|
||||||
|
if ser.in_waiting > 0:
|
||||||
|
try:
|
||||||
|
line = ser.readline().decode('utf-8', errors='replace').strip()
|
||||||
|
if line:
|
||||||
|
print(f"[{time.time() - start_time:.3f}s] {line}")
|
||||||
|
output_lines.append(line)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error reading line: {e}")
|
||||||
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
ser.close()
|
||||||
|
print("\nSerial monitoring complete")
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
print("\n=== SUMMARY ===")
|
||||||
|
supply_voltage_lines = [line for line in output_lines if "Supply voltage" in line]
|
||||||
|
if supply_voltage_lines:
|
||||||
|
print("Supply voltage readings:")
|
||||||
|
for line in supply_voltage_lines:
|
||||||
|
print(f" {line}")
|
||||||
|
else:
|
||||||
|
print("No supply voltage readings found")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
monitor_serial_with_reset()
|
||||||
Loading…
Reference in New Issue