Skip to content

Commit 5da04bc

Browse files
Health Monitor - WIP
1 parent 86a89bb commit 5da04bc

10 files changed

Lines changed: 312 additions & 0 deletions

File tree

soc/nordic/nrf54l/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ zephyr_library_sources(
77
)
88
zephyr_include_directories(.)
99

10+
add_subdirectory_ifdef(CONFIG_SOC_HEALTH_MONITORING health_monitoring_nrfx)
11+
1012
dt_nodelabel(kmu_push_area_node NODELABEL nrf_kmu_reserved_push_area)
1113

1214
# We need a buffer in memory in a static location which can be used by

soc/nordic/nrf54l/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,12 @@ config SOC_NRF54L_ANOMALY_56_WORKAROUND
107107
help
108108
This option enables configuration workaround 56 for nRF54L Series SoCs.
109109

110+
config VBAT_MONITOR_ENABLE
111+
bool "Enable VBAT monitoring"
112+
select SOC_HEALTH_MONITORING
113+
114+
config VINTERNAL_MONITOR_ENABLE
115+
bool "Enable VInternal monitoring"
116+
select SOC_HEALTH_MONITORING
117+
110118
endif # SOC_SERIES_NRF54L

soc/nordic/nrf54l/Kconfig.defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
if SOC_SERIES_NRF54L
77

88
rsource "Kconfig.defconfig.nrf54l*"
9+
rsource "health_monitoring_nrfx/Kconfig"
910

1011
if ARM
1112

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright (c) 2026 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
cmake_minimum_required(VERSION 3.20.0)
5+
6+
zephyr_library_sources(health_monitoring.c
7+
battery_monitor.c)
8+
9+
zephyr_include_directories(.)
10+
11+
# TODO Currently this does not cause an error when enabled from Kconfig
12+
if (CONFIG_ADC)
13+
message(FATAL_ERROR
14+
"Health Monitoring feautre can not be used in conjunction with other ADC measurements,
15+
see sample XXXX to implement an alternative, disabling this feature may have
16+
adverse effects on performance over temperature")
17+
endif()
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Nordic Semiconductor nRF71 MCU line
2+
3+
# Copyright (c) 2025 Nordic Semiconductor ASA
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config VBAT_MONITOR_ENABLE
7+
bool "Enable VBAT monitoring"
8+
select SOC_HEALTH_MONITORING
9+
10+
config VINTERNAL_MONITOR_ENABLE
11+
bool "Enable VInternal monitoring"
12+
select SOC_HEALTH_MONITORING
13+
14+
config SOC_HEALTH_MONITORING
15+
bool "Enable health monitoring system"
16+
default n
17+
select NRFX_SAADC
18+
# select ADC # TODO: Only enable to verify error in CMakelists.txt
19+
select SOC_LATE_INIT_HOOK
20+
# Debug
21+
select SHELL
22+
select KERNEL_SHELL
23+
select LOG
24+
help
25+
Enable periodic VBAT voltage monitoring using SAADC.
26+
When enabled, the application will periodically measure
27+
the battery voltage and trigger a callback if it falls
28+
below the configured threshold.
29+
30+
if VBAT_MONITOR_ENABLE
31+
32+
config VBAT_MONITOR_INTERVAL_MS
33+
int "VBAT measurement interval (milliseconds)"
34+
default 5000
35+
range 100 3600000
36+
help
37+
Interval between VBAT measurements in milliseconds.
38+
Minimum: 100 ms
39+
Maximum: 3600000 ms (1 hour)
40+
41+
config VBAT_MONITOR_LOWER_THRESHOLD_MV
42+
int "VBAT low threshold (millivolts)"
43+
default 250
44+
range 200 5000
45+
help
46+
Battery voltage threshold in millivolts. When the measured
47+
voltage falls below this value, the low battery callback
48+
will be triggered.
49+
Typical range: 2000-5000 mV
50+
51+
endif # VBAT_MONITOR_ENABLE
52+
53+
if SOC_HEALTH_MONITORING
54+
55+
#TODO: Align this with style used elsewhere
56+
config HEALTH_MONITOR_LOG_LEVEL
57+
int "VBAT monitor log level"
58+
default 3
59+
range 0 4
60+
help
61+
Log level for VBAT monitor module:
62+
0 = OFF
63+
1 = ERROR
64+
2 = WARN
65+
3 = INFO
66+
4 = DEBUG
67+
68+
endif # VBAT_MONITOR_ENABLE
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#include <zephyr/device.h>
2+
#include <zephyr/devicetree.h>
3+
#include <zephyr/logging/log.h>
4+
#include <nrfx_saadc.h>
5+
#include "battery_monitor.h"
6+
7+
LOG_MODULE_REGISTER(battery_monitoring, CONFIG_HEALTH_MONITOR_LOG_LEVEL);
8+
9+
/** @brief global variable to store battery voltage */
10+
static atomic_t battery_voltage;
11+
12+
13+
/** @brief SAADC channel to measure battery level */
14+
static const nrfx_saadc_channel_t m_multiple_channels[] = {
15+
/* TODO: If def around for measure battery and temp ADC
16+
* Need to update to correct pins.
17+
*/
18+
NRFX_SAADC_DEFAULT_CHANNEL_SE(NRFX_ANALOG_EXTERNAL_AIN6, 0),
19+
NRFX_SAADC_DEFAULT_CHANNEL_SE(NRFX_ANALOG_EXTERNAL_AIN4, 1),
20+
};
21+
22+
#define CHANNEL_COUNT NRFX_ARRAY_SIZE(m_multiple_channels)
23+
24+
/** @brief Samples buffer defined with the size of @ref CHANNEL_COUNT
25+
* symbol to store values from each channel ( @ref m_multiple_channels).
26+
*/
27+
static nrf_saadc_value_t m_samples_buffer[CHANNEL_COUNT];
28+
29+
static void get_battery_voltage_work_handler(struct k_work *work);
30+
31+
K_WORK_DELAYABLE_DEFINE(get_battery_voltage, get_battery_voltage_work_handler);
32+
33+
/**
34+
* @brief Function for getting pointer to vbatt.
35+
*
36+
* @return Pointer to atomic variable holding battery voltage
37+
*/
38+
atomic_t *get_battery_voltage_pointer(void)
39+
{
40+
return &battery_voltage;
41+
}
42+
43+
/**
44+
* @brief Function for handling SAADC driver events.
45+
*
46+
* @param[in] p_event Pointer to an SAADC driver event.
47+
*/
48+
static void saadc_handler(nrfx_saadc_evt_t const *p_event)
49+
{
50+
int status;
51+
uint16_t samples_number;
52+
/* TODO: Assign from buffer directly to atomic variable */
53+
static int32_t voltage;
54+
/* Temp value allows observability of functionality for reading data */
55+
static int32_t voltage_temp;
56+
57+
switch (p_event->type)
58+
{
59+
case NRFX_SAADC_EVT_DONE:
60+
LOG_INF("SAADC event: DONE");
61+
samples_number = p_event->data.done.size;
62+
for (uint16_t i = 0; i < samples_number; i++)
63+
{
64+
voltage = NRFX_SAADC_SAMPLE_GET(p_event->data.done.p_buffer, i);
65+
LOG_INF("[Sample %d] value == %d",i, voltage);
66+
67+
}
68+
// atomic_set(&battery_voltage,voltage);
69+
atomic_set(&battery_voltage,voltage_temp);
70+
voltage_temp++;
71+
k_work_schedule(&get_battery_voltage, K_MSEC(CONFIG_VBAT_MONITOR_INTERVAL_MS));
72+
break;
73+
74+
case NRFX_SAADC_EVT_CALIBRATEDONE:
75+
LOG_INF("SAADC event: CALIBRATEDONE");
76+
status = nrfx_saadc_mode_trigger();
77+
NRFX_ASSERT(status == 0);
78+
break;
79+
80+
default:
81+
break;
82+
}
83+
}
84+
85+
// TODO: Add weak callback for when adc is below low threshold
86+
// static void adc_low_threshold_trigger(void)
87+
// {
88+
// g_adc_below_threshold = true;
89+
// g_adc_low_trigger_count++;
90+
// LOG_WRN("ADC below threshold: %d mV (raw %d)\n",
91+
// (int)g_adc_value_mv, (int)g_adc_value_raw);
92+
// }
93+
94+
int battery_monitoring_setup()
95+
{
96+
int err = 0;
97+
98+
// TODO: This should cause error from CMakeLists.txt
99+
IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_SAADC), IRQ_PRIO_LOWEST, nrfx_saadc_irq_handler, 0, 0);
100+
101+
//TODO: What to do in event of failure in setup
102+
err = nrfx_saadc_init(NRFX_SAADC_DEFAULT_CONFIG_IRQ_PRIORITY);
103+
if (err != 0) {
104+
LOG_ERR("SAADC init failed");
105+
return err;
106+
}
107+
108+
err = nrfx_saadc_channels_config(m_multiple_channels, CHANNEL_COUNT);
109+
if (err != 0) {
110+
LOG_ERR("SAADC channel config failed");
111+
return err;
112+
}
113+
114+
err = nrfx_saadc_simple_mode_set(nrfx_saadc_channels_configured_get(),
115+
NRF_SAADC_RESOLUTION_8BIT,
116+
NRF_SAADC_OVERSAMPLE_DISABLED,
117+
saadc_handler);
118+
if (err != 0) {
119+
LOG_ERR("SAADC mode setting failed");
120+
return err;
121+
}
122+
123+
err = nrfx_saadc_buffer_set(m_samples_buffer, CHANNEL_COUNT);
124+
if (err != 0) {
125+
LOG_ERR("Setting SAADC buffer failed");
126+
return err;
127+
}
128+
129+
LOG_INF("SAADC Configured");
130+
k_work_schedule(&get_battery_voltage, K_MSEC(CONFIG_VBAT_MONITOR_INTERVAL_MS));
131+
return err;
132+
}
133+
134+
static void get_battery_voltage_work_handler(struct k_work *work)
135+
{
136+
int err;
137+
138+
(void)work;
139+
140+
err = nrfx_saadc_offset_calibrate(saadc_handler);
141+
if (err != 0) {
142+
LOG_ERR("Offset calibration failed");
143+
}
144+
LOG_INF("Calibration Requested");
145+
return;
146+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright (c) 2024 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef _BATTERY_MONITOR_H_
8+
#define _BATTERY_MONITOR_H_
9+
10+
#include <zephyr/sys/atomic.h>
11+
12+
int battery_monitoring_setup(void);
13+
atomic_t* get_battery_voltage_pointer(void);
14+
15+
#endif /* _BATTERY_MONITOR_H_ */
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <zephyr/kernel.h>
2+
#include <zephyr/sys/atomic.h>
3+
#include <zephyr/logging/log.h>
4+
#include "battery_monitor.h"
5+
6+
LOG_MODULE_REGISTER(health_monitoring, CONFIG_HEALTH_MONITOR_LOG_LEVEL);
7+
8+
//TODO: Implement temperature measurements
9+
10+
typedef struct {
11+
atomic_t* g_vbatt_value;
12+
atomic_t temp_value;
13+
} health_metrics_t;
14+
15+
health_metrics_t health_metrics = {};
16+
17+
int health_monitoring_setup(void)
18+
{
19+
battery_monitoring_setup();
20+
atomic_t* g_vbatt_value = get_battery_voltage_pointer();
21+
22+
//Here for now to demonstrate functionality
23+
for (;;) {
24+
k_sleep(K_SECONDS(10));
25+
uint32_t voltage = atomic_get(g_vbatt_value);
26+
/* Optional: print global state from main loop */
27+
printk("Global ADC: %d mV\n", voltage);
28+
}
29+
30+
return 0;
31+
}
32+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright (c) 2024 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef _HEALTH_MONITORING_H_
8+
#define _HEALTH_MONITORING_H_
9+
10+
int health_monitoring_setup(void);
11+
12+
#endif /* _HEALTH_MONITORING_H_ */

soc/nordic/nrf54l/soc.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ LOG_MODULE_REGISTER(soc, CONFIG_SOC_LOG_LEVEL);
3232
#include <hal/nrf_regulators.h>
3333
#include <zephyr/dt-bindings/regulator/nrf5x.h>
3434

35+
#ifdef CONFIG_SOC_HEALTH_MONITORING
36+
#include "health_monitoring.h"
37+
#endif
38+
39+
3540
#define LFXO_NODE DT_NODELABEL(lfxo)
3641
#define HFXO_NODE DT_NODELABEL(hfxo)
3742

@@ -166,3 +171,9 @@ void arch_busy_wait(uint32_t time_us)
166171
}
167172

168173
SYS_INIT(nordicsemi_nrf54l_init, PRE_KERNEL_1, 0);
174+
175+
#ifdef CONFIG_SOC_HEALTH_MONITORING
176+
void soc_late_init_hook(){
177+
health_monitoring_setup();
178+
}
179+
#endif

0 commit comments

Comments
 (0)