文件
phonerail/firmware/main/chgmode.c
T
zhou yang和Claude Opus 5.5 9fb5bba367 固件 0.1.5~0.1.13-c5:刷机模式、多组 WiFi、热插拔初始化、总线锁误判、硬重启断 VBUS
- 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]>
2026-09-29 08:16:21 +08:00

111 行
3.1 KiB
C
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
/* 刷机模式的窗口表。契约与线程约定在 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;
}
}