mirror of
https://github.com/esphome/esphome.git
synced 2026-05-28 13:37:24 +08: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:
@@ -25,6 +25,7 @@ from esphome.const import (
|
|||||||
CONF_FRAMEWORK,
|
CONF_FRAMEWORK,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_RESET_PIN,
|
CONF_RESET_PIN,
|
||||||
|
CONF_VOLTAGE,
|
||||||
KEY_CORE,
|
KEY_CORE,
|
||||||
KEY_FRAMEWORK_VERSION,
|
KEY_FRAMEWORK_VERSION,
|
||||||
KEY_TARGET_FRAMEWORK,
|
KEY_TARGET_FRAMEWORK,
|
||||||
@@ -102,6 +103,11 @@ nrf52_ns = cg.esphome_ns.namespace("nrf52")
|
|||||||
DeviceFirmwareUpdate = nrf52_ns.class_("DeviceFirmwareUpdate", cg.Component)
|
DeviceFirmwareUpdate = nrf52_ns.class_("DeviceFirmwareUpdate", cg.Component)
|
||||||
|
|
||||||
CONF_DFU = "dfu"
|
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(
|
CONFIG_SCHEMA = cv.All(
|
||||||
_detect_bootloader,
|
_detect_bootloader,
|
||||||
@@ -116,6 +122,18 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
cv.Required(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
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):
|
if dfu_config := config.get(CONF_DFU):
|
||||||
CORE.add_job(_dfu_to_code, dfu_config)
|
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)
|
@coroutine_with_priority(CoroPriority.DIAGNOSTICS)
|
||||||
async def _dfu_to_code(dfu_config):
|
async def _dfu_to_code(dfu_config):
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -287,6 +287,8 @@
|
|||||||
|
|
||||||
#ifdef USE_NRF52
|
#ifdef USE_NRF52
|
||||||
#define USE_NRF52_DFU
|
#define USE_NRF52_DFU
|
||||||
|
#define USE_NRF52_REG0_VOUT 5
|
||||||
|
#define USE_NRF52_UICR_ERASE
|
||||||
#define USE_SOFTDEVICE_ID 7
|
#define USE_SOFTDEVICE_ID 7
|
||||||
#define USE_SOFTDEVICE_VERSION 1
|
#define USE_SOFTDEVICE_VERSION 1
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -15,3 +15,6 @@ nrf52:
|
|||||||
inverted: true
|
inverted: true
|
||||||
mode:
|
mode:
|
||||||
output: true
|
output: true
|
||||||
|
reg0:
|
||||||
|
voltage: 2.1V
|
||||||
|
uicr_erase: true
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
nrf52:
|
||||||
|
reg0:
|
||||||
|
voltage: 3.3V
|
||||||
|
uicr_erase: true
|
||||||
|
|||||||
@@ -5,3 +5,5 @@ nrf52:
|
|||||||
inverted: true
|
inverted: true
|
||||||
mode:
|
mode:
|
||||||
output: true
|
output: true
|
||||||
|
reg0:
|
||||||
|
voltage: default
|
||||||
|
|||||||
Reference in New Issue
Block a user