基于rk3568方案的Android系统can bus通信开发

文章目录

  • 前言
  • 一、开发环境
  • 二、使用步骤
    • 定义报文交互类
    • 实现报文收发类
  • 总结


前言

can通信广泛应用在车载各个设备的通信上,本文主要介绍一种场景下的can通信开发:Android设备如何通过Can网卡来与总线中的其他设备进行通信


一、开发环境

  • 主板:rk3568,自带can网卡硬件,底层驱动支持can,can-fd通信
  • 系统:Android12

二、使用步骤

定义报文交互类

代码如下:

#ifndef _CANDEVICE_H
#define _CANDEVICE_H
#include "vector"
#include 
#include 

/**
 * Can设备封装类
 */
class CanDevice {
public:
    CanDevice() = default;
    ~CanDevice() = default;
    //打开can设备
    int openDevice(const char* device_name, const std::vector<uint32_t>& subscribe_ids);
    //发送can报文
    int sendCanFrame(const can_frame& _frame) const;
    //关闭can设备
    int closeDevice();
    using callback_t = std::function<void(const can_frame& can_frame_)>;
    //设置can报文数据监听器
    void setCanDataCallback(callback_t cb_);
    //获取回调接口
    callback_t getCallback() const{
        return callback;
    }

private:
    //每次下发2帧报文
    const uint8_t _SEND_CYCLE_SIZE = 2;
    static void threadLoop(const void* args);
    callback_t callback;
    struct sockaddr_can sock_ar{};
    int sock_fd = -1;
};
#endif //_CANDEVICE_H

实现报文收发类

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include "CanDevice.h"
#include "thread"
#include "EasyLog.h"
#include "Utility.h"

static std::mutex can_mutex;
int CanDevice::openDevice(const char *device_name, const std::vector<uint32_t>& subscribe_ids) {
    std::unique_lock<std::mutex> can_guard(can_mutex);
    if(sock_fd != -1){
        ALOGE("The can can_device[%s] already opened", device_name);
        return -1;
    }
    ALOGD("%s %s", __func__ , device_name);
    struct ifreq ifr{};

    //创建套接字
    sock_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if(sock_fd < 0) {
        ALOGE("open devices failed for send sock");
        return -1;
    }

    strcpy(ifr.ifr_name, device_name);
    if(ioctl(sock_fd, SIOCGIFINDEX, &ifr) < 0){
        ALOGE("The Can [%s] ioctl error", device_name);
        sock_fd = -1;
        return -1;
    }

	//是否启动回环测试,为1,我们发送的时候,马上自己就能收到
    int loopback = 0; // 0 表示关闭, 1 表示开启( 默认)
    if(setsockopt(sock_fd, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback)) != 0){
        close(sock_fd);
        sock_fd = -1;
        return -1;
    }

    sock_ar.can_family = AF_CAN;
    sock_ar.can_ifindex = ifr.ifr_ifindex;
    if(bind(sock_fd, (struct sockaddr *)&sock_ar, sizeof(sock_ar)) < 0) {
        ALOGE("The can can_device[%s] bind failed", device_name);
        close(sock_fd);
        sock_fd = -1;
        return - 1;
    }

    struct timeval timeout = {0,20}; //20ms
    if(setsockopt(sock_fd, SOL_SOCKET,SO_SNDTIMEO, (char *)&timeout, sizeof(struct timeval)) != 0){
        close(sock_fd);
        sock_fd = -1;
        return -1;
    }

    if(setsockopt(sock_fd, SOL_SOCKET,SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval)) != 0) {
        close(sock_fd);
        sock_fd = -1;
        return -1;
    }
    can_guard.unlock();

    std::thread can_read_thread(threadLoop, this);
    can_read_thread.detach();
    ALOGD("The device open success");
    return 0;
}

int CanDevice::closeDevice() {
    ALOGI("%s", __func__ );
    std::unique_lock<std::mutex> can_guard(can_mutex);
    if(sock_fd > 0){
        close(sock_fd);
        sock_fd = -1;
        ALOGD("CanDevice Close done...");
    }
    return 0;
}

void CanDevice::setCanDataCallback(CanDevice::callback_t cb_) {
    callback = std::move(cb_);
}

void CanDevice::threadLoop(const void* args) {
    if(!args) {
        ALOGE("%s exit: CanDevice could not be nullptr", __func__);
        return;
    }
    auto dev = (const CanDevice*)args;
    ssize_t _len;
    while(dev->sock_fd > 0){
        //接收报文
        struct can_frame frame{};
        _len = recv(dev->sock_fd, &frame, sizeof(frame), 0);
        if(_len > 0){
            ALOGD("ID=0x%X DLC=%d data[0]=0x%X", frame.can_id, frame.can_dlc, frame.data[0]);
            dev->getCallback()(frame);
        }
    }
    ALOGI("threadLoop exit...");
}

int CanDevice::sendCanFrame(const can_frame &_frame) const {
    ALOGI("send::id = %02x, content = %s",_frame.can_id, Utility::UCharToString(_frame.data, _frame.can_dlc).c_str());
    uint8_t send_times = _SEND_CYCLE_SIZE;
    ssize_t send_bytes;

    while(send_times -- > 0 && sock_fd > 0){
        send_bytes = sendto(sock_fd, &_frame, sizeof(struct can_frame), 0, (struct sockaddr*)&sock_ar, sizeof(sock_ar));
        usleep(10 * 1000);
        if(send_bytes <= 0){
            ALOGE("Send can_frame id[%02x] error, send_bytes = [%zd] in send_times[%d]",_frame.can_id, send_bytes, send_times);
            FILE *fp = popen("su -c ip -detail link show can0 | grep BUS-OFF", "r");
            char buf[1024] = {0};
            memset(buf, 0, 1024);
            fread(buf, 1, 1024, fp);
            if(strstr(buf, "BUS-OFF")){
                ALOGE("CanBus in offload, restarting...");
                system("su -c ifconfig can0 down");
                system("su -c ifconfig can0 up");
            }
            pclose(fp);
        }
    }
    ALOGI("send:: done");
    return 0;
}

总结

rk3568提供了硬件以及底层驱动支持上层直接通过linux标准的can网卡读写接口去获取can总线数据,在通信启动前,可以通过 ifconfig 命令查看can0网卡是否已经被加载,否则通讯不通。数据解析需要注意协议格式中定义的字节顺序,字节顺序一共有三种类型,分别为Motorola_LSB、Motorola_MSB、INTEL,每一种的字节排序都不一样,网上有很多关于这三种排序类型的解析,这里就不再赘述。

你可能感兴趣的:(Android车联网,android,canbus,can通信,Android,can通信)