/***************************************************
 *作     者:温子祺
 *联系方式: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-循环冗余校验代码如下:

 

1
2 #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-循环冗余校验相关推荐

  1. CRC16 循环冗余校验

    循环冗余校验再单片机的通讯数据传输环节用得比较多,目的是方式电讯号干扰导致得误传. #include <stdio.h> #include <stdlib.h> #includ ...

  2. 【算法集中营】循环冗余校验

    CRC的全称为Cyclic Redundancy Check,中文名称为循环冗余校验.它是一类重要的线性分组码,编码和解码方法简单,检错和纠错能力强,在通信领域广泛地用于实现差错控制.实际上,除 数据 ...

  3. CRC32(Cyclic Redundancy Check)循环冗余校验:推导

    Table of Contents 什么是循环冗余校验和CRC-32? CRC-32输出的长度是多少? CRC-8,CRC-16,CRC-32和CRC-64有什么区别? CRC32源代码 CRC32算 ...

  4. 数据链路层的检错技术——循环冗余校验CRC(Cyclic Redundancy Check)

    数据链路层的循环冗余校验CRC(Cyclic Redundancy Check) 简介 背景 原理(以CRC-16/XMODEM为例) 应用场景 CRC各版本及反转.初始值含义详解 计算过程 计算过程 ...

  5. 从原理到代码理解CRC循环冗余校验

    概述:本文详细介绍了CRC循环冗余计算的数学原理,算法中使用的参数说明,并以Modbus协议中的CRC-16算法为例,进行手算验证,同时提供LabVIEW和C语言的直接计算CRC-16 值的代码以及C ...

  6. 详述循环冗余校验CRC(附代码)

    内容包括循环校验码CRC及其生成原理介绍,CRC-16校验码的使用与其校验码计算方法,程序示例.紫色文字是超链接,点击自动跳转至相关博文.持续更新,原创不易! 目录: 一.CRC概念 1.什么是CRC ...

  7. 为了进行差错控制,必须对传送的数据帧进行校验。在局域网中广泛使用的校验方法是循环冗余校验。当接收端发现错误后采取的措施是

    为了进行差错控制,必须对传送的数据帧进行校验.在局域网中广泛使用的校验方法是循环冗余校验.当接收端发现错误后采取的措施是 (D) . A.重新计算原始数据 B.报告上层协议 C.自动纠错 D.自动请求 ...

  8. 【算法】CRC 循环冗余校验

    1.概述 循环冗余校验CRC (Cyclic Redundancy Check)是一种数据链路层的差错控制技术. 在数据的传输过程中可能会产生比特错误: 1可能变为0,0可能变为1. 在一段时间内,传 ...

  9. 进制转换及如何求校验码(海明校验码及循环冗余校验CRC码)

    文章目录 前言 一.进制转换 1.1 二进制转换为八进制数和十六进制数 1.2 任意进制数转换为十进制数 1.3 十进制转换为任意进制 二.校验码求取 2.1海明校验码 2.2循环冗余校验CRC码 总 ...

  10. crc循环冗余校验 php,crc 循环冗余校验

    --- title: crc 循环冗余校验 date: 2018-09-26 updated: 2018-10-06 --- # crc 循环冗余校验 CRC(Cyclic Redundancy Ch ...

最新文章

  1. 什么是折线图?怎样用Python绘制?怎么用?终于有人讲明白了(附代码)
  2. python Dask库安装方法
  3. 用flood测试web服务器响应时间,用Flood测试Web服务器响应时间(1)
  4. 标准化Keras:TensorFlow 2.0中的高级API指南
  5. web站点性能测试经验点滴
  6. apache ignite系列(九):ignite调优
  7. POJ1062 昂贵的聘礼(最短路径)
  8. redis 应用场景和数据类型
  9. lisp语言与python_5种语言混合编程:C++、JS、python、Lisp、汇编
  10. bug3-自定义层的注意事项
  11. HDU 2665 Kth number(主席树静态区间第K大)题解
  12. 143.根据文件头判断类型
  13. [WebApi] 捣鼓一个资源管理器--多文件上传+数据库辅助
  14. 学习日志——2019/08/18
  15. Python处理海量手机号码
  16. 数据分析|基础概念/excel/tableau自学笔记
  17. java 假币问题_假币问题-题解(Java代码)
  18. html5刮刮卡,canvas 实现刮刮卡
  19. Canvas绘制网格
  20. PX4模块设计之二:uORB消息代理

热门文章

  1. linux 利用yum源安装mysql5.7
  2. mysql 删除数据 降低_活见鬼,明明删除了数据,空间却没减少!
  3. 底层实现_Redis有序集合zset的底层实现
  4. JMM中的happens-before
  5. linux在没有x远程桌面,xmanager远程桌面控制linux
  6. oracle datafilereuse,Oracle 数据文件 reuse 属性 说明
  7. 使用 Pandas, Jinja 和 WeasyPrint,轻松创建一个 PDF 报表
  8. 远程无法连接svn服务器失败_windows vscode 远程连接linux服务器
  9. 黑苹果关机重启后蓝牙连接不上_手机要关机吗,要贴膜吗,要套壳吗?看完这条,都有答案了_政务_澎湃新闻...
  10. java解密方法,java加密,解密方法