Skip to content

feat(i2c): Add and stabilize end-to-end I2C0 transport for CBoard and RmcsBoard#46

Closed
gqsdjhh wants to merge 20 commits into
mainfrom
dev/i2c
Closed

feat(i2c): Add and stabilize end-to-end I2C0 transport for CBoard and RmcsBoard#46
gqsdjhh wants to merge 20 commits into
mainfrom
dev/i2c

Conversation

@gqsdjhh
Copy link
Copy Markdown

@gqsdjhh gqsdjhh commented Apr 18, 2026

Summary

This PR introduces I2C0 as a first-class protocol channel and then hardens it across core protocol, host SDK, and firmware (c_board +
rmcs_board).

Commit range: 74caf44 (feat(i2c)) -> 18b590f (18 commits).

Core protocol

  • Added DataId::kI2c0, I2cDataView, I2cReadConfigView, and I2cErrorView.
  • Added I2C serialization/deserialization paths for write, read request, read result, and error payloads.
  • Centralized I2C protocol limits in core/include/librmcs/protocol/i2c.hpp and aligned all size checks to the shared limit.
  • Hardened protocol validation: reject invalid slave addresses and empty write/read-request transfers; preserve full error context.

Host SDK

  • Added I2C support in Handler::PacketBuilder: write_i2c, write_i2c_read_config, and write_i2c_read_result.
  • Added agent-level APIs: i2c0_write and i2c0_read for CBoard, RmcsBoardLite, and RmcsBoardPro.
  • Made I2C send behavior strict on buffer allocation failures (no silent success for I2C kBadAlloc).
  • Extracted shared I2C0 logic to agent/detail/i2c0_common.hpp.
  • Fixed I2C error callback dispatch and overload-hiding issues so callbacks route consistently.

Firmware: c_board (STM32)

  • Implemented logical I2C0 transport over physical I2C2.
  • Added CubeMX-side I2C2/DMA/IRQ wiring and integrated I2C update handling into the app loop.
  • Added timeout recovery and DMA abort/re-init flow to recover stuck transfers.
  • Lowered I2C IRQ priority to reduce USB conflicts.
  • Preserved uplink read results/errors under backpressure via pending uplink buffering.

Firmware: rmcs_board (HPM)

  • Implemented non-blocking DMA I2C transport with request queueing, timeout checks, and polling.
  • Added robust uplink preservation (pending + blocked + retained buffers) to avoid response loss.
  • Added/updated board-level I2C init hooks for Lite/Pro variants.
  • Updated hpm_sdk reference to restore hpm5300evk board definitions needed by this series.

Behavior changes

  • Empty I2C writes and empty I2C read requests are now rejected.
  • I2C errors now consistently carry context (slave_address, reg_address, data_length, is_read).
  • Host-side I2C packet building fails fast when transmit buffer allocation fails.

Misc

  • Includes small devcontainer/gitignore maintenance changes:
  • .devcontainer/devcontainer.json: deterministic startup and opt-in user tool bootstrap.
  • .gitignore: ignore firmware/rmcs_board/build-lite/.

I2C0 端到端传输支持与稳定性加强(概述)

本 PR 将 I2C0 提升为一等协议通道,并在核心协议、Host SDK、以及两块固件(c_board:STM32F4,rmcs_board:HPM)中实现并加固了端到端支持,包含严格验证、错误上下文保留、DMA/中断驱动传输与 USB 背压处理。提交范围:74caf44 -> 18b590f(18 个提交);分支:dev/i2c → main;状态:open。作者请求 reviewer:@coderabbitai。CodeRabbit 触发了自动回复,无其他审查讨论或批准。

核心协议层

  • 新增 DataId::kI2c0 及数据视图:I2cDataView、I2cReadConfigView、I2cErrorView(含可选寄存器地址与读/写标识)。
  • 新增常量与位宽定义:kI2cDataLengthBits = 9,kI2cMaxDataLength = 511;新增 I2cHeader 位域并做静态断言以保持一致性。
  • 序列化/反序列化:
    • Serializer 增加 write_i2c_write / write_i2c_read_config / write_i2c_read_result / write_i2c_error(包含大小/从机地址/空载校验与所需序列化长度计算)。
    • Deserializer 新增 process_i2c_field 协程,支持 write、read request、read result、error 的解析与严格验证(拒绝空写/空读请求、非法从地址、错误标志组合等),并向回调派发完整视图或错误视图。
  • DataCallback 接口扩展:新增 i2c_receive_callback、i2c_error_callback 及辅助构造错误的 helper。

Host SDK(协议与 Agent)

  • 协议层:PacketBuilder 新增 write_i2c、write_i2c_read_config、write_i2c_read_result;对 I2C 路径采用更严格的序列化结果处理(kBadAlloc 导致失败返回,避免静默成功)。
  • Agent 层:
    • 新增共享实现 host/include/librmcs/agent/detail/i2c0_common.hpp:
      • SingleI2c0DataCallback:按 DataId 路由到 i2c0 的适配器并提供弱可覆写钩子。
      • I2c0PacketBuilderMixin:提供 i2c0_write()、i2c0_read()(参数校验、错误抛出 / 无缓冲时迅速失败),用于 PacketBuilder 混入实现。
    • CBoard、RmcsBoardLite、RmcsBoardPro 修改为基于上述回调适配器与 mixin,并通过 protected using 导出 i2c0 回调接口。

c_board 固件(STM32F4 — 逻辑 I2C0 映射到物理 I2C2)

  • 新增中断/DMA 驱动的逻辑 I2C 实现(firmware/c_board/app/src/i2c/):I2c 类管理下行队列(写/读配置)、顺序 DMA 传输、超时与恢复、完成/错误处理,并发布上行(读结果/错误)。
  • 将逻辑 i2c0 绑定到 hi2c2(新增 MX_I2C2_Init、i2c0.init() 调用),主循环集成 i2c::i2c0->update()。
  • CubeMX/HAL 修改:添加 I2C2、DMA 流(DMA1 Stream2/Stream7)、GPIO PF0/PF1、I2C 中断与 NVIC 配置;将 stm32 HAL I2C 源文件加入构建。
  • USB 集成:vendor.hpp 增加 I2C 下行反序列化处理(将 DataId::kI2c0 的写/读请求路由到 i2c0),并将非法下行改为丢弃而非断言。
  • InterruptSafeBuffer 的解锁语义由 try_unlock_and_clear() 改为 try_unlock()(解锁不再自动清批次内容),上行排队在缓冲不可用时按现有策略保留或触发上报。

rmcs_board 固件(HPM — 非阻塞 DMA)

  • 新增非阻塞 DMA I2C 驱动(firmware/rmcs_board/app/src/i2c/):请求队列、超时检测、转移启动/回调、L1 缓存写回/失效处理,以及三层上行缓冲(pending/blocked/retained)以应对 USB 背压。
  • 提供 i2c_dma_complete_callback 与全局 Lazy 单例 i2c0(基于 BOARD_APP_I2C_BASE_ADDR 或 HPM_I2C0_BASE)。
  • boards/lite 与 boards/pro 新增 board_init_i2c 接口:引脚复用、时钟启用、总线清除/恢复、并使用 HPM SDK 初始化主模式;并在 pro 固件 init 中增加总线清除重试以避免初始化时因总线状态导致失败(参见修复提交)。
  • USB 层新增 uplink 缓冲区满钩子(弱符号可覆盖),vendor.hpp 同样集成下行反序列化路由与错误处理的调整。

行为变更与兼容性要点

  • 拒绝空负载:空写入与空读请求在序列化/反序列化与固件输入点均被显式拒绝。
  • 错误上下文:I2C 错误携带完整上下文(is_read、slave_address、可选寄存器地址、data_length),便于上层定位与处理。
  • 主机端缓冲分配失败策略:I2C 相关的 packet 构建在遇到缓冲分配失败时立即失败返回(kBadAlloc → false),以实现 fail-fast 行为。
  • USB 上行缓冲保留:固件实现 pending/blocked/retained 机制以在 USB 背压下保留读结果与错误;当缓冲满时可触发平台钩子(弱符号,允许平台覆盖)。
  • 反序列化/固件对非法下行的处理更宽容:不再对非法下行断言崩溃,而是记录或丢弃以提高稳健性。

其他杂项

  • devcontainer: 禁用 userEnvProbe,并将用户工具安装改为可选(RMCS_DEVCONTAINER_INSTALL_USER_TOOLS 环境变量)。
  • .gitignore: 添加 firmware/rmcs_board/build-lite/ 忽略条目。
  • firmware/rmcs_board/app/CMakeLists.txt: 定义 CONFIG_HPM_I2C。

影响与风险提示(建议审查重点)

  • 该 PR 改动面广,涉及协议、Host SDK、两个固件平台与构建配置,建议逐模块细审并在硬件上做端到端验收测试。审查关注点包括:
    • 序列化/反序列化的边界与一致性(位域定义、长度上限、从机地址约束、标志位组合校验)。
    • 固件端 DMA/IRQ 的超时与恢复路径(abort/re-init、取消/重试逻辑、资源释放与并发安全)。
    • USB 背压时上行缓冲策略(避免丢失关键错误/结果、钩子行为与弱符号覆盖情况)。
    • Host SDK 的异常契约(mixin 抛出 std::invalid_argument / std::runtime_error 的情形与上层处理)。
  • 提醒注意:已包含一处针对 rmcs_board 初始化的修复提交——在初始化阶段重试 I2C 总线清除以避免首次清除失败导致初始化失败。

总体评价:该 PR 系统性地将 I2C0 引入为一等通道并在多层面实现了鲁棒性与回退机制,功能完整且考虑了主机/固件交互的异常情形;因改动范围大,建议分区审查并进行硬件回归测试。

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 947eacff-ccd4-4709-84ca-15bf4c01578b

📥 Commits

Reviewing files that changed from the base of the PR and between a06053e and a484285.

📒 Files selected for processing (2)
  • firmware/rmcs_board/boards/lite/board.c
  • firmware/rmcs_board/boards/pro/board.c
🚧 Files skipped from review as they are similar to previous changes (2)
  • firmware/rmcs_board/boards/lite/board.c
  • firmware/rmcs_board/boards/pro/board.c

Walkthrough

新增 I2C0 端到端支持:数据模型、协议常量、序列化/反序列化、主机代理扩展、STM32/HPM 平台固件驱动(DMA/中断/恢复/上行发布),并伴随构建、CubeMX 与开发容器配置变更。

Changes

Cohort / File(s) Summary
核心数据与协议
core/include/librmcs/data/datas.hpp, core/include/librmcs/protocol/i2c.hpp, core/src/protocol/protocol.hpp
新增 DataId::kI2c0、I2C 视图类型与 I2C header bitfield,添加协议常量与静态断言,扩展回调接口。
序列化 / 反序列化
core/src/protocol/serializer.hpp, core/src/protocol/deserializer.hpp, core/src/protocol/deserializer.cpp
实现 I2C 序列化 API 与反序列化路径(新增 process_i2c_field 协程),含参数校验与回调分发。
主机端协议与代理
host/include/librmcs/agent/detail/i2c0_common.hpp, host/include/librmcs/agent/*, host/include/librmcs/protocol/handler.hpp, host/src/protocol/handler.cpp
添加 I2C0 回调封装与 PacketBuilder mixin,代理类改为继承该基类,扩展主机端 I2C 发送与上行处理。
STM32 BSP 与 HAL(C-Board)
firmware/c_board/bsp/cubemx/.../i2c.h, .../i2c.c, .../stm32f4xx_hal_conf.h, .../stm32f4xx_it.h, .../stm32f4xx_it.c, .../dma.c, .../gpio.c, .../rmcs_slave.ioc, .../CMakeLists.txt
引入 I2C2 驱动文件、DMA/IRQ 与引脚配置,添加 MX_I2C2_Init 与 NVIC/DMA 流配置,并将 I2C 源加入构建列表。
STM32 应用层(C-Board)
firmware/c_board/app/src/i2c/*, firmware/c_board/app/src/app.cpp, firmware/c_board/app/src/usb/*
实现 STM32 平台逻辑 I2C 驱动(请求队列、DMA 启动、超时/恢复、上行发布),接入 HAL 回调并在主循环 update;调整 USB vendor 路由与缓冲解锁。
HPM 平台 / RMCS-Board
firmware/rmcs_board/app/src/i2c/*, firmware/rmcs_board/boards/*, firmware/rmcs_board/app/src/usb/*, firmware/rmcs_board/app/CMakeLists.txt
新增 HPM 平台 I2C 驱动与 DMA 完成回调、板级初始化接口、上/下行集成,并在 CMake 中启用 I2C 标志。
USB 中断安全缓冲与上行钩子
firmware/*/app/src/usb/interrupt_safe_buffer.hpp, firmware/rmcs_board/app/src/usb/uplink_buffer_hook.cpp
try_unlock_and_clear() 重命名为 try_unlock()(不再自动 clear),新增弱连结的上行缓冲已满 C 钩子声明与默认弱实现;调整原子内存序。
开发环境与忽略规则
.devcontainer/devcontainer.json, .gitignore
将 devcontainer 的用户工具安装改为可选(环境变量控制)、禁用 host-shell 探测;新增 firmware/rmcs_board/build-lite/ 忽略规则。

Sequence Diagram(s)

sequenceDiagram
    rect rgba(200,220,255,0.5)
    participant USB as USB 下行
    participant Deser as 反序列化器
    participant HostCB as 主机回调/Handler
    end
    rect rgba(200,255,200,0.5)
    participant App as I2C 驱动 (固件)
    participant DMA as DMA/HAL
    participant Dev as I2C 设备
    end

    USB->>Deser: 收到 I2C 下行帧
    Deser->>HostCB: i2c_*_deserialized_callback(...)
    HostCB->>App: route to board I2C handler (handle_downlink_*)
    App->>DMA: start DMA transfer / HAL 非阻塞调用
    DMA->>Dev: 总线传输(SCL/SDA)
    Dev-->>DMA: 返回数据 / ACK / NACK
    DMA-->>App: 完成或错误回调
    App->>HostCB: publish read result 或 error 上行
    HostCB->>USB: 序列化并发送 USB 上行
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 分钟

Possibly related PRs

兔子的诗

🐰 我在总线上轻快跳,
数据排队像胡萝卜饱,
DMA 拍手传声到设备,
读写回复跑回主机道,
两板协作一切通畅好。

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.61% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确总结了主要变更:为 CBoard 和 RmcsBoard 添加和稳定化端到端 I2C0 传输支持,与 PR 目标和广泛的代码改动完全相关。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev/i2c

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gqsdjhh
Copy link
Copy Markdown
Author

gqsdjhh commented Apr 18, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
firmware/rmcs_board/app/src/usb/interrupt_safe_buffer.hpp (1)

42-47: ⚠️ Potential issue | 🟡 Minor

缓冲区满时缺少指示钩子,与 c_board 行为不一致。

c_board 版本在 allocate() 缓冲区满时调用 led::led->uplink_buffer_full(),而此处仅留 TODO 并静默返回空 span。这会导致 rmcs_board 上行丢包时用户/开发者无任何可观测信号,调试困难。建议至少接一个平台无关的钩子(弱符号函数或回调),即便初期为空实现也便于后续替换。

基于 learnings(qzhhhi, PR 32):allocate() 缓冲区满应由本层直接指示(LED/log),不应向上层传播 kBadAlloc;此分层设计要求 rmcs_board 也在本层提供指示入口,而非留空 TODO。

需要我草拟一个与 c_board 对齐的弱钩子接口(例如 librmcs::firmware::usb::on_uplink_buffer_full())吗?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/rmcs_board/app/src/usb/interrupt_safe_buffer.hpp` around lines 42 -
47, The allocate() path currently silently returns an empty span when writeable
== 0, missing the buffer-full indication present in c_board; add a
platform-agnostic weak hook that this function calls before returning (e.g.
declare a weak function like librmcs::firmware::usb::on_uplink_buffer_full() or
a static callback invoked from allocate()), and invoke it in the branch where
writeable == 0 (around the TODO) so rmcs_board can provide an LED/logging
implementation while keeping a no-op default in the library.
🧹 Nitpick comments (7)
firmware/rmcs_board/app/src/usb/interrupt_safe_buffer.hpp (1)

92-92: 内存序常量风格与文件其余部分不一致。

本文件其余处统一使用 C++20 枚举式 std::memory_order::relaxed / std::memory_order::release,但此两处使用了旧式宏 std::memory_order_release。功能等价,仅为风格一致性。

♻️ 建议统一写法
-        std::atomic_signal_fence(std::memory_order_release);
+        std::atomic_signal_fence(std::memory_order::release);

(line 92 与 line 119 各一处)

Also applies to: 119-119

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/rmcs_board/app/src/usb/interrupt_safe_buffer.hpp` at line 92,
Replace the old-style macro memory order constants with the C++20 enum-style
form for consistency: locate the two calls to std::atomic_signal_fence that
currently use std::memory_order_release (lines around the occurrences) and
change them to use std::memory_order::release (and similarly change any
std::memory_order_relaxed to std::memory_order::relaxed if present); ensure you
update both occurrences (the one at the std::atomic_signal_fence call near line
92 and the other near line 119) so the file consistently uses
std::memory_order::release/::relaxed style.
firmware/c_board/bsp/cubemx/Core/Src/dma.c (1)

47-61: DMA1_Stream2/7 的中断优先级与 USB OTG 优先级需要协调。

DMA1_Stream2_IRQn(I2C2 RX)和 DMA1_Stream7_IRQn(I2C2 TX)的抢占优先级均为 1,而 OTG_FS_IRQn 优先级为 0,这意味着 USB OTG 中断可以抢占 I2C2 DMA 完成处理。同时,I2C2_EV_IRQn 和 I2C2_ER_IRQn 也被设为优先级 1。由于 DMA 完成回调会驱动 I2C 状态机,建议确认是否应将 DMA1_Stream2/7 的优先级调整至低于 USB OTG(例如优先级 4 或 6,与其它 DMA 流一致),以确保 I2C2 数据传输路径不被 USB 处理中断抢占,避免 TinyUSB 出现掉包或状态不一致。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/c_board/bsp/cubemx/Core/Src/dma.c` around lines 47 - 61,
DMA1_Stream2_IRQn and DMA1_Stream7_IRQn are currently set to priority 1 which
allows USB OTG (priority 0) to preempt I2C2 DMA handlers and can cause I2C state
issues; change the HAL_NVIC_SetPriority calls for DMA1_Stream2_IRQn and
DMA1_Stream7_IRQn to a lower priority value (e.g., 4 or 6 to match other DMA
streams) so they cannot be preempted by OTG_FS_IRQn, and ensure this change is
consistent with I2C2_EV_IRQn / I2C2_ER_IRQn priority assignments.
firmware/rmcs_board/boards/pro/board.c (1)

173-185: i2c_config_t config 进行零初始化。

当前代码只显式初始化了 i2c_modeis_10bit_addressing 两个字段。由于 i2c_config_t 定义在外部 HPM SDK 中,如果该结构体有其他成员在 i2c_init_master() 中被读取,未初始化的字段将传入驱动,导致未定义行为。使用 {0} 零初始化成本极低,是 C 编程最佳实践,也便于应对未来 SDK 版本中结构体定义的变化。

建议修正
 bool board_init_i2c(I2C_Type* ptr) {
-    i2c_config_t config;
+    i2c_config_t config = {0};
     const uint32_t freq = board_init_i2c_clock(ptr);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/rmcs_board/boards/pro/board.c` around lines 173 - 185,
Zero-initialize the i2c_config_t before setting individual fields in
board_init_i2c so any unspecified members don't remain indeterminate; locate the
i2c_config_t config declaration in board_init_i2c and replace its uninitialized
declaration with a zero-initialized one (e.g., initialize config with {0} or use
memset) then keep setting config.i2c_mode and config.is_10bit_addressing as
before before calling i2c_init_master.
core/src/protocol/deserializer.cpp (1)

313-314: slave_address > 0x7F 为结构上不可达分支(仅作为防御层保留)

I2cHeader::SlaveAddress 定义为 BitfieldMember<8, 7>,bitfield 提取出的值上界即 0x7F,故 slave_address > 0x7F 永远为 false,分支是死代码。根据历史学习笔记,此校验是刻意作为“多层防御”保留的一环(协议层兜底),所以不建议删除;但位宽变更时该断言会静默失效。

可选增强:把防御由运行期改为编译期约束,位宽变化时会直接编译报错,意图与效果更清晰。

♻️ 可选:转为 static_assert
-    if (slave_address > 0x7F) [[unlikely]]
-        co_return false;
+    static_assert(
+        I2cHeader::SlaveAddress::kBitWidth <= 7,
+        "slave_address bitfield width must keep values within 7-bit I2C address range");

根据学习笔记 Learnt from: gqsdjhh PR#43 core/src/protocol/deserializer.cpp:313 —— 此 0x7F 校验是协议层兜底的多层防御之一,本建议不改变其作为“防御点”的定位,仅把运行期兜底替换为编译期约束。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/src/protocol/deserializer.cpp` around lines 313 - 314, The runtime check
"if (slave_address > 0x7F)" is dead because I2cHeader::SlaveAddress is defined
as BitfieldMember<8, 7> so the extracted value cannot exceed 0x7F; replace the
runtime guard with a compile-time assertion to preserve the defensive intent:
add a static_assert that validates the bitfield width/maximum (e.g. that
BitfieldMember<8,7>::max_value == 0x7F or that the bit-width used by
I2cHeader::SlaveAddress is 7) near the I2cHeader/BitfieldMember definitions and
remove the unreachable runtime branch in deserializer.cpp so changes to
BitfieldMember widths will produce a compile error instead of silently disabling
the check.
firmware/rmcs_board/app/src/usb/vendor.hpp (1)

161-171: i2c_read_result / i2c_error 回调静默丢弃的风格建议

can_deserialized_callback / uart_deserialized_callback 等对未知 DataId 均走 assert_failed_always();此处对 read_resulterror 则不分 id 一律静默丢弃。从语义上,host 一般不应向固件下行 kReadResult / kError,收到属于协议滥用。当前实现与 error_callback() 改为“丢弃而非 trap”的整体策略兼容,但相对其它回调的 default: assert_failed_always() 略显不一致。

建议二选一以便意图更明确:

  • 若确定“任何方向的下行 read_result/error 都视为可忽略流量”,在函数体加一行注释明确这是按设计的静默丢弃(与 error_callback 中的注释风格一致);
  • 若希望与 can/uart 保持一致,对未知 id 使用 core::utility::assert_failed_always(),对合法 id 仍 (void)

不阻塞合并。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/rmcs_board/app/src/usb/vendor.hpp` around lines 161 - 171, The two
I2C callbacks i2c_read_result_deserialized_callback and
i2c_error_deserialized_callback currently silently drop all ids; pick one
consistent intent and make it explicit: either (A) mark silent-drop as
deliberate by adding a one-line comment inside each function stating "deliberate
silent discard of downstream kReadResult/kError" (matching error_callback's
comment style) while keeping the (void) casts, or (B) make them strict like
can_deserialized_callback by calling core::utility::assert_failed_always() for
unknown/unsupported ids while still keeping (void)id/(void)data for any handled
ids; update both i2c_read_result_deserialized_callback and
i2c_error_deserialized_callback accordingly so the intent is clear and
consistent with other callbacks.
firmware/rmcs_board/boards/lite/board.c (1)

172-184: 建议对 i2c_config_t 进行零初始化以防御 SDK 字段扩展

当前代码在 i2c_init_master 前显式设置 i2c_modeis_10bit_addressing 两个字段。HPM SDK 后续若在 i2c_config_t 中新增字段,未初始化的字节会被读取并可能引发隐患。做一次零初始化成本为零且可提前规避该风险。

建议的改动
 bool board_init_i2c(I2C_Type* ptr) {
-    i2c_config_t config;
+    i2c_config_t config = {};
     const uint32_t freq = board_init_i2c_clock(ptr);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/rmcs_board/boards/lite/board.c` around lines 172 - 184, The
i2c_config_t config in board_init_i2c is only partially initialized and may read
uninitialized bytes if the SDK adds fields; zero-initialize config (e.g., set to
all zeros or use memset/aggregate initialization) before assigning
config.i2c_mode and config.is_10bit_addressing so that i2c_init_master always
receives a fully-initialized structure.
firmware/rmcs_board/app/src/i2c/i2c.hpp (1)

720-727: 如果 BOARD_APP_I2C_BASE 被定义为指针,constinit 分支会编译失败。

当前代码能成功编译,因为 BOARD_APP_I2C_BASE 在项目中未被定义,故总是走 #ifndef 分支使用 HPM_I2C0_BASE。但如果未来某块板级头将 BOARD_APP_I2C_BASE 定义为 typed pointer(如 I2C_Type* BOARD_APP_I2C_BASE = HPM_I2C0),则 reinterpret_cast<uintptr_t>(BOARD_APP_I2C_BASE)consteval 上下文中无法作为编译期常量表达式被求值,导致编译失败。

建议采用下列方案之一避免此隐患:

  1. 要求板级头提供整数型 base-address 宏(如 #define BOARD_APP_I2C0_BASE 0x...),而非指针
  2. LazyI2c 构造阶段引入中间转换层,允许接受指针但在初始化时(非 consteval 阶段)转换为 uintptr_t
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.devcontainer/devcontainer.json:
- Line 28: Update the postCreateCommand: remove the directory existence checks
`[ -d ~/.codex ]` and `[ -d ~/.claude ]` so the codex and claude install
branches run based on whether the executables are present (the `command -v
codex` / `command -v claude` checks remain), and enable pipefail so failures in
pipelines are propagated (apply `set -o pipefail` or use `bash -o pipefail` for
the shell invocation that runs the curl | bash pipeline). Target the
postCreateCommand entry that contains the codex and claude install logic and the
`curl ... | bash` pipeline.

---

Outside diff comments:
In `@firmware/rmcs_board/app/src/usb/interrupt_safe_buffer.hpp`:
- Around line 42-47: The allocate() path currently silently returns an empty
span when writeable == 0, missing the buffer-full indication present in c_board;
add a platform-agnostic weak hook that this function calls before returning
(e.g. declare a weak function like
librmcs::firmware::usb::on_uplink_buffer_full() or a static callback invoked
from allocate()), and invoke it in the branch where writeable == 0 (around the
TODO) so rmcs_board can provide an LED/logging implementation while keeping a
no-op default in the library.

---

Nitpick comments:
In `@core/src/protocol/deserializer.cpp`:
- Around line 313-314: The runtime check "if (slave_address > 0x7F)" is dead
because I2cHeader::SlaveAddress is defined as BitfieldMember<8, 7> so the
extracted value cannot exceed 0x7F; replace the runtime guard with a
compile-time assertion to preserve the defensive intent: add a static_assert
that validates the bitfield width/maximum (e.g. that
BitfieldMember<8,7>::max_value == 0x7F or that the bit-width used by
I2cHeader::SlaveAddress is 7) near the I2cHeader/BitfieldMember definitions and
remove the unreachable runtime branch in deserializer.cpp so changes to
BitfieldMember widths will produce a compile error instead of silently disabling
the check.

In `@firmware/c_board/bsp/cubemx/Core/Src/dma.c`:
- Around line 47-61: DMA1_Stream2_IRQn and DMA1_Stream7_IRQn are currently set
to priority 1 which allows USB OTG (priority 0) to preempt I2C2 DMA handlers and
can cause I2C state issues; change the HAL_NVIC_SetPriority calls for
DMA1_Stream2_IRQn and DMA1_Stream7_IRQn to a lower priority value (e.g., 4 or 6
to match other DMA streams) so they cannot be preempted by OTG_FS_IRQn, and
ensure this change is consistent with I2C2_EV_IRQn / I2C2_ER_IRQn priority
assignments.

In `@firmware/rmcs_board/app/src/usb/interrupt_safe_buffer.hpp`:
- Line 92: Replace the old-style macro memory order constants with the C++20
enum-style form for consistency: locate the two calls to
std::atomic_signal_fence that currently use std::memory_order_release (lines
around the occurrences) and change them to use std::memory_order::release (and
similarly change any std::memory_order_relaxed to std::memory_order::relaxed if
present); ensure you update both occurrences (the one at the
std::atomic_signal_fence call near line 92 and the other near line 119) so the
file consistently uses std::memory_order::release/::relaxed style.

In `@firmware/rmcs_board/app/src/usb/vendor.hpp`:
- Around line 161-171: The two I2C callbacks
i2c_read_result_deserialized_callback and i2c_error_deserialized_callback
currently silently drop all ids; pick one consistent intent and make it
explicit: either (A) mark silent-drop as deliberate by adding a one-line comment
inside each function stating "deliberate silent discard of downstream
kReadResult/kError" (matching error_callback's comment style) while keeping the
(void) casts, or (B) make them strict like can_deserialized_callback by calling
core::utility::assert_failed_always() for unknown/unsupported ids while still
keeping (void)id/(void)data for any handled ids; update both
i2c_read_result_deserialized_callback and i2c_error_deserialized_callback
accordingly so the intent is clear and consistent with other callbacks.

In `@firmware/rmcs_board/boards/lite/board.c`:
- Around line 172-184: The i2c_config_t config in board_init_i2c is only
partially initialized and may read uninitialized bytes if the SDK adds fields;
zero-initialize config (e.g., set to all zeros or use memset/aggregate
initialization) before assigning config.i2c_mode and config.is_10bit_addressing
so that i2c_init_master always receives a fully-initialized structure.

In `@firmware/rmcs_board/boards/pro/board.c`:
- Around line 173-185: Zero-initialize the i2c_config_t before setting
individual fields in board_init_i2c so any unspecified members don't remain
indeterminate; locate the i2c_config_t config declaration in board_init_i2c and
replace its uninitialized declaration with a zero-initialized one (e.g.,
initialize config with {0} or use memset) then keep setting config.i2c_mode and
config.is_10bit_addressing as before before calling i2c_init_master.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c11d5a41-d2be-4cda-95db-a5f05abf0df4

📥 Commits

Reviewing files that changed from the base of the PR and between 9a1bda6 and 18b590f.

📒 Files selected for processing (40)
  • .codex
  • .devcontainer/devcontainer.json
  • .gitignore
  • core/include/librmcs/data/datas.hpp
  • core/include/librmcs/protocol/i2c.hpp
  • core/src/protocol/deserializer.cpp
  • core/src/protocol/deserializer.hpp
  • core/src/protocol/protocol.hpp
  • core/src/protocol/serializer.hpp
  • firmware/c_board/app/src/app.cpp
  • firmware/c_board/app/src/i2c/i2c.cpp
  • firmware/c_board/app/src/i2c/i2c.hpp
  • firmware/c_board/app/src/usb/interrupt_safe_buffer.hpp
  • firmware/c_board/app/src/usb/vendor.hpp
  • firmware/c_board/bsp/cubemx/Core/Inc/i2c.h
  • firmware/c_board/bsp/cubemx/Core/Inc/stm32f4xx_hal_conf.h
  • firmware/c_board/bsp/cubemx/Core/Inc/stm32f4xx_it.h
  • firmware/c_board/bsp/cubemx/Core/Src/dma.c
  • firmware/c_board/bsp/cubemx/Core/Src/gpio.c
  • firmware/c_board/bsp/cubemx/Core/Src/i2c.c
  • firmware/c_board/bsp/cubemx/Core/Src/main.c
  • firmware/c_board/bsp/cubemx/Core/Src/stm32f4xx_it.c
  • firmware/c_board/bsp/cubemx/cmake/stm32cubemx/CMakeLists.txt
  • firmware/c_board/bsp/cubemx/rmcs_slave.ioc
  • firmware/rmcs_board/app/CMakeLists.txt
  • firmware/rmcs_board/app/src/app.cpp
  • firmware/rmcs_board/app/src/i2c/i2c.cpp
  • firmware/rmcs_board/app/src/i2c/i2c.hpp
  • firmware/rmcs_board/app/src/usb/interrupt_safe_buffer.hpp
  • firmware/rmcs_board/app/src/usb/vendor.hpp
  • firmware/rmcs_board/boards/lite/board.c
  • firmware/rmcs_board/boards/lite/board.h
  • firmware/rmcs_board/boards/pro/board.c
  • firmware/rmcs_board/boards/pro/board.h
  • host/include/librmcs/agent/c_board.hpp
  • host/include/librmcs/agent/detail/i2c0_common.hpp
  • host/include/librmcs/agent/rmcs_board_lite.hpp
  • host/include/librmcs/agent/rmcs_board_pro.hpp
  • host/include/librmcs/protocol/handler.hpp
  • host/src/protocol/handler.cpp

Comment thread .devcontainer/devcontainer.json Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
firmware/rmcs_board/boards/pro/board.c (1)

173-184: board_init_i2c 流程本身合理,但总线清失败时未做状态清理

时序为 clock → pins → bus_clear → master init,在 board_i2c_bus_clear 失败时直接 return false,此时 clock_i2c0 已被加入 group、PA18/PA19 也已切到 I2C 功能但控制器未初始化。上层 I2c::try_initialize 会反复重试 board_init_i2c,每次都会再调用 clock_add_to_group(幂等)和重写 IOC(幂等),行为上是可接受的。

不过若 SCL 被从机持续拉低(i2c_get_line_scl_status == false 立即 return false),这里永远不会触发 i2c_gen_reset_signal,也不会对控制器做任何恢复尝试。考虑在 SCL 低的分支里也先尝试一次 i2c_gen_reset_signal 再判定,可覆盖上电瞬间 SCL 尚未稳定的边缘场景。非阻塞重试建议,不是必须。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/rmcs_board/boards/pro/board.c` around lines 173 - 184,
board_init_i2c currently returns false immediately when board_i2c_bus_clear
fails, leaving clock/pins configured and not attempting controller recovery;
modify board_init_i2c to, when board_i2c_bus_clear reports failure due to SCL
held low (use i2c_get_line_scl_status to detect), call i2c_gen_reset_signal once
to try to recover the bus and then re-run board_i2c_bus_clear (non-blocking
single retry) before giving up, keeping existing clock_add_to_group and
board_init_i2c_pins behavior and only returning false if the reset+retry also
fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@firmware/rmcs_board/boards/pro/board.c`:
- Around line 125-136: board_init_i2c_pins() sets
IOC_PAD_FUNC_CTL_LOOP_BACK_MASK for PA18 (SCL) but not PA19 (SDA), causing
i2c_get_line_sda_status() in board_i2c_bus_clear() to read SDA unreliably;
update the PA19 FUNC_CTL assignment in board_init_i2c_pins (both in pro and lite
board.c) to include IOC_PAD_FUNC_CTL_LOOP_BACK_MASK alongside
IOC_PA19_FUNC_CTL_I2C0_SDA so SDA is driven/sensed symmetrically with SCL (i.e.,
change the IOC_PAD[IOC_PAD_PA19].FUNC_CTL value to OR in
IOC_PAD_FUNC_CTL_LOOP_BACK_MASK).

---

Nitpick comments:
In `@firmware/rmcs_board/boards/pro/board.c`:
- Around line 173-184: board_init_i2c currently returns false immediately when
board_i2c_bus_clear fails, leaving clock/pins configured and not attempting
controller recovery; modify board_init_i2c to, when board_i2c_bus_clear reports
failure due to SCL held low (use i2c_get_line_scl_status to detect), call
i2c_gen_reset_signal once to try to recover the bus and then re-run
board_i2c_bus_clear (non-blocking single retry) before giving up, keeping
existing clock_add_to_group and board_init_i2c_pins behavior and only returning
false if the reset+retry also fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 59077b22-ac42-483a-9886-3118b42662b6

📥 Commits

Reviewing files that changed from the base of the PR and between 18b590f and a06053e.

📒 Files selected for processing (12)
  • .devcontainer/devcontainer.json
  • core/src/protocol/deserializer.cpp
  • core/src/protocol/protocol.hpp
  • firmware/c_board/app/src/usb/vendor.hpp
  • firmware/rmcs_board/app/src/i2c/i2c.hpp
  • firmware/rmcs_board/app/src/usb/interrupt_safe_buffer.hpp
  • firmware/rmcs_board/app/src/usb/uplink_buffer_hook.cpp
  • firmware/rmcs_board/app/src/usb/vendor.hpp
  • firmware/rmcs_board/boards/lite/board.c
  • firmware/rmcs_board/boards/lite/board.h
  • firmware/rmcs_board/boards/pro/board.c
  • firmware/rmcs_board/boards/pro/board.h
✅ Files skipped from review due to trivial changes (1)
  • core/src/protocol/deserializer.cpp
🚧 Files skipped from review as they are similar to previous changes (6)
  • firmware/rmcs_board/boards/lite/board.h
  • .devcontainer/devcontainer.json
  • firmware/rmcs_board/app/src/usb/vendor.hpp
  • firmware/rmcs_board/boards/lite/board.c
  • firmware/c_board/app/src/usb/vendor.hpp
  • firmware/rmcs_board/boards/pro/board.h

Comment thread firmware/rmcs_board/boards/pro/board.c
@gqsdjhh
Copy link
Copy Markdown
Author

gqsdjhh commented Apr 18, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@gqsdjhh gqsdjhh closed this Apr 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in RMCS Slave SDK Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant