CRC16-循环冗余校验

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

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月7日 下午3:41
下一篇 2023年2月7日 下午3:42

相关推荐