# 温湿度风扇固件需求规格书 | 项 | 内容 | |---|---| | 产品 | 温湿度风扇(爬宠) | | 芯片 | JL7016 / AC7016C(BR28) | | 工程 | `温湿度风扇_soc_JieLi-JL7016` | | 产品码 | ProdtCode=`CBR3`,productCode=`PRCBR3` | | 协议 | 见宝 SOC 55AA,V1.0.0 | | 文档版本 | V1.2.0 | | 日期 | 2026.08.31 | | 作者 | cyWu | | 读者 | WorkBuddy / 固件开发 | | 参考工程 | `D:\Jianbao\Project\产品\宠物\门夹弹力绳\门夹弹力绳_soc_JieLi-JL7016` | > **V1.2.0 变更(2026-08-31):** 风扇档位占空比按用户拍板改为 **1档 50%(5000/10000)、2档 75%(7500/10000)**(原 35%/65%,第 5.3/7/17/18 章同步)。 > > **V1.1.0 变更(2026-08-31):** IO 分离。CT1642 改走 **PG0(CLK)/PG1(DATA)**,AHT20 独占 **PC4(SCL)/PC5(SDA)**,两路物理隔离,**删除 `bsp_bus` 共线分时机制**(原 8.3 节整体替换为"IO 独立")。同时把 2.2 节显示协议修正为实测 **18bit 帧**。 本文是 **已拍板的共同理解**,不是草稿。实现时以本文 + `Doc\温湿度风扇DP配置.xlsx` 为准。Excel 与工程代码冲突时:**Excel 字节布局优先,本文行为优先**。 --- ## 0. WorkBuddy 开干前必读(硬约束) 1. **先改协议再写业务。** Excel 里 `cond_config1~7` 已是 **7 字节**,当前工程 `jb_protocol.*` / `dp_definition.json` 仍是 6 字节。必须按 Excel 重新生成协议,禁止继续按 6 字节解析。 2. **不要把业务算法写进 `jb_product.c`。** 只把 DP 写入 `gDevData` 并转调 `app_*`。组合根是 `usr_jb_main.c`(照门夹)。 3. **不要抄门夹的电池 / USB 直通开机 / 低电拒绝开机。** 本机 USB 常电、无电池。插上电 **不会** 自动工作,必须长按 3s。 4. **不要把调试 UART 放在 PC5。** 调试 UART TX = **PG6**(`IO_PORTG_06`)。PC5 是 AHT20 SDA(独立使用)。 5. **DP0 `power` 预留,不当开关。** 真开关只有按键 3s 软开关机。运行中上报 `power=1`,写忽略。 6. **CT1642 与 AHT20 已 IO 分离(V1.1.0)。** CT1642 走 PG0(CLK)/PG1(DATA),AHT20 走 PC4(SCL)/PC5(SDA),两路独立、无总线竞争,**不存在分时/总线锁**(见 8.3)。 7. 每个模块必须能用 `board_pin.h` 的 `BOARD_xxx_ENABLE` **单独关掉** 仍能编过、能配网上报(关掉的模块返回空操作)。 8. 日志全部 **英文**。中文只出现在注释和本文。 9. 编码风格对齐门夹 + 工程师规范:文件小写+下划线,`g_` / `s_` 前缀,枚举 `Pascal_t`,接口 `xxx_init` / `xxx_run` 或 tick / `xxx_get_status`。 --- ## 1. 产品一句话 USB 供电的爬宠温湿度风扇:本地按键开关机和调档,双两位一体数码管显示整数温湿度,BLE 连 APP;APP 可下发通风/强力模式、7 组温湿度条件、7 组定时。条件与定时仅在设备处于开机态执行;定时还必须 RTC 已对时。 --- ## 2. 硬件资源 ### 2.1 引脚(以用户原理图说明 + 本文为准) | 功能 | MCU | 电平 / 说明 | |---|---|---| | CT1642 DATA | **PG1** `IO_PORTG_01` | 推挽输出(V1.1.0 改板飞线,独立) | | CT1642 CLK | **PG0** `IO_PORTG_00` | 推挽输出(同上) | | AHT20 SDA | **PC5** `IO_PORTC_05` | 开漏+内部上拉(外部 4.7~10k 双保险),独立 | | AHT20 SCLK | **PC4** `IO_PORTC_04` | 推挽驱动,独立 | | 风扇 PWM | **PC3** `IO_PORTC_03` | **高电平导通**,MCPWM | | 状态 LED | **PC2** `IO_PORTC_02` | **低电平亮** | | 按键 | **PB1** `IO_PORTB_01` | 与门夹相同,低电平按下(若实测相反只改 `BOARD_KEY_ACTIVE_LEVEL`) | | 调试 UART TX | **PG6** `IO_PORTG_06` | 只发打印,不占用 PC5 | | USB | Type-C → LDO 3.3V | 无电池、无充电管理 | 板级文件仍指向 `CONFIG_BOARD_JL701N_DEMO`。产品 GPIO **全部集中到新建** `apps/usr_periph/board_pin.h`,并改 `board_jl701n_demo_cfg.h`: ```c #define TCFG_UART0_TX_PORT IO_PORTG_06 ``` 关掉会与 PC3/PC4/PC5 冲突的 demo 外设(SD0、HW SPI1 等)。PWM LED 走产品 `app_led`,保持 `TCFG_PWMLED_ENABLE=DISABLE`。PG0/PG1 无 demo 外设占用(`cpu/br28/mcpwm.c` 的 `io_ext_interrupt_test()` 是 while(1) 演示函数,产品不调用),可直接作 CT1642 引脚。 ### 2.2 显示器件 - 驱动:类 CT1642,CLK+DATA 串行(原理图位号 S1)。 - 两块 2 位 7 段数码管:温度一块、湿度一块(含 DP 段,本产品 **不显示小数**,DP 恒灭)。 - **帧协议(2026-08-29 实测定型,与 CSDN yitx 一致)**:一帧 **18bit**,CLK 上升沿采样 DATA,顺序 **C1-C4 位选(4bit,低有效,入参 ~com 先发 C1)→ 6bit 保留(固定填 1)→ Q2-Q9 段码(8bit,LSB 先,seg bit0 → Q2)**。 - DATA 只允许在 **CLK=低** 时改变。 - 发完 18bit:CLK=高时 DATA 上升沿 = **锁存**;CLK=低时 DATA 上升沿 = **输出刷新**(一次只点亮锁存的那 1 位)。 - CT1642 内部只有 1 个 18 位移位 + 1 个 12 位锁存,**无按位显示 RAM、不自动扫描 4 位**,必须 MCU 持续快节拍轮询(`BOARD_DISP_SCAN_MS=4`)4 位才能同亮不闪。 段码与 COM 的 PCB 网络为 `SET1~8`、`CAN1~4`。P0 必须用写死数字扫清 **哪几 bit 对应温度十位/个位、湿度十位/个位、A~G**。扫不清之前禁止把随机码当产品逻辑。 ### 2.3 AHT20 - I2C 7 位地址 **0x38**(写 0x70 / 读 0x71)。 - 上电后 ≥5ms 再操作总线。 - 测量:`0xAC 0x33 0x00`,等 ≥80ms,读 Status + 20bit 湿度 + 20bit 温度 + CRC8。 - `T = (ST / 1048576) * 200 - 50`(℃);湿度按手册 20bit 公式,显示与 DP 用 0~100 整数 %。 - 建议 I2C 100kHz;采集周期 **2s**(手册要求 >1s)。 - CRC 多项式 0x31,初值 0xFF。CRC 失败当本次采样失败。 --- ## 3. 软件架构(对齐门夹,删电池,加风扇/显示/传感) ``` apps/earphone/app_main.c └─ usr_rtc_init() └─ usr_jb_init() ← 唯一组合根 bsp_hw_init() app_led_init() ← 必须在开机等待之前 app_boot_wait_key() ← 3s/5s/12s,失败则 soft poweroff 不返回 jbInit() + userInit() 若 usr_goto_updata → OTA app_nvm_load() ← 恢复 VM app_fan_init() app_disp_init() app_th_init() app_mode_init() app_cond_init() app_timer_init() app_function_init() user_key_init() + lock_if_held usr_timer_rearm(20ms) ``` **20ms 业务节拍**(`usr_timer_tick`,与门夹相同,禁止再开 1ms ISR): 1. `jbTimerIrq()` × dt 2. `user_key_scan` + `user_key_handle` 3. `app_th_poll`(内部 2s 才真正采;IO 分离后采集与显示互不干扰) 4. `app_cond_run` / `app_timer_run` / `app_mode_run`(仲裁见第 12 章) 5. `app_fan_run` 6. `app_disp_run`(闪烁、刷新) 7. `app_led_run` 8. `app_function_poll`(同步 DP) 9. 重装定时器(按键按住 / 风扇转 / 关机流程中 → 不可睡) `usr_timer_loop()`(BLE 500ms)只做:`userHandle()` 填温湿度 DP + `jbProtocolHandle()`。 可直接复用门夹源码(拷贝后改引脚/事件表,不要改框架语义): | 复用 | 路径 | 改什么 | |---|---|---| | 按键 FSM | `key.c` / `key.h` / `key_manager.c` | 短按切档、长按关机 | | LED 事件库 | `led.c` / `led.h` / `led_manager.c` | 单灯 PC2,删红绿双通道 | | 开机判定 | `app_boot.c` | **删除 USB 直通、低电拒绝** | | 配对会话 | `app_pair.c` | 120s 超时,灯效改单灯 | | 软关机 | `app_power.c` 精简 | 无电池、无充电;只保留 request/finalize | | RTC | `usr_rtc.c` | 原样 | | 协议骨架 | `usr_jb_proto` | 重生 7 字节条件 | **不要拷贝:** `app_battery.c`、充电 GPIO、电机 `app_mode` 步进表(本产品 mode 是通风/强力,重写)。 --- ## 4. 模块开关(独立使能 / 屏蔽调试) 全部放 `apps/usr_periph/board_pin.h`。`0` 时:对应 `init/run` 直接 return,GPIO 不配,日志打一次 `xxx disabled`。 ```c #define BOARD_LED_ENABLE 1 #define BOARD_KEY_ENABLE 1 #define BOARD_FAN_ENABLE 1 #define BOARD_DISP_ENABLE 1 #define BOARD_TH_ENABLE 1 #define BOARD_MODE_ENABLE 1 /* 通风/强力状态机 */ #define BOARD_COND_ENABLE 1 /* 条件引擎;P0/P1 可先 0 */ #define BOARD_TIMER_ENABLE 1 /* 定时引擎;P0/P1 可先 0 */ #define BOARD_NVM_ENABLE 1 /* VM 持久化;P0 可 0 */ ``` 总线约定(V1.1.0:IO 独立,无共享总线): - `DISP=1, TH=0`:PG0/PG1 由显示独占,按 CT1642 时序刷;PC4/PC5 保持高阻不配。 - `DISP=0, TH=1`:PG0/PG1 不配;PC4/PC5 当软件 I2C(AHT20)。 - 都为 1:**两路各走各的**,无锁、无分时(见 8.3)。 - 都为 0:两组脚保持输入/高阻,可单独调 KEY/LED/FAN/BLE。 P0 建议先 `MODE/COND/TIMER/NVM=0`,台上只验按键、灯、PWM、数码管、传感器。 --- ## 5. 电源与按键 ### 5.1 关机 / 开机 - 关机 = `power_set_soft_poweroff()`:风扇 PWM=0 并关 MCPWM,数码管锁存灭(全段灭),LED 灭,BLE 停。 - **插 USB / 冷启动不会自动进入工作态。** 必须按住 3s。 - DP0 不参与。不要用 APP 开关机。 - 无电池逻辑。 ### 5.2 关机态按住(`app_boot.c`,用 `timer_get_ms()` 轮询,与运行态 FSM 分开) | 松开时已按住 | 行为 | |---|---| | < 3000 ms | 继续软关机 | | ≥ 3000 且 < 5000 ms | 正常开机,加载 VM,恢复上次 mode/gear 并从周期起点开跑 | | ≥ 5000 且 < 12000 ms | 开机 + `usr_clear_pair_info` + 进配对(换手机用这条) | | ≥ 12000 ms | 进 OTA(门夹 `usr_app_ota_init` 路径) | 按住过程 LED 提示(单灯): - ≥3s:常亮(将开机) - ≥5s:100ms 闪(将配对) - ≥12s:200ms 闪(将 OTA) 开机后若键仍按着:`user_key_lock_if_held()`,直到松开才接受短按/长按。 ### 5.3 开机态按键 | 事件 | 行为 | |---|---| | 短按 | 风扇档位循环:1(50%) → 2(75%) → 3(100%) → 0(关风扇) → 1 … **立刻退出通风/强力**,改为手动档。设备仍开机,数码管继续刷 | | 长按 3s | 请求软关机。VM **不清** | 配对进行中:长按关机可忽略(与门夹一致,避免配到一半睡死)。短按仍可切档。 --- ## 6. LED(仅 1 颗,PC2 低亮) | 事件 | 图案 | 优先级(高者覆盖) | |---|---|---| | 软关机 / 未开机 | 灭 | — | | 正常开机 | 常亮 | 低 | | 配对中 | 100ms 亮 / 100ms 灭,直到成功或 120s 失败 | 中 | | 配对成功 | 常亮(可保持 3s 再回到常亮,允许直接常亮) | 中 | | 配对失败 | 200ms 闪 3s,然后常亮 | 中 | | OTA | 200ms 亮 / 200ms 灭,无限 | 最高 | LED **不** 表示风扇档位,**不** 表示温湿度告警(告警走数码管闪)。 --- ## 7. 风扇 PWM - 引脚 PC3,**高有效**,频率 **20 kHz**,占空比单位 0~10000。 - 档位: | `fan_gear` | 占空比 | 说明 | |---|---|---| | 0 | 0% | 关风扇,MCPWM 可关 | | 1 | 50% | 5000/10000(用户拍板 2026-08-31) | | 2 | 75% | 7500/10000(用户拍板 2026-08-31) | | 3 | 100% | 10000/10000 | - 模块接口建议:`app_fan_init` / `app_fan_set_gear(uint8_t)` / `app_fan_get_gear` / `app_fan_run`。 - DP2 必须等于 **当前真实档位**(通风停 5 分钟那段报 0)。 - 换档无需软爬坡(P0 不做)。若 100% 上电冲击过大,允许后续加 200ms 爬升,不作为 P0 验收。 --- ## 8. 数码管 + AHT20(IO 独立) ### 8.1 显示内容 | 管子 | 内容 | 范围 | |---|---|---| | 温度 2 位 | 整数 ℃ | 0~99 显示绝对值;<0 显示 `-9`~`-1`(负号用 G 段或自定义,P0 扫段后定);<−9 钳位 `-9`;>99 钳位 `99` | | 湿度 2 位 | 整数 % | 0~99,>99 钳位 `99` | | 读失败 | 双管 `--` | 连续失败 **3** 次后;`fault_code=2` | APP DP3 仍是 0.1℃ 精度 float;显示不跟小数。 ### 8.2 告警闪烁(与吹风解耦) - 任一 **已使能** 且 `cond_en=1` 的条件组,其温度比较 **当前已成立**(**不等待** Byte4 时长)→ 温度管 **500ms 亮 / 500ms 灭**。 - 湿度同理。两路可同时闪。 - 比较不再成立 → 该侧停闪,常亮显示数值。 - 同步:`fault_code=1`(温湿度异常)。传感器故障优先于 1:失败达 3 次则 `fault_code=2` 且 `--`。 - `alarm_code` 预留,不上报具体码(保持 0)。 - `BOARD_DISP_ENABLE=0` 时不闪、不驱动管子,fault 仍可上报。 ### 8.3 IO 独立(V1.1.0,2026-08-31 改板分离) CT1642 用 **PG0(CLK)/PG1(DATA)**(推挽,`bsp_ct1642_init()` 配置),AHT20 用 **PC4(SCL)/PC5(SDA)**(`bsp_aht20_init()` 配置:PC5 内部上拉 + 外部 4.7~10k 双保险)。两路物理隔离、无总线竞争,**删除 `bsp_bus` 模块**,不存在 lock/unlock/模式切换。 运行时序: ``` 需要刷显示(每 4ms 扫描回调): 直接发帧(CT1642 单锁存,一次只亮 1 位,靠快节拍轮询 4 位同亮) 鬼影靠"值不变不刷新"或提速分时压(见 board_pin.h 注释与 bsp_ct1642.c) 需要采 AHT20(每 2s,一次完成): 发 0xAC 0x33 0x00 → 等 ≥80ms(期间 I2C 空闲,显示照常刷新,互不影响) → 读 7 字节 + CRC8 ``` 规则(V1.1.0 起): - **无总线锁**:显示扫描(priority=1 定时器 ISR)与 AHT20 采样可同时进行,不再有"采样期冻结显示"(原共线时代每 2s 闪一下的根因已物理消除)。 - `bsp_aht20_measure_start/finish` 不再返回 -2(总线忙),上层无 busy 重试。 - 等待 AHT20 的 80ms 由 `app_th_poll` 状态机跨 tick 完成(期间 `clr_wdt`),不阻塞 20ms 主节拍。 - 共线时代遗留约束(已作废,勿再套用):原"锁只包裹真实硬件交互窗口、长等待必放锁"适用于 PC4/PC5 共线方案,IO 分离后不需要。 ### 8.4 采样失败与条件计时 - 条件「持续时长」只在 **本次采样成功且比较成立** 时累加。 - 采样失败:不累加、不清零已累计时长;显示保持上次有效值,直到连续 3 次失败改 `--`。 - `--` 期间条件比较视为不成立(不误触发吹风)。 --- ## 9. 工作模式(DP1) | 值 | 名称 | 行为 | |---|---|---| | 0 | 关闭模式 | 不按模式表驱动风扇;风扇跟手动档 / 其它仲裁胜者 | | 1 | 通风 | 强制 1 档,开 **25min**,停 **5min**,循环,直到退出 | | 2 | 强力 | 强制 3 档,连续 **15min** 后:风扇 0,mode 回到 0 | 退出方式(任一即退): - 短按切档(含切到 0) - APP 写 `fan_gear` - APP 写 `mode=0` - 定时窗结束(第 11 章) - 条件恢复快照(第 10 章,若当前模式是条件开的) **开机恢复(不存相位):** VM 里 `mode=1` 则从 25min 开段 **从头** 计;`mode=2` 则从 15min **从头** 计。 DP 同步:`mode` = 逻辑模式;`fan_gear` = 实际 PWM 档(通风停段为 0)。 --- ## 10. 条件引擎(P2,7 组) ### 10.1 总开关 - `cond_en=0`:不启动新条件;若正在因条件跑模式,视为条件结束,**按 Q9 A3 恢复触发前快照**。 - `cond_en=1`:扫描 config1~7。 ### 10.2 7 字节布局(Excel 枚举列;备注里 Byte5 位域是笔误,以本表 Byte6 为准) | 偏移 | 字段 | 编码 | |---|---|---| | Byte0 | 使能 | 0=关,非 0=开 | | Byte1–2 | 温度阈值 | **有符号 int16 大端**,传输值 = ℃ × 10。例 30.5℃ → `0x01 0x31`;−5.0℃ → `0xFF 0xCE` | | Byte3 | 湿度阈值 | 0~100,单位 % | | Byte4 | 持续分钟 | 1~255 按分钟计;**0 = 比较一成立立即触发** | | Byte5 | 动作 | **模式**:0=关闭模式,1=通风,2=强力。**不是档位** | | Byte6 | 条件 | Bit7–5 温度比较,Bit4–2 湿度比较,Bit1–0 保留 | 比较编码(3 bit): | 值 | 含义 | |---|---| | 000 | 无条件(忽略该侧) | | 001 | < | | 010 | <= | | 011 | == | | 100 | >= | | 101 | > | | 110/111 | 视为无条件 | 两侧都「无条件」的组视为无效,不触发。 两侧都有效时 **AND**。一侧无条件则只判另一侧。 无回差,比较式怎么写怎么判。 ### 10.3 触发与保持 1. 组使能且 `cond_en=1`,比较成立,开始累加持续时间(采样周期 2s,满 `Byte4` 分钟)。 2. 未满时长时比较变不成立 → 该组计时清零。 3. 满时长 → **触发**:快照当前 `mode`+`fan_gear`(若尚无「条件占用」),然后切到 Byte5 模式。 4. 触发后比较仍成立 → **保持**(C2)。强力 15min 到点若仍成立 → **立即再开一轮 15min 强力**(Q16 A1),不要恢复快照。 5. 比较变不成立 → 本轮条件结束,**恢复快照**(Q9 A3),该组重新进入「需满时长」。 6. 另一组后触发 → **覆盖** 当前条件模式,**不回退条件栈**(D1)。快照仍是 **第一次条件抢权之前** 的用户/定时状态(后覆盖不改快照)。后者失效且比较不成立 → 恢复这份原始快照,即使前者比较仍成立也不自动回去(D1)。前者若要再开,必须重新满时长。 7. 手动/APP 介入:见第 12 章,本轮条件作废(RAM 标志,**不写 VM**)。必须先比较不成立,再成立并满时长,才允许再次触发。 通风由条件启动时:按 25/5 循环,直到第 5/6/7 条退出。 --- ## 11. 定时引擎(P2,7 组) ### 11.1 8 字节(Excel 已定义) | 偏移 | 字段 | |---|---| | Byte0 | 使能 | | Byte1 | 模式 0/1/2 | | Byte2 | 类型:0=单次,1=每天,2=每周 | | Byte3 | 星期位图 Bit0=周一 … Bit6=周日(仅类型 2) | | Byte4 | 开始小时 0~23 | | Byte5 | 开始分钟 0~59 | | Byte6 | 结束小时 | | Byte7 | 结束分钟 | - 支持跨午夜:开始 22:00 结束 06:00 = 当晚 22 点到次日 6 点。 - 类型 1:每天该窗。 - 类型 2:仅位图置位的星期(跨午夜的窗,以 **开始时刻所在的本地星期** 判断是否该跑)。 - 类型 0 单次:结束时刻过后把 **Byte0 清 0 并写 VM**,不再跑。 ### 11.2 RTC 门闩(Q20,强制) ``` usr_rtc_get_time() 失败 → 全部定时不执行(等同 time_en=0) 仅 BLE 已连接但从未对时 → 同上 对时成功瞬间 → 立刻补判:若当前落在某条有效窗内,进入该窗模式 ``` 无电池,拔电后 RTC 丢失。开机后必须等 APP/网关 `usr_rtc_set_from_unix` 成功。 `time_en=0`:所有窗立刻视为结束(停风扇、mode=0),直到再置 1 并重新到开始沿或补窗。 ### 11.3 窗内行为 - 进入窗:切到 Byte1 模式(通风循环或强力一轮)。 - 强力:只跑 **一轮 15min**,自行停下(mode=0,fan=0),**等到结束时刻再做一次收口**(已经停则保持停)。窗内不循环强力(Q16 B1)。 - 通风:窗内一直 25/5,直到结束时刻。 - 结束时刻:风扇 0,mode=0。 - 结束时若条件比较仍成立:**不要** 立刻让条件接手,必须重新满时长(第 12 章)。 --- ## 12. 仲裁(唯一决策表) 优先级 **高 → 低**: 1. **手动**:短按、APP 写 `fan_gear`、APP 写 `mode` 2. **定时窗**(RTC 有效且 `time_en=1` 且当前落在窗内) 3. **条件**(`cond_en=1` 且已触发保持中) 规则: | 冲突 | 胜者 | 副作用 | |---|---|---| | 手动 vs 正在跑的模式(无论谁开的) | 手动 | 立刻退模式;若原是条件开的 → 条件本轮作废(RAM) | | 手动 vs 定时窗 | 手动 | 本窗剩余时间不再自动抢回;等到下一窗开始沿或下次对时补窗 | | 定时窗 vs 条件 | 定时 | 条件即使已满时长也不开;窗结束后条件要重新计时长 | | 条件 vs 空闲 | 条件 | 快照空闲态的 mode/gear | | 两路条件 | 后触发覆盖 | 不回栈 | 作废标志不存 VM。冷启动后条件从 0 重新计时(即使拔电前用户刚按停)。 `cond_en`/`time_en` 运行中被写成 0:立刻停对应自动化;条件停 → 恢复快照;定时停 → 窗结束语义。 --- ## 13. 持久化(VM) 存(冷启动 / 软关都保留): - `mode`、`fan_gear` - `cond_en`、`cond_config1~7`(各 7B) - `time_en`、`time_config1~7`(各 8B) - `fault_en`、`alarm_en`(若有写入) 不存: - DP0(忽略) - 条件作废标志、模式已运行秒数、AHT20 原始采样 - BLE 三元组(走现有 `CFG_USER_PAIR_INFO`,出厂重置 **不清**) 开机 3s 成功后:加载上述字段,**按上次 mode/gear 从周期起点继续工作**。 首次上电(VM 无效):mode=0,gear=0,条件/定时全 0,`cond_en=time_en=0`。 写 VM 时机:APP 改配置后、单次定时自毁使能时、软关机前、档位/模式稳定变化时(可做 500ms 防抖,避免短按循环打爆 Flash)。 --- ## 14. 出厂重置(DP25 写 1) - **清全部业务 VM**(第 13 章列表)。 - **保留 BLE 配对,不断开、不软复位。** - RAM 中 mode=0,gear=0,风扇停,条件/定时停止。 - 换机:用户关机后 **长按 5s** 清配对进绑定广播。 --- ## 15. BLE / DP 行为摘要 身份:`CBR3` / `PRCBR3`。帧格式见已生成 `jb_protocol.h`(重生后以新头文件为准)。 | DP | 处理 | |---|---| | 0 power | 预留。运行中上报 1,写忽略 | | 1 mode | 写则手动抢权,切通风/强力/关 | | 2 fan_gear | 写则手动抢权,退模式 | | 3/4 温湿度 | 仅上报。变化防抖 2s,全量 10min(协议已有) | | 5 cond_en | 见第 10/12 章 | | 6~12 cond_config | 7 字节 memcpy + 存 VM + 条件状态机复位该组计时 | | 13 time_en | 见第 11 章 | | 14~20 time_config | 8 字节 + 存 VM | | 21/23 en | 原样存,P0 不做位级过滤 | | 22 fault_code | 0 无 / 1 温湿度比较异常 / 2 传感器故障 | | 24 alarm_code | 保持 0 | | 25 factory_reset | 第 14 章 | | 26~28 | 存 RAM/VM 可不实现动作 | OTA:12s 开机走门夹 `usr_app_ota_init`。`jbOnOtaNotify` 等协议 OTA **P2 可继续 stub**,注释写明未实现,禁止假装升级成功后擦用户区。 配对超时 120s,失败保持开机。 --- ## 16. 建议文件清单(新建,门夹已有的拷贝改) ``` apps/usr_periph/ board_pin.h # 引脚、时序、BOARD_xxx_ENABLE bsp_hw.c/.h # LED/KEY/FAN GPIO+MCPWM、sleep IO bsp_ct1642.c/.h # PG0/PG1,移位、锁存、送出 bsp_aht20.c/.h # PC4/PC5,软件 I2C + 测量 usr_rtc.c/.h # 已有则保留 apps/usr_app/ key.c/.h, key_manager.c/.h # 拷贝门夹 led.c/.h, led_manager.c/.h # 改单灯 app_boot.c/.h # 拷贝后删 USB/电池 app_led.c/.h app_power.c/.h # 精简 app_pair.c/.h app_fan.c/.h app_disp.c/.h # 数值、闪烁、-- app_th.c/.h # 2s 采样、失败计数 app_mode.c/.h # 通风/强力(重写,勿用门夹电机表) app_cond.c/.h # P2 app_timer_task.c/.h # P2,避免与 usr_rtc 文件名混 app_nvm.c/.h app_function.c/.h # DP 与按键总装 app_arbiter.c/.h # 可选:把第 12 章收成一处,避免 mode/cond/timer 互调成环 ``` `usr_jb_main.c` 只编排,不写算法。 --- ## 17. 分阶段交付与验收 ### P0 — 台上 Bring-up(不依赖 APP) **目标:** 两路 IO(CT1642 显示 / AHT20 采集)、PWM、按键、灯可独立开关验证。 - [ ] `board_pin.h` + UART TX=PG6,打印正常,且不影响 PC5 - [ ] 关机态 3s 开机、<3s 仍睡;开机态 3s 关机 - [ ] 短按四态循环,示波器/听感确认 50/75/100/0% - [ ] LED 开机常亮、模拟配对闪、模拟 OTA 闪(可用宏强制) - [ ] CT1642 两管能稳定显示写死的 `25` 和 `60`,无鬼影 - [ ] AHT20 串口打印 ℃/%,CRC 正确;采数时数码管不闪、不乱码(IO 独立,无需锁存) - [ ] `BOARD_TH_ENABLE=0` 时显示仍可刷;`BOARD_DISP_ENABLE=0` 时传感器仍可打印 - [ ] 12s 能进 OTA 广播(可先只打日志 + LED) **P0 关闭:** `BOARD_MODE/COND/TIMER/NVM_ENABLE=0` ### P1 — 最小产品闭环 - [ ] 5s 配对,APP 能读温湿度,能写档位/模式 - [ ] 通风 25/5、强力 15min 后停;短按立即退模式 - [ ] 超限闪(可用调试宏灌假温度)、失败 `--` - [ ] DP0 写 0 风扇不变、设备不关 - [ ] VM:改档/改模式/写下条件定时后断电,3s 开机从周期头恢复 mode - [ ] 定时引擎可只存不跑(`BOARD_TIMER_ENABLE=0` 仍能保存 DP) ### P2 — 自动化 - [ ] 条件 7 字节解析与 AND、时长、快照恢复、后覆盖不回栈 - [ ] 条件强力循环 15min(仍超限再来一轮) - [ ] 手动作废条件,须回安全再满时长 - [ ] 定时 RTC 门闩:未对时绝不跑;对时后补窗 - [ ] 跨午夜、单次自毁使能 - [ ] 定时强力只一轮;窗结束收口;结束后条件重新计时 - [ ] 出厂重置清业务留配对 - [ ] 仲裁表第 12 章逐条用例(见下) ### P2 仲裁最小用例(必须写进测试笔记) 1. 手动 2 档 → 条件触发通风 → 应切通风;短按到 0 → 风扇停且保持停,即使仍超限。 2. 用例 1 之后温湿度回到安全再超限,满时长才再次通风。 3. 定时窗启动强力 → 15min 停 → 窗未结束前保持停 → 结束时刻仍停。 4. 定时窗内再短按切 1 档 → 保持 1 档到窗结束也不要被定时抢回。 5. 未对时,把设备时钟拨乱或不对时 → 定时配置使能也不转。对时后若在窗内立即进模式。 6. 两组条件先后触发,后者强力覆盖通风;后者失效恢复 **手动快照** 而不是第一组通风。 --- ## 18. `board_pin.h` 时序宏(实现时抄这份数) ```c #define BOARD_UART_TX_PIN IO_PORTG_06 #define BOARD_DISP_CLK_PIN IO_PORTG_00 #define BOARD_DISP_DATA_PIN IO_PORTG_01 #define BOARD_TH_SCL_PIN IO_PORTC_04 #define BOARD_TH_SDA_PIN IO_PORTC_05 #define BOARD_FAN_PIN IO_PORTC_03 #define BOARD_LED_PIN IO_PORTC_02 #define BOARD_KEY_PIN IO_PORTB_01 #define BOARD_LED_ON_LEVEL 0 #define BOARD_FAN_ON_LEVEL 1 #define BOARD_KEY_ACTIVE_LEVEL 0 #define BOARD_KEY_POWER_MS 3000 #define BOARD_KEY_PAIR_MS 5000 #define BOARD_KEY_OTA_MS 12000 #define BOARD_PAIR_TIMEOUT_MS (120u * 1000u) #define BOARD_LED_FAST_ON_MS 100 #define BOARD_LED_FAST_OFF_MS 100 #define BOARD_LED_OTA_ON_MS 200 #define BOARD_LED_OTA_OFF_MS 200 #define BOARD_DISP_BLINK_MS 500 #define BOARD_FAN_PWM_HZ 20000 #define BOARD_FAN_DUTY_GEAR1 5000 #define BOARD_FAN_DUTY_GEAR2 7500 #define BOARD_FAN_DUTY_GEAR3 10000 #define BOARD_TH_PERIOD_MS 2000 #define BOARD_TH_FAIL_MAX 3 #define BOARD_AHT20_WAIT_MS 80 #define BOARD_MODE_VENT_ON_MS (25u * 60u * 1000u) #define BOARD_MODE_VENT_OFF_MS (5u * 60u * 1000u) #define BOARD_MODE_POWER_ON_MS (15u * 60u * 1000u) ``` --- ## 19. 风险与不要擅自发挥的点 | 风险 | 要求 | |---|---| | Excel 备注 Byte5 vs 枚举 Byte5/6 | 以本文第 10.2 节为准 | | CT1642 鬼影/异显微亮 | IO 独立不解决:单锁存 + 移位实时输出的固有特性,软件靠"值不变不刷新"/提速分时压;残余微亮需 Q2-Q9 加 1~10k 硬件下拉 | | 10k 上拉偏弱 | 软件 I2C 放慢;不行再提硬件改 4.7k(SDA 建议外部 4.7k) | | 条件强力循环可能把箱体吹干 | 已拍板(Q16 A1),不要擅自改成通风兜底 | | 门夹 USB 插入直通开机 | **禁止** 移植 | | 协议 OTA stub 返回 OK | 注释标明未升级;P2 不要装作已刷写 | | `Makefile` 引用缺失 board 文件 | 产品只编实际存在的 `board_jl701n_demo.c` | | 负温度段码 | P0 扫段后在 `app_disp` 注释画真值表 | --- ## 20. 给 WorkBuddy 的推荐开工顺序 1. 按 Excel 重生 7 字节 `jb_protocol` / `dp_definition.json`。 2. 新建 `board_pin.h`,UART 改 PG6,关掉 PC3/4/5 上的 demo 外设。 3. 移植 key/led/boot/power/pair(删电池与 USB 直通)。 4. `bsp_hw` 风扇 PWM + LED + KEY。 5. `bsp_ct1642`(PG0/PG1)写死 25/60。 6. `bsp_aht20` 串口打印,再接到显示。 7. P1:`app_mode` + DP 同步 + NVM。 8. P2:`app_cond` + `app_timer_task` + 仲裁用例。 每一步都保持对应 `BOARD_xxx_ENABLE` 可关。 --- **文档结束。** 行为争议以本文第 5~12 章为准;字节布局以第 10.2 / 11.1 节与 Excel「枚举/值定义」列为准。