目标:使用链表实现 CAN 总线数据的分帧发送和分帧数据的接收,同时将接收到的多帧数据合并成一个完整的数据包。

使用场合:当一个CAN总线网络上有多个端口对同一个端口发送分帧数据,且来自不同端口的分帧数据穿插接收,因此需要将不同端口的数据分别进行合并,形成完整的数据包

下面的代码使用纯C实现,方便移植。

CAN扩展帧标识如下:

typedef union
{
  unsigned long extId;
  struct
  {
    unsigned long sesId : 8;
    unsigned long funId : 5;
    unsigned long srcId : 8;
    unsigned long desId : 8;
    unsigned long _null : 3;
  }atr;
}uextId_t;

八字节数据域,做如下处理:前两字节作为帧序号,后六字节为有效数据。

帧序号从零开始。帧序号为零时,表示信息帧,后面四字节保存数据总字节数;帧序号不为零时表示实际发送的数据帧数,每帧六字节数据。

实验环境:VS2012

实验过程:使用多个线程,每个线程将一个较大的数据包,按照帧格式拆成多帧数据,然后将帧数据全部保存到一个非常大的数组中,由于是多个线程 同时工作,所以对于每个数据包的帧来说都不是按顺序进入数组中的,而是多个数据包的帧穿插着存入数组,最后调用数据接收处理API函数,对数组中的帧一个个进行接收处理,最后输出帧合并后的数据包。

代码注释比较清晰,因此这里就不废话了,直接上代码。

驱动文件

CanDrv.c

#include <stdio.h>
#include <stdlib.h>
#include "CanDrv.h"

// Define NULL pointer value
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

// 需求:CAN端口可能会收到来自多个节点的分帧数据,在多个节点的分帧数据穿插接收进来后,根据帧标识将多个节点的分帧数据整理合并.
// 思路:建立一条单向链表,链表的一个节点代表CAN总线的一个端点的一个完整数据包,接收到一个分帧数据后将其存储在对应的链表节点中.

typedef union
{
 unsigned long extId;
 struct
 {
  unsigned long sesId : 8; // 会话ID,每发一个数据包,自增一次
  unsigned long funId : 5; // 功能码
  unsigned long srcId : 8; // 本地ID
  unsigned long desId : 8; // 目标ID
  unsigned long _null : 3; // 未使用bit
 }atr;
}uextId_t;
#pragma pack(push, 1)
typedef struct _sCanData_t
{
 unsigned char desId; // 目标ID
 unsigned char srcId; // 本地ID
 unsigned char funId; // 功能码
 unsigned char sesId; // 会话ID
 unsigned char txLen; // 发送字节数
 unsigned char Buf[8]; // 发送缓存区
}sCanData_t;
typedef struct _sCanRecvData_t
{
 unsigned char desId; // 目标ID
 unsigned char srcId; // 本地ID
 unsigned char funId; // 功能码
 unsigned char sesId; // 会话ID
 
 unsigned char *pBuf; // 接收缓存
 unsigned long rxLen; // 当前已经接收的字节数
 unsigned long rxTotalLen; // 需要接收的总字节数
}sCanRecvData_t;
typedef struct _sCanRecvList_t
{
 struct _sCanRecvData_t *pBuf;
 struct _sCanRecvList_t *pNext;
}sCanRecvList_t;
#pragma pack(pop)

static sCanRecvList_t *sCanRecvListHandle = NULL; // 链表句柄
// 动态分配内存
// 返回值:内存起始地址
static void *pMalloc(unsigned int size)
{
 return malloc(size);
}
// 释放动态内存
static void pFree(void *pmem)
{
 free(pmem);
}

// 搜索对应的CAN节点在链表中的位置
// 返回值:若相应的CAN节点存在,则返回CAN节点在链表中的节点地址,否则返回NULL
static sCanRecvList_t *CanNodeSearch(const sCanRecvList_t *pHeadNode, sCanData_t *sCanData)
{
 sCanRecvList_t *pNode = NULL;
 if(sCanData == NULL)
 {
  return NULL;
 }
 pNode = (sCanRecvList_t *)pHeadNode->pNext; // 头节点不存放数据,所以搜索CAN节点时从第一个节点开始
 while(pNode != NULL)
 {
  if(pNode->pBuf->srcId == sCanData->srcId && pNode->pBuf->funId == sCanData->funId && pNode->pBuf->sesId == sCanData->sesId)
  {
   return pNode;
  }
  pNode = pNode->pNext;
 }
 return NULL;
}
// 创建链表头节点,头节点不存放数据
// 返回值:创建成功则返回头节点地址,否则返回NULL
static sCanRecvList_t *ListCreate(void)
{
 sCanRecvList_t *head = NULL;
 head = (sCanRecvList_t *)pMalloc(sizeof(sCanRecvList_t));
 if(head == NULL)
 {
  return NULL;
 }
 head->pBuf = NULL;
 head->pNext = NULL;
 return head;
}
// 创建一个链表节点
// 返回值:创建成功返回节点地址,否则返回NULL
static sCanRecvList_t *ListNodeCreate(sCanData_t *sCanData)
{
 sCanRecvList_t *node = NULL;
 if(!sCanData)
 {
  return NULL; // 数据异常
 }
 if(sCanData->Buf[0] != 0x00 || sCanData->Buf[1] != 0x00)
 {
  return NULL; // 帧序号不为0,说明不是头帧
 }
 node = (sCanRecvList_t *)pMalloc(sizeof(sCanRecvList_t));
 if(node == NULL)
 {
  return NULL;
 }
 node->pNext = NULL;
 node->pBuf = (sCanRecvData_t *)pMalloc(sizeof(sCanRecvData_t));
 if(node->pBuf == NULL)
 {
  pFree(node);
  node = NULL;
  return NULL;
 }
 node->pBuf->rxLen = 0;
 node->pBuf->desId = sCanData->desId;
 node->pBuf->srcId = sCanData->srcId;
 node->pBuf->funId = sCanData->funId;
 node->pBuf->sesId = sCanData->sesId;
 node->pBuf->rxTotalLen = (node->pBuf->rxTotalLen << 8) + sCanData->Buf[5];
 node->pBuf->rxTotalLen = (node->pBuf->rxTotalLen << 8) + sCanData->Buf[4];
 node->pBuf->rxTotalLen = (node->pBuf->rxTotalLen << 8) + sCanData->Buf[3];
 node->pBuf->rxTotalLen = (node->pBuf->rxTotalLen << 8) + sCanData->Buf[2];
 node->pBuf->pBuf = (unsigned char *)pMalloc(node->pBuf->rxTotalLen);
 if(node->pBuf->pBuf == NULL)
 {
  pFree(node->pBuf);
  node->pBuf = NULL;
  pFree(node);
  node = NULL;
  return NULL;
 }
 return node;
}
// 查找链表中的指定节点。当指定节点参数为NULL时,表示搜索尾节点
// 返回值:链表尾节点地址,链表为空或者没有找到时返回NULL
static sCanRecvList_t *ListNodeSearch(const sCanRecvList_t *pHeadNode, const sCanRecvList_t *pSearchNode)
{
 sCanRecvList_t *pNode = (sCanRecvList_t *)pHeadNode;
 if(pNode == NULL)
 {
  return NULL;
 }
 if(pSearchNode == NULL)
 {
  while(pNode->pNext != NULL)
  {
   pNode = pNode->pNext; // 搜索尾节点
  }
 }
 else
 {
  while(pNode != pSearchNode)
  {
   pNode = pNode->pNext; // 搜索指定节点
   if(pNode == NULL)
   {
    return NULL;
   }
  }
 }
 return pNode;
}
// 在链表的末尾插入一个新节点
static void ListNodeInssert(const sCanRecvList_t *pHeadNode, sCanRecvList_t * const pNewNode)
{
 sCanRecvList_t *pNode = NULL;
 if(pHeadNode == NULL || pNewNode == NULL)
 {
  return;
 }
 pNode = ListNodeSearch(pHeadNode, NULL); // 搜索尾节点
 if(pNode != NULL)
 {
  pNode->pNext = pNewNode; // 在链表末尾插入一个新节点
  pNewNode->pNext = NULL;
 }
}
// 删除指定节点
static void ListNodeDelete(const sCanRecvList_t *pHeadNode, sCanRecvList_t *pDeleteNode)
{
 sCanRecvList_t *pLastNode = (sCanRecvList_t *)pHeadNode;
 if(pHeadNode == NULL || pDeleteNode == NULL)
 {
  return;
 }
 // 查找删除节点的上一个节点
 while(pLastNode->pNext != pDeleteNode)
 {
  pLastNode = pLastNode->pNext;
 }
 if(pLastNode != NULL)
 {
  // 删除节点
  pLastNode->pNext = pDeleteNode->pNext;
  // 释放内存,注意释放顺序
  pFree(pDeleteNode->pBuf->pBuf);
  pDeleteNode->pBuf->pBuf = NULL;
  pFree(pDeleteNode->pBuf);
  pDeleteNode->pBuf = NULL;
  pDeleteNode->pNext = NULL;
  pFree(pDeleteNode);
  pDeleteNode = NULL;
 }
}
// 接收 CAN 总线帧数据,并对分帧数据进行组包
// p:RxMsg 数据域数据指针(RxMsg.Data)
// len:有效字节数(RxMsg.DLC)
// extId:扩展帧ID(RxMsg.ExtId)
// 返回值:0=succ,1=data error,2=memory error
unsigned char CanRecvDataProcess(const void *p, unsigned char len, unsigned long extId)
{
 unsigned char i;
 uextId_t uextId;
 sCanData_t sCanData;
 sCanRecvList_t *pNode = NULL;
 static sCanRecvList_t *pHeadNode = NULL; // 创建一条双向链表
 unsigned char *pBuf = (unsigned char *)p;
 if(!p || len < 1)
 {
  return 1; // 数据异常
 }
 // 帧标识符格式转换
 uextId.extId = extId;
 sCanData.desId = uextId.atr.desId;
 sCanData.srcId = uextId.atr.srcId;
 sCanData.funId = uextId.atr.funId;
 sCanData.sesId = uextId.atr.sesId;
 sCanData.txLen = len;
 for(i = 0; i < sizeof(sCanData.Buf); i++)
 {
  sCanData.Buf[i] = pBuf[i];
 }
 // 检查链表是否存在,不存在就创建
 if(pHeadNode == NULL)
 {
  pHeadNode = ListCreate(); // 链表为空就创建链表
  if(pHeadNode == NULL)
  {
   return 2; // 链表创建失败的原因只有内存申请失败
  }
  sCanRecvListHandle = pHeadNode;
 }
 // 检查节点是否存在,不存在就创建
 pNode = CanNodeSearch(pHeadNode, &sCanData);
 if(pNode == NULL)
 {
  pNode = ListNodeCreate(&sCanData); // 创建一个新节点
  if(pNode == NULL)
  {
   return 2;
  }
  ListNodeInssert(pHeadNode, pNode); // 向链表中添加节点
 }
 else
 {
  // 帧数据合法性验证
  unsigned int index = sCanData.Buf[1];
  index = (index << 8) + sCanData.Buf[0];
  if((index - 1) * 6 != pNode->pBuf->rxLen)
  {
   return 0; // 帧序号不正确,直接丢弃
  }
  // 向链表节点中添加数据
  for(i = 0; i < sCanData.txLen - 2; i++)
  {
   pNode->pBuf->pBuf[pNode->pBuf->rxLen++] = sCanData.Buf[i + 2];
  }
  // 将数据通过回调函数传递给应用层
  if(pNode->pBuf->rxLen == pNode->pBuf->rxTotalLen)
  {
   CanRead(pNode->pBuf->pBuf, pNode->pBuf->rxLen);
   ListNodeDelete(sCanRecvListHandle, pNode);
  }
 }
 return 0;
}
 
//=========================================================================================================================================================
//                                                            发送区
//=========================================================================================================================================================
 
// 向 CAN 总线发送一帧数据
// 返回值:0=succ,1=data error,2=timeout
static unsigned char CanSendFrame(const void *p, unsigned char len)
{
 uextId_t uextId;
 sCanData_t *sCanData = (sCanData_t *)p;
 if(sCanData == NULL)
 {
  return 1;
 }
 // 帧标识符格式转换
 uextId.atr.desId = sCanData->desId;
 uextId.atr.srcId = sCanData->srcId;
 uextId.atr.funId = sCanData->funId;
 uextId.atr.sesId = sCanData->sesId;
 // 发送数据
 return CanWrite(sCanData->Buf, sCanData->txLen, uextId.extId);
}
// 向 CAN 总线发送数据
// desId:目标ID
// srcId:本地ID
// funId:功能码
// sesId:会话ID,每次发送前自增1
// p:发送数据指针
// len:发送字节数(长度不限)
// 返回值:0=succ,1=error
unsigned char CanSendData(unsigned char desId, unsigned char srcId, unsigned char funId, unsigned char sesId, const void *p, unsigned int len)
{
 sCanData_t sCanData;
 unsigned int i = 0;
 unsigned int FrameCount = 0;
 unsigned char *pBuf = (unsigned char *)p;
 if(!p || len < 1)
 {
  return 1;
 }
 sCanData.desId = desId;
 sCanData.srcId = srcId;
 sCanData.funId = funId;
 sCanData.sesId = sesId;
 // 第一帧——信息帧
 sCanData.Buf[0] = 0x00;
 sCanData.Buf[1] = 0x00; // 帧序号
 sCanData.Buf[2] = (unsigned char)(len);
 sCanData.Buf[3] = (unsigned char)(len >> 8);
 sCanData.Buf[4] = (unsigned char)(len >> 16);
 sCanData.Buf[5] = (unsigned char)(len >> 24); // 总长度
 sCanData.txLen = 6;
 CanSendFrame(&sCanData, sizeof(sCanData));
 // 后续帧——数据帧
 FrameCount = len / 6;
 for(i = 0; i < FrameCount; i++)
 {
  unsigned char k;
  // 帧序号
  sCanData.Buf[0] = (unsigned char)(i + 1);
  sCanData.Buf[1] = (unsigned char)((i + 1) >> 8);
  // 帧数据
  for(k = 0; k < 6; k++)
  {
   sCanData.Buf[k + 2] = pBuf[i * 6 + k];
  }
  sCanData.txLen = 8;
  CanSendFrame(&sCanData, sizeof(sCanData));
 }
 // 检查最后一帧是否被发送
 if((len % 6) != 0)
 {
  // 帧序号
  sCanData.Buf[0] = (unsigned char)(FrameCount + 1);
  sCanData.Buf[1] = (unsigned char)((FrameCount + 1) >> 8);
  // 帧数据
  for(i = 0; i < len % 6; i++)
  {
   sCanData.Buf[i + 2] = pBuf[FrameCount * 6 + i];
  }
  sCanData.txLen = i + 2;
  CanSendFrame(&sCanData, sizeof(sCanData));
 }
 return 0;
}
 
CanDrv.h
#ifndef __CAN_DRV_H
#define __CAN_DRV_H
 
// 接收 CAN 总线帧数据,并对分帧数据进行组包
// p:RxMsg 数据域数据指针(RxMsg.Data)
// len:有效字节数(RxMsg.DLC)
// extId:扩展帧ID(RxMsg.ExtId)
// 返回值:0=succ,1=data error,2=memory error
unsigned char CanRecvDataProcess(const void *p, unsigned char len, unsigned long extId);

// 向 CAN 总线发送数据
// desId:目标ID
// srcId:本地ID
// funId:功能码
// sesId:会话ID,每次发送前自增1
// p:发送数据指针
// len:发送字节数(长度不限)
// 返回值:0=succ,1=error
unsigned char CanSendData(unsigned char desId, unsigned char srcId, unsigned char funId, unsigned char sesId, const void *p, unsigned int len);

//===============================================================================================================================================
//                                                      需外部实现的函数
//===============================================================================================================================================
// 读取 CAN 接收到的有效数据
// p:数据指针
// len:接收字节数
extern void CanRead(const void *p, unsigned int len);
// 实现例程,注:此函数内部处理时间不宜过长,应越短越好
//void CanRead(const void *p, unsigned int len)
//{
// unsigned char *pbuf = (unsigned char *)p;
//
// if(!pbuf || len < 1)
// {
//  return;
// }
//
// for(unsigned int i = 0; i < len; i++)
// {
//  printf("%d ", pbuf[i]); // 打印 CAN 端口数据
// }
//}
 
// CAN 总线底层发送函数
// p:数据指针(数据域数据)
// len:发送字节数(数据域长度)
// extId:扩展帧ID
// 返回值:0=succ,1=data error,2=timeout
extern unsigned char CanWrite(const void *p, unsigned int len, unsigned long extId);
// STM32F407 CAN 底层发送函数例程
//unsigned char CanWrite(const void *p, unsigned int len, unsigned long extId)
//{
// unsigned short int retry = 0;
// unsigned char TransmitMailbox = 0;
// if(!pbuf || len < 1)
// {
//  return 1;
// }
//
// CanTxMsg TxMsg;     // 发送帧结构体
// TxMsg.StdId = 0x00;    // 标准ID:0x00
// TxMsg.ExtId = extId;   // 设置扩展标示符(29位)
// TxMsg.IDE = CAN_Id_Extended; // 使用扩展标识符
// TxMsg.RTR = CAN_RTR_Data;  // 消息类型为数据帧
// TxMsg.DLC = len;    // 数据长度
// memcpy(TxMsg.Data, p, len);  // 拷贝数据
//
// // 数据发送至 CAN 网络
// TransmitMailbox = CAN_Transmit(CAN1, &TxMsg);
// while(CAN_TransmitStatus(CAN1, TransmitMailbox) != CANTXOK) // 等待发送完成
// {
//  if(++retry > 0xFFF)
//  {
//   return 2; // 数据发送超时
//  }
// }
//
// return 0; // 数据发送成功
//}
#endif
 
 
测试文件如下:
 
CanTest.c
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>
#include "CanTest.h"
#include "CanDrv.h"

#pragma pack(push, 1)
typedef struct _sCanMsg_t
{
 unsigned long ExtId; // 扩展ID
 unsigned char DLC; // 发送字节数
 unsigned char Data[8]; // 发送缓存区
}sCanMsg_t;
#pragma pack(pop)

unsigned int id = 0;
unsigned int BufLen = 0;
unsigned char buf[1000][sizeof(sCanMsg_t)] = {0};
CRITICAL_SECTION CriticalSection; // 临界区结构对象

#define PrintBytes(p, len) CanRead(p, len)

void CanRead(unsigned char *p, unsigned int len)
{
 unsigned int i = 0;
 for(i = 0; i < len; i++)
 {
  printf("%d ", p[i]);
 }
 printf("\r\n");
}
unsigned char CanWrite(const void *p, unsigned int len, unsigned long extId)
{
 unsigned char i;
 unsigned char *pbuf = NULL;
 sCanMsg_t TxMsg;
 EnterCriticalSection(&CriticalSection); // 进入临界区
 TxMsg.DLC = len;
 TxMsg.ExtId = extId;
 memcpy(TxMsg.Data, p, len);
 pbuf = (unsigned char *)&TxMsg;
 memcpy(buf[BufLen], pbuf, sizeof(sCanMsg_t));
 
// PrintBytes(buf[BufLen], len + 4);
 BufLen++;
 LeaveCriticalSection(&CriticalSection); // 退出临界区
 Sleep(10);
 return 0;
}

// 数据发送子线程
UINT WINAPI SendChildThread(void *arg)
{
 unsigned int k = 0;
 unsigned char buf[49] = {0};
 id++;
 
 for(k = 0; k < sizeof(buf); k++)
 {
  buf[k] = id + k;
 }
 return CanSendData(id, 0x01, id + 1, id, buf, sizeof(buf));
}

void CanRecvTest(void)
{
 unsigned int i;
 sCanMsg_t *RxMsg;
 HANDLE SendThread[16];
 // 数据发生器初始化
 InitializeCriticalSection(&CriticalSection); // 初始化临界区变量
 for(i = 0; i < sizeof(SendThread) / sizeof(SendThread[0]); i++)
 {
  SendThread[i] = (HANDLE)_beginthreadex(NULL, 0, SendChildThread, NULL, 0, NULL);
 }
 Sleep(3000);
 for(i = 0; i < sizeof(SendThread) / sizeof(SendThread[0]); i++)
 {
  CloseHandle(SendThread[i]);
 }
 // 处理接收的数据
 printf("\r\nData Frame Count:%d\r\n", BufLen);
 for(i = 0; i < BufLen; i++)
 {
  // 模拟 CAN 端口数据格式
  RxMsg = (sCanMsg_t *)buf[i];
  CanRecvDataProcess(RxMsg->Data, RxMsg->DLC, RxMsg->ExtId);
 }
 memset(buf, 0, sizeof(buf));
 BufLen = 0;
 id = 0;
}
 
CanTest.h
#ifndef __CAN_TEST_H
#define __CAN_TEST_H

void CanRecvTest(void);
#endif
 
 
测试数据:(下面每行数据代表CAN总线上的一帧数据)
1 34 32 192 6 0 0 49 0 0
13 46 160 193 6 0 0 49 0 0
3 36 96 192 6 0 0 49 0 0
4 37 128 192 6 0 0 49 0 0
5 38 160 192 6 0 0 49 0 0
6 39 192 192 6 0 0 49 0 0
7 40 224 192 6 0 0 49 0 0
8 41 0 193 6 0 0 49 0 0
9 42 32 193 6 0 0 49 0 0
10 43 64 193 6 0 0 49 0 0
11 44 96 193 6 0 0 49 0 0
12 45 128 193 6 0 0 49 0 0
2 35 64 192 6 0 0 49 0 0
14 47 192 193 6 0 0 49 0 0
15 48 224 193 6 0 0 49 0 0
16 49 0 194 6 0 0 49 0 0
14 47 192 193 8 1 0 14 15 16 17 18
16 49 0 194 8 1 0 16 17 18 19 20
12 45 128 193 8 1 0 12 13 14 15 16
10 43 64 193 8 1 0 10 11 12 13 14
9 42 32 193 8 1 0 9 10 11 12 13
15 48 224 193 8 1 0 15 16 17 18 19
4 37 128 192 8 1 0 4 5 6 7 8
7 40 224 192 8 1 0 7 8 9 10 11
3 36 96 192 8 1 0 3 4 5 6 7
2 35 64 192 8 1 0 2 3 4 5 6
8 41 0 193 8 1 0 8 9 10 11 12
11 44 96 193 8 1 0 11 12 13 14 15
5 38 160 192 8 1 0 5 6 7 8 9
6 39 192 192 8 1 0 6 7 8 9 10
1 34 32 192 8 1 0 1 2 3 4 5
16 49 0 194 8 2 0 22 23 24 25 26
10 43 64 193 8 2 0 16 17 18 19 20
13 46 160 193 8 1 0 13 14 15 16 17
12 45 128 193 8 2 0 18 19 20 21 22
14 47 192 193 8 2 0 20 21 22 23 24
7 40 224 192 8 2 0 13 14 15 16 17
15 48 224 193 8 2 0 21 22 23 24 25
4 37 128 192 8 2 0 10 11 12 13 14
9 42 32 193 8 2 0 15 16 17 18 19
6 39 192 192 8 2 0 12 13 14 15 16
1 34 32 192 8 2 0 7 8 9 10 11
5 38 160 192 8 2 0 11 12 13 14 15
2 35 64 192 8 2 0 8 9 10 11 12
3 36 96 192 8 2 0 9 10 11 12 13
11 44 96 193 8 2 0 17 18 19 20 21
8 41 0 193 8 2 0 14 15 16 17 18
16 49 0 194 8 3 0 28 29 30 31 32
7 40 224 192 8 3 0 19 20 21 22 23
14 47 192 193 8 3 0 26 27 28 29 30
12 45 128 193 8 3 0 24 25 26 27 28
10 43 64 193 8 3 0 22 23 24 25 26
13 46 160 193 8 2 0 19 20 21 22 23
9 42 32 193 8 3 0 21 22 23 24 25
16 49 0 194 8 4 0 34 35 36 37 38
8 41 0 193 8 3 0 20 21 22 23 24
3 36 96 192 8 3 0 15 16 17 18 19
2 35 64 192 8 3 0 14 15 16 17 18
5 38 160 192 8 3 0 17 18 19 20 21
7 40 224 192 8 4 0 25 26 27 28 29
4 37 128 192 8 3 0 16 17 18 19 20
6 39 192 192 8 3 0 18 19 20 21 22
15 48 224 193 8 3 0 27 28 29 30 31
1 34 32 192 8 3 0 13 14 15 16 17
11 44 96 193 8 3 0 23 24 25 26 27
12 45 128 193 8 4 0 30 31 32 33 34
10 43 64 193 8 4 0 28 29 30 31 32
14 47 192 193 8 4 0 32 33 34 35 36
9 42 32 193 8 4 0 27 28 29 30 31
13 46 160 193 8 3 0 25 26 27 28 29
16 49 0 194 8 5 0 40 41 42 43 44
3 36 96 192 8 4 0 21 22 23 24 25
8 41 0 193 8 4 0 26 27 28 29 30
7 40 224 192 8 5 0 31 32 33 34 35
2 35 64 192 8 4 0 20 21 22 23 24
5 38 160 192 8 4 0 23 24 25 26 27
6 39 192 192 8 4 0 24 25 26 27 28
4 37 128 192 8 4 0 22 23 24 25 26
15 48 224 193 8 4 0 33 34 35 36 37
11 44 96 193 8 4 0 29 30 31 32 33
1 34 32 192 8 4 0 19 20 21 22 23
10 43 64 193 8 5 0 34 35 36 37 38
13 46 160 193 8 4 0 31 32 33 34 35
12 45 128 193 8 5 0 36 37 38 39 40
9 42 32 193 8 5 0 33 34 35 36 37
14 47 192 193 8 5 0 38 39 40 41 42
7 40 224 192 8 6 0 37 38 39 40 41
16 49 0 194 8 6 0 46 47 48 49 50
3 36 96 192 8 5 0 27 28 29 30 31
8 41 0 193 8 5 0 32 33 34 35 36
2 35 64 192 8 5 0 26 27 28 29 30
4 37 128 192 8 5 0 28 29 30 31 32
15 48 224 193 8 5 0 39 40 41 42 43
6 39 192 192 8 5 0 30 31 32 33 34
5 38 160 192 8 5 0 29 30 31 32 33
14 47 192 193 8 6 0 44 45 46 47 48
9 42 32 193 8 6 0 39 40 41 42 43
12 45 128 193 8 6 0 42 43 44 45 46
10 43 64 193 8 6 0 40 41 42 43 44
13 46 160 193 8 5 0 37 38 39 40 41
1 34 32 192 8 5 0 25 26 27 28 29
11 44 96 193 8 5 0 35 36 37 38 39
2 35 64 192 8 6 0 32 33 34 35 36
4 37 128 192 8 6 0 34 35 36 37 38
16 49 0 194 8 7 0 52 53 54 55 56
7 40 224 192 8 7 0 43 44 45 46 47
3 36 96 192 8 6 0 33 34 35 36 37
8 41 0 193 8 6 0 38 39 40 41 42
15 48 224 193 8 6 0 45 46 47 48 49
6 39 192 192 8 6 0 36 37 38 39 40
9 42 32 193 8 7 0 45 46 47 48 49
14 47 192 193 8 7 0 50 51 52 53 54
5 38 160 192 8 6 0 35 36 37 38 39
12 45 128 193 8 7 0 48 49 50 51 52
1 34 32 192 8 6 0 31 32 33 34 35
13 46 160 193 8 6 0 43 44 45 46 47
10 43 64 193 8 7 0 46 47 48 49 50
16 49 0 194 8 8 0 58 59 60 61 62
11 44 96 193 8 6 0 41 42 43 44 45
2 35 64 192 8 7 0 38 39 40 41 42
4 37 128 192 8 7 0 40 41 42 43 44
7 40 224 192 8 8 0 49 50 51 52 53
3 36 96 192 8 7 0 39 40 41 42 43
15 48 224 193 8 7 0 51 52 53 54 55
6 39 192 192 8 7 0 42 43 44 45 46
8 41 0 193 8 7 0 44 45 46 47 48
14 47 192 193 8 8 0 56 57 58 59 60
5 38 160 192 8 7 0 41 42 43 44 45
9 42 32 193 8 8 0 51 52 53 54 55
12 45 128 193 8 8 0 54 55 56 57 58
10 43 64 193 8 8 0 52 53 54 55 56
11 44 96 193 8 7 0 47 48 49 50 51
1 34 32 192 8 7 0 37 38 39 40 41
13 46 160 193 8 7 0 49 50 51 52 53
16 49 0 194 3 9 0
7 40 224 192 3 9 0
3 36 96 192 8 8 0 45 46 47 48 49
4 37 128 192 8 8 0 46 47 48 49 50
15 48 224 193 8 8 0 57 58 59 60 61
6 39 192 192 8 8 0 48 49 50 51 52
2 35 64 192 8 8 0 44 45 46 47 48
10 43 64 193 3 9 0
12 45 128 193 3 9 0
14 47 192 193 3 9 0
9 42 32 193 3 9 0
5 38 160 192 8 8 0 47 48 49 50 51
8 41 0 193 8 8 0 50 51 52 53 54
3 36 96 192 3 9 0
13 46 160 193 8 8 0 55 56 57 58 59
1 34 32 192 8 8 0 43 44 45 46 47
11 44 96 193 8 8 0 53 54 55 56 57
4 37 128 192 3 9 0
6 39 192 192 3 9 0
15 48 224 193 3 9 0
2 35 64 192 3 9 0
1 34 32 192 3 9 0
13 46 160 193 3 9 0
8 41 0 193 3 9 0
5 38 160 192 3 9 0
11 44 96 193 3 9 0
 
接收合并后的数据包:
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
 
 
 
代码粘贴上来居然没有格式,对于一个很注重代码格式的猴子来说,这是无法接受的。

CAN 总线数据收发驱动的更多相关文章

  1. Linux SPI总线和设备驱动架构之三:SPI控制器驱动

    通过第一篇文章,我们已经知道,整个SPI驱动架构可以分为协议驱动.通用接口层和控制器驱动三大部分.其中,控制器驱动负责最底层的数据收发工作,为了完成数据的收发工作,控制器驱动需要完成以下这些功能:1. ...

  2. FPGA的GTP(aurora 协议)高速串行接口数据收发(转)

    reference:https://blog.csdn.net/qq_40261818/article/details/83039829 PG046-Aurora 8B/10B  Logicore I ...

  3. Linux SPI总线和设备驱动架构之四:SPI数据传输的队列化

    我们知道,SPI数据传输可以有两种方式:同步方式和异步方式.所谓同步方式是指数据传输的发起者必须等待本次传输的结束,期间不能做其它事情,用代码来解释就是,调用传输的函数后,直到数据传输完成,函数才会返 ...

  4. STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发

    这里我主要说一下如何做一个USB下位机,这里主要分3部分:1.建立工程:2.添加报文描述符:3.数据的传输.这里就不讲USB的理论知识了,有想要了解的自行百度一下就可以了. 建立工程:工程建立参考:h ...

  5. 转载 STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发

    STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发  本文转载自 https://www.cnblogs.com/xingboy/p/9913963.html 这里我主要说一 ...

  6. Linux驱动之I2C总线设备以及驱动

    [ 导读] 本文通过阅读内核代码,来梳理一下I2C子系统的整体视图.在开发I2C设备驱动程序时,往往缺乏对于系统整体的认识,导致没有一个清晰的思路.所以从高层级来分析一下I2C系统的设计思路,将有助于 ...

  7. HAL UART DMA 数据收发

    UART使用DMA进行数据收发,实现功能,串口2发送指令到上位机,上位机返回数据给串口2,串口2收到数据后由串口1进行转发,该功能为实验功能 1.UART与DMA通道进行绑定 void HAL_UAR ...

  8. java网络编程——多线程数据收发并行

    基本介绍与思路 收发并行 前一篇博客中,完成了客户端与服务端的简单TCP交互,但这种交互是触发式的:客户端发送一条消息,服务端收到后再回送一条.没有做到收发并行.收发并行的字面意思很容易理解,即数据的 ...

  9. Linux I2C核心、总线和设备驱动

    目录 更新记录 一.Linux I2C 体系结构 1.1 Linux I2C 体系结构的组成部分 1.2 内核源码文件 1.3 重要的数据结构 二.Linux I2C 核心 2.1 流程 2.2 主要 ...

随机推荐

  1. centos7安装python3x,使用virtualenv创建python3的隔离环境

    centos7默认python程序是2x,如果要使用3x可以使用EPEL仓库安装.同时为了使用隔离的python环境可以安装virtualenv. 1.启用EPEL sudo yum install ...

  2. 树莓派设定笔记(Raspberry Pi 3 B+)

    树莓派默认用户名密码 pi / raspberry 一.启用root用户 设置root用户密码 sudo passwd root 开启root账户 sudo passwd --unlock root ...

  3. CSS 分类 (Classification)

    ★★CSS 分类属性 (Classification)★★ ⑴CSS 分类属性允许你控制如何显示元素,设置图像显示于另一元素中的何处,相对于其正常位置来定位元素,使用绝对值来定位元素,以及元素的可见度 ...

  4. CSS3 阴影与圆角边框

    ㈠css3的新特性实际应用 ⑴文本阴影效果,用代码编写的方式实现   ⑵鼠标悬停的动态效果 左侧三幅图片,上面初始状态是没有说明文本的,但把鼠标放在上面的时候,这个图片上面就出现了说明文字   ⑶分栏 ...

  5. Spring实例化相关问题

    1.当Controller或者Service使用new来实例化时,能不能正常调用使用Resource声明的变量 不能,使用new来实例化时,所有使用Resource声明的变量均为null

  6. Jmeter(二) Jmeter组件介绍

    一.测试计划 测试的起点,同时也是其他所有组件的容器 二.线程(用户) Setup 线程组:一种特殊类型的线程,可用于执行预测试操作.即执行测试前进行定期线程组的执行 Teardown 线程组:一种特 ...

  7. Linux命令-文件管理(一)

    Linux命令-文件管理(一) 1.命令:cat cat命令用于把档案串连接后传到基本输出(萤幕或加 > fileName 到另一个档案) 使用权限:所有使用者 语法格式:cat [-AbeEn ...

  8. Map循环/迭代/遍历效率、性能问题。

    项目开发完毕,为了找点事打发一下时间,于是用findBugs插件对当前完工的项目进行扫描,发现了很多问题.其中有个关于性能的问题,在这里记录一下. 提示信息为:Inefficient use of k ...

  9. x-admin

    https://blog.csdn.net/u014793102/article/details/80316335

  10. TL;DR

    英文文章中,偶尔会出现TL;DR 的字符. TL;DR=>Too Long; Don’t Read=>太长了,读不下去=>长话短说 一般用于在文章开头先给出干货.