前言
这是前篇:
本篇介绍下can的使用, 采用米尔MYD-YA157C
板子出厂配置的系统. 板子自带一路CAN:
连接到CAN分析仪上, 启用120Ω终端电阻:
CAN终端测试
查看CAN接口:
root@myir:~# ls /sys/class/net/
can0 eth0 lo usb0 wlan0
或者ifconfig -a
:
root@myir:~# ifconfig -a
can0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
NOARP MTU:16 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:10
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
Interrupt:41
可以先关闭CAN网络, 设置波特率等参数, 然后再打开CAN, 进行发送和接收的测试
root@myir:~# ifconfig can0 down
root@myir:~# ip link set can0 type can bitrate 500000
[ 211.454557] m_can 4400e000.can can0: bitrate error 0.7%
root@myir:~# ifconfig can0 up
root@myir:~# cansend can0 18F#11.22.33.44.55.66.77.88
root@myir:~# candump can0
其中, 测试发送的cansend can0 18F#11.22.33.44.55.66.77.88
(后面是十六进制), 可以在主机上收到信息, 但测试接收的candump can0
, 通过can分析仪发送没有收到数据, 还不太清楚原因.
发送时的格式如下:
<can_id>#{R|data} for CAN 2.0 frames
<can_id>##<flags>{data} for CAN FD frames
<can_id> can have 3 (SFF) or 8 (EFF) hex chars
{data} has 0..8 (0..64 CAN FD) ASCII hex-values (optionally separated by '.')
<flags> a single ASCII Hex value (0 .. F) which defines canfd_frame.flags
e.g. 5A1#11.2233.44556677.88 / 123#DEADBEEF / 5AA# / 123##1 / 213##311
1F334455#1122334455667788 / 123#R for remote transmission request.
需要注意的是:
- 标准帧是3个字节, 扩展帧是8个字节(如
cansend can0 018F0000#11.22.33.44.55.66.77.88
, 前面的0不能少). - CAN 2.0是一个
#
, CAN FD是两个#
- 配置的参数掉电就会消失, 可以写成脚本放到开机启动里面, 参考TX2/Linux下can总线的接收与发送详解
SocketCAN
SocketCAN - Controller Area Network, 这是Linux内核SocketCAN的介绍.
SocketCAN使用Berkeley套接字API, Linux网络堆栈, 将CAN设备驱动程序实现为网络接口, 这样可以用bind等, 避免了复杂的操作, 但与TCP / IP和以太网网络不同的是,CAN总线仅仅是广播媒介,没有像以太网一样的MAC层寻址。CAN标识符(can_id)用于在CAN总线上进行仲裁(ID越小优先级越高)。因此,最好将CAN-ID视为一种源地址。
C语言CAN发送示例
米尔官方的can_send例子需要./can_test -d can0 -i 123 11 2233 44 55 66 77 88
, 这里简化下, 编写can_test.c
用于测试CAN发送:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <getopt.h>
#define CAN_DEVICE "can0"
#define CAN_ID0 0x18f
#define CAN_DLC 8u
int can_data[8] = {250, 2, 3, 4, 5, 6, 7, 8};
int main(int argc, char *argv[])
{
int s, nbytes, i;
struct ifreq ifr;
struct sockaddr_can addr;
struct can_frame frame[1];
/* create a socket */
s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
strcpy(ifr.ifr_name, CAN_DEVICE);
/* determine the interface index */
ioctl(s, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
/* bind the socket to a CAN interface */
bind(s, (struct sockaddr *)&addr, sizeof(addr));
/* Set the filter rules */
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
/* generate CAN frames */
frame[0].can_id = CAN_ID0;
frame[0].can_dlc = CAN_DLC;
for (i = 0; i < frame[0].can_dlc; i++)
{
frame[0].data[i] = can_data[i];
}
/* send CAN frames */
while (1)
{
nbytes = write(s, &frame[0], sizeof(frame[0]));
if (nbytes < 0)
{
perror("can raw socket write");
return 1;
}
++frame[0].data[7];
/* paranoid check ... */
if (nbytes < sizeof(struct can_frame))
{
fprintf(stderr, "read: incomplete CAN frame\n");
return 1;
}
usleep(1000000);
}
close(s);
return 0;
}
交叉编译arm-linux-gnueabihf-gcc can_test.c -o can_test2
, 放到板子上运行./can_test2
, 默认的回环开启:
CAN分析仪收到的数据:
C语言CAN接收示例
直接把米尔官方的can_receive.c挪过来:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <getopt.h>
void print_usage (FILE *stream, int exit_code)
{
fprintf(stream, "Usage: option [ dev... ] \n");
fprintf(stream,
"\t-h --help Display this usage information.\n"
"\t-d --device The device can[0-1]\n"
"\t-i --id Set the can id that want to receive\n");
exit(exit_code);
}
/**
* @brief: main function
* @Param: argc: number of parameters
* @Param: argv: parameters list
*/
int main()
{
int s, nbytes, i;
char *device="can0";
int id, next_option, device_flag=0, id_flag=0;
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame;
struct can_filter rfilter[1];
const char *const short_options = "hd:i:";
const struct option long_options[] = {
{ "help", 0, NULL, 'h'},
{ "device", 1, NULL, 'd'},
{ "id", 1, NULL, 'i'},
{ NULL, 0, NULL, 0 }
};
/* create a socket */
s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
strcpy(ifr.ifr_name, device);
/* determine the interface index */
ioctl(s, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
/* bind the socket to a CAN interface */
bind(s, (struct sockaddr *)&addr, sizeof(addr));
if (id_flag) {
/* define the filter rules */
rfilter[0].can_id = id;
rfilter[0].can_mask = CAN_SFF_MASK;
/* Set the filter rules */
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
}
while(1) {
/* receive frame */
nbytes = read(s, &frame, sizeof(frame));
/* printf the received frame */
if (nbytes > 0) {
printf("%s %#x [%d] ", ifr.ifr_name, frame.can_id, frame.can_dlc);
for (i = 0; i < frame.can_dlc; i++)
printf("%#x ", frame.data[i]);
printf("\n");
}
}
close(s);
return 0;
}
然而, 上面终端测试没有收到, 这个自然也没有收到, 原因待排查.
如果想要加滤波器, 搬来知乎的这篇写的非常好的文章 Linux CAN编程详解中的例子:
/* 2. 报文过滤接收程序 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
int main()
{
int s, nbytes;
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame;
struct can_filter rfilter[1];
s = socket(PF_CAN, SOCK_RAW, CAN_RAW); //创建套接字
strcpy(ifr.ifr_name, "can0");
ioctl(s, SIOCGIFINDEX, &ifr); //指定 can0 设备
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
bind(s, (struct sockaddr *)&addr, sizeof(addr)); //将套接字与 can0 绑定
//定义接收规则,只接收表示符等于 0x11 的报文
rfilter[0].can_id = 0x11;
rfilter[0].can_mask = CAN_SFF_MASK;
//设置过滤规则
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
while (1)
{
nbytes = read(s, &frame, sizeof(frame)); //接收报文
//显示报文
if (nbytes > 0)
{
printf("ID = 0x %X DLC = %d data[0] = 0x %X\n", frame.can_id, frame.can_dlc, frame.data[0]);
}
}
close(s);
return 0;
}
微信公众号
欢迎扫描关注我的微信公众号, 及时获取最新文章: