文件

122 行
5.5 KiB
C

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
/* Hub(U32 USB2517I)按需启停(0.1.14,2026-09-29 用户定,推翻 R2 D13 代价 2 / R-11 的
「运行期不许动 `HUB_RST#`」)。
为什么要有它:五台手机的 VBUS 平时全关,但 Hub 只要上行插着电脑就一直是活动态 ——
ESP32 自己的 USB-Serial-JTAG 挂在第 6 口上,电脑永远不会把它挂起。
2026-09-29 板 B 实测 Hub 工作 vs 按住复位差 1.03 W(12 V 输入差约 88 mA),而这 1 W 大部分时间什么都没在干。
把 `RESET_N`(43 脚)拉低 ⇒ Hub 从电脑那边消失,PHY 与 24 MHz 晶振都停,只剩 U31 的静态电流。
---- 什么时候开(任一条成立就开,并刷新「最后一次需要」的时刻)-------------
| why | 条件 |
|---------|------|
| mode | 模式 = 常开(`hub` 命令,存 NVS) |
| vbus | 任一路 VBUS 通着 —— 手机的 USB 只有这时才在 Hub 上 |
| flash | 任一路刷机模式窗口内(chgmode.h) |
| act | 任一路执行器在跑序列(EDL 要枚举 9008;硬重启中途 VBUS 暂时是关的) |
| hold | `hub` 命令给的保持窗口内(要用 ESP32 的 USB 控制台 / 烧录) |
| link | MQTT 连续 HUB_LINK_LOST_MS 没连上(含从没配过网)⇒ **USB 是唯一的路,必须开** |
| idle | 以上都不成立,但离「最后一次需要」还不到 HUB_IDLE_MS |
「最后一次需要」还会被三件事刷新:启动、上行电脑插上(`UP_VBUS_DET` 上升沿)、
USB 链路上来了一条命令 —— 都是「有人正要用 USB」的信号。
全不成立 ⇒ 关(`HUB_RST#` 拉低)。
---- 退路(这是推翻 R-11 的前提,一条都不许删)-----------------------------
1. `link`:WiFi/MQTT 断了 ⇒ 1 分钟内 Hub 自己回来,USB 控制台可用;
2. ESP32 任何一次复位(上电 / 看门狗 / esp_restart / esptool / J33 EN)引脚都回到高阻,
R716 把 Hub 拉出复位,而且启动算一次「需要」⇒ 至少 HUB_IDLE_MS 的窗口;
3. 板子断电重上 = 第 2 条;
4. J33 UART 应急口不经过 Hub,永远在。
⚠️ 只许**拉低**或**放成输入**(靠 R716 拉高),不许主动驱高 —— 和 bringup 的脉冲同一口径:
放开 = 高阻,ESP32 这一侧出任何事 Hub 都停在「工作」那一边。
---- 线程 ----------------------------------------------------------------
**只有执行器任务写**(`hubctl_tick()` 在执行器循环里、命令处理器也在那个任务里),
status 跨任务读几个单字,遥测无害(cmd.h 同一条)。 */
#ifndef HUBCTL_H
#define HUBCTL_H
#include <stdbool.h>
#include <stdint.h>
#include "hal_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HUB_IDLE_MS (10u * 60000u) /* 最后一次需要之后再开多久 */
#define HUB_LINK_LOST_MS 60000u /* MQTT 断多久算「只剩 USB 这条路」 */
#define HUB_HOLD_MAX_MIN 240
/* 模式。⚠️ 进 NVS,冻结。 */
typedef enum {
HUB_MODE_AUTO = 0,
HUB_MODE_ON = 1,
HUB_MODE_COUNT,
} hub_mode_t;
/* 为什么开着 / 关着。⚠️ 进事件(LOG_EV_HUB 的 bus_mv),冻结,只许在末尾追加。 */
typedef enum {
HUB_WHY_OFF = 0,
HUB_WHY_MODE = 1,
HUB_WHY_VBUS = 2,
HUB_WHY_FLASH = 3,
HUB_WHY_ACT = 4,
HUB_WHY_HOLD = 5,
HUB_WHY_LINK = 6,
HUB_WHY_IDLE = 7,
HUB_WHY_COUNT,
} hub_why_t;
/* LOG_EV_HUB 的 arg。⚠️ 进 flash,冻结。 */
#define HUB_EV_OFF 0u
#define HUB_EV_ON 1u
#define HUB_EV_MODE 0x10u /* | 新模式 */
/* 一拍的输入(`hubctl_tick()` 自己采;宿主机测试直接喂 `hub_decide()`)。 */
typedef struct {
bool vbus_any;
bool flash_any;
bool act_any;
bool link_up;
bool host; /* UP_VBUS_DET */
} hub_in_t;
/* 模式存盘回调(app_main 写 NVS)。NULL = 不存(宿主机测试)。 */
typedef void (*hub_save_fn)(void *ctx, hub_mode_t mode);
/* 起步:记下模式、把 `HUB_RST#` 放成输入(Hub 工作),启动算一次「需要」。
模式越界按 AUTO。 */
void hubctl_init(hub_mode_t mode, hub_save_fn save, void *ctx, uint32_t now);
/* 纯判定:这一拍 Hub 该不该开、为什么。会推进内部的时刻(最后一次需要 / 断线起点 / 上行边沿),
**不碰引脚**。宿主机测试用它。 */
hub_why_t hub_decide(const hub_in_t *in, uint32_t now);
/* 执行器循环每拍调:采输入 → `hub_decide()` → 状态变了才动引脚、记事件。 */
void hubctl_tick(uint32_t now);
/* USB 链路上来了一条命令(cmd.c 调):算一次「需要」。 */
void hubctl_touch(uint32_t now);
/* 命令:改模式(存盘、记事件)。越界 HAL_ERR_ARG。 */
hal_err_t hubctl_set_mode(hub_mode_t mode);
/* 命令:保持 minutes 分钟(1~HUB_HOLD_MAX_MIN,覆盖原窗口);0 = 取消保持**并结束 idle 窗口**
(没有别的理由就下一拍关 —— 实测省电用)。越界 HAL_ERR_ARG。 */
hal_err_t hubctl_hold(uint32_t minutes, uint32_t now);
hub_mode_t hubctl_mode(void);
bool hubctl_is_on(void); /* 引脚此刻放着(Hub 在工作) */
hub_why_t hubctl_why(void); /* 上一拍的结论 */
uint32_t hubctl_hold_left_s(uint32_t now);
uint32_t hubctl_idle_left_s(uint32_t now); /* why == idle 时离自动关还有几秒,否则 0 */
const char *hub_mode_str(hub_mode_t m);
const char *hub_why_str(hub_why_t w);
bool hub_mode_parse(const char *s, hub_mode_t *out);
#ifdef __cplusplus
}
#endif
#endif /* HUBCTL_H */