feat(i2c): Add i2c support and stabilize c_board transfer#41
feat(i2c): Add i2c support and stabilize c_board transfer#41gqsdjhh wants to merge 3 commits intoAlliance-Algorithm:mainfrom
Conversation
走读添加了I2C协议支持,涵盖数据结构定义、协议序列化/反序列化、STM32和HPM硬件驱动实现,以及主机端的I2C通信接口。 变更
序列图sequenceDiagram
participant Host as 主机应用
participant Serializer as 协议序列化器
participant USB as USB驱动
participant Deserializer as 协议反序列化器
participant Handler as 消息处理器
participant I2C as I2C驱动
participant HAL as STM32/HPM HAL
Host->>Serializer: write_i2c_write(kI2c0, data)
Serializer->>USB: 发送序列化报文
USB->>Deserializer: 接收字节流
Deserializer->>Deserializer: process_i2c_field()
Deserializer->>Handler: i2c_write_deserialized_callback()
Handler->>I2C: handle_downlink_write()
I2C->>HAL: HAL_I2C_Mem_Write/Master_Transmit
HAL-->>I2C: 成功或失败
alt 写入成功
I2C-->>Handler: 返回
else 写入失败
I2C->>Serializer: write_i2c_error()
Serializer->>USB: 发送错误报文
end
Host->>Serializer: write_i2c_read_config(kI2c0, config)
Serializer->>USB: 发送读请求
USB->>Deserializer: 接收字节流
Deserializer->>Handler: i2c_read_config_deserialized_callback()
Handler->>I2C: handle_downlink_read_config()
I2C->>HAL: HAL_I2C_Mem_Read/Master_Receive
HAL-->>I2C: 数据或失败
alt 读取成功
I2C->>Serializer: write_i2c_read_result()
Serializer->>USB: 发送读结果
USB->>Host: 上行数据
else 读取失败
I2C->>Serializer: write_i2c_error()
Serializer->>USB: 发送错误报文
end
估算代码审查工作量🎯 4 (复杂) | ⏱️ ~50 分钟 可能相关的PR
诗歌
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
firmware/c_board/app/src/app.cpp (1)
86-86: 建议明确 Line 86 的 no-op 调用意图。Line 86 当前调用
i2c::i2c0->update(),但firmware/c_board/app/src/i2c/i2c.hpp的update()为空实现。建议加注释说明“保留扩展点”,避免后续维护误判。可选改进示例
- i2c::i2c0->update(); + // Keep this call as an extension point for future non-blocking I2C work. + i2c::i2c0->update();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@firmware/c_board/app/src/app.cpp` at line 86, 在调用 i2c::i2c0->update() 的位置(当前在 app.cpp 第 86 行)明确说明这是一处空实现的保留扩展点:在调用旁边添加注释,注明 i2c::I2C::update() 在 firmware/c_board/app/src/i2c/i2c.hpp 中目前为空实现、用于未来扩展或平台特定实现,并指明不要删除或误以为是忘写逻辑;同时报错/断言等不可行时保持调用以便兼容未来实现。host/include/librmcs/agent/rmcs_board.hpp (1)
75-85: 考虑添加[[unlikely]]属性以保持一致性。
c_board.hpp中的i2c0_write和i2c0_read方法在错误条件上使用了[[unlikely]]属性,但此文件缺少该属性。建议保持两个 board 实现的一致性。♻️ 建议的修改
PacketBuilder& i2c0_write(const librmcs::data::I2cDataView& data) { - if (data.payload.empty() || !builder_.write_i2c(data::DataId::kI2c0, data)) + if (data.payload.empty() || !builder_.write_i2c(data::DataId::kI2c0, data)) [[unlikely]] throw std::invalid_argument{"I2C0 write failed: Invalid I2C data"}; return *this; } PacketBuilder& i2c0_read(const librmcs::data::I2cReadConfigView& data) { - if (data.read_length == 0 || !builder_.write_i2c_read_config(data::DataId::kI2c0, data)) + if (data.read_length == 0 || !builder_.write_i2c_read_config(data::DataId::kI2c0, data)) + [[unlikely]] throw std::invalid_argument{"I2C0 read failed: Invalid I2C read config"}; return *this; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@host/include/librmcs/agent/rmcs_board.hpp` around lines 75 - 85, The error branches in PacketBuilder::i2c0_write and PacketBuilder::i2c0_read lack the [[unlikely]] attribute used in the c_board.hpp equivalent; update the conditional checks so the throw paths are annotated with [[unlikely]] (i.e., mark the failing condition as unlikely when checking data.payload.empty() or data.read_length == 0 and the builder_.write_* calls) to match the other board implementation and maintain consistency between i2c0_write and i2c0_read error branches.firmware/c_board/app/src/i2c/i2c.hpp (1)
28-40: 空 payload 静默返回 vs 发送错误通知 - 建议对齐行为。当
payload.empty()时,此方法静默返回(line 29-30),而当payload.size() > kMaxDataLength时发送错误通知(line 32-35)。对比
rmcs_board版本的handle_downlink_write(同样静默返回),两者行为一致。但从协议角度,主机端可能期望收到反馈。如果这是预期行为(空写入不是错误),可以添加注释说明设计意图。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@firmware/c_board/app/src/i2c/i2c.hpp` around lines 28 - 40, The function handle_downlink_write currently returns silently when data.payload.empty() but sends an error when payload.size() > kMaxDataLength, causing inconsistent host feedback; update handle_downlink_write to either call write_error(data.slave_address) for empty payloads to align behavior with oversized payloads, or add a clear code comment above the empty-payload check explaining that an empty write is intentionally treated as a no-op and not an error (and reference write_error, kMaxDataLength, and write_blocking in the comment to clarify the contract).
🤖 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/c_board/bsp/cubemx/Core/Src/i2c.c`:
- Around line 85-88: 当前在 i2c.c 中对 I2C2 事件/错误中断使用 HAL_NVIC_SetPriority(..., 0, 0)
并启用 IRQ (HAL_NVIC_EnableIRQ(I2C2_EV_IRQn)/HAL_NVIC_EnableIRQ(I2C2_ER_IRQn)),会与
USB (OTG_FS_IRQn) 冲突且你的传输调用 HAL_I2C_Master_Transmit()/HAL_I2C_Master_Receive()
是阻塞式,不需要中断;请在配置中将 I2C2_EV_IRQn 和 I2C2_ER_IRQn 的优先级改为 HAL_NVIC_SetPriority(...,
1, 0) 或直接移除/注释掉对应的 HAL_NVIC_EnableIRQ 调用(如果不需要错误中断处理),并确保修改发生在配置 I2C2
中断的初始化函数(查找 HAL_NVIC_SetPriority / HAL_NVIC_EnableIRQ 对 I2C2 的调用处)。
In `@firmware/rmcs_board/app/src/i2c/i2c.hpp`:
- Around line 58-60: handle_downlink_read_config currently silently returns when
data.read_length is 0 or > kMaxDataLength; change this to send the same I2C
error notification the c_board implementation uses instead of silent return. In
the handle_downlink_read_config function (and when validating
data::I2cReadConfigView.read_length against kMaxDataLength) emit the I2C error
notification/response (use the project’s existing notify/send I2C error API used
by the c_board codebase) with a descriptive error reason before returning so the
host receives the error feedback.
- Around line 32-56: handle_downlink_write currently omits a payload size limit
check: before using data.payload in i2c_master_write/i2c_master_address_write
validate that data.payload.size() <= kMaxDataLength (same limit used in
c_board), and if it exceeds, call
usb::get_serializer().write_i2c_error(data_id_, data.slave_address) (assert on
non-kInvalidArgument like existing error paths) and return; reference symbols:
handle_downlink_write, data::I2cDataView, kMaxDataLength, write_i2c_error,
data_id_.
In `@firmware/rmcs_board/bsp/hpm_sdk`:
- Line 1: 回退移除了 GPIO 初始化(board_init_gpio_pins)和 Flash/XPI 配置,可能破坏依赖于 GPIO 状态的
HAL I2C 传输;请在 PR 中补充回退原因与兼容性评估、并在代码里恢复或有条件调用
board_init_gpio_pins()(或提供替代初始化路径)以保证 I2C 初始化/传输和中断处理行为一致;在本地或 CI 运行覆盖性验证:1) 验证
HAL I2C init/transfer 在无 board_init_gpio_pins 情况下是否失败;2) 验证中断和 I/O 线路的电平/复用设置;3)
恢复或迁移 Flash/XPI NOR board 配置并记录任何行为差异;在 PR 描述中列出对比 a82a5354 与 132349c9
的差异、测试结果和最终决定(恢复、条件初始化或明确不兼容)。
---
Nitpick comments:
In `@firmware/c_board/app/src/app.cpp`:
- Line 86: 在调用 i2c::i2c0->update() 的位置(当前在 app.cpp 第 86
行)明确说明这是一处空实现的保留扩展点:在调用旁边添加注释,注明 i2c::I2C::update() 在
firmware/c_board/app/src/i2c/i2c.hpp
中目前为空实现、用于未来扩展或平台特定实现,并指明不要删除或误以为是忘写逻辑;同时报错/断言等不可行时保持调用以便兼容未来实现。
In `@firmware/c_board/app/src/i2c/i2c.hpp`:
- Around line 28-40: The function handle_downlink_write currently returns
silently when data.payload.empty() but sends an error when payload.size() >
kMaxDataLength, causing inconsistent host feedback; update handle_downlink_write
to either call write_error(data.slave_address) for empty payloads to align
behavior with oversized payloads, or add a clear code comment above the
empty-payload check explaining that an empty write is intentionally treated as a
no-op and not an error (and reference write_error, kMaxDataLength, and
write_blocking in the comment to clarify the contract).
In `@host/include/librmcs/agent/rmcs_board.hpp`:
- Around line 75-85: The error branches in PacketBuilder::i2c0_write and
PacketBuilder::i2c0_read lack the [[unlikely]] attribute used in the c_board.hpp
equivalent; update the conditional checks so the throw paths are annotated with
[[unlikely]] (i.e., mark the failing condition as unlikely when checking
data.payload.empty() or data.read_length == 0 and the builder_.write_* calls) to
match the other board implementation and maintain consistency between i2c0_write
and i2c0_read error branches.
🪄 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: 99e630ac-7991-4c1f-89de-584c2a600765
📒 Files selected for processing (26)
.codexcore/include/librmcs/data/datas.hppcore/src/protocol/deserializer.cppcore/src/protocol/deserializer.hppcore/src/protocol/protocol.hppcore/src/protocol/serializer.hppfirmware/c_board/app/src/app.cppfirmware/c_board/app/src/i2c/i2c.hppfirmware/c_board/app/src/usb/vendor.hppfirmware/c_board/bsp/cubemx/Core/Inc/i2c.hfirmware/c_board/bsp/cubemx/Core/Inc/stm32f4xx_hal_conf.hfirmware/c_board/bsp/cubemx/Core/Inc/stm32f4xx_it.hfirmware/c_board/bsp/cubemx/Core/Src/gpio.cfirmware/c_board/bsp/cubemx/Core/Src/i2c.cfirmware/c_board/bsp/cubemx/Core/Src/main.cfirmware/c_board/bsp/cubemx/Core/Src/stm32f4xx_it.cfirmware/c_board/bsp/cubemx/cmake/stm32cubemx/CMakeLists.txtfirmware/c_board/bsp/cubemx/rmcs_slave.iocfirmware/rmcs_board/app/src/app.cppfirmware/rmcs_board/app/src/i2c/i2c.hppfirmware/rmcs_board/app/src/usb/vendor.hppfirmware/rmcs_board/bsp/hpm_sdkhost/include/librmcs/agent/c_board.hpphost/include/librmcs/agent/rmcs_board.hpphost/include/librmcs/protocol/handler.hpphost/src/protocol/handler.cpp
| HAL_NVIC_SetPriority(I2C2_EV_IRQn, 0, 0); | ||
| HAL_NVIC_EnableIRQ(I2C2_EV_IRQn); | ||
| HAL_NVIC_SetPriority(I2C2_ER_IRQn, 0, 0); | ||
| HAL_NVIC_EnableIRQ(I2C2_ER_IRQn); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 检查其他外设的中断优先级配置
echo "=== 检查 c_board BSP 中其他外设的中断优先级 ==="
rg "HAL_NVIC_SetPriority" firmware/c_board/bsp/cubemx/ --type c -n
echo ""
echo "=== 检查 I2C 模块是否使用阻塞式或中断式传输 ==="
rg "HAL_I2C_(Master_Transmit|Master_Receive)" firmware/c_board/app/src/i2c/ --type cpp -n -A 2Repository: Alliance-Algorithm/librmcs
Length of output: 2522
I2C2 中断优先级与 USB 冲突,且阻塞式传输不需要中断。
I2C2 事件和错误中断配置优先级 (0, 0) 与 USB OTG (OTG_FS_IRQn) 共享最高优先级,可能导致中断竞争。同时,代码在 firmware/c_board/app/src/i2c/i2c.hpp 中使用 HAL_I2C_Master_Transmit() 和 HAL_I2C_Master_Receive() 等阻塞式传输函数(polling 模式),不需要启用中断来完成传输控制。建议将 I2C2 中断优先级降低至 (1, 0) 以避免与 USB 冲突,或如果中断仅用于错误处理且该功能可通过其他机制实现,考虑禁用这些中断。
🤖 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/i2c.c` around lines 85 - 88, 当前在 i2c.c
中对 I2C2 事件/错误中断使用 HAL_NVIC_SetPriority(..., 0, 0) 并启用 IRQ
(HAL_NVIC_EnableIRQ(I2C2_EV_IRQn)/HAL_NVIC_EnableIRQ(I2C2_ER_IRQn)),会与 USB
(OTG_FS_IRQn) 冲突且你的传输调用 HAL_I2C_Master_Transmit()/HAL_I2C_Master_Receive()
是阻塞式,不需要中断;请在配置中将 I2C2_EV_IRQn 和 I2C2_ER_IRQn 的优先级改为 HAL_NVIC_SetPriority(...,
1, 0) 或直接移除/注释掉对应的 HAL_NVIC_EnableIRQ 调用(如果不需要错误中断处理),并确保修改发生在配置 I2C2
中断的初始化函数(查找 HAL_NVIC_SetPriority / HAL_NVIC_EnableIRQ 对 I2C2 的调用处)。
| void handle_downlink_write(const data::I2cDataView& data) { | ||
| if (data.payload.empty()) | ||
| return; | ||
|
|
||
| const auto* payload = reinterpret_cast<const uint8_t*>(data.payload.data()); | ||
| hpm_stat_t status = status_fail; | ||
| if (data.has_register) { | ||
| uint8_t reg_address = data.reg_address; | ||
| status = i2c_master_address_write( | ||
| i2c_base_, data.slave_address, ®_address, sizeof(reg_address), | ||
| const_cast<uint8_t*>(payload), static_cast<uint32_t>(data.payload.size())); | ||
| } else { | ||
| status = i2c_master_write( | ||
| i2c_base_, data.slave_address, const_cast<uint8_t*>(payload), | ||
| static_cast<uint32_t>(data.payload.size())); | ||
| } | ||
|
|
||
| if (status != status_success) { | ||
| auto& serializer = usb::get_serializer(); | ||
| core::utility::assert_always( | ||
| serializer.write_i2c_error(data_id_, data.slave_address) | ||
| != core::protocol::Serializer::SerializeResult::kInvalidArgument); | ||
| return; | ||
| } | ||
| } |
There was a problem hiding this comment.
缺少 payload 大小上限验证。
c_board 版本在 handle_downlink_write 中验证 payload.size() > kMaxDataLength 并发送错误通知(firmware/c_board/app/src/i2c/i2c.hpp lines 32-35),但此实现缺少该检查。
如果 payload 超过 511 字节,传输可能会失败或产生未定义行为。建议添加与 c_board 一致的验证逻辑。
🛡️ 建议添加大小验证
void handle_downlink_write(const data::I2cDataView& data) {
if (data.payload.empty())
return;
+ if (data.payload.size() > kMaxDataLength) {
+ auto& serializer = usb::get_serializer();
+ core::utility::assert_always(
+ serializer.write_i2c_error(data_id_, data.slave_address)
+ != core::protocol::Serializer::SerializeResult::kInvalidArgument);
+ return;
+ }
+
const auto* payload = reinterpret_cast<const uint8_t*>(data.payload.data());🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@firmware/rmcs_board/app/src/i2c/i2c.hpp` around lines 32 - 56,
handle_downlink_write currently omits a payload size limit check: before using
data.payload in i2c_master_write/i2c_master_address_write validate that
data.payload.size() <= kMaxDataLength (same limit used in c_board), and if it
exceeds, call usb::get_serializer().write_i2c_error(data_id_,
data.slave_address) (assert on non-kInvalidArgument like existing error paths)
and return; reference symbols: handle_downlink_write, data::I2cDataView,
kMaxDataLength, write_i2c_error, data_id_.
| void handle_downlink_read_config(const data::I2cReadConfigView& data) { | ||
| if (data.read_length == 0 || data.read_length > kMaxDataLength) | ||
| return; |
There was a problem hiding this comment.
无效 read_length 时应发送错误通知而非静默返回。
当 read_length == 0 或 > kMaxDataLength 时,此实现静默返回(line 59-60),但 c_board 版本会发送 I2C 错误通知给主机(firmware/c_board/app/src/i2c/i2c.hpp lines 43-46)。
这种不一致可能导致主机端在不同板子上收到不同的错误反馈行为。
🛡️ 建议添加错误通知
void handle_downlink_read_config(const data::I2cReadConfigView& data) {
- if (data.read_length == 0 || data.read_length > kMaxDataLength)
+ if (data.read_length == 0 || data.read_length > kMaxDataLength) {
+ auto& serializer = usb::get_serializer();
+ core::utility::assert_always(
+ serializer.write_i2c_error(data_id_, data.slave_address)
+ != core::protocol::Serializer::SerializeResult::kInvalidArgument);
return;
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@firmware/rmcs_board/app/src/i2c/i2c.hpp` around lines 58 - 60,
handle_downlink_read_config currently silently returns when data.read_length is
0 or > kMaxDataLength; change this to send the same I2C error notification the
c_board implementation uses instead of silent return. In the
handle_downlink_read_config function (and when validating
data::I2cReadConfigView.read_length against kMaxDataLength) emit the I2C error
notification/response (use the project’s existing notify/send I2C error API used
by the c_board codebase) with a descriptive error reason before returning so the
host receives the error feedback.
| @@ -1 +1 @@ | |||
| Subproject commit a82a53544d5679a43b70f390c8bbbb8e91f6b8c1 | |||
| Subproject commit 132349c9ddad3cbeca7211345d5d6086355f20b9 | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Submodule pointer change =="
git diff --submodule=log origin/main...HEAD -- firmware/rmcs_board/bsp/hpm_sdk || true
echo
echo "== .gitmodules entry =="
sed -n '/firmware\/rmcs_board\/bsp\/hpm_sdk/,+5p' .gitmodules || true
echo
echo "== Old/New gitlinks =="
echo "[origin/main]"
git ls-tree origin/main -- firmware/rmcs_board/bsp/hpm_sdk || true
echo
echo "[HEAD]"
git ls-tree HEAD -- firmware/rmcs_board/bsp/hpm_sdk || trueRepository: Alliance-Algorithm/librmcs
Length of output: 676
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Check if hpm_sdk submodule is initialized =="
if [ -d "firmware/rmcs_board/bsp/hpm_sdk/.git" ]; then
echo "✓ Submodule is initialized"
cd firmware/rmcs_board/bsp/hpm_sdk
echo
echo "== Commit log between old and new commit =="
git log --oneline a82a53544d5679a43b70f390c8bbbb8e91f6b8c1..132349c9ddad3cbeca7211345d5d6086355f20b9 || true
echo
echo "== Detailed commit info for new commit =="
git show --stat 132349c9ddad3cbeca7211345d5d6086355f20b9 | head -100 || true
else
echo "✗ Submodule not initialized locally"
echo "Submodule path: firmware/rmcs_board/bsp/hpm_sdk"
fiRepository: Alliance-Algorithm/librmcs
Length of output: 201
🌐 Web query:
site:github.com/Alliance-Algorithm/hpm_sdk compare a82a53544d5679a43b70f390c8bbbb8e91f6b8c1...132349c9ddad3cbeca7211345d5d6086355f20b9
💡 Result:
GitHub shows no changes to compare between these commits: a82a5354... already contains all commits from 132349c9... (i.e., 132349c9... is behind / an ancestor of a82a5354...). (github.com)
If you want to see the reverse difference, switch the base/head (compare 132349c9... as base against a82a5354... as head). (github.com)
Citations:
🌐 Web query:
site:github.com/Alliance-Algorithm/hpm_sdk compare 132349c9ddad3cbeca7211345d5d6086355f20b9...a82a53544d5679a43b70f390c8bbbb8e91f6b8c1
💡 Result:
Comparison 132349c9ddad3cbeca7211345d5d6086355f20b9...a82a53544d5679a43b70f390c8bbbb8e91f6b8c1 in Alliance-Algorithm/hpm_sdk contains 1 commit (“iter1”, committed Mar 31, 2026 by qzhhhi), touching 2 files with +13 / -3 lines. GitHub couldn’t render the full compare view (“taking too long to generate”), but the commit diff is visible. [1][2]
Changes:
boards/hpm5300evk/board.c: adds a new functionboard_init_gpio_pins()that sets the app GPIO pin as input viagpio_set_pin_input(...). [2]boards/hpm5300evk/board.h:- adds board flash/XPI NOR related defines:
BOARD_FLASH_BASE_ADDRESS,BOARD_FLASH_SIZE,BOARD_APP_XPI_NOR_XPI_BASE, andBOARD_APP_XPI_NOR_CFG_OPT_*. [2] - changes the
board_init_gpio_pinsdeclaration from commented-out to enabled (void board_init_gpio_pins(void);). [2]
- adds board flash/XPI NOR related defines:
Sources: [1] compare view, [2] commit a82a535 diff.
这是版本回退,移除了 GPIO 初始化代码——需要明确说明回退原因和兼容性影响。
这次变更并非升级,而是从 a82a5354 回退到其祖先 132349c9。被回退的 commit(2026-03-31)移除了:
board_init_gpio_pins()函数及其 GPIO 初始化逻辑- Flash/XPI NOR 相关的 board 配置定义
当前 PR 直接依赖 HAL I2C 传输,如果 I2C 初始化或数据线依赖于 board_init_gpio_pins() 的 GPIO 状态配置,这个回退可能导致 I2C 传输失败。PR 缺少:
- 说明为什么需要此次回退
- GPIO 初始化被移除后的兼容性验证
- I2C/中断处理与 GPIO 初始化间的依赖关系说明
请补充这些信息或在本地完整验证后再提交。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@firmware/rmcs_board/bsp/hpm_sdk` at line 1, 回退移除了 GPIO
初始化(board_init_gpio_pins)和 Flash/XPI 配置,可能破坏依赖于 GPIO 状态的 HAL I2C 传输;请在 PR
中补充回退原因与兼容性评估、并在代码里恢复或有条件调用 board_init_gpio_pins()(或提供替代初始化路径)以保证 I2C
初始化/传输和中断处理行为一致;在本地或 CI 运行覆盖性验证:1) 验证 HAL I2C init/transfer 在无
board_init_gpio_pins 情况下是否失败;2) 验证中断和 I/O 线路的电平/复用设置;3) 恢复或迁移 Flash/XPI NOR
board 配置并记录任何行为差异;在 PR 描述中列出对比 a82a5354 与 132349c9
的差异、测试结果和最终决定(恢复、条件初始化或明确不兼容)。
This PR adds end-to-end I2C support across the protocol, host SDK, and firmware.
It starts by introducing the I2C protocol and
RmcsBoardsupport, then addsc_boardI2C2 transport support, and finally improvesc_boardreliability by replacing the previous queued interrupt-driven implementation with blocking HAL I2C transfers.
The main motivation for the last change is to avoid internal queue-overflow failures during bursty workloads such as OLED initialization and full-screen updates.
Included Changes
RmcsBoardrmcs_boardCBoardc_boardc_boardCubeMX I2C and interrupt plumbingc_boardwith blocking HAL I2C operationsI2C 支持端到端实现与 c_board 传输稳定化
协议与数据结构
DataId::kI2c0 = 16标识符用于 I2C 0 通道。I2cDataView结构体,包含从机地址、有效负载跨度及可选的寄存器寻址字段 (has_register/reg_address)。I2cReadConfigView结构体,定义 I2C 读取配置,包含从机地址、读取长度及可选的寄存器信息。I2cHeader位域结构体 (utility::Bitfield<3>),定义有效负载类型枚举 (kWrite = 0、kReadRequest = 1、kReadResult = 2、kError = 3) 及相关位字段成员 (PayloadType、HasRegister、ErrorFlag、SlaveAddress、DataLength)。DataCallback接口,新增i2c_receive_callback()和i2c_error_callback()两个纯虚函数。协议序列化与反序列化
write_i2c_write()、write_i2c_read_config()、write_i2c_read_result()及write_i2c_error()方法,均包含参数验证 (如从机地址 ≤ 0x7F)、缓冲区分配检查及条件性有效负载复制。required_i2c_size()辅助函数,用于计算所需缓冲区大小,验证有效负载/数据长度约束。process_i2c_field(),解析I2cHeader、可选的寄存器字节,根据有效负载类型分发到相应的反序列化回调 (i2c_write_deserialized_callback、i2c_read_config_deserialized_callback、i2c_read_result_deserialized_callback、i2c_error_deserialized_callback)。C Board 固件 (STM32F4xx)
i2c::I2c类,通过 STM32 HAL 提供 阻塞式 I2C 传输 替代之前的队列驱动异步方式,从而避免 I2C 队列溢出故障。handle_downlink_write()和handle_downlink_read_config()方法,分别调用HAL_I2C_Master_Transmit()/HAL_I2C_Mem_Write()和HAL_I2C_Master_Receive()/HAL_I2C_Mem_Read(),均使用固定超时和内部固定大小缓冲区。i2c0绑定至data::DataId::kI2c0和 HAL 句柄&hi2c2。HAL_I2C_MODULE_ENABLED)。I2C2_EV_IRQHandler、I2C2_ER_IRQHandler),委托至 HAL 驱动。i2c.c及 STM32 HAL I2C 驱动实现 (stm32f4xx_hal_i2c.c、stm32f4xx_hal_i2c_ex.c)。DataId::kI2c0路由至i2c::i2c0的处理方法。RmcsBoard 固件 (HPM SDK)
i2c::I2c类,封装 I2C 初始化和下行处理,支持寄存器寻址和原始从机访问。handle_downlink_write()和handle_downlink_read_config()方法,分别调用i2c_master_address_write()/i2c_master_write()和i2c_master_address_read()/i2c_master_read()。i2c0使用HPM_I2C0_BASE或可配置的BOARD_APP_I2C_BASE。主机端 API
RmcsBoard
PacketBuilder新增i2c0_write(const I2cDataView&)和i2c0_read(const I2cReadConfigView&)方法,均进行输入验证 (非空有效负载/非零读取长度)。i2c0_receive_callback()和i2c0_error_callback(),缺省为空操作。CBoard
PacketBuilder新增i2c0_write()和i2c0_read()方法,同样进行输入验证。i2c0_receive_callback()和i2c0_error_callback(),缺省为空操作。协议处理层 (Handler)
PacketBuilder新增write_i2c()、write_i2c_read_config()及write_i2c_read_result()方法,用于构建和发送 I2C 消息。关键设计决策
has_register标志支持寄存器内存访问,使用条件性的HAL_I2C_Mem_*()或基础HAL_I2C_Master_*()调用。