# can_protocol **Repository Path**: userlinkz/can_protocol ## Basic Information - **Project Name**: can_protocol - **Description**: No description available - **Primary Language**: C - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-20 - **Last Updated**: 2026-07-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # can_protocol — CAN 通信协议库(纯协议层) 一个**与硬件解耦**的 CAN 应用层协议库,采用 **UAVCAN v0 / DroneCAN 兼容**的 29-bit 扩展帧 ID 格式,支持: - ✅ 广播消息(Broadcast)与服务消息(Service:请求 / 响应) - ✅ 单帧传输(≤7 字节负载) - ✅ 多帧自动分段 / 重组(>7 字节),含 CRC-16-CCITT 校验 - ✅ 服务请求的**异步回调 + 超时**模型 - ✅ 收到完整 transfer 后按订阅触发回调 - ✅ 完全硬件解耦:所有 CAN 收发 / 时钟 / 临界区通过回调注入 - ✅ 与标准 DroneCAN 设备互通 本库**只做协议,不做驱动**。移植时只需实现 4 个硬件回调(见 [PORTING.md](PORTING.md))。 > 📖 **新手必读**:[移植与使用手册.md](移植与使用手册.md) —— 从零基础到跑通的完整教程,含数据结构详解、API 清单、5 步移植、4 个使用例子、常见错误排查。 --- ## 目录结构 ``` can_protocol/ ├── README.md 本文档 ├── PORTING.md 移植指南(硬件回调实现清单) ├── can_protocol.h 公共 API(类型、句柄、收发、订阅) ├── can_protocol.c 协议核心(CRC + ID 编解码 + tail byte + 多帧 + 服务匹配) ├── example/ │ ├── example_port.c 平台回调实现示例 │ └── example_app.c 广播收发 + 服务请求/响应示例 └── tests/ └── test_protocol.c 主机端自测(gcc 编译运行) ``` --- ## 协议规范 ### 1. 29-bit CAN ID 编码 #### 广播消息(Service 标志位 = 0) | 字段 | 比特位 | 宽度 | 说明 | | ------------- | ----------- | ---- | ---------------------------- | | Priority | [28:24] | 5 | 优先级,0 最高 | | MessageTypeID | [23:8] | 16 | 消息数据类型 ID | | Service Flag | [7] | 1 | **始终为 0**(广播) | | SrcNodeID | [6:0] | 7 | 源节点 ID,1…127(0=匿名) | #### 服务消息(Service 标志位 = 1) | 字段 | 比特位 | 宽度 | 说明 | | ----------------- | ----------- | ---- | ----------------------------- | | Priority | [28:24] | 5 | 优先级 | | ServiceTypeID | [23:16] | 8 | 服务数据类型 ID | | RequestNotResp | [15] | 1 | **1=请求,0=响应** | | DstNodeID | [14:8] | 7 | 目标节点 ID,1…127 | | Service Flag | [7] | 1 | **始终为 1**(服务) | | SrcNodeID | [6:0] | 7 | 源节点 ID,1…127 | ### 2. Tail Byte(每帧最后一字节) 经典 CAN 单帧最多 8 字节,**最后一字节固定为 Tail Byte**,因此负载最多 7 字节: | 字段 | 比特位 | 说明 | | ----------- | ------ | ------------------------------------- | | SOT | [7] | Transfer Start(首帧 = 1) | | EOT | [6] | Transfer End(末帧 = 1) | | Toggle | [5] | 切换位,多帧中间逐帧翻转 | | TransferID | [4:0] | 5 位递增计数器(0…31),同 transfer 内不变 | ### 3. 单帧 / 多帧规则(DLC ≤ 8) - **`len ≤ 7`(含 0)**:单帧。格式 `payload[0..len-1] + tail`,其中 `SOT=1, EOT=1, Toggle=0`。 - **`len > 7`**:多帧。先对**整个原始 payload** 计算 CRC-16-CCITT。 - **首帧**:`CRC_lo, CRC_hi` + 5B 数据 + tail(`SOT=1, EOT=0, Toggle=0`),DLC = 8。 - **中间帧**:7B 数据 + tail(`SOT=0, EOT=0, Toggle 逐帧翻转`),DLC = 8。 - **末帧**:剩余 `<7B` 数据 + tail(`SOT=0, EOT=1`)。 - 同一 transfer 的所有帧 **TransferID 相同**;下一个 transfer(同端点)TransferID + 1 (mod 32)。 - CRC 算法:**CRC-16-CCITT**(poly `0x1021`,初值 `0xFFFF`,无最终异或,MSB first),与 DroneCAN 完全一致。 > 多帧首帧额外占 2 字节 CRC,故首帧仅携带 5 字节数据——这是与标准互通的关键。 --- ## 快速上手 ### 1. 实现硬件回调(移植层) 详见 [PORTING.md](PORTING.md)。最小实现只需 4 个回调:`hw_send`、`hw_now_ms`、`hw_lock`、`hw_unlock`。 ### 2. 初始化并订阅 ```c #include "can_protocol.h" static canp_t g_can; static int my_send(uint32_t id, uint8_t ide, const uint8_t *d, uint8_t n, void *u) { /* 调底层 CAN 驱动 */ return 0; } static uint32_t my_now(void *u) { /* 返回 SysTick ms */ return 0; } static void my_lock(void *u) { /* __disable_irq() */ } static void my_unlock(void *u) { /* __enable_irq() */ } void can_setup(void) { canp_config_t cfg = { .hw_send = my_send, .hw_now_ms = my_now, .hw_lock = my_lock, .hw_unlock = my_unlock, .node_id = 10, }; canp_init(&g_can, &cfg); canp_subscribe_broadcast(&g_can, 0x0100, my_bc_cb, NULL); canp_subscribe_service(&g_can, 0x40, my_svc_cb, NULL); } ``` ### 3. 接收(在 CAN RX ISR 中) ```c void CAN_RX0_IRQHandler(void) { /* 读出 CAN 帧得到 id/data/dlc */ canp_rx_feed(&g_can, id, data, dlc); } ``` ### 4. 周期任务(主循环,1…10ms) ```c while (1) { canp_poll(&g_can); /* 处理服务超时 + 多帧会话老化 */ /* ...其他工作 */ } ``` ### 5. 发送 ```c /* 广播(自动单/多帧) */ canp_send_broadcast(&g_can, CANP_PRIORITY_DEFAULT, 0x0100, data, 4); /* 服务请求 + 异步等待响应 */ canp_send_service_request(&g_can, CANP_PRIORITY_DEFAULT, 0x40, 20, req, 2, on_response, NULL, 500 /*ms*/); /* 服务响应 */ canp_send_service_response(&g_can, CANP_PRIORITY_DEFAULT, 0x40, dst, resp, n); ``` --- ## API 速览 | 函数 | 作用 | | ---------------------------- | ------------------------------------------ | | `canp_init` | 初始化句柄 | | `canp_set_node_id` | 设置/修改本机节点 ID | | `canp_rx_feed` | 注入一帧收到的 CAN 数据(ISR/轮询调用) | | `canp_poll` | 周期任务(超时检测、会话老化) | | `canp_send_broadcast` | 发送广播消息 | | `canp_send_service_request` | 发送服务请求并登记等待响应 | | `canp_send_service_response` | 发送服务响应 | | `canp_cancel_request` | 取消未完成的服务请求 | | `canp_subscribe_broadcast` | 订阅广播消息类型 | | `canp_subscribe_service` | 订阅服务(收到发往本节点的请求时触发) | 完整签名见 `can_protocol.h`。 --- ## 运行自测 ### 完整测试套件(推荐) ```bash gcc -Wall -Wextra -std=c99 -O2 \ tests/test_internals.c tests/test_harness.c tests/test_fixture.c \ tests/test_crc.c tests/test_id_codec.c tests/test_tail.c \ tests/test_init.c tests/test_tx_single.c tests/test_tx_multi.c \ tests/test_tx_tid.c tests/test_rx_single.c tests/test_rx_multi.c \ tests/test_rx_service.c tests/test_service_match.c \ tests/test_subscribe.c tests/test_session_table.c \ tests/test_poll_timeout.c tests/test_concurrency.c \ tests/test_interop.c tests/test_edge_input.c \ tests/main.c -o tests/test && ./tests/test ``` 覆盖 17 个测试组、115 个用例:ID 编解码、单/多帧分段重组、CRC 篡改/丢帧/失序检测、服务请求/响应配对、超时、会话表、tick 回绕、临界区、互通字节布局、异常输入鲁棒性。 测试结果与发现的缺陷清单见 [tests/TEST_REPORT.md](tests/TEST_REPORT.md)。 ### 早期单文件自测(deprecated) ```bash gcc can_protocol.c tests/test_protocol.c -o tests/test_old && ./tests/test_old ``` --- ## 可调参数(编译宏覆盖) | 宏 | 默认 | 说明 | | ----------------------------- | ---- | -------------------------- | | `CANP_MAX_PAYLOAD` | 256 | 单次 transfer 最大负载 | | `CANP_RX_SESSION_SLOTS` | 8 | 接收重组会话槽数 | | `CANP_TX_ENDPOINT_SLOTS` | 8 | 发送端点(TID 计数器)槽数 | | `CANP_SVC_PENDING_SLOTS` | 4 | 未完成服务请求槽数 | | `CANP_SUB_BC_SLOTS` | 8 | 广播订阅槽数 | | `CANP_SUB_SVC_SLOTS` | 8 | 服务订阅槽数 | | `CANP_RX_SESSION_TIMEOUT_MS` | 100 | 多帧重组间隙超时 | --- ## 设计要点 - **零硬依赖**:仅 ``,不含任何 MCU 头文件。 - **静态分配**:所有表项在句柄内静态数组,无 `malloc`(内存回调为可选)。 - **线程安全**:会话表读写用 `hw_lock/hw_unlock` 包裹,`canp_rx_feed` 可在 ISR 中调用。 - **Transfer ID 管理**:每个发送端点 `(kind, type_id, peer)` 自动维护 5 位计数器。 - **兼容性**:ID 编码、tail byte、CRC-16-CCITT、多帧分段规则均严格遵循 UAVCAN v0。