单片机RS485通讯的程序,工作中的成熟应用案例。分享给大家。
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
由于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<<RXC0)));
ModbusFunctionUnion.Data[IsrCount++]=UDR0;
}
单片机源程序如下:
所有资料51hei提供下载:
01.02.26通讯.rar
(77.34 KB, 下载次数: 479)
|