树莓派CAN通讯教程 - MCP2515
1. 楔2. 硬件连接和环境准备2.1. 硬件连接2.2. 环境准备2.3. 检测MCP2515是否被正确挂载3. CAN 通讯设置及程序开发3.1. 系统命令3.2. c语言调用socket接口进行开发3.3. 实际效果1. 楔
在上篇文章树莓派GPIO和PWM控制教程中,笔者详细介绍了如何使用树莓进行普通IO控制模拟,以及PWM 波形发送等操作,同时还提到了汽车电子常见的CAN 通讯也能够使用树莓派完成,本文针对此进行详细说明。
使用硬件为树莓派3b+, MCP2515 spiCAN模块 ,总成本控制在300 元以内。使用树莓派4b也可以,不过近期的4b价格离谱,而且仅仅can和io的测试,3b+性能完全够用。
操作系统使用ubuntu server ,如使用Raspberry Pi OS 也可以,本教程也可适用。需要注意的是,不能魔法上网的同学需要首先进行操作系统换源,以及python3 的换源。而其中ubuntu server arm 的系统不能够按照常用的清华源教程替换,需要使用后缀为-ports的源,笔者使用的源如下:
/etc/apt/sources.list
# 默认注释了源码仓库,如有需要可自行取消注释deb https://mirrors./ubuntu-ports/ focal main restricted universe multiverse# deb-src https://mirrors./ubuntu-ports/ focal main main restricted universe multiversedeb https://mirrors./ubuntu-ports/ focal-updates main restricted universe multiverse# deb-src https://mirrors./ubuntu-ports/ focal-updates main restricted universe multiversedeb https://mirrors./ubuntu-ports/ focal-backports main restricted universe multiverse# deb-src https://mirrors./ubuntu-ports/ focal-backports main restricted universe multiversedeb https://mirrors./ubuntu-ports/ focal-security main restricted universe multiverse# deb-src https://mirrors./ubuntu-ports/ focal-security main restricted universe multiverse
2. 硬件连接和环境准备
连接MCP2515和树莓派spi接口,并在操作系统中开启spi,整个的运行原理就是让MCP2515的CAN 通讯作为网络通讯接口,挂接到socketCAN 上,使用系统驱动spi,无需手工编写spi驱动以及can wrapper部分。针对socketCAN,网上有很多优秀的开源工具可以使用,笔者这里使用的是cantools ,可以使用dbc进行报文格式解析。
2.1. 硬件连接
RPi Pin RPi LabelCAN Module02---------5V------------VCC06---------GND-----------GND19---------GPIO10--------MOSI (SI)21---------GPIO9---------MISO (SO)22---------GPIO25--------INT23---------GPIO11--------SCK24---------GPIO8---------CS
2.2. 环境准备
安装socket can工具以及cantools工具
sudo apt install can-utilspip3 install cantools
使能树莓派SPI并加载MCP2515内核驱动
针对Ubuntu server 操作系统,在/boot/firmware/usercfg.txt
文件后添加如下内容,若操作系统为Raspberry Pi OS,则在/boot/config.txt
文件后添加如下内容:
dtparam=spi=ondtoverlay=mcp2515-can0,oscillator=16000000,interrupt=25dtoverlay=spi1-1cs
重启sudo reboot -h now
2.3. 检测MCP2515是否被正确挂载
输入sudo ifconfig -a
指令可以看到已经挂载了网络通讯卡CAN0,如没有ifconfig,则使用sudo apt install net-tools
进行安装
ubuntu@ubuntu:~$ sudo ifconfig -acan0: flags=193<UP,RUNNING,NOARP> mtu 16unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 10 (UNSPEC)RX packets 9343435 bytes 74747480 (74.7 MB)RX errors 0 dropped 0 overruns 0 frame 0TX packets 8 bytes 64 (64.0 B)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0eth0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500ether b8:27:eb:c2:61:84 txqueuelen 1000 (Ethernet)RX packets 0 bytes 0 (0.0 B)RX errors 0 dropped 0 overruns 0 frame 0TX packets 0 bytes 0 (0.0 B)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
输入sudo ip -s -d link show can0
查看can0 通讯是否进入ready状态。
ubuntu@ubuntu:~$ sudo ip -s -d link show can03: can0: <NOARP,ECHO> mtu 16 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 10link/can promiscuity 0 minmtu 0 maxmtu 0can state STOPPED restart-ms 0bitrate 1000000 sample-point 0.750tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1mcp251x: tseg1 3..16 tseg2 2..8 sjw 1..4 brp 1..64 brp-inc 1clock 8000000re-started bus-errors arbit-lost error-warn error-pass bus-off000000 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535RX: bytes packets errors dropped overrun mcast74955120 9369390 0 0 0 0TX: bytes packets errors dropped carrier collsns64 8 0 0 0 0
3. CAN 通讯设置及程序开发
如上步骤如果已经完成,则可进行相关CAN 通讯开发,使用python和c都可以,因为系统支持命令行进行报文发送读取及设定,所以python可以简单的调用系统命令。c的话不建议调用系统命令,而是使用socket接口进行编程。
需注意,由于硬件限制,此方案can通讯波特率最高仅支持500Kbps
3.1. 系统命令
关闭can0
sudo ip link set can0 down
设置波特率 500K ,需注意bitrate 需要除2才是常规的通讯波特率
sudo ip link set can0 type can bitrate 1000000
开启can0
sudo ip link set can0 up
查看状态
sudo ip -s -d link show can0
接收报文命令
candump any,0:0,#FFFFFFFF
联合 cantools 使用dbc文件进行报文解码
candump can0 | cantools decode temp.dbc
发送报文命令
cansend can0 123#1122334455667788
设置回环 波特率 250K ,用于测试can通路,在没有其它硬件连接测试的情况下,可以设定成回环,自发自收
sudo ip link set can0 type can bitrate 500000 loopback on
3.2. c语言调用socket接口进行开发
/***-----------------------------------------------------------------------------* @file can_control.c* @brief * @author Tomato* @version 0.1* @date -07-22* @note [change history] * * @copyright NAAAAA*-----------------------------------------------------------------------------*/#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>#define command "ip link set can0 type can bitrate 1000000"//将CAN0波特率设置为500K#define up "ifconfig can0 up"//打开CAN0#define down "ifconfig can0 down"//关闭CAN0int can_init(){//关闭CAN设备,设置波特率后,重新打开CAN设备system(down);system(command);system(up);return 0;}int can_send(can_frame frame){int s, nbytes;struct sockaddr_can addr;struct ifreq ifr;//创建套接字s = socket(PF_CAN, SOCK_RAW, CAN_RAW);strcpy(ifr.ifr_name, "can0" );//指定 can0 设备ioctl(s, SIOCGIFINDEX, &ifr); addr.can_family = AF_CAN;addr.can_ifindex = ifr.ifr_ifindex;//将套接字与 can0 绑定bind(s, (struct sockaddr *)&addr, sizeof(addr));//发送 frame[0]nbytes = write(s, &frame, sizeof(frame));if(nbytes != sizeof(frame)){printf("Send Error frame[0]\n!");}close(s);return 0;}int can_receive(struct can_frame * r_frame,unsigned int filter_id){int s, nbytes = 0;struct sockaddr_can addr;struct ifreq ifr;struct can_frame frame;struct can_filter rfilter;// Initial frammemset(&frame,0,sizeof(can_frame));//创建套接字s = socket(PF_CAN, SOCK_RAW, CAN_RAW);strcpy(ifr.ifr_name, "can0" );//指定 can0 设备ioctl(s, SIOCGIFINDEX, &ifr); addr.can_family = AF_CAN;addr.can_ifindex = ifr.ifr_ifindex;//将套接字与 can0 绑定bind(s, (struct sockaddr *)&addr, sizeof(addr));//设置过滤规则,取消当前注释为禁用过滤规则,即不接收所有报文,// 不设置此项(即如当前代码被注释)为接收所有ID的报文。if (filter_id != 0){rfilter.can_id = 0x123;// CAN_EFF_MASK | CAN_SFF_MASKrfilter.can_mask = CAN_SFF_MASK;setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));}while (nbytes == 0){//接收总线上的报文保存在frame中nbytes = read(s, &frame, sizeof(frame));}*r_frame = frame;#ifdef MSG_DEBUGprintf("the nbytes:%d\n", nbytes);printf("length:%d", sizeof(frame));printf("ID=0x%X DLC=%d\n", frame.can_id, frame.can_dlc);printf("data0=0x%02x\n",frame.data[0]);printf("data1=0x%02x\n",frame.data[1]);printf("data2=0x%02x\n",frame.data[2]);printf("data3=0x%02x\n",frame.data[3]);printf("data4=0x%02x\n",frame.data[4]);printf("data5=0x%02x\n",frame.data[5]);printf("data6=0x%02x\n",frame.data[6]);printf("data7=0x%02x\n",frame.data[7]);#endifreturn 0;}int led_ctl_on(void){struct can_frame frame;memset(&frame, 0, sizeof(can_frame));frame.can_id = 0x101;frame.can_dlc = 8;frame.data[0] = 1;can_send(frame);return 0;}int led_ctl_off(void){struct can_frame frame;memset(&frame, 0, sizeof(can_frame));frame.can_id = 0x101;frame.can_dlc = 8;frame.data[0] = 2;can_send(frame);return 0;}float can_get_vol(void){float vol_vle = 0;struct can_frame frame;memset(&frame, 0, sizeof(can_frame));// wait until can frame 100 receivedcan_receive(&frame,0);printf("###############################\n");printf("length:%d", sizeof(frame));printf("ID=0x%X DLC=%d\n", frame.can_id, frame.can_dlc);printf("data0=0x%02x\n",frame.data[0]);printf("data1=0x%02x\n",frame.data[1]);printf("data2=0x%02x\n",frame.data[2]);printf("data3=0x%02x\n",frame.data[3]);printf("data4=0x%02x\n",frame.data[4]);printf("data5=0x%02x\n",frame.data[5]);printf("data6=0x%02x\n",frame.data[6]);printf("data7=0x%02x\n",frame.data[7]);vol_vle = (float)frame.data[0]/50;return vol_vle;}int main(int argc, char* argv[]){char control_str[15]; float vol_val = 0;if (argc < 2) {printf("can_control service_type\n"" example: ./can_control led_off/led_on/get_vol\n");return 0;}strcpy(control_str,argv[1]);// debugprintf("Argc : %d\n",argc);printf("Argv : %s\n , %s\n",argv[0], argv[1]);// can_init();if (strcmp(control_str,"led_off")==0){led_ctl_off();}else if (strcmp(control_str,"led_on")==0){led_ctl_on();}else if (strcmp(control_str,"get_vol")==0){vol_val = can_get_vol();printf("Voltage is : %5.2f V\n", vol_val);}else{/* Do nothing */}return 0;}
3.3. 实际效果
如果觉得《树莓派CAN通讯教程 - MCP2515》对你有帮助,请点赞、收藏,并留下你的观点哦!