模块 1 实践项目
目录
练习 1:虚拟 CAN 环境搭建
目标:独立创建和验证 vcan 设备。
难度:入门
步骤:
- 加载 vcan 内核模块
- 创建 vcan0 和 vcan1 两个虚拟总线
- 验证两个总线互相独立(向 vcan1 发送,vcan0 不应收到)
- 编写清理脚本
vcan-cleanup.sh,一键删除所有 vcan 设备
验证标准:
# 应该能看到两个独立的 vcan 设备
ip link show | grep vcan
# vcan0 和 vcan1 的帧互不干扰
candump vcan0 & # 后台运行,只监听 vcan0
cansend vcan1 123#FF # 发往 vcan1
# candump vcan0 不应收到此帧提示:
- 使用
ip link del <name>删除 vcan 设备 - 使用
ls /sys/class/net/ | grep vcan列出所有 vcan 设备
练习 2:CAN 帧收发工具
目标:编写一个交互式的 CAN 收发工具。
难度:进阶
要求:
- 命令行参数指定 CAN 设备(如
./can_tool vcan0) - 提供交互模式:输入
ID:data1 data2 ...即可发送 - 同时接收并打印所有帧
- 支持按 CAN ID 过滤(命令行参数
--filter 0x123)
示例用法:
./can_tool vcan0
> 0x123:11 22 33 44 # 发送帧
> 0x000:01 05 # 发送 NMT 启动命令
> quit # 退出提示:
- 使用
select()或poll()同时监听 stdin 和 CAN socket - 或使用两个线程:一个发送,一个接收
- 解析用户输入时注意十六进制转换
验证标准:
# 终端 1
./can_tool vcan0
# 终端 2
cansend vcan0 123#112233
cansend vcan0 456#AABBCC
# 终端 1 应显示接收到的两帧练习 3:NMT 命令工具
目标:编写一个完整的 NMT 命令行工具,支持所有 NMT 命令。
难度:进阶
要求:
- 支持子命令:
start/stop/preop/reset-node/reset-comm - 支持单播(指定节点 ID)和广播(节点 ID = 0)
- 打印友好的提示信息
示例用法:
./nmt_tool vcan0 start 1 # 启动节点 1
./nmt_tool vcan0 stop 0 # 广播停止所有节点
./nmt_tool vcan0 preop 5 # 节点 5 进入 Pre-Operational
./nmt_tool vcan0 reset-comm 1 # 重置节点 1 的通信NMT 命令码参考:
| 命令 | 命令码 | 说明 |
|---|---|---|
start | 0x01 | 启动节点(进入 Operational) |
stop | 0x02 | 停止节点(进入 Stopped) |
preop | 0x80 | 进入 Pre-Operational |
reset-node | 0x81 | 重置节点 |
reset-comm | 0x82 | 重置通信 |
提示:
- 参考
examples/03-nmt-command/中的nmt_start_node.c - 将发送逻辑封装为函数,通过参数传递命令码和节点 ID
练习 4:CANOpen COB-ID 分析器
目标:编写一个 CAN 帧解析工具,自动识别 CANOpen COB-ID 类型。
难度:高级
要求:
- 接收 CAN 帧并解析字段
- 区分标准帧和扩展帧
- 识别常见的 CANOpen COB-ID(NMT、SDO、PDO、Heartbeat、EMCY)
- 显示人类可读的描述信息
示例输出:
[0x000] NMT Command: Start -> Node 1
[0x701] Heartbeat from Node 1 (State: Operational)
[0x601] SDO Request to Node 1
[0x581] SDO Response from Node 1
[0x181] PDO1 TX from Node 1
[0x081] EMCY from Node 1
[0x123] Unknown COB-ID (可能是自定义)COB-ID 识别参考:
| COB-ID 范围 | 类型 | 节点 ID 提取 |
|---|---|---|
0x000 | NMT | 数据 Byte 1 |
0x080 + N | EMCY | COB-ID - 0x080 |
0x180 + N | PDO1 TX | COB-ID - 0x180 |
0x200 + N | PDO1 RX | COB-ID - 0x200 |
0x580 + N | SDO TX | COB-ID - 0x580 |
0x600 + N | SDO RX | COB-ID - 0x600 |
0x700 + N | Heartbeat | COB-ID - 0x700 |
提示:
- 使用
switch语句或查表法识别 COB-ID 范围 - 心跳状态解析:
0x00= Boot-up,0x04= Stopped,0x05= Operational,0x7F= Pre-Operational
思考题
物理层:为什么 CAN 使用差分信号而不是单端信号?在工业环境(电机、变频器附近)中的优势是什么?
仲裁机制:如果两个节点同时发送,ID 分别为
0x100和0x200,哪个会赢得仲裁?如果 ID 相同会怎样?帧格式:CAN 数据帧最多只能携带 8 字节数据。如果需要通过 SDO 传输 100 字节的对象字典条目,该怎么处理?
COB-ID:为什么 NMT 命令的 COB-ID 固定为
0x000?这与 CAN 的仲裁机制有什么关系?SocketCAN:
struct can_frame中的can_id字段如何区分标准帧和扩展帧?(提示:查看CAN_EFF_FLAG的定义)错误处理:如果
candump没有收到cansend发送的帧,可能的原因有哪些?(至少列出 3 种)
练习提交与验证
每个练习完成后,确保:
- 代码可以编译通过(
cmake + make无错误无警告) - 可以在 vcan 上正常运行
- 有基本的错误处理(参数校验、socket 错误检查)
- 代码注释清晰,变量命名合理
如果这篇文章对你有帮助,欢迎请我喝杯咖啡 ☕
支付宝
微信