can通信广泛应用在车载各个设备的通信上,本文主要介绍一种场景下的can通信开发:Android设备如何通过Can网卡来与总线中的其他设备进行通信
代码如下:
#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,每一种的字节排序都不一样,网上有很多关于这三种排序类型的解析,这里就不再赘述。