feat(gpio)!: Add GPIO support for rmcs_board#48
Conversation
|
Note Reviews pausedUse the following commands to manage reviews:
Use the checkboxes below for quick actions:
Walkthrough本PR将GPIO通道从数据视图字段移除为显式的 Changes
Sequence Diagram(s)sequenceDiagram
participant Host as Host/Agent
participant Serializer as Host Serializer
participant Transport as USB/Transport
participant Deserializer as Firmware Deserializer
participant GPIO as Firmware GPIO Handler
participant SerializerFW as Firmware Serializer
participant HostHandler as Host Handler
Host->>Serializer: 构建 GPIO 报文(含 channel_index)
Serializer->>Transport: 发送报文字节
Transport->>Deserializer: 传递报文
Deserializer->>GPIO: gpio_*_deserialized(channel_index, data_view)
GPIO->>SerializerFW: write_gpio_*_read_result(channel_index, data_view)
SerializerFW->>Transport: 发送结果报文
Transport->>HostHandler: 传递结果报文
HostHandler->>Host: 回调 gpio_*_read_result_callback(channel_index, data_view)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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: 1
🧹 Nitpick comments (4)
core/include/librmcs/spec/c_board/gpio.hpp (1)
46-48: 建议在operator[]中加入越界断言。当前
operator[]未做边界检查,若上游忘记channel_index < size()校验即直接索引将触发 UB。可参考firmware/c_board/app/src/gpio/gpio.hpp中channel_state()/channel_hardware()的做法,加一个assert_debug提高防御性。虽然现有调用点(vendor.hpp)已做了前置校验,但作为公开可见的容器访问接口,断言能避免未来新增调用方忘记检查。♻️ 建议改动
- static constexpr const GpioDescriptor& operator[](std::size_t channel_index) noexcept { - return kArray[channel_index]; - } + static constexpr const GpioDescriptor& operator[](std::size_t channel_index) noexcept { + // NOTE: 需先引入 assert 头;非 constexpr 上下文的越界会触发断言。 + // 对 constexpr 求值,越界访问会自动编译失败。 + return kArray[channel_index]; + }(若希望保持
constexpr,可考虑改用std::array并在非 constexpr 求值路径上通过utility::assert_debug校验,或直接保留现状并在文档注释中明确 API 前置条件。)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/include/librmcs/spec/c_board/gpio.hpp` around lines 46 - 48, The operator[] accessor for GpioDescriptor (static constexpr const GpioDescriptor& operator[](std::size_t channel_index) noexcept) lacks a bounds check and can trigger undefined behavior; add a defensive assert_debug that verifies channel_index < size() (or kArray.size()) at the start of operator[] before returning kArray[channel_index]; keep the noexcept/constexpr intent in mind (you can keep the assert_guard disabled in constexpr evaluation or follow the existing pattern used in channel_state()/channel_hardware() in firmware/c_board/app/src/gpio/gpio.hpp).core/src/protocol/serializer.hpp (1)
123-291: 建议在 6 个write_gpio_*入口统一校验channel_index是否超出 6 bit 范围。
GpioHeader::ChannelIndex是BitfieldMember<8, 6>(6 bit,最大 63)。目前 6 个write_gpio_*方法都没有校验channel_index,若上游传入>= 64的值,位域写入会静默截断高位,产生错误的线协议编码而不返回kInvalidArgument。现阶段调用方都基于kGpioDescriptors(size=7)做了前置约束,但 serializer 作为通用协议原语,加一行防御性校验更稳妥,也与本文件中period_ms <= (1U << 13) - 1U的校验风格一致。♻️ 建议改动(以 `write_gpio_digital_data` 为例;其余 5 个方法同样处理)
SerializeResult write_gpio_digital_data( uint8_t channel_index, const data::GpioDigitalDataView& view) noexcept { + LIBRMCS_VERIFY_LIKELY(channel_index < (1U << 6), SerializeResult::kInvalidArgument); const auto payload_type = view.high ? GpioHeader::PayloadEnum::kDigitalWriteHigh : GpioHeader::PayloadEnum::kDigitalWriteLow;可考虑抽成一个私有常量,例如
static constexpr std::uint8_t kMaxChannelIndex = (1U << 6) - 1U;,在全部 6 个 GPIO 写入方法中复用。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/src/protocol/serializer.hpp` around lines 123 - 291, Add a defensive bounds check for channel_index against the 6-bit limit (max 63) before allocating/writing in all GPIO serializers: write_gpio_digital_data, write_gpio_digital_read_config, write_gpio_analog_data, write_gpio_analog_read_config, write_gpio_digital_read_result, and write_gpio_analog_read_result; define a private constant like kMaxChannelIndex = (1U << 6) - 1U and verify channel_index <= kMaxChannelIndex (since GpioHeader::ChannelIndex is BitfieldMember<8,6>) and return SerializeResult::kInvalidArgument when out of range, mirroring the existing period_ms validation style.core/include/librmcs/data/datas.hpp (1)
68-75: 建议给supported()补上noexcept和[[nodiscard]]。该函数的所有子调用(
gpio.supports(...))均为noexcept,且返回值是能力校验结果,丢弃结果基本都属于误用;加上[[nodiscard]]可由编译器阻止忽略返回值的情况,noexcept则便于在noexcept路径(如vendor.hpp的反序列化回调)中无开销调用。♻️ 建议改动
- bool supported(const spec::GpioDescriptor& gpio) const { + [[nodiscard]] bool supported(const spec::GpioDescriptor& gpio) const noexcept { return (!asap || gpio.supports(spec::GpioCapability::kDigitalReadOnce)) && (!period_ms || gpio.supports(spec::GpioCapability::kDigitalReadPeriodic)) && ((!rising_edge && !falling_edge) || gpio.supports(spec::GpioCapability::kDigitalReadInterrupt)) && (pull != GpioPull::kUp || gpio.supports(spec::GpioCapability::kPullUp)) && (pull != GpioPull::kDown || gpio.supports(spec::GpioCapability::kPullDown)); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/include/librmcs/data/datas.hpp` around lines 68 - 75, The supported(const spec::GpioDescriptor& gpio) const method should be marked [[nodiscard]] and noexcept; update the method signature in its declaration/definition (function supported) to read as a [[nodiscard]] bool supported(...) const noexcept so callers are warned if they ignore the result and the function can be used in noexcept contexts (also update any out-of-line definition or forward declaration for supported to match this signature).host/include/librmcs/agent/c_board.hpp (1)
10-12: 项目头文件引用风格不一致同一文件内,第 10 行使用
<librmcs/spec/gpio.hpp>(尖括号),而第 12 行对同样属于本仓库的librmcs/spec/c_board/gpio.hpp使用了"..."(双引号)。本文件中其它项目头文件(librmcs/agent/common.hpp、librmcs/data/datas.hpp、librmcs/protocol/handler.hpp)均使用尖括号,建议统一:🔧 建议修改
`#include` <librmcs/spec/gpio.hpp> - -#include "librmcs/spec/c_board/gpio.hpp" +#include <librmcs/spec/c_board/gpio.hpp>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@host/include/librmcs/agent/c_board.hpp` around lines 10 - 12, The include style is inconsistent: <librmcs/spec/gpio.hpp> uses angle brackets while "librmcs/spec/c_board/gpio.hpp" uses quotes; change the latter to use angle brackets to match the project's other includes (e.g., <librmcs/agent/common.hpp>, <librmcs/data/datas.hpp>, <librmcs/protocol/handler.hpp>), so replace "librmcs/spec/c_board/gpio.hpp" with <librmcs/spec/c_board/gpio.hpp> in the c_board.hpp header to unify include style.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@core/include/librmcs/spec/gpio.hpp`:
- Around line 36-45: Remove the dead constant kNoExtiLine from the
GpioDescriptor struct: locate the struct GpioDescriptor and delete the static
constexpr std::uint8_t kNoExtiLine = 0xFFU declaration, leaving only the used
members (channel_index, capability_mask and supports()); ensure no other code
references GpioDescriptor::kNoExtiLine before deletion.
---
Nitpick comments:
In `@core/include/librmcs/data/datas.hpp`:
- Around line 68-75: The supported(const spec::GpioDescriptor& gpio) const
method should be marked [[nodiscard]] and noexcept; update the method signature
in its declaration/definition (function supported) to read as a [[nodiscard]]
bool supported(...) const noexcept so callers are warned if they ignore the
result and the function can be used in noexcept contexts (also update any
out-of-line definition or forward declaration for supported to match this
signature).
In `@core/include/librmcs/spec/c_board/gpio.hpp`:
- Around line 46-48: The operator[] accessor for GpioDescriptor (static
constexpr const GpioDescriptor& operator[](std::size_t channel_index) noexcept)
lacks a bounds check and can trigger undefined behavior; add a defensive
assert_debug that verifies channel_index < size() (or kArray.size()) at the
start of operator[] before returning kArray[channel_index]; keep the
noexcept/constexpr intent in mind (you can keep the assert_guard disabled in
constexpr evaluation or follow the existing pattern used in
channel_state()/channel_hardware() in firmware/c_board/app/src/gpio/gpio.hpp).
In `@core/src/protocol/serializer.hpp`:
- Around line 123-291: Add a defensive bounds check for channel_index against
the 6-bit limit (max 63) before allocating/writing in all GPIO serializers:
write_gpio_digital_data, write_gpio_digital_read_config, write_gpio_analog_data,
write_gpio_analog_read_config, write_gpio_digital_read_result, and
write_gpio_analog_read_result; define a private constant like kMaxChannelIndex =
(1U << 6) - 1U and verify channel_index <= kMaxChannelIndex (since
GpioHeader::ChannelIndex is BitfieldMember<8,6>) and return
SerializeResult::kInvalidArgument when out of range, mirroring the existing
period_ms validation style.
In `@host/include/librmcs/agent/c_board.hpp`:
- Around line 10-12: The include style is inconsistent: <librmcs/spec/gpio.hpp>
uses angle brackets while "librmcs/spec/c_board/gpio.hpp" uses quotes; change
the latter to use angle brackets to match the project's other includes (e.g.,
<librmcs/agent/common.hpp>, <librmcs/data/datas.hpp>,
<librmcs/protocol/handler.hpp>), so replace "librmcs/spec/c_board/gpio.hpp" with
<librmcs/spec/c_board/gpio.hpp> in the c_board.hpp header to unify include
style.
🪄 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: 7d4d34b8-b70e-491a-a1d3-ee3df3627467
📒 Files selected for processing (18)
core/include/librmcs/data/datas.hppcore/include/librmcs/spec/c_board/gpio.hppcore/include/librmcs/spec/gpio.hppcore/src/protocol/deserializer.cppcore/src/protocol/deserializer.hppcore/src/protocol/protocol.hppcore/src/protocol/serializer.hppfirmware/c_board/CMakeLists.txtfirmware/c_board/app/src/gpio/gpio.hppfirmware/c_board/app/src/usb/vendor.hppfirmware/rmcs_board/app/CMakeLists.txtfirmware/rmcs_board/app/src/usb/vendor.hppfirmware/rmcs_board/bootloader/CMakeLists.txthost/include/librmcs/agent/c_board.hpphost/include/librmcs/agent/rmcs_board_lite.hpphost/include/librmcs/agent/rmcs_board_pro.hpphost/include/librmcs/protocol/handler.hpphost/src/protocol/handler.cpp
c992634 to
3683953
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
firmware/c_board/app/src/timer/timer.hpp (1)
70-75: 建议补充check_reached的半周期前提。这个无状态回绕比较只在
deadline没有被安排到超过半个 32-bit tick 周期的未来时可靠;否则过远的未来时间点会被误判为“已到达”。当前 GPIO 调用路径看起来由to_duration_checked()约束,但这个方法是 public,建议把前提写在接口旁边,避免后续误用。可选注释补充
+ // Precondition: `deadline` must not be scheduled more than `kMaxDurationTicks` + // ticks in the future; farther deadlines are ambiguous under modular time. [[nodiscard]] bool check_reached(TimePoint deadline) const { const uint32_t deadline_ticks = deadline.time_since_epoch().count(); const uint32_t now_ticks = timepoint().time_since_epoch().count(); const uint32_t elapsed_ticks = now_ticks - deadline_ticks; return elapsed_ticks < kMaxDurationTicks; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@firmware/c_board/app/src/timer/timer.hpp` around lines 70 - 75, Add a clear precondition to check_reached: document (and optionally assert) that the supplied deadline must not be scheduled more than half of the 32‑bit tick wrap period into the future because the unsigned wrap-around comparison using kMaxDurationTicks is only reliable within that half‑period; reference the check_reached function name, the kMaxDurationTicks constant, and the public to_duration_checked() method in the comment near the API so callers know the constraint and avoid misusing the public to_duration_checked() path.host/include/librmcs/agent/rmcs_board_lite.hpp (1)
138-151: 建议与rmcs_board_pro.hpp保持一致,使用final替代override。
RmcsBoardPro对应的两个路由入口(rmcs_board_pro.hpp第 140 / 147 行)声明为final,防止用户子类误覆盖 channel_index 版本而旁路描述符分发;lite 这里只用override,功能上可行但与 pro 不对称,且一旦用户意外覆盖该重载会绕过边界检查与描述符路由。♻️ 建议的修改
void gpio_digital_read_result_callback( - uint8_t channel_index, const librmcs::data::GpioDigitalDataView& data) override { + uint8_t channel_index, const librmcs::data::GpioDigitalDataView& data) final { if (channel_index >= spec::rmcs_board_lite::kGpioDescriptors.size()) [[unlikely]] return; gpio_digital_read_result_callback( spec::rmcs_board_lite::kGpioDescriptors[channel_index], data); } void gpio_analog_read_result_callback( - uint8_t channel_index, const librmcs::data::GpioAnalogDataView& data) override { + uint8_t channel_index, const librmcs::data::GpioAnalogDataView& data) final { if (channel_index >= spec::rmcs_board_lite::kGpioDescriptors.size()) [[unlikely]] return; gpio_analog_read_result_callback( spec::rmcs_board_lite::kGpioDescriptors[channel_index], data); }Based on learnings: "avoid using both
finalandoverrideon the same virtual method: if a method is declaredfinal, do not also addoverridesince it is redundant. Usefinalalone forfinaloverrides."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@host/include/librmcs/agent/rmcs_board_lite.hpp` around lines 138 - 151, The two channel-index overloads gpio_digital_read_result_callback and gpio_analog_read_result_callback in class RmcsBoardLite should match RmcsBoardPro by being declared final (not override) to prevent subclasses from bypassing the descriptor-boundary check and dispatch; find the methods in rmcs_board_lite.hpp and replace the trailing override specifier with final for both methods (do not use both override and final together).firmware/rmcs_board/app/src/gpio/gpio.hpp (1)
53-85: LGTM(一个小的可选建议)。
handle_port_interrupt遍历所有通道匹配端口并check_clear_interrupt_flag(),对 N=17 规模没问题;poll_periodic_input_samples的调度器正确地只在DigitalInput且period_ticks != 0时采样。一个可选的稳健性改进:当调度被长时间阻塞时,
state.next_sample_tick = now + state.period_ticks;会丢失相位(相对原计划时刻漂移)。若希望保持周期稳定,可改为state.next_sample_tick += state.period_ticks;并在严重滞后时快速赶齐或丢弃过期点。当前策略对大多数上位机采样场景也够用,属于可选优化。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@firmware/rmcs_board/app/src/gpio/gpio.hpp` around lines 53 - 85, Update poll_periodic_input_samples to advance state.next_sample_tick by adding state.period_ticks instead of resetting to now to preserve phase: in poll_periodic_input_samples, replace the assignment state.next_sample_tick = now + state.period_ticks with state.next_sample_tick += state.period_ticks and add logic to catch up/skew handling (e.g., while now >= state.next_sample_tick { publish_digital_input_sample(...); state.next_sample_tick += state.period_ticks; } or drop excessive missed ticks) so the scheduler in poll_periodic_input_samples keeps a consistent phase; touch the same ChannelState fields and use publish_digital_input_sample/static_cast<uint8_t>(channel_index) and now_ticks() as needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@core/include/librmcs/spec/c_board/gpio.hpp`:
- Line 18: The constructor parameter for the derived GpioDescriptor in c_board
(and likewise in rmcs_board_pro and rmcs_board_lite) uses uint8_t; change its
signature to use std::uint8_t to match the base class spec::GpioDescriptor and
the file's fully-qualified style — update the constructor
declarations/definitions named GpioDescriptor(uint8_t channel_index,
GpioCapability capability_mask) to GpioDescriptor(std::uint8_t channel_index,
GpioCapability capability_mask) in each derived class.
In `@core/include/librmcs/spec/rmcs_board_lite/gpio.hpp`:
- Line 18: Replace the unqualified uint8_t parameter with std::uint8_t in the
GpioDescriptor constructors (e.g., change constexpr GpioDescriptor(uint8_t
channel_index, GpioCapability capability_mask) to use std::uint8_t) in the
GpioDescriptor declarations across the three board header files; also ensure
each header includes <cstdint> (or the common header that provides std::uint8_t)
so the qualified type compiles consistently with the base class.
In `@core/include/librmcs/spec/rmcs_board_pro/gpio.hpp`:
- Line 18: 构造函数参数类型在 rmcs_board_pro 的 GpioDescriptor(spec::GpioDescriptor)中使用了裸的
uint8_t;请将构造函数签名中的参数类型改为 std::uint8_t 以与基类
spec::GpioDescriptor(及其成员)保持一致,并对其它两个类似实现(rmcs_board_lite、c_board 中的
GpioDescriptor)做相同修改;确保包含或保留 <cstdint> 可用以解析 std::uint8_t。
In `@firmware/rmcs_board/app/src/gpio/gpio.hpp`:
- Around line 208-212: The duty16_to_pwm_compare function currently inverts duty
semantics vs c_board (duty=0→full on), causing a contradiction with
GpioAnalogDataView; first confirm whether inverted (active-low) PWM is
intentional for rmcs_board, and if so add a clear comment in
duty16_to_pwm_compare and/or GpioAnalogDataView documenting the active-low
convention and rationale; if not intentional, change duty16_to_pwm_compare to
standard semantics to match c_board by special-casing duty==65535 to return
pwm_reload_+1 and using return (static_cast<uint64_t>(pwm_reload_) * duty) /
65535U for the general case so duty=0→CMP≈0 and duty=65535→CMP≈pwm_reload_.
---
Nitpick comments:
In `@firmware/c_board/app/src/timer/timer.hpp`:
- Around line 70-75: Add a clear precondition to check_reached: document (and
optionally assert) that the supplied deadline must not be scheduled more than
half of the 32‑bit tick wrap period into the future because the unsigned
wrap-around comparison using kMaxDurationTicks is only reliable within that
half‑period; reference the check_reached function name, the kMaxDurationTicks
constant, and the public to_duration_checked() method in the comment near the
API so callers know the constraint and avoid misusing the public
to_duration_checked() path.
In `@firmware/rmcs_board/app/src/gpio/gpio.hpp`:
- Around line 53-85: Update poll_periodic_input_samples to advance
state.next_sample_tick by adding state.period_ticks instead of resetting to now
to preserve phase: in poll_periodic_input_samples, replace the assignment
state.next_sample_tick = now + state.period_ticks with state.next_sample_tick +=
state.period_ticks and add logic to catch up/skew handling (e.g., while now >=
state.next_sample_tick { publish_digital_input_sample(...);
state.next_sample_tick += state.period_ticks; } or drop excessive missed ticks)
so the scheduler in poll_periodic_input_samples keeps a consistent phase; touch
the same ChannelState fields and use
publish_digital_input_sample/static_cast<uint8_t>(channel_index) and now_ticks()
as needed.
In `@host/include/librmcs/agent/rmcs_board_lite.hpp`:
- Around line 138-151: The two channel-index overloads
gpio_digital_read_result_callback and gpio_analog_read_result_callback in class
RmcsBoardLite should match RmcsBoardPro by being declared final (not override)
to prevent subclasses from bypassing the descriptor-boundary check and dispatch;
find the methods in rmcs_board_lite.hpp and replace the trailing override
specifier with final for both methods (do not use both override and final
together).
🪄 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: 38f83cd1-524e-4726-9b47-ce8100d8a04d
📒 Files selected for processing (29)
core/include/librmcs/data/datas.hppcore/include/librmcs/spec/c_board/gpio.hppcore/include/librmcs/spec/gpio.hppcore/include/librmcs/spec/rmcs_board_lite/gpio.hppcore/include/librmcs/spec/rmcs_board_pro/gpio.hppcore/src/protocol/deserializer.cppcore/src/protocol/deserializer.hppcore/src/protocol/protocol.hppcore/src/protocol/serializer.hppfirmware/c_board/CMakeLists.txtfirmware/c_board/app/src/gpio/gpio.hppfirmware/c_board/app/src/timer/timer.hppfirmware/c_board/app/src/usb/vendor.hppfirmware/rmcs_board/app/CMakeLists.txtfirmware/rmcs_board/app/src/app.cppfirmware/rmcs_board/app/src/gpio/analog_gpio_pin.hppfirmware/rmcs_board/app/src/gpio/gpio.cppfirmware/rmcs_board/app/src/gpio/gpio.hppfirmware/rmcs_board/app/src/usb/vendor.hppfirmware/rmcs_board/boards/lite/app/board_app.cppfirmware/rmcs_board/boards/lite/app/board_app.hppfirmware/rmcs_board/boards/pro/app/board_app.cppfirmware/rmcs_board/boards/pro/app/board_app.hppfirmware/rmcs_board/bootloader/CMakeLists.txthost/include/librmcs/agent/c_board.hpphost/include/librmcs/agent/rmcs_board_lite.hpphost/include/librmcs/agent/rmcs_board_pro.hpphost/include/librmcs/protocol/handler.hpphost/src/protocol/handler.cpp
✅ Files skipped from review due to trivial changes (3)
- firmware/rmcs_board/app/CMakeLists.txt
- host/src/protocol/handler.cpp
- core/include/librmcs/spec/gpio.hpp
🚧 Files skipped from review as they are similar to previous changes (9)
- firmware/rmcs_board/bootloader/CMakeLists.txt
- firmware/c_board/CMakeLists.txt
- core/src/protocol/protocol.hpp
- firmware/c_board/app/src/usb/vendor.hpp
- core/include/librmcs/data/datas.hpp
- core/src/protocol/deserializer.hpp
- firmware/c_board/app/src/gpio/gpio.hpp
- core/src/protocol/serializer.hpp
- host/include/librmcs/agent/c_board.hpp
3683953 to
9b23ead
Compare
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
core/src/protocol/serializer.hpp (1)
123-288:⚠️ Potential issue | 🟠 Major把
channel_index校验升级为运行时参数校验。这些方法是公共序列化入口;当前只用
assert_debug校验通道位宽,release 构建里无效通道可能被写入/截断到协议头,而不是返回kInvalidArgument。建议像period_ms一样用LIBRMCS_VERIFY_LIKELY保护所有 GPIO 写入方法。建议修复
SerializeResult write_gpio_digital_data( uint8_t channel_index, const data::GpioDigitalDataView& view) noexcept { - utility::assert_debug(channel_index < (1U << GpioHeader::ChannelIndex::kBitWidth)); + LIBRMCS_VERIFY_LIKELY(valid_gpio_channel_index(channel_index), + SerializeResult::kInvalidArgument); const auto payload_type = view.high ? GpioHeader::PayloadEnum::kDigitalWriteHigh : GpioHeader::PayloadEnum::kDigitalWriteLow; @@ SerializeResult write_gpio_digital_read_config( uint8_t channel_index, const data::GpioReadConfigView& view) noexcept { - utility::assert_debug(channel_index < (1U << GpioHeader::ChannelIndex::kBitWidth)); + LIBRMCS_VERIFY_LIKELY(valid_gpio_channel_index(channel_index), + SerializeResult::kInvalidArgument); @@ SerializeResult write_gpio_analog_data( uint8_t channel_index, const data::GpioAnalogDataView& view) noexcept { - utility::assert_debug(channel_index < (1U << GpioHeader::ChannelIndex::kBitWidth)); + LIBRMCS_VERIFY_LIKELY(valid_gpio_channel_index(channel_index), + SerializeResult::kInvalidArgument); @@ SerializeResult write_gpio_analog_read_config( uint8_t channel_index, const data::GpioReadConfigView& view) noexcept { - utility::assert_debug(channel_index < (1U << GpioHeader::ChannelIndex::kBitWidth)); + LIBRMCS_VERIFY_LIKELY(valid_gpio_channel_index(channel_index), + SerializeResult::kInvalidArgument); @@ SerializeResult write_gpio_digital_read_result( uint8_t channel_index, const data::GpioDigitalDataView& view) noexcept { - utility::assert_debug(channel_index < (1U << GpioHeader::ChannelIndex::kBitWidth)); + LIBRMCS_VERIFY_LIKELY(valid_gpio_channel_index(channel_index), + SerializeResult::kInvalidArgument); @@ SerializeResult write_gpio_analog_read_result( uint8_t channel_index, const data::GpioAnalogDataView& view) noexcept { - utility::assert_debug(channel_index < (1U << GpioHeader::ChannelIndex::kBitWidth)); + LIBRMCS_VERIFY_LIKELY(valid_gpio_channel_index(channel_index), + SerializeResult::kInvalidArgument); @@ private: + static constexpr bool valid_gpio_channel_index(uint8_t channel_index) noexcept { + return channel_index < (1U << GpioHeader::ChannelIndex::kBitWidth); + } + static constexpr bool use_extended_field_header(FieldId field_id) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/src/protocol/serializer.hpp` around lines 123 - 288, The GPIO functions currently use utility::assert_debug to validate channel_index which is stripped in release builds; replace those asserts with a runtime check using LIBRMCS_VERIFY_LIKELY(channel_index < (1U << GpioHeader::ChannelIndex::kBitWidth), SerializeResult::kInvalidArgument) in each public serializer entry (write_gpio_digital_data, write_gpio_digital_read_config, write_gpio_analog_data, write_gpio_analog_read_config, write_gpio_digital_read_result, write_gpio_analog_read_result) — perform this verification before computing required size/allocating buffer so invalid channel indices return SerializeResult::kInvalidArgument instead of being truncated in release.
🧹 Nitpick comments (4)
core/include/librmcs/data/datas.hpp (1)
68-75: 建议给supported()加上[[nodiscard]]与noexcept,与GpioDescriptor::supports()保持一致。同文件里紧邻的
GpioDescriptor::supports()声明为[[nodiscard]] constexpr bool ... const noexcept。此处supported()没有返回值属性也没有noexcept,但实现只做 bool 短路 +supports()的 noexcept 调用,实际是无异常的纯函数,返回值也不应被调用方忽略(丢弃返回值就等于绕过能力校验)。♻️ 建议修改
- bool supported(const spec::GpioDescriptor& gpio) const { + [[nodiscard]] constexpr bool supported(const spec::GpioDescriptor& gpio) const noexcept { return (!asap || gpio.supports(spec::GpioCapability::kDigitalReadOnce)) && (!period_ms || gpio.supports(spec::GpioCapability::kDigitalReadPeriodic)) && ((!rising_edge && !falling_edge) || gpio.supports(spec::GpioCapability::kDigitalReadInterrupt)) && (pull != GpioPull::kUp || gpio.supports(spec::GpioCapability::kPullUp)) && (pull != GpioPull::kDown || gpio.supports(spec::GpioCapability::kPullDown)); }补
constexpr还有额外收益:配合kGpioDescriptors的 constexpr 描述符,future 代码中static_assert(config.supported(...))成为可能。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/include/librmcs/data/datas.hpp` around lines 68 - 75, The supported(...) member should be marked [[nodiscard]] and noexcept (and consider constexpr) to match spec::GpioDescriptor::supports() and reflect that it is a pure, non-throwing boolean check; update the signature of supported(const spec::GpioDescriptor& gpio) to be [[nodiscard]] constexpr bool supported(...) const noexcept and keep the existing implementation unchanged so callers cannot accidentally ignore the result and the function may be used in compile-time contexts.firmware/rmcs_board/boards/pro/app/board_app.hpp (2)
16-16:core/include/...包含路径与 spec 头自身使用的<librmcs/spec/gpio.hpp>形式不一致。本文件用
"core/include/librmcs/spec/rmcs_board_pro/gpio.hpp"(相对仓库根的 quoted 路径),而该头内部却用#include <librmcs/spec/gpio.hpp>(angle-bracket,依赖core/include被加入 include path)。两种风格在同一调用链上混用会让新贡献者困惑:既然 firmware 构建目标已加入core/include(符合 PR 描述),这里也可以直接写#include <librmcs/spec/rmcs_board_pro/gpio.hpp>,与 host/spec 侧保持一致。非阻塞。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@firmware/rmcs_board/boards/pro/app/board_app.hpp` at line 16, 在 board_app.hpp 中的包含使用了相对仓库根的 quoted 路径 ("core/include/librmcs/spec/rmcs_board_pro/gpio.hpp"),与该头内部和其他模块使用的 angle-bracket 风格 (<librmcs/spec/gpio.hpp>) 不一致;请在 board_app.hpp 中将该 include 改为 <librmcs/spec/rmcs_board_pro/gpio.hpp>(保留 IWYU pragma: export),以与其他 spec 头的包含风格一致并利用已加入的 core/include include-path;定位符:board_app.hpp 中的 `#include` 行和目标头 rmcs_board_pro/gpio.hpp。
60-79:kGpioHardwareDescriptors与 spec 侧kGpioDescriptors的映射已核对。按 channel_index:0–3 (PB00–PB03, PWM0_P_0..3) 对应
kPwm1..kPwm4;4–10 (PA9/10/11/12/13/18/19) 对应kSpiI2cSocket1..7;11–12 (PA22/PA23) 对应 UART5 RX/TX (kUart0Rx/Tx);13–14 (PA14/PA15) 对应 UART3 RX/TX (kUart1Rx/Tx);15–16 (PA1/PA0) 对应 UART0 RX/TX (kUart2Rx/Tx)。UART 映射与init_uart()中的 IOC 配置完全吻合;static_assert亦保障尺寸一致。一点建议:该表中 channel 顺序上"跳跃"(Socket 从 A9 连续到 A13 后跳到 A18/A19/A22/A23,随后 UART2 落回 A14/A15,再跳到 A0/A1)不容易一眼看出含义,建议在每个分组上方加一行
// PWM1..4/// SpiI2cSocket1..7/// UART0 RX/TX等简短注释(或按kGpioDescriptors中的kUart0Rx等命名引用同步命名),便于后续维护时和 spec 头对照。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@firmware/rmcs_board/boards/pro/app/board_app.hpp` around lines 60 - 79, The GPIO descriptor array kGpioHardwareDescriptors is correct but its grouping/order is hard to read; add brief inline comments above each logical group (e.g., "// PWM1..4" for entries 0–3, "// SpiI2cSocket1..7" for entries 4–10, "// UART0 RX/TX" etc.) to map channel_index ranges to their spec names (kPwm1..kPwm4, kSpiI2cSocket1..7, kUart0Rx/Tx, kUart1Rx/Tx, kUart2Rx/Tx) so maintainers can quickly cross-reference kGpioDescriptors and init_uart() IOC mappings; keep the existing ordering and static_assert as-is.core/include/librmcs/spec/gpio.hpp (1)
43-45:supports()对组合 mask 的语义建议注释明确。
supports(capability)通过capability_mask & capability != 0判断,在传入单个位常量(如kDigitalWrite)时行为等价于 "contains",这也是目前所有调用点(vendor.hpp、datas.hpp::supported、host agents)使用的方式。但如果将来有人写descriptor.supports(kDigitalReadPeriodic | kDigitalReadInterrupt)期望"两项都支持",当前实现会在仅支持其中一项时错误返回true(any-of 语义)。建议二选一:
- 在注释中明确 "capability 必须是单一位" 的使用契约(若真的只打算支持单 bit)。
- 把实现改为
contains语义:(capability_mask & capability) == capability,这样单 bit / 组合 bit 都语义自然,也为kPwmCapabilities这类聚合位的后续使用留出空间。- [[nodiscard]] constexpr bool supports(GpioCapability capability) const noexcept { - return static_cast<std::uint8_t>(capability_mask & capability) != 0; - } + // Returns true iff `capability_mask` supports all bits set in `capability`. + [[nodiscard]] constexpr bool supports(GpioCapability capability) const noexcept { + return (capability_mask & capability) == capability; + }非阻塞;请按项目意图决定保留 any-of 还是改为 all-of。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/include/librmcs/spec/gpio.hpp` around lines 43 - 45, The supports(GpioCapability) method currently uses (capability_mask & capability) != 0 which implements an "any-of" test; decide whether to require a single-bit input or to accept multi-bit masks and update accordingly: either add a clear comment on supports/GpioCapability stating the contract "capability must be a single-bit flag (e.g. kDigitalWrite)" and keep the current expression, or change the implementation to an all-of/contains test by replacing the expression with (capability_mask & capability) == capability so combinations like kDigitalReadPeriodic | kDigitalReadInterrupt and aggregates like kPwmCapabilities behave naturally; update the comment accordingly and ensure callers (vendor.hpp, datas.hpp::supported, host agents) conform to the chosen 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/app/src/gpio/gpio.hpp`:
- Around line 58-63: When handle_digital_read calls configure_digital_input_mode
and then immediately publishes an ASAP sample via publish_digital_input_sample
(when data.asap is true), postpone the next periodic deadline by one sample
period to avoid an immediate duplicate from poll_periodic_input_samples: after
publishing, advance the configured next-sample timestamp by data.sample_period
(or the internal sample_period used in configure_digital_input_mode) so the
periodic scheduler won't emit another sample immediately; apply the same change
to the analogous handler elsewhere in the diff that also uses config + asap
publishing.
In `@firmware/rmcs_board/app/src/gpio/analog_gpio_pin.hpp`:
- Around line 30-31: 当前代码在 gpio/analog_gpio_pin.hpp 中用 __builtin_trap() 检查
pwm_ioc_function 范围,这违反了“禁用 GNU 扩展”的约定;请用项目的断言工具替换:包含的 assert.hpp 中的
assert_debug 应用于 pwm_ioc_function 检查(例如用 assert_debug(pwm_ioc_function <= 31)
或先判断再 assert_debug(false)),将 __builtin_trap() 替换为
assert_debug(...),保持原有约束并符合项目标准。
In `@firmware/rmcs_board/app/src/gpio/gpio.hpp`:
- Around line 46-51: handle_digital_read currently calls
configure_digital_input_mode (set_digital_input_mode_state) then, if data.asap,
immediately calls publish_digital_input_sample which can be followed immediately
by the periodic sampler because set_digital_input_mode_state initialized
next_sample_tick to now; after publishing when data.asap is true and the
configured period_ms > 0, advance the mode's next_sample_tick by one period
(e.g., next_sample_tick += period_ms or set to current_tick + period_ms) so the
next periodic sample is deferred one cycle; update this logic in
handle_digital_read (or in set_digital_input_mode_state) referencing
next_sample_tick, period_ms, data.asap, and publish_digital_input_sample.
In `@firmware/rmcs_board/app/src/usb/vendor.hpp`:
- Around line 156-160: 函数 gpio_analog_read_config_deserialized_callback 目前无视
channel_index 和 data;请改为按照 gpio_digital_read_config_deserialized_callback
的防御性模式实现:对 channel_index 做边界检查(与其他 GPIO 回调相同的 channel 数量判断),构造或获取对应的 descriptor
并用 data.supported(descriptor) 验证支持位(参照 spec::GpioCapability 的位掩码逻辑),在 validation
通过时再将请求转发到现有处理路径,否则记录或安全忽略;保持行为即便当前无 kAnalogRead* 能力位也遵循同样的验证与转发模式以避免未来静默丢包。
In `@firmware/rmcs_board/boards/pro/app/board_app.cpp`:
- Around line 205-208: The init_gpio_pins() function is intentionally an empty
stub; add a clear comment inside init_gpio_pins() stating that PWM IOC channels
are configured in descriptors, user buttons/switches and interrupt pins are
initialized in their own init routines, and remaining channels are configured
on-demand via
configure_digital_output_mode/configure_pwm_output_mode/configure_digital_input_mode
to avoid future confusion—refer to symbols init_gpio_pins,
SDK_DECLARE_EXT_ISR_M/gpio_a_isr, gpio_irq_handler, and GPIO_DI_GPIOA for
context.
In `@host/include/librmcs/agent/c_board.hpp`:
- Around line 52-74: 在 gpio_digital_write、gpio_digital_read 和 gpio_analog_write
三个方法中除了现有 capability/数据校验外还要验证传入的 GpioDescriptor 的 channel_index 落在 c_board
可接受的范围内:在每个方法开始处加入对 gpio.channel_index 的范围检查(例如与
spec::c_board::kGpioDescriptors.size() 或相应的描述符计数常量比较),如果超出范围则像其他失败情况一样抛出
std::invalid_argument(带上类似 "GPIO channel index out of range for c_board"
的描述性信息);把这个范围校验放在 capability/data 写入之前以避免发送无效通道到主机。
---
Outside diff comments:
In `@core/src/protocol/serializer.hpp`:
- Around line 123-288: The GPIO functions currently use utility::assert_debug to
validate channel_index which is stripped in release builds; replace those
asserts with a runtime check using LIBRMCS_VERIFY_LIKELY(channel_index < (1U <<
GpioHeader::ChannelIndex::kBitWidth), SerializeResult::kInvalidArgument) in each
public serializer entry (write_gpio_digital_data,
write_gpio_digital_read_config, write_gpio_analog_data,
write_gpio_analog_read_config, write_gpio_digital_read_result,
write_gpio_analog_read_result) — perform this verification before computing
required size/allocating buffer so invalid channel indices return
SerializeResult::kInvalidArgument instead of being truncated in release.
---
Nitpick comments:
In `@core/include/librmcs/data/datas.hpp`:
- Around line 68-75: The supported(...) member should be marked [[nodiscard]]
and noexcept (and consider constexpr) to match spec::GpioDescriptor::supports()
and reflect that it is a pure, non-throwing boolean check; update the signature
of supported(const spec::GpioDescriptor& gpio) to be [[nodiscard]] constexpr
bool supported(...) const noexcept and keep the existing implementation
unchanged so callers cannot accidentally ignore the result and the function may
be used in compile-time contexts.
In `@core/include/librmcs/spec/gpio.hpp`:
- Around line 43-45: The supports(GpioCapability) method currently uses
(capability_mask & capability) != 0 which implements an "any-of" test; decide
whether to require a single-bit input or to accept multi-bit masks and update
accordingly: either add a clear comment on supports/GpioCapability stating the
contract "capability must be a single-bit flag (e.g. kDigitalWrite)" and keep
the current expression, or change the implementation to an all-of/contains test
by replacing the expression with (capability_mask & capability) == capability so
combinations like kDigitalReadPeriodic | kDigitalReadInterrupt and aggregates
like kPwmCapabilities behave naturally; update the comment accordingly and
ensure callers (vendor.hpp, datas.hpp::supported, host agents) conform to the
chosen contract.
In `@firmware/rmcs_board/boards/pro/app/board_app.hpp`:
- Line 16: 在 board_app.hpp 中的包含使用了相对仓库根的 quoted 路径
("core/include/librmcs/spec/rmcs_board_pro/gpio.hpp"),与该头内部和其他模块使用的
angle-bracket 风格 (<librmcs/spec/gpio.hpp>) 不一致;请在 board_app.hpp 中将该 include 改为
<librmcs/spec/rmcs_board_pro/gpio.hpp>(保留 IWYU pragma: export),以与其他 spec
头的包含风格一致并利用已加入的 core/include include-path;定位符:board_app.hpp 中的 `#include` 行和目标头
rmcs_board_pro/gpio.hpp。
- Around line 60-79: The GPIO descriptor array kGpioHardwareDescriptors is
correct but its grouping/order is hard to read; add brief inline comments above
each logical group (e.g., "// PWM1..4" for entries 0–3, "// SpiI2cSocket1..7"
for entries 4–10, "// UART0 RX/TX" etc.) to map channel_index ranges to their
spec names (kPwm1..kPwm4, kSpiI2cSocket1..7, kUart0Rx/Tx, kUart1Rx/Tx,
kUart2Rx/Tx) so maintainers can quickly cross-reference kGpioDescriptors and
init_uart() IOC mappings; keep the existing ordering and static_assert as-is.
🪄 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: 1f3ff6b9-fa56-41f8-8129-8a1d6307b557
📒 Files selected for processing (29)
core/include/librmcs/data/datas.hppcore/include/librmcs/spec/c_board/gpio.hppcore/include/librmcs/spec/gpio.hppcore/include/librmcs/spec/rmcs_board_lite/gpio.hppcore/include/librmcs/spec/rmcs_board_pro/gpio.hppcore/src/protocol/deserializer.cppcore/src/protocol/deserializer.hppcore/src/protocol/protocol.hppcore/src/protocol/serializer.hppfirmware/c_board/CMakeLists.txtfirmware/c_board/app/src/gpio/gpio.hppfirmware/c_board/app/src/timer/timer.hppfirmware/c_board/app/src/usb/vendor.hppfirmware/rmcs_board/app/CMakeLists.txtfirmware/rmcs_board/app/src/app.cppfirmware/rmcs_board/app/src/gpio/analog_gpio_pin.hppfirmware/rmcs_board/app/src/gpio/gpio.cppfirmware/rmcs_board/app/src/gpio/gpio.hppfirmware/rmcs_board/app/src/usb/vendor.hppfirmware/rmcs_board/boards/lite/app/board_app.cppfirmware/rmcs_board/boards/lite/app/board_app.hppfirmware/rmcs_board/boards/pro/app/board_app.cppfirmware/rmcs_board/boards/pro/app/board_app.hppfirmware/rmcs_board/bootloader/CMakeLists.txthost/include/librmcs/agent/c_board.hpphost/include/librmcs/agent/rmcs_board_lite.hpphost/include/librmcs/agent/rmcs_board_pro.hpphost/include/librmcs/protocol/handler.hpphost/src/protocol/handler.cpp
✅ Files skipped from review due to trivial changes (4)
- firmware/rmcs_board/bootloader/CMakeLists.txt
- firmware/rmcs_board/app/CMakeLists.txt
- core/src/protocol/protocol.hpp
- firmware/rmcs_board/app/src/gpio/gpio.cpp
🚧 Files skipped from review as they are similar to previous changes (12)
- firmware/c_board/CMakeLists.txt
- firmware/rmcs_board/app/src/app.cpp
- firmware/rmcs_board/boards/lite/app/board_app.cpp
- core/src/protocol/deserializer.cpp
- firmware/rmcs_board/boards/lite/app/board_app.hpp
- firmware/c_board/app/src/timer/timer.hpp
- firmware/c_board/app/src/usb/vendor.hpp
- core/src/protocol/deserializer.hpp
- host/include/librmcs/protocol/handler.hpp
- host/src/protocol/handler.cpp
- host/include/librmcs/agent/rmcs_board_lite.hpp
- host/include/librmcs/agent/rmcs_board_pro.hpp
9b23ead to
be1161e
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai pause |
✅ Actions performedReviews paused. |
- Extract channel field from GPIO data views (GpioDigitalDataView, GpioAnalogDataView, GpioReadConfigView) into an explicit channel_index parameter threaded through serialization, deserialization, and callbacks. - Introduce GpioCapability bitmask enum and GpioDescriptor in core/spec, with c_board-specific descriptors that declare per-channel capabilities (e.g. channel 5 lacks interrupt support). - Add GpioReadConfigView::supported() for unified host/firmware validation of read configurations against descriptor capabilities. - Replace hardcoded channel-6 EXTI exclusion in c_board firmware with declarative capability mask in the shared descriptor table. - Rename wire-protocol field GpioHeader::Channel to ChannelIndex and switch c_board channel numbering from 1-based to 0-based. - Update c_board host agent API to accept GpioDescriptor references instead of raw channel numbers. - Add core/include to firmware build targets that consume shared specs. BREAKING CHANGE: GPIO data views no longer carry a channel field; all GPIO protocol and callback APIs now take a zero-based channel_index. c_board host agent methods require a GpioDescriptor reference instead of a raw channel number.
… semantics (#48) - Add board-specific GPIO descriptors and host-side typed GPIO APIs for rmcs_board lite and pro. - Implement rmcs_board firmware GPIO handling for digital input, digital output, interrupt-driven input reporting, and PWM output on supported pro channels. - Refactor c_board digital input scheduling to use deadline-based periodic sampling. This changes the sampling behavior for read configurations with period_ms > 0. BREAKING CHANGE: Digital GPIO read configurations with period_ms > 0 now emit the first periodic sample on the next polling cycle instead of waiting for a full period to elapse.
be1161e to
e77f3d3
Compare
feat(gpio)!: Add descriptor-based GPIO capability model
BREAKING CHANGE: GPIO data views no longer carry a channel field; all GPIO protocol and callback APIs now take a zero-based channel_index. c_board host agent methods require a GpioDescriptor reference instead of a raw channel number.
feat(gpio)!: Add GPIO support for rmcs_board and align input sampling semantics
BREAKING CHANGE: Digital GPIO read configurations with period_ms > 0 now emit the first periodic sample on the next polling cycle instead of waiting for a full period to elapse.
GPIO 描述符模型重构(更新)
概览
本 PR 将 GPIO 支持从多处硬编码与 1-based 通道编号迁移为声明式的描述符驱动模型,统一使用零基 channel_index。改动横跨 core 协议、共享 spec、主机代理与固件(c_board、rmcs_board 及板级变体),引入能力位掩码、板级描述符表、协议字段/签名变更,并重构固件 GPIO 子系统以按描述符驱动行为与能力检查。
主要变更点
破坏性变更(需注意)
迁移建议
审查重点
总体而言,本 PR 用描述符与能力模型替换了多处板级特例并统一了通道索引语义,提升可扩展性与表达能力,但伴随较多接口变更与迁移工作。