# 交接 更新:2026-09-06 完成任务包 **A2**(HAL 接口 + sim 实现) ## 一句话现状 四路 HAL(i2c / gpio / pwm / time)的接口定死了,两套实现都在: 宿主机链 `sim/`(假 I²C 器件 + 虚拟时钟 + 故障注入),ESP32-S3 链 `esp/`(IDF v5.4 新版 `i2c_master` / gpio / LEDC / esp_timer)。3 条 ctest(16 个用例)全绿,`idf.py build` 零警告。 ## 下一步 **A3**:INA226 驱动 + 六片地址表(`0x40/41/44/45/42/43`,设计文档 §8.2)。 驱动只准通过 `hal_i2c_*` 访问总线;测试用 `sim_i2c_add(addr, 2)` 挂假器件、 `sim_i2c_set_reg()` 预置值。**16 位寄存器是大端**,sim 已按大端实现。 ## 本次新增/修改的文件 - `components/phonerail_hal/include/hal_{err,i2c,gpio,pwm,time}.h` — 接口(唯一的对外契约) - `components/phonerail_hal/hal_err.c` — 两个目标共用的错误码字符串 - `components/phonerail_hal/esp/hal_{i2c,gpio,pwm,time}_esp.c` — ESP-IDF 实现 - `components/phonerail_hal/sim/sim_hal.h` — **假板子的控制面**(应用代码不许包含) - `components/phonerail_hal/sim/{sim_hal,hal_i2c_sim,hal_gpio_sim,hal_pwm_sim,hal_time_sim}.c` - `components/phonerail_hal/CMakeLists.txt` — IDF 组件(只收 `esp/`,不收 `sim/`) - `test/test_hal_i2c_sim.c`、`test/test_hal_sim.c` — 本包的测试 - `host/CMakeLists.txt` — 加 `PHONERAIL_HAL_SIM_SRCS` 与两个 include 目录 - `main/CMakeLists.txt` — `REQUIRES phonerail_hal`;`main/app_main.c` 调一次 HAL 保证链接 - `../固件开发计划.md` §4.1 — 记下组件重名那个坑 ## 验收命令与结果 ``` $ bash host/test.sh 100% tests passed, 0 tests failed out of 3 # test_hal_i2c_sim / test_hal_sim / test_version $ .\build_idf.ps1 # PowerShell phonerail.bin binary size 0x32550 bytes. ... 0xcdab0 bytes (80%) free. Project build complete. ``` ## 踩到的坑(只写会再犯的) - **组件名不能叫 `hal`**:ESP-IDF 自己有 `components/hal`(`driver` 依赖它), 工程里同名会把它盖掉。所以叫 `phonerail_hal`。头文件名 `hal_*.h` 不冲突。 - **新增 HAL 实现要改两处 CMake**:`components/phonerail_hal/CMakeLists.txt`(esp 那份) 和 `host/CMakeLists.txt` 的 `PHONERAIL_HAL_SIM_SRCS`(sim 那份)。漏一处 = 「宿主机绿了但上板编不过」,反之亦然。`main/` 里的 .c 仍是老规矩的另外两处。 - **IDF 组件的 `SRCS` 是显式列举**,所以 `sim/` 放在组件目录里也不会被 IDF 编到。 ## HAL 的几条约定(写驱动前看一眼,省得回头改) - 16 位 I²C 寄存器一律**大端**;写事务的第一个字节是寄存器指针,指针**跨事务保持**并自增。 - 读写长度不是 `reg_width` 整数倍 → `HAL_ERR_ARG`(真器件上是未定义行为,不许悄悄凑合)。 - 器件不在 / `present=false` / 注入 NAK → `HAL_ERR_NAK`;**没 `hal_i2c_init()` 就用 → `HAL_ERR_STATE`**。 - **写一个没配成输出的 GPIO 返回 `HAL_ERR_ARG`**(板子上 `gpio_set_level()` 是静默无效的)。 `HAL_GPIO_INPUT_PULLUP` 配完默认读回 **1** —— 五路 FLT/PG 是开漏 + 上拉,默认 0 会被当成一直有故障。 - PWM 占空比单位是**千分比 0~1000**,应用层不需要知道 LEDC 是几位。 - 时刻比较一律用 `hal_time_elapsed_ms()`,不要写 `now - then > x`(32 位毫秒 49.7 天回绕)。 - sim 的时钟是虚拟的:`hal_time_delay_ms()` 只把时间往前拨,测试零耗时。 每个测试 `setUp()` 里调 `sim_hal_reset()`。