1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-16 06:45:48 +00:00

[nrf52,gpio] add gpio levels for high voltage mode (#9858)

Co-authored-by: J. Nick Koston <nick+github@koston.org>
This commit is contained in:
tomaszduda23
2025-11-11 16:17:25 +01:00
committed by GitHub
parent 7a700ca077
commit a6b7c1f18c
6 changed files with 147 additions and 0 deletions

View File

@@ -25,6 +25,7 @@ from esphome.const import (
CONF_FRAMEWORK,
CONF_ID,
CONF_RESET_PIN,
CONF_VOLTAGE,
KEY_CORE,
KEY_FRAMEWORK_VERSION,
KEY_TARGET_FRAMEWORK,
@@ -102,6 +103,11 @@ nrf52_ns = cg.esphome_ns.namespace("nrf52")
DeviceFirmwareUpdate = nrf52_ns.class_("DeviceFirmwareUpdate", cg.Component)
CONF_DFU = "dfu"
CONF_REG0 = "reg0"
CONF_UICR_ERASE = "uicr_erase"
VOLTAGE_LEVELS = [1.8, 2.1, 2.4, 2.7, 3.0, 3.3]
DEFAULT_VOLTAGE_LEVEL = "default"
CONFIG_SCHEMA = cv.All(
_detect_bootloader,
@@ -116,6 +122,18 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_RESET_PIN): pins.gpio_output_pin_schema,
}
),
cv.Optional(CONF_REG0): cv.Schema(
{
cv.Required(CONF_VOLTAGE): cv.Any(
cv.All(
cv.voltage,
cv.one_of(*VOLTAGE_LEVELS, float=True),
),
cv.one_of(*[DEFAULT_VOLTAGE_LEVEL], lower=True),
),
cv.Optional(CONF_UICR_ERASE, default=False): cv.boolean,
}
),
}
),
)
@@ -183,6 +201,14 @@ async def to_code(config: ConfigType) -> None:
if dfu_config := config.get(CONF_DFU):
CORE.add_job(_dfu_to_code, dfu_config)
if reg0_config := config.get(CONF_REG0):
value = 7 # DEFAULT_VOLTAGE_LEVEL
if reg0_config[CONF_VOLTAGE] in VOLTAGE_LEVELS:
value = VOLTAGE_LEVELS.index(reg0_config[CONF_VOLTAGE])
cg.add_define("USE_NRF52_REG0_VOUT", value)
if reg0_config[CONF_UICR_ERASE]:
cg.add_define("USE_NRF52_UICR_ERASE")
@coroutine_with_priority(CoroPriority.DIAGNOSTICS)
async def _dfu_to_code(dfu_config):

View File

@@ -0,0 +1,110 @@
#include "esphome/core/defines.h"
#ifdef USE_NRF52_REG0_VOUT
#include <zephyr/init.h>
#include <hal/nrf_power.h>
#include <zephyr/sys/printk.h>
extern "C" {
void nvmc_config(uint32_t mode);
void nvmc_wait();
nrfx_err_t nrfx_nvmc_uicr_erase();
}
namespace esphome::nrf52 {
enum class StatusFlags : uint8_t {
OK = 0x00,
NEED_RESET = 0x01,
NEED_ERASE = 0x02,
};
constexpr StatusFlags &operator|=(StatusFlags &a, StatusFlags b) {
a = static_cast<StatusFlags>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b));
return a;
}
constexpr bool operator&(StatusFlags a, StatusFlags b) {
return (static_cast<uint8_t>(a) & static_cast<uint8_t>(b)) != 0;
}
static bool regout0_ok() {
return (NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) == (USE_NRF52_REG0_VOUT << UICR_REGOUT0_VOUT_Pos);
}
static StatusFlags set_regout0() {
/* If the board is powered from USB (high voltage mode),
* GPIO output voltage is set to 1.8 volts by default.
*/
if (!regout0_ok()) {
nvmc_config(NVMC_CONFIG_WEN_Wen);
NRF_UICR->REGOUT0 =
(NRF_UICR->REGOUT0 & ~((uint32_t) UICR_REGOUT0_VOUT_Msk)) | (USE_NRF52_REG0_VOUT << UICR_REGOUT0_VOUT_Pos);
nvmc_wait();
nvmc_config(NVMC_CONFIG_WEN_Ren);
return regout0_ok() ? StatusFlags::NEED_RESET : StatusFlags::NEED_ERASE;
}
return StatusFlags::OK;
}
#ifndef USE_BOOTLOADER_MCUBOOT
// https://github.com/adafruit/Adafruit_nRF52_Bootloader/blob/6a9a6a3e6d0f86918e9286188426a279976645bd/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h#L61
constexpr uint32_t BOOTLOADER_REGION_START = 0x000F4000;
constexpr uint32_t BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS = 0x000FE000;
static bool bootloader_ok() {
return NRF_UICR->NRFFW[0] == BOOTLOADER_REGION_START && NRF_UICR->NRFFW[1] == BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS;
}
static StatusFlags fix_bootloader() {
if (!bootloader_ok()) {
nvmc_config(NVMC_CONFIG_WEN_Wen);
NRF_UICR->NRFFW[0] = BOOTLOADER_REGION_START;
NRF_UICR->NRFFW[1] = BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS;
nvmc_wait();
nvmc_config(NVMC_CONFIG_WEN_Ren);
return bootloader_ok() ? StatusFlags::NEED_RESET : StatusFlags::NEED_ERASE;
}
return StatusFlags::OK;
}
#endif
static StatusFlags set_uicr() {
StatusFlags status = StatusFlags::OK;
status |= set_regout0();
#ifndef USE_BOOTLOADER_MCUBOOT
status |= fix_bootloader();
#endif
return status;
}
static int board_esphome_init() {
StatusFlags status = set_uicr();
#ifdef USE_NRF52_UICR_ERASE
if (status & StatusFlags::NEED_ERASE) {
nrfx_err_t ret = nrfx_nvmc_uicr_erase();
if (ret != NRFX_SUCCESS) {
#ifdef CONFIG_PRINTK
printk("nrfx_nvmc_uicr_erase failed %d\n", ret);
#endif
} else {
status |= set_uicr();
}
}
#endif
if (status & StatusFlags::NEED_RESET) {
/* a reset is required for changes to take effect */
NVIC_SystemReset();
}
return 0;
}
} // namespace esphome::nrf52
static int board_esphome_init() { return esphome::nrf52::board_esphome_init(); }
SYS_INIT(board_esphome_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
#endif

View File

@@ -287,6 +287,8 @@
#ifdef USE_NRF52
#define USE_NRF52_DFU
#define USE_NRF52_REG0_VOUT 5
#define USE_NRF52_UICR_ERASE
#define USE_SOFTDEVICE_ID 7
#define USE_SOFTDEVICE_VERSION 1
#endif

View File

@@ -15,3 +15,6 @@ nrf52:
inverted: true
mode:
output: true
reg0:
voltage: 2.1V
uicr_erase: true

View File

@@ -0,0 +1,4 @@
nrf52:
reg0:
voltage: 3.3V
uicr_erase: true

View File

@@ -5,3 +5,5 @@ nrf52:
inverted: true
mode:
output: true
reg0:
voltage: default