目标:使用链表实现 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. 如何让一个sprite绕一个点旋转,同时又可以实现指定旋转角度并慢慢停下的效果

    如何让一个sprite绕一个点旋转,同时又可以实现指定旋转角度并慢慢停下的效果 首先列出sprite围绕一个点旋转的公式,这个可以自己推导,假设sprite的起始位置为(x1,y1),围绕旋转的中心点 ...

  2. C# 常用方法——图片转base64字符串

    其他扩展方法详见:https://www.cnblogs.com/zhuanjiao/p/12060937.html /// <summary> /// Image 转成 base64 / ...

  3. [Linux系统] 如何修改CentOS7网卡名

    一.关闭一致性网络设备命名法 cat /etc/sysconfig/grub GRUB_TIMEOUT= GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g ...

  4. jquery target属性 语法

    jquery target属性 语法 作用:target 属性规定哪个 DOM 元素触发了该事件.大理石平台精度等级 语法:event.targe 参数: 参数 描述 event     必需.规定需 ...

  5. jquery radio选择器 语法

    jquery radio选择器 语法 作用::radio 选择器选取类型为 radio 的 <input> 元素.大理石平台价格表 语法:$(":radio") jqu ...

  6. props 父组件给子组件传递参数

    话不多说,直接上代码 父组件: <span><humidity-component ref="soilHumidityBot" :title='title2'&g ...

  7. python之timeit模块

    timeit模块: timeit 模块定义了接受两个参数的 Timer 类.两个参数都是字符串. 第一个参数是你要计时的语句或者函数. 传递给 Timer 的第二个参数是为第一个参数语句构建环境的导入 ...

  8. vue项目使用cropperjs制作图片剪裁,压缩组件

    项目中裁剪图片效果 代码部分:(将上传部分 封装成一个组件直接调用当前组件) <template> <div id="demo"> <!-- 遮罩层 ...

  9. spark MLlib 概念 2:Stratified sampling 层次抽样

    定义: In statistical surveys, when subpopulations within an overall population vary, it is advantageou ...

  10. cucumber+selenium

    工程结构 pom <?xml version="1.0" encoding="UTF-8"?> <project xmlns="ht ...