1.实验原理
打开扩展板原理图对照扩展板可以看到扩展板上
CAN
总线相关电路如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/633e239a2856d5b3cf90294371b12d3e.png)
图中
SN65HVD230
是德州仪器公司生产的
3.3V CAN
收发器,该器件适用于较高通讯速率、良好抗干扰能力和高可靠性 CAN
总线的串行通信。
SN65HVD230 RXD
和
RXD
分别接的是 STM32MP1
的
CAN1_TX
和
CAN1_RX
。
![](https://i-blog.csdnimg.cn/blog_migrate/eca53b1225e8c8d8933135424672e818.png)
由于 STM32MP157 有多个 IO 具备 CAN1_RX 和 CAN1_TX 的功能,需由电路确认电路 中使用的是哪个 IO,由上图最终确认 CAN1_RX 使用的是 PI9,CAN1_TX 使用的是 PH13。 查看手册管脚功能说明如下:
2.实验原理
导入交叉编译工具链
linux@ubuntu:$ source /opt/st/stm32mp1/3.1-openstlinux-5.4-dunfell-mp1-20-06-24/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
1) 内核配置
内核中有
STM32MP1
的
CAN
标准驱动,只要在内核中配置对应选项即可,驱动路径为:
drivers/net/can/m_can/m_can.c
执行
make menuconfig
配置内核对应选项
linux@ubuntu:$ make menuconfig[*] Networking support ---><*> CAN bus subsystem support --->CAN Device Drivers ---><*> Bosch M_CAN devices
2) 修改设备树
参考
linux
内核文档:
Documentation/devicetree/bindings/net/can/m_can.txt
修改设备树文件:
arch/arm/boot/dts/stm32mp157a-fsmp1a-extended.dts
由于
can1
相关内容在
stm32mp151.dtsi
中已完成定义,这里需要在原有基础添加与硬件对应的相关信息,在文件 stm32mp157a-fsmp1a-extended.dts
末尾集成并添加
uart5
相关内容:
&m_can1 {pinctrl-names = "default", "sleep";pinctrl-0 = <&m_can1_pins_a>;pinctrl-1 = <&m_can1_sleep_pins_a>;status = "okay";can-transmitter {max-bitrate = <5000000>;};};
关于
CAN1
管脚的配置,在是
stm32mp157-pinctrl.dtsi
中已经定义,且
IO
管脚使用情况与我们的设备一致,这里就不再重新定义直接使用即可。
3) 重新编译内核和设备树文件
linux@ubuntu:$ make -j4 uImage dtbs LOADADDR=0xC2000040
4) 更新系统内核和设备树
5) 测试
在设备的终端上执行
ifconfig -a
可以发现多出一个
can0
的设备,即表示移植成功。
root@fsmp1a:~# ifconfig -acan0 Link encap:UNSPEC HWaddr 00-00-00-00-0-00-00-00NOARP MTU:16 Metric:1RX packets:0 errors:0 dropped:0 overruns:0 frame:0TX packets:0 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:10RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)Interrupt:35eth0 Link encap:Ethernet HWaddr 00:80:E1:42:60:17inet addr:192.168.11.81 Bcast:192.168.11.255 Mask:255.255.255.0inet6 addr: fe80::280:e1ff:fe42:6017/64 Scope:LinkUP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1RX packets:101 errors:0 dropped:22 overruns:0 frame:0TX packets:58 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000RX bytes:13771 (13.4 KiB) TX bytes:10400 (10.1 KiB)Interrupt:65……
6) 编写测试程序测试
发送端程序:
can_send.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> int main(int argc, char *argv[]) { int s, nbytes, i; char *device = "can0"; int id = 0x22 ; struct sockaddr_can addr; struct ifreq ifr; struct can_frame frame[1]; if (argc < 2) { printf("Usage: ./cansend [data]\n"); return 1; } /* create a socket */ s = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (s < 0) { perror("socket"); } 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)); /* Set the filter rules */ setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); /* generate CAN frames */ frame[0].can_id = id; frame[0].can_dlc = strlen(argv[1]); for(i = 0; i < frame[0].can_dlc; i++) { frame[0].data[i] = argv[1][i]; } /* send CAN frames */ while(1) { nbytes = write(s, &frame[0], sizeof(frame[0])); if (nbytes < 0) { perror("can raw socket write"); return 1; } /* paranoid check ... */ if (nbytes < sizeof(struct can_frame)) { fprintf(stderr, "read: incomplete CAN frame\n"); return 1; } usleep(1000000); } close(s); return 0; }
接收端程序:
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> int main() { int s, nbytes, i; char *device="can0"; int id = 0x22; struct sockaddr_can addr; struct ifreq ifr; struct can_frame frame; struct can_filter rfilter[1]; /* 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)); /* 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("%c", frame.data[i]); printf("\n"); } } close(s); return 0; }
can
总线测试需要外接设备,比如两个
FS-MP1A
通过
can
连接,两端分别执行:
接收端:
root@fsmpa:~# ./can_receiveHello