CAN 总线数据收发驱动
1、CAN通信
CAN 是Controller Area Network 的缩写(以下称为CAN),是ISO国际标准化的串行通信协议。
该通信使用的是ISO11898标准,该标准的物理层特征如图1所示:
CAN协议是通过以下5种类型的帧进行通信的:
1、数据帧
2、摇控帧
3、错误帧
4、过载帧
5、帧间隔
另外,数据帧和遥控帧有标准格式和扩展格式两种格式。标准格式有11 个位的标识符(ID),扩展格式有29 个位的ID。
大部分系统使用的都是数据帧 ,我这里使用的也是数据帧。
标准数据帧一般由7个段构成,即:
1、帧起始。表示数据帧开始的段。
2、仲裁段。表示该帧优先级的段。
3、控制段。表示数据的字节数及保留位的段。
4、数据段。数据的内容,一帧可发送0~8个字节的数据。
5、CRC段。检查帧的传输错误的段。
6、ACK段。表示确认正常接收的段。
7、帧结束。表示数据帧结束的段。
本实验使用自定义数据帧格式如下:
扩展帧标识(29bit):
1、会话ID(bit0 ~ bit7)
2、功能码(bit8 ~ bit12)
3、本地ID(bit13 ~ bit20)
4、目标ID(bit21 ~ bit28)
数据段:
1、byte0 ~ byte1:帧序号
2、byte2 ~ byte7:数据
注:帧序号从零开始,帧序号为零的时候,记录的是四字节数据总长度;帧序号大于零时,记录的才是数据内容,每帧最多六字节数据。
2、实验目的
在实际使用中,每次通信的数据量不可能都小于八字节,因此就需要将数据包拆分后(即分帧)进行发送。当CAN总线上多个节点同时对同一个目标节点发送分帧数据,此时该目标节点就需要对接收的数据进行分类接收最后再合并成一个完成的数据包。而下面的CAN驱动代码就是完成接收数据并且合并成数据包的逻辑代码。代码使用纯C实现,方便移植。
实验环境:VS2012
测试过程:使用多个线程,每个线程将一个较大的数据包,按照帧格式拆成多帧数据,然后将帧数据全部保存到一个非常大的数组中,由于是多个线程 同时工作,所以对于每个数据包的帧来说都不是按顺序进入数组中的,而是多个数据包的帧穿插着存入数组,最后调用数据接收处理API函数,对数组中的帧一个个进行接收处理,最后输出帧合并后的数据包。
3、驱动文件
CanDrv.c
#include <stdlib.h>
#include "CanDrv.h"
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
// 思路:建立一条单向链表,链表的一个节点代表CAN总线的一个端点的一个完整数据包,接收到一个分帧数据后将其存储在对应的链表节点中.
{
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;
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;
{
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;
{
struct _sCanRecvData_t *pBuf;
struct _sCanRecvList_t *pNext;
}sCanRecvList_t;
#pragma pack(pop)
// 返回值:内存起始地址
static void *pMalloc(unsigned int size)
{
return malloc(size);
}
static void pFree(void *pmem)
{
free(pmem);
}
// 返回值:若相应的CAN节点存在,则返回CAN节点在链表中的节点地址,否则返回NULL
static sCanRecvList_t *CanNodeSearch(const sCanRecvList_t *pHeadNode, sCanData_t *sCanData)
{
sCanRecvList_t *pNode = NULL;
{
return NULL;
}
{
if(pNode->pBuf->srcId == sCanData->srcId && pNode->pBuf->funId == sCanData->funId && pNode->pBuf->sesId == sCanData->sesId)
{
return pNode;
}
}
}
// 返回值:创建成功则返回头节点地址,否则返回NULL
static sCanRecvList_t *ListCreate(void)
{
sCanRecvList_t *head = NULL;
if(head == NULL)
{
return NULL;
}
head->pNext = NULL;
}
// 返回值:创建成功返回节点地址,否则返回NULL
static sCanRecvList_t *ListNodeCreate(sCanData_t *sCanData)
{
sCanRecvList_t *node = NULL;
{
return NULL; // 数据异常
}
{
return NULL; // 帧序号不为0,说明不是头帧
}
if(node == NULL)
{
return NULL;
}
node->pBuf = (sCanRecvData_t *)pMalloc(sizeof(sCanRecvData_t));
if(node->pBuf == NULL)
{
pFree(node);
node = NULL;
return NULL;
}
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];
if(node->pBuf->pBuf == NULL)
{
pFree(node->pBuf);
node->pBuf = NULL;
pFree(node);
node = NULL;
return NULL;
}
}
// 返回值:链表尾节点地址,链表为空或者没有找到时返回NULL
static sCanRecvList_t *ListNodeSearch(const sCanRecvList_t *pHeadNode, const sCanRecvList_t *pSearchNode)
{
sCanRecvList_t *pNode = (sCanRecvList_t *)pHeadNode;
{
return NULL;
}
{
while(pNode->pNext != NULL)
{
pNode = pNode->pNext; // 搜索尾节点
}
}
else
{
while(pNode != pSearchNode)
{
pNode = pNode->pNext; // 搜索指定节点
if(pNode == NULL)
{
return NULL;
}
}
}
}
static void ListNodeInssert(const sCanRecvList_t *pHeadNode, sCanRecvList_t * const pNewNode)
{
sCanRecvList_t *pNode = NULL;
{
return;
}
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;
{
return;
}
while(pLastNode->pNext != pDeleteNode)
{
pLastNode = pLastNode->pNext;
}
{
// 删除节点
pLastNode->pNext = pDeleteNode->pNext;
pFree(pDeleteNode->pBuf->pBuf);
pDeleteNode->pBuf->pBuf = NULL;
pDeleteNode->pBuf = NULL;
pDeleteNode->pNext = NULL;
pDeleteNode = NULL;
}
}
// 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;
{
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;
}
}
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;
}
// 返回值: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;
{
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);
}
// 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;
{
return 1;
}
sCanData.srcId = srcId;
sCanData.funId = funId;
sCanData.sesId = sesId;
sCanData.Buf[0] = 0x00;
sCanData.Buf[1] = 0x00; // 帧序号
sCanData.Buf[3] = (unsigned char)(len >> 8);
sCanData.Buf[4] = (unsigned char)(len >> 16);
sCanData.Buf[5] = (unsigned char)(len >> 24); // 总长度
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];
}
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];
}
CanSendFrame(&sCanData, sizeof(sCanData));
}
}
#define __CAN_DRV_H
// 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);
//===============================================================================================================================================
// 需外部实现的函数
//===============================================================================================================================================
// 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 端口数据
// }
//}
// p:数据指针(数据域数据)
// len:发送字节数(数据域长度)
// extId:扩展帧ID
// 返回值:0=succ,1=data error,2=timeout
extern unsigned char CanWrite(const void *p, unsigned int len, unsigned long extId);
//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; // 数据发送成功
//}
#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 i;
unsigned char *pbuf = NULL;
TxMsg.ExtId = extId;
memcpy(TxMsg.Data, p, len);
memcpy(buf[BufLen], pbuf, sizeof(sCanMsg_t));
// PrintBytes(buf[BufLen], len + 4);
Sleep(10);
return 0;
}
// 数据发送子线程
UINT WINAPI SendChildThread(void *arg)
{
unsigned int k = 0;
unsigned char buf[49] = {0};
for(k = 0; k < sizeof(buf); k++)
{
buf[k] = id + k;
}
}
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);
}
{
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);
}
BufLen = 0;
id = 0;
}
#define __CAN_TEST_H
void CanRecvTest(void);
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