仿真原理图如下
由于MODBUS-RTU 要求每帧信息中的数据间隔时间不得超过1.5字符的静止时间。
因此:
(1)、当串口产生接收中断后,立即重装“超时定时器”的初始值。(注:超时定时器的初始值和波特率有关)
问题如下:
(a)、由于要求在串口接收中断中重装超时定时器初始值,那么,该超时定时器就只能采用“溢出模式”,而不能采用“CTC模式”
(b)、超时定时器必须在程序初始化时就一直开启。
我的理解对吗?
(2)两帧之间的3.5字符的静止时间该如何实现呢?
接收到每个字节的时候,初始化定时器就行,最后一个字节后,定时器就溢出了
利用单独的软件定时器,来判断一帧接收报文结束,可以防止若报文接收不完整,该帧通信任务无法结束而影响下一帧的接收。
由于一帧报文中字节与字节之间的时间间隔和帧与帧之间的时间间隔相比要小得多,因此每当接收一个新字节,就启动软件定时器开始计时,定时器的时间设定为帧与帧的最小时间间隔。波特率不同,该时间间隔也不同。若不到预定的时间内又接收到下一个字节,则说明一帧报文未结束,定时器重新计时;若定时器顺利计数到预定时间,就会触发相应的中断号,在该定时器中断子程序中设定帧结束标志字节,表明一帧报文接收完毕。当主程序内检测到一帧报文接收完毕后,会通过核查从方地址及循环冗余校验字节是否正确来判断该帧的有效性。若确定接收到的是一帧发送给已方的正确报文,则会根据报文内的功能码对该帧命令进行相应的处理,并准备发送帧。
上面就是解决以下两个问题的方法
(1)、当前帧两个字节之间的1.5字符静止时间
(2)、两帧之间3.5字符的静止时间
这里有个SPI串口扩展芯片uCSU122P,内置MODBUS引擎,DIP28,或许对你有用点击此处下载 (原文件名:ucmu2_dat_v122.pdf)
其实这个时间不用这么准确啦,因为是问答式的协议,你可以以某个定时时间查询串口缓冲区字符的长度,如果两次读入的长度一样就认为一帧结束了,这个查询间隔根据波特率微调,就是3.5个字符时间。
7楼的做法不严格,如果第一次定时查询的时候正在收最后一个字节,第二次查显然收完了,第三次查数据不变,那么就导致了7个字符的间隔,如果对方在3.5~7字符之间又来了数据,就麻烦了;
T1.5和T3.5最严格的方法还是开定时器,但是可以灵活一点;低波特率(<19200)的时候严格定时,和波特率相关;高波特率(>19200)的时候就固定定时(T1.5=750us,T3.5=1750us),这样降低了CPU中断响应的负担。
给你这个程序片段应该可以解决你的问题,我的程序经过严格的测试,高扫描周期、波特率19200下连续运行了一个星期,没出一个错误
#pragma interrupt_handler Timer1:iv_TIMER1_OVF
void Timer1(void)
{
unsigned short CRC;
TCNT1=65525-51*11;//65535-(11*(ubbr+1)) 波特率9600
if(CNT<8)
{
CNT++;
if(CNT==4)
{
ModBusQueryDataLong=IsrCount;
IsrCount=0;
}
else if(CNT==8)
{
if(ModBusQueryDataLong>2)
{
CRC=CRC16((unsigned char *)&ModbusFunctionUnion,ModBusQueryDataLong-2);
if((ModbusFunctionUnion.Data[ModBusQueryDataLong-2]==MSB(CRC))&&
(ModbusFunctionUnion.Data[ModBusQueryDataLong-1]==LSB(CRC)))
{
FrameStatu=1;
}
}
}
}
}
#pragma interrupt_handler UART_isr:iv_USART0_RX
void UART_isr(void)
{
CNT=0;
while(!(UCSR0A&(1<
}
单片机源程序如下:
/*------------------------------------------------------------------------------
MEASURE.C: Remote Measurement Recorder using the C51 COMPILER
Copyright 1990-2005 Keil Software, Inc.
------------------------------------------------------------------------------*/
#include "includes.h"
sbit CS=P1^0; //看门狗端口
sbit ctrl_485=P1^5; //RS485控制端口
unsigned char RxBuf[LenRxBuf]; //接收缓冲区
unsigned char *inRxBuf,*outRxBuf; //接收缓冲区指针
/***************判断静止字符**************/
unsigned char time_3;
unsigned char time_2;
bit rt_rxbuf;
bit rt_ctrl;
/***************接收字符**************/
unsigned char ComBuf[MaxLenComBuf+1] ;
unsigned char ch;
unsigned char k1;
unsigned char n1;
int i=-1;
int State=StatInputCom;
/*
*********************************************************************************************************
* 串口初始化函数
*********************************************************************************************************
*/
void InitSerial()
{
TMOD=TMOD&0x0F;
TMOD=TMOD|0x20;
TH1=0xF4;//2400 , 11.0592MHz
TL1=0xF4,
SCON=0x50;PCON=0x00;
TR1=1;
}
/*
*********************************************************************************************************
* 缓冲区初始化(只定义接收缓冲区)
*********************************************************************************************************
*/
void InitSerialBuffer(void) //串口缓冲区初始化
{
inRxBuf=RxBuf;outRxBuf=RxBuf;
ctrl_485=0; //接收模式
ES=1;
EA=1;
}
/*
*********************************************************************************************************
* 定时器函数
*********************************************************************************************************
*/
void timefunc(void)
{ unsigned char k;
unsigned char *tt;
if(!rt_ctrl) time_2++; //判断静止时间,配合下面添加结束符
time_3++;
if(time_2>20&&!rt_ctrl)
{ //ES=0;
for(k=0;k<4;k++)
{
tt=inRxBuf;tt++; //数据暂存 5555
if(tt==RxBuf+LenRxBuf) tt=RxBuf; // 5555
if(tt==outRxBuf) {break;} //如果缓冲期满停止加结束标志 5555
else
{
if(k==0)*inRxBuf=0x4A ;
if(k==1)*inRxBuf=0x59 ;
if(k==2)*inRxBuf=0x48 ;
if(k==3)*inRxBuf=0x59 ;
inRxBuf=tt;
} // 5555
}
rt_ctrl=1;
// ES=1;
}
// time_1++;
// time_4++;//接收数据时间判断
}
/*
*********************************************************************************************************
* 串口接收
*********************************************************************************************************
*/
void serial(void) //串口中断服务子程序
{
unsigned char *t;
if(RI)
{
RI=0;
if(time_3>4) //判断静止时间
{ time_3=0;
// rt_rxbuf=0;
rt_rxbuf=1; // if(SBUF==0x01)rt_rxbuf=1;
// time_3的作用是为判断静止字符之后必须为i本机地址,否则不接受数据,判断下一个静止字符
}
上一篇:51单片机汇编学习例程(1)——KEY篇
下一篇:自己制作的单片机万年历 程序+原理图
推荐阅读最新更新时间:2024-11-09 17:11
设计资源 培训 开发板 精华推荐
- STEVAL-IPT005V1,基于 ST8034P 的智能卡接口评估板
- RT9179、300mA LDO可调稳压器典型应用电路
- LT6654BHS6-5 扩展电源范围电压基准的典型应用
- LTC1930ES5/LTC1931ES5 演示板、1.2 MHz、DC/DC 转换器
- 【CW32】BLDC无刷驱动器
- REP023: 从350MHz变频至3.5GHz具有9dB增益和1.3dBm IIP3的上变频器
- 适用于工业应用的 C8051F503 MCU 的 C8051F500DK、8051 开发系统
- 采用高频 LCC 变压器的 100W LED 驱动器评估板,实现低成本小型化设计
- MAXREFDES1172:采用MAX17690的90%效率的小尺寸12V / 1A无光反激式DC-DC转换器
- 使用 NXP Semiconductors 的 ISP1705 的参考设计