- 0.1.5/0.1.6:刷机模式(flash_mode,最多 2 路,60 分钟可加钟)——窗口内顶高按 4450 mV 绝对值判、不断 VBUS - 0.1.7/0.1.8:反灌断 VBUS 阈值放宽到 -300 mA(adb reboot bootloader 过渡实测 -208 mA) - 0.1.9:WiFi 最多存 4 组,连不上轮换;NVS v1 自动迁移并双写(OTA 回滚仍能上网) - 0.1.10:热插拔进来的刀片重新初始化 PCA9555 + INA226(否则 VBUS 常开、电流恒读 0) - 0.1.11:i2cbus 嵌套检测改看本任务持锁(原全局标志让 key 随机回 busy、vbus 随机回 io) - 0.1.12:硬重启先断 VBUS,开机校验通过 60 s 后再接回(否则假重启 / 误判没开机 / 进关机充电) - 0.1.13:这一路被安全判据断电时一并退出刷机模式(事件 21 arg 254) 26 ctest 全绿;新增用例均先在旧代码上确认变红。板 B 已 OTA 到 0.1.13-c5 并实测。 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
111 行
3.1 KiB
C
111 行
3.1 KiB
C
/* 刷机模式的窗口表。契约与线程约定在 chgmode.h。 */
|
||
#include "chgmode.h"
|
||
|
||
#include "phonerail.h"
|
||
|
||
typedef struct {
|
||
volatile uint32_t until; /* 窗口截止时刻(ms,回绕安全比较) */
|
||
volatile uint8_t set; /* 1 = 窗口或宽限期还没被 tick 清掉 */
|
||
uint8_t by_cmd; /* 1 = 命令关掉的(已记过事件,tick 不再报「到期」);只有执行器任务读写 */
|
||
} chg_t;
|
||
|
||
static chg_t s_chg[PHONERAIL_RAIL_COUNT];
|
||
|
||
static bool rail_ok(int rail) { return rail >= 1 && rail <= PHONERAIL_RAIL_COUNT; }
|
||
|
||
/* `a` 在 `b` 之前(回绕安全)。 */
|
||
static bool before(uint32_t a, uint32_t b) { return (int32_t)(a - b) < 0; }
|
||
|
||
bool chgmode_active(int rail, uint32_t now)
|
||
{
|
||
return rail_ok(rail) && s_chg[rail - 1].set && before(now, s_chg[rail - 1].until);
|
||
}
|
||
|
||
bool chgmode_relaxed(int rail, uint32_t now)
|
||
{
|
||
return rail_ok(rail) && s_chg[rail - 1].set &&
|
||
before(now, s_chg[rail - 1].until + CHG_MODE_GRACE_MS);
|
||
}
|
||
|
||
uint32_t chgmode_left_s(int rail, uint32_t now)
|
||
{
|
||
if (!chgmode_active(rail, now)) {
|
||
return 0;
|
||
}
|
||
return (s_chg[rail - 1].until - now + 999u) / 1000u;
|
||
}
|
||
|
||
int chgmode_count(uint32_t now)
|
||
{
|
||
int n = 0;
|
||
for (int r = 1; r <= PHONERAIL_RAIL_COUNT; r++) {
|
||
n += chgmode_active(r, now) ? 1 : 0;
|
||
}
|
||
return n;
|
||
}
|
||
|
||
hal_err_t chgmode_set(int rail, uint32_t minutes, uint32_t now)
|
||
{
|
||
if (!rail_ok(rail) || minutes > CHG_MODE_MAX_MIN) {
|
||
return HAL_ERR_ARG;
|
||
}
|
||
chg_t *c = &s_chg[rail - 1];
|
||
if (minutes == 0) {
|
||
if (chgmode_active(rail, now)) {
|
||
c->until = now; /* 立刻结束窗口,宽限期从现在算 */
|
||
c->by_cmd = 1;
|
||
}
|
||
return HAL_OK;
|
||
}
|
||
if (!chgmode_active(rail, now) && chgmode_count(now) >= CHG_MODE_MAX_RAILS) {
|
||
return HAL_ERR_STATE;
|
||
}
|
||
/* 先写时刻、后置标志:巡检任务读到 set=1 时 until 一定是新的。 */
|
||
c->until = now + minutes * 60000u;
|
||
c->by_cmd = 0;
|
||
c->set = 1;
|
||
return HAL_OK;
|
||
}
|
||
|
||
hal_err_t chgmode_extend(int rail, uint32_t minutes, uint32_t now)
|
||
{
|
||
if (!rail_ok(rail) || minutes < 1 || minutes > CHG_MODE_MAX_MIN) {
|
||
return HAL_ERR_ARG;
|
||
}
|
||
if (!chgmode_active(rail, now)) {
|
||
return HAL_ERR_STATE;
|
||
}
|
||
chg_t *c = &s_chg[rail - 1];
|
||
const uint32_t cap = CHG_MODE_MAX_MIN * 60000u;
|
||
uint32_t left = c->until - now + minutes * 60000u; /* 窗口内 until − now 为正 */
|
||
if (left > cap) {
|
||
left = cap;
|
||
}
|
||
c->until = now + left; /* set 已是 1;单字写,巡检看到的要么旧要么新 */
|
||
return HAL_OK;
|
||
}
|
||
|
||
uint32_t chgmode_tick(uint32_t now)
|
||
{
|
||
uint32_t ended = 0;
|
||
for (int r = 1; r <= PHONERAIL_RAIL_COUNT; r++) {
|
||
chg_t *c = &s_chg[r - 1];
|
||
if (c->set && !before(now, c->until + CHG_MODE_GRACE_MS)) {
|
||
c->set = 0;
|
||
if (!c->by_cmd) {
|
||
ended |= 1u << (r - 1);
|
||
}
|
||
}
|
||
}
|
||
return ended;
|
||
}
|
||
|
||
void chgmode_reset(void)
|
||
{
|
||
for (int r = 0; r < PHONERAIL_RAIL_COUNT; r++) {
|
||
s_chg[r].set = 0;
|
||
s_chg[r].until = 0;
|
||
s_chg[r].by_cmd = 0;
|
||
}
|
||
}
|