Initialize DC/DC on nine circuit-verified nRF52 boards
This commit is contained in:
1 parent
364e3cabd5
commit
dcabcb8cb2
11 files changed
+97
-18
No files matched your search
@@ -120,3 +120,31 @@ compare battery-only cold starts, low-voltage recovery, BLE operation and
|
||||
settings writes/reboots on the actual board/baseboard revisions before a fleet
|
||||
rollout. USB power can mask the reported startup symptoms. A current meter is
|
||||
still needed to quantify the power difference.
|
||||
|
||||
The following boards also now call their declared `NRF52BoardDCDC` initializer
|
||||
instead of bypassing it with `NRF52Board::begin()`. This applies to every role
|
||||
that uses the board class, not just Companion, and does not require enabling
|
||||
the runtime sleep policy. The nRF52840 hardware manages the enabled regulator
|
||||
according to load. This is REG1 only, not a blanket change to REG0 or an
|
||||
external LDO, PMIC output, or radio regulator.
|
||||
|
||||
| Board | Circuit evidence used for REG1 support |
|
||||
| --- | --- |
|
||||
| Heltec T1 | [T1 V1.0 schematic](https://resource.heltec.cn/download/Mesh_Node_T1/schematic/Mesh_Node_T1_V1.0.pdf), DCC-to-DEC4 inductors and capacitors. |
|
||||
| Heltec MeshPocket | [MeshPocket schematic](https://resource.heltec.cn/download/MeshPocket/schematic/MeshPocket_nRF52840%2BSX1262.pdf), DCC-to-DEC4 L1/L2 and C5/C6. |
|
||||
| Heltec MeshSolar and MeshTower V2 | [HT-N5262M module circuit](https://resource.heltec.cn/download/MeshTower-V2/schematic/HT-N5262M_Schematic_Diagram.pdf), L1/L2 and C1/C2. Heltec links this circuit from its [MeshTower V2 resources](https://heltec.org/project/meshtower/); [MeshSolar's datasheet](https://resource.heltec.cn/download/MeshSolar/datasheet/MeshSolar_V1.0.0.pdf) identifies the module. |
|
||||
| LilyGO T-Echo | [T-Echo schematic](https://raw.githubusercontent.com/Xinyuan-LilyGO/T-Echo/main/schematic/T-Echo_Schematic.pdf), REG1 DC/DC network and explicit DC/DC-enabled design note. |
|
||||
| Ikoka Handheld, Nano and Stick nRF52 | [XIAO nRF52840 circuit](https://files.seeedstudio.com/wiki/XIAO-BLE/Seeed_Studio_XIAO_nRF52840_PDF.pdf), DCC L1 and DEC4 capacitors. The designers identify XIAO as the MCU for [Handheld](https://github.com/ndoo/ikoka-handheld-meshtastic-device), [Nano](https://ndoo.sg/projects:amateur_radio:meshtastic:diy_devices:ikoka_nano) and [Stick](https://github.com/ndoo/ikoka-stick-meshtastic-device). |
|
||||
| Minew ME25LS01 | [Manufacturer datasheet](https://store.minewsemi.com/wp-content/uploads/2024/03/ME25LS01_LR1110_Datasheet_K_EN.pdf), electrical schematic page 9: MCU DCC through ML2/ML3 to DEC4_6 and MC15/MC16. |
|
||||
|
||||
The startup tests execute each real `begin()` with SoftDevice off/on and power
|
||||
management compiled out/in. They require DC/DC before peripheral initialization
|
||||
and before any enabled boot-voltage check, while retaining one common board
|
||||
initialization. The previous bypass fails these tests.
|
||||
|
||||
ThinkNode M3/M4/M6 and generic ProMicro remain unchanged pending exact circuit
|
||||
or physical-board confirmation; their DC/DC inheritance alone is not evidence.
|
||||
Nano G2 Ultra, ThinkNode M1/M8 and Keepteen LT1 retain their plain `NRF52Board`
|
||||
initializers for the same reason. The MeshTracker X1's LR2021 regulator likewise
|
||||
remains unchanged. Do not turn on a switching regulator without its required
|
||||
inductor/capacitor network. The MCU and LoRa regulator choices are independent.
|
||||
@@ -11,16 +11,41 @@ from cpp_source import body
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
# Each target has manufacturer/board-designer circuit evidence recorded in
|
||||
# docs/power_saving_diagnostics.md. Do not infer eligibility from inheritance.
|
||||
VERIFIED_BOARDS = (
|
||||
('heltec_t1/T1Board.cpp', 'T1Board', True),
|
||||
('heltec_mesh_solar/MeshSolarBoard.cpp', 'MeshSolarBoard', False),
|
||||
('heltec_tower_v2/HeltecTowerV2Board.cpp', 'HeltecTowerV2Board', True),
|
||||
('mesh_pocket/MeshPocket.cpp', 'HeltecMeshPocket', False),
|
||||
('lilygo_techo/TechoBoard.cpp', 'TechoBoard', False),
|
||||
('ikoka_handheld_nrf/IkokaNrf52Board.cpp', 'IkokaNrf52Board', False),
|
||||
('ikoka_nano_nrf/IkokaNanoNRFBoard.cpp', 'IkokaNanoNRFBoard', False),
|
||||
('ikoka_stick_nrf/IkokaStickNRFBoard.cpp', 'IkokaStickNRFBoard', False),
|
||||
('minewsemi_me25ls01/MinewsemiME25LS01Board.cpp', 'MinewsemiME25LS01Board', False),
|
||||
)
|
||||
|
||||
HARNESS = r'''
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#define NRF52_POWER_MANAGEMENT 1
|
||||
#define PIN_BOARD_SDA 1
|
||||
#define PIN_BOARD_SCL 2
|
||||
#define PIN_USER_BTN 9
|
||||
#define PIN_USER_BTN_ANA 31
|
||||
#define PIN_WIRE_SDA 1
|
||||
#define PIN_WIRE_SCL 2
|
||||
#define P_LORA_TX_LED 28
|
||||
#define BUTTON_PIN 9
|
||||
#define DISPLAY_CLASS TestDisplay
|
||||
#define DISPLAY_ROTATION 0
|
||||
constexpr int INPUT = 0, OUTPUT = 1, INPUT_PULLUP = 2, LOW = 0, HIGH = 1;
|
||||
constexpr int PIN_VBAT_READ = 5, WB_IO2 = 34, SX126X_POWER_EN = 37;
|
||||
constexpr int PIN_VBAT = 5, VBAT_ENABLE = 14, PIN_BAT_CTL = 15, LED_PIN = 28;
|
||||
constexpr int PIN_GPS_EN = 10, PIN_GPS_RESET = 11, PIN_GPS_STANDBY = 12;
|
||||
constexpr int PIN_SENSOR_EN = 13, PIN_BUZZER = 16;
|
||||
constexpr int PIN_BUZZER_VOLTAGE_MULTIPLIER_1 = 17, PIN_BUZZER_VOLTAGE_MULTIPLIER_2 = 18;
|
||||
constexpr int LED_STATE_ON = 1, PIN_GPS_EN_ACTIVE = 1, PIN_SENSOR_EN_ACTIVE = 1;
|
||||
constexpr int GPS_RESET_MODE = 0;
|
||||
constexpr int NRF_POWER_DCDC_ENABLE = 1, NRF_POWER_DCDC_DISABLE = 0;
|
||||
static unsigned base_calls, sdk_calls, voltage_checks;
|
||||
static uint8_t softdevice;
|
||||
@@ -33,19 +58,33 @@ void sd_power_dcdc_mode_set(int mode) {
|
||||
assert(base_calls == 1 && softdevice && mode == NRF_POWER_DCDC_ENABLE);
|
||||
++sdk_calls;
|
||||
}
|
||||
void pinMode(int, int) {}
|
||||
void digitalWrite(int, int) {}
|
||||
void requireDcdc() {
|
||||
assert(base_calls == 1);
|
||||
assert(softdevice ? sdk_calls == 1 : power.DCDCEN == 1);
|
||||
}
|
||||
void pinMode(int, int) { requireDcdc(); }
|
||||
void digitalWrite(int, int) { requireDcdc(); }
|
||||
void delay(unsigned) {}
|
||||
struct Bus { void setPins(int, int) {} void begin() {} } Wire;
|
||||
void meshSolarStart() { requireDcdc(); }
|
||||
struct Bus {
|
||||
void setPins(int, int) { requireDcdc(); }
|
||||
void begin() { requireDcdc(); }
|
||||
} Wire;
|
||||
struct Uart { void begin(unsigned) { requireDcdc(); } } Serial;
|
||||
struct Peripheral {
|
||||
void begin() { requireDcdc(); }
|
||||
void init() { requireDcdc(); }
|
||||
};
|
||||
struct Config {} power_config;
|
||||
struct NRF52Board { void begin() { ++base_calls; } };
|
||||
struct NRF52BoardDCDC : NRF52Board { void begin(); };
|
||||
struct @BOARD@ : NRF52BoardDCDC {
|
||||
Peripheral periph_power, loRaFEMControl;
|
||||
unsigned btn_prev_state = 0;
|
||||
void begin();
|
||||
void checkBootVoltage(const Config*) {
|
||||
++voltage_checks;
|
||||
assert(base_calls == 1);
|
||||
assert(softdevice ? sdk_calls == 1 : power.DCDCEN == 1);
|
||||
requireDcdc();
|
||||
}
|
||||
};
|
||||
@METHODS@
|
||||
@@ -54,7 +93,7 @@ int main() {
|
||||
power.DCDCEN = base_calls = sdk_calls = voltage_checks = 0;
|
||||
@BOARD@ board;
|
||||
board.begin();
|
||||
assert(base_calls == 1 && voltage_checks == 1);
|
||||
assert(base_calls == 1 && voltage_checks == @VOLTAGE_CHECKS@);
|
||||
assert(sdk_calls == softdevice);
|
||||
assert(power.DCDCEN == (softdevice ? 0U : 1U));
|
||||
}
|
||||
@@ -63,11 +102,15 @@ int main() {
|
||||
|
||||
|
||||
class Nrf52BoardRegulatorTests(unittest.TestCase):
|
||||
def run_startup(self, filename, board, flags=()):
|
||||
def run_startup(self, filename, board, flags=(), power_management=True,
|
||||
has_voltage_check=True):
|
||||
methods = body((ROOT / 'src/helpers/NRF52Board.cpp').read_text(),
|
||||
'void NRF52BoardDCDC::begin()')
|
||||
methods += body((ROOT / filename).read_text(), f'void {board}::begin()')
|
||||
source = HARNESS.replace('@BOARD@', board).replace('@METHODS@', methods)
|
||||
source = source.replace('@VOLTAGE_CHECKS@', str(int(power_management and has_voltage_check)))
|
||||
if power_management:
|
||||
flags = (*flags, '-DNRF52_POWER_MANAGEMENT=1')
|
||||
with tempfile.TemporaryDirectory(prefix='meshcore-board-regulator-') as tmp:
|
||||
cpp, binary = Path(tmp) / 'test.cpp', Path(tmp) / 'test'
|
||||
cpp.write_text(source, encoding='ascii')
|
||||
@@ -86,6 +129,14 @@ class Nrf52BoardRegulatorTests(unittest.TestCase):
|
||||
with self.subTest(flags=flags):
|
||||
self.run_startup('variants/rak4631/RAK4631Board.cpp', 'RAK4631Board', flags)
|
||||
|
||||
def test_verified_boards_initialize_dcdc_before_peripherals(self):
|
||||
for filename, board, has_voltage_check in VERIFIED_BOARDS:
|
||||
for power_management in (False, True):
|
||||
with self.subTest(board=board, power_management=power_management):
|
||||
self.run_startup('variants/' + filename, board,
|
||||
power_management=power_management,
|
||||
has_voltage_check=has_voltage_check)
|
||||
|
||||
def test_rak_companion_profiles_do_not_force_ldo(self):
|
||||
config = configparser.ConfigParser(interpolation=None)
|
||||
config.read(ROOT / 'variants/rak4631/platformio.ini')
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "MeshSolarBoard.h"
|
||||
|
||||
void MeshSolarBoard::begin() {
|
||||
NRF52Board::begin();
|
||||
NRF52BoardDCDC::begin();
|
||||
|
||||
meshSolarStart();
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ void T1Board::initiateShutdown(uint8_t reason) {
|
||||
#endif
|
||||
|
||||
void T1Board::begin() {
|
||||
NRF52Board::begin();
|
||||
NRF52BoardDCDC::begin();
|
||||
|
||||
#ifdef NRF52_POWER_MANAGEMENT
|
||||
checkBootVoltage(&power_config);
|
||||
|
||||
@@ -36,7 +36,7 @@ void HeltecTowerV2Board::initiateShutdown(uint8_t reason) {
|
||||
#endif
|
||||
|
||||
void HeltecTowerV2Board::begin() {
|
||||
NRF52Board::begin();
|
||||
NRF52BoardDCDC::begin();
|
||||
|
||||
pinMode(P_LORA_TX_LED, OUTPUT);
|
||||
digitalWrite(P_LORA_TX_LED, !LED_STATE_ON);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "IkokaNrf52Board.h"
|
||||
|
||||
void IkokaNrf52Board::begin() {
|
||||
NRF52Board::begin();
|
||||
NRF52BoardDCDC::begin();
|
||||
|
||||
// ensure we have pull ups on the screen i2c, this isn't always available
|
||||
// in hardware and it should only be 20k ohms. Disable the pullups if we
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "IkokaNanoNRFBoard.h"
|
||||
|
||||
void IkokaNanoNRFBoard::begin() {
|
||||
NRF52Board::begin();
|
||||
NRF52BoardDCDC::begin();
|
||||
|
||||
pinMode(PIN_VBAT, INPUT);
|
||||
// Seeed's XIAO charging guidance requires READ_BAT_ENABLE to stay LOW.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "IkokaStickNRFBoard.h"
|
||||
|
||||
void IkokaStickNRFBoard::begin() {
|
||||
NRF52Board::begin();
|
||||
NRF52BoardDCDC::begin();
|
||||
|
||||
pinMode(PIN_VBAT, INPUT);
|
||||
// Seeed's XIAO charging guidance requires READ_BAT_ENABLE to stay LOW.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#ifdef LILYGO_TECHO
|
||||
|
||||
void TechoBoard::begin() {
|
||||
NRF52Board::begin();
|
||||
NRF52BoardDCDC::begin();
|
||||
|
||||
Wire.begin();
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "MeshPocket.h"
|
||||
|
||||
void HeltecMeshPocket::begin() {
|
||||
NRF52Board::begin();
|
||||
NRF52BoardDCDC::begin();
|
||||
Serial.begin(115200);
|
||||
pinMode(PIN_VBAT_READ, INPUT);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "MinewsemiME25LS01Board.h"
|
||||
|
||||
void MinewsemiME25LS01Board::begin() {
|
||||
NRF52Board::begin();
|
||||
NRF52BoardDCDC::begin();
|
||||
btn_prev_state = HIGH;
|
||||
|
||||
pinMode(PIN_VBAT_READ, INPUT);
|
||||
@@ -26,4 +26,4 @@ void MinewsemiME25LS01Board::begin() {
|
||||
#endif
|
||||
|
||||
delay(10); // give sx1262 some time to power up
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user