#C51串口通讯5-#一串数据#中断定时+超时接收+接收应答+CRC校验

发布者:pi26最新更新时间:2022-09-16 来源: csdn关键字:C51  串口通讯  中断定时  超时接收  CRC校验 手机看文章 扫描二维码
随时随地手机看文章

本章前言

简介:

1.在#2章基础上(中断定时+超时接收)升级版

2.增加CRC校验方式及接收应答处理

3.指令解析,主函数执行


提示:以下是本篇文章正文内容,下面案例可供参考


一、场景

示例:

主机下发命令,从机解析并应答,CRC校验


二、编程实现

1.自定义协议

如:

image.png

##1

数据类型(功能码):

控制数码管显示0x01、蜂鸣器控制0x02


##2从机握手应答:

a.地址错误:


不予理睬


b.CRC校验正确:


完整数据返回

数据区数值显示在数码管上,仅作最大2组显示为例


c.CRC校验错误:


地址+[数据类型高位置1]+数据区+CRC


2.代码设计

第一步:#C51串口通讯2-#一串数据#定时中断实现超时接收(推荐)

样例工程的基础上继续开发,验证数据收发正确性。

第二步:添加数码管显示、led功能

第三步:验证CRC校验,样例采用CRC16-MODBUS(8005,FFFF,0000)

附上CRC代码(查表法):


 unsigned int GetCRC16(unsigned char *puchMsg, unsigned int usDataLen) 

 { 

unsigned char uchCRCHi = 0xFF ; //*高CRC字节初始化 

unsigned char uchCRCLo = 0xFF ; //*低CRC字节初始化  

unsigned long uIndex ; // CRC循环中的索引 


// CRC 高位字节值表

unsigned char code auchCRCHi[260] = { 

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 

} ; 

// CRC低位字节值表

unsigned char code  auchCRCLo[260] = { 

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 (usDataLen--)               //传输消息缓冲区 

uIndex = uchCRCLo ^ *puchMsg++ ; // 计算CRC  

uchCRCLo = uchCRCHi ^ auchCRCHi[uIndex] ; 

uchCRCHi = auchCRCLo[uIndex] ; 

return (uchCRCHi << 8 | uchCRCLo);

}


第三步:

”中断定时+超时收发”代码不再张贴,详见参考基础例程3#


void uart_ISR() interrupt 4{}

void Timer0_ISR() interrupt 1{}


第4步:

1.缓冲区数据CRC校验 2. 校验判断 3.接收应答 4. 解析


if(recv_flag)

{

recv_flag = 0;

timer_start = 0; //关掉定时器,防止T0一直在执行

for (i = 0; i < cRealLen; i++)

{

uart_Recv[i] = vbuf[i];

}


// 1.CRC校验

CRC = GetCRC16(uart_Recv, cRealLen-2); //CRCL/CRCH

CRCL = CRC & 0x00FF;

CRCH = CRC >> 8;


// 2.校验判断

if((CRCL == uart_Recv[cRealLen - 2]) && (CRCH == uart_Recv[cRealLen - 1]))

{

sendData(uart_Recv,cRealLen); //@@@@ 丢弃使用sendString(),否则遇0就停,包括校验码的0 @@@@

if(0x01 != uart_Recv[0]) //地址正确,则执行命令,否则不响应

{

;

}

else

{

Exchange_Func();

fLedShine = 1;

}

}

else //3.校验错误,数据类型高位置1,返回整个数据

{

uart_Recv[1] |= 0x80;

CRC = GetCRC16(uart_Recv, cRealLen-2); //CRCL/CRCH

CRCL = CRC & 0x00FF;

CRCH = CRC >> 8;

uart_Recv[cRealLen - 2] = CRCL;

uart_Recv[cRealLen - 1] = CRCH;

sendData(uart_Recv,cRealLen);

}

// sendString(vbuf); //test

// sendString(uart_Recv);

clr_recvbuffer(vbuf);

}


void Exchange_Func(void)

{

if(0x01 == uart_Recv[0]) //仅作演示,数据类型01控制数码管显示

{

switch(uart_Recv[1]) //数据长度代表显示的位数

{

case 1:

SEG_DisBuf[0] = 23;

SEG_DisBuf[1] = 23;

SEG_DisBuf[2] = uart_Recv[2] >> 4;

SEG_DisBuf[3] = uart_Recv[2] & 0x0F;

break;

case 2:

SEG_DisBuf[0] = uart_Recv[2] >> 4;

SEG_DisBuf[1] = uart_Recv[2] & 0x0F;

SEG_DisBuf[2] = uart_Recv[3] >> 4;

SEG_DisBuf[3] = uart_Recv[3] & 0x0F;

break;

case 3:

break;

default:break;

}

}


3.测试验证

@1 正确应答:

@2 校验错误应答

@3 数据长度改变

在这里插入图片描述
在这里插入图片描述

总结

1.在T0定时中断中的数码管动态扫描,消影的操作优先处理,WX_DIS = 0xFF,测试所用的是swicth/ case语句。

2.数码管个数*扫描间隔 控制在10ms内,显示处理函数放置在T0定时中断服务函数中,因此不要作ms级延时。时差会引起串口数据错误

3.模块化设计,测试后调用。

如网上找的代码CRC,实际测试发现高低位位置错误,可用printf()将结果打印在串口观察问题。

关键字:C51  串口通讯  中断定时  超时接收  CRC校验 引用地址:#C51串口通讯5-#一串数据#中断定时+超时接收+接收应答+CRC校验

上一篇:C51单总线时序图分析与底层编程配置(DS18B20为例)
下一篇:#C51串口通讯4-#一串数据#中断即时解析用户自定义协议(握手接收应答)

推荐阅读最新更新时间:2024-11-09 11:29

利用定时中断接收不定长度的串口数据
在使用串口时,通常会遇到一些功能,如在TFT屏幕上显示串口收到的字符串,这些字符串直接是对方printf过来的,没有任何协议,此时为了保证显示内容是一整个句子(通常句子发送会有间隔),这是我们可以用定时器进行判断是否接收完成。 以stm32f4为例,代码基于正点原子的例程,为了阅读方便,删除了部分注释。 我们需要用到定时器和串口两部分: timer.h进行定时器初始化函数的声明。 #ifndef _TIMER_H #define _TIMER_H #include sys.h void TIM7_Int_Init(u16 arr,u16 psc); #endif timer.c进行定时器的定义及中断函数说明
[单片机]
S3C2440 开发板实战(5):定时中断
一、定时器大体结构 查看芯片手册,可以找到以下定时器结构框图 从做到右看,对该图进行分析:(不考虑) Prescaler:定时器0和1共享一个8位分频器,而定时器2、3、4共享另一个8位分频器。分频器将输入的PCLK分频为:PCLK/(prescaler+1)。 Clock divider & MUX:每个定时器有一个时钟分频器,它产生5个不同的分频信号(1/2,1/4,1/8,1/16,和TCLK)。每个定时器块从时钟分频器接收自己的时钟信号,时钟分频器从相应的8位分频器接收时钟。8位分频器是可编程的,根据加载值对PCLK进行划分,存储在TCFG0和TCFG1寄存器中。此时定时器的时钟频率为:Timer input
[单片机]
S3C2440 开发板实战(5):<font color='red'>定时</font>器<font color='red'>中断</font>
#C51串口通讯1-#一串数据#接收与发送(基础概念)
一.场景 测试一帧数据(字符串、字符)、固定长度收/发(不建议),基础概念篇 1.方案设计 方案1:检测固定字节的数据,下位机接收并加1返回至上位机 方案2:检测回车符结束/固定帧尾的数据,下位机接收并加1返回至上位机 2.代码设计 2.1方案1: 2.1.1 中断服务函数传输一帧数据(1Byte),寄存器SBUF 2.1.2 建立缓冲区数组recv_buf 。其中,MAX_LENGTH***数组长度大小要大于实际接收的数据长度***。因为字符串结尾是’’,否则会出错 unsigned char recv_buf ; #define MAX_NUM 3 //一串数组数据最大的索引号 #define MAX_LEN
[单片机]
#<font color='red'>C51</font><font color='red'>串口通讯</font>1-#一串数据#<font color='red'>接收</font>与发送(基础概念)
C51---1 新建C51工程 + 2.1 并点亮LED灯 + 3.1 按键控制LED亮灭
1 新建工程 选择好目录路径后,选择 添加文件 2.1 并点亮LED灯 LED原理图 main函数代码 led1为P2_0 P2控制8位设置为1111 1110时第0位为0,LED二极管导通点亮。 #include REGX52.H void main() { P2=0xFE;//1111 1110 while(1); } 烧录后 点亮4颗led #include REGX52.H void main() { P2=0x55;//0101 0101 while(1); } 3.1 按键控制LED亮灭 原理图 main函数 按键1按下时,LED1亮
[单片机]
C51---1 新建<font color='red'>C51</font>工程 + 2.1 并点亮LED灯 + 3.1 按键控制LED亮灭
C51单片机学习笔记(四)——单片机的中断系统及应用
1.单片机的中断系统 中断的概念: CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理(中断产生); CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务); 待CPU将事件B处理完毕后,再回到原来事件A中断的地方继续处理事件A(中断返回),这一过程称为中断。 引起CPU中断的根源叫做中断源。中断源向CPU的请求,叫做中断请求。 CUP暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再 回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的 部件称为中断系统(中断机构)。 51单片机的中断源:引起中断的事件称为中断源,51单片机一共有5个中断源,如下图: 中断优先级:当单片机正在
[单片机]
<font color='red'>C51</font>单片机学习笔记(四)——单片机的<font color='red'>中断</font>系统及应用
Keil C51单片机变量的使用方法详细介绍
引言 8051内核单片机是一种通用单片机,在国内占有较大的市场份额。在将C语言用于51内核单片机的研究方面,Keil公司做得最为成功。由于51内核单片机的存储结构的特殊性,Keil C51中变量的使用与标准C有所不同。正确地使用变量,有利于获得高效的目标代码。下面详细介绍Keil C51中变量的使用方法。 1 CPU存储结构与变量的关系 变量都需要有存储空间,存储空间的不同使得变量使用时的工作效率也不同。 标准C的典型运行环境是8086(含IA-32系列)内核,其存储结构是CPU内部有寄存器,外部有存储器,寄存器的访问速度大大高于存储器的访问速度。在标准C中,不加特别定义的变量是放在存储器中的,使用register可以强制变量
[单片机]
Keil <font color='red'>C51</font>单片机变量的使用方法详细介绍
C51(DHT11)温湿度+LCD1602
前言:软件延时真的很坑,校园网真的很烂 直接上码 LCD1602部分 (1).LCD1602.C #include reg52.h #include LCD1602.h #include INTRINS.h sbit LCD_RS=P2^6; sbit LCD_RW=P2^5; sbit LCD_E=P2^7; #define LCD_DatrPort P0 //指令函数 void LCD_WeitrCommand(unsigned char Command) { LCD_RS=0;//0写指令,1写数据 LCD_RW=0;//write mode LCD_DatrPort=Command; LCD_E
[单片机]
<font color='red'>C51</font>(DHT11)温湿度+LCD1602
定时中断程序-AVR STUDIO6-ATMEGA88PA
#include avr/io.h #include avr/interrupt.h unsigned int CNT = 0; void timer0_init() { TCCR0A = 0x00; //普通模式 TCCR0B = 0x00; //停止计数 TCNT0 = 0xFA; //计数初值,为了单步时能快点计数溢出,就用0xFA作初值了 TIMSK0 = 0x01; //计数溢出使能 } int main(void) { timer0_init(); TCCR0B = 0x01; //计数频率=系统时钟频率 sei(); //开启全局中断 while(1) { ;//TODO
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved