From 227158870af7f8162ac83dc4e481e77fdb1ce57e Mon Sep 17 00:00:00 2001 From: skiinder Date: Sat, 11 Apr 2026 18:21:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0CDC=E5=8D=8F=E8=AE=AE?= =?UTF-8?q?=E9=80=9A=E4=BF=A1=E6=A8=A1=E5=9D=97=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 集成nanopb库用于protobuf序列化 - 创建cdc_wrapper_module.c实现帧解析功能 - 实现protocol_module.c处理协议编解码 - 定义device_comm.proto通信协议 - 修改CMakeLists.txt添加protobuf源文件 - 更新配置启用NANOPB支持 - 移除usb_cdc_module中基于行的处理逻辑 --- CMakeLists.txt | 10 +- inc/cdc_wrapper_module.h | 12 ++ inc/protocol_module.h | 26 ++++ prj.conf | 1 + proto/device_comm.proto | 21 +++ src/cdc_wrapper_module.c | 275 +++++++++++++++++++++++++++++++++++++++ src/protocol_module.c | 133 +++++++++++++++++++ src/usb_cdc_module.c | 40 +----- 8 files changed, 478 insertions(+), 40 deletions(-) create mode 100644 inc/cdc_wrapper_module.h create mode 100644 inc/protocol_module.h create mode 100644 proto/device_comm.proto create mode 100644 src/cdc_wrapper_module.c create mode 100644 src/protocol_module.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 73112b0..3e9f266 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,12 +4,19 @@ cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(blinky) +list(APPEND CMAKE_MODULE_PATH ${ZEPHYR_BASE}/modules/nanopb) +include(nanopb) + zephyr_include_directories( inc inc/events ) add_subdirectory(drivers) +zephyr_nanopb_sources(app + proto/device_comm.proto +) + target_sources(app PRIVATE src/main.c src/battery_module.c @@ -22,8 +29,9 @@ target_sources(app PRIVATE src/hid_flowctrl_module.c src/keyboard_core_module.c src/ui/ui_main.c + src/cdc_wrapper_module.c + src/protocol_module.c src/usb_cdc_module.c - src/usb_cdc_test_module.c src/usb_device_module.c src/usb_hid_module.c src/events/bat_state_event.c diff --git a/inc/cdc_wrapper_module.h b/inc/cdc_wrapper_module.h new file mode 100644 index 0000000..f024ebe --- /dev/null +++ b/inc/cdc_wrapper_module.h @@ -0,0 +1,12 @@ +#ifndef BLINKY_CDC_WRAPPER_MODULE_H_ +#define BLINKY_CDC_WRAPPER_MODULE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* BLINKY_CDC_WRAPPER_MODULE_H_ */ diff --git a/inc/protocol_module.h b/inc/protocol_module.h new file mode 100644 index 0000000..e04ab93 --- /dev/null +++ b/inc/protocol_module.h @@ -0,0 +1,26 @@ +#ifndef BLINKY_PROTOCOL_MODULE_H_ +#define BLINKY_PROTOCOL_MODULE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CDC_PROTO_TYPE_HELLO_REQ 0x01U +#define CDC_PROTO_TYPE_HELLO_RSP 0x02U + +int protocol_module_process_cdc_packet(uint8_t req_type, + const uint8_t *req_payload, + size_t req_payload_len, + uint8_t *rsp_type, + uint8_t *rsp_payload, + size_t rsp_payload_buf_size, + size_t *rsp_payload_len); + +#ifdef __cplusplus +} +#endif + +#endif /* BLINKY_PROTOCOL_MODULE_H_ */ diff --git a/prj.conf b/prj.conf index 41ea7a1..dd8eb0f 100644 --- a/prj.conf +++ b/prj.conf @@ -34,6 +34,7 @@ CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_UART_LINE_CTRL=y CONFIG_UART_USE_RUNTIME_CONFIGURE=y +CONFIG_NANOPB=y CONFIG_USBD_HID_SUPPORT=y CONFIG_USBD_CDC_ACM_CLASS=y CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=n diff --git a/proto/device_comm.proto b/proto/device_comm.proto new file mode 100644 index 0000000..63ad07d --- /dev/null +++ b/proto/device_comm.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +message HelloReq { + uint32 protocol_version = 1; +} + +message HelloRsp { + uint32 protocol_version = 1; + uint32 vendor_id = 2; + uint32 product_id = 3; + uint32 firmware_major = 4; + uint32 firmware_minor = 5; + uint32 capability_flags = 6; +} + +message CdcPacketBody { + oneof body { + HelloReq hello_req = 1; + HelloRsp hello_rsp = 2; + } +} diff --git a/src/cdc_wrapper_module.c b/src/cdc_wrapper_module.c new file mode 100644 index 0000000..af110c1 --- /dev/null +++ b/src/cdc_wrapper_module.c @@ -0,0 +1,275 @@ +#include +#include +#include +#include +#include + +#include + +#define MODULE cdc_wrapper_module +#include +#include + +#include + +#include "protocol_module.h" +#include "usb_cdc_rx_event.h" +#include "usb_cdc_tx_event.h" + +LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); + +#define CDC_WRAPPER_HEAD1 0xAAU +#define CDC_WRAPPER_HEAD2 0x55U +#define CDC_WRAPPER_MAX_PAYLOAD_LEN 64U +#define CDC_WRAPPER_MAX_FRAME_LEN (2U + 1U + 1U + CDC_WRAPPER_MAX_PAYLOAD_LEN + 1U) + +enum frame_parse_state { + FRAME_PARSE_HEAD1, + FRAME_PARSE_HEAD2, + FRAME_PARSE_LEN, + FRAME_PARSE_TYPE, + FRAME_PARSE_PAYLOAD, + FRAME_PARSE_CHECKSUM, +}; + +struct cdc_frame_parser { + enum frame_parse_state state; + uint8_t len; + uint8_t type; + uint8_t checksum; + uint8_t payload[CDC_WRAPPER_MAX_PAYLOAD_LEN]; + size_t payload_pos; +}; + +static bool initialized; +static bool running; +static struct cdc_frame_parser parser; + +static void parser_reset(void) +{ + parser.state = FRAME_PARSE_HEAD1; + parser.len = 0U; + parser.type = 0U; + parser.checksum = 0U; + parser.payload_pos = 0U; +} + +static uint8_t frame_checksum(uint8_t len, uint8_t type, + const uint8_t *payload, size_t payload_len) +{ + uint8_t checksum = CDC_WRAPPER_HEAD1 ^ CDC_WRAPPER_HEAD2 ^ len ^ type; + + for (size_t i = 0; i < payload_len; i++) { + checksum ^= payload[i]; + } + + return checksum; +} + +static void submit_tx_frame(uint8_t type, const uint8_t *payload, size_t payload_len) +{ + struct usb_cdc_tx_event *event; + size_t frame_len = 2U + 1U + 1U + payload_len + 1U; + + event = new_usb_cdc_tx_event(frame_len); + event->dyndata.data[0] = CDC_WRAPPER_HEAD1; + event->dyndata.data[1] = CDC_WRAPPER_HEAD2; + event->dyndata.data[2] = (uint8_t)payload_len; + event->dyndata.data[3] = type; + memcpy(&event->dyndata.data[4], payload, payload_len); + event->dyndata.data[4U + payload_len] = + frame_checksum((uint8_t)payload_len, type, payload, payload_len); + APP_EVENT_SUBMIT(event); +} + +static void process_complete_frame(void) +{ + uint8_t rsp_type; + uint8_t rsp_payload[CDC_WRAPPER_MAX_PAYLOAD_LEN]; + size_t rsp_payload_len = 0U; + int err; + + err = protocol_module_process_cdc_packet(parser.type, + parser.payload, + parser.payload_pos, + &rsp_type, + rsp_payload, + sizeof(rsp_payload), + &rsp_payload_len); + if (err == -ENOTSUP) { + LOG_WRN("Ignore unsupported CDC packet type:0x%02x", parser.type); + return; + } + + if (err) { + LOG_WRN("Protocol processing failed (%d)", err); + return; + } + + LOG_INF("CDC HelloRsp encoded len:%u", (uint32_t)rsp_payload_len); + submit_tx_frame(rsp_type, rsp_payload, rsp_payload_len); +} + +static void consume_byte(uint8_t byte) +{ + switch (parser.state) { + case FRAME_PARSE_HEAD1: + if (byte == CDC_WRAPPER_HEAD1) { + parser.state = FRAME_PARSE_HEAD2; + } + break; + + case FRAME_PARSE_HEAD2: + if (byte == CDC_WRAPPER_HEAD2) { + parser.state = FRAME_PARSE_LEN; + } else if (byte != CDC_WRAPPER_HEAD1) { + parser.state = FRAME_PARSE_HEAD1; + } + break; + + case FRAME_PARSE_LEN: + if (byte > CDC_WRAPPER_MAX_PAYLOAD_LEN) { + LOG_WRN("Drop CDC frame with invalid len:%u", byte); + parser_reset(); + break; + } + + parser.len = byte; + parser.payload_pos = 0U; + parser.state = FRAME_PARSE_TYPE; + break; + + case FRAME_PARSE_TYPE: + parser.type = byte; + parser.state = (parser.len == 0U) ? FRAME_PARSE_CHECKSUM : + FRAME_PARSE_PAYLOAD; + break; + + case FRAME_PARSE_PAYLOAD: + parser.payload[parser.payload_pos++] = byte; + if (parser.payload_pos >= parser.len) { + parser.state = FRAME_PARSE_CHECKSUM; + } + break; + + case FRAME_PARSE_CHECKSUM: + if (byte != frame_checksum(parser.len, parser.type, + parser.payload, parser.payload_pos)) { + LOG_WRN("Drop CDC frame with invalid checksum"); + parser_reset(); + break; + } + + process_complete_frame(); + parser_reset(); + break; + + default: + parser_reset(); + break; + } +} + +static bool handle_usb_cdc_rx_event(const struct usb_cdc_rx_event *event) +{ + if (!running) { + return false; + } + + for (size_t i = 0; i < event->dyndata.size; i++) { + consume_byte(event->dyndata.data[i]); + } + + return false; +} + +static int module_init(void) +{ + parser_reset(); + return 0; +} + +static int module_start(void) +{ + if (running) { + return 0; + } + + running = true; + return 0; +} + +static void module_pause(void) +{ + if (!running) { + return; + } + + running = false; + parser_reset(); +} + +static bool app_event_handler(const struct app_event_header *aeh) +{ + if (is_usb_cdc_rx_event(aeh)) { + return handle_usb_cdc_rx_event(cast_usb_cdc_rx_event(aeh)); + } + + if (is_module_state_event(aeh)) { + const struct module_state_event *event = cast_module_state_event(aeh); + + if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { + int err; + + if (!initialized) { + err = module_init(); + if (err) { + module_set_state(MODULE_STATE_ERROR); + return false; + } + + initialized = true; + } + + err = module_start(); + if (err) { + module_set_state(MODULE_STATE_ERROR); + } else { + module_set_state(MODULE_STATE_READY); + } + } + + return false; + } + + if (is_power_down_event(aeh)) { + if (initialized) { + module_pause(); + module_set_state(MODULE_STATE_STANDBY); + } + + return false; + } + + if (is_wake_up_event(aeh)) { + if (initialized) { + int err = module_start(); + + if (err) { + module_set_state(MODULE_STATE_ERROR); + } else { + module_set_state(MODULE_STATE_READY); + } + } + + return false; + } + + return false; +} + +APP_EVENT_LISTENER(MODULE, app_event_handler); +APP_EVENT_SUBSCRIBE(MODULE, module_state_event); +APP_EVENT_SUBSCRIBE(MODULE, usb_cdc_rx_event); +APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); +APP_EVENT_SUBSCRIBE(MODULE, wake_up_event); diff --git a/src/protocol_module.c b/src/protocol_module.c new file mode 100644 index 0000000..6cd1d2c --- /dev/null +++ b/src/protocol_module.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "protocol_module.h" + +LOG_MODULE_REGISTER(protocol_module, LOG_LEVEL_INF); + +#define PROTOCOL_VERSION 1U +#define PROTOCOL_VENDOR_ID 0x1915U +#define PROTOCOL_PRODUCT_ID 0x52F0U +#define PROTOCOL_FIRMWARE_MAJOR 0U +#define PROTOCOL_FIRMWARE_MINOR 0U +#define PROTOCOL_CAPABILITY_FLAGS 0U + +static bool type_matches_body(uint8_t type, const CdcPacketBody *body) +{ + switch (type) { + case CDC_PROTO_TYPE_HELLO_REQ: + return body->which_body == CdcPacketBody_hello_req_tag; + case CDC_PROTO_TYPE_HELLO_RSP: + return body->which_body == CdcPacketBody_hello_rsp_tag; + default: + return false; + } +} + +static int decode_body(const uint8_t *payload, size_t payload_len, + CdcPacketBody *body) +{ + pb_istream_t stream; + + if ((payload == NULL) || (body == NULL)) { + return -EINVAL; + } + + *body = (CdcPacketBody)CdcPacketBody_init_zero; + stream = pb_istream_from_buffer(payload, payload_len); + + if (!pb_decode(&stream, CdcPacketBody_fields, body)) { + LOG_WRN("pb_decode failed: %s", PB_GET_ERROR(&stream)); + return -EBADMSG; + } + + return 0; +} + +static int encode_hello_rsp(uint8_t *rsp_payload, size_t rsp_payload_buf_size, + size_t *rsp_payload_len) +{ + CdcPacketBody body = CdcPacketBody_init_zero; + pb_ostream_t stream; + + if ((rsp_payload == NULL) || (rsp_payload_len == NULL)) { + return -EINVAL; + } + + body.which_body = CdcPacketBody_hello_rsp_tag; + body.body.hello_rsp.protocol_version = PROTOCOL_VERSION; + body.body.hello_rsp.vendor_id = PROTOCOL_VENDOR_ID; + body.body.hello_rsp.product_id = PROTOCOL_PRODUCT_ID; + body.body.hello_rsp.firmware_major = PROTOCOL_FIRMWARE_MAJOR; + body.body.hello_rsp.firmware_minor = PROTOCOL_FIRMWARE_MINOR; + body.body.hello_rsp.capability_flags = PROTOCOL_CAPABILITY_FLAGS; + + stream = pb_ostream_from_buffer(rsp_payload, rsp_payload_buf_size); + + if (!pb_encode(&stream, CdcPacketBody_fields, &body)) { + LOG_WRN("pb_encode failed: %s", PB_GET_ERROR(&stream)); + return -EIO; + } + + *rsp_payload_len = stream.bytes_written; + return 0; +} + +int protocol_module_process_cdc_packet(uint8_t req_type, + const uint8_t *req_payload, + size_t req_payload_len, + uint8_t *rsp_type, + uint8_t *rsp_payload, + size_t rsp_payload_buf_size, + size_t *rsp_payload_len) +{ + CdcPacketBody body; + int err; + + if ((rsp_type == NULL) || (rsp_payload == NULL) || (rsp_payload_len == NULL)) { + return -EINVAL; + } + + err = decode_body(req_payload, req_payload_len, &body); + if (err) { + return err; + } + + if (!type_matches_body(req_type, &body)) { + LOG_WRN("CDC type/body mismatch type:0x%02x body_case:%d", + req_type, body.which_body); + return -EBADMSG; + } + + switch (req_type) { + case CDC_PROTO_TYPE_HELLO_REQ: + LOG_INF("HelloReq protocol_version:%u", + body.body.hello_req.protocol_version); + + if (body.body.hello_req.protocol_version != PROTOCOL_VERSION) { + LOG_WRN("Unexpected protocol version:%u", + body.body.hello_req.protocol_version); + } + + err = encode_hello_rsp(rsp_payload, rsp_payload_buf_size, rsp_payload_len); + if (err) { + return err; + } + + *rsp_type = CDC_PROTO_TYPE_HELLO_RSP; + return 0; + + default: + LOG_WRN("Unsupported CDC protocol type:0x%02x", req_type); + return -ENOTSUP; + } +} diff --git a/src/usb_cdc_module.c b/src/usb_cdc_module.c index 0bbc2e8..bd0d59d 100644 --- a/src/usb_cdc_module.c +++ b/src/usb_cdc_module.c @@ -28,7 +28,6 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); #define USB_CDC_RX_RING_BUF_SIZE 256 #define USB_CDC_TX_RING_BUF_SIZE 256 #define USB_CDC_RX_CHUNK_SIZE 32 -#define USB_CDC_MAX_LINE_SIZE 96 #define USB_CDC_CONTROL_POLL_INTERVAL K_MSEC(100) #define USB_CDC_EXPECTED_BAUDRATE 115200U @@ -46,9 +45,6 @@ static bool usb_active; static bool usb_function_prepared; static bool dtr_ready; static bool rx_enabled; -static bool rx_line_overflow; -static uint8_t rx_line_buf[USB_CDC_MAX_LINE_SIZE]; -static size_t rx_line_len; static void submit_usb_cdc_rx_event(const uint8_t *data, size_t len) { @@ -72,8 +68,6 @@ static void reset_ring_buffers(void) ring_buf_init(&rx_ringbuf, sizeof(rx_ring_buffer), rx_ring_buffer); ring_buf_init(&tx_ringbuf, sizeof(tx_ring_buffer), tx_ring_buffer); - rx_line_len = 0U; - rx_line_overflow = false; irq_unlock(key); } @@ -134,36 +128,6 @@ static void validate_line_coding(void) #endif } -static void process_rx_byte(uint8_t byte) -{ - if ((byte == '\r') || (byte == '\n')) { - if (rx_line_overflow) { - LOG_WRN("Drop oversized CDC line"); - rx_line_overflow = false; - rx_line_len = 0U; - return; - } - - if (rx_line_len > 0U) { - submit_usb_cdc_rx_event(rx_line_buf, rx_line_len); - rx_line_len = 0U; - } - - return; - } - - if (rx_line_overflow) { - return; - } - - if (rx_line_len >= sizeof(rx_line_buf)) { - rx_line_overflow = true; - return; - } - - rx_line_buf[rx_line_len++] = byte; -} - static void rx_work_handler(struct k_work *work) { uint8_t buffer[USB_CDC_RX_CHUNK_SIZE]; @@ -181,9 +145,7 @@ static void rx_work_handler(struct k_work *work) return; } - for (uint32_t i = 0; i < len; i++) { - process_rx_byte(buffer[i]); - } + submit_usb_cdc_rx_event(buffer, len); } }