固件 0.1.14~0.1.16-c5:Hub 按需启停(hubctl)、开机长按统一 5 s
Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -0,0 +1,205 @@
|
||||
/* Hub 按需启停。契约、判据表与退路在 hubctl.h。 */
|
||||
#include "hubctl.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "board_pins.h"
|
||||
#include "chgmode.h"
|
||||
#include "evlog.h"
|
||||
#include "hal_gpio.h"
|
||||
#include "netcfg.h"
|
||||
#include "phonerail.h"
|
||||
#include "slot.h"
|
||||
#include "t_actuator.h"
|
||||
|
||||
static hub_mode_t s_mode;
|
||||
static hub_save_fn s_save;
|
||||
static void *s_save_ctx;
|
||||
|
||||
static uint32_t s_last_need; /* 最后一次「需要」的时刻 */
|
||||
static bool s_idle; /* idle 窗口还开着(过期即清:32 位时钟 49.7 天回绕,放着不管会「复活」) */
|
||||
static uint32_t s_hold_until;
|
||||
static bool s_hold;
|
||||
static uint32_t s_down_since; /* MQTT 从什么时候开始不通 */
|
||||
static bool s_down;
|
||||
static bool s_host_prev;
|
||||
|
||||
static bool s_on;
|
||||
static hub_why_t s_why;
|
||||
|
||||
/* `a` 在 `b` 之前(回绕安全)。 */
|
||||
static bool before(uint32_t a, uint32_t b) { return (int32_t)(a - b) < 0; }
|
||||
|
||||
static void need(uint32_t now)
|
||||
{
|
||||
s_last_need = now;
|
||||
s_idle = true;
|
||||
}
|
||||
|
||||
/* 开 = 放成输入,R716 拉高;关 = 配成输出、拉低。**不主动驱高**(hubctl.h)。 */
|
||||
static hal_err_t apply(bool on)
|
||||
{
|
||||
if (on) {
|
||||
return hal_gpio_config(BOARD_PIN_HUB_RST_N, HAL_GPIO_INPUT);
|
||||
}
|
||||
hal_err_t e = hal_gpio_config(BOARD_PIN_HUB_RST_N, HAL_GPIO_OUTPUT);
|
||||
if (e == HAL_OK) {
|
||||
e = hal_gpio_write(BOARD_PIN_HUB_RST_N, 0);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
void hubctl_init(hub_mode_t mode, hub_save_fn save, void *ctx, uint32_t now)
|
||||
{
|
||||
s_mode = (mode < HUB_MODE_COUNT) ? mode : HUB_MODE_AUTO;
|
||||
s_save = save;
|
||||
s_save_ctx = ctx;
|
||||
s_hold = false;
|
||||
s_hold_until = 0;
|
||||
s_down = false;
|
||||
s_down_since = now;
|
||||
s_host_prev = false;
|
||||
need(now); /* 启动算一次「需要」:刚复位完,最可能就是有人要用 USB */
|
||||
s_on = (apply(true) == HAL_OK);
|
||||
s_why = HUB_WHY_IDLE;
|
||||
}
|
||||
|
||||
hub_why_t hub_decide(const hub_in_t *in, uint32_t now)
|
||||
{
|
||||
if (in->host && !s_host_prev) {
|
||||
need(now); /* 电脑刚插上 */
|
||||
}
|
||||
s_host_prev = in->host;
|
||||
|
||||
if (in->link_up) {
|
||||
s_down = false;
|
||||
} else if (!s_down) {
|
||||
s_down = true;
|
||||
s_down_since = now;
|
||||
}
|
||||
if (s_hold && !before(now, s_hold_until)) {
|
||||
s_hold = false;
|
||||
}
|
||||
|
||||
hub_why_t why = HUB_WHY_OFF;
|
||||
if (s_mode == HUB_MODE_ON) {
|
||||
why = HUB_WHY_MODE;
|
||||
} else if (in->vbus_any) {
|
||||
why = HUB_WHY_VBUS;
|
||||
} else if (in->flash_any) {
|
||||
why = HUB_WHY_FLASH;
|
||||
} else if (in->act_any) {
|
||||
why = HUB_WHY_ACT;
|
||||
} else if (s_hold) {
|
||||
why = HUB_WHY_HOLD;
|
||||
} else if (s_down && !before(now, s_down_since + HUB_LINK_LOST_MS)) {
|
||||
why = HUB_WHY_LINK;
|
||||
}
|
||||
if (why != HUB_WHY_OFF) {
|
||||
need(now);
|
||||
return why;
|
||||
}
|
||||
if (s_idle && before(now, s_last_need + HUB_IDLE_MS)) {
|
||||
return HUB_WHY_IDLE;
|
||||
}
|
||||
s_idle = false;
|
||||
return HUB_WHY_OFF;
|
||||
}
|
||||
|
||||
void hubctl_tick(uint32_t now)
|
||||
{
|
||||
hub_in_t in;
|
||||
memset(&in, 0, sizeof(in));
|
||||
for (int r = 1; r <= PHONERAIL_RAIL_COUNT; r++) {
|
||||
in.vbus_any = in.vbus_any || slot_vbus_is_on(r);
|
||||
in.act_any = in.act_any || t_actuator_busy(r);
|
||||
}
|
||||
in.flash_any = chgmode_count(now) > 0;
|
||||
net_status_t st;
|
||||
net_status_get(&st);
|
||||
in.link_up = (st.mqtt == NET_UP);
|
||||
int host = 0;
|
||||
in.host = (hal_gpio_read(BOARD_PIN_UP_VBUS_DET, &host) == HAL_OK) && host != 0;
|
||||
|
||||
s_why = hub_decide(&in, now);
|
||||
const bool want = (s_why != HUB_WHY_OFF);
|
||||
if (want == s_on) {
|
||||
return;
|
||||
}
|
||||
if (apply(want) != HAL_OK) {
|
||||
return; /* 引脚号写错才会走到这 —— 下一拍再试,状态不假装变了 */
|
||||
}
|
||||
s_on = want;
|
||||
evlog_put(0, LOG_EV_HUB, want ? HUB_EV_ON : HUB_EV_OFF, (int32_t)s_why, 0);
|
||||
}
|
||||
|
||||
void hubctl_touch(uint32_t now) { need(now); }
|
||||
|
||||
hal_err_t hubctl_set_mode(hub_mode_t mode)
|
||||
{
|
||||
if (mode >= HUB_MODE_COUNT) {
|
||||
return HAL_ERR_ARG;
|
||||
}
|
||||
if (mode != s_mode) {
|
||||
s_mode = mode;
|
||||
evlog_put(0, LOG_EV_HUB, (uint8_t)(HUB_EV_MODE | (uint32_t)mode), (int32_t)s_why, 0);
|
||||
}
|
||||
if (s_save != NULL) {
|
||||
s_save(s_save_ctx, mode); /* 没变也交给它:存盘函数自己判「没变不写」 */
|
||||
}
|
||||
return HAL_OK;
|
||||
}
|
||||
|
||||
hal_err_t hubctl_hold(uint32_t minutes, uint32_t now)
|
||||
{
|
||||
if (minutes > HUB_HOLD_MAX_MIN) {
|
||||
return HAL_ERR_ARG;
|
||||
}
|
||||
if (minutes == 0) {
|
||||
s_hold = false;
|
||||
s_idle = false;
|
||||
return HAL_OK;
|
||||
}
|
||||
s_hold_until = now + minutes * 60000u;
|
||||
s_hold = true;
|
||||
return HAL_OK;
|
||||
}
|
||||
|
||||
hub_mode_t hubctl_mode(void) { return s_mode; }
|
||||
bool hubctl_is_on(void) { return s_on; }
|
||||
hub_why_t hubctl_why(void) { return s_why; }
|
||||
|
||||
uint32_t hubctl_hold_left_s(uint32_t now)
|
||||
{
|
||||
if (!s_hold || !before(now, s_hold_until)) {
|
||||
return 0;
|
||||
}
|
||||
return (s_hold_until - now + 999u) / 1000u;
|
||||
}
|
||||
|
||||
uint32_t hubctl_idle_left_s(uint32_t now)
|
||||
{
|
||||
if (s_why != HUB_WHY_IDLE || !s_idle || !before(now, s_last_need + HUB_IDLE_MS)) {
|
||||
return 0;
|
||||
}
|
||||
return (s_last_need + HUB_IDLE_MS - now + 999u) / 1000u;
|
||||
}
|
||||
|
||||
static const char *const k_modes[HUB_MODE_COUNT] = { "auto", "on" };
|
||||
static const char *const k_whys[HUB_WHY_COUNT] = {
|
||||
"off", "mode", "vbus", "flash", "act", "hold", "link", "idle",
|
||||
};
|
||||
|
||||
const char *hub_mode_str(hub_mode_t m) { return (m < HUB_MODE_COUNT) ? k_modes[m] : "?"; }
|
||||
const char *hub_why_str(hub_why_t w) { return (w < HUB_WHY_COUNT) ? k_whys[w] : "?"; }
|
||||
|
||||
bool hub_mode_parse(const char *s, hub_mode_t *out)
|
||||
{
|
||||
for (int i = 0; i < HUB_MODE_COUNT; i++) {
|
||||
if (strcmp(s, k_modes[i]) == 0) {
|
||||
*out = (hub_mode_t)i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户