登录达到上限Staticlinker上限,请先在参数设置中手动设置该数值,再加载存档?意思?

怎样可以更改电脑的配置参数?_百度知道
怎样可以更改电脑的配置参数?
用什么办法可以修改电脑里的硬盘、内存、CPU、的参数,把原本很低的配置改到很高,而且在“属性”窗口下看不出破绽?
我有更好的答案
GenuineIntel_-_x86_Family_15_Model_4\_0]右键点&_0&表项-权限-添加……说比做累;的勾去掉找到中框的&S页面文件: 8MB 已用,2040MB 可用&找到中框的&SSYSTEM\CurrentControlSet\Enum\PCI\VEN_10DE&DEV_0322&SUBSYS_&REV_A1\4&299ccbfa&0&0008]在右框能看到有你显卡在设备管理器里显示的设备名称就对了,我以现在的为教程给4&299ccbfa&0&0008加入当前用户权限************************************************************************************一:硬盘是怎么改的。8G改成了80G 二,Y; 的勾去掉找到中框的&芯片类型: GeForce FX 7800GS&在以上操作一样把&SStatic:x& 下面的 &SStatic:芯片类型;ControlSet001&#92:x&的&quot:&quot,140)和上面操作一下;system32\sysdm.cpl 打开sysdm.cpl 点&quot:CPU!上面的操作目的就是把当用操作用户添加到权限列表中 要求要完全控制权限更改右框&可见&quot修改我的电脑右键所看到的硬件信息(CPU和RAM)工具:eXeScope 6; 在中框看到有&Link Window&quot,宽度)下面不再作说明把&Static.7GHz, ~4.69GHz&Static:x&quot:x& 的 &quot,182。大家小心! 本文目的: 修改我的电脑右键属性中查看到的硬件信息 修改DXDiag程序中查看到的的硬件信息 修改设备管理器中查看到的的硬件信息 以上足以欺骗只懂一般计算机常识的人. 修改我的电脑右键所看到的硬件信息(CPU和RAM) 工具:eXeScope 6;Link Window& 字样,共有9行第3行到第6行&Static:x&quot,共有9行 第3行到第6行&quot:&-&101&quot,200)分别为(X:页面文件:& 下面的 &-&6203&-&quot:估计内存总数:& 标题改为:&估计内存总数;可见&quot:&quot:制造商;找到中框的&S-&6201&-&quot:&quot:&quot: 2048MB RAM& 附我参数(55: NVIDIA GeForce FX 7800GS&在以上操作一样把&S下面的&Static:制造商.cpl打开sysdm.cpl 点&-&对话框&中文(中国)& 标题改为;资源&-&101&quot: NVIDIA&在以上操作一样把&Static:名称:& 标题改为:&名称; 附我参数(99,208,140)和上面操作一下,把 &S资源&quot.50汉化版文件,195:处理器:&quot:芯片类型:&下面的&quot:Intel(R) Pentium4(R) CPU第8行的坐标(X,Y)为(141,192)内容为:4.7 GHz第9行的坐标(X,Y)为(141,203)内容为:2048 MB 的内存现在,再来修改在dxdiag程序中显示的硬件信息(CPU、RAM、VGA)文件; 的 &可见& 的勾去掉找到中框的&Static:&quot:%windir%\system32&#92,Y)为(141: Intel(R) Pentium4(R) CPU 4;FriendlyName&Link Window&全把&可见&的勾取消第7行的坐标(X:名称:&下面的&quot:打开dxdiag.exe 点& 字样,把 &Static:估计内存总数、内存、显卡的基本信息怎么改的? 以下是一位朋友提供的修改方法。;中文(中国)&下面的&SStatic:内存;制造商:& 标题改为;在中框看到有& 标题改为;可见&在以上操作一样把&quot,181)内容为:处理器; 附我参数(120.0 MB&quot:&-&对话框& 的 &可见& 的勾去掉修改VGA信息:内存:& 标题改为:&内存;dxdiag.exe修改CPU:%windir%\system32&#92.50汉化版 文件:%windir%\ 的 &可见& 的勾去掉找到中框的&Static:&-&对话框&Enum\PCI]下的第一个表项的第一个表项我这里是[HKEY_LOCAL_MACHINE&#92:&处理器: 1024;-&对话框&下面的&Static:x& 的 &quot:& 标题改为;字符串的数值如下:&FriendlyName&=& 的勾去掉修改设备管理器里的硬件信息(CPU、VGA)工具、RAM信息;Link Window&全把&可见&的勾取消 第7行的坐标(X,Y)为(141,181)内容为:Intel(R) Pentium4(R) CPU 第8行的坐标(X,Y)为(141,192)内容为:4.7 GHz 第9行的坐标(X,Y)为(141,203)内容为:2048 MB 的内存 现在,再来修改在dxdiag程序中显示的硬件信息(CPU、RAM、VGA) 文件:%windir%\system32\dxdiag.exe 修改CPU、RAM信息: 打开dxdiag.exe 点&资源&-&对话框&-&6201&-&中文(中国)& 找到中框的&Static:处理器:& 标题改为:&处理器: Intel(R) Pentium4(R) CPU 4.7GHz, ~4.69GHz& 附我参数(120,182,200)分别为(X,Y,宽度)下面不再作说明 把&Static:处理器:&下面的&Static:x&的&可见&的勾去掉 找到中框的&Static:内存:& 标题改为:&内存: 2048MB RAM& 附我参数(55,195,140) 和上面操作一下,把 &Static:内存:& 下面的 &Static:x& 的 &可见& 的勾去掉 找到中框的&Static:页面文件:& 标题改为:&页面文件: 8MB 已用,2040MB 可用& 附我参数(99,208,140) 和上面操作一下,把 &Static:页面文件:& 下面的 &Static:x& 的 &可见& 的勾去掉 修改VGA信息: 打开dxdiag.exe 点&资源&-&对话框&-&6203&-&中文(中国)& 找到中框的&Static:名称:& 标题改为:&名称: NVIDIA GeForce FX 7800GS& 在以上操作一样把&Static:名称:&下面的&Static:x& 的 &可见& 的勾去掉 找到中框的&Static:制造商:& 标题改为:&制造商: NVIDIA& 在以上操作一样把&Static:制造商:&下面的&Static:x& 的 &可见& 的勾去掉 找到中框的&Static:芯片类型:& 标题改为:&芯片类型: GeForce FX 7800GS& 在以上操作一样把&Static:芯片类型:&下面的&Static:x& 的 &可见& 的勾去掉 找到中框的&Static:估计内存总数:& 标题改为:&估计内存总数: 1024.0 MB& 在以上操作一样把&Static:估计内存总数:&下面的&Static:x& 的 &可见& 的勾去掉 修改设备管理器里的硬件信息(CPU、VGA)工具:注册表编辑器(regedit.exe) 打开REGEDIT 找到以下表项 [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Enum\ACPI\GenuineIntel_-_x86_Family_15_Model_4\_0] 右键点&_0&表项-权限-添加……说比做累! 上面的操作目的就是把当用操作用户添加到权限列表中 要求要完全控制权限 更改右框&FriendlyName&字符串的数值如下: &FriendlyName&=&Intel(R) Celeron(R) CPU 4.70GHz& 找到以下表项 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\PCI]下的第一个表项的第一个表项 我这里是 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\PCI\VEN_10DE&DEV_0322&SUBSYS_&REV_A1\4&299ccbfa&0&0008] 在右框能看到有你显卡在设备管理器里显示的设备名称就对了,我以现在的为教程 给4&299ccbfa&0&0008加入当前用户权限 更改键值如下: &DeviceDesc&=&NVIDIA GeForce FX 7800GS& 硬盘做假手法介绍做假大硬盘有以下常用的两种方法: 1:复制分区。用WINDOWS里面带的DOS外部命令SUBST,SUBST是一个虚拟软盘命令。例:拿SCSI 9.1G改20G为例,先分C盘为3.5G,格式化后重装系统,剩下做D盘。 然后在D盘上创建子目录E.F,再把子目录E.F隐藏。最后编辑AUTOEXEC.BAT自 动批处理文件,在此文件里面加入以下两行命令(SUBST空格E:空格D:\E) (SUBST空格F:空格D:\F)。 注意:骗子在第一行加入(@ECHO空格OFF)让人看不到他在运行什么命令,重启后,在系统里面就四个区的了。熟悉DOS 命令的朋友就会更清楚了。 2:分区表出错。用普通的分区工具就可以了,骗子一般用SFDISK,但不一定一步到位, 有时要做多次尝试。一定要先装好系统,然后再分扩展分区! 例:拿SCSI9.1G改20G为例。首先分C盘为4.5G格式化,装系统(剩下的空间先别分, 让留着)。装好系统,在DOS下运行分区工具SFDISK,分扩展分区:D盘分100M,剩下 给E盘(注意D盘不要格式化,只格式化E盘就可以了),重启,系统里面也是四个区。 有时不是一次就成功,要多做几次尝试分后面的扩展分区。假如要分成30G的话,就 在中间分多一个不用格式化的100M的分区。 为什么要用SCSI硬盘呢,就因为SCSI在CMOS里面看不到参数。用IDE盘也可以做,先插一个40G的进CMOS里面认参数并储存,接着再插上一个10G的不要再认参数,你再分区格式化,都是40G的,不过在一装系统就成了10了。如果骗子结合以上方法做,那就人不知鬼不觉的成了40G了。哈哈! 第一种方法,E.F盘不能在系统里面进行磁盘扫描。第二种方法,每个盘都可以在系统里面进行磁盘扫描。 另外在系统属性里骗子也用软件修改过CPU及内存参数的,一般菜鸟是看不出的哟! 以下是收集的几种检测办法 一:用工具测试一下 比如:EVEREST Ultimate Edition 二:进BIOS还原出厂看下!:打开dxdiag.exe 点&可见&Intel(R) Celeron(R) CPU 4:注册表编辑器(regedit.exe)打开REGEDIT 找到以下表项[HKEY_LOCAL_MACHINE\SYSTEM\资源& 的勾去掉找到中框的&CurrentControlSet\Static:页面文件.70GHz&找到以下表项[HKEY_LOCAL_MACHINE\SYSTEM\Enum\ACPI&#92:x& 的 &资源&quot
59 配电脑没有最佳,一个人一个配法,只要有钱想怎么配都行,他的配置数值是多少:AM2 4000+主版! 问题补充
21:05 双核 问题补充
21:07 想知道答案的就关注这个问题吧没有悬赏,高手是不是不屑回答啊。给你推荐一个:显示器LG19寸有款1599(现在在做活动19寸才1600很便宜牌子也正)CPU已解决问题 收藏 转载到QQ空间 家庭电脑最佳配置参数 [ 标签,麻烦回答下,呵呵 匿名 回答:4 人气:根本关系不到电脑的游戏质量:家庭,电脑 配置,参数 ] 主要是游戏,服务大家啊,次之办公,再次之摆看。向高手请教,给组数据,到市场上也好充充高手啊,随自己高兴想买多大的就买多大的:TAF4TS显卡:镭姬杀手 X1650PRO/256M内存:推荐你买2G的:24 解决时间: 22,让鄙人参考一下,因为影响游戏的主要原因就是网速和内存,我内存买时候买的512,在主城人多的时候就一顿一顿的后来又买了个1G的加上就是1,说的详细点.5G现在在主城来回跑没有一点呆滞状态,要想剩钱就买1G的。硬盘
捏鼻子呼眼睛啊,就像把自己的QQ搞六个太阳,没用的,而且硬件是多大,用检测软件一查就知道了。
用优化大师可以改部分参数
其他1条回答
为您推荐:
其他类似问题
您可能关注的内容
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。关于GCC中同时使用动态和静态库链接的操作参数和解释
&&&&&&&&&&&&&&&&&&&&
作者:梁华勇
在我们开发的一个系统中,由于动态链接其中的一个动态库时,编译时没有问题,而运行时不能进行,如果将该库静态连接时,运行却没有问题。具体什么原因,一直没有搞清楚,权且当作暂时的解决办法。
&& 如何同时同时使用动态和静态库链接,同事周楠提供了一个参数的用法,在GCC指令参数中具体参数如下:
-Wl,-Bstatic -L/usr/local/sqlite-arm-linux/.libs -lsqlite
-Wl,-Bdynamic -L/usr/local/arm/3.3.2/lib
&具体用途解释:sqlite库静态连接,其它库动态连接。
-Wl,-Bstatic
与-Wl,-Bdynamic参数,从字面意义上可以理解,有静态和动态的意思,但是具体的真正规则在查找了GCC的原版手册上有说明。
Note - if the linker is being invoked indirectly, via a compiler
driver (eg gcc) then all the linker command line
options should be prefixed by -Wl, (or whatever is
appropriate for the particular compiler driver) like this:
gcc -Wl,--startgroup foo.o bar.o -Wl,--endgroup
This is important, because otherwise the compiler driver program
may silently drop the linker options, resulting in a bad link.
实际上主要针对隐式应用LINKER的参数,用“-Wl,”来标识,,“--startgroup
foo.o bar.o -Wl,--endgroup”表示一组,,-Bstatic -Bdynamic
作为关键字与-WL,不可分,在GCC连接库时,默认链接是动态链接,现在用上面的指令限制在链接sqlite库时采用静态链接。
-Bstatic 还有三个写法: -dn和-non_shared
-Bdynamic&还有两个写法:-dy
和-call_shared
上面参数“-L/usr/local/sqlite-arm-linux/.libs
”放不放在-Wl,...之间无所谓,因为它只是提供了sqlite动静态库的位置。可以改成下面的参数形式,更直观。
-L/usr/local/sqlite-arm-linux/.libs
-L/usr/local/arm/3.3.2/lib -Wl,-dn -lsqlite
-Wl,-dn 和
-Wl,-dy成对出现才能起到标题所说的作用。&&
关于-Wl,后面的参数还有很多,全部明白我也不能。
还有一个问题值得注意,在-Wl,后面不能有空格,否则会出错!
关于-Wl,option
说明还有一段说明
GCC命令参数的英文原文
-Wl,option
Pass option as an option to the linker. If
option contains commas, it is split into multiple options at the
传递参数option作为linker的一个参数,如果option包含逗号,将在逗号处分割成几个参数。
-Wl,-dn –lsqlite
-dn 开始静态链接
-lsqlite 静态链接sqlite库
静态链接完后,然后需要动态链接
重新开始动态链接。
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)查看: 174422|回复: 49
第18章 RS485通信和Modbus协议
本教材现以连载的方式由网络发布,并将于2014年由清华大学出版社出版最终完整版,版权归作者和清华大学出版社所有。本着开源、分享的理念,本教材可以自由传播及学习使用,但是务必请注明出处来自金沙滩工作室
&&在工业控制、电力通讯、智能仪表等领域,通常情况下是采用串口通信的方式进行数据交换。最初采用的方式是RS232接口,由于工业现场比较复杂,各种电气设备会在环境中产生比较多的电磁干扰,会导致信号传输错误。除此之外,RS232接口只能实现点对点通信,不具备联网功能,最大传输距离也只能达到几十米,不能满足远距离通信要求。而RS485则解决了这些问题,数据信号采用差分传输方式,可以有效的解决共模干扰问题,最大距离可以到1200米,并且允许多个收发设备接到同一条总线上。随着工业应用通信越来越多,1979年施耐德电气制定了一个用于工业现场的总线协议Modbus协议,现在工业中使用RS485通信场合很多都采用Modbus协议,本节课我们要讲解一下RS485通信和Modbus协议。单单使用一块KST-51开发板是不能够进行RS485实验的,应很多同学的要求,把这节课作为扩展课程讲一下,如果要做本课相关实验,需要自行购买USB转485通信模块。18.1
RS485通信实际上在RS485之前RS232就已经诞生,但是RS232有几处不足的地方:1、接口的信号电平值较高,达到十几V,容易损坏接口电路的芯片,而且和TTL电平不兼容,因此和单片机电路接起来的话必须加转换电路。2、传输速率有局限,不可以过高,一般到几十Kb/s就到极限了。3、接口使用信号线和GND与其他设备形成共地模式的通信,这种共地模式传输容易产生干扰,并且抗干扰性能也比较弱。4、传输距离有限,最多只能通信几十米。5、通信的时候只能两点之间进行通信,不能够实现多机联网通信。针对RS232接口的不足,就不断出现了一些新的接口标准,RS485就是其中之一,他具备以下的特点:1、我们在讲A/D的时候,讲过差分信号输入的概念,同时也介绍了差分输入的好处,最大的优势是可以抑制共模干扰。尤其工业现场的环境比较复杂,干扰比较多,所以通信如果采用的是差分方式,就可以有效的抑制共模干扰。而RS485就是一种差分通信方式,它的通信线路是两根,通常用A和B或者D+和D-来表示。逻辑“1”以两线之间的电压差为+(0.2~6)V表示,逻辑“0”以两线间的电压差为-(0.2~6)V来表示,是一种典型的差分通信。2、RS485通信速度快,最大传输速度可以达到10Mb/s以上。3、RS485内部的物理结构,采用的是平衡驱动器和差分接收器的组合,抗干扰能力也大大增加。4、传输距离最远可以达到1200米左右,但是他的传输速率和传输距离是成反比的,只有在100Kb/s以下的传输速度,才能达到最大的通信距离,如果需要传输更远距离可以使用中继。5、可以在总线上进行联网实现多机通信,总线上允许挂多个收发器,从现有的RS485芯片来看,有可以挂32、64、128、256等不同个设备的驱动器。RS485的接口非常简单,和RS232所使用的MAX232是类似的,只需要一个RS485转换器,就可以直接和我们单片机的UART串行接口连接起来,并且完全使用的是和UART一致的异步串行通信协议。但是由于RS485是差分通信,因此接收数据和发送数据是不能同时进行的,也就是说它是一种半双工通信。那我们如何判断什么时候发送,什么时候接收呢?RS485类的芯片很多,这节课我们以MAX485为例讲解RS485通信,如图18-1所示。
psb(7).jpeg (54.51 KB, 下载次数: 277)
14:44 上传
图18-1 MAX485硬件接口MAX485是美信(Maxim)推出的一款常用RS485转换器。其中5脚和8脚是电源引脚,6脚和7脚就是485通信中的A和B两个引脚,而1脚和4脚分别接到我们单片机的RXD和TXD引脚上,直接使用单片机UART进行数据接收和发送。而2脚和3脚就是方向引脚了,其中2脚是低电平使能接收器,3脚是高电平使能输出驱动器。我们把这两个引脚连到一起,平时不发送数据的时候,保持这两个引脚是低电平,让MAX485处于接收状态,当需要发送数据的时候,把这个引脚拉高,发送数据,发送完毕后再拉低这个引脚就可以了。为了提高RS485的抗干扰性能,需要在靠近MAX485的A和B引脚之间并接一个电阻,这个电阻阻值从100欧到1K都可以。在这里我们还要介绍一下如何使用KST-51单片机开发板进行外围扩展实验。我们的开发板只能把基本的功能给同学们做出来提供实验练习,但是同学们学习的脚步不应该停留在这个实验板上。如果想进行更多的实验,就可以通过单片机开发板的扩展接口进行扩展实验。大家可以看到蓝绿色的单片机座周围有32个插针,这32个插针就是把单片机的32个IO引脚全部都引出来了。在原理图上体现出来的就是我们的J4、J5、J6、J7这4个器件,如图18-2所示。
psb(8).jpeg (43.67 KB, 下载次数: 204)
14:44 上传
图18-2 单片机扩展接口这32个IO口不是所有的IO口都可以用来对外扩展,其中既作为数据输出,又可以作为数据输入的引脚是不可以用的,比如P3.2、P3.4、P3.6引脚,这三个引脚是不可用的。比如P3.2这个引脚,如果我们用来扩展,发送的信号如果和DS18B20的时序吻合,会导致DS18B20拉低引脚,影响通信。除这3个IO口以外的其他29个IO口,都可以使用杜邦线接上插针,扩展出来使用。当然了,如果把当前的IO口应用于扩展功能了,板子上的相应的功能就实现不了了,也就是说需要扩展功能和板载功能二选一。在进行RS485实验中,我们通信用的引脚必须是P3.0和P3.1,此外还有一个方向控制引脚,我们使用杜邦线将其连接到P1.7上去。RS485的另外一端,大家可以使用一个USB转485模块,用双绞线把开发板和模块上的A和B分别对应连起来,USB那头插入电脑,然后就可以进行通信了。学习了第13章的实用串口通信的方法和程序后,做这种串口通信的方法就很简单了,基本是一致的。我们使用实用串口通信的思路,做了一个简单的程序,通过串口调试助手下发任意个字符,单片机接收到后在末尾添加“回车+换行”符后再送回,在调试助手上重新显示出来,先把程序贴出来。程序中需要注意的一点是:因为平常都是将485设置为接收状态,只有在发送数据的时候才将485改为发送状态,所以在UartWrite()函数开头将485方向引脚拉高,函数退出前再拉低。但是这里有一个细节,就是单片机的发送和接收中断产生的时刻都是在停止位的一半上,也就是说每当停止位传送了一半的时候,RI或TI就已经置位并且马上进入中断(如果中断使能的话)函数了,接收的时候自然不会存在问题,但发送的时候就不一样了:当紧接这向SBUF写入一个字节数据时,UART硬件会在完成上一个停止位的发送后,再开始新字节的发送,但如果此时不是继续发送下一个字节,而是已经发送完毕了,要停止发送并将485方向引脚拉低以使485重新处于接收状态时就有问题了,因为这时候最后的这个停止位实际只发送了一半,还没有完全完成,所以就有了UartWrite()函数内DelayX10us(5)这个操作,这是人为的增加了延时50us,这50us的时间正好让剩下的一半停止位完成,那么这个时间自然就是由通信波特率决定的了,为波特率周期的一半。/***********************RS485.c文件程序源代码*************************/#include &reg52.h&#include &intrins.h& sbit RS485_DIR = P1^7;&&//RS485方向选择引脚 bit flagOnceTxd = 0;&&//单次发送完成标志,即发送完一个字节bit cmdArrived = 0;& &//命令到达标志,即接收到上位机下发的命令unsigned char cntRxd = 0;unsigned char pdata bufRxd[40]; //串口接收缓冲区 void ConfigUART(unsigned int baud)&&//串口配置函数,baud为波特率{& & RS485_DIR = 0; //RS485设置为接收方向& & SCON = 0x50;& &//配置串口为模式1& & TMOD &= 0x0F;&&//清零T1的控制位& & TMOD |= 0x20;&&//配置T1为模式2& & TH1 = 256 - (/32) /&&//计算T1重载值& & TL1 = TH1;& &&&//初值等于重载值& & ET1 = 0;& && & //禁止T1中断& & ES&&= 1;& && & //使能串口中断& & TR1 = 1;& && & //启动T1}unsigned char UartRead(unsigned char *buf, unsigned char len) //串口数据读取函数,数据接收指针buf,读取数据长度len,返回值为实际读取到的数据长度{& && & & & if (len & cntRxd) //读取长度大于接收到的数据长度时,& & {& && &&&len = cntR //读取长度设置为实际接收到的数据长度& & }& & for (i=0; i& i++) //拷贝接收到的数据& & {& && &&&*buf = bufRxd[ i];& && &&&buf++;& & }& & cntRxd = 0;&&//清零接收计数器& & & &&&//返回实际读取长度}void DelayX10us(unsigned char t)&&//软件延时函数,延时时间(t*10)us{& & do {& && &&&_nop_();& && &&&_nop_();& && &&&_nop_();& && &&&_nop_();& && &&&_nop_();& && &&&_nop_();& && &&&_nop_();& && &&&_nop_();& && &&&} while (--t);}void UartWrite(unsigned char *buf, unsigned char len) //串口数据写入函数,即串口发送函数,待发送数据指针buf,数据长度len{& & RS485_DIR = 1;&&//RS485设置为发送& & while (len--)& &//发送数据& & {& && &&&flagOnceTxd = 0;& && &&&SBUF = *& && &&&buf++;& && &&&while (!flagOnceTxd);& & }& & DelayX10us(5);&&//等待最后的停止位完成,延时时间由波特率决定& & RS485_DIR = 0;&&//RS485设置为接收} void UartDriver() //串口驱动函数,检测接收到的命令并执行相应动作{& && & unsigned char buf[30]; & & if (cmdArrived) //有命令到达时,读取处理该命令& & {& && &&&cmdArrived = 0;& && &&&len = UartRead(buf, sizeof(buf)-2); //将接收到的命令读取到缓冲区中& && &&&buf[len++] = '\r';& &//在接收到的数据帧后添加换车换行符后发回& && &&&buf[len++] = '\n';& && &&&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 = cntR& && && && &idletmr = 0;& && &&&}& && &&&else& && &&&{& && && && &if (idletmr & 30)&&//接收计数器未改变,即总线空闲时,累积空闲时间& && && && &{& && && && && & idletmr +=& && && && && & if (idletmr &= 30)&&//空闲时间超过30ms即认为一帧命令接收完毕& && && && && & {& && && && && && &&&cmdArrived = 1; //设置命令到达标志& && && && && & }& && && && &}& && &&&}& & }& & else& & {& && &&&cntbkp = 0;& & }}void InterruptUART() interrupt 4&&//UART中断服务函数{& & & & & & if (RI)&&//接收到字节& & {& && &&&RI = 0;& &//手动清零接收中断标志位& && &&&if (cntRxd & sizeof(bufRxd)) //接收缓冲区尚未用完时,& && &&&{& && && && &bufRxd[cntRxd++] = SBUF; //保存接收字节,并递增计数器& && &&&}& & & & & & }& & & & & & if (TI)&&//字节发送完毕& & {& && &&&& & & & TI = 0;& &//手动清零发送中断标志位& && &&&flagOnceTxd = 1;&&//设置单次发送完成标志& & & & & & }}/***********************main.c文件程序源代码*************************/#include &reg52.h& unsigned char T0RH = 0;&&//T0重载值的高字节unsigned char T0RL = 0;&&//T0重载值的低字节 void ConfigTimer0(unsigned int ms);extern void ConfigUART(unsigned int baud);extern void UartRxMonitor(unsigned char ms);extern void UartDriver(); void main (){& & EA = 1;& && && &&&//开总中断& & ConfigTimer0(1);&&//配置T0定时1ms& & ConfigUART(9600); //配置波特率为9600& & & & while(1)& & {& && &&&UartDriver();& & }} void ConfigTimer0(unsigned int ms)&&//T0配置函数{& && & & & tmp =
/ 12;& && &//定时器计数频率& & tmp = (tmp * ms) / 1000;&&//计算所需的计数值& & tmp = 65536 -& && &&&//计算定时器重载值& & tmp = tmp + 34;& && && &&&//修正中断响应延时造成的误差& & & & T0RH = (unsigned char)(tmp && 8);&&//定时器重载值拆分为高低字节& & T0RL = (unsigned char)& & TMOD &= 0xF0;& &//清零T0的控制位& & TMOD |= 0x01;& &//配置T0为模式1& & TH0 = T0RH;& &&&//加载T0重载值& & TL0 = T0RL;& & ET0 = 1;& && &&&//使能T0中断& & TR0 = 1;& && &&&//启动T0}void InterruptTimer0() interrupt 1&&//T0中断服务函数{& & TH0 = T0RH;&&//定时器重新加载重载值& & TL0 = T0RL;& & UartRxMonitor(1);&&//串口接收监控}& & 现在看这种串口程序,是不是感觉很简单了呢?串口通信程序我们反反复复的使用,加上随着我们学习的模块越来越多,实践的越来越多,原先感觉很复杂的东西,现在就会感到简单了。我们的下载程序模块用的是COM4,而USB转485虚拟的是COM5,通信的时候我们用的是COM5口,如图18-3所示。
psb(9).jpeg (218.55 KB, 下载次数: 237)
14:44 上传
图18-3 RS485串行通信18.2
Modbus通信协议介绍 我们前边学习UART、I2C、SPI这些通信协议,都是最底层的协议,是“位”级别的协议。而我们在学习13章实用串口通信程序的时候,我们通过串口发给单片机三条指令,让单片机做了三件不同的事情,分别是&buzz on&、&buzz off&、和&showstr&。随着我们系统复杂性的增加,我们希望可以实现更多的指令。而指令越来越多,带来的后果就是非常杂乱无章,尤其是这个人喜欢写成&buzz on&、&buzz off&,而另外一个人喜欢写成&on buzz&、&off buzz&。导致不同开发人员写出来的代码指令不兼容,不同厂家的产品不能挂到一条总线上通信。随着这种矛盾的日益严重,就会有聪明人提出更合理的解决方案,提出一些标准来,今后我们的编程必须按照这个标准来,这种标准也是一种通信协议,但是和UART、I2C、SPI通信协议不同的是,这种通信协议是字节级别的,叫做应用层通信协议。在1979年由Modicon(现为施耐德电气公司的一个品牌)提出了全球第一个真正用于工业现场总线的协议,就是Modbus协议。18.2.1 Modbus协议特点Modbus协议是应用于电子控制器上的一种通用语言。通过此协议,控制器相互之间、控制器经由网络(例如以太网)和其他设备之间可以通信,已经成为一种工业标准。有了它,不同厂商生产的控制设备可以连成工业网络,进行集中监控。这种协议定义了一种控制器能够认识使用的数据结构,而不管它们是经过何种网络进行通信的。它描述了控制器请求访问其他设备的过程,如何回应来自其他设备的请求,以及怎样侦测错误记录,它制定了通信数据的格局和内容的公共格式。在进行多机通信的时候,Modbus协议规定每个控制器必须要知道他们的设备地址,识别按照地址发送过来的数据,决定是否要产生动作,产生何种动作,如果要回应,控制器将生成的反馈信息用Modbus协议发出。Modbus协议允许在各种网络体系结构内进行简单通信,每种设备(PLC、人机界面、控制面板、驱动程序、输入输出设备)都能使用Modbus协议来启动远程操作,一些网关允许在几种使用Modbus协议的总线或网络之间的通信,如图18-4所示。
psb(10).jpeg (179.55 KB, 下载次数: 203)
14:44 上传
图18-4 Modbus网络体系结构实例Modbus协议的整体架构和格式比较复杂和庞大,在我们的课程里,我们重点介绍数据帧结构和数据通信控制方式,作为一个入门级别的了解。如果大家要详细了解,或者使用Modbus开发相关设备,可以查阅相关的国标文件再进行深入学习。1.2.2 RTU协议帧数据Modbus有两种通信传输方式,一种是ASCII模式,一种是RTU模式。由于ASCII模式的数据字节是7bit数据位,51单片机无法实现,而且应用也相对较少,所以这里我们只用RTU模式。两种模式相似,会用一种另外一种也就会了。一条典型的RTU数据帧如图18-5所示。
psb(11).jpeg (36.14 KB, 下载次数: 197)
14:44 上传
图18-5 RTU数据帧和我们实用串口通信程序类似,我们一次发送的数据帧必须是作为一个连续的数据流进行传输。我们在实用串口通信程序中采用的方法是定义30ms,如果接收到的数据超过了30ms还没有接收到下一个字节,我们就认为这次的数据结束。而Modbus的RTU模式规定不同数据帧之间的间隔是3.5个字节通信时间以上。如果在一帧数据完成之前有超过3.5个字节时间的停顿,接收设备将刷新当前的消息并假定下一个字节是一个新的数据帧的开始。同样的,如果一个新消息在小于3.5个字节时间内接着前边一个数据开始的,接收的设备将会认为它是前一帧数据的延续。这将会导致一个错误,因此大家看RTU数据帧最后还有16bit的CRC校验。起始位和结束符:图18-5上代表的是一个数据帧,前后都至少有3.5个字节的时间间隔,起始位和结束符实际上没有任何数据,T1-T2-T3-T4代表的是时间间隔3.5个字节以上的时间,而真正有意义的第一个字节是设备地址。设备地址:很多同学不理解,在多机通信的时候,数据那么多,我们依靠什么判断这个数据帧是哪个设备的呢?没错,就是依靠这个设备地址字节。每个设备都有一个自己的地址,当设备接收到一帧数据后,程序首先对设备地址字节进行判断比较,如果与自己的地址不同,则对这帧数据直接不予理会,如果如果与自己的地址相同,就要对这帧数据进行解析,按照之后的功能码执行相应的功能。如果地址是0x00,则认为是一个广播命令,就是所有的从机设备都要执行的指令。功能代码:在第二个字节功能代码字节中,Modbus规定了部分功能代码,此外也保留了一部分功能代码作为备用或者用户自定义,这些功能码大家不需要去记忆,甚至都不用去看,直到你有用到的那天再过来查这个表格即可,如表18-1所示。表18-1 Modbus功能码功能码名称作用01读取线圈状态取得一组逻辑线圈的当前状态(ON/OFF)02读取输入状态取得一组开关输入的当前状态(ON/OFF)03读取保持寄存器 在一个或多个保持寄存器中取得当前的二进制值04读取输入寄存器在一个或多个输入寄存器中取得当前的二进制值05强置单线圈强置一个逻辑线圈的通断状态06预置单寄存器把具体二进值装入一个保持寄存器 07读取异常状态取得8 个内部线圈的通断状态,这 8 个线圈的地址由控制器决定,用户逻辑可以将这些线圈定义,以说明从机状态,短报文适宜于迅速读取状态 08回送诊断校验把诊断校验报文送从机,以对通信处理进行评鉴09编程(只用于484)使主机模拟编程器作用,修改PC从机逻辑10控询(只用于484)可使主机与一台正在执行长程序任务从机通信,探询该从机是否已完成其操作任务,仅在含有功能码 9 的报文发送后,本功能码才发送 11读取事件计数可使主机发出单询问,并随即判定操作是否成功,尤其是该命令或其他应答产生通信错误时 12读取通信事件记录可是主机检索每台从机的ModBus事务处理通信事件记录。如果某项事务处理完成,记录会给出有关错误13编程(184/384 484 584 )可使主机模拟编程器功能修改PC从机逻辑 14探询(184/384 484 584)可使主机与正在执行任务的从机通信,定期控询该从机是否已完成其程序操作,仅在含有功能13的报文发送后,本功能码才得发送15强置多线圈强置一串连续逻辑线圈的通断16预置多寄存器把具体的二进制值装入一串连续的保持寄存器17报告从机标识可使主机判断编址从机的类型及该从机运行指示灯的状态18884 和MICRO 84可使主机模拟编程功能,修改PC状态逻辑19重置通信链路发生非可修改错误后,是从机复位于已知状态,可重置顺序字节 20读取通用参数(584L)显示扩展存储器文件中的数据信息& & 21写入通用参数(584L)把通用参数写入扩展存储文件,或修改22~64保留作扩展功能备用 65~72保留以备用户功能所用留作用户功能的扩展编码 73~119非法功能 120~127保留 留作内部作用128~255保留用于异常应答& & 我们程序对功能码的处理,就是程序来检测这个字节的数值,然后根据其数值来做相应的功能处理。数据:跟在功能代码后边的是n个8bit的数据。这个n值的到底是多少,是功能代码来确定的,不同的功能代码后边跟的数据数量不同。举个例子,如果功能码是0x03,也就是读保持寄存器,那么主机发送数据n的组成部分就是:2个字节的寄存器起始地址,加2个字节的寄存器数量N*。从机数据n的组成部分是:1个字节的字节数,因为我们回复的寄存器的值是2个字节,所以这个字节数也就是2N*个,再加上2N*个寄存器的值,如图18-6所示。
psb(13).jpeg (44.74 KB, 下载次数: 178)
14:46 上传
图18-6 读保持寄存器数据结构CRC校验:CRC校验是一种数据算法,是用来校验数据对错的。CRC校验函数把一帧数据除最后两个字节外,前边所有的字节进行特定的算法计算,计算完后生成了一个16bit的数据,作为CRC校验码,添加在一帧数据的最后。接收方接收到数据后,同样会把前边的字节进行CRC计算,计算完了再和发过来的CRC的16bit的数据进行比较,如果相同则认为数据正常,没有出错,如果比较不相同,则说明数据在传输中发生了错误,这帧数据将被丢弃,就像没收到一样,而发送方会在得不到回应后做相应的处理错误处理。RTU模式的每个字节的位是这样分布的:1个起始位、8个数据位,最小有效位先发送、1个奇偶校验位(如果无校验则没有这一位)、1位停止位(有校验位时)或者2个停止位(无校验位时)。18.3
Modbus多机通信例程给从机下发不同的指令,从机去执行不同的操作,这个就是判断一下功能码即可,和我们前边学的实用串口例程是类似的。多机通信,无非就是添加了一个设备地址判断而已,难度也不是很大。我们找了一个Modbus调试精灵,通过设置设备地址,读写寄存器的地址以及数值数量等参数,可以直接替代串口调试助手,比较方便的下发多个字节的数据,如图18-7所示。我们先来就图中的设置和数据来对Modbus做进一步的分析,图中的数据来自于调试精灵与我们接下来要讲的例程之间的交互。
psb(12).jpeg (57.24 KB, 下载次数: 254)
14:44 上传
图18-7 Modbus调试精灵如图:我们的USB转485模块虚拟出的是COM5,波特率9600,无校验位,数据位是8位,1位停止位,设备地址假设为1。写寄存器的时候,如果我们要把01写到一个地址是0000的寄存器地址里,点一下“写入”,就会出现发送指令:01 06 00 00 00 01 48 0A。我们来分析一下这帧数据,其中01是设备地址,06是功能码,代表写寄存器这个功能,后边跟00 00表示的是要写入的寄存器的地址,00 01就是要写入的数据,48 0A就是CRC校验码,这是软件自动算出来了。而根据Modbus协议,当写寄存器的时候,从机成功完成该指令的操作后,会把主机发送的指令直接返回,我们的调试精灵会接收到这样一帧数据:01 06 00 00 00 01 48 0A。假如我们现在要从寄存器地址0002开始读取寄存器,并且读取的数量是2个。点一下“读出”,就会出现发送指令:01 03 00 02 00 02 65 CB。其中01是设备地址,03是功能码,代表写寄存器这个功能,00 02就是读寄存器的起始地址,后一个00 02就是要读取2个寄存器的数值,65 CB就是CRC校验。而接收到的数据是:01 03 04 00 00 00 00 FA 33。其中01是设备地址,03是功能码,04代表的是后边读到的数据字节数是4个,00 00 00 00分别是地址为00 02和00 03的寄存器内部的数据,而FA 33就是CRC校验了。似乎越来越明朗了,所谓的Modbus这种通信协议,无非就是主机下发了不同的指令,从机根据指令的判断来执行不同的操作而已。由于我们的开发板没有Modbus功能码那么多相应的功能,我们在程序中定义了一个数组regGroup[5],相当于5个寄存器,此外又定义了第6个寄存器,控制蜂鸣器,通过下发不同的指令我们改变寄存器组的数据或者改变蜂鸣器的开关状态。在Modbus协议里寄存器的地址和数值都是16位的,即2个字节,我们默认高字节是0x00,低字节就是数组regGroup对应的值。其中地址0x0000到0x0004对应的就是regGroup数组中的元素,我们写入的同时把数字又显示到我们的LCD1602液晶上,而0x0005这个地址,写入0x00,蜂鸣器就不响,写入任何其他数字,蜂鸣器就报警。我们单片机的主要工作也就是解析串口接收的数据执行不同操作,也就是主要在RS485.C这个文件中了/***********************RS485.c文件程序源代码*************************/#include &reg52.h&#include &intrins.h& sbit RS485_DIR = P1^7;&&//RS485方向选择引脚 bit flagOnceTxd = 0;&&//单次发送完成标志,即发送完一个字节bit cmdArrived = 0;& &//命令到达标志,即接收到上位机下发的命令unsigned char cntRxd = 0;unsigned char pdata bufRxd[40]; //串口接收缓冲区 unsigned char regGroup[5];&&//Modbus寄存器组,地址为0x00~0x04 extern 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 = 0x50;& &//配置串口为模式1& & TMOD &= 0x0F;&&//清零T1的控制位& & TMOD |= 0x20;&&//配置T1为模式2& & TH1 = 256 - (/32) /&&//计算T1重载值& & TL1 = TH1;& &&&//初值等于重载值& & ET1 = 0;& && & //禁止T1中断& & ES&&= 1;& && & //使能串口中断& & TR1 = 1;& && & //启动T1}unsigned char UartRead(unsigned char *buf, unsigned char len) //串口数据读取函数,数据接收指针buf,读取数据长度len,返回值为实际读取到的数据长度{& && & & & if (len & cntRxd) //读取长度大于接收到的数据长度时,& & {& && &&&len = cntR //读取长度设置为实际接收到的数据长度& & }& & for (i=0; i& i++) //拷贝接收到的数据& & {& && &&&*buf = bufRxd[ i];& && &&&buf++;& & }& & cntRxd = 0;&&//清零接收计数器& & & &&&//返回实际读取长度}void DelayX10us(unsigned char t)&&//软件延时函数,延时时间(t*10)us{& & do {& && &&&_nop_();& && &&&_nop_();& && &&&_nop_();& && &&&_nop_();& && &&&_nop_();& && &&&_nop_();& && &&&_nop_();& && &&&_nop_();& & } while (--t);}void UartWrite(unsigned char *buf, unsigned char len) //串口数据写入函数,即串口发送函数,待发送数据指针buf,数据长度len{& & RS485_DIR = 1;&&//RS485设置为发送& & while (len--)& &//发送数据& & {& && &&&flagOnceTxd = 0;& && &&&SBUF = *& && &&&buf++;& && &&&while (!flagOnceTxd);& & }& & DelayX10us(5);&&//等待最后的停止位完成,延时时间由波特率决定& & RS485_DIR = 0;&&//RS485设置为接收} void UartDriver() //串口驱动函数,检测接收到的命令并执行相应动作{& && && && & unsigned char buf[30];& & unsigned char str[4];& & unsigned int&&& & unsigned char crch, & & if (cmdArrived) //有命令到达时,读取处理该命令& & {& && &&&cmdArrived = 0;& && &&&len = UartRead(buf, sizeof(buf)); //将接收到的命令读取到缓冲区中& && &&&if (buf[0] == 0x01)&&//核对地址以决定是否响应命令,本例本机地址为0x01& && &&&{& && && && &crc = GetCRC16(buf, len-2); //计算CRC校验值& && && && &crch = crc && 8;& && && && &crcl = crc & 0xFF;& && && && &if ((buf[len-2] == crch) && (buf[len-1] == crcl)) //判断CRC校验是否正确& && && && &{& && && && && & switch (buf[1]) //按功能码执行操作& && && && && & {& && && && && && &&&case 0x03:&&//读取一个或连续的寄存器& && && && && && && && &if ((buf[2] == 0x00) && (buf[3] &= 0x05)) //寄存器地址支持0x0000~0x0005& && && && && && && && &{& && && && && && && && && & if (buf[3] &= 0x04)& && && && && && && && && & {& && && && && && && && && && &&&i = buf[3];& && &//提取寄存器地址& && && && && && && && && && &&&cnt = buf[5];& & //提取待读取的寄存器数量& && && && && && && && && && &&&buf[2] = cnt*2;&&//读取数据的字节数,为寄存器数*2,因Modbus定义的寄存器为16位& && && && && && && && && && &&&len = 3;& && && && && && && && && && &&&while (cnt--)& && && && && && && && && && &&&{& && && && && && && && && && && && &buf[len++] = 0x00;& && &//寄存器高字节补0& && && && && && && && && && && && &buf[len++] = regGroup[ i++]; //低字节& && && && && && && && && && &&&}& && && && && && && && && & }& && && && && && && && && & else&&//地址0x05为蜂鸣器状态& && && && && && && && && & {& && && && && && && && && && &&&buf[2] = 2;&&//读取数据的字节数& && && && && && && && && && &&&buf[3] = 0x00;& && && && && && && && && && &&&buf[4] = flagBuzzOn;& && && && && && && && && && &&&len = 5;& && && && && && && && && & }& && && && && && && && && && && && && && && && && &}& && && && && && && && &else&&//寄存器地址不被支持时,返回错误码& && && && && && && && &{& && && && && && && && && & buf[1] = 0x83;&&//功能码最高位置1& && && && && && && && && & buf[2] = 0x02;&&//设置异常码为02-无效地址& && && && && && && && && & len = 3;& && && && && && && && && && && && && && && && && &}& && && && && && && && && && && && && && &&&case 0x06:&&//写入单个寄存器& && && && && && && && &if ((buf[2] == 0x00) && (buf[3] &= 0x05)) //寄存器地址支持0x0000~0x0005& && && && && && && && &{& && && && && && && && && & if (buf[3] &= 0x04)& && && && && && && && && & {& && && && && && && && && && &&&i = buf[3];& && && && & //提取寄存器地址& && && && && && && && && && &&&regGroup[ i] = buf[5];& &//保存寄存器数据& && && && && && && && && && &&&cnt = regGroup[ i] && 4; //显示到液晶上& && && && && && && && && && &&&if (cnt &= 0xA)& && && && && && && && && && && && &str[0] = cnt - 0xA + 'A';& && && && && && && && && && &&&else& && && && && && && && && && && && &str[0] = cnt + '0';& && && && && && && && && && &&&cnt = regGroup[ i] & 0x0F;& && && && && && && && && && &&&if (cnt &= 0xA)& && && && && && && && && && && && &str[1] = cnt - 0xA + 'A';& && && && && && && && && && &&&else& && && && && && && && && && && && &str[1] = cnt + '0';& && && && && && && && && && &&&str[2] = '\0';& && && && && && && && && && &&&LcdShowStr(i*3, 0, str);& && && && && && && && && & }& && && && && && && && && & else&&//地址0x05为蜂鸣器状态& && && && && && && && && & {& && && && && && && && && && &&&flagBuzzOn = (bit)buf[5]; //寄存器值转换为蜂鸣器的开关& && && && && && && && && & }& && && && && && && && && & len -= 2; //长度-2以重新计算CRC并返回原帧& && && && && && && && && && && && && && && && && &}& && && && && && && && &else&&//寄存器地址不被支持时,返回错误码& && && && && && && && &{& && && && && && && && && & buf[1] = 0x86;&&//功能码最高位置1& && && && && && && && && & buf[2] = 0x02;&&//设置异常码为02-无效地址& && && && && && && && && & len = 3;& && && && && && && && && && && && && && && && && &}& && && && && && && && && && && && && && &&&default:&&//其它不支持的功能码& && && && && && && && &buf[1] |= 0x80;&&//功能码最高位置1& && && && && && && && &buf[2] = 0x01;& &//设置异常码为01-无效功能& && && && && && && && &len = 3;& && && && && && && && && && && && && & }& && && && && & crc = GetCRC16(buf, len); //计算CRC校验值& && && && && & buf[len++] = crc && 8;& & //CRC高字节& && && && && & buf[len++] = crc & 0xFF;&&//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 = cntR& && && && &idletmr = 0;& && &&&}& && &&&else& && &&&{& && && && &if (idletmr & 5)&&//接收计数器未改变,即总线空闲时,累积空闲时间& && && && &{& && && && && & idletmr +=& && && && && & if (idletmr &= 5)&&//空闲时间超过4个字节传输时间即认为一帧命令接收完毕& && && && && & {& && && && && && &&&cmdArrived = 1; //设置命令到达标志& && && && && & }& && && && &}& && &&&}& & }& & else& & {& && &&&cntbkp = 0;& & }}void InterruptUART() interrupt 4&&//UART中断服务函数{& & & & & & if (RI)&&//接收到字节& & {& && &&&RI = 0;& &//手动清零接收中断标志位& && &&&if (cntRxd & sizeof(bufRxd)) //接收缓冲区尚未用完时,& && &&&{& && && && &bufRxd[cntRxd++] = SBUF; //保存接收字节,并递增计数器& && &&&}& & & & & & }& & & & & & if (TI)&&//字节发送完毕& & {& & & & & && &&&TI = 0;& &//手动清零发送中断标志位& && &&&flagOnceTxd = 1;&&//设置单次发送完成标志& & }}/***********************lcd1602.c文件程序源代码*************************/#include &reg52.h& #define LCD1602_DB& &P0 sbit LCD1602_RS = P1^0;sbit LCD1602_RW = P1^1;sbit LCD1602_E&&= P1^5; void LcdWaitReady()&&//等待液晶准备好{& && & & & LCD1602_DB = 0xFF;& & LCD1602_RS = 0;& & LCD1602_RW = 1;& & do& & {& && &&&LCD1602_E = 1;& && &&&sta = LCD1602_DB; //读取状态字& && &&&LCD1602_E = 0;& & } while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止}void LcdWriteCmd(unsigned char cmd)&&//写入命令函数{& & LcdWaitReady();& & LCD1602_RS = 0;& & LCD1602_RW = 0;& & LCD1602_DB =& & LCD1602_E&&= 1;& & LCD1602_E&&= 0;}void LcdWriteDat(unsigned char dat)&&//写入数据函数{& & LcdWaitReady();& & LCD1602_RS = 1;& & LCD1602_RW = 0;& & LCD1602_DB =& & LCD1602_E&&= 1;& & LCD1602_E&&= 0;}void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str)&&//显示字符串,屏幕起始坐标(x,y),字符串指针str{& && & & & //由输入的显示坐标计算显示RAM的地址& & if (y == 0)& && &&&addr = 0x00 + //第一行字符地址从0x00起始& & else& && &&&addr = 0x40 + //第二行字符地址从0x40起始& & & & //由起始显示RAM地址连续写入字符串& & LcdWriteCmd(addr | 0x80); //写入起始地址& & while (*str != '\0')& && &//连续写入字符串数据,直到检测到结束符& & {& && &&&LcdWriteDat(*str);& && &&&str++;& & }}void LcdInit()&&//液晶初始化函数{& & LcdWriteCmd(0x38);&&//16*2显示,5*7点阵,8位数据接口& & LcdWriteCmd(0x0C);&&//显示器开,光标关闭& & LcdWriteCmd(0x06);&&//文字不动,地址自动+1& & LcdWriteCmd(0x01);&&//清屏}& &关于CRC校验的算法,如果不是专门学习校验算法本身,大家可以不去研究这个程序的细节,文档直接给我们提供了函数,我们直接调用即可。 /***********************CRC16.c文件程序源代码*************************/unsigned int GetCRC16(unsigned char *ptr,&&unsigned char len){ & && & unsigned char crch = 0xFF;&&//高CRC字节& & unsigned char crcl = 0xFF;&&//低CRC字节& & unsigned char code TabH[] = {&&//CRC高位字节值表& && &&&0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,&&& && &&&0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,&&& && &&&0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,&&& && &&&0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,&&& && &&&0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,&&& && &&&0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,&&& && &&&0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,&&& && &&&0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,&&& && &&&0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,&&& && &&&0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,&&& && &&&0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,&&& && &&&0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,&&& && &&&0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,&&& && &&&0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,&&& && &&&0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,&&& && &&&0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,&&& && &&&0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,&&& && &&&0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,&&& && &&&0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,&&& && &&&0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,&&& && &&&0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,&&& && &&&0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,&&& && &&&0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,&&& && &&&0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,&&& && &&&0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,&&& && &&&0x80, 0x41, 0x00, 0xC1, 0x81, 0x40&&& & } ;&&& & unsigned char code TabL[] = {&&//CRC低位字节值表& && &&&0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,&&& && &&&0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,&&& && &&&0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,&&& && &&&0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,&&& && &&&0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,&&& && &&&0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,&&& && &&&0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,&&& && &&&0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,&&& && &&&0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,&&& && &&&0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,&&& && &&&0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,&&& && &&&0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,&&& && &&&0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,&&& && &&&0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,&&& && &&&0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,&&& && &&&0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,&&& && &&&0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,&&& && &&&0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,&&& && &&&0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,&&& && &&&0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,&&& && &&&0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,&&& && &&&0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,&&& && &&&0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,&&& && &&&0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,&&& && &&&0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,&&& && &&&0x43, 0x83, 0x41, 0x81, 0x80, 0x40&&& & } ; & & while (len--)&&//计算指定长度的CRC& & {& && &&&index = crch ^ *ptr++;& && &&&crch = crcl ^ TabH[ index];& && &&&crcl = TabL[ index];& & }& & & & return ((crch&&8) | crcl);&&}& && && && && && && && && &/***********************main.c文件程序源代码*************************/void ConfigTimer0(unsigned int ms);extern void LcdInit();extern void ConfigUART(unsigned int baud);extern void UartRxMonitor(unsigned char ms);extern void UartDriver(); void main (){& & EA = 1;& && && &&&//开总中断& & ConfigTimer0(1);&&//配置T0定时1ms& & ConfigUART(9600); //配置波特率为9600& & LcdInit();& && &&&//初始化液晶& & & & while(1)& & {& && &&&UartDriver();& & }} void ConfigTimer0(unsigned int ms)&&//T0配置函数{& && & & & tmp =
/ 12;& && &//定时器计数频率& & tmp = (tmp * ms) / 1000;&&//计算所需的计数值& & tmp = 65536 -& && &&&//计算定时器重载值& & tmp = tmp + 34;& && && &&&//修正中断响应延时造成的误差& & & & T0RH = (unsigned char)(tmp && 8);&&//定时器重载值拆分为高低字节& & T0RL = (unsigned char)& & TMOD &= 0xF0;& &//清零T0的控制位& & TMOD |= 0x01;& &//配置T0为模式1& & TH0 = T0RH;& &&&//加载T0重载值& & TL0 = T0RL;& & ET0 = 1;& && &&&//使能T0中断& & TR0 = 1;& && &&&//启动T0}void InterruptTimer0() interrupt 1&&//T0中断服务函数{& & TH0 = T0RH;&&//定时器重新加载重载值& & TL0 = T0RL;& & if (flagBuzzOn)&&//蜂鸣器鸣叫或关闭& && &&&BUZZ = ~BUZZ;& & else& && &&&BUZZ = 1;& & UartRxMonitor(1);&&//串口接收监控}18.4 作业1、了解RS485通信以及和RS232的不同用法。2、了解Modbus协议以及RTU数据帧的规则。3、写一个电子钟程序,并且可以通过485调试器校时。
大家有什么问题可以在回帖中提出
很好,最近正在做一个modbus和485的一个项目,可以好好参考一下你的,顶一个!{:soso_e179:}
好帖&&不错不错
文章写得不错,老师一点也不保守。
你好,我想问下:485从机里面寄存器地址表,怎么写?就是,modbus地址映射到dsp寄存器。地址表怎么用C写?谢谢
楼主您好,首先感谢您分享的东西,受益匪浅,还有就是请问有没有modbus用C语言编程的入门资料哇,可否传给我一些,谢谢您
谢谢老师通过学习得到了知识,我问一下上面的程序有汇编的吗?
最近正在学习这方面的内容,有点疑问请教楼主;
unsigned char regGroup[5];&&//Modbus寄存器组,地址为0x00~0x04
为什么这个地址就是0x00~0x04?是keilC编译的时候自动分配的内存地址?为什么不是其他地址呢?
请指教,谢谢了?
老师你好!请问关于MODBUS RTU 的CRC校验码,我在网上搜到以下的计算方法,是不是跟你上面的帖的代码是一样的。
1.预置1个16位的寄存器为十六进制FFFF(即全为1);称此寄存器为CRC寄存器;
2.把第一个8位二进制数据(既通讯信息帧的第一个字节)与16位的CRC寄存器的低
& &8位相异或,把结果放于CRC寄存器;
3.把CRC寄存器的内容右移一位(朝低位)用0填补最高位,并检查右移后的移出位;
4.如果移出位为0:重复第3步(再次右移一位);
如果移出位为1:CRC寄存器与多项式A001(00 0001)进行异或;
5.重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理;
6.重复步骤2到步骤5,进行通讯信息帧下一个字节的处理;
7.将该通讯信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低
& &字节进行交换;
8.最后得到的CRC寄存器内容即为:CRC码。
mark学习,
比较实用,值得学习
在五一单片机上面怎么实现通讯方式为RS485 协议为Modbus。去读取数据
学习了 不过没看太懂
顶!顶!顶!顶!顶!
楼主是使用C编程实现通讯的,问一下,我使用的是C#编写的程序,向在程序中加载一项能够读写PLC226数据的模块,楼主可以简单写一下吗?
楼主前辈写的通讯程序是单片机做从的写法吗?如果单片机做主,也是这样写吗?
程序来来回回看了很多遍了,编译没有错,写的很好,为什么我移植上去就是收不到数据,总是显示通讯超时,我用的是MODBUS调试精灵,照着楼主的讲解来的,硬件电路是USB-RS232-RS485出来通过485总线连接MAX485芯片,另一端连着单片机。希望楼主指导一下哪里出错?
这是读什么的?变频器(PLC)的?没用过,不太懂。
学习RS485通信
大家有什么问题可以在回帖中提出
楼主前辈写的通讯程序是单片机做从的写法吗?如果单片机做主,也是这样写吗?
楼主强大,从你的例程中对我项目很有帮助,谢谢
这个例子比较实用,赞
你好,你的第一个rs485通信程序,我用字符格式接收,接收到的都是乱码,咨询一下这个是怎么回事吗
**** 作者被禁止或删除 内容自动屏蔽 ****
我也是同样的问题,请问你问题解决没?
你好,你的第一个rs485通信程序,我用字符格式接收,接收到的都是乱码,咨询一下这个是怎么回事吗
问题已经解决了,是波特率设置问题,我将此代码中的串口初始化函数改了就好了。Modbus的那个例子,对于半双工通信是错的,需要该一下UartDriver()函数中的一句就OK了,谢谢楼主了!!!!
听君一席话,胜读天朝的教授+砖家写的那些所谓的专著十年。有点茅塞顿开的感觉,万分感谢楼主!
问题已经解决了,是波特率设置问题,我将此代码中的串口初始化函数改了就好了。Modbus的那个例子,对于半 ...
轻微一下我用modbus调试精灵为什么就不能接收到信息了
写得很好&&顶一下&&慢慢看
问题已经解决了,是波特率设置问题,我将此代码中的串口初始化函数改了就好了。Modbus的那个例子,对于半 ...
请问怎么改modbus的例子?
轻微一下我用modbus调试精灵为什么就不能接收到信息了
我的也是,你的解决了吗
程序稍作修改,用modbus精灵测试写不进程序,想先用串口检查一下(P3.0/P3.1),应该怎么改才可以测试
问题已经解决了,是波特率设置问题,我将此代码中的串口初始化函数改了就好了。Modbus的那个例子,对于半 ...
怎么改的能够请教下么?
第一天学习& && && && &&&
RS485跟CAN相比的话,是不是CAN更好用一点儿?
好贴,学习modbus很实用。
有没有使用STM32的啊?
Powered by

我要回帖

更多关于 联通登录达到上限 的文章

 

随机推荐