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
    }
}

仅供参考。。。。。。。

 

  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在C语言中实现CAN通讯,您需要使用CAN控制器和CAN收发器。这些硬件组件负责实现CAN通讯的物理层和数据链路层。您还需要安装CAN驱动程序和CAN API库。 以下是一些实现CAN通讯的基本步骤: 1. 初始化CAN控制器和CAN收发器。 2. 配置CAN通讯参数,例如波特率和格式。 3. 发送CAN。 4. 接收CAN并解析数据。 以下是一个简单的示例代码,演示如何在C语言中使用SocketCAN接口实现CAN通讯: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <net/if.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <linux/can.h> #include <linux/can/raw.h> int main(void) { int s, nbytes; struct sockaddr_can addr; struct ifreq ifr; struct can_frame frame; const char *ifname = "can0"; /* 创建CAN socket */ s = socket(PF_CAN, SOCK_RAW, CAN_RAW); /* 获取CAN接口索引 */ strcpy(ifr.ifr_name, ifname); ioctl(s, SIOCGIFINDEX, &ifr); /* 绑定CAN socket到CAN接口 */ addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; bind(s, (struct sockaddr *)&addr, sizeof(addr)); /* 配置CAN */ frame.can_id = 0x123; frame.can_dlc = 2; frame.data[0] = 0x11; frame.data[1] = 0x22; /* 发送CAN */ nbytes = write(s, &frame, sizeof(frame)); /* 接收CAN */ nbytes = read(s, &frame, sizeof(frame)); /* 解析CAN数据 */ printf("CAN ID = 0x%X\n", frame.can_id); printf("CAN DLC = %d\n", frame.can_dlc); printf("CAN Data = "); for (int i = 0; i < frame.can_dlc; i++) { printf("%02X ", frame.data[i]); } printf("\n"); close(s); return 0; } ``` 这个示例代码使用了Linux下的SocketCAN接口,通过CAN_RAW套接字实现CAN通讯。您可以根据自己的需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值