|
|
STC8H.PDF(STC8H系列单片机原理及应用)
18.10.11 串口中断收发-MODBUS协议 Page 1387
在这一节里有一个MODBUS_RTU通讯的例子。
MODBUS_RT报文间有一个3.5位间隔时间的规定,
由于该系列没有·超时中断,这个超时时间是用定时器实现的
这个3.5位超时时间,在这个例子里是这样实现的:
原串口中断函数:
void UART1_ISR(void) interrupt 4
{
if(RI)
{
RI=0;
if(!B_RX1_OK)//接收缓冲器空闲
{
if(RX1_cnt>=RX1_Length) RX1_cnt=0;
RX1_Buffer[RX1_cnt++]=SBUF;
RX1_TimeOut=36;//接收超时计时器,35个位时间(这里应该是波特率位,原文是这样注释的)
}
}
if(TI)
//省略
}
原定时器中断
void timer0_ISR(void) interrupt 1
{
if(RX1_TimeOut!=0)
{
if(--RX1_TimerOut==0)//超时
{
if(RX1_cnt !=0) //有数据
{
B_RX1_OK=1;//标志已收到数据块
}
}
}
}
把MODBUS_RTU的3.5位间隔时间分成35等分,
每一等分定时器都需要产生一次中断。
假如波特率为115200,数据格式:8N1,则需要8.68uS中断一次,
定时器中断非常频繁,如果有2个这样的通讯口,对8位单片机而言恐怕
系统什么事情都做不了。
并且串口不通讯时,定时器也不断产生中断,非常占用资源。
改进:
还是按上面的假设,波特率为:115200,数据格式:8N1。
MODBUS_RTU规定的报文的3.5位间隔时间,大约303.8uS,取304(可以取稍大一点305等)。
把定时器设置为这个值,把2个中断函数改成如下:
改进后串口中断函数:
void UART1_ISR(void) interrupt 4
{
if(RI)
{
RI=0;
if(!B_RX1_OK)//接收缓冲器空闲
{
if(RX1_cnt>=RX1_Length) RX1_cnt=0;
RX1_Buffer[RX1_cnt++]=SBUF;
//只有在接收到数据时,才需要开启定时器中断
TR0=0;
TH0=reloadH; //重载值高位
TL0=reloadL; //重载值低位
TR0=1;
}
}
if(TI)
//省略
}
改进后定时器·中断函数改为
void timer0_ISR(void) interrupt 1
{
B_RX1_OK=1;//标志已收到数据块
TR0=0; //报文已经接收完毕,不需要再产生中断了
}
这样改进后只有在串口接收数据时,才开启定时器中断(定时器初始化时是不开中断的),串口不工作时,定时器是停止的
并且一个报文定时器只产生一次中断(不会频繁产生中断)。
对STM32F103,GD32F103等很早以前的很多单片机也是没有串口超时中断的,
但一般有空闲中断,不过用空闲中断不符合MODBUS_RTU报文3.5位间隔时间的规定。
假设一条总线上接了很多设备,可能会有问题。
|
|