CRC16-循环冗余校验
/***************************************************
?*作???? 者:溫子祺
?*聯(lián)系方式:wenziqi@hotmail.com
?*說??? 明 :CRC16-循環(huán)冗余校驗(yàn)??
?***************************************************/
?
【例子】通過CRC-16循環(huán)冗余校驗(yàn)的方式實(shí)現(xiàn)數(shù)據(jù)傳輸與控制,例如控制LED燈、蜂鳴器、發(fā)送數(shù)據(jù)到上位機(jī)。
???? 由于是數(shù)據(jù)傳輸與控制,需要定制一個(gè)結(jié)構(gòu)體、共用體方便數(shù)據(jù)識(shí)別,同時(shí)增強(qiáng)可讀性。從數(shù)據(jù)幀格式定義中可以定義為“PKT_CRC_EX”類型。
?識(shí)別數(shù)據(jù)請(qǐng)求什么操作可以通過以下手段來識(shí)別:識(shí)別數(shù)據(jù)頭部1、數(shù)據(jù)頭部2,操作碼。當(dāng)完全接收數(shù)據(jù)完畢后通過校驗(yàn)該數(shù)據(jù)得出的校驗(yàn)值與該數(shù)
據(jù)的尾部的校驗(yàn)值是否匹配。若匹配,則根據(jù)操作碼的請(qǐng)求進(jìn)行操作;若不匹配則丟棄當(dāng)前數(shù)據(jù)幀,等待下一個(gè)數(shù)據(jù)幀的到來。
?
結(jié)構(gòu)體定義如下:
(1)
typedef? struct _ PKT_CRC
{
??? ??? UINT8 m_ucHead1;?????? //首部
??? ??? UINT8 m_ucHead2;?????? //首部
??? ??? UINT8 m_ucOptCode;???? //操作碼
??? ??? UINT8 m_ucDataLength;? //數(shù)據(jù)長(zhǎng)度
??? ??? UINT8 m_szDataBuf[16]; //數(shù)據(jù)
?
????????UINT8 m_szCrc[2];????? //CRC16校驗(yàn)值為2個(gè)字節(jié)?
?
}PKT_CRC;
(2)
typedef union _PKT_PARITY_EX
{
??? PKT_PARITY r;
??? UINT8 buf[32];
} PKT_PARITY_EX;
?
PKT_PARITY_EX PktParityEx;
?
CRC16-循環(huán)冗余校驗(yàn)代碼如下:
?
?
??
???
12 #include "stc.h"
3
4 ?/***************************************************
5 * 類型定義,方便代碼移植
6 ***************************************************/
7 typedef unsigned char UINT8;
8 typedef unsigned int UINT16;
9 typedef unsigned long UINT32;
10
11 typedef char INT8;
12 typedef int INT16;
13 typedef long INT32;
14 typedef bit BOOL;
15
16 ?/***************************************************
17 * 大量宏定義,便于代碼移植和閱讀
18 ***************************************************/
19 ?//--------------------------------
20 //----頭部----
21 #define DCMD_CTRL_HEAD1 0x10 //PC下傳控制包頭部1
22 #define DCMD_CTRL_HEAD2 0x01 //PC下傳控制包頭部2
23
24 //----命令碼----
25 #define DCMD_NULL 0x00 //命令碼:空操作
26 #define DCMD_CTRL_BELL 0x01 //命令碼:控制蜂鳴器
27 #define DCMD_CTRL_LED 0x02 //命令碼:控制LED
28 #define DCMD_REQ_DATA 0x03 //命令碼:請(qǐng)求數(shù)據(jù)
29
30 //----數(shù)據(jù)----
31 #define DCTRL_BELL_ON 0x01 //蜂鳴器響
32 #define DCTRL_BELL_OFF 0x02 //蜂鳴器禁鳴
33 #define DCTRL_LED_ON 0x03 //LED亮
34 #define DCTRL_LED_OFF 0x04 //LED滅
35
36 //--------------------------------
37 //----頭部----
38 #define UCMD_CTRL_HEAD1 0x20 //MCU上傳控制包頭部1
39 #define UCMD_CTRL_HEAD2 0x01 //MCU上傳控制包頭部2
40
41 //----命令碼----
42 #define UCMD_NULL 0x00 //命令碼:空操作
43 #define UCMD_REQ_DATA 0x01 //命令碼:請(qǐng)求數(shù)據(jù)
44
45
46 #define CTRL_FRAME_LEN 0x04 //幀長(zhǎng)度(不包含數(shù)據(jù)和校驗(yàn)值)
47 #define CRC16_LEN 0x02 //檢驗(yàn)值長(zhǎng)度
48
49 #define EN_UART() ES=1 //允許串口中斷
50 #define NOT_EN_UART() ES=0 //禁止串口中斷
51
52 #define BELL(x) {if((x))P0_6=1 ;else P0_6=0;} //蜂鳴器控制宏函數(shù)
53 #define LED(x) {if((x))P2=0x00;else P2=0xFF;}//LED控制宏函數(shù)
54
55 #define TRUE 1
56 #define FALSE 0
57
58 #define HIGH 1
59 #define LOW 0
60
61 #define ON 1
62 #define OFF 0
63
64 #define NULL (void *)0
65
66 /*使用結(jié)構(gòu)體對(duì)數(shù)據(jù)包進(jìn)行封裝
67 *方便操作數(shù)據(jù)
68 */
69 typedef struct _PKT_CRC
70 {
71 UINT8 m_ucHead1; //首部1
72 UINT8 m_ucHead2; //首部2
73 UINT8 m_ucOptCode; //操作碼
74 UINT8 m_ucDataLength; //數(shù)據(jù)長(zhǎng)度
75 UINT8 m_szDataBuf[16]; //數(shù)據(jù)
76
77 UINT8 m_szCrc[2]; //CRC16為2個(gè)字節(jié)
78
79 }PKT_CRC;
80
81 /*使用共用體再一次對(duì)數(shù)據(jù)包進(jìn)行封裝
82 *操作數(shù)據(jù)更加方便
83 */
84 typedef union _PKT_CRC_EX
85 {
86 PKT_CRC r;
87 UINT8 p[32];
88 } PKT_CRC_EX;
89
90
91 PKT_CRC_EX PktCrcEx; //定義數(shù)據(jù)包變量
92
93
94 BOOL bLedOn=FALSE; //定義是否點(diǎn)亮LED布爾變量
95 BOOL bBellOn=FALSE; //定義是否蜂鳴器響布爾變量
96 BOOL bReqData=FALSE; //定義是否請(qǐng)求數(shù)據(jù)布爾變量
97
98 /****************************************************
99 ** 函數(shù)名稱: CRC16Check
100 ** 輸 入: buf 要校驗(yàn)的數(shù)據(jù);
101 len 要校驗(yàn)的數(shù)據(jù)的長(zhǎng)度
102 ** 輸 出: 校驗(yàn)值
103 ** 功能描述: CRC16循環(huán)冗余校驗(yàn)
104 *****************************************************/
105 UINT16 CRC16Check(UINT8 *buf, UINT8 len)
106 {
107 UINT8 i, j;
108 UINT16 uncrcReg = 0xffff;
109 UINT16 uncur;
110
111 for (i = 0; i < len; i++)
112 {
113 uncur = buf[i] << 8;
114
115 for (j = 0; j < 8; j++)
116 {
117 if ((INT16)(uncrcReg ^ uncur) < 0)
118 {
119 uncrcReg = (uncrcReg << 1) ^ 0x1021;
120 }
121 else
122 {
123 uncrcReg <<= 1;
124 }
125
126 uncur <<= 1;
127 }
128 }
129
130 return uncrcReg;
131 }
132 /*************************************************************
133 * 函數(shù)名稱:BufCpy
134 * 輸 入:dest目標(biāo)緩沖區(qū);
135 Src 源緩沖區(qū)
136 size 復(fù)制數(shù)據(jù)的大小
137 * 輸 出:無
138 * 說 明:復(fù)制緩沖區(qū)
139 **************************************************************/
140 BOOL BufCpy(UINT8 * dest,UINT8 * src,UINT32 size)
141 {
142 if(NULL ==dest || NULL==src ||NULL==size)
143 {
144 return FALSE;
145 }
146
147 do
148 {
149 *dest++ = *src++;
150
151 }while(--size!=0);
152
153 return TRUE;
154 }
155 /****************************************************
156 ** 函數(shù)名稱: UartInit
157 ** 輸 入: 無
158 ** 輸 出: 無
159 ** 功能描述: 串口初始化
160 *****************************************************/
161 void UartInit(void)
162 {
163 SCON=0x40;
164 T2CON=0x34;
165 RCAP2L=0xD9;
166 RCAP2H=0xFF;
167 REN=1;
168 ES=1;
169 }
170 /****************************************************
171 ** 函數(shù)名稱: UARTSendByte
172 ** 輸 入: b 單個(gè)字節(jié)
173 ** 輸 出: 無
174 ** 功能描述: 串口 發(fā)送單個(gè)字節(jié)
175 *****************************************************/
176 void UARTSendByte(UINT8 b)
177 {
178 SBUF=b;
179 while(TI==0);
180 TI=0;
181 }
182 /****************************************************
183 ** 函數(shù)名稱: UartSendNBytes
184 ** 輸 入: buf 數(shù)據(jù)緩沖區(qū);
185 len 發(fā)送數(shù)據(jù)長(zhǎng)度
186 ** 輸 出: 無
187 ** 功能描述: 串口 發(fā)送多個(gè)字節(jié)
188 *****************************************************/
189 void UartSendNBytes(UINT8 *buf,UINT8 len)
190 {
191 while(len--)
192 {
193 UARTSendByte(*buf++);
194 }
195 }
196 /****************************************************
197 ** 函數(shù)名稱: main
198 ** 輸 入: 無
199 ** 輸 出: 無
200 ** 功能描述: 函數(shù)主體
201 *****************************************************/
202 void main(void)
203 {
204 UINT8 i=0;
205 UINT16 uscrc=0;
206
207 UartInit();//串口初始化
208
209 EA=1; //開總中斷
210
211 while(1)
212 {
213 if(bLedOn) //是否點(diǎn)亮Led
214 {
215 LED(ON);
216 }
217 else
218 {
219 LED(OFF);
220 }
221
222
223 if(bBellOn)//是否響蜂鳴器
224 {
225 BELL(ON);
226 }
227 else
228 {
229 BELL(OFF);
230 }
231
232 if(bReqData)//是否請(qǐng)求數(shù)據(jù)
233 {
234 bReqData=FALSE;
235
236 NOT_EN_UART(); //禁止串口中斷
237
238 PktCrcEx.r.m_ucHead1=UCMD_CTRL_HEAD1;//MCU上傳數(shù)據(jù)幀頭部1
239 PktCrcEx.r.m_ucHead2=UCMD_CTRL_HEAD2;//MCU上傳數(shù)據(jù)幀頭部2
240 PktCrcEx.r.m_ucOptCode=UCMD_REQ_DATA;//MCU上傳數(shù)據(jù)幀命令碼
241
242
243 uscrc=CRC16Check(PktCrcEx.p,
244 CTRL_FRAME_LEN+
245 PktCrcEx.r.m_ucDataLength);//計(jì)算校驗(yàn)值
246
247 PktCrcEx.r.m_szCrc[0]=(UINT8) uscrc; //校驗(yàn)值低字節(jié)
248 PktCrcEx.r.m_szCrc[1]=(UINT8)(uscrc>>8);//校驗(yàn)值高字節(jié)
249
250 /*
251 這樣做的原因是因?yàn)橛袝r(shí)寫數(shù)據(jù)長(zhǎng)度不一樣,
252 導(dǎo)致PktCrcEx.r.m_szCrc會(huì)出現(xiàn)為0的情況
253 所以使用BufCpy將校驗(yàn)值復(fù)制到相應(yīng)的位置
254 */
255
256 BufCpy(&PktCrcEx.p[CTRL_FRAME_LEN+PktCrcEx.r.m_ucDataLength],
257 PktCrcEx.r.m_szCrc,
258 CRC16_LEN);
259
260 UartSendNBytes(PktCrcEx.p,
261 CTRL_FRAME_LEN+
262 PktCrcEx.r.m_ucDataLength+CRC16_LEN);//發(fā)送數(shù)據(jù)
263
264 EN_UART();//允許串口中斷
265
266 }
267 }
268 }
269 /****************************************************
270 ** 函數(shù)名稱: UartIRQ
271 ** 輸 入: 無
272 ** 輸 出: 無
273 ** 功能描述: 串口中斷服務(wù)函數(shù)
274 *****************************************************/
275 void UartIRQ(void)interrupt 4
276 {
277 static UINT8 uccnt=0;
278 UINT8 uclen;
279 UINT16 uscrc;
280
281 if(RI) //是否接收到數(shù)據(jù)
282 {
283 RI=0;
284
285 PktCrcEx.p[uccnt++]=SBUF;//獲取單個(gè)字節(jié)
286
287
288 if(PktCrcEx.r.m_ucHead1 == DCMD_CTRL_HEAD1)//是否有效的數(shù)據(jù)幀頭部1
289 {
290 if(uccnt<CTRL_FRAME_LEN+PktCrcEx.r.m_ucDataLength+CRC16_LEN)//是否接收完所有數(shù)據(jù)
291 {
292 if(uccnt>=2 && PktCrcEx.r.m_ucHead2!=DCMD_CTRL_HEAD2)//是否有效的數(shù)據(jù)幀頭部2
293 {
294 uccnt=0;
295
296 return;
297 }
298
299 }
300 else
301 {
302
303 uclen=CTRL_FRAME_LEN+PktCrcEx.r.m_ucDataLength;//獲取數(shù)據(jù)幀有效長(zhǎng)度(不包括校驗(yàn)值)
304
305 uscrc=CRC16Check(PktCrcEx.p,uclen);//計(jì)算校驗(yàn)值
306
307 /*
308 這樣做的原因是因?yàn)橛袝r(shí)寫數(shù)據(jù)長(zhǎng)度不一樣,
309 導(dǎo)致PktCrcEx.r.m_szCrc會(huì)出現(xiàn)為0的情況
310 所以使用BufCpy將校驗(yàn)值復(fù)制到相應(yīng)的位置
311 */
312 BufCpy(PktCrcEx.r.m_szCrc,&PktCrcEx.p[uclen],CRC16_LEN);
313
314 if((UINT8)(uscrc>>8) !=PktCrcEx.r.m_szCrc[1]\
315 ||(UINT8) uscrc =PktCrcEx.r.m_szCrc[0])//校驗(yàn)值是否匹配
316 {
317 uccnt=0;
318
319 return;
320 }
321
322 switch(PktCrcEx.r.m_ucOptCode)//從命令碼中獲取相對(duì)應(yīng)的操作
323 {
324 case DCMD_CTRL_BELL://控制蜂鳴器命令碼
325 {
326 if(DCTRL_BELL_ON==PktCrcEx.r.m_szDataBuf[0])//數(shù)據(jù)部分含控制碼
327 {
328 bBellOn=TRUE;
329 }
330 else
331 {
332 bBellOn=FALSE;
333 }
334 }
335 break;
336
337 case DCMD_CTRL_LED://控制LED命令碼
338 {
339
340 if(DCTRL_LED_ON==PktCrcEx.r.m_szDataBuf[0])//數(shù)據(jù)部分含控制碼
341 {
342 bLedOn=TRUE;
343 }
344 else
345 {
346 bLedOn=FALSE;
347 }
348 }
349 break;
350
351 case DCMD_REQ_DATA://請(qǐng)求數(shù)據(jù)命令碼
352 {
353 bReqData=TRUE;
354 }
355 break;
356
357 }
358
359 uccnt=0;
360
361 return;
362 }
363
364 }
365 else
366 {
367 uccnt=0;
368 }
369
370 }
371 }
372
?
?
?代碼分析
(1)在main函數(shù)主體中,主要檢測(cè)bLedOn、bBellOn、bReqData這三個(gè)標(biāo)志位的變化,根據(jù)每個(gè)標(biāo)志位的當(dāng)前值然后進(jìn)行相對(duì)應(yīng)的操作。
(2)在UartIRQ中斷服務(wù)函數(shù)當(dāng)中,主要處理數(shù)據(jù)接收和數(shù)據(jù)校驗(yàn),當(dāng)數(shù)據(jù)校驗(yàn)成功后,
通過switch(PktCrcEx.r.m_ucOptCode)獲取命令碼,根據(jù)命令碼來設(shè)置bLedOn、bBellOn、bReqData的值。
轉(zhuǎn)載于:https://www.cnblogs.com/wenziqi/archive/2010/07/01/1769362.html
總結(jié)
以上是生活随笔為你收集整理的CRC16-循环冗余校验的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SqlCommandBuilder
- 下一篇: Samba安装配置用于虚拟机共享文件_很