分享-基于CRC16校验串口超时接收用户自定义通讯协议的编程代码

351 阅读7分钟

`/*


  • 文件名:main.c
  • 描 述:主程序
  • 版本号:
  • 备 注:

/ /*********************************************************************************** 功能:带CRC校验的 自定义通讯协议 01 01 02 03 CRC16/MODBUS 地址 功能字 数据 校验字

	例如:      
		01  01  80  80  30  78  LED点亮      0x8080毫秒		
	  01  02  80  80  C0  78  蜂鸣器鸣响   0x8080毫秒	
CRC16.c中的CRC校验算法计算得到的CRC校验字和在线工具中
	计算的CRC校验字高低字节正好相反。
CRC在线计算工具网址:http://www.ip33.com/crc.html
	可以根据自己的应用需求进行协议的扩展,如控制多路继电器,
	同时还可以扩展到RS485多从机系统中使用

**************************************************************************************/ //头文件 #include <reg51.h> #include "delay.h" #include "uart.h" #include "CRC16.h" #include "time.h"

//本机地址 #define LOCAL_ADRRESS 0x01

void main(void) { unsigned char i; unsigned int crc; unsigned char crch,crcl; Timer0Init();//定时器初始化 UartInit();//串口初始化 EA = 1;//开总中断 DelayXms(10); while(1) { if(recv_flag)//接收到数据 { recv_flag = 0;//清零接收标志位 start_timer = 0;//关软件定时器 //校验本机地址,是本机的,则接收处理,否则退出 if(recv_buf[0] != LOCAL_ADRRESS) { return; } //CRC16校验,校验正确,我们才处理,否则直接返回,并给出错误码 crc = GetCRC16(recv_buf,recv_cnt - 2); crch = crc >> 8; crcl = crc & 0xFF; if((recv_buf[recv_cnt - 2] != crch) || (recv_buf[recv_cnt - 1] != crcl)) { recv_buf[1] = recv_buf[1] | 0x80; crc = GetCRC16(recv_buf,recv_cnt - 2); recv_buf[4] = crc & 0xFF; recv_buf[5] = crc >> 8; for(i = 0;i<recv_cnt;i++) { sendByte(recv_buf[i]); } return; } switch(recv_buf[1])//命令字/功能字判断 { case 1:led_data = recv_buf[2]; led_data = led_data << 8; led_data = led_data + recv_buf[3];//取数据 led_cnt = 0;//目的是使LED点亮上述接收的数据的时间 break; case 2:beep_data = recv_buf[2]; beep_data = beep_data << 8; beep_data = beep_data + recv_buf[3];//取数据 beep_cnt = beep_data;//蜂鸣器鸣响时间数据 break; default:break; } //返回接收数据 测试用 for(i = 0;i<recv_cnt;i++) { sendByte(recv_buf[i]); } clr_recvbuffer(recv_buf);//清除缓冲buffer recv_cnt = 0; } } } /*


  • 文件名:time.c
  • 描 述:定时/计数器0初始化及中断处理
  • 版本号:
  • 备 注:

*/ #include "time.h" #include "uart.h"

//全局变量定义 unsigned int led_data; unsigned int beep_data; unsigned int led_cnt; unsigned int beep_cnt;

void Timer0Init(void) //1毫秒@11.0592MHz { TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x01; //设置定时器模式 TL0 = 0x66; //设置定时初值 TH0 = 0xFC; //设置定时初值 TF0 = 0; //清除TF0标志 ET0 = 1; TR0 = 1; //定时器0开始计时 }

void timer0_ISR() interrupt 1 { TR0 = 0; if(start_timer == 1) { recv_timer_cnt++;//1、累加定时时间计数器 if(recv_timer_cnt > MAX_REV_TIME)//2、判断定时时间是否超过了设定的最大的阈值, //超过则说明等待一段时间后没有新的数据到,我们 //判断一包数据接收完毕 { recv_timer_cnt = 0;//3、清除定时计数器 处理数据 清除buffer(放到数据处理之后) recv_flag = 1; } } //LED点亮时间控制 if(led_cnt < led_data) { led_cnt++; LED = 0; } else { LED = 1; } //蜂鸣器鸣响时间控制 if(beep_cnt != 0) { beep_cnt--; BEEP = ~BEEP; } TL0 = 0x66; //设置定时初值 TH0 = 0xFC; //设置定时初值 TR0 = 1; } //time.h #ifndef TIME_H #define TIME_H

#include <reg51.h>

sbit LED = P1^0; sbit BEEP = P3^7;

extern unsigned int led_data; extern unsigned int beep_data; extern unsigned int led_cnt; extern unsigned int beep_cnt;

void Timer0Init(void);

#endif

/*************************************** *

  • 文件名:uart.c
  • 描 述:串口处理
  • 版本号:
  • 备 注: ***************************************/

#include "uart.h"

unsigned char recv_flag = 0; unsigned char start_timer = 0; unsigned char recv_buf[MAX_REV_NUM]; unsigned char recv_cnt; unsigned char recv_timer_cnt;

void UartInit(void) //9600bps@11.0592MHz { PCON &= 0x7F; //波特率不倍速 SCON = 0x50; //8位数据,可变波特率 TMOD &= 0x0F; //清除定时器1模式位 TMOD |= 0x20; //设定定时器1为8位自动重装方式 TL1 = 0xFD; //设定定时初值 TH1 = 0xFD; //设定定时器重装值 ET1 = 0; //禁止定时器1中断 ES = 1; TR1 = 1; //启动定时器1 }

void sendByte(unsigned char dat) { SBUF = dat; while(!TI); TI = 0; }

void sendString(unsigned char *dat)//Hello World! { while(*dat != '\0') { sendByte(*dat++); } }

char putchar(char c) { sendByte(c); return c; } void clr_recvbuffer(unsigned char *buf) { unsigned char i; for(i = 0;i<MAX_REV_NUM;i++) { buf[i] = 0; } }

/************************************
*中断服务函数一定是一个没有返回值的函数
*2、中断服务函数一定是没有参数的函数
*3、中断服务函数函数名后跟关键字 interrupt 
*4、interrupt n 0 - 4   5个中断源     8*n + 0003H  
*    0003H INT0 000BH T0  0013H INT1 001BH T1 0023H ES
*5、中断服务函数不能北主程序或其他程序所调用
*6、 n 后面 跟 using m (0 - 3 )工作寄存器组
***************************************/

void uart_ISR() interrupt 4 { if(RI) { RI = 0; start_timer = 1;//1、每接收一帧数据的时候,打开软件定时器,去计数 if(recv_cnt < MAX_REV_NUM) { recv_buf[recv_cnt] = SBUF;//2、接收数据到数据缓冲区,注意缓冲区的大小范围问题 recv_cnt++; } else { recv_cnt = MAX_REV_NUM; } recv_timer_cnt = 0;//3、每接收一帧数据,记得把定时计数清零 相当于是喂狗信号 //但是在定时中断里面会不断累加 } if(TI) { TI = 0; } } //uart.h #ifndef UART_H #define UART_H

#include <reg51.h> #include <stdio.h>

#define MAX_REV_NUM 20 #define MAX_REV_TIME 5

extern unsigned char start_timer; extern unsigned char recv_buf[MAX_REV_NUM]; extern unsigned char recv_cnt; extern unsigned char recv_timer_cnt; extern unsigned char recv_flag;

void UartInit(void); void sendByte(unsigned char dat); void sendString(unsigned char *dat); void clr_recvbuffer(unsigned char *buf);

#endif

/*


  • 文件名:CRC16.c
  • 描 述:通用的CRC16校验算法文件
  • 版本号:v1.0
  • 备 注:此文件代码源自网络

*/

#include "CRC16.h" /* CRC16计算函数,ptr-数据指针,len-数据长度,返回值-计算出的CRC16数值 */ unsigned int GetCRC16(unsigned char *ptr, unsigned char len) { unsigned int index; unsigned char crch = 0xFF; //高CRC字节 unsigned char crcl = 0xFF; //低CRC字节 unsigned char code TabH[] = { //CRC高位字节值表 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
} ;
unsigned char code TabL[] = { //CRC低位字节值表 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
0x43, 0x83, 0x41, 0x81, 0x80, 0x40
} ;

while (len--)  //计算指定长度的CRC
{
    index = crch ^ *ptr++;
    crch = crcl ^ TabH[index];
    crcl = TabL[index];
}

return ((crch<<8) | crcl);  

}`

//CRC16.h
#ifndef __CRC16_H__
#define __CRC16_H__

unsigned int GetCRC16(unsigned char *ptr,  unsigned char len);

#endif