Compare commits

..

No commits in common. "d48281436eb2e886c8059110bde5c1a1fd887814" and "2c21f1f9cbdb752e1246e9478e37c214c02bc22b" have entirely different histories.

20 changed files with 241 additions and 586 deletions

View File

@ -1,8 +0,0 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(ADC)
target_sources(app PRIVATE src/main.c)

View File

@ -1,62 +0,0 @@
.. zephyr:code-sample:: adc_dt
:name: Analog-to-Digital Converter (ADC) with devicetree
:relevant-api: adc_interface
Read analog inputs from ADC channels.
Overview
********
This sample demonstrates how to use the :ref:`ADC driver API <adc_api>`.
Depending on the target board, it reads ADC samples from one or more channels
and prints the readings on the console. If voltage of the used reference can
be obtained, the raw readings are converted to millivolts.
The pins of the ADC channels are board-specific. Please refer to the board
or MCU datasheet for further details.
Building and Running
********************
The ADC peripheral and pinmux is configured in the board's ``.dts`` file. Make
sure that the ADC is enabled (``status = "okay";``).
In addition to that, this sample requires an ADC channel specified in the
``io-channels`` property of the ``zephyr,user`` node. This is usually done with
a devicetree overlay. The example overlay in the ``boards`` subdirectory for
the ``nucleo_l073rz`` board can be easily adjusted for other boards.
Configuration of channels (settings like gain, reference, or acquisition time)
also needs to be specified in devicetree, in ADC controller child nodes. Also
the ADC resolution and oversampling setting (if used) need to be specified
there. See :zephyr_file:`boards/nrf52840dk_nrf52840.overlay
<samples/drivers/adc/adc_dt/boards/nrf52840dk_nrf52840.overlay>` for an example of
such setup.
Building and Running for ST Nucleo L073RZ
=========================================
The sample can be built and executed for the
:zephyr:board:`nucleo_l073rz` as follows:
.. zephyr-app-commands::
:zephyr-app: samples/drivers/adc/adc_dt
:board: nucleo_l073rz
:goals: build flash
:compact:
To build for another board, change "nucleo_l073rz" above to that board's name
and provide a corresponding devicetree overlay.
Sample output
=============
You should get a similar output as below, repeated every second:
.. code-block:: console
ADC reading:
- ADC_0, channel 7: 36 = 65mV
.. note:: If the ADC is not supported, the output will be an error message.

View File

@ -1,13 +1,6 @@
/ { /{
vdd_sense: voltage-divider { zephyr,user {
compatible = "voltage-divider"; io-channels = <&adc1 1>, <&adc1 12>;
/*
* This reference must provide one argument (the channel number)
* because of the "#io-channel-cells = <1>" in the &adc1 node.
*/
io-channels = <&adc1 1>;
output-ohms = <2200>;
full-ohms = <3200>;
}; };
}; };
@ -22,17 +15,20 @@
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
/*
* This line is required by the st,stm32-adc driver binding.
* It declares that references to its channels need one extra argument.
*/
#io-channel-cells = <1>;
adc_channel_1: channel@1 { channel@1 {
reg = <1>; reg = <1>;
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>;
}; };
channel@c {
reg = <0xc>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
}; };

View File

@ -1,4 +0,0 @@
CONFIG_ADC=y
CONFIG_SENSOR=y
CONFIG_VOLTAGE_DIVIDER=y
CONFIG_LOG=y

View File

@ -1,53 +0,0 @@
sample:
name: ADC devicetree driver sample
tests:
sample.drivers.adc.adc_dt:
tags:
- adc
depends_on: adc
platform_allow:
- nucleo_l073rz
- disco_l475_iot1
- cc3220sf_launchxl
- cc3235sf_launchxl
- cy8cproto_063_ble
- stm32l496g_disco
- stm32h735g_disco
- nrf51dk/nrf51822
- nrf52840dk/nrf52840
- nrf54l15dk/nrf54l15/cpuapp
- nrf54h20dk/nrf54h20/cpuapp
- ophelia4ev/nrf54l15/cpuapp
- mec172xevb_assy6906
- gd32f350r_eval
- gd32f450i_eval
- gd32vf103v_eval
- gd32f403z_eval
- esp32_devkitc/esp32/procpu
- esp32s2_saola
- esp32c3_devkitm
- gd32l233r_eval
- lpcxpresso55s36
- mr_canhubk3
- longan_nano
- longan_nano/gd32vf103/lite
- rd_rw612_bga
- frdm_mcxn947/mcxn947/cpu0
- mcx_n9xx_evk/mcxn947/cpu0
- frdm_mcxc242
- ucans32k1sic
- xg24_rb4187c
- xg29_rb4412a
- raytac_an54l15q_db/nrf54l15/cpuapp
- frdm_mcxa166
- frdm_mcxa276
integration_platforms:
- nucleo_l073rz
- nrf52840dk/nrf52840
harness: console
timeout: 10
harness_config:
type: multi_line
regex:
- "ADC reading\\[\\d+\\]:"
- "- .+, channel \\d+: -?\\d+"

View File

@ -1,25 +0,0 @@
/*
* Copyright (c) 2022 Wolter HV <wolterhv@gmx.de>
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
zephyr,user {
io-channels = <&adc0 0>;
};
};
&adc0 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1_4";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};

View File

@ -1,25 +0,0 @@
/*
* Copyright (c) 2022 Wolter HV <wolterhv@gmx.de>
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
zephyr,user {
io-channels = <&adc0 0>;
};
};
&adc0 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1_4";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};

View File

@ -1,25 +0,0 @@
/*
* Copyright (c) 2022 Wolter HV <wolterhv@gmx.de>
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
zephyr,user {
io-channels = <&adc0 0>;
};
};
&adc0 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1_4";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};

View File

@ -1,25 +0,0 @@
/*
* Copyright (c) 2022 Wolter HV <wolterhv@gmx.de>
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
zephyr,user {
io-channels = <&adc0 0>;
};
};
&adc0 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1_4";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};

View File

@ -1,43 +1,100 @@
#include <zephyr/kernel.h> /*
* Copyright (c) 2020 Libre Solar Technologies GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <zephyr/device.h> #include <zephyr/device.h>
#include <zephyr/devicetree.h> #include <zephyr/devicetree.h>
#include <zephyr/drivers/sensor.h> #include <zephyr/drivers/adc.h>
#include <zephyr/logging/log.h> #include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/util.h>
LOG_MODULE_REGISTER(adc_dt_example, LOG_LEVEL_DBG); #if !DT_NODE_EXISTS(DT_PATH(zephyr_user)) || \
!DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels)
#error "No suitable devicetree overlay specified"
#endif
/* Get the voltage divider device */ #define DT_SPEC_AND_COMMA(node_id, prop, idx) \
#define VOLTAGE_DIVIDER_NODE DT_NODELABEL(vdd_sense) ADC_DT_SPEC_GET_BY_IDX(node_id, idx),
/* Data of ADC io-channels specified in devicetree. */
static const struct adc_dt_spec adc_channels[] = {
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels,
DT_SPEC_AND_COMMA)
};
int main(void) int main(void)
{ {
const struct device *vdd_dev = DEVICE_DT_GET(VOLTAGE_DIVIDER_NODE);
struct sensor_value val;
int err; int err;
uint32_t count = 0;
uint16_t buf;
struct adc_sequence sequence = {
.buffer = &buf,
/* buffer size in bytes, not number of samples */
.buffer_size = sizeof(buf),
};
if (!device_is_ready(vdd_dev)) { /* Configure channels individually prior to sampling. */
LOG_ERR("Voltage divider device not ready"); for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) {
if (!adc_is_ready_dt(&adc_channels[i])) {
printk("ADC controller device %s not ready\n", adc_channels[i].dev->name);
return 0; return 0;
} }
LOG_INF("Voltage divider device ready!"); err = adc_channel_setup_dt(&adc_channels[i]);
if (err < 0) {
printk("Could not setup channel #%d (%d)\n", i, err);
return 0;
}
}
#ifndef CONFIG_COVERAGE
while (1) { while (1) {
err = sensor_sample_fetch(vdd_dev); #else
for (int k = 0; k < 10; k++) {
#endif
printk("ADC reading[%u]:\n", count++);
for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) {
int32_t val_mv;
printk("- %s, channel %d: ",
adc_channels[i].dev->name,
adc_channels[i].channel_id);
(void)adc_sequence_init_dt(&adc_channels[i], &sequence);
err = adc_read_dt(&adc_channels[i], &sequence);
if (err < 0) { if (err < 0) {
LOG_ERR("Could not fetch sample (%d)", err); printk("Could not read (%d)\n", err);
k_sleep(K_MSEC(1000));
continue; continue;
} }
err = sensor_channel_get(vdd_dev, SENSOR_CHAN_VOLTAGE, &val); /*
if (err < 0) { * If using differential mode, the 16 bit value
LOG_ERR("Could not get channel (%d)", err); * in the ADC sample buffer should be a signed 2's
k_sleep(K_MSEC(1000)); * complement value.
continue; */
if (adc_channels[i].channel_cfg.differential) {
val_mv = (int32_t)((int16_t)buf);
} else {
val_mv = (int32_t)buf;
}
printk("%"PRId32, val_mv);
err = adc_raw_to_millivolts_dt(&adc_channels[i],
&val_mv);
/* conversion to mV may not be supported, skip if not */
if (err < 0) {
printk(" (value in mV not available)\n");
} else {
printk(" = %"PRId32" mV\n", val_mv);
}
} }
LOG_INF("Voltage reading: %d.%06d V", val.val1, val.val2);
k_sleep(K_MSEC(1000)); k_sleep(K_MSEC(1000));
} }

View File

@ -1,6 +0,0 @@
cmake_minimum_required(VERSION 3.20)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(adc_test)
target_sources(app PRIVATE src/main.c)

View File

@ -1,8 +1,31 @@
&adc1 { / {
pinctrl-0 = <&adc1_in1_pa0>; zephyr,user {
pinctrl-names = "default"; io-channels = <&adc1 1>;
status = "okay"; io-channel-names = "multisense";
};
};
&adc1 {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
st,adc-clock-source = "SYNC"; st,adc-clock-source = "SYNC";
st,adc-prescaler = <4>; 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)>;
};
}; };

View File

@ -1,73 +1,62 @@
/*
* Copyright (c) 2024 Your Name
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h> #include <zephyr/kernel.h>
#include <zephyr/drivers/adc.h> #include <zephyr/drivers/adc.h>
#include <zephyr/device.h> #include <zephyr/logging/log.h>
#include <zephyr/sys/printk.h>
// ADC-Knoten holen LOG_MODULE_REGISTER(adc_test, LOG_LEVEL_DBG);
static const struct device *adc_dev = DEVICE_DT_GET(DT_NODELABEL(adc1));
// Kanaldefinitionen #if !DT_NODE_EXISTS(DT_PATH(zephyr_user))
#define MY_SIGNAL_CHANNEL 1 // PA0 #error "zephyr,user node not found"
#define ADC_VREFINT_CHANNEL 18 // Intern #endif
// Puffer für ZWEI Messwerte static const struct adc_dt_spec adc_channel = ADC_DT_SPEC_GET_BY_NAME(DT_PATH(zephyr_user), multisense);
static int16_t sample_buffer[2];
void main(void) int main(void)
{ {
int err; int err;
// Die VREFINT-Spannung in mV aus dem Datenblatt deines Controllers
#define VREFINT_MV 1212
printk("*** ADC Ratiometric Measurement (Single Sequence) ***\n"); if (!device_is_ready(adc_channel.dev)) {
LOG_ERR("ADC device not found: %s", adc_channel.dev->name);
if (!device_is_ready(adc_dev)) { return 0;
printk("ADC device not ready!\n");
return;
} }
// --- Einmaliges Setup der beiden Kanäle --- err = adc_channel_setup_dt(&adc_channel);
const struct adc_channel_cfg signal_channel_cfg = { if (err < 0) {
.gain = ADC_GAIN_1, LOG_ERR("Could not setup channel #%d, error %d", adc_channel.channel_id, err);
.reference = ADC_REF_INTERNAL, return 0;
.acquisition_time = ADC_ACQ_TIME_DEFAULT, // Kurz für niederohmige Quellen }
.channel_id = MY_SIGNAL_CHANNEL,
};
const struct adc_channel_cfg vrefint_channel_cfg = {
.gain = ADC_GAIN_1,
.reference = ADC_REF_INTERNAL,
.acquisition_time = ADC_ACQ_TIME_MAX, // Lang für VREFINT
.channel_id = ADC_VREFINT_CHANNEL,
};
adc_channel_setup(adc_dev, &signal_channel_cfg);
adc_channel_setup(adc_dev, &vrefint_channel_cfg);
// --- EINE Sequenz, die BEIDE Kanäle enthält ---
const struct adc_sequence sequence = {
.channels = BIT(MY_SIGNAL_CHANNEL) | BIT(ADC_VREFINT_CHANNEL),
.buffer = sample_buffer,
.buffer_size = sizeof(sample_buffer),
.resolution = 12,
};
while (1) { while (1) {
err = adc_read(adc_dev, &sequence); int16_t buffer[1];
if (err != 0) { struct adc_sequence sequence = {
printk("ADC read failed with code %d\n", err); .channels = BIT(adc_channel.channel_id),
} else { .buffer = buffer,
// Die Ergebnisse sind in der Reihenfolge der Kanalnummern im Puffer .buffer_size = sizeof(buffer),
// Kanal 1 (MY_SIGNAL_CHANNEL) kommt vor Kanal 18 (ADC_VREFINT_CHANNEL) .resolution = adc_channel.resolution,
int16_t signal_raw = sample_buffer[0]; .calibrate = true,
int16_t vrefint_raw = sample_buffer[1]; };
// Ratiometrische Berechnung err = adc_read(adc_channel.dev, &sequence);
int32_t signal_mv = (int32_t)signal_raw * VREFINT_MV / vrefint_raw; if (err < 0) {
LOG_ERR("Could not read ADC, error %d", err);
printk("Signal: raw=%4d | VREFINT: raw=%4d | Calculated Voltage: %d mV\n", continue;
signal_raw, vrefint_raw, signal_mv);
} }
k_msleep(2000); 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;
} }

View File

@ -1,80 +0,0 @@
#include <zephyr/kernel.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/device.h>
// Definiere die Kanäle
#define ADC_VREFINT_CHANNEL 18 // Muss mit dem DTS übereinstimmen
#define MY_SIGNAL_CHANNEL 1 // Muss mit dem pinctrl im DTS übereinstimmen
// ADC Device
static const struct device *adc_dev = DEVICE_DT_GET(DT_NODELABEL(adc1));
// ADC Kanal Konfigurationen
static const struct adc_channel_cfg vrefint_channel_cfg = {
.gain = ADC_GAIN_1,
.reference = ADC_REF_INTERNAL, // Bedeutet VDDA
.acquisition_time = ADC_ACQ_TIME_MAX,
.channel_id = ADC_VREFINT_CHANNEL,
.differential = 0,
};
static const struct adc_channel_cfg signal_channel_cfg = {
.gain = ADC_GAIN_1,
.reference = ADC_REF_INTERNAL, // Bedeutet VDDA
.acquisition_time = ADC_ACQ_TIME_MAX,
.channel_id = MY_SIGNAL_CHANNEL,
.differential = 0,
};
// Puffer für die Messwerte
#define BUFFER_SIZE 1
static int16_t sample_buffer[BUFFER_SIZE];
// Sequenz für die Messungen
struct adc_sequence sequence_vrefint = {
.channels = BIT(ADC_VREFINT_CHANNEL),
.buffer = sample_buffer,
.buffer_size = sizeof(sample_buffer),
.resolution = 12, // STM32G4 hat 12-bit
};
struct adc_sequence sequence_signal = {
.channels = BIT(MY_SIGNAL_CHANNEL),
.buffer = sample_buffer,
.buffer_size = sizeof(sample_buffer),
.resolution = 12,
};
void main(void) {
if (!device_is_ready(adc_dev)) {
printk("ADC device not found\n");
return;
}
// Kanäle konfigurieren
adc_channel_setup(adc_dev, &vrefint_channel_cfg);
adc_channel_setup(adc_dev, &signal_channel_cfg);
while (1) {
// 1. VREFINT messen zur Kalibrierung
adc_read(adc_dev, &sequence_vrefint);
int16_t vrefint_raw = sample_buffer[0];
// 2. Dein eigentliches Signal messen
adc_read(adc_dev, &sequence_signal);
int16_t signal_raw = sample_buffer[0];
// 3. Spannung berechnen
// VREFINT Wert für STM32G431 bei 3.0V Vdda ist typ. 1.212V (1212 mV)
// Überprüfe den genauen Wert im Datenblatt für deinen Controller!
#define VREFINT_MV 1212
int32_t signal_mv = (int32_t)signal_raw * VREFINT_MV / vrefint_raw;
printk("VREFINT raw: %d, Signal raw: %d, Calculated Voltage: %d mV\n",
vrefint_raw, signal_raw, signal_mv);
k_msleep(1000);
}
}

View File

@ -1,38 +0,0 @@
#include <zephyr.h>
#include <drivers/adc.h>
#define PA0_PIN 0x04
#define ADC_CHANNEL 0x03
int main(void) {
int16_t adc_value = 0;
// Initialize the ADC
adc_config_t adc_config;
adc_config.mode = ADC_MODE_SINGLE_SHOT;
adc_config.channel = ADC_CHANNEL_PA0;
adc_config.sampling_rate = ADC_SAMP_RATE_1MS;
adc_config.data_rate = ADC_DATA_RATE_4MS;
adc_config.aux = ADC_AUX_ALL;
adc_config.atten = ADC_ATTEN_DB_11;
adc_config.ref = ADC_REF_INTERNAL;
adc_config.cal = ADC_CAL_ALL;
if (adc_config_data(&adc_config, &adc_context) < 0) {
zephyr_printf("Failed to configure ADC\n");
return -1;
}
// Read the analog input value
if (adc_read(&adc_context, &adc_value) < 0) {
zephyr_printf("Failed to read ADC value\n");
return -1;
}
zephyr_printf("ADC Value: %d\n", adc_value);
return 0;
}

View File

@ -25,10 +25,10 @@
&adc1 { &adc1 {
status = "okay"; status = "okay";
pinctrl-0 = <&adc1_in1_pa0 &adc1_in15_pb0>; pinctrl-0 = <&adc1_in1_pa0>;
pinctrl-names = "default"; pinctrl-names = "default";
st,adc-clock-source = "SYNC"; st,adc-clock-source = "SYNC";
st,adc-prescaler = <1>; st,adc-prescaler = <4>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
@ -36,18 +36,9 @@
reg = <1>; reg = <1>;
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_MAX>; // Use maximum acquisition time for stability
zephyr,resolution = <12>;
zephyr,vref-mv = <2048>; // STM32G431 VREFBUF at 2.048V
};
channel@15 {
reg = <15>;
zephyr,gain = "ADC_GAIN_1";
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 = <2048>; // STM32G431 VREFBUF at 2.048V zephyr,vref-mv = <3300>;
}; };
}; };
@ -56,9 +47,4 @@
adc1_in1_pa0: adc1_in1_pa0 { adc1_in1_pa0: adc1_in1_pa0 {
pinmux = <STM32_PINMUX('A', 0, ANALOG)>; // PA0 in den Analogmodus setzen pinmux = <STM32_PINMUX('A', 0, ANALOG)>; // PA0 in den Analogmodus setzen
}; };
// Pinmux für PB0 als ADC1_IN15 (Analogmodus) - for lab supply testing
adc1_in15_pb0: adc1_in15_pb0 {
pinmux = <STM32_PINMUX('B', 0, ANALOG)>; // PB0 in den Analogmodus setzen
};
}; };

View File

@ -23,12 +23,9 @@ int main(void)
return 0; return 0;
} }
// Test supply voltage reading periodically // Test supply voltage reading
while (1) {
uint16_t supply_voltage = valve_get_supply_voltage(); uint16_t supply_voltage = valve_get_supply_voltage();
LOG_INF("Supply voltage: %u mV", supply_voltage); LOG_INF("Supply voltage: %u mV", supply_voltage);
k_msleep(5000); // Read every 5 seconds
}
LOG_INF("Irrigation System Slave Node started successfully"); LOG_INF("Irrigation System Slave Node started successfully");
return 0; return 0;

View File

@ -12,10 +12,9 @@ LOG_MODULE_REGISTER(valve, LOG_LEVEL_DBG);
static const struct device *adc_dev = DEVICE_DT_GET(DT_NODELABEL(adc1)); static const struct device *adc_dev = DEVICE_DT_GET(DT_NODELABEL(adc1));
static const struct adc_channel_cfg adc_channel_cfg = { static const struct adc_channel_cfg adc_channel_cfg = {
.gain = ADC_GAIN_1, .gain = ADC_GAIN_1,
.reference = ADC_REF_INTERNAL, // STM32 only supports internal ref (1.2V) .reference = ADC_REF_INTERNAL,
.acquisition_time = ADC_ACQ_TIME_DEFAULT, // Use default acquisition time .acquisition_time = ADC_ACQ_TIME_DEFAULT,
.channel_id = 1, // ADC1_IN1 (PA0) .channel_id = 1, // ADC1_IN1 (PA0)
.differential = 0,
}; };
static const struct valve_gpios valve_gpios = { static const struct valve_gpios valve_gpios = {
@ -113,34 +112,15 @@ uint16_t valve_get_motor_current(void) { return (current_movement != VALVE_MOVEM
uint16_t valve_get_supply_voltage(void) uint16_t valve_get_supply_voltage(void)
{ {
LOG_INF("=== ADC TEST MODE - PA0 LAB SUPPLY TEST ==="); LOG_DBG("Starting supply voltage measurement");
LOG_INF("Connect lab supply to PA0. Recommended: 1.0V");
LOG_INF("Expected raw value for 1.0V: ~2007 (using 2.048V VREFBUF)");
LOG_INF("ADC range: 0-2.048V (STM32G431 VREFBUF internal reference)");
LOG_INF("");
// No VND7050AJ configuration - pure ADC test // Ensure VND7050AJ is enabled (RST=HIGH)
// Just make sure pins are in safe state LOG_DBG("Enabling VND7050AJ (RST=1)");
gpio_pin_configure_dt(&valve_gpios.rst, GPIO_OUTPUT); gpio_pin_set_dt(&valve_gpios.rst, 1);
gpio_pin_configure_dt(&valve_gpios.sen, GPIO_OUTPUT);
gpio_pin_configure_dt(&valve_gpios.s0, GPIO_OUTPUT);
gpio_pin_configure_dt(&valve_gpios.s1, GPIO_OUTPUT);
gpio_pin_configure_dt(&valve_gpios.in0, GPIO_OUTPUT);
gpio_pin_configure_dt(&valve_gpios.in1, GPIO_OUTPUT);
// Set all VND7050AJ pins LOW for safety // Wait for VND7050AJ to power up and stabilize
gpio_pin_set_dt(&valve_gpios.rst, 0); k_msleep(50);
gpio_pin_set_dt(&valve_gpios.s0, 0);
gpio_pin_set_dt(&valve_gpios.s1, 0);
gpio_pin_set_dt(&valve_gpios.sen, 0);
gpio_pin_set_dt(&valve_gpios.in0, 0);
gpio_pin_set_dt(&valve_gpios.in1, 0);
LOG_INF("VND7050AJ disabled - all pins LOW");
LOG_INF("PA0 is now isolated for lab supply testing");
k_msleep(100);
// Setup simple ADC sequence
int16_t buf; int16_t buf;
struct adc_sequence sequence = { struct adc_sequence sequence = {
.buffer = &buf, .buffer = &buf,
@ -149,65 +129,47 @@ uint16_t valve_get_supply_voltage(void)
.resolution = 12, .resolution = 12,
}; };
LOG_INF("Starting continuous ADC readings every 500ms..."); // 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);
// Continuous monitoring loop with improved stability // Enable sensing
int reading_count = 0; LOG_DBG("Enabling MULTISENSE (SEN=1)");
int32_t samples[10]; // Buffer for averaging gpio_pin_set_dt(&valve_gpios.sen, 1);
while (1) { // Wait for voltage to stabilize
// Take multiple samples and average them for stability k_msleep(10);
int valid_samples = 0;
int32_t sum = 0;
for (int i = 0; i < 10; i++) { // Read ADC value
k_msleep(50); // Longer delay between samples for stability LOG_DBG("Reading ADC channel %d", adc_channel_cfg.channel_id);
int adc_ret = adc_read(adc_dev, &sequence); int ret = adc_read(adc_dev, &sequence);
if (ret < 0) {
if (adc_ret == 0 && buf > 100) { // Filter out near-zero readings (floating input) LOG_ERR("Could not read ADC (%d)", ret);
samples[i] = buf; gpio_pin_set_dt(&valve_gpios.sen, 0);
sum += buf; return 0;
valid_samples++;
} else {
LOG_WRN("Sample %d invalid: raw=%d, ret=%d", i, buf, adc_ret);
samples[i] = 0; // Mark as invalid
}
} }
if (valid_samples > 0) { // Disable sensing to save power
// Calculate average LOG_DBG("Disabling MULTISENSE (SEN=0)");
int32_t avg_raw = sum / valid_samples; gpio_pin_set_dt(&valve_gpios.sen, 0);
// Calculate voltage using the correct VREFBUF reference (2.048V) // Convert ADC value to millivolts
int32_t pa0_mv = (avg_raw * 2048) / 4096; // Using 2.048V VREFBUF // VDD = 3.3V, ADC resolution = 12-bit (4096 steps)
// ADC voltage = (buf / 4096) * 3300 mV
int32_t val_mv = ((int32_t)buf * 3300) / 4096;
// Calculate standard deviation to show stability // VND7050AJ MULTISENSE voltage divider:
int32_t variance = 0; // According to datasheet page 35, MULTISENSE = VCC / 8 (8:1 voltage divider)
for (int i = 0; i < valid_samples; i++) { // So actual supply voltage = MULTISENSE * 8
int32_t diff = samples[i] - avg_raw; uint16_t supply_voltage_mv = (uint16_t)(val_mv * 8);
variance += diff * diff;
}
int32_t std_dev = (valid_samples > 1) ? variance / (valid_samples - 1) : 0;
// Find min/max for this sample set LOG_INF("Supply voltage: %u mV (ADC raw: %d, ADC mV: %d)",
int32_t min_raw = samples[0], max_raw = samples[0]; supply_voltage_mv, buf, (int)val_mv);
for (int i = 1; i < valid_samples; i++) {
if (samples[i] < min_raw) min_raw = samples[i];
if (samples[i] > max_raw) max_raw = samples[i];
}
LOG_INF("Reading %d: avg_raw=%d (%dmV) | range=%d-%d | std_dev=%d | samples=%d/10", return supply_voltage_mv;
reading_count, (int)avg_raw, (int)pa0_mv,
(int)min_raw, (int)max_raw, (int)std_dev, valid_samples);
} else {
LOG_ERR("Reading %d: All ADC samples failed", reading_count);
}
reading_count++;
k_msleep(400); // Wait before next reading set
}
return 0; // Never reached
} }
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)); }

View File

@ -2,13 +2,12 @@
import serial import serial
import time import time
import sys import sys
import argparse
def monitor_serial(port): def monitor_serial():
try: try:
# Open serial connection # Open serial connection
ser = serial.Serial(port, 115200, timeout=1) ser = serial.Serial('/dev/ttyACM3', 115200, timeout=1)
print(f"Connected to {port}") print("Connected to /dev/ttyACM3")
# Send reset command # Send reset command
ser.write(b'reset\n') ser.write(b'reset\n')
@ -19,7 +18,7 @@ def monitor_serial(port):
# Read output for 10 seconds # Read output for 10 seconds
start_time = time.time() start_time = time.time()
while 1: #time.time() - start_time < 10: while time.time() - start_time < 10:
if ser.in_waiting > 0: if ser.in_waiting > 0:
data = ser.read(ser.in_waiting) data = ser.read(ser.in_waiting)
try: try:
@ -37,7 +36,4 @@ def monitor_serial(port):
sys.exit(1) sys.exit(1)
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Serial monitor.') monitor_serial()
parser.add_argument('-p', '--port', help='Serial port to connect to', required=True)
args = parser.parse_args()
monitor_serial(args.port)

View File

@ -6,7 +6,7 @@ import sys
def monitor_serial_with_reset(): def monitor_serial_with_reset():
try: try:
# Open serial port # Open serial port
ser = serial.Serial('/dev/ttyACM1', 115200, timeout=1) ser = serial.Serial('/dev/ttyACM3', 115200, timeout=1)
print("Serial port opened successfully") print("Serial port opened successfully")
# Clear any existing data # Clear any existing data