数据帧
遥控帧
错误帧
过载帧
帧间隔
访问 CAN 设备
查找 CAN 设备
应用程序根据 CAN 设备名称查找设备获取设备句柄,进而可以操作 CAN 设备,查找设备函数如下所示,
1rt_device_t rt_device_find(const char* name);
1#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
2
3static rt_device_t can_dev; /* CAN 设备句柄 */
4/* 查找 CAN 设备 */
5can_dev = rt_device_find(CAN_DEV_NAME);
通过设备句柄,应用程序可以打开和关闭设备,打开设备时,会检测设备是否已经初始化,没有初始化则会默认调用初始化接口初始化设备。通过如下函数打开设备:
1rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
1#define RT_DEVICE_FLAG_INT_RX 0x100 /* 中断接收模式 */
2#define RT_DEVICE_FLAG_INT_TX 0x400 /* 中断发送模式 */
以中断接收及发送模式打开 CAN 设备的示例如下所示:
1#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
2
3static rt_device_t can_dev; /* CAN 设备句柄 */
4/* 查找 CAN 设备 */
5can_dev = rt_device_find(CAN_DEV_NAME);
6/* 以中断接收及发送模式打开 CAN 设备 */
7rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
控制 CAN 设备
1rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
1#define RT_DEVICE_CTRL_RESUME 0x01 /* 恢复设备 */
2#define RT_DEVICE_CTRL_SUSPEND 0x02 /* 挂起设备 */
3#define RT_DEVICE_CTRL_CONFIG 0x03 /* 配置设备 */
4
5#define RT_CAN_CMD_SET_FILTER 0x13 /* 设置硬件过滤表 */
6#define RT_CAN_CMD_SET_BAUD 0x14 /* 设置波特率 */
7#define RT_CAN_CMD_SET_MODE 0x15 /* 设置 CAN 工作模式 */
8#define RT_CAN_CMD_SET_PRIV 0x16 /* 设置发送优先级 */
9#define RT_CAN_CMD_GET_STATUS 0x17 /* 获取 CAN 设备状态 */
10#define RT_CAN_CMD_SET_STATUS_IND 0x18 /* 设置状态回调函数 */
11#define RT_CAN_CMD_SET_BUS_HOOK 0x19 /* 设置 CAN 总线钩子函数 */
设置波特率
设置波特率的示例代码如下所示:
1#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
2
3static rt_device_t can_dev; /* CAN 设备句柄 */
4
5/* 查找 CAN 设备 */
6can_dev = rt_device_find(CAN_DEV_NAME);
7/* 以中断接收及发送方式打开 CAN 设备 */
8res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
9/* 设置 CAN 通信的波特率为 500kbit/s*/
10res = rt_device_control(can_dev, RT_CAN_CMD_SET_BAUD, (void *)CAN500kBaud);
设置工作模式
设置工作模式的示例代码如下所示:
1#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
2
3static rt_device_t can_dev; /* CAN 设备句柄 */
4
5/* 查找 CAN 设备 */
6can_dev = rt_device_find(CAN_DEV_NAME);
7/* 以中断接收及发送方式打开 CAN 设备 */
8res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
9/* 设置 CAN 的工作模式为正常工作模式 */
10res = rt_device_control(can_dev, RT_CAN_CMD_SET_MODE, (void *)RT_CAN_MODE_NORMAL);
获取 CAN 设备状态
1#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
2
3static rt_device_t can_dev; /* CAN 设备句柄 */
4static struct rt_can_status status; /* 获取到的 CAN 总线状态 */
5
6/* 查找 CAN 设备 */
7can_dev = rt_device_find(CAN_DEV_NAME);
8/* 以中断接收及发送方式打开 CAN 设备 */
9res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
10/* 获取 CAN 总线设备的状态 */
11res = rt_device_control(can_dev, RT_CAN_CMD_GET_STATUS, &status);
设置硬件过滤表
过滤表控制块各成员描述如下所示:
1struct rt_can_filter_item
2{
3 rt_uint32_t id : 29; /* 报文 ID */
4 rt_uint32_t ide : 1; /* 扩展帧标识位 */
5 rt_uint32_t rtr : 1; /* 远程帧标识位 */
6 rt_uint32_t mode : 1; /* 过滤表模式 */
7 rt_uint32_t mask; /* ID 掩码,0 表示对应的位不关心,1 表示对应的位必须匹配 */
8 rt_int32_t hdr; /* -1 表示不指定过滤表号,对应的过滤表控制块也不会被初始化,正数为过滤表号,对应的过滤表控制块会被初始化 */
9#ifdef RT_CAN_USING_HDR
10 /* 过滤表回调函数 */
11 rt_err_t (*ind)(rt_device_t dev, void *args , rt_int32_t hdr, rt_size_t size);
12 /* 回调函数参数 */
13 void *args;
14#endif /*RT_CAN_USING_HDR*/
15};
1struct rt_can_filter_item filter;
2/* 报文 ID */
3filter.id = 0x01;
4/* 标准格式 */
5filter.ide = 0x00;
6/* 数据帧 */
7filter.rtr = 0x00;
8/* 过滤表模式 */
9filter.mode = 0x01;
10/* 匹配 ID */
11filter.mask = 0x01;
12/* 使用默认过滤表 */
13filter.hdr = -1;
为了方便表示过滤表的各个成员变量的值, RT-Thread 系统提供了匹配过滤表的宏,
1#define RT_CAN_FILTER_ITEM_INIT(id,ide,rtr,mode,mask,ind,args) \
2 {(id), (ide), (rtr), (mode), (mask), -1, (ind), (args)}
1RT_CAN_FILTER_ITEM_INIT(0x01, 0, 0, 1, 0x01, RT_NULL, RT_NULL);
当需要使用过滤表时还需要指定过滤表配置控制块的成员变量,过滤表的配置控制块成员变量的组成如下所示:
1struct rt_can_filter_config
2{
3 rt_uint32_t count; /* 过滤表数量 */
4 rt_uint32_t actived; /* 过滤表激活选项,1 表示初始化过滤表控制块,0 表示去初始化过滤表控制块 */
5 struct rt_can_filter_item *items; /* 过滤表指针,可指向一个过滤表数组 */
6};
1#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
2
3static rt_device_t can_dev; /* CAN 设备句柄 */
4
5can_dev = rt_device_find(CAN_DEV_NAME);
6
7/* 以中断接收及发送模式打开 CAN 设备 */
8rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
9
10struct rt_can_filter_item items[1] =
11{
12 RT_CAN_FILTER_ITEM_INIT(0x01, 0, 0, 1, 0x01, RT_NULL, RT_NULL),
13 /* 过滤 ID 为 0x01,match ID:0x100~0x1ff,hdr 为 - 1,设置默认过滤表 */
14};
15struct rt_can_filter_config cfg = {1, 1, items}; /* 一共有 1 个过滤表 */
16/* 设置硬件过滤表 */
17res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg);
发送数据
使用 CAN 设备发送数据,可以通过如下函数完成:
1rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
1struct rt_can_msg
2{
3 rt_uint32_t id : 29; /* CAN ID, 标志格式 11 位,扩展格式 29 位 */
4 rt_uint32_t ide : 1; /* 扩展帧标识位 */
5 rt_uint32_t rtr : 1; /* 远程帧标识位 */
6 rt_uint32_t rsv : 1; /* 保留位 */
7 rt_uint32_t len : 8; /* 数据段长度 */
8 rt_uint32_t priv : 8; /* 报文发送优先级 */
9 rt_uint32_t hdr : 8; /* 硬件过滤表号 */
10 rt_uint32_t reserved : 8;
11 rt_uint8_t data[8]; /* 数据段 */
12};
1#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
2
3static rt_device_t can_dev; /* CAN 设备句柄 */
4struct rt_can_msg msg = {0}; /* CAN 消息 */
5
6can_dev = rt_device_find(CAN_DEV_NAME);
7
8/* 以中断接收及发送模式打开 CAN 设备 */
9rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
10
11msg.id = 0x78; /* ID 为 0x78 */
12msg.ide = RT_CAN_STDID; /* 标准格式 */
13msg.rtr = RT_CAN_DTR; /* 数据帧 */
14msg.len = 8; /* 数据长度为 8 */
15/* 待发送的 8 字节数据 */
16msg.data[0] = 0x00;
17msg.data[1] = 0x11;
18msg.data[2] = 0x22;
19msg.data[3] = 0x33;
20msg.data[4] = 0x44;
21msg.data[5] = 0x55;
22msg.data[6] = 0x66;
23msg.data[7] = 0x77;
24/* 发送一帧 CAN 数据 */
25size = rt_device_write(can_dev, 0, &msg, sizeof(msg));
设置接收回调函数
可以通过如下函数来设置数据接收指示,当 CAN 收到数据时,通知上层应用线程有数据到达 :
1#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
2static rt_device_t can_dev; /* CAN 设备句柄 */
3struct rt_can_msg msg = {0}; /* CAN 消息 */
4
5/* 接收数据回调函数 */
6static rt_err_t can_rx_call(rt_device_t dev, rt_size_t size)
7{
8 /* CAN 接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
9 rt_sem_release(&rx_sem);
10
11 return RT_EOK;
12}
13
14/* 设置接收回调函数 */
15rt_device_set_rx_indicate(can_dev, can_rx_call);
接收数据
可调用如下函数读取 CAN 设备接收到的数据:
1rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
1#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
2
3static rt_device_t can_dev; /* CAN 设备句柄 */
4struct rt_can_msg rxmsg = {0}; /* CAN 接收消息缓冲区 */
5
6/* hdr 值为 - 1,表示直接从 uselist 链表读取数据 */
7rxmsg.hdr = -1;
8
9/* 阻塞等待接收信号量 */
10rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
11/* 从 CAN 读取一帧数据 */
12rt_device_read(can_dev, 0, &rxmsg, sizeof(rxmsg));
关闭 CAN 设备
当应用程序完成 CAN 操作后,可以关闭 CAN 设备,通过如下函数完成:
1rt_err_t rt_device_close(rt_device_t dev);
can_sample
即可运行示例代码,后面数据为 CAN 设备接收到的数据:
1 \ | /
2- RT - Thread Operating System
3 / | \ 4.0.1 build Jun 24 2019
4 2006 - 2019 Copyright by rt-thread team
5msh >can_sample
6ID:486 0 11 22 33 0 23 4 86
7ID:111 0 11 22 33 0 23 1 11
8ID:555 0 11 22 33 0 23 5 55
9ID:211 0 11 22 33 0 23 2 11
10ID:344 0 11 22 33 0 23 3 44
可以使用 CAN 分析工具连接对应 CAN 设备收发数据,第一帧数据为 CAN 示例代码发送的 ID 为 0X78的数据, 效果如下图所示:
1/*
2 * 程序清单:这是一个 CAN 设备使用例程
3 * 例程导出了 can_sample 命令到控制终端
4 * 命令调用格式:can_sample can1
5 * 命令解释:命令第二个参数是要使用的 CAN 设备名称,为空则使用默认的 CAN 设备
6 * 程序功能:通过 CAN 设备发送一帧,并创建一个线程接收数据然后打印输出。
7*/
8
9#include <rtthread.h>
10#include "rtdevice.h"
11
12#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
13
14static struct rt_semaphore rx_sem; /* 用于接收消息的信号量 */
15static rt_device_t can_dev; /* CAN 设备句柄 */
16
17/* 接收数据回调函数 */
18static rt_err_t can_rx_call(rt_device_t dev, rt_size_t size)
19{
20 /* CAN 接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
21 rt_sem_release(&rx_sem);
22
23 return RT_EOK;
24}
25
26static void can_rx_thread(void *parameter)
27{
28 int i;
29 rt_err_t res;
30 struct rt_can_msg rxmsg = {0};
31
32 /* 设置接收回调函数 */
33 rt_device_set_rx_indicate(can_dev, can_rx_call);
34
35#ifdef RT_CAN_USING_HDR
36 struct rt_can_filter_item items[5] =
37 {
38 RT_CAN_FILTER_ITEM_INIT(0x100, 0, 0, 1, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x100~0x1ff,hdr 为 - 1,设置默认过滤表 */
39 RT_CAN_FILTER_ITEM_INIT(0x300, 0, 0, 1, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x300~0x3ff,hdr 为 - 1 */
40 RT_CAN_FILTER_ITEM_INIT(0x211, 0, 0, 1, 0x7ff, RT_NULL, RT_NULL), /* std,match ID:0x211,hdr 为 - 1 */
41 RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL), /* std,match ID:0x486,hdr 为 - 1 */
42 {0x555, 0, 0, 1, 0x7ff, 7,} /* std,match ID:0x555,hdr 为 7,指定设置 7 号过滤表 */
43 };
44 struct rt_can_filter_config cfg = {5, 1, items}; /* 一共有 5 个过滤表 */
45 /* 设置硬件过滤表 */
46 res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg);
47 RT_ASSERT(res == RT_EOK);
48#endif
49
50 while (1)
51 {
52 /* hdr 值为 - 1,表示直接从 uselist 链表读取数据 */
53 rxmsg.hdr = -1;
54 /* 阻塞等待接收信号量 */
55 rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
56 /* 从 CAN 读取一帧数据 */
57 rt_device_read(can_dev, 0, &rxmsg, sizeof(rxmsg));
58 /* 打印数据 ID 及内容 */
59 rt_kprintf("ID:%x", rxmsg.id);
60 for (i = 0; i < 8; i++)
61 {
62 rt_kprintf("%2x", rxmsg.data[i]);
63 }
64
65 rt_kprintf("\n");
66 }
67}
68
69int can_sample(int argc, char *argv[])
70{
71 struct rt_can_msg msg = {0};
72 rt_err_t res;
73 rt_size_t size;
74 rt_thread_t thread;
75 char can_name[RT_NAME_MAX];
76
77 if (argc == 2)
78 {
79 rt_strncpy(can_name, argv[1], RT_NAME_MAX);
80 }
81 else
82 {
83 rt_strncpy(can_name, CAN_DEV_NAME, RT_NAME_MAX);
84 }
85 /* 查找 CAN 设备 */
86 can_dev = rt_device_find(can_name);
87 if (!can_dev)
88 {
89 rt_kprintf("find %s failed!\n", can_name);
90 return RT_ERROR;
91 }
92
93 /* 初始化 CAN 接收信号量 */
94 rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
95
96 /* 以中断接收及发送方式打开 CAN 设备 */
97 res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
98 RT_ASSERT(res == RT_EOK);
99 /* 创建数据接收线程 */
100 thread = rt_thread_create("can_rx", can_rx_thread, RT_NULL, 1024, 25, 10);
101 if (thread != RT_NULL)
102 {
103 rt_thread_startup(thread);
104 }
105 else
106 {
107 rt_kprintf("create can_rx thread failed!\n");
108 }
109
110 msg.id = 0x78; /* ID 为 0x78 */
111 msg.ide = RT_CAN_STDID; /* 标准格式 */
112 msg.rtr = RT_CAN_DTR; /* 数据帧 */
113 msg.len = 8; /* 数据长度为 8 */
114 /* 待发送的 8 字节数据 */
115 msg.data[0] = 0x00;
116 msg.data[1] = 0x11;
117 msg.data[2] = 0x22;
118 msg.data[3] = 0x33;
119 msg.data[4] = 0x44;
120 msg.data[5] = 0x55;
121 msg.data[6] = 0x66;
122 msg.data[7] = 0x77;
123 /* 发送一帧 CAN 数据 */
124 size = rt_device_write(can_dev, 0, &msg, sizeof(msg));
125 if (size == 0)
126 {
127 rt_kprintf("can dev write data failed!\n");
128 }
129
130 return res;
131}
132/* 导出到 msh 命令列表中 */
133MSH_CMD_EXPORT(can_sample, can device sample);
END
RT-Thread线上活动
1、【RT-Thread能力认证考试12月——RCEA】经过第一次考试的验证,
能力认证官网链接:https://www.rt-thread.org/page/rac.html(在外部浏览器打开)
立即报名
#题外话# 喜欢RT-Thread不要忘了在GitHub上留下你的STAR哦,你的star对我们来说非常重要!链接地址:https://github.com/RT-Thread/rt-thread
RT-Thread线下活动
1、STM32全国研讨会,RT-Thread近期参展城市预告:上海、广州、顺德
你可以添加微信18917005679为好友,注明:公司+姓名,拉进 RT-Thread 官方微信交流群
RT-Thread
长按二维码,关注我们
点击阅读原文进入GitHub