固件 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]>
这个提交包含在:
+13
-1
@@ -11,6 +11,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户