From 489cd6a6f3a898d13915cda208d24bc0d566ebbb Mon Sep 17 00:00:00 2001 From: zhou yang Date: Tue, 6 Oct 2026 06:31:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=BA=E4=BB=B6=200.1.17~0.1.18-c5=EF=BC=9AC?= =?UTF-8?q?H224A=20=E7=9A=84=20PD=20=E6=A1=A3=E4=BD=8D=E5=AE=9E=E6=97=B6?= =?UTF-8?q?=E8=BF=9B=E9=81=A5=E6=B5=8B=EF=BC=88pdmon=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原来只在启动时读一次(bringup.c),而多口充电器重新分配功率时 20 V 档的电流上限当场会变。 - 新模块 pdmon:每秒读 0x09(协议状态)+ 0x50(档位电流),只读;先试上次应答的支路(主干 / mux 通道 5)。 档位变了(连续两拍一致、非 0)记 LOG_EV_PD_LIMIT,arg 2 = 运行中换档。只报不动,降载仍没人做。 - status 新增 "pd":{"st":..,"ma":..},这一拍没读到是 null(满配 1164 / 1536 字节,余量断言改成至少留 256)。 - 0.1.17 放在热管理任务里读,板 B 实测 safety.skipped 每秒涨 1(持锁撞上巡检的下一拍); 0.1.18 挪进巡检任务(t_safety_step 之后、每 50 圈一次),skipped 不再涨。 - 测试:新增 test_pdmon(6 例),test_cmd 加 pd 断言;28 ctest 全绿。 板 B 已 OTA 到 0.1.18-c5(两次都是热重启,五路没断电),读到 st 0x88 / 3000 mA。 Co-Authored-By: Claude Opus 5.5 --- firmware/HANDOFF.md | 13 ++- firmware/host/CMakeLists.txt | 1 + firmware/main/CMakeLists.txt | 1 + firmware/main/app_main.c | 8 ++ firmware/main/cmd_report.c | 15 +++ firmware/main/pdmon.c | 72 ++++++++++++++ firmware/main/pdmon.h | 51 ++++++++++ firmware/main/phonerail.h | 2 +- firmware/test/test_cmd.c | 14 ++- firmware/test/test_pdmon.c | 178 +++++++++++++++++++++++++++++++++++ 10 files changed, 352 insertions(+), 3 deletions(-) create mode 100644 firmware/main/pdmon.c create mode 100644 firmware/main/pdmon.h create mode 100644 firmware/test/test_pdmon.c diff --git a/firmware/HANDOFF.md b/firmware/HANDOFF.md index 0048022..a19b63d 100644 --- a/firmware/HANDOFF.md +++ b/firmware/HANDOFF.md @@ -1,5 +1,16 @@ # 交接 -更新:2026-09-30 固件 **0.1.16-c5**(开机键统一 5 s);板 B 经 OTA 推上;rack 已部署 +更新:2026-10-06 固件 **0.1.18-c5**(CH224A 档位进遥测);板 B 经 OTA 推上;rack 已部署 + +## 0.1.17 / 0.1.18:CH224A 档位实时进遥测(2026-10-06) +- 起因:rack 要显示 PD 供电信息,而档位只在 `bringup.c` 启动时读一次;板 B 历史上读到过 5 A → 3.25 A → 3 A。 +- 新模块 `pdmon.{h,c}`:每秒读 `0x09`(协议状态)+ `0x50`(档位电流),**只读**。先试上次应答的支路(主干 / mux 通道 5)。 + 档位变了(连续两拍一致、非 0、和日志里最后那条不同)记 `LOG_EV_PD_LIMIT`,**arg 2 = 运行中换档**(0 / 1 仍是启动那条)。 + 只报不动:`pd_limit_mw` 仍是启动时定的,降载(§8.3)照旧没人做。 +- status 新增 `"pd":{"st":136,"ma":3000}`,这一拍没读到是 `null`(满配 1164 / 1536 字节,`test_cmd` 的余量断言改成「至少留 256」)。 +- ⚠️ **0.1.17 放在热管理任务里读,板 B 实测 `safety.skipped` 每秒涨 1**(持锁那一下正好撞上巡检的下一拍,那一圈五路都没读)。 + 0.1.18 挪进巡检任务(`t_safety_step()` 之后、每 50 圈一次),`skipped` 停在启动时的 1 不再涨。别再挪出去。 +- 板 B 实测:`st` = 0x88(bit3 = PD 握上;bit7 手册没写,rack 只认已知位)、`ma` = 3000。两次 OTA 都是热重启,五路 en 全程保持。 +- 测试:新增 `test_pdmon`(6 例),`test_cmd` 加 `pd` 两条断言;**28 ctest 全绿**。rack 侧见 `rack/frontend/src/pages/Board.vue` 的「PD 供电」格。 ## 0.1.15 / 0.1.16:开机长按 3 s 试验失败,所有开机操作统一 5 s(2026-09-30) - 0.1.15 把 `ACT_KEY_MS_ON` 5000 → 3000:板 B 第 2 路 `power_cycle` 三轮全是「按住 330~390 mA、松手掉回 0」⇒ `no-boot`(事件 19); diff --git a/firmware/host/CMakeLists.txt b/firmware/host/CMakeLists.txt index 1257267..d4e70cd 100644 --- a/firmware/host/CMakeLists.txt +++ b/firmware/host/CMakeLists.txt @@ -30,6 +30,7 @@ set(PHONERAIL_CORE_SRCS ${FW_ROOT}/main/tasks.c ${FW_ROOT}/main/chgmode.c ${FW_ROOT}/main/hubctl.c + ${FW_ROOT}/main/pdmon.c ${FW_ROOT}/main/t_safety.c ${FW_ROOT}/main/t_actuator.c ${FW_ROOT}/main/t_thermal.c diff --git a/firmware/main/CMakeLists.txt b/firmware/main/CMakeLists.txt index 59ce29b..9e04dac 100644 --- a/firmware/main/CMakeLists.txt +++ b/firmware/main/CMakeLists.txt @@ -14,6 +14,7 @@ idf_component_register( "tasks.c" "chgmode.c" "hubctl.c" + "pdmon.c" "t_safety.c" "t_actuator.c" "t_thermal.c" diff --git a/firmware/main/app_main.c b/firmware/main/app_main.c index f19f3fa..f19a4e3 100644 --- a/firmware/main/app_main.c +++ b/firmware/main/app_main.c @@ -33,6 +33,7 @@ #include "nvs.h" #include "nvs_flash.h" #include "ota.h" +#include "pdmon.h" #include "ota_esp.h" #include "warm_esp.h" #include "ulink_esp.h" @@ -151,6 +152,12 @@ static void task_safety(void *arg) for (;;) { (void)t_safety_step(snapp); /* 器件读失败不在这里处理:§10.2 失联 ≠ 断电 */ + /* 0.1.17:CH224A 的档位进遥测,1 s 一拍(pdmon.h「线程」那一段:为什么非得在这里读)。 + 错开 250 的倍数,不和下面那行日志挤在同一圈。 */ + if ((snapp->cycles % 50u) == 25u) { + pdmon_poll(); + } + /* 每 5 s 报一次总线占用。§10.3 手算是「约 1.5 ms / 约 8 %」, 首板上电时要拿逻辑分析仪复核这个数(§10.3 末尾那句)。 */ if ((snapp->cycles % 250u) == 0u) { @@ -462,6 +469,7 @@ void app_main(void) abort(); } log_report(&boot); + pdmon_init(boot.pd_ma); if (adopt != 0) { evlog_put(0, LOG_EV_OTA, OTA_EV_WARM, (int32_t)boot.adopted, (int32_t)boot.adopt_dropped); ESP_LOGI(TAG, "接管:一直开着 0x%02x,关掉 0x%02x(刀片不在 / mux 失联)", diff --git a/firmware/main/cmd_report.c b/firmware/main/cmd_report.c index d46645d..a774637 100644 --- a/firmware/main/cmd_report.c +++ b/firmware/main/cmd_report.c @@ -15,6 +15,7 @@ #include "hubctl.h" #include "ota.h" #include "pca9555.h" +#include "pdmon.h" #include "phonerail.h" #include "rail.h" #include "rail_cfg.h" @@ -114,6 +115,19 @@ void cmd_report_hub(cj_writer_t *w) (unsigned long)hubctl_hold_left_s(now), (unsigned long)hubctl_idle_left_s(now)); } +/* 0.1.17:`"pd":{"st":8,"ma":3000}` —— CH224A 此刻的协议状态(0x09 原值)与 20 V 档的最大电流(pdmon.h)。 + 上一拍没读到(或还没读过第一拍)写 null,**不报旧值**:上位机拿它当「现在」。 */ +static void pd_obj(cj_writer_t *w) +{ + pdmon_snap_t pd; + pdmon_get(&pd); + if (pd.ok) { + cj_w(w, ",\"pd\":{\"st\":%u,\"ma\":%lu}", (unsigned)pd.status, (unsigned long)pd.ma); + } else { + cj_w(w, ",\"pd\":null"); + } +} + void cmd_report_status(cj_writer_t *w, const cmd_env_t *env) { const safety_snap_t *s = env->safety; @@ -127,6 +141,7 @@ void cmd_report_status(cj_writer_t *w, const cmd_env_t *env) cj_w(w, ",\"host\":null"); } cmd_report_hub(w); + pd_obj(w); if (s != NULL) { if (s->input_ok) { cj_w(w, ",\"in\":{\"mv\":%ld,\"ua\":%ld}", (long)s->input.bus_mv, diff --git a/firmware/main/pdmon.c b/firmware/main/pdmon.c new file mode 100644 index 0000000..875faa9 --- /dev/null +++ b/firmware/main/pdmon.c @@ -0,0 +1,72 @@ +/* CH224A 运行期轮询。契约见 pdmon.h。 */ +#include "pdmon.h" + +#include + +#include "ch224a.h" +#include "evlog.h" +#include "i2cbus.h" +#include "slot.h" +#include "tca9548a.h" + +typedef struct { + uint8_t status; + uint32_t ma; +} pd_read_t; + +static pdmon_snap_t s_snap; +static int s_ch = I2CBUS_CH_TRUNK; /* 上次应答的支路 */ +static uint32_t s_logged_ma; /* 最近一次进了日志的档位 */ +static uint32_t s_prev_ma; /* 上一拍读到的(去抖用) */ + +static hal_err_t read_fn(void *arg) +{ + pd_read_t *r = (pd_read_t *)arg; + const hal_err_t err = ch224a_read_status(CH224A_ADDR_DEFAULT, &r->status); + return (err == HAL_OK) ? ch224a_read_max_ma(CH224A_ADDR_DEFAULT, &r->ma) : err; +} + +void pdmon_init(uint32_t boot_ma) +{ + s_snap = (pdmon_snap_t){ 0 }; + s_ch = I2CBUS_CH_TRUNK; + s_logged_ma = boot_ma; + s_prev_ma = boot_ma; +} + +void pdmon_poll(void) +{ + pd_read_t r = { 0 }; + hal_err_t err = i2cbus_xact(s_ch, SLOT_LOCK_TIMEOUT_MS, read_fn, &r); + if (err != HAL_OK) { + const int other = (s_ch == I2CBUS_CH_TRUNK) ? TCA9548A_CH_PD : I2CBUS_CH_TRUNK; + err = i2cbus_xact(other, SLOT_LOCK_TIMEOUT_MS, read_fn, &r); + if (err == HAL_OK) { + s_ch = other; + } + } + s_snap.polls++; + if (err != HAL_OK) { + s_snap.ok = false; /* 值留着:旧的档位比一个 0 可信(ch224a.h 同一条) */ + s_snap.errors++; + return; + } + s_snap.status = r.status; + s_snap.ma = r.ma; + s_snap.ok = true; + + /* 换档:连续两拍读到同一个非零值、且和日志里最后那条不一样。0 = 协商中途 / 没协商上,不记。 */ + if (r.ma != 0 && r.ma == s_prev_ma && r.ma != s_logged_ma) { + s_logged_ma = r.ma; + evlog_put(0, LOG_EV_PD_LIMIT, PDMON_EV_ARG_CHANGED, + (int32_t)(ch224a_power_limit_mw(r.ma) / 100u), (int32_t)r.ma); + } + s_prev_ma = r.ma; +} + +void pdmon_get(pdmon_snap_t *out) +{ + if (out != NULL) { + *out = s_snap; + } +} diff --git a/firmware/main/pdmon.h b/firmware/main/pdmon.h new file mode 100644 index 0000000..1e8b202 --- /dev/null +++ b/firmware/main/pdmon.h @@ -0,0 +1,51 @@ +/* CH224A 运行期轮询(0.1.17):把充电器**此刻**给的 PD 档位放进遥测(status 的 `pd`)。 + + 为什么不够用启动那一次(bringup.c 的 `pd_up()`):多口充电器在别的口插拔时会重新分配功率、 + 重发 Source_Capabilities,20 V 档的电流上限当场就变(板 B 实测过 5 A → 3.25 A → 3 A), + 而 20 V 不一定掉、板子不一定重启 —— 只在启动读一次,面板上看到的就是旧档位。 + + 做的事只有读:`0x09`(协议状态)和 `0x50`(档位电流)。**不写任何寄存器**(ch224a.h 那个框)。 + 读到的档位和上一次记进日志的不一样(连续两拍一致才算数,免得协商中途的瞬时值刷日志) + ⇒ 记一条 `LOG_EV_PD_LIMIT`,arg = 2(运行中换档)。 + ⚠️ 和 bringup 一样只**报**不**动**:逼近上限的降载(§8.3)还没人做(HANDOFF)。 + + ---- 线程 ---------------------------------------------------------------- + `pdmon_poll()` 只在**巡检任务**里调,排在 `t_safety_step()` 之后、每 50 圈(1 s)一次, + 一次 ≈ 选主干 + 两个单字节读。status 跨任务读快照,遥测无害(cmd.h 同一条)。 + ⚠️ 别挪到别的任务去:2026-10-06 板 B 实测放在热管理任务里时,它持锁的那一下正好撞上 + 巡检的下一拍,`skipped` 每秒整整涨 1(每秒有一圈五路都没读)。巡检自己读就没有人跟它抢。 */ +#ifndef PDMON_H +#define PDMON_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* `LOG_EV_PD_LIMIT` 的 arg:0 = 启动时读到,1 = 启动时读不到 / 读到 0、按保守值(bringup.c), + 2 = 运行中档位变了。⚠️ 冻结(上位机 protocol.py)。 */ +#define PDMON_EV_ARG_CHANGED 2u + +typedef struct { + bool ok; /* 上一拍读到了;false 时下面两个是最后一次读到的值 */ + uint8_t status; /* 0x09 原值 */ + uint32_t ma; /* 0x50 换算成 mA(没协商上 PD 时是 0) */ + uint32_t polls; + uint32_t errors; +} pdmon_snap_t; + +/* `boot_ma` = 启动时读到的档位(`bringup_report_t.pd_ma`,读不到是 0):换档事件拿它当基线。 */ +void pdmon_init(uint32_t boot_ma); + +/* 读一拍。先试上次应答的那条支路,不应答再试另一条(主干 / mux 通道 5,bringup.c `pd_up()` 的注释)。 */ +void pdmon_poll(void); + +void pdmon_get(pdmon_snap_t *out); + +#ifdef __cplusplus +} +#endif + +#endif /* PDMON_H */ diff --git a/firmware/main/phonerail.h b/firmware/main/phonerail.h index a50ed02..526e132 100644 --- a/firmware/main/phonerail.h +++ b/firmware/main/phonerail.h @@ -3,7 +3,7 @@ #define PHONERAIL_H /* 固件版本。规则:<主>.<次>.<修订>-<最后完成的任务包> */ -#define PHONERAIL_FW_VERSION "0.1.16-c5" +#define PHONERAIL_FW_VERSION "0.1.18-c5" /* 通道数。五路直供,见设计文档 §2。 */ #define PHONERAIL_RAIL_COUNT 5 diff --git a/firmware/test/test_cmd.c b/firmware/test/test_cmd.c index 4d7f0ae..e6e7f27 100644 --- a/firmware/test/test_cmd.c +++ b/firmware/test/test_cmd.c @@ -11,6 +11,7 @@ #include #include "board_pins.h" +#include "ch224a.h" #include "chgmode.h" #include "cmd.h" #include "dac63204.h" @@ -21,6 +22,7 @@ #include "hal_time.h" #include "i2cbus.h" #include "pca9555.h" +#include "pdmon.h" #include "phonerail.h" #include "rail.h" #include "rail_cfg.h" @@ -689,9 +691,18 @@ static void test_status_fits_and_reports_unknowns_as_null(void) ASSERT_HAS(send("{\"cmd\":\"status\"}"), "\"host\":false"); sim_gpio_drive(BOARD_PIN_UP_VBUS_DET, 1); /* 留着高电平:下面量满配长度 */ + /* 0.1.17 CH224A:没读到过 ⇒ null;读到了报原值。 */ + pdmon_init(0); + ASSERT_HAS(send("{\"cmd\":\"status\"}"), "\"pd\":null"); + sim_i2c_add(CH224A_ADDR_DEFAULT, 1); + sim_i2c_set_reg(CH224A_ADDR_DEFAULT, CH224A_REG_STATUS, 0x08); + sim_i2c_set_reg(CH224A_ADDR_DEFAULT, CH224A_REG_CURRENT, 5000 / 50); + pdmon_poll(); + const char *r = send("{\"seq\":4000000000,\"cmd\":\"status\"}"); ASSERT_HAS(r, "\"host\":true"); ASSERT_HAS(r, "{\"seq\":4000000000,\"ok\":true,\"fw\":\"" PHONERAIL_FW_VERSION "\""); + ASSERT_HAS(r, "\"pd\":{\"st\":8,\"ma\":5000}"); ASSERT_HAS(r, "\"in\":{\"mv\":12030,\"ua\":1234567}"); ASSERT_HAS(r, "{\"r\":2,\"pr\":true,\"en\":false,\"vb\":false,\"set\":3900,\"mv\":null,\"ua\":null"); ASSERT_HAS(r, "\"r\":4,"); @@ -701,7 +712,8 @@ static void test_status_fits_and_reports_unknowns_as_null(void) ASSERT_HAS(r, "\"st\":\"idle\""); TEST_ASSERT_EQUAL_CHAR('}', r[strlen(r) - 1]); printf("status 满配 %u 字节(上限 %u)\n", (unsigned)strlen(r), (unsigned)CMD_OUT_MAX); - TEST_ASSERT_TRUE(strlen(r) < CMD_OUT_MAX * 3 / 4); /* 留四分之一给以后加字段 */ + /* 原来留的是四分之一;0.1.17 加 `pd` 用掉了一点(1164 字节)。再加字段先看这个数,不够就该涨 CMD_OUT_MAX 了。 */ + TEST_ASSERT_TRUE(strlen(r) < CMD_OUT_MAX - 256); /* 不给快照:只报影子,照样是一行合法回应。 */ cmd_init(NULL); diff --git a/firmware/test/test_pdmon.c b/firmware/test/test_pdmon.c new file mode 100644 index 0000000..72e97e1 --- /dev/null +++ b/firmware/test/test_pdmon.c @@ -0,0 +1,178 @@ +/* 0.1.17:CH224A 运行期轮询(pdmon.h)。 + + 守四件事: + 1. 读到的就是芯片此刻的值,换了档下一拍就跟着变(这个模块存在的理由); + 2. 读不到时 `ok` 落下、旧值留着,恢复后自己回来; + 3. 换档事件:连续两拍一致才记、同一个档位只记一次、和启动时那条不重复; + 4. 主干不应答就去 mux 通道 5 找(设计与实物不一致,bringup.c `pd_up()`)。 */ +#include "pdmon.h" + +#include "board_pins.h" +#include "ch224a.h" +#include "evlog.h" +#include "hal_i2c.h" +#include "hal_lock.h" +#include "i2cbus.h" +#include "sim_hal.h" +#include "tca9548a.h" +#include "unity.h" + +static int count_pd_ev(evlog_rec_t *last) +{ + (void)evlog_flush(0); + evlog_stats_t st; + evlog_stats(&st); + int n = 0; + evlog_rec_t rec; + for (uint32_t seq = st.oldest_seq; seq < st.next_seq; seq++) { + if (evlog_read(seq, &rec, 1) == 1 && rec.type == (uint8_t)LOG_EV_PD_LIMIT) { + n++; + if (last != NULL) { + *last = rec; + } + } + } + return n; +} + +static void charger(uint32_t ma) +{ + sim_i2c_set_reg(CH224A_ADDR_DEFAULT, CH224A_REG_STATUS, 0x08); + sim_i2c_set_reg(CH224A_ADDR_DEFAULT, CH224A_REG_CURRENT, (uint16_t)(ma / 50u)); +} + +void setUp(void) +{ + sim_hal_reset(); + TEST_ASSERT_EQUAL_INT(HAL_OK, hal_i2c_init(NULL)); + TEST_ASSERT_EQUAL_INT(HAL_OK, hal_lock_init()); + sim_i2c_add_mux(TCA9548A_ADDR, BOARD_PIN_MUX_RST_N); + TEST_ASSERT_EQUAL_INT(HAL_OK, i2cbus_init()); + TEST_ASSERT_EQUAL_INT(HAL_OK, evlog_init_ex(0)); +} + +void tearDown(void) {} + +static void test_reports_what_the_chip_says_now(void) +{ + sim_i2c_add(CH224A_ADDR_DEFAULT, 1); + charger(3000); + pdmon_init(3000); + + pdmon_snap_t s; + pdmon_get(&s); + TEST_ASSERT_FALSE(s.ok); /* 还没读过第一拍 */ + + pdmon_poll(); + pdmon_get(&s); + TEST_ASSERT_TRUE(s.ok); + TEST_ASSERT_EQUAL_UINT8(0x08, s.status); + TEST_ASSERT_EQUAL_UINT32(3000, s.ma); + + charger(5000); + pdmon_poll(); + pdmon_get(&s); + TEST_ASSERT_EQUAL_UINT32(5000, s.ma); /* 下一拍就是新值,不等去抖 */ +} + +static void test_read_failure_keeps_the_old_value_and_recovers(void) +{ + sim_i2c_add(CH224A_ADDR_DEFAULT, 1); + charger(3250); + pdmon_init(3250); + pdmon_poll(); + + sim_i2c_set_present(CH224A_ADDR_DEFAULT, false); + pdmon_poll(); + pdmon_snap_t s; + pdmon_get(&s); + TEST_ASSERT_FALSE(s.ok); + TEST_ASSERT_EQUAL_UINT32(3250, s.ma); + TEST_ASSERT_EQUAL_UINT32(1, s.errors); + + sim_i2c_set_present(CH224A_ADDR_DEFAULT, true); + pdmon_poll(); + pdmon_get(&s); + TEST_ASSERT_TRUE(s.ok); + TEST_ASSERT_EQUAL_INT(0, count_pd_ev(NULL)); /* 掉线再回来、档位没变:不记 */ +} + +static void test_change_is_logged_once_after_two_matching_reads(void) +{ + sim_i2c_add(CH224A_ADDR_DEFAULT, 1); + charger(5000); + pdmon_init(5000); /* 启动时 bringup 已经记过 5 A 那条 */ + pdmon_poll(); + pdmon_poll(); + TEST_ASSERT_EQUAL_INT(0, count_pd_ev(NULL)); + + charger(0); /* 重新协商的中途 */ + pdmon_poll(); + pdmon_poll(); + TEST_ASSERT_EQUAL_INT(0, count_pd_ev(NULL)); + + charger(3000); + pdmon_poll(); + TEST_ASSERT_EQUAL_INT(0, count_pd_ev(NULL)); /* 只见过一拍 */ + pdmon_poll(); + evlog_rec_t rec; + TEST_ASSERT_EQUAL_INT(1, count_pd_ev(&rec)); + TEST_ASSERT_EQUAL_UINT8(PDMON_EV_ARG_CHANGED, rec.arg); + TEST_ASSERT_EQUAL_INT32(600, rec.bus_mv); /* 60 W,单位 mW/100 */ + TEST_ASSERT_EQUAL_INT32(3000, rec.current_ua); + + for (int i = 0; i < 5; i++) { + pdmon_poll(); + } + TEST_ASSERT_EQUAL_INT(1, count_pd_ev(NULL)); +} + +static void test_boot_missed_it_but_runtime_reads_it(void) +{ + sim_i2c_add(CH224A_ADDR_DEFAULT, 1); + charger(3000); + pdmon_init(0); /* 启动时没读到,日志里是「按保守值」那条 */ + pdmon_poll(); + pdmon_poll(); + evlog_rec_t rec; + TEST_ASSERT_EQUAL_INT(1, count_pd_ev(&rec)); + TEST_ASSERT_EQUAL_INT32(3000, rec.current_ua); +} + +static void test_found_behind_channel_5(void) +{ + sim_i2c_add_ch(TCA9548A_CH_PD, CH224A_ADDR_DEFAULT, 1); + sim_i2c_set_reg_ch(TCA9548A_CH_PD, CH224A_ADDR_DEFAULT, CH224A_REG_STATUS, 0x08); + sim_i2c_set_reg_ch(TCA9548A_CH_PD, CH224A_ADDR_DEFAULT, CH224A_REG_CURRENT, 5000 / 50); + pdmon_init(5000); + pdmon_poll(); + pdmon_snap_t s; + pdmon_get(&s); + TEST_ASSERT_TRUE(s.ok); + TEST_ASSERT_EQUAL_UINT32(5000, s.ma); + TEST_ASSERT_EQUAL_UINT32(0, s.errors); +} + +static void test_absent_chip_is_just_not_ok(void) +{ + pdmon_init(0); + pdmon_poll(); + pdmon_snap_t s; + pdmon_get(&s); + TEST_ASSERT_FALSE(s.ok); + TEST_ASSERT_EQUAL_UINT32(1, s.polls); + TEST_ASSERT_EQUAL_UINT32(1, s.errors); + TEST_ASSERT_EQUAL_INT(0, count_pd_ev(NULL)); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_reports_what_the_chip_says_now); + RUN_TEST(test_read_failure_keeps_the_old_value_and_recovers); + RUN_TEST(test_change_is_logged_once_after_two_matching_reads); + RUN_TEST(test_boot_missed_it_but_runtime_reads_it); + RUN_TEST(test_found_behind_channel_5); + RUN_TEST(test_absent_chip_is_just_not_ok); + return UNITY_END(); +}