扩展板 CAN 总线驱动移植

文章详细介绍了如何在STM32MP1平台上配置和测试CAN总线。首先,解释了CAN总线的硬件基础,包括SN65HVD230收发器的使用。接着,讲述了内核配置步骤,启用CAN驱动,并修改设备树以匹配硬件。然后,通过重新编译内核和设备树,更新系统。最后,提供了发送和接收CAN帧的测试程序,以及如何验证CAN通信的正确性。
摘要由CSDN通过智能技术生成

1.实验原理

打开扩展板原理图对照扩展板可以看到扩展板上 CAN 总线相关电路如下图:

图中 SN65HVD230 是德州仪器公司生产的 3.3V CAN 收发器,该器件适用于较高通讯速率、良好抗干扰能力和高可靠性 CAN 总线的串行通信。 SN65HVD230 RXD RXD 分别接的是 STM32MP1 CAN1_TX CAN1_RX

由于 STM32MP157 有多个 IO 具备 CAN1_RX CAN1_TX 的功能,需由电路确认电路 中使用的是哪个 IO,由上图最终确认 CAN1_RX 使用的是 PI9CAN1_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 -a
can0 Link encap:UNSPEC HWaddr 00-00-00-00-0-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:35
eth0 Link encap:Ethernet HWaddr 00:80:E1:42:60:17
inet addr:192.168.11.81 Bcast:192.168.11.255 Mask:255.255.255.0
inet6 addr: fe80::280:e1ff:fe42:6017/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:101 errors:0 dropped:22 overruns:0 frame:0
TX packets:58 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX 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_receive
Hello
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小徐的记事本

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值