文件
zhou yang和Claude Opus 5.5 489cd6a6f3 固件 0.1.17~0.1.18-c5:CH224A 的 PD 档位实时进遥测(pdmon)
原来只在启动时读一次(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 <[email protected]>
2026-10-06 06:31:04 +08:00

179 行
5.1 KiB
C

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
/* 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();
}