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