6 Commits

Author SHA1 Message Date
84e7d02db8 defunct: playing around with bootloaders 2025-07-08 14:11:22 +02:00
cc6b4488ee Fix MCUboot and app flash partitioning
- Corrected device tree overlays to prevent MCUboot and app overlap
- MCUboot now at 0x8000000 (32KB), app at 0x8008000 (96KB)
- Successfully boots MCUboot which chains to application
- Shell and reset command working properly
- Black Magic Probe flashing confirmed working for both domains
2025-07-07 16:04:29 +02:00
928a176e7c Step 2 complete: MCUboot integration with single-slot configuration
- Created sysbuild configuration for MCUboot bootloader
- Added device tree overlays for correct partition layout (32KB MCUboot, 96KB app)
- Fixed MCUboot partition addressing to use boot_partition instead of slot0_partition
- MCUboot successfully boots and chains to application
- Application runs with shell and custom reset command
- Disabled signature validation for testing purposes
2025-07-07 15:59:41 +02:00
8255b2a672 Add firmware_node app - Step 1: Shell with reset command
- Create new Zephyr app for firmware management
- Target: weact_stm32g431_core board
- Features: Shell interface with custom reset command
- Tested on Zephyr 4.1.99
- Flash usage: 55,400 bytes (42.27% of 128KB)
- RAM usage: 12,864 bytes (39.26% of 32KB)
- Black Magic Probe flash runner support
2025-07-07 13:49:03 +02:00
d48281436e Fix ADC devicetree compilation error for voltage divider
- Fix voltage divider devicetree configuration to reference ADC controller directly instead of channel node
- Switch from ADC API to sensor API for voltage divider usage
- Add required sensor and voltage divider configuration options
- Remove unnecessary zephyr,user node that was causing compilation issues
- The voltage divider now properly uses sensor framework and builds successfully

Hardware setup:
- Uses ADC1 channel 1 on pin PA0
- Voltage divider with 2.2kΩ output and 3.2kΩ total resistance
- Provides voltage readings through sensor API accounting for divider ratio
2025-07-07 13:36:44 +02:00
dcb73c0a25 Working DT ADC sample WITHOUT resistor divider 2025-07-07 12:32:53 +02:00
47 changed files with 1256 additions and 241 deletions

View File

@@ -8,5 +8,10 @@
// File Associations // File Associations
"files.associations": { "files.associations": {
"array": "c",
"string_view": "c",
"initializer_list": "c",
"span": "c",
"format": "c"
} }
} }

View File

@@ -0,0 +1,8 @@
# 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

@@ -0,0 +1,62 @@
.. 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,34 +1,38 @@
/{ / {
zephyr,user { vdd_sense: voltage-divider {
io-channels = <&adc1 1>, <&adc1 12>; compatible = "voltage-divider";
}; /*
* 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>;
};
}; };
&adc1 { &adc1 {
status = "okay"; status = "okay";
pinctrl-0 = <&adc1_in1_pa0>; 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>;
/*
* 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>;
channel@1 { adc_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

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

View File

@@ -0,0 +1,53 @@
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

@@ -0,0 +1,25 @@
/*
* 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

@@ -0,0 +1,25 @@
/*
* 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

@@ -0,0 +1,25 @@
/*
* 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

@@ -0,0 +1,25 @@
/*
* 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,101 +1,44 @@
/* #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/adc.h> #include <zephyr/drivers/sensor.h>
#include <zephyr/kernel.h> #include <zephyr/logging/log.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/util.h>
#if !DT_NODE_EXISTS(DT_PATH(zephyr_user)) || \ LOG_MODULE_REGISTER(adc_dt_example, LOG_LEVEL_DBG);
!DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels)
#error "No suitable devicetree overlay specified"
#endif
#define DT_SPEC_AND_COMMA(node_id, prop, idx) \ /* Get the voltage divider device */
ADC_DT_SPEC_GET_BY_IDX(node_id, idx), #define VOLTAGE_DIVIDER_NODE DT_NODELABEL(vdd_sense)
/* 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),
};
/* Configure channels individually prior to sampling. */ if (!device_is_ready(vdd_dev)) {
for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) { LOG_ERR("Voltage divider device not ready");
if (!adc_is_ready_dt(&adc_channels[i])) { return 0;
printk("ADC controller device %s not ready\n", adc_channels[i].dev->name);
return 0;
}
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 LOG_INF("Voltage divider device ready!");
while (1) { while (1) {
#else err = sensor_sample_fetch(vdd_dev);
for (int k = 0; k < 10; k++) { if (err < 0) {
#endif LOG_ERR("Could not fetch sample (%d)", err);
printk("ADC reading[%u]:\n", count++); k_sleep(K_MSEC(1000));
for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) { continue;
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) {
printk("Could not read (%d)\n", err);
continue;
}
/*
* If using differential mode, the 16 bit value
* in the ADC sample buffer should be a signed 2's
* complement value.
*/
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);
}
} }
err = sensor_channel_get(vdd_dev, SENSOR_CHAN_VOLTAGE, &val);
if (err < 0) {
LOG_ERR("Could not get channel (%d)", err);
k_sleep(K_MSEC(1000));
continue;
}
LOG_INF("Voltage reading: %d.%06d V", val.val1, val.val2);
k_sleep(K_MSEC(1000)); k_sleep(K_MSEC(1000));
} }
return 0; return 0;

View File

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

View File

@@ -0,0 +1,80 @@
#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

@@ -0,0 +1,38 @@
#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

@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.20)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(firmware_node LANGUAGES C)
zephyr_include_directories(../../include)
add_subdirectory(../../lib lib)
target_sources(app PRIVATE src/main.c)

View File

@@ -0,0 +1,34 @@
# Firmware Node Application
This Zephyr application provides firmware management capabilities for the irrigation system.
**Tested on Zephyr 4.1.99**
## Features
### Step 1: Shell with Reset Command
- Shell interface with custom "reset" command
- Warm reboot functionality
### Planned Features
- MCUboot support with partition manager
- Firmware version display
- MCUmgr support for OTA updates
## Building
```bash
west build -p auto -b weact_stm32g431_core apps/firmware_node -- -DBOARD_FLASH_RUNNER=blackmagicprobe
```
## Flashing
```bash
west flash
```
## Usage
Connect to the device via serial console and use the shell:
- `reset` - Reboot the system
- `help` - Show available commands

View File

@@ -0,0 +1,29 @@
/*
* Flash partition layout for STM32G431 (128KB total flash)
* MCUboot + single application slot configuration
*/
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
boot_partition: partition@0 {
label = "mcuboot";
reg = <0x00000000 0x0000A000>; /* 40 KB for MCUboot */
read-only;
};
slot0_partition: partition@A000 {
label = "image-0";
reg = <0x0000A000 0x00016000>; /* 88 KB for application */
};
};
};
/ {
chosen {
zephyr,code-partition = &slot0_partition;
};
};

View File

@@ -0,0 +1,2 @@
# Board specific configuration for weact_stm32g431_core
# This file can be used for board-specific overrides if needed

View File

@@ -0,0 +1,7 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "flash_partitions_128kb.dtsi"

View File

@@ -0,0 +1,18 @@
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
boot_partition: partition@0 {
label = "mcuboot";
reg = <0x00000000 0x00008000>; /* 32 KB */
read-only;
};
slot0_partition: partition@8000 {
label = "image-0";
reg = <0x00008000 0x00018000>; /* 96 KB */
};
};
};

View File

@@ -0,0 +1,25 @@
# Partition manager configuration for firmware_node
# Boot partition (MCUboot)
mcuboot_primary:
address: 0x00000000
size: 0x8000
region: flash_primary
# Application partition (primary slot)
mcuboot_primary_app:
address: 0x00008000
size: 0x18000
region: flash_primary
# Secondary slot for updates
mcuboot_secondary:
address: 0x00020000
size: 0x18000
region: flash_primary
# Settings partition
settings_partition:
address: 0x00038000
size: 0x8000
region: flash_primary

View File

@@ -0,0 +1,21 @@
# Enable Console and printk for logging
CONFIG_CONSOLE=y
CONFIG_LOG=y
CONFIG_LOG_PROCESS_THREAD=y
# Enable Shell
CONFIG_SHELL=y
CONFIG_REBOOT=y
# Enable the reset command
CONFIG_KERNEL_SHELL=y
# Enable settings for persistent storage
CONFIG_SETTINGS=y
CONFIG_SETTINGS_NVS=y
CONFIG_NVS=y
# Enable Flash and Flash Map for image trailer manipulation
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_FLASH_PAGE_LAYOUT=y

View File

@@ -0,0 +1,167 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/shell/shell.h>
#include <zephyr/sys/reboot.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/storage/flash_map.h>
#include <zephyr/devicetree.h>
LOG_MODULE_REGISTER(firmware_node, LOG_LEVEL_INF);
// Image header magic number (from MCUboot)
#define IMAGE_MAGIC 0x96f3b83d
#define IMAGE_HEADER_SIZE 32
// Function to invalidate current image and trigger serial recovery
static int invalidate_current_image(void)
{
const struct flash_area *fa;
int rc;
// Get the flash area for the current image slot (slot0_partition)
rc = flash_area_open(FIXED_PARTITION_ID(slot0_partition), &fa);
if (rc != 0) {
LOG_ERR("Failed to open flash area: %d", rc);
return rc;
}
// Ensure the flash area is valid
if (fa->fa_id != FIXED_PARTITION_ID(slot0_partition)) {
LOG_ERR("Invalid flash area ID: %d", fa->fa_id);
flash_area_close(fa);
return -EINVAL;
}
// Get the flash device associated with this area
// This is necessary to perform erase operations
const struct device *flash_dev = flash_area_get_device(fa);
if (flash_dev == NULL) {
LOG_ERR("Failed to get flash device for area");
flash_area_close(fa);
return -ENODEV;
}
struct flash_pages_info page_info;
off_t last_block_offset;
// Find the last block of the flash area
rc = flash_get_page_info_by_offs(flash_dev, fa->fa_off + fa->fa_size - 1, &page_info);
if (rc != 0) {
LOG_ERR("Failed to get page info: %d", rc);
flash_area_close(fa);
return rc;
}
// Calculate the last block offset
rc = flash_get_page_info_by_offs(flash_dev, fa->fa_off + fa->fa_size - 1, &page_info);
if (rc != 0) {
LOG_ERR("Failed to get page info: %d", rc);
flash_area_close(fa);
return rc;
}
last_block_offset = page_info.start_offset;
// Convert absolute flash offset to relative offset within the flash area
off_t relative_offset = last_block_offset - fa->fa_off;
// Erase the image trailer/metadata at the end of the partition
LOG_INF("Erasing image trailer at absolute offset: %ld, relative offset: %ld, size: %d bytes",
last_block_offset, relative_offset, page_info.size);
rc = flash_area_erase(fa, relative_offset, page_info.size);
if (rc != 0) {
LOG_ERR("Failed to erase image trailer: %d", rc);
} else {
LOG_INF("Image trailer erased successfully");
}
flash_area_close(fa);
return rc;
}
// Custom reset command handler
static int cmd_reset(const struct shell *shell, size_t argc, char **argv)
{
ARG_UNUSED(argc);
ARG_UNUSED(argv);
shell_print(shell, "Resetting system...");
k_msleep(100); // Give time for the message to be sent
sys_reboot(SYS_REBOOT_COLD);
return 0;
}
// MCUboot serial recovery command handler
static int cmd_recovery(const struct shell *shell, size_t argc, char **argv)
{
ARG_UNUSED(argc);
ARG_UNUSED(argv);
shell_print(shell, "Entering MCUboot serial recovery mode...");
shell_print(shell, "Corrupting current image magic to trigger recovery...");
// Invalidate the current image by corrupting its header
int rc = invalidate_current_image();
if (rc != 0) {
shell_error(shell, "Failed to invalidate image: %d", rc);
return rc;
}
shell_print(shell, "Image magic corrupted. System will reset and MCUboot will detect bad image.");
shell_print(shell, "MCUboot should show error and wait for recovery.");
k_msleep(100); // Give time for the message to be sent
// Reset the system - MCUboot will detect invalid image and enter serial recovery
// log_process(true);
// sys_reboot(SYS_REBOOT_COLD);
return 0;
}
// Command to show firmware info
static int cmd_info(const struct shell *shell, size_t argc, char **argv)
{
ARG_UNUSED(argc);
ARG_UNUSED(argv);
const struct flash_area *fa;
int rc = flash_area_open(FIXED_PARTITION_ID(slot0_partition), &fa);
if (rc != 0) {
shell_error(shell, "Failed to open flash area: %d", rc);
return rc;
}
// Read the first few bytes to check the image header
uint32_t magic;
rc = flash_area_read(fa, 0, &magic, sizeof(magic));
if (rc == 0) {
shell_print(shell, "Image magic: 0x%08x", magic);
if (magic == IMAGE_MAGIC) {
shell_print(shell, "Image header is valid");
shell_print(shell, "Image starts at flash offset: 0x%lx", (unsigned long)fa->fa_off);
shell_print(shell, "Image partition size: %d bytes", fa->fa_size);
} else {
shell_print(shell, "Image header is INVALID (expected 0x%08x)", IMAGE_MAGIC);
}
} else {
shell_error(shell, "Failed to read image header: %d", rc);
}
flash_area_close(fa);
return 0;
}
SHELL_CMD_REGISTER(reset, NULL, "Reset the system", cmd_reset);
SHELL_CMD_REGISTER(recovery, NULL, "Enter MCUboot serial recovery mode", cmd_recovery);
SHELL_CMD_REGISTER(info, NULL, "Show firmware info", cmd_info);
int main(void)
{
LOG_INF("Firmware Node starting up");
LOG_INF("Shell with reset command available");
LOG_INF("Serial recovery command available");
return 0;
}

View File

@@ -0,0 +1,4 @@
# Sysbuild configuration for firmware_node with MCUboot
# Enable MCUboot as bootloader
set(SB_CONFIG_BOOTLOADER_MCUBOOT TRUE)

View File

@@ -0,0 +1,5 @@
# Sysbuild configuration for firmware_node with MCUboot
# Enable MCUboot as bootloader
SB_CONFIG_BOOTLOADER_MCUBOOT=y
SB_CONFIG_MCUBOOT_MODE_SINGLE_APP=y

View File

@@ -0,0 +1,13 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "../boards/flash_partitions_128kb.dtsi"
/ {
chosen {
zephyr,code-partition = &slot0_partition;
};
};

View File

@@ -0,0 +1,31 @@
# MCUboot configuration for firmware_node
# Enable basic console and logging for debugging
CONFIG_LOG=y
CONFIG_BOOT_BANNER=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_PRINTK=y
# Single slot configuration (no upgrades)
CONFIG_SINGLE_APPLICATION_SLOT=y
# Enable serial recovery mode (temporarily commented out for debugging)
# CONFIG_MCUBOOT_SERIAL=y
# CONFIG_BOOT_SERIAL_UART=y
# CONFIG_BOOT_SERIAL_DETECT_PORT=y
# Disable signature validation for testing to save space
CONFIG_BOOT_SIGNATURE_TYPE_NONE=y
# Size optimizations to fit in 40KB flash
CONFIG_SIZE_OPTIMIZATIONS=y
CONFIG_CBPRINTF_NANO=y
CONFIG_MINIMAL_LIBC=y
CONFIG_ASSERT=n
# Disable debug features for size
CONFIG_DEBUG_INFO=n
CONFIG_DEBUG_OPTIMIZATIONS=n
# Minimal heap for size optimization
CONFIG_HEAP_MEM_POOL_SIZE=0

View File

@@ -0,0 +1,12 @@
/*
* MCUboot device tree overlay for firmware_node
* Uses shared flash partition layout
*/
#include "../boards/flash_partitions_128kb.dtsi"
/ {
chosen {
zephyr,code-partition = &boot_partition;
};
};

View File

@@ -0,0 +1,33 @@
/*
* MCUboot specific overlay for weact_stm32g431_core
* This overlay defines flash partitions for MCUboot
*/
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
boot_partition: partition@0 {
label = "mcuboot";
reg = <0x00000000 0x00008000>;
};
slot0_partition: partition@8000 {
label = "image-0";
reg = <0x00008000 0x0000E000>;
};
slot1_partition: partition@16000 {
label = "image-1";
reg = <0x00016000 0x0000E000>;
};
storage_partition: partition@24000 {
label = "storage";
reg = <0x00024000 0x00004000>;
};
};
};
&chosen {
zephyr,boot-partition = &boot_partition;
};

View File

@@ -25,10 +25,10 @@
&adc1 { &adc1 {
status = "okay"; status = "okay";
pinctrl-0 = <&adc1_in1_pa0>; pinctrl-0 = <&adc1_in1_pa0 &adc1_in15_pb0>;
pinctrl-names = "default"; pinctrl-names = "default";
st,adc-clock-source = "SYNC"; st,adc-clock-source = "SYNC";
st,adc-prescaler = <4>; st,adc-prescaler = <1>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
@@ -36,9 +36,18 @@
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 = <3300>; zephyr,vref-mv = <2048>; // STM32G431 VREFBUF at 2.048V
}; };
}; };
@@ -47,4 +56,9 @@
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,9 +23,12 @@ int main(void)
return 0; return 0;
} }
// Test supply voltage reading // Test supply voltage reading periodically
uint16_t supply_voltage = valve_get_supply_voltage(); while (1) {
LOG_INF("Supply voltage: %u mV", supply_voltage); uint16_t supply_voltage = valve_get_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

@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.20)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(bootloader LANGUAGES C)
zephyr_include_directories(../../../include)
add_subdirectory(../../../lib lib)
target_sources(app PRIVATE src/main.c)

View File

@@ -0,0 +1,34 @@
# Firmware Node Application
This Zephyr application provides firmware management capabilities for the irrigation system.
**Tested on Zephyr 4.1.99**
## Features
### Step 1: Shell with Reset Command
- Shell interface with custom "reset" command
- Warm reboot functionality
### Planned Features
- MCUboot support with partition manager
- Firmware version display
- MCUmgr support for OTA updates
## Building
```bash
west build -p auto -b weact_stm32g431_core apps/firmware_node -- -DBOARD_FLASH_RUNNER=blackmagicprobe
```
## Flashing
```bash
west flash
```
## Usage
Connect to the device via serial console and use the shell:
- `reset` - Reboot the system
- `help` - Show available commands

View File

@@ -0,0 +1,5 @@
VERSION_MAJOR = 0
VERSION_MINOR = 0
PATCHLEVEL = 1
VERSION_TWEAK = 0
EXTRAVERSION = testing

View File

@@ -0,0 +1,8 @@
#!/bin/bash
/home/edi/zephyr-sdk-0.17.1/arm-zephyr-eabi/bin/arm-zephyr-eabi-gdb \
-ex 'target extended-remote /dev/ttyACM0' \
-ex 'monitor swdp_scan' \
-ex 'attach 1' \
-ex 'monitor erase_mass' \
-ex 'detach' \
-ex 'quit' \

View File

@@ -0,0 +1,16 @@
CONFIG_SHELL=y
CONFIG_REBOOT=y
# MCUboot support for recovery request function
CONFIG_MCUBOOT_BOOTUTIL_LIB=y
CONFIG_MCUBOOT_IMG_MANAGER=y
CONFIG_IMG_MANAGER=y
# Flash and Stream Configuration (required for IMG_MANAGER)
CONFIG_FLASH=y
CONFIG_STREAM_FLASH=y
# Retention system
CONFIG_RETENTION=y
CONFIG_RETENTION_BOOT_MODE=y
CONFIG_RETAINED_MEM=y

View File

@@ -0,0 +1,42 @@
#include <zephyr/kernel.h>
#include <app_version.h>
#include <zephyr/shell/shell.h>
#include <zephyr/sys/reboot.h>
#include <zephyr/dfu/mcuboot.h>
#include <zephyr/retention/bootmode.h>
#include <stdlib.h>
/* Shell command handler for "reset" */
static int cmd_reset(const struct shell *sh, size_t argc, char **argv)
{
shell_print(sh, "Rebooting system...");
k_sleep(K_MSEC(100)); // Optional delay for user to see the message
sys_reboot(SYS_REBOOT_WARM);
return 0;
}
static int cmd_download(const struct shell *sh, size_t argc, char **argv)
{
int rc;
/* Set boot mode to serial recovery */
rc = bootmode_set(BOOT_MODE_TYPE_BOOTLOADER);
if (rc < 0) {
shell_error(sh, "Failed to set boot mode: %d", rc);
return rc;
}
shell_print(sh, "Boot mode set to recovery. Rebooting to bootloader...");
k_sleep(K_MSEC(100));
sys_reboot(SYS_REBOOT_WARM);
return 0;
}
/* Register the shell command */
SHELL_CMD_REGISTER(reset, NULL, "Reboot the system", cmd_reset);
SHELL_CMD_REGISTER(download, NULL, "Download firmware", cmd_download);
int main(void){
printk("Bootloader test version %s\n", APP_VERSION_EXTENDED_STRING);
return 0;
}

View File

@@ -0,0 +1,5 @@
# Sysbuild configuration for firmware_node with MCUboot
# Enable MCUboot as bootloader
SB_CONFIG_BOOTLOADER_MCUBOOT=y
SB_CONFIG_MCUBOOT_MODE_SINGLE_APP=y

View File

@@ -0,0 +1,13 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "flash_partitions_128kb.dtsi"
/ {
chosen {
zephyr,code-partition = &slot0_partition;
};
};

View File

@@ -0,0 +1,62 @@
/*
* Devicetree Overlay for 128KB Flash
* - MCUboot Bootloader (32KB)
* - Application Slot (96KB)
*/
&flash0 {
/delete-node/ partitions;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
boot_partition: partition@0 {
label = "mcuboot";
reg = <0x00000000 DT_SIZE_K(32)>;
read-only;
};
slot0_partition: partition@8000 {
label = "image-0";
reg = <0x00008000 DT_SIZE_K(96)>;
};
};
};
/* Add retention memory to the existing SRAM node */
&sram0 {
#address-cells = <1>;
#size-cells = <1>;
retainedmem {
compatible = "zephyr,retained-ram";
status = "okay";
#address-cells = <1>;
#size-cells = <1>;
boot_mode: retention@7f00 {
compatible = "zephyr,retention";
status = "okay";
reg = <0x7f00 0x100>;
prefix = [08 04];
checksum = <1>;
};
};
};
/ {
chosen {
zephyr,boot-mode = &boot_mode;
zephyr,console = &cdc_acm_uart0;
};
};
&zephyr_udc0 {
status = "okay";
cdc_acm_uart0: cdc_acm_uart0 {
compatible = "zephyr,cdc-acm-uart";
label = "CDC_ACM_0";
};
};

View File

@@ -0,0 +1,46 @@
#
# MCUboot Configuration for Serial Recovery over USB-CDC
#
# Enables serial recovery mode in MCUboot.
CONFIG_MCUBOOT_SERIAL=y
# Tell MCUboot to check for a trigger to enter recovery
CONFIG_BOOT_SERIAL_BOOT_MODE=y
# --- USB Stack Configuration ---
CONFIG_USB_DEVICE_STACK=y
CONFIG_USB_DEVICE_PRODUCT="MCUboot Serial Recovery"
# Use USB CDC ACM for MCUboot serial recovery (not UART)
CONFIG_BOOT_SERIAL_CDC_ACM=y
# --- Disable Zephyr Console to avoid conflicts ---
# MCUboot's serial_adapter doesn't work well with the general console subsystem.
CONFIG_UART_CONSOLE=n
CONFIG_CONSOLE_HANDLER=n
CONFIG_CONSOLE=n
# --- Flash and Stream Configuration (required for IMG_MANAGER) ---
CONFIG_FLASH=y
CONFIG_STREAM_FLASH=y
# --- mcumgr Configuration ---
# MCUMGR requires NET_BUF, even for serial transport.
CONFIG_NET_BUF=y
CONFIG_NET_LOG=n
# Enables the mcumgr library and necessary command handlers
CONFIG_MCUMGR=y
CONFIG_IMG_MANAGER=y
CONFIG_MCUMGR_GRP_IMG=y
CONFIG_MCUMGR_GRP_OS=y
# --- Retention Configuration ---
CONFIG_RETAINED_MEM=y
CONFIG_RETENTION=y
CONFIG_RETENTION_BOOT_MODE=y
# --- Optional: Reduce memory usage ---
CONFIG_MAIN_STACK_SIZE=2048
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1024

View File

@@ -0,0 +1,17 @@
#include "flash_partitions_128kb.dtsi"
/ {
chosen {
zephyr,code-partition = &boot_partition;
zephyr,console = &cdc_acm_uart0;
};
};
&zephyr_udc0 {
status = "okay";
cdc_acm_uart0: cdc_acm_uart0 {
compatible = "zephyr,cdc-acm-uart";
label = "CDC_ACM_0";
};
};

View File

@@ -12,9 +12,10 @@ 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, .reference = ADC_REF_INTERNAL, // STM32 only supports internal ref (1.2V)
.acquisition_time = ADC_ACQ_TIME_DEFAULT, .acquisition_time = ADC_ACQ_TIME_DEFAULT, // Use default acquisition time
.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 = {
@@ -112,15 +113,34 @@ 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_DBG("Starting supply voltage measurement"); LOG_INF("=== ADC TEST MODE - PA0 LAB SUPPLY TEST ===");
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("");
// Ensure VND7050AJ is enabled (RST=HIGH) // No VND7050AJ configuration - pure ADC test
LOG_DBG("Enabling VND7050AJ (RST=1)"); // Just make sure pins are in safe state
gpio_pin_set_dt(&valve_gpios.rst, 1); gpio_pin_configure_dt(&valve_gpios.rst, GPIO_OUTPUT);
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);
// Wait for VND7050AJ to power up and stabilize // Set all VND7050AJ pins LOW for safety
k_msleep(50); gpio_pin_set_dt(&valve_gpios.rst, 0);
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,
@@ -129,47 +149,65 @@ uint16_t valve_get_supply_voltage(void)
.resolution = 12, .resolution = 12,
}; };
// Configure VND7050AJ to output supply voltage on MULTISENSE LOG_INF("Starting continuous ADC readings every 500ms...");
// 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 // Continuous monitoring loop with improved stability
LOG_DBG("Enabling MULTISENSE (SEN=1)"); int reading_count = 0;
gpio_pin_set_dt(&valve_gpios.sen, 1); int32_t samples[10]; // Buffer for averaging
// Wait for voltage to stabilize while (1) {
k_msleep(10); // Take multiple samples and average them for stability
int valid_samples = 0;
// Read ADC value int32_t sum = 0;
LOG_DBG("Reading ADC channel %d", adc_channel_cfg.channel_id);
int ret = adc_read(adc_dev, &sequence); for (int i = 0; i < 10; i++) {
if (ret < 0) { k_msleep(50); // Longer delay between samples for stability
LOG_ERR("Could not read ADC (%d)", ret); int adc_ret = adc_read(adc_dev, &sequence);
gpio_pin_set_dt(&valve_gpios.sen, 0);
return 0; if (adc_ret == 0 && buf > 100) { // Filter out near-zero readings (floating input)
samples[i] = buf;
sum += buf;
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) {
// Calculate average
int32_t avg_raw = sum / valid_samples;
// Calculate voltage using the correct VREFBUF reference (2.048V)
int32_t pa0_mv = (avg_raw * 2048) / 4096; // Using 2.048V VREFBUF
// Calculate standard deviation to show stability
int32_t variance = 0;
for (int i = 0; i < valid_samples; i++) {
int32_t diff = samples[i] - avg_raw;
variance += diff * diff;
}
int32_t std_dev = (valid_samples > 1) ? variance / (valid_samples - 1) : 0;
// Find min/max for this sample set
int32_t min_raw = samples[0], max_raw = samples[0];
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",
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
} }
// Disable sensing to save power return 0; // Never reached
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)); }

View File

@@ -2,12 +2,13 @@
import serial import serial
import time import time
import sys import sys
import argparse
def monitor_serial(): def monitor_serial(port):
try: try:
# Open serial connection # Open serial connection
ser = serial.Serial('/dev/ttyACM3', 115200, timeout=1) ser = serial.Serial(port, 115200, timeout=1)
print("Connected to /dev/ttyACM3") print(f"Connected to {port}")
# Send reset command # Send reset command
ser.write(b'reset\n') ser.write(b'reset\n')
@@ -18,7 +19,7 @@ def monitor_serial():
# Read output for 10 seconds # Read output for 10 seconds
start_time = time.time() start_time = time.time()
while time.time() - start_time < 10: while 1: #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:
@@ -36,4 +37,7 @@ def monitor_serial():
sys.exit(1) sys.exit(1)
if __name__ == "__main__": if __name__ == "__main__":
monitor_serial() parser = argparse.ArgumentParser(description='Serial monitor.')
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/ttyACM3', 115200, timeout=1) ser = serial.Serial('/dev/ttyACM1', 115200, timeout=1)
print("Serial port opened successfully") print("Serial port opened successfully")
# Clear any existing data # Clear any existing data