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-循環冗余校驗代碼如下:
?
?
??
???
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 //命令碼:請求數據
29
30 //----數據----
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 //命令碼:請求數據
44
45
46 #define CTRL_FRAME_LEN 0x04 //幀長度(不包含數據和校驗值)
47 #define CRC16_LEN 0x02 //檢驗值長度
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;} //蜂鳴器控制宏函數
53 #define LED(x) {if((x))P2=0x00;else P2=0xFF;}//LED控制宏函數
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 /*使用結構體對數據包進行封裝
67 *方便操作數據
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; //數據長度
75 UINT8 m_szDataBuf[16]; //數據
76
77 UINT8 m_szCrc[2]; //CRC16為2個字節
78
79 }PKT_CRC;
80
81 /*使用共用體再一次對數據包進行封裝
82 *操作數據更加方便
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; //定義數據包變量
92
93
94 BOOL bLedOn=FALSE; //定義是否點亮LED布爾變量
95 BOOL bBellOn=FALSE; //定義是否蜂鳴器響布爾變量
96 BOOL bReqData=FALSE; //定義是否請求數據布爾變量
97
98 /****************************************************
99 ** 函數名稱: CRC16Check
100 ** 輸 入: buf 要校驗的數據;
101 len 要校驗的數據的長度
102 ** 輸 出: 校驗值
103 ** 功能描述: CRC16循環冗余校驗
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 * 函數名稱:BufCpy
134 * 輸 入:dest目標緩沖區;
135 Src 源緩沖區
136 size 復制數據的大小
137 * 輸 出:無
138 * 說 明:復制緩沖區
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 ** 函數名稱: 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 ** 函數名稱: UARTSendByte
172 ** 輸 入: b 單個字節
173 ** 輸 出: 無
174 ** 功能描述: 串口 發送單個字節
175 *****************************************************/
176 void UARTSendByte(UINT8 b)
177 {
178 SBUF=b;
179 while(TI==0);
180 TI=0;
181 }
182 /****************************************************
183 ** 函數名稱: UartSendNBytes
184 ** 輸 入: buf 數據緩沖區;
185 len 發送數據長度
186 ** 輸 出: 無
187 ** 功能描述: 串口 發送多個字節
188 *****************************************************/
189 void UartSendNBytes(UINT8 *buf,UINT8 len)
190 {
191 while(len--)
192 {
193 UARTSendByte(*buf++);
194 }
195 }
196 /****************************************************
197 ** 函數名稱: main
198 ** 輸 入: 無
199 ** 輸 出: 無
200 ** 功能描述: 函數主體
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) //是否點亮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)//是否請求數據
233 {
234 bReqData=FALSE;
235
236 NOT_EN_UART(); //禁止串口中斷
237
238 PktCrcEx.r.m_ucHead1=UCMD_CTRL_HEAD1;//MCU上傳數據幀頭部1
239 PktCrcEx.r.m_ucHead2=UCMD_CTRL_HEAD2;//MCU上傳數據幀頭部2
240 PktCrcEx.r.m_ucOptCode=UCMD_REQ_DATA;//MCU上傳數據幀命令碼
241
242
243 uscrc=CRC16Check(PktCrcEx.p,
244 CTRL_FRAME_LEN+
245 PktCrcEx.r.m_ucDataLength);//計算校驗值
246
247 PktCrcEx.r.m_szCrc[0]=(UINT8) uscrc; //校驗值低字節
248 PktCrcEx.r.m_szCrc[1]=(UINT8)(uscrc>>8);//校驗值高字節
249
250 /*
251 這樣做的原因是因為有時寫數據長度不一樣,
252 導致PktCrcEx.r.m_szCrc會出現為0的情況
253 所以使用BufCpy將校驗值復制到相應的位置
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);//發送數據
263
264 EN_UART();//允許串口中斷
265
266 }
267 }
268 }
269 /****************************************************
270 ** 函數名稱: UartIRQ
271 ** 輸 入: 無
272 ** 輸 出: 無
273 ** 功能描述: 串口中斷服務函數
274 *****************************************************/
275 void UartIRQ(void)interrupt 4
276 {
277 static UINT8 uccnt=0;
278 UINT8 uclen;
279 UINT16 uscrc;
280
281 if(RI) //是否接收到數據
282 {
283 RI=0;
284
285 PktCrcEx.p[uccnt++]=SBUF;//獲取單個字節
286
287
288 if(PktCrcEx.r.m_ucHead1 == DCMD_CTRL_HEAD1)//是否有效的數據幀頭部1
289 {
290 if(uccnt<CTRL_FRAME_LEN+PktCrcEx.r.m_ucDataLength+CRC16_LEN)//是否接收完所有數據
291 {
292 if(uccnt>=2 && PktCrcEx.r.m_ucHead2!=DCMD_CTRL_HEAD2)//是否有效的數據幀頭部2
293 {
294 uccnt=0;
295
296 return;
297 }
298
299 }
300 else
301 {
302
303 uclen=CTRL_FRAME_LEN+PktCrcEx.r.m_ucDataLength;//獲取數據幀有效長度(不包括校驗值)
304
305 uscrc=CRC16Check(PktCrcEx.p,uclen);//計算校驗值
306
307 /*
308 這樣做的原因是因為有時寫數據長度不一樣,
309 導致PktCrcEx.r.m_szCrc會出現為0的情況
310 所以使用BufCpy將校驗值復制到相應的位置
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])//校驗值是否匹配
316 {
317 uccnt=0;
318
319 return;
320 }
321
322 switch(PktCrcEx.r.m_ucOptCode)//從命令碼中獲取相對應的操作
323 {
324 case DCMD_CTRL_BELL://控制蜂鳴器命令碼
325 {
326 if(DCTRL_BELL_ON==PktCrcEx.r.m_szDataBuf[0])//數據部分含控制碼
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])//數據部分含控制碼
341 {
342 bLedOn=TRUE;
343 }
344 else
345 {
346 bLedOn=FALSE;
347 }
348 }
349 break;
350
351 case DCMD_REQ_DATA://請求數據命令碼
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函數主體中,主要檢測bLedOn、bBellOn、bReqData這三個標志位的變化,根據每個標志位的當前值然后進行相對應的操作。
(2)在UartIRQ中斷服務函數當中,主要處理數據接收和數據校驗,當數據校驗成功后,
通過switch(PktCrcEx.r.m_ucOptCode)獲取命令碼,根據命令碼來設置bLedOn、bBellOn、bReqData的值。
轉載于:https://www.cnblogs.com/wenziqi/archive/2010/07/01/1769362.html
總結
以上是生活随笔為你收集整理的CRC16-循环冗余校验的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SqlCommandBuilder
- 下一篇: Samba安装配置用于虚拟机共享文件_很