文件
phonerail/firmware/host/CMakeLists.txt
T
zhou yang和Claude Opus 5 18d3d69856 A11:事件日志(环形缓冲 + 落盘)
事件从 evlog_put*() 进一个 256 条的 RAM 环(临界区里两条指令,**不阻塞、
不碰 flash**),由最低优先级的 t_log 攒够 16 条或最老那条等满 1 s 时批量写进
新的 `evlog` 分区(17 扇区 × 4 KB = **4335 条**)。A10 的事件出口原样接上,
判据那一层一个字没动。

八条实现约定(详见需求文档 §10.7):

· 落盘不用 NVS 也不用 SPIFFS,是一块裸 data 分区 —— NVS 的页式 GC 会把
  「写一条事件」变成一次不可预测的搬移。HAL 因此多出第六样 hal_store;
· 17 个扇区不是 16:每扇区要拿一个 16 B 的槽当扇区头,4 KB 只装 255 条,
  16 扇区 = 4080,差 16 条不到 §10.7 要求的 4096;
· 序号 = flash 里的位置(epoch × 255 + 槽号),不是自增计数器 —— 重启接着数
  不用存计数器;代价是没落盘的事件还没有序号,A12 读之前要先 flush;
· 入队走关中断的临界区,**不是第三把锁**:入队方是最高优先级的 t_safety,
  停在一把 t_log 持着的锁上等于让最低优先级的任务决定巡检节拍;
· 丢事件可以,悄悄丢不行 —— 缓冲满了盖最旧的,但补一条 LOG_EV_LOST 记着丢了几条;
· 每条记录带 CRC-8(见下),写失败就放弃该扇区剩余部分,被放弃的那段在序号
  空间里是个洞,读回时给一条空记录占位(跳过的话上位机游标会卡住);
· 每次上电记一条 LOG_EV_BOOT:时间戳上电从 0 计,没有它两次上电的记录会穿插。

事件类型的数字是 flash 格式的一部分,只许往末尾加;safety_ev_t → log_ev_t
是显式 switch 不是加偏移。§10.7 里还没人产生的六类号已占住(A12/A15/A16 去填)。

27 个用例;23 条注错逐个跑过,21 条变红。第一版校验和用异或,漏掉的正是最常见
的那条:payload 全 0 的记录被掉电切在第 8 个字节上时,后 8 个字节留在 0xFF,
而 8 个 0xFF 的异或恰好是 0 —— 半条的校验和与整条一模一样,扫描把半条当成
一条真事件。换成 CRC-8 之后拦住。另外两个只在这里才踩得到的坑:「掉电重启」
不能用 sim_hal_reset()(那是换新板子,flash 一起擦,测试永远绿),为此加了
sim_hal_reboot();「读到洞就跳过」这个错被测试里 static 数组的零初始化掩盖过
一次,缓冲要先填成非零。

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 09:20:02 +08:00

84 行
3.2 KiB
CMake
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# PhoneRail 宿主机构建 —— 不依赖 ESP-IDF,用来跑单元测试(CTest)。
#
# bash host/test.sh 配置 + 编译 + ctest
#
# 与 IDF 目标共享的源码只有 ../main 里的与硬件无关部分。
# 新增一个既要上板又要被测的 .c,必须同时加进:
# ../main/CMakeLists.txt 的 SRCS 和 下面的 PHONERAIL_CORE_SRCS
# HAL 里新增一路实现则要同时加进:
# ../components/phonerail_hal/CMakeLists.txt 的 SRCS(esp 那份)
# 和下面的 PHONERAIL_HAL_SIM_SRCS(sim 那份)
# 新增一个器件驱动则是:
# ../components/drv/CMakeLists.txt 的 SRCS 和 下面的 PHONERAIL_DRV_SRCS
cmake_minimum_required(VERSION 3.16)
project(phonerail_host C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
add_compile_options(-Wall -Wextra -Werror)
set(FW_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/..)
# ---- 被测代码 -------------------------------------------------------------
set(PHONERAIL_CORE_SRCS
${FW_ROOT}/main/version.c
${FW_ROOT}/main/rail.c
${FW_ROOT}/main/rail_cfg.c
${FW_ROOT}/main/sched.c
${FW_ROOT}/main/tasks.c
${FW_ROOT}/main/t_safety.c
${FW_ROOT}/main/t_actuator.c
${FW_ROOT}/main/evlog.c
)
# HAL:接口在 include/,宿主机链 sim/ 的实现(esp/ 那份只有 IDF 目标编)。
set(PHONERAIL_HAL_DIR ${FW_ROOT}/components/phonerail_hal)
set(PHONERAIL_HAL_SIM_SRCS
${PHONERAIL_HAL_DIR}/hal_err.c
${PHONERAIL_HAL_DIR}/hal_i2c_acct.c
${PHONERAIL_HAL_DIR}/sim/hal_lock_sim.c
${PHONERAIL_HAL_DIR}/sim/sim_hal.c
${PHONERAIL_HAL_DIR}/sim/hal_i2c_sim.c
${PHONERAIL_HAL_DIR}/sim/hal_gpio_sim.c
${PHONERAIL_HAL_DIR}/sim/sim_sr595.c
${PHONERAIL_HAL_DIR}/sim/sim_pca9555.c
${PHONERAIL_HAL_DIR}/sim/hal_pwm_sim.c
${PHONERAIL_HAL_DIR}/sim/hal_time_sim.c
${PHONERAIL_HAL_DIR}/sim/hal_store_sim.c
)
# 器件驱动:与平台无关(只经 hal_i2c_*),两个目标编的是同一份源码。
set(PHONERAIL_DRV_DIR ${FW_ROOT}/components/drv)
set(PHONERAIL_DRV_SRCS
${PHONERAIL_DRV_DIR}/ina226.c
${PHONERAIL_DRV_DIR}/dac63204.c
${PHONERAIL_DRV_DIR}/sr595.c
${PHONERAIL_DRV_DIR}/pca9555.c
)
add_library(phonerail_core STATIC
${PHONERAIL_CORE_SRCS} ${PHONERAIL_HAL_SIM_SRCS} ${PHONERAIL_DRV_SRCS})
target_include_directories(phonerail_core PUBLIC
${FW_ROOT}/main
${PHONERAIL_HAL_DIR}/include
${PHONERAIL_HAL_DIR}/sim
${PHONERAIL_DRV_DIR}/include)
# ---- 测试框架 Unity(从 ESP-IDF v5.4 原样复制到 third_party/,与上板测试同一套)
add_library(unity STATIC ${FW_ROOT}/third_party/unity/unity.c)
target_include_directories(unity PUBLIC ${FW_ROOT}/third_party/unity)
target_compile_definitions(unity PUBLIC UNITY_OUTPUT_COLOR)
# 第三方源码不吃本工程的 -Werror
target_compile_options(unity PRIVATE -w)
# ---- 测试用例:test/test_*.c 一个文件一个可执行 + 一条 ctest ---------------
enable_testing()
file(GLOB PHONERAIL_TESTS ${FW_ROOT}/test/test_*.c)
foreach(test_src ${PHONERAIL_TESTS})
get_filename_component(test_name ${test_src} NAME_WE)
add_executable(${test_name} ${test_src})
target_link_libraries(${test_name} PRIVATE phonerail_core unity)
add_test(NAME ${test_name} COMMAND ${test_name})
endforeach()