上一篇《NVIDIA TX2 CAN端口的使用(一)》介绍了如何在TX2平台上添加CAN相关的模块,本篇将介绍如何进行CAN编码。在linux驱动中,can是以网口的形式进行处理,因此完全可以使用linux下的五种编程模型。以socket为例,常用的IO模型是select、poll、epoll,因本人最近刚学习完epoll(epoll是真心强大,并且好用),所以就直接用了epoll来实现can的应用驱动。
epoll主要包含三个接口函数(相比于select和poll确实精简了不少):
1. int epoll_create(int size):epoll 描述符创建函数;
2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event):epoll时间注册函数;
3. int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout): epoll时间等待函数;
鉴于篇幅,这里对epoll不做详细介绍,如果需要学习请移步至《linux高效率编程:epoll和多线程 》。
按照我自己的使用习惯,通常将外设设计成初始化、打开、接收、发送、 关闭五大接口。这里考虑到CAN协议的多适配和可拓展性,将数据接收设计成回调函数的形式,以便适配各自的数据协议。具体接口如下:
1. int can_init(can_device_t* device, char *can_name,callback_fn rec_cb);
2. void can_open(can_device_t* candevice);
3. void can_close(can_device_t* candevice);
4. int can_transimit(can_device_t *candevice, can_obj_t *obj_buff, int obj_count);
参数 简单说明:
can_device_t:can设备结构体,
char *can_name: can口名称,在TX2下有两个,can0和can1;
callback_fn rec_cb: 这个就是接收数据的回调函数,将接口暴露出来,方便拓展;
can_obj_t *obj_buff:需要发送的can报文,发送本来想设计成多帧报文发送,实际项目用不上,也就没有再去实现;
还有一点需要说明,如果使用epoll模型,接收和发送都可以以事件的形式添加到epoll事件集,让epoll实现自动发送;但我这里考虑到发送时机的灵活性,并未将发送事件添加到epoll,也就是说epoll只是实现了can数据的接收(一般情况下也就足够了,目前这种设计模式完全可以应用在其他外设,比如串口、I2C、SPI等);
代码大放送时间:
-----------------------------------------main.c-------------------------------
/**
******************************************************************************
* Copyright(c) 2018-2023Vincent All rights reserved.
* - Filename
* - Author Vincent
* - Version V1.0.0
* - Date 2019/1/5
* - Brief
* - FunctionList:
******************************************************************************
* History:
*
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "can_interface.h"
#include "dbg.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
/* prama define discrible -----------------------------------------------------*/
/* strcuct discrible --------------------------------------------------------*/
/* prama discrible ---------------------------------------------------------*/
/* private fucntion declaration --------------------------------------------*/
void canrecive(int can_id, uint8_t *buf, int buf_len)
{
int i;
printf("ID: 0x%04X,LEN: %02d\t", can_id, buf_len);
for (i = 0; i < buf_len; i++)
{
printf("%02X ", *(buf + i));
}
printf("\r\n");
}
/*--------------------------------------------------------------------------
- @Name:
- @Discrible:
- @Param:
- @Return:
- @Others:
--------------------------------------------------------------------------
- @Recode date version author modify
- ------------------------------------------------------------------
- 20190130 V1.0 VINCENT Create
-
--------------------------------------------------------------------------*/
int main()
{
int ret ;
can_device_t vcu_can;
can_obj_t send_obj;
uint8_t i = 0;
ret = can_init(&vcu_can, "can0", canrecive);
if(ret < 0 )
{
printf(">> can device init error!\r\n");
return 0;
}
can_open(&vcu_can);
while (1)
{
sleep(2);
send_obj.id = i++;
strcpy(send_obj.data_buf, "HELLO");
send_obj.data_len = strlen("HELLO");
printf("send: ID: 0x%04X len: %02d\r\n", send_obj.id, send_obj.data_len);
can_transimit(&vcu_can, &send_obj, 1);
}
can_close(&vcu_can);
return 0;
}
/************************ (.c) END OF FILE ************************************/
-----------------------------------------can_interface.H-------------------------------
/**
******************************************************************************
* Copyright(c) 2018-2023Vincent All rights reserved.
* - Filename:
* - Author: Vincent
* - Version: V1.0.0
* - Date: 2019/1/5
* - Brief:
* - FunctionList:
******************************************************************************
* History:
*
*
*
******************************************************************************
*/
#ifndef __CAN_INTERFACE_H__
#define __CAN_INTERFACE_H__
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdlib.h>
/* prama define discrible -----------------------------------------------------*/
#define CAN_RX_MSG_BUF ((int)128)
#define CAN_TX_MSG_BUF ((int)128)
#define CAN_MSG_LEN ((int)8)
/* strcuct discrible --------------------------------------------------------*/
typedef void (*callback_fn)(int,uint8_t*,int);
/* can 消息结构体*/
typedef struct
{
uint16_t id;
uint16_t time_stamp;
uint8_t time_flag;
uint8_t send_type;
uint8_t remote_type;
uint8_t extern_flag;
uint8_t data_len;
uint8_t data_buf[CAN_MSG_LEN];
uint8_t reserved[3];
}can_obj_t;
/* 设备结构体*/
typedef struct
{
int sock_fd;
int epoll_fd;
callback_fn recive_callback;
}can_device_t;
/* prama discrible ---------------------------------------------------------*/
/* private fucntion declaration --------------------------------------------*/
/* private fucntion discrible -----------------------------------------------*/
int can_init(can_device_t* device, char *can_name,callback_fn rec_cb);
void can_open(can_device_t* candevice);
void can_close(can_device_t* candevice);
int can_transimit(can_device_t *candevice, can_obj_t *obj_buff, int obj_count)
#endif /*__CAN_DRIVER_H__*/
/************************ (.h) END OF FILE ************************************/
-----------------------------------------can_interface.c-------------------------------
/**
******************************************************************************
* Copyright(c) 2018-2023Vincent All rights reserved.
* - Filename
* - Author Vincent
* - Version V1.0.0
* - Date 2019/1/5
* - Brief
* - FunctionList:
******************************************************************************
* History:
*
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <sys/epoll.h>
#include <unistd.h>
#include "dbg.h"
#include "can_interface.h"
/* prama define discrible -----------------------------------------------------*/
#define EPOLL_SIZE 1024
/* strcuct discrible --------------------------------------------------------*/
/* prama discrible ---------------------------------------------------------*/
pthread_t can_thread = -1;
static pthread_mutex_t recive_lock;
/* private fucntion declaration --------------------------------------------*/
/* private fucntion discrible -----------------------------------------------*/
void *can_recive_thread(void *param);
int can_port_init(char *can_name);
int add_epoll_fd(int sock_fd);
/*--------------------------------------------------------------------------
- @Name:
- @Discrible:
- @Param:
- @Return:
- @Others:
--------------------------------------------------------------------------
- @Recode date version author modify send_frame
- ------------------------------------------------------------------
- 20190130 V1.0 VINCENT Create
-
--------------------------------------------------------------------------*/
int can_init(can_device_t *device, char *can_name, callback_fn rec_cb)
{
/*bind can 端口*/
device->sock_fd = can_port_init(can_name);
if (device->sock_fd < 0)
{
printf(">>: can socket create failed!\r\n");
return -1;
}
/*开启epoll事件监听*/
device ->epoll_fd = add_epoll_fd(device->sock_fd);
if (device->epoll_fd < 0)
{
close(device->sock_fd);
printf(">>: can socket epoll create failed!\r\n");
return -1;
}
/**注册接收回调函数*/
if (rec_cb)
{
device->recive_callback = rec_cb;
}
printf(">>: can init success, sock_fd: %d, epoll_fd: %d !\r\n", device->sock_fd, device->epoll_fd);
return 1;
}
/*--------------------------------------------------------------------------
- @Name:
- @Discrible:
- @Param:
- @Return:
- @Others:
--------------------------------------------------------------------------
- @Recode date version author modify send_frame
- ------------------------------------------------------------------
- 20190130 V1.0 VINCENT Create
-
--------------------------------------------------------------------------*/
void can_open(can_device_t *candevice)
{
/*创建接收进程*/
pthread_mutex_init(&recive_lock, NULL);
pthread_create(&can_thread, NULL, can_recive_thread, candevice);
printf(">>: epoll thread fd: %d\r\n", (int)can_thread);
}
/*--------------------------------------------------------------------------
- @Name:
- @Discrible:
- @Param:
- @Return:
- @Others:
--------------------------------------------------------------------------
- @Recode date version author modify send_frame
- ------------------------------------------------------------------
- 20190130 V1.0 VINCENT Create
-
--------------------------------------------------------------------------*/
void can_close(can_device_t *candevice)
{
if (can_thread > 0)
{
void *recycle;
/*回收线程资源 */
pthread_join(can_thread, &recycle);
pthread_mutex_destroy(&recive_lock);
printf(">>: epoll thread destroy!\r\n");
}
/*关闭epoll*/
if (candevice->epoll_fd > 0)
{
close(candevice->epoll_fd);
printf(">>: epoll close!\r\n");
}
/*关闭CAN外设*/
if (candevice->sock_fd > 0)
{
close(candevice->sock_fd);
printf(">>: can socket thread destroy!\r\n");
}
printf(">>: can device close!\r\n");
}
/*--------------------------------------------------------------------------
- @Name:
- @Discrible:
- @Param:
- @Return:
- @Others:
--------------------------------------------------------------------------
- @Recode date version author modify send_frame
- ------------------------------------------------------------------
- 20190130 V1.0 VINCENT Create
-
--------------------------------------------------------------------------*/
int can_transimit(can_device_t *candevice, can_obj_t *obj_buff, int obj_count)
{
int tran_count_ret = 0;
struct can_frame tx_frame;
bzero(&tx_frame, sizeof(tx_frame));
tx_frame.can_id = obj_buff->id;
tx_frame.can_dlc = obj_buff->data_len;
memcpy(tx_frame.data, obj_buff->data_buf, obj_buff->data_len);
tran_count_ret = write(candevice->sock_fd, &tx_frame, sizeof(tx_frame));
if (tran_count_ret != sizeof(tx_frame))
{
printf(">>: transimit failed!\r\n");
}
return tran_count_ret;
}
/*--------------------------------------------------------------------------
- @Name:
- @Discrible:
- @Param:
- @Return:
- @Others:
--------------------------------------------------------------------------
- @Recode date version author modify send_frame
- ------------------------------------------------------------------
- 20190130 V1.0 VINCENT Create
-
--------------------------------------------------------------------------*/
int can_recive(can_device_t *candevice, can_obj_t *obj_buff, int obj_count)
{
int recive_count_ret = 0;
pthread_mutex_lock(&recive_lock);
pthread_mutex_unlock(&recive_lock);
return recive_count_ret;
}
/*--------------------------------------------------------------------------
- @Name:
- @Discrible:
- @Param:
- @Return:
- @Others:
--------------------------------------------------------------------------
- @Recode date version author modify send_frame
- ------------------------------------------------------------------
- 20190130 V1.0 VINCENT Create
-
--------------------------------------------------------------------------*/
int can_port_init(char *can_name)
{
int can_fd;
struct ifreq ifr;
struct sockaddr_can addr;
/*创建套接字*/
can_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (can_fd < 0)
{
return -1;
}
/*指定 can 设备*/
strcpy(ifr.ifr_name, can_name);
ioctl(can_fd, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
/*关闭回环模式*/
int loopback = 0; /* 0 = disabled, 1 = enabled (default) */
setsockopt(can_fd, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));
/*关闭自收自发*/
int recv_own_msgs = 0; /* 0 = disabled (default), 1 = enabled */
setsockopt(can_fd, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &recv_own_msgs, sizeof(recv_own_msgs));
/*将套接字与 can0 绑定*/
bind(can_fd, (struct sockaddr *)&addr, sizeof(addr));
return can_fd;
}
/*--------------------------------------------------------------------------
- @Name:
- @Discrible:
- @Param:
- @Return:
- @Others:
--------------------------------------------------------------------------
- @Recode date version author modify send_frame
- ------------------------------------------------------------------
- 20190130 V1.0 VINCENT Create
-
--------------------------------------------------------------------------*/
int add_epoll_fd(int sock_fd)
{
int epfd;
struct epoll_event event;
/*创建epoll模型*/
epfd = epoll_create(EPOLL_SIZE);
if (epfd < 0)
{
return -1;
}
/*监听sock_fd 的可读事件*/
event.data.fd = sock_fd;
event.events = EPOLLIN;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, sock_fd, &event) < 0)
{
return -1;
}
return epfd;
}
/******************************************************************************
* @Function:
* @Discrible:
* @Param:
* @Return:
* @Others:
******************************************************************************
* @Recode date version author modify
* ------------------------------------------------------------------
* 20190130 V1.0 VINCENT Create
*
*******************************************************************************/
void *can_recive_thread(void *param)
{
int i, nfds;
int timeout = 2;
uint64_t nbytes;
struct can_frame rx_frame;
struct epoll_event events[EPOLL_SIZE];
can_device_t *can_device_temp = (can_device_t *)param;
while (1)
{
nfds = epoll_wait(can_device_temp->epoll_fd, events, EPOLL_SIZE, timeout);
if (nfds < 0)
{
// printf(">>: epoll wait error!\r\n");
}
for (int i = 0; i < nfds; i++)
{
if (events[i].events & EPOLLIN)
{
nbytes = read(events[i].data.fd, &rx_frame, sizeof(rx_frame));
if (nbytes > 0)
{
if (can_device_temp->recive_callback)
{
can_device_temp->recive_callback(rx_frame.can_id, rx_frame.data, rx_frame.can_dlc);
}
}
}
}
}
}
/************************ (.c) END OF FILE ************************************/