找回密码
 立即注册
查看: 138|回复: 3

关于STC8H单片机MODBUS通讯的改进

[复制链接]
已绑定手机

4

主题

4

回帖

102

积分

注册会员

积分
102
发表于 2026-9-15 12:40:26 | 显示全部楼层 |阅读模式
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位间隔时间的规定。

假设一条总线上接了很多设备,可能会有问题。

回复

使用道具 举报 送花

11

主题

148

回帖

1445

积分

金牌会员

积分
1445
发表于 2026-9-15 17:22:25 | 显示全部楼层
实际使用有空闲中断足够了,现在编程一般而言都使用DMA或中断自触发发送数据,不会一个while循环里面有很多任务,发送任务每循环一次只发一个数据的情况
微信qsssuv
回复

使用道具 举报 送花

已绑定手机

4

主题

4

回帖

102

积分

注册会员

积分
102
 楼主| 发表于 2026-9-15 21:34:28 | 显示全部楼层
MODBUS_RTU字符间间隔小于1.5个字符是可以作为同一个报文的。空闲中断只有一个字符间隔,是可以作为同一个报文内字符的。一个字符间隔一般处理是报文作废,并不作为报文结束标志。虽然相同间隔参数的设备可以正常通讯,并不等于所有场景都可用。一条总线上挂接很多来自不同厂家,不同类型设备,很有可能产生问题。在空闲中断解码然后向总线发送数据,很可能小于3.5个字符时间。工业场合是严格按MODBUS_RTU标准处理的。
回复

使用道具 举报 送花

83

主题

8317

回帖

1万

积分

超级版主

积分
17932
发表于 2026-9-16 00:06:14 | 显示全部楼层
至少35个位空闲,大于也可以的,只要双方协议好,比如9600波特率我常用5ms,115200我就用2ms。
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-11-8 03:07 , Processed in 0.071374 second(s), 54 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表