CRC16-循环冗余校验
/*******
作 者:温子祺
联系方式:wenziqi@hotmail.com
说 明 :CRC16-循环冗余校验
********/
【例子】通过CRC-16循环冗余校验的方式实现数据传输与控制,例如控制LED灯、蜂鸣器、发送数据到上位机。
由于是数据传输与控制,需要定制一个结构体、共用体方便数据识别,同时增强可读性。从数据帧格式定义中可以定义为“PKT_CRC_EX”类型。
识别数据请求什么操作可以通过以下手段来识别:识别数据头部1、数据头部2,操作码。当完全接收数据完毕后通过校验该数据得出的校验值与该数
据的尾部的校验值是否匹配。若匹配,则根据操作码的请求进行操作;若不匹配则丢弃当前数据帧,等待下一个数据帧的到来。
结构体定义如下:
(1)
typedef struct _ PKT_CRC
{
UINT8 m_ucHead1; //首部
UINT8 m_ucHead2; //首部
UINT8 m_ucOptCode; //操作码
UINT8 m_ucDataLength; //数据长度
UINT8 m_szDataBuf[16]; //数据
UINT8 m_szCrc[2]; //CRC16校验值为2个字节
}PKT_CRC;
(2)
typedef union _PKT_PARITY_EX
{
PKT_PARITY r;
UINT8 buf[32];
} PKT_PARITY_EX;
PKT_PARITY_EX PktParityEx;
CRC16-循环冗余校验代码如下:
2#include"stc.h"
3
4/*******
5 类型定义,方便代码移植
6*******/
7typedef unsignedcharUINT8;
8typedef unsignedintUINT16;
9typedef unsignedlongUINT32;
10
11typedefcharINT8;
12typedefintINT16;
13typedeflongINT32;
14typedef bit BOOL;
15
16/*******
17 大量宏定义,便于代码移植和阅读
18*******/
19//--------------------------------
20//----头部----
21#defineDCMD_CTRL_HEAD1 0x10//PC下传控制包头部1
22#defineDCMD_CTRL_HEAD2 0x01//PC下传控制包头部2
23
24//----命令码----
25#defineDCMD_NULL 0x00//命令码:空操作
26#defineDCMD_CTRL_BELL 0x01//命令码:控制蜂鸣器
27#defineDCMD_CTRL_LED 0x02//命令码:控制LED
28#defineDCMD_REQ_DATA 0x03//命令码:请求数据
29
30//----数据----
31#defineDCTRL_BELL_ON 0x01//蜂鸣器响
32#defineDCTRL_BELL_OFF 0x02//蜂鸣器禁鸣
33#defineDCTRL_LED_ON 0x03//LED亮
34#defineDCTRL_LED_OFF 0x04//LED灭
35
36//--------------------------------
37//----头部----
38#defineUCMD_CTRL_HEAD1 0x20//MCU上传控制包头部1
39#defineUCMD_CTRL_HEAD2 0x01//MCU上传控制包头部2
40
41//----命令码----
42#defineUCMD_NULL 0x00//命令码:空操作
43#defineUCMD_REQ_DATA 0x01//命令码:请求数据
44
45
46#defineCTRL_FRAME_LEN 0x04//帧长度(不包含数据和校验值)
47#defineCRC16_LEN 0x02//检验值长度
48
49#defineEN_UART() ES=1//允许串口中断
50#defineNOT_EN_UART() ES=0//禁止串口中断
51
52#defineBELL(x) {if((x))P0_6=1 ;else P0_6=0;}//蜂鸣器控制宏函数
53#defineLED(x) {if((x))P2=0x00;else P2=0xFF;}//LED控制宏函数
54
55#defineTRUE 1
56#defineFALSE 0
57
58#defineHIGH 1
59#defineLOW 0
60
61#defineON 1
62#defineOFF 0
63
64#defineNULL (void )0
65
66/使用结构体对数据包进行封装
67方便操作数据
68/
69typedefstruct_PKT_CRC
70{
71UINT8 m_ucHead1;//首部1
72UINT8 m_ucHead2;//首部2
73UINT8 m_ucOptCode;//操作码
74UINT8 m_ucDataLength;//数据长度
75UINT8 m_szDataBuf[16];//数据
76
77UINT8 m_szCrc[2];//CRC16为2个字节
78
79}PKT_CRC;
80
81/使用共用体再一次对数据包进行封装
82操作数据更加方便
83/
84typedef union _PKT_CRC_EX
85{
86PKT_CRC r;
87UINT8 p[32];
88} PKT_CRC_EX;
89
90
91PKT_CRC_EX PktCrcEx;//定义数据包变量
92
93
94BOOL bLedOn=FALSE;//定义是否点亮LED布尔变量
95BOOL bBellOn=FALSE;//定义是否蜂鸣器响布尔变量
96BOOL bReqData=FALSE;//定义是否请求数据布尔变量
97
98/*******
99 函数名称: CRC16Check
100 输 入: buf 要校验的数据;
101len 要校验的数据的长度
102 输 出: 校验值
103 功能描述: CRC16循环冗余校验
104*******/
105UINT16 CRC16Check(UINT8buf, UINT8 len)
106{
107UINT8 i, j;
108UINT16 uncrcReg=0xffff;
109UINT16 uncur;
110
111for(i=0; i<len; i++)
112{
113uncur=buf[i]<<8;
114
115for(j=0; j<8; j++)
116{
117if((INT16)(uncrcReg^uncur)<0)
118{
119uncrcReg=(uncrcReg<<1)^0x1021;
120}
121else
122{
123uncrcReg<<=1;
124}
125
126uncur<<=1;
127}
128}
129
130returnuncrcReg;
131}
132/*********
133 函数名称:BufCpy
134 输 入:dest目标缓冲区;
135Src 源缓冲区
136size 复制数据的大小
137 输 出:无
138* 说 明:复制缓冲区
139********/
140BOOL BufCpy(UINT8dest,UINT8src,UINT32 size)
141{
142if(NULL==dest||NULL==src||NULL==size)
143{
144returnFALSE;
145}
146
147do
148{
149dest++=src++;
150
151}while(--size!=0);
152
153returnTRUE;
154}
155/********
156 函数名称: UartInit
157 输 入: 无
158 输 出: 无
159 功能描述: 串口初始化
160*******/
161voidUartInit(void)
162{
163SCON=0x40;
164T2CON=0x34;
165RCAP2L=0xD9;
166RCAP2H=0xFF;
167REN=1;
168ES=1;
169}
170/********
171 函数名称: UARTSendByte
172 输 入: b 单个字节
173 输 出: 无
174 功能描述: 串口 发送单个字节
175*******/
176voidUARTSendByte(UINT8 b)
177{
178SBUF=b;
179while(TI==0);
180TI=0;
181}
182/********
183 函数名称: UartSendNBytes
184 输 入: buf 数据缓冲区;
185len 发送数据长度
186 输 出: 无
187 功能描述: 串口 发送多个字节
188*******/
189voidUartSendNBytes(UINT8buf,UINT8 len)
190{
191while(len--)
192{
193UARTSendByte(buf++);
194}
195}
196/********
197 函数名称: main
198 输 入: 无
199 输 出: 无
200 功能描述: 函数主体
201*******/
202voidmain(void)
203{
204UINT8 i=0;
205UINT16 uscrc=0;
206
207UartInit();//串口初始化
208
209EA=1;//开总中断
210
211while(1)
212{
213if(bLedOn)//是否点亮Led
214{
215LED(ON);
216}
217else
218{
219LED(OFF);
220}
221
222
223if(bBellOn)//是否响蜂鸣器
224{
225BELL(ON);
226}
227else
228{
229BELL(OFF);
230}
231
232if(bReqData)//是否请求数据
233{
234bReqData=FALSE;
235
236NOT_EN_UART();//禁止串口中断
237
238PktCrcEx.r.m_ucHead1=UCMD_CTRL_HEAD1;//MCU上传数据帧头部1
239PktCrcEx.r.m_ucHead2=UCMD_CTRL_HEAD2;//MCU上传数据帧头部2
240PktCrcEx.r.m_ucOptCode=UCMD_REQ_DATA;//MCU上传数据帧命令码
241
242
243uscrc=CRC16Check(PktCrcEx.p,
244CTRL_FRAME_LEN+
245PktCrcEx.r.m_ucDataLength);//计算校验值
246
247PktCrcEx.r.m_szCrc[0]=(UINT8) uscrc;//校验值低字节
248PktCrcEx.r.m_szCrc[1]=(UINT8)(uscrc>>8);//校验值高字节
249
250/
251这样做的原因是因为有时写数据长度不一样,
252导致PktCrcEx.r.m_szCrc会出现为0的情况
253所以使用BufCpy将校验值复制到相应的位置
254/
255
256BufCpy(&PktCrcEx.p[CTRL_FRAME_LEN+PktCrcEx.r.m_ucDataLength],
257PktCrcEx.r.m_szCrc,
258CRC16_LEN);
259
260UartSendNBytes(PktCrcEx.p,
261CTRL_FRAME_LEN+
262PktCrcEx.r.m_ucDataLength+CRC16_LEN);//发送数据
263
264EN_UART();//允许串口中断
265
266}
267}
268}
269/********
270 函数名称: UartIRQ
271 输 入: 无
272 输 出: 无
273 功能描述: 串口中断服务函数
274*******/
275voidUartIRQ(void)interrupt4
276{
277staticUINT8 uccnt=0;
278UINT8 uclen;
279UINT16 uscrc;
280
281if(RI)//是否接收到数据
282{
283RI=0;
284
285PktCrcEx.p[uccnt++]=SBUF;//获取单个字节
286
287
288if(PktCrcEx.r.m_ucHead1==DCMD_CTRL_HEAD1)//是否有效的数据帧头部1
289{
290if(uccnt<CTRL_FRAME_LEN+PktCrcEx.r.m_ucDataLength+CRC16_LEN)//是否接收完所有数据
291{
292if(uccnt>=2&&PktCrcEx.r.m_ucHead2!=DCMD_CTRL_HEAD2)//是否有效的数据帧头部2
293{
294uccnt=0;
295
296return;
297}
298
299}
300else
301{
302
303uclen=CTRL_FRAME_LEN+PktCrcEx.r.m_ucDataLength;//获取数据帧有效长度(不包括校验值)
304
305uscrc=CRC16Check(PktCrcEx.p,uclen);//计算校验值
306
307/
308这样做的原因是因为有时写数据长度不一样,
309导致PktCrcEx.r.m_szCrc会出现为0的情况
310所以使用BufCpy将校验值复制到相应的位置
311/
312BufCpy(PktCrcEx.r.m_szCrc,&PktCrcEx.p[uclen],CRC16_LEN);
313
314if((UINT8)(uscrc>>8)!=PktCrcEx.r.m_szCrc[1]\
315||(UINT8) uscrc=PktCrcEx.r.m_szCrc[0])//校验值是否匹配
316{
317uccnt=0;
318
319return;
320}
321
322switch(PktCrcEx.r.m_ucOptCode)//从命令码中获取相对应的操作
323{
324caseDCMD_CTRL_BELL://控制蜂鸣器命令码
325{
326if(DCTRL_BELL_ON==PktCrcEx.r.m_szDataBuf[0])//数据部分含控制码
327{
328bBellOn=TRUE;
329}
330else
331{
332bBellOn=FALSE;
333}
334}
335break;
336
337caseDCMD_CTRL_LED://控制LED命令码
338{
339
340if(DCTRL_LED_ON==PktCrcEx.r.m_szDataBuf[0])//数据部分含控制码
341{
342bLedOn=TRUE;
343}
344else
345{
346bLedOn=FALSE;
347}
348}
349break;
350
351caseDCMD_REQ_DATA://请求数据命令码
352{
353bReqData=TRUE;
354}
355break;
356
357}
358
359uccnt=0;
360
361return;
362}
363
364}
365else
366{
367uccnt=0;
368}
369
370}
371}
372
原文链接: https://www.cnblogs.com/tangwei039/archive/2010/10/01/1840626.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/15680
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!