zynq+linux+ramdisk can调试
由于采用ramdisk文件系统,自带的ip工具版本太旧无法配置can,需要自行编译ip,具体参见参考文献2
1.vivado配置ps
2.设备树增加can0,一般开发板均已提供此配置
can@e0008000 { compatible = "xlnx,zynq-can-1.0"; status = "okay"; clocks = <0x1 0x13 0x1 0x24>; clock-names = "can_clk", "pclk"; reg = <0xe0008000 0x1000>; interrupts = <0x0 0x1c 0x4>; interrupt-parent = <0x3>; tx-fifo-depth = <0x40>; rx-fifo-depth = <0x40>; };
3.kernel配置,一般已配好,具体参见参考文献1
4.测试
# 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:22 eth0 Link encap:Ethernet HWaddr 00:0A:35:00:01:22 inet addr:192.168.0.120 Bcast:192.168.0.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:397 errors:0 dropped:0 overruns:0 frame:0 TX packets:242 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:476490 (465.3 KiB) TX bytes:18536 (18.1 KiB) Interrupt:148 Base address:0xb000 lo Link encap:Local Loopback LOOPBACK MTU:65536 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:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
设置can0的波特率,这里设置的是100k, ip程序一般需要自己编译生成,参见2
#./ip link set can0 up type can bitrate 100000
显示can0状态信息
#./ip -d -s link show can0
Z-turn# ./ip link set can0 type can bitrate 100000 xilinx_can e0008000.can can0: bitrate error 0.0% Z-turn#./ip link set can0 up Z-turn#./ip -d -s link show can0 2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10 link/can promiscuity 0 can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0 bitrate 99999 sample-point 0.750 tq 2500 prop-seg 1 phase-seg1 1 phase-seg2 1 sjw 1 xilinx_can: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..256 brp-inc 1 clock 99999999 re-started bus-errors arbit-lost error-warn error-pass bus-off 0 0 0 0 0 0 numtxqueues 1 numrxqueues 1 RX: bytes packets errors dropped overrun mcast 0 0 0 0 0 0 TX: bytes packets errors dropped carrier collsns 0 0 0 0 0 0
5.下面程序为收发测试,也可参见参考文献3
/***************************************************************************** * Copyright (c) 2014-2017 MYIR Tech Ltd. * File: can-test.c * Date: 2014/11/3 * Author: Kevin Su * Description: A demo program to show how to transmit/receive data with * socket can interface on CAN bus. * Please note that, this demo needs two boards to run as * transmitter and receiver. * Before run "can-test", we need to config the bitrate with * "ip" command: * # ip link set can0 up type can bitrate 100000 */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <net/if.h> #include <linux/can.h> #include <linux/can/raw.h> #define DEBUG 1 #define ERR_MSG(fmt, args...) fprintf(stderr, fmt, ##args) #ifdef DEBUG #define DBG_MSG(fmt, args...) fprintf(stdout, fmt, ##args) #else #define DBG_MSG(fmt, args...) #endif #ifndef PF_CAN #define PF_CAN 29 #endif #ifndef AF_CAN #define AF_CAN PF_CAN #endif int main(int argc, char *argv[]) { int fd, ret, flag, len; char senddata[32] = "test"; struct sockaddr_can addr; struct ifreq ifr; struct can_frame frame; socklen_t socket_len = sizeof(struct sockaddr_can); /* Create a socket with PF_CAN family, SOCK_RAW and CAN_RAW protocol */ fd = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (fd < 0) { ERR_MSG("Open socket failed!\n"); return fd; } /* Use can0 */ strcpy((char *)(ifr.ifr_name), "can0"); /* Get information */ ret = ioctl(fd, SIOCGIFINDEX, &ifr); if (ret != 0) { ERR_MSG("SIOCGIFINDEX failed! ret:%d\n", ret); close(fd); return ret; } DBG_MSG("can0 can_ifindex = %x\n",ifr.ifr_ifindex); /* Disable loopback */ flag = 0; ret = setsockopt(fd, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &flag, sizeof(flag)); if (ret != 0) { ERR_MSG("Set loopback disable failed! ret:%d\n", ret); close(fd); return ret; } DBG_MSG("Set can0 loopback disable\n"); /* Disable receiving own message */ flag = 0; ret = setsockopt(fd, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &flag, sizeof(flag)); if (ret != 0) { ERR_MSG("Disable receiving own message failed! ret:%d\n", ret); close(fd); return ret; } DBG_MSG("Disable receiving own message\n"); /* Use AF_CAN protocol family */ addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; /* Binding socket */ ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr)); if (ret != 0) { ERR_MSG("Bind socket failed! ret:%d\n", ret); close(fd); return ret; } DBG_MSG("Bind can0 socket\n"); frame.can_id = 0x123; len = strlen(senddata); while (1) { strncpy((char *)frame.data, senddata, len); frame.can_dlc = len; ret = sendto(fd, &frame, sizeof(struct can_frame), 0, (struct sockaddr*)&addr, sizeof(addr)); if (ret > 0) { DBG_MSG("Send success: [%s], ret=%d\n", senddata, ret); ret = recvfrom(fd, &frame, sizeof(struct can_frame), 0, (struct sockaddr *)&addr, &socket_len); if (ret > 0) { DBG_MSG("Recv message: [%s], ret=%d\n", frame.data, ret); } } usleep(500000); } return 0; }
超时设置(参考文献4):
struct timeval timeout = {1,0}; //1.0s //设置发送超时 setsockopt(socket,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(struct timeval)); //设置接收超时 setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval));
对于offline节点处理:
如果该控制器没有连接到can网络上,调用send会返回成功,但只是发送至kernel的缓冲区,并非发送至can网络。kernel会一直尝试重发,即使关闭socket或结束进程都不会取消重 发操作,此时如果将控制器连接到can网络上则会一次性将缓冲区内所有内容发出,有时这并非所要结果。如果想要清空发送和接收缓冲区,只能重启can接口
ifconfig can0 down ifconfig can0 up
在程序中可使用system调用。
有两点注意就是:
1)recv ()的第四个参数需为MSG_WAITALL,在阻塞模式下不等到指定数目的数据不会返回,除非超时时间到。
2)即使等待超时时间值未到,但对方已经关闭了socket, 则此时recv()会立即返回,并收到多少数据返回多少数据。
参考文献:
1.http://xilinx.eetrend.com/blog/12062
2.http://www.cnblogs.com/hujianhua/p/8446291.html
3.https://wenku.baidu.com/view/65baea51bb68a98271fefaa7.html
4.https://blog.csdn.net/newger/article/details/2459113