进入串口中断程序需要关闭中断吗(定时中断实现串口超时接收数据包的方法代码分享)
上节分享了串口发送一串数据到上位机及Printf串口输出重定向的实现的代码,本节分享下视频课程:单片机串行口定时中断实现超时接收一串数据的通用编程框架中的串口超时接收代码,视频课程的具体链接:https://www.ixigua.com/6843790816306004492。关于串口接收一帧数据及串口接收一串数据的基础的实现代码,就不再单独贴出了,这两个功能的代码都比较简单,需要了解基础的同学,可以参见前两期的视频课程单片机应用之单片机串行口从上位机接收一帧数据的编程实现方法和单片机应用之串行口从上位机接收一串数据的编程实现,视频课程的链接分别为:https://www.ixigua.com/6843035850968662539,https://www.ixigua.com/6843418308113334787。本次分享的代码还是有一定的实用性的,只有掌握了超时接收,后面大家学习RS485及MODBUS通信协议的时候,才会更加轻松一些。超时接收串口数据,也是我们工程中经常使用的一种程序设计方法,当然,此处的代码仅仅是完成接收数据包的功能,并没有任何的通信协议在里面,后面会分享关于自定义通信协议及通信协议解析的相关的程序代码,欢迎关注。另外,由于51单片机的内存有限,接收缓冲区并不能开辟的太大,同时因为没有采用循环队列,所以接收数据的字节数需要进行限制。关于循环队列,大家可以在网上找到相关的模块文件,只需要适当的移植,就可以完成相应的功能,此处不再过多介绍。超时接收的原理,大家要根据代码适当的思考下程序的执行流程,加深理解,主要是需要注意的是在串口中断中每接收一帧数据,就会把超时计数器清零,使其长不大,同时,在定时器中断中,又会累加该计数器,当没有新的数据帧过来时,自然该计数器会长大,当超过一定的时间,我们就认为是一包数据接收完成。如果有不是特别理解的,可以在视频课程中寻找答案,课程中有具体的分析和讲解。话不多说,上代码。主要代码包括main.c,uart.c,uart.h。delay.c和delay.h,前期代码已经有分享过,就不再贴出了。
//main.c
/****************************************************
程序功能:串口中断 定时中断实现超时接收串口数据
上位机发送一串数据,单片机串口接收并返回接收的数据
配置串口,遵循“9600,N,8,1”
波特率:9600bps @11.0592MHz
无校验位:N
数据位:8
停止位:1
****************************************************/
#include <reg51.h>
#include "delay.h"
#include "uart.h"
void Timer0Init(void);
void main()
{
Timer0Init();//定时器初始化
UartInit();//串口初始化
EA = 1;//开总中断
//提示信息
printf("Wait for Serial Communication Test Start.
");
printf("Please Send a string of data:
");
DelayXms(100);
while(1)
{
if(recv_flag)//如果接收到数据
{
recv_flag = 0;//清零接收标志位
start_timer = 0;//关软件定时器
sendString(recv_buf);//处理数据
clr_recvbuffer(recv_buf);//清除缓冲buffer
}
}
}
void Timer0Init(void) //1毫秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
ET0 = 1;//开定时器0中断
TR0 = 1; //定时器0开始计时
}
//定时器中断服务函数
void timer0_ISR() interrupt 1
{
TR0 = 0;
if(start_timer == 1)//如果软件定时器标志位置1
{
recv_timer_cnt ;//1、累加定时时间计数器
if(recv_timer_cnt > MAX_REV_TIME)//2、判断定时时间是否超过了设定的最大的阈值,
//超过则说明等待一段时间后没有新的数据到,我们
//判断一包数据接收完毕
{
recv_timer_cnt = 0;//3、清除定时计数器 处理数据 清除buffer(放到数据处理之后)
recv_cnt = 0;
recv_flag = 1;//置接收完成标志
}
}
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
TR0 = 1;//打开定时器
}
//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)
{
while(*dat != )
{
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;
}
}
/*************************************
1、中断服务函数一定是一个没有返回值的函数
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 //超时时间5*1ms = 5ms,可根据实际需要调整
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
通过proteus进行相关的仿真调试的结果如下图所示,借助虚拟串口工具,通过上位机串口调试助手,发送一串数据,单片机可以正常接收到该数据包,并将该数据包发送回上位机。大家可以根据自己的功能需求,进行相应的接收数据后的业务逻辑处理工作,举一反三。当然由于水平有限,代码难免存在不足之处,欢迎一起交流讨论,继续优化。
proteus仿真界面
虚拟串口终端监控界面
上位机串口调试助手(借助虚拟串口工具和单片机通信)
,免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。