Android实现CAN通信

2,916 阅读3分钟

我们了解了CAN通信协议的基本原理后,让后就可以去考虑,Android代码怎么实现CAN通信了,如果不了解,请移步

车载Android开发的秘密--搞懂CAN通信 Android代码的实现,其实相对于来说还是很简单的。因为Linux的SocketCAN 我们可以直接拿来用了,我们这里就不分析Linux的源码了。。就分析Android显示层,到NDK调用Linux的SocketCan,如果有想要深入了解的同学,可以去下载NDK源码,去看一看,或者去下载SocketCAN的源码。话不多说进入正题。 这篇文章是基于3568的芯片,解析的。

1、打开CAN,设置波特率

打开3568的CAN口,我们是使用的,ProcessBuilder,ProcessBuilder是Java中的一个类,是用于管理外部进程的一个工具类,可以用来执行Shell命令。

    ProcessBuilder processBuilder = new ProcessBuilder("su");
    processBuilder.redirectErrorStream(true);
    Process process = processBuilder.start();
    process.getOutputStream().write(" ip link set can0 down\n".getBytes());
    process.getOutputStream().write("ip link set can0 type can bitrate 500000 dbitrate 500000 fd on\n".getBytes());
    process.getOutputStream().write(" ip link set can0 up\n".getBytes());
    process.getOutputStream().flush();

su的权限,是root权限,如果没有root权限,是用不了的,所以你的开发板先root。
无论CAN口是什么状态,先关闭,在开启,先down 后up。

2、openSocketCan,去打开Socket

因为是Linux的SocketCan,所以我们用NDK去打开。 domain:协议族(Protocol Family)。PF_CAN 表示使用 CAN 协议族。 type:套接字类型。SOCK_RAW 表示创建一个原始套接字,可以直接访问底层网络协议。 protocol:协议类型。CAN_RAW 表示使用 CAN 的原始协议

    int fd;
    struct ifreq ifr;
    struct sockaddr_can addr;

    /* open socket */
    if ((fd = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
        return -1;
    }

    const char *str = env->GetStringUTFChars(canx, 0);
    strcpy(ifr.ifr_name, str);
    ioctl(fd, SIOCGIFINDEX, &ifr);

    memset(&addr, 0, sizeof(addr));
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;

    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        return -2;
    }

    return fd;

3、打开了Socket 就可以去读取和发送数据了,同样,我们使用NDK去操作

使用Socket 去读数据

    jlongArray ret;
    ret = env->NewLongArray(12);
    struct can_frame frame;
    read(fd, &frame, sizeof(struct can_frame));
    int64_t data[12];
    data[0] = frame.can_id & 0x1FFFFFFF;
    data[1] = (frame.can_id >> 31) & 0x1;
    data[2] = (frame.can_id >> 30) & 0x1;
    data[3] = frame.can_dlc;
    for(uint8_t i = 0; i < frame.can_dlc; i++) {
        data[i + 4] = frame.data[i];
    }
    env->SetLongArrayRegion(ret, 0, 12, data);
    return ret;

使用Socket 去写数据

    struct can_frame frame;
    frame.can_id = (eff << 31) | (rtr << 30) | canid;
    frame.can_dlc = len;
    jint *pdata = env->GetIntArrayElements(data, 0);
    for(uint8_t i = 0; i < len; i++) {
        frame.data[i] = pdata[i] & 0xFF;
    }
    int ret =  write(fd, &frame, sizeof(struct can_frame));
    env->ReleaseIntArrayElements(data, pdata, 0);
    return  ret;

这样看起来,还是蛮简单的。

4、NDK里关于CAN是直接封装了的,如果给写好了,那我们直接拿去用就好了,,哈哈

can.h在 ndk 下边的linux 的根目录,然后linux目录下边还有个can目录。

image.png

相比于串口来说,我们中间多了一个,Socket层。串口是直接读取文件句柄,读取缓冲区,就可以了。CAN通信的实现,也不用我们去使用第三方的框架, 我们直接使用NDK里的工具进行开发即可。

5、总结

CAN通信Android的实现,就到这里了,希望对兄弟们有用。以后再也不用为CAN而困扰。还有可能就是对C和C++不太熟悉,不过没关系,多写写就好了。兄弟们加油。