RS485通信和Modbus协议

上传人:沈*** 文档编号:187097015 上传时间:2023-02-10 格式:PDF 页数:24 大小:1.56MB
收藏 版权申诉 举报 下载
RS485通信和Modbus协议_第1页
第1页 / 共24页
RS485通信和Modbus协议_第2页
第2页 / 共24页
RS485通信和Modbus协议_第3页
第3页 / 共24页
资源描述:

《RS485通信和Modbus协议》由会员分享,可在线阅读,更多相关《RS485通信和Modbus协议(24页珍藏版)》请在装配图网上搜索。

1、在工业控制、电力通讯、智能仪表等领域,通常情况下是采用串口通信的方式进行数据交换.最初采用的方式是 RS232 接口,由于工业现场比较复杂,各种电气设备会在环境中产生比较多的电磁干扰,会导致信号传输错误。除此之外,RS232 接口只能实现点对点通信,不具备联网功能,最大传输距离也只能达到几十米,不能满足远距离通信要求。而 RS485 则解决了这些问题,数据信号采用差分传输方式,可以有效的解决共模干扰问题,最大距离可以到 1200 米,并且允许多个收发设备接到同一条总线上.随着工业应用通信越来越多,1979 年施耐德电气制定了一个用于工业现场的总线协议Modbus 协议,现在工业中使用 RS48

2、5 通信场合很多都采用 Modbus 协议,本节课我们要讲解一下 RS485 通信和 Modbus 协议。单单使用一块KST-51开发板是不能够进行RS485实验的,应很多同学的要求,把这节课作为扩展课程讲一下,如果要做本课相关实验,需要自行购买 USB 转 485 通信模块。18.118.1RS485RS485 通信通信实际上在 RS485 之前 RS232 就已经诞生,但是 RS232 有几处不足的地方:1、接口的信号电平值较高,达到十几 V,容易损坏接口电路的芯片,而且和 TTL 电平不兼容,因此和单片机电路接起来的话必须加转换电路.2、传输速率有局限,不可以过高,一般到几十 Kb/s

3、就到极限了。3、接口使用信号线和 GND 与其他设备形成共地模式的通信,这种共地模式传输容易产生干扰,并且抗干扰性能也比较弱。4、传输距离有限,最多只能通信几十米。5、通信的时候只能两点之间进行通信,不能够实现多机联网通信.针对 RS232 接口的不足,就不断出现了一些新的接口标准,RS485就是其中之一,他具备以下的特点:1、我们在讲 A/D 的时候,讲过差分信号输入的概念,同时也介绍了差分输入的好处,最大的优势是可以抑制共模干扰.尤其工业现场的环境比较复杂,干扰比较多,所以通信如果采用的是差分方式,就可以有效的抑制共模干扰。而 RS485 就是一种差分通信方式,它的通信线路是两根,通常用

4、A 和 B 或者 D+和 D来表示。逻辑“1”以两线之间的电压差为+(0。26)V 表示,逻辑“0”以两线间的电压差为-(0。26)V 来表示,是一种典型的差分通信。2、RS485 通信速度快,最大传输速度可以达到10Mb/s 以上。3、RS485 内部的物理结构,采用的是平衡驱动器和差分接收器的组合,抗干扰能力也大大增加。4、传输距离最远可以达到 1200 米左右,但是他的传输速率和传输距离是成反比的,只有在 100Kb/s 以下的传输速度,才能达到最大的通信距离,如果需要传输更远距离可以使用中继。5、可以在总线上进行联网实现多机通信,总线上允许挂多个收发器,从现有的 RS485 芯片来看,

5、有可以挂 32、64、128、256 等不同个设备的驱动器。RS485 的接口非常简单,和 RS232 所使用的 MAX232 是类似的,只需要一个 RS485 转换器,就可以直接和我们单片机的 UART 串行接口连接起来,并且完全使用的是和 UART 一致的异步串行通信协议。但是由于 RS485 是差分通信,因此接收数据和发送数据是不能同时进行的,也就是说它是一种半双工通信。那我们如何判断什么时候发送,什么时候接收呢?RS485 类的芯片很多,这节课我们以 MAX485 为例讲解 RS485 通信,如图 18-1 所示。图 18-1 MAX485硬件接口MAX485 是美信(Maxim)推出

6、的一款常用 RS485 转换器。其中 5脚和 8 脚是电源引脚,6 脚和 7 脚就是 485 通信中的 A 和 B 两个引脚,而 1 脚和 4 脚分别接到我们单片机的 RXD 和 TXD 引脚上,直接使用单片机 UART 进行数据接收和发送。而 2 脚和 3 脚就是方向引脚了,其中 2 脚是低电平使能接收器,3 脚是高电平使能输出驱动器。我们把这两个引脚连到一起,平时不发送数据的时候,保持这两个引脚是低电平,让 MAX485 处于接收状态,当需要发送数据的时候,把这个引脚拉高,发送数据,发送完毕后再拉低这个引脚就可以了。为了提高RS485 的抗干扰性能,需要在靠近 MAX485 的 A 和 B

7、 引脚之间并接一个电阻,这个电阻阻值从 100 欧到 1K 都可以.在这里我们还要介绍一下如何使用 KST51 单片机开发板进行外围扩展实验。我们的开发板只能把基本的功能给同学们做出来提供实验练习,但是同学们学习的脚步不应该停留在这个实验板上.如果想进行更多的实验,就可以通过单片机开发板的扩展接口进行扩展实验。大家可以看到蓝绿色的单片机座周围有 32 个插针,这 32 个插针就是把单片机的 32 个 IO 引脚全部都引出来了。在原理图上体现出来的就是我们的 J4、J5、J6、J7 这 4 个器件,如图 182 所示。图 182 单片机扩展接口这 32 个 IO 口不是所有的 IO 口都可以用来

8、对外扩展,其中既作为数据输出,又可以作为数据输入的引脚是不可以用的,比如 P3。2、P3。4、P3.6 引脚,这三个引脚是不可用的.比如 P3。2 这个引脚,如果我们用来扩展,发送的信号如果和 DS18B20 的时序吻合,会导致DS18B20 拉低引脚,影响通信。除这 3 个 IO 口以外的其他 29 个 IO口,都可以使用杜邦线接上插针,扩展出来使用。当然了,如果把当前的 IO 口应用于扩展功能了,板子上的相应的功能就实现不了了,也就是说需要扩展功能和板载功能二选一。在进行 RS485 实验中,我们通信用的引脚必须是 P3。0 和 P3。1,此外还有一个方向控制引脚,我们使用杜邦线将其连接到

9、 P1。7 上去。RS485 的另外一端,大家可以使用一个USB 转 485 模块,用双绞线把开发板和模块上的 A 和 B 分别对应连起来,USB 那头插入电脑,然后就可以进行通信了。学习了第 13 章的实用串口通信的方法和程序后,做这种串口通信的方法就很简单了,基本是一致的.我们使用实用串口通信的思路,做了一个简单的程序,通过串口调试助手下发任意个字符,单片机接收到后在末尾添加“回车+换行”符后再送回,在调试助手上重新显示出来,先把程序贴出来。程序中需要注意的一点是:因为平常都是将 485 设置为接收状态,只有在发送数据的时候才将485 改为发送状态,所以在 UartWrite()函数开头将

10、 485 方向引脚拉高,函数退出前再拉低。但是这里有一个细节,就是单片机的发送和接收中断产生的时刻都是在停止位的一半上,也就是说每当停止位传送了一半的时候,RI 或 TI 就已经置位并且马上进入中断(如果中断使能的话)函数了,接收的时候自然不会存在问题,但发送的时候就不一样了:当紧接这向SBUF 写入一个字节数据时,UART 硬件会在完成上一个停止位的发送后,再开始新字节的发送,但如果此时不是继续发送下一个字节,而是已经发送完毕了,要停止发送并将 485 方向引脚拉低以使 485 重新处于接收状态时就有问题了,因为这时候最后的这个停止位实际只发送了一半,还没有完全完成,所以就有了UartWri

11、te()函数内DelayX10us(5)这个操作,这是人为的增加了延时 50us,这 50us 的时间正好让剩下的一半停止位完成,那么这个时间自然就是由通信波特率决定的了,为波特率周期的一半./*RS485。c 文件程序源代码*/#include reg52.hinclude cntRxd)/读取长度大于接收到的数据长度时,len=cntRxd;/读取长度设置为实际接收到的数据长度for(i=0;ilen;i+)/拷贝接收到的数据 *buf=bufRxd i;buf+;cntRxd=0;/清零接收计数器return len;/返回实际读取长度void DelayX10us(unsigned c

12、har t)/软件延时函数,延时时间(t10)usdo _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();while(t);void UartWrite(unsigned char buf,unsigned char len)/串口数据写入函数,即串口发送函数,待发送数据指针buf,数据长度 lenRS485_DIR=1;/RS485 设置为发送while(len-)/发送数据 flagOnceTxd=0;SBUF=buf;buf+;while(!flagOnceTxd);DelayX10us(5);/等待最后的停止

13、位完成,延时时间由波特率决定RS485_DIR=0;/RS485 设置为接收void UartDriver()/串口驱动函数,检测接收到的命令并执行相应动作unsigned char len;unsigned char buf30;if(cmdArrived)/有命令到达时,读取处理该命令 cmdArrived=0;len=UartRead(buf,sizeof(buf)2);/将接收到的命令读取到缓冲区中 buflen+=r;/在接收到的数据帧后添加换车换行符后发回 buflen+=n;UartWrite(buf,len);void UartRxMonitor(unsigned char m

14、s)/串口接收监控函数static unsigned char cntbkp=0;static unsigned char idletmr=0;if(cntRxd 0)/接收计数器大于零时,监控总线空闲时间 if(cntbkp!=cntRxd)/接收计数器改变,即刚接收到数据时,清零空闲计时 cntbkp=cntRxd;idletmr=0;else if(idletmr 30)/接收计数器未改变,即总线空闲时,累积空闲时间 idletmr+=ms;if(idletmr=30)/空闲时间超过 30ms 即认为一帧命令接收完毕 cmdArrived=1;/设置命令到达标志 else cntbkp=

15、0;void InterruptUART()interrupt 4 /UART中断服务函数if(RI)/接收到字节 RI=0;/手动清零接收中断标志位 if(cntRxd sizeof(bufRxd)/接收缓冲区尚未用完时,bufRxdcntRxd+=SBUF;/保存接收字节,并递增计数器if(TI)/字节发送完毕TI=0;/手动清零发送中断标志位 flagOnceTxd=1;/设置单次发送完成标志/*main.c 文件程序源代码*/#include reg52。hunsigned char T0RH=0;/T0重载值的高字节unsigned char T0RL=0;/T0重载值的低字节voi

16、d ConfigTimer0(unsigned intms);extern void ConfigUART(unsigned int baud);extern void UartRxMonitor(unsigned char ms);extern void UartDriver();void main()EA=1;/开总中断ConfigTimer0(1);/配置 T0 定时 1msConfigUART(9600);/配置波特率为 9600while(1)UartDriver();void ConfigTimer0(unsigned intms)/T0配置函数unsigned long tmp;

17、tmp=11059200/12;/定时器计数频率tmp=(tmp*ms)/1000;/计算所需的计数值tmp=65536 tmp;/计算定时器重载值tmp=tmp+34;/修正中断响应延时造成的误差T0RH=(unsigned char)(tmp 8);/定时器重载值拆分为高低字节T0RL=(unsigned char)tmp;TMOD&=0 xF0;/清零 T0 的控制位TMOD|=0 x01;/配置 T0 为模式 1TH0=T0RH;/加载 T0 重载值TL0=T0RL;ET0=1;/使能 T0 中断TR0=1;/启动 T0void InterruptTimer0()interrupt 1

18、 /T0 中断服务函数TH0=T0RH;/定时器重新加载重载值TL0=T0RL;UartRxMonitor(1);/串口接收监控现在看这种串口程序,是不是感觉很简单了呢?串口通信程序我们反反复复的使用,加上随着我们学习的模块越来越多,实践的越来越多,原先感觉很复杂的东西,现在就会感到简单了。我们的下载程序模块用的是 COM4,而 USB 转 485 虚拟的是 COM5,通信的时候我们用的是 COM5 口,如图 183 所示。图 18-3 RS485 串行通信1818。2 2ModbusModbus 通信协议介绍通信协议介绍我们前边学习 UART、I2C、SPI 这些通信协议,都是最底层的协议,

19、是“位”级别的协议。而我们在学习 13 章实用串口通信程序的时候,我们通过串口发给单片机三条指令,让单片机做了三件不同的事情,分别是buzz on”、buzz off”、和”showstr”。随着我们系统复杂性的增加,我们希望可以实现更多的指令。而指令越来越多,带来的后果就是非常杂乱无章,尤其是这个人喜欢写成”buzzon、”buzzoff,而另外一个人喜欢写成”on buzz”、off buzz”。导致不同开发人员写出来的代码指令不兼容,不同厂家的产品不能挂到一条总线上通信。随着这种矛盾的日益严重,就会有聪明人提出更合理的解决方案,提出一些标准来,今后我们的编程必须按照这个标准来,这种标准也

20、是一种通信协议,但是和 UART、I2C、SPI 通信协议不同的是,这种通信协议是字节级别的,叫做应用层通信协议。在 1979 年由 Modicon(现为施耐德电气公司的一个品牌)提出了全球第一个真正用于工业现场总线的协议,就是 Modbus 协议。1818。2.12.1 ModbusModbus 协议特点协议特点Modbus 协议是应用于电子控制器上的一种通用语言。通过此协议,控制器相互之间、控制器经由网络(例如以太网)和其他设备之间可以通信,已经成为一种工业标准。有了它,不同厂商生产的控制设备可以连成工业网络,进行集中监控。这种协议定义了一种控制器能够认识使用的数据结构,而不管它们是经过何

21、种网络进行通信的.它描述了控制器请求访问其他设备的过程,如何回应来自其他设备的请求,以及怎样侦测错误记录,它制定了通信数据的格局和内容的公共格式。在进行多机通信的时候,Modbus 协议规定每个控制器必须要知道他们的设备地址,识别按照地址发送过来的数据,决定是否要产生动作,产生何种动作,如果要回应,控制器将生成的反馈信息用Modbus协议发出。Modbus协议允许在各种网络体系结构内进行简单通信,每种设备(PLC、人机界面、控制面板、驱动程序、输入输出设备)都能使用Modbus协议来启动远程操作,一些网关允许在几种使用 Modbus 协议的总线或网络之间的通信,如图 184 所示。图 184

22、Modbus 网络体系结构实例Modbus 协议的整体架构和格式比较复杂和庞大,在我们的课程里,我们重点介绍数据帧结构和数据通信控制方式,作为一个入门级别的了解.如果大家要详细了解,或者使用 Modbus 开发相关设备,可以查阅相关的国标文件再进行深入学习。1 1。2.22.2 RTURTU 协议帧数据协议帧数据Modbus 有两种通信传输方式,一种是 ASCII 模式,一种是 RTU 模式.由于 ASCII 模式的数据字节是 7bit 数据位,51 单片机无法实现,而且应用也相对较少,所以这里我们只用 RTU 模式.两种模式相似,会用一种另外一种也就会了.一条典型的 RTU 数据帧如图 18

23、5 所示。图 185 RTU 数据帧和我们实用串口通信程序类似,我们一次发送的数据帧必须是作为一个连续的数据流进行传输。我们在实用串口通信程序中采用的方法是定义 30ms,如果接收到的数据超过了30ms 还没有接收到下一个字节,我们就认为这次的数据结束。而Modbus 的 RTU 模式规定不同数据帧之间的间隔是 3.5 个字节通信时间以上。如果在一帧数据完成之前有超过 3.5 个字节时间的停顿,接收设备将刷新当前的消息并假定下一个字节是一个新的数据帧的开始。同样的,如果一个新消息在小于 3。5 个字节时间内接着前边一个数据开始的,接收的设备将会认为它是前一帧数据的延续.这将会导致一个错误,因此

24、大家看 RTU数据帧最后还有 16bit 的 CRC 校验。起始位和结束符:图 185 上代表的是一个数据帧,前后都至少有 3.5 个字节的时间间隔,起始位和结束符实际上没有任何数据,T1-T2T3T4 代表的是时间间隔 3。5 个字节以上的时间,而真正有意义的第一个字节是设备地址.设备地址:很多同学不理解,在多机通信的时候,数据那么多,我们依靠什么判断这个数据帧是哪个设备的呢?没错,就是依靠这个设备地址字节。每个设备都有一个自己的地址,当设备接收到一帧数据后,程序首先对设备地址字节进行判断比较,如果与自己的地址不同,则对这帧数据直接不予理会,如果如果与自己的地址相同,就要对这帧数据进行解析,

25、按照之后的功能码执行相应的功能。如果地址是 0 x00,则认为是一个广播命令,就是所有的从机设备都要执行的指令。功能代码:在第二个字节功能代码字节中,Modbus 规定了部分功能代码,此外也保留了一部分功能代码作为备用或者用户自定义,这些功能码大家不需要去记忆,甚至都不用去看,直到你有用到的那天再过来查这个表格即可,如表 18-1 所示。表 18-1 Modbus 功能码功能码010203040506070809名称作用读取线圈状态取得一组逻辑线圈的当前状态(ON/OFF)读取输入状态取得一组开关输入的当前状态(ON/OFF)读取保持寄存器 在一个或多个保持寄存器中取得当前的二进制值读取输入寄

26、存器 在一个或多个输入寄存器中取得当前的二进制值强置单线圈强置一个逻辑线圈的通断状态预置单寄存器把具体二进值装入一个保持寄存器取得 8 个内部线圈的通断状态,这8 个线圈的地址由控制器读取异常状态决定,用户逻辑可以将这些线圈定义,以说明从机状态,短报文适宜于迅速读取状态回送诊断校验把诊断校验报文送从机,以对通信处理进行评鉴编程(只用于484)控询(只用于484)读取事件计数使主机模拟编程器作用,修改 PC 从机逻辑可使主机与一台正在执行长程序任务从机通信,探询该从机是否已完成其操作任务,仅在含有功能码9 的报文发送后,本功能码才发送可使主机发出单询问,并随即判定操作是否成功,尤其是该命令或其他

27、应答产生通信错误时10111213读取通信事件记 可是主机检索每台从机的ModBus 事务处理通信事件记录。如录果某项事务处理完成,记录会给出有关错误编程(184/384可使主机模拟编程器功能修改PC 从机逻辑484 584)探询(184/384484 584)强置多线圈可使主机与正在执行任务的从机通信,定期控询该从机是否已完成其程序操作,仅在含有功能13 的报文发送后,本功能码才得发送强置一串连续逻辑线圈的通断141516171819预置多寄存器把具体的二进制值装入一串连续的保持寄存器报告从机标识可使主机判断编址从机的类型及该从机运行指示灯的状态884 和 MICRO84重置通信链路可使主机

28、模拟编程功能,修改PC 状态逻辑发生非可修改错误后,是从机复位于已知状态,可重置顺序字节20212264657273119120127128255读取通用参数(584L)写入通用参数(584L)保留作扩展功能备用显示扩展存储器文件中的数据信息把通用参数写入扩展存储文件,或修改保留以备用户功留作用户功能的扩展编码能所用非法功能保留保留留作内部作用用于异常应答我们程序对功能码的处理,就是程序来检测这个字节的数值,然后根据其数值来做相应的功能处理。数据:跟在功能代码后边的是 n 个 8bit 的数据。这个 n 值的到底是多少,是功能代码来确定的,不同的功能代码后边跟的数据数量不同。举个例子,如果功能

29、码是0 x03,也就是读保持寄存器,那么主机发送数据 n 的组成部分就是:2 个字节的寄存器起始地址,加 2 个字节的寄存器数量 N。从机数据 n 的组成部分是:1 个字节的字节数,因为我们回复的寄存器的值是 2 个字节,所以这个字节数也就是2N个,再加上 2N*个寄存器的值,如图 18-6 所示。图 186 读保持寄存器数据结构CRC 校验:CRC 校验是一种数据算法,是用来校验数据对错的。CRC校验函数把一帧数据除最后两个字节外,前边所有的字节进行特定的算法计算,计算完后生成了一个16bit 的数据,作为CRC 校验码,添加在一帧数据的最后。接收方接收到数据后,同样会把前边的字节进行 CR

30、C 计算,计算完了再和发过来的 CRC 的 16bit 的数据进行比较,如果相同则认为数据正常,没有出错,如果比较不相同,则说明数据在传输中发生了错误,这帧数据将被丢弃,就像没收到一样,而发送方会在得不到回应后做相应的处理错误处理。RTU 模式的每个字节的位是这样分布的:1 个起始位、8 个数据位,最小有效位先发送、1 个奇偶校验位(如果无校验则没有这一位)、1 位停止位(有校验位时)或者 2 个停止位(无校验位时)。18.318.3ModbusModbus 多机通信例程多机通信例程给从机下发不同的指令,从机去执行不同的操作,这个就是判断一下功能码即可,和我们前边学的实用串口例程是类似的。多机

31、通信,无非就是添加了一个设备地址判断而已,难度也不是很大。我们找了一个 Modbus 调试精灵,通过设置设备地址,读写寄存器的地址以及数值数量等参数,可以直接替代串口调试助手,比较方便的下发多个字节的数据,如图 18-7 所示。我们先来就图中的设置和数据来对Modbus做进一步的分析,图中的数据来自于调试精灵与我们接下来要讲的例程之间的交互。图 18-7 Modbus 调试精灵如图:我们的 USB 转 485 模块虚拟出的是 COM5,波特率 9600,无校验位,数据位是 8 位,1 位停止位,设备地址假设为 1。写寄存器的时候,如果我们要把 01 写到一个地址是 0000 的寄存器地址里,点

32、一下“写入”,就会出现发送指令:01 06 00 00 00 0148 0A。我们来分析一下这帧数据,其中 01 是设备地址,06 是功能码,代表写寄存器这个功能,后边跟00 00 表示的是要写入的寄存器的地址,00 01 就是要写入的数据,48 0A 就是 CRC 校验码,这是软件自动算出来了。而根据 Modbus 协议,当写寄存器的时候,从机成功完成该指令的操作后,会把主机发送的指令直接返回,我们的调试精灵会接收到这样一帧数据:01 06 00 00 00 01 48 0A。假如我们现在要从寄存器地址 0002 开始读取寄存器,并且读取的数量是 2 个。点一下“读出”,就会出现发送指令:0

33、1 03 00 02 0002 65 CB。其中 01 是设备地址,03 是功能码,代表写寄存器这个功能,00 02 就是读寄存器的起始地址,后一个 00 02 就是要读取 2 个寄存器的数值,65 CB 就是 CRC 校验。而接收到的数据是:01 03 04 0000 00 00 FA 33.其中 01 是设备地址,03 是功能码,04 代表的是后边读到的数据字节数是 4 个,00 00 00 00 分别是地址为 00 02 和 00 03的寄存器内部的数据,而 FA 33 就是 CRC 校验了.似乎越来越明朗了,所谓的 Modbus 这种通信协议,无非就是主机下发了不同的指令,从机根据指令

34、的判断来执行不同的操作而已。由于我们的开发板没有 Modbus 功能码那么多相应的功能,我们在程序中定义了一个数组 regGroup5,相当于 5 个寄存器,此外又定义了第 6 个寄存器,控制蜂鸣器,通过下发不同的指令我们改变寄存器组的数据或者改变蜂鸣器的开关状态.在 Modbus 协议里寄存器的地址和数值都是 16 位的,即 2 个字节,我们默认高字节是0 x00,低字节就是数组regGroup对应的值.其中地址0 x0000到0 x0004对应的就是regGroup 数组中的元素,我们写入的同时把数字又显示到我们的LCD1602 液晶上,而 0 x0005 这个地址,写入0 x00,蜂鸣器

35、就不响,写入任何其他数字,蜂鸣器就报警。我们单片机的主要工作也就是解析串口接收的数据执行不同操作,也就是主要在RS485。C 这个文件中了/*RS485。c 文件程序源代码*/include reg52.h#include intrins。hsbit RS485_DIR=P17;/RS485 方向选择引脚bit flagOnceTxd=0;/单次发送完成标志,即发送完一个字节bit cmdArrived=0;/命令到达标志,即接收到上位机下发的命令unsigned char cntRxd=0;unsigned char pdatabufRxd40;/串口接收缓冲区unsigned char r

36、egGroup5;/Modbus 寄存器组,地址为 0 x000 x04extern bit flagBuzzOn;extern void LcdShowStr(unsigned char x,unsigned char y,const unsigned char str);extern unsigned int GetCRC16(unsigned char ptr,unsigned char len);void ConfigUART(unsigned int baud)/串口配置函数,baud 为波特率RS485_DIR=0;/RS485 设置为接收方向SCON=0 x50;/配置串口为模式

37、 1TMOD=0 x0F;/清零 T1 的控制位TMOD=0 x20;/配置 T1 为模式 2TH1=256-(11059200/12/32)/baud;/计算 T1 重载值TL1=TH1;/初值等于重载值ET1=0;/禁止 T1 中断ES =1;/使能串口中断TR1=1;/启动 T1unsigned char UartRead(unsigned char*buf,unsigned char len)/串口数据读取函数,数据接收指针 buf,读取数据长度 len,返回值为实际读取到的数据长度unsigned char i;if(len cntRxd)/读取长度大于接收到的数据长度时,len=c

38、ntRxd;/读取长度设置为实际接收到的数据长度for(i=0;i 8;crcl=crc&0 xFF;if(buflen-2=crch)(buflen1=crcl))/判断 CRC 校验是否正确 switch(buf1)/按功能码执行操作 case 0 x03:/读取一个或连续的寄存器 if(buf2=0 x00)&(buf3=0 x05)/寄存器地址支持0 x00000 x0005 if(buf3=0 x04)i=buf3;/提取寄存器地址 cnt=buf5;/提取待读取的寄存器数量 buf 2=cnt2;/读取数据的字节数,为寄存器数2,因 Modbus定义的寄存器为 16 位 len=3

39、;while(cnt-)buflen+=0 x00;/寄存器高字节补 0 buflen+=regGroup i+;/低字节 else /地址 0 x05 为蜂鸣器状态 buf2=2;/读取数据的字节数 buf3=0 x00;buf4=flagBuzzOn;len=5;break;else /寄存器地址不被支持时,返回错误码 buf1=0 x83;/功能码最高位置 1buf2=0 x02;/设置异常码为 02无效地址len=3;break;case 0 x06:/写入单个寄存器 if(buf2=0 x00)&(buf3=0 xA)str1=cnt 0 xA+A;else str1=cnt+0;s

40、tr2=0;LcdShowStr(i*3,0,str);else /地址 0 x05 为蜂鸣器状态 flagBuzzOn=(bit)buf5;/寄存器值转换为蜂鸣器的开关len-=2;/长度2 以重新计算 CRC 并返回原帧break;else /寄存器地址不被支持时,返回错误码 buf1=0 x86;/功能码最高位置 1buf2=0 x02;/设置异常码为 02无效地址len=3;break;default:/其它不支持的功能码 buf1|=0 x80;/功能码最高位置 1 buf2=0 x01;/设置异常码为 01无效功能 len=3;break;crc=GetCRC16(buf,len)

41、;/计算 CRC 校验值buflen+=crc 8;/CRC 高字节buflen+=crc 0 xFF;/CRC 低字节UartWrite(buf,len);/发送响应帧 void UartRxMonitor(unsigned char ms)/串口接收监控函数static unsigned char cntbkp=0;static unsigned char idletmr=0;if(cntRxd 0)/接收计数器大于零时,监控总线空闲时间 if(cntbkp!=cntRxd)/接收计数器改变,即刚接收到数据时,清零空闲计时 cntbkp=cntRxd;idletmr=0;else if(i

42、dletmr 5)/接收计数器未改变,即总线空闲时,累积空闲时间idletmr+=ms;if(idletmr=5)/空闲时间超过 4 个字节传输时间即认为一帧命令接收完毕 cmdArrived=1;/设置命令到达标志 else cntbkp=0;void InterruptUART()interrupt 4 /UART中断服务函数if(RI)/接收到字节 RI=0;/手动清零接收中断标志位 if(cntRxd sizeof(bufRxd))/接收缓冲区尚未用完时,bufRxdcntRxd+=SBUF;/保存接收字节,并递增计数器 if(TI)/字节发送完毕 TI=0;/手动清零发送中断标志位

43、flagOnceTxd=1;/设置单次发送完成标志/*lcd1602.c 文件程序源代码*/include reg52。hdefine LCD1602_DB P0sbit LCD1602_RS=P10;sbit LCD1602_RW=P11;sbit LCD1602_E =P15;void LcdWaitReady()/等待液晶准备好unsigned char sta;LCD1602_DB=0 xFF;LCD1602_RS=0;LCD1602_RW=1;do LCD1602_E=1;sta=LCD1602_DB;/读取状态字 LCD1602_E=0;while(sta&0 x80);/bit7

44、 等于 1 表示液晶正忙,重复检测直到其等于0 为止void LcdWriteCmd(unsigned char cmd)/写入命令函数LcdWaitReady();LCD1602_RS=0;LCD1602_RW=0;LCD1602_DB=cmd;LCD1602_E =1;LCD1602_E =0;void LcdWriteDat(unsigned char dat)/写入数据函数LcdWaitReady();LCD1602_RS=1;LCD1602_RW=0;LCD1602_DB=dat;LCD1602_E =1;LCD1602_E =0;void LcdShowStr(unsigned c

45、har x,unsigned char y,const unsigned char str)/显示字符串,屏幕起始坐标(x,y),字符串指针 strunsigned char addr;/由输入的显示坐标计算显示RAM 的地址if(y=0)addr=0 x00+x;/第一行字符地址从 0 x00 起始else addr=0 x40+x;/第二行字符地址从 0 x40 起始/由起始显示 RAM 地址连续写入字符串LcdWriteCmd(addr|0 x80);/写入起始地址while(*str!=0)/连续写入字符串数据,直到检测到结束符 LcdWriteDat(str);str+;void L

46、cdInit()/液晶初始化函数LcdWriteCmd(0 x38);/16*2 显示,5*7 点阵,8 位数据接口LcdWriteCmd(0 x0C);/显示器开,光标关闭LcdWriteCmd(0 x06);/文字不动,地址自动+1LcdWriteCmd(0 x01);/清屏关于 CRC 校验的算法,如果不是专门学习校验算法本身,大家可以不去研究这个程序的细节,文档直接给我们提供了函数,我们直接调用即可。/*CRC16。c 文件程序源代码*/unsignedint GetCRC16(unsigned char*ptr,unsigned char len)unsignedint index;

47、unsigned char crch=0 xFF;/高 CRC 字节unsigned char crcl=0 xFF;/低 CRC 字节unsigned char code TabH=/CRC高位字节值表 0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01

48、,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81

49、,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00

50、,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81

51、,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x00

52、,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40,0 x01,0 xC0,0 x80,0 x41,0 x01,0 xC0,0 x80,0 x41,0 x00,0 xC1,0 x81,0 x40;unsigned char code TabL=/CRC 低位字节值表 0 x00,0 xC0,0 xC1,0 x01,0 xC3,0 x03,0 x02,0 xC2,0 xC6,0 x06,0 x07,0 xC7,0 x05,0 xC5,0 xC4,0 x04,0 xCC,0 x0C,0 x0D,0 xCD,0 x0F

53、,0 xCF,0 xCE,0 x0E,0 x0A,0 xCA,0 xCB,0 x0B,0 xC9,0 x09,0 x08,0 xC8,0 xD8,0 x18,0 x19,0 xD9,0 x1B,0 xDB,0 xDA,0 x1A,0 x1E,0 xDE,0 xDF,0 x1F,0 xDD,0 x1D,0 x1C,0 xDC,0 x14,0 xD4,0 xD5,0 x15,0 xD7,0 x17,0 x16,0 xD6,0 xD2,0 x12,0 x13,0 xD3,0 x11,0 xD1,0 xD0,0 x10,0 xF0,0 x30,0 x31,0 xF1,0 x33,0 xF3,0 xF2

54、,0 x32,0 x36,0 xF6,0 xF7,0 x37,0 xF5,0 x35,0 x34,0 xF4,0 x3C,0 xFC,0 xFD,0 x3D,0 xFF,0 x3F,0 x3E,0 xFE,0 xFA,0 x3A,0 x3B,0 xFB,0 x39,0 xF9,0 xF8,0 x38,0 x28,0 xE8,0 xE9,0 x29,0 xEB,0 x2B,0 x2A,0 xEA,0 xEE,0 x2E,0 x2F,0 xEF,0 x2D,0 xED,0 xEC,0 x2C,0 xE4,0 x24,0 x25,0 xE5,0 x27,0 xE7,0 xE6,0 x26,0 x22

55、,0 xE2,0 xE3,0 x23,0 xE1,0 x21,0 x20,0 xE0,0 xA0,0 x60,0 x61,0 xA1,0 x63,0 xA3,0 xA2,0 x62,0 x66,0 xA6,0 xA7,0 x67,0 xA5,0 x65,0 x64,0 xA4,0 x6C,0 xAC,0 xAD,0 x6D,0 xAF,0 x6F,0 x6E,0 xAE,0 xAA,0 x6A,0 x6B,0 xAB,0 x69,0 xA9,0 xA8,0 x68,0 x78,0 xB8,0 xB9,0 x79,0 xBB,0 x7B,0 x7A,0 xBA,0 xBE,0 x7E,0 x7F

56、,0 xBF,0 x7D,0 xBD,0 xBC,0 x7C,0 xB4,0 x74,0 x75,0 xB5,0 x77,0 xB7,0 xB6,0 x76,0 x72,0 xB2,0 xB3,0 x73,0 xB1,0 x71,0 x70,0 xB0,0 x50,0 x90,0 x91,0 x51,0 x93,0 x53,0 x52,0 x92,0 x96,0 x56,0 x57,0 x97,0 x55,0 x95,0 x94,0 x54,0 x9C,0 x5C,0 x5D,0 x9D,0 x5F,0 x9F,0 x9E,0 x5E,0 x5A,0 x9A,0 x9B,0 x5B,0 x99

57、,0 x59,0 x58,0 x98,0 x88,0 x48,0 x49,0 x89,0 x4B,0 x8B,0 x8A,0 x4A,0 x4E,0 x8E,0 x8F,0 x4F,0 x8D,0 x4D,0 x4C,0 x8C,0 x44,0 x84,0 x85,0 x45,0 x87,0 x47,0 x46,0 x86,0 x82,0 x42,0 x43,0 x83,0 x41,0 x81,0 x80,0 x40;while(len)/计算指定长度的 CRC index=crch ptr+;crch=crcl TabH index;crcl=TabL index;return((crch

58、8);/定时器重载值拆分为高低字节T0RL=(unsigned char)tmp;TMOD&=0 xF0;/清零 T0 的控制位TMOD=0 x01;/配置 T0 为模式 1TH0=T0RH;/加载 T0 重载值TL0=T0RL;ET0=1;/使能 T0 中断TR0=1;/启动 T0void InterruptTimer0()interrupt 1 /T0 中断服务函数TH0=T0RH;/定时器重新加载重载值TL0=T0RL;if(flagBuzzOn)/蜂鸣器鸣叫或关闭 BUZZ=BUZZ;else BUZZ=1;UartRxMonitor(1);/串口接收监控1818。4 4 作业作业1、了解 RS485 通信以及和 RS232 的不同用法。2、了解 Modbus 协议以及 RTU 数据帧的规则。3、写一个电子钟程序,并且可以通过 485 调试器校时。

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!