c语言 CAN多帧发送接收

#include <stdio.h>
#include <main.h>


/******************************************************************************************************************************
帧序号	 Data1	   		    Data2	     Data3					Data4	 			   Data5	  Data6	     Data7	     Data8
1	    当前报文帧序号	     包序号	    报文有效数据长度低字节	   报文有效数据长度高字节	有效数据01	有效数据02	有效数据03	有效数据04
2	    当前报文帧序号	    有效数据05	有效数据06	              有效数据07	          有效数据08	...	      ...	     ...
...	...	...	...	...	...	...	...	...
...	...	...	...       	  有效数据N	  校验码低字节	            校验码高字节	           00H	      00H
******************************************************************************************************************************/


/*
函数说明: 累加和校验
@param 1: uint8_t *recv_buf 需要校验的数据
@param 2: uint16_t buff_len 数据长度
返回值: uint16_t 2个字节的校验码
*/
static uint16_t u16_check_sum(uint8_t *recv_buf, uint16_t buff_len)
{
    uint16_t i, ret = 0;

    for (i = 0; i < buff_len; i++) {
        ret += *(recv_buf++);
    }
    return ret;
}


//多包数据
typedef struct
{
    uint8_t  recv_data[7];  // 数据
    uint8_t  recv_flag;     // 包标记,0:未接收或未发送,1:己接收或己发送
} packet_t;

/* can应用层数据结构体定义,整个数据包 */
typedef struct
{
    uint8_t   total_frames;  // 报文总帧数
    uint16_t  data_len;      // 有效数字长度
    packet_t  recv_packet[TCU_PACKET_MAX_NUM];
} can_mult_pack_t;



/*
函数说明: 多包CAN 接收数据
@param 1: can_mult_pack_t *p_msg  多包的缓存区
@param 2: uint8_t *pdata          CAN的8个字节数据
@param 3: uint8_t *pbuff          有效数据的存储地址
返回值:   0:接收失败   大于0:接收成功
*/
int mult_pack_read_can_data(can_mult_pack_t *p_msg, uint8_t *pdata, uint8_t *pbuff) 
{
    uint16_t i = 0;
    uint16_t sum_check_code = 0;
    uint16_t data_len; //有效数据长度
    uint8_t recv_buff[1300] = {0}; //临时缓冲区
    if ((pdata == NULL) || (p_msg == NULL)) {
        return 0;
    }
    if (((pdata[0]-1) >= TCU_PACKET_MAX_NUM) || ((pdata[0]-1) < 0)) {
        return 0;
    }
    p_msg->recv_packet[pdata[0]-1].recv_flag = true;
    memcpy(p_msg->recv_packet[pdata[0]-1].recv_data, &pdata[1], 7);
    /*第一包数据*/
    if (pdata[0] == 1) {
        p_msg->total_frames = pdata[1]; //报文总帧数
        p_msg->data_len = (pdata[2] | (pdata[3] << 8)); //取有效长度

        if (p_msg->data_len > (TCU_PACKET_MAX_NUM * 7)) {
            memset(p_msg, 0, sizeof(can_mult_pack_t));
            return 0; //长度非法
        }

        for (i = 0; i < p_msg->total_frames-1; i++) {
            p_msg->recv_packet[i+1].recv_flag = false; //保证除了第一包数据标志,都为0
            return 0;
        }
    }

    for (i = 0; i < p_msg->total_frames; i++) {
        if (p_msg->recv_packet[i].recv_flag == false) {
            return 0; //没收到完整数据,继续接收
        }
    }

    /*接收到完整数据*/
    memset(recv_buff, 0, sizeof(recv_buff));
    for (i = 0; i < p_msg->data_len+MULT_PACK_OTHER_LEN; i++) {
        recv_buff[i] = p_msg->recv_packet[i/7].recv_data[i%7]; //取出数据放在临时缓冲区里
    }

    //取出校验码
    sum_check_code = recv_buff[3+p_msg->data_len] | (recv_buff[3+p_msg->data_len+1] << 8); 
    /*报文总帧数到有效数据,进行校验*/
    if (u16_check_sum(recv_buff, p_msg->data_len+3)  != sum_check_code) {
        //校验码不对,数据错误
        memset(p_msg, 0, sizeof(can_mult_pack_t));
        printf("校验没通过\n"); //
        return 0;
    }
    
    printf("接收完整\n"); //
    data_len = p_msg->data_len;
    memcpy(pbuff, &recv_buff[3], p_msg->data_len); //取出有效数据
    memset(p_msg, 0, sizeof(can_mult_pack_t)); //清空CAN多包缓存区
    

    return data_len;
}





/******************************************************************/
/*
函数说明: 多包 CAN 发送数据
@param 1: int can_fd 文件描述符
@param 2: uint8_t dest_addr 目的地址
@param 3: uint8_t *pdata 发送数据
@param 4: uint16_t data_len 数据长度
返回值:   false:失败   true:成功
*/
bool mult_pack_write_can_data(int can_fd, uint32_t can_id, uint8_t *pdata, uint16_t data_size)
{
    uint16_t  frame_count = 0;    //多包帧数
    uint16_t  total_size = 0;     //多包总长度,除了序号
    uint16_t  write_size = 0;     //已写入的大小
    uint16_t  sum_check_code = 0; //累计和校验码
    uint16_t  i = 0;
    uint8_t   send_data[1300] = {0}; //发送缓冲区

    can_msg_t tx_msg;

    if (pdata == NULL) {
        return false;
    }
    
    total_size = data_size + MULT_PACK_OTHER_LEN; 
    frame_count = FRAME_COUNT(total_size);

    //将数据组好,放在缓冲区
    send_data[0] = frame_count; //总帧数
    send_data[1] = data_size & 0xFF; //有效长度低字节
    send_data[2] = data_size >> 8;   //有效长度高字节
    memcpy(&send_data[3], pdata, data_size); //有效数据
    sum_check_code = u16_check_sum(send_data, data_size+3); //获取累计和校验码,有效数据+报文总帧数+有效长度低+有效长度高
    send_data[3+data_size] = sum_check_code; //校验低字节
    send_data[3+data_size+1] = sum_check_code >> 8; //校验高字节
    /*CAN数据*/
    memset(&tx_msg, 0, sizeof(tx_msg));
    tx_msg.id.can_id = can_id;
    tx_msg.len = CAN_SEND_LEN;
    tx_msg.ch = 0;
    tx_msg.format = 1;
    tx_msg.type = 0;
    for (i = 0; i < frame_count; i++) {
        memset(&tx_msg.data[1], 0, 7);
        tx_msg.data[0]++; //序号
        /*总长度减去已经发送的长度,剩余长度,大于或等于7*/
        if ((total_size-write_size) >= 7) {
            memcpy(&tx_msg.data[1], &send_data[write_size], 7);
        } else {
            /*最后一包,剩多少没发*/
            memcpy(&tx_msg.data[1], &send_data[write_size], total_size-write_size);
        }
        write_size += 7;
        
        single_pack_write_can_data(can_fd, &tx_msg); //数据发送
        usleep(5000); //防止长时间占用CPU
    }
}

仅供参考。。。。。。。

 

 

 

### CAN通信中远程发送接收 在控制器局域网(CAN)协议中,远程用于请求特定数据而不携带数据有效载荷。当节点需要另一节点的数据时会发送这种类型的。 下面展示的是如何通过SocketCAN接口来实现Linux环境下的C语言程序,该程序可以发送并监听远程: #### 发送远程示例代码 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <net/if.h> #include <sys/ioctl.h> #include <linux/can.h> #include <linux/can/raw.h> int main(void){ int s; struct ifreq ifr; struct sockaddr_can addr; /* 创建socket */ if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { perror("Error while opening socket"); return -1; } strcpy(ifr.ifr_name, "can0" ); ioctl(s, SIOCGIFINDEX, &ifr); memset(&addr, 0, sizeof(addr)); addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; bind(s, (struct sockaddr *)&addr, sizeof(addr)); can_frame frame; // 设置为远程 frame.can_id = 0x123; // 使用标准ID格式设置目标地址 frame.can_dlc = 8; // 远程中的DLC字段应设为期望接收到的数据长度 frame.rtr = 1; // 将RTR位置高表示这是一个远程传输请求 write(s, &frame , sizeof(struct can_frame)); close(s); } ``` 这段代码创建了一个套接字连接到指定名称`can0`的CAN网络接口上,并向其写入了一条带有预定义标识符的标准格式远程[^1]。 #### 接收远程响应示例代码 为了处理来自其他设备对于上述发出的远程所作出的回答,在同一进程中可以通过读取相同的套接字来进行操作: ```c // 继续上面的例子... while(1){ struct can_frame frame_rd; socklen_t len = sizeof(addr); // 阻塞等待直到有消息到达 read(s, &frame_rd, sizeof(struct can_frame)); printf("Received Frame ID=%X DLC=%d\n", frame_rd.can_id, frame_rd.can_dlc); if(frame_rd.can_id == 0x123 && !frame_rd.rtr){ // 如果是针对我们之前发送出去的那个远程的有效回应,则打印出来 for(int i=0;i<frame_rd.can_dlc;i++) printf("%02X ", frame_rd.data[i]); puts(""); } } close(s); return 0; ``` 此部分展示了怎样持续监控传入的消息流,并过滤出那些匹配先前发起查询条件的数据。一旦发现符合条件的信息包就会将其内容显示给用户查看。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值