如何调试explorer内存泄露调试露

酷勤网 C 程序员的那点事!
当前位置: >
浏览次数:次
从上个世纪90年代Java诞生之日起,Java的类和资源的加载就一直是个问题。由于它增加了启动和初始化时间,因此这个问题在Java应用服务器上则尤为明显。为了缓解这个问题,大家试过了不同的访问,比如说以exploaded方式部署,但这只对简单的应用有效;还有2001年发明的Java热插拔的机制。启用热插拔的话,你在一个现有的方法内的改动马上就会生效。由于方法的边界限制,这个方法并不是特别有用,通常它只是在调试的阶段使用。对于现在的应用来说,编译,部署以及重启,等待个5到15分钟已经不是什么稀奇事儿了。越大型的应用服务器,这种情况可能就越明显。
存在的问题
一旦某个Java类被类加载器加载了,它就是不可变的,只要类加载器还存在,它也会一直存在下去。类的唯一标识是它的类名以及类加载器的标识,要重启一个应用的话,你需要创建一个新的类加载器,并加载最新版本的类。你不能把一个已经存在的对象映射到一个新类上面,因此重新加载时的状态迁移非常重要。这意味着你得初始化应用和配置的状态,拷贝用户的会话信息,以便重新生成整个应用的对象图。通常来说这非常耗时并很容易产生内存泄露。
说到类加载器的内存泄露,由于Java使用的内存模型的原因,哪怕是一小行代码的泄露都会产生很大的影响。比如说,一个类加载器的实例,它拥有自己加载的所有类的引用,以及这些类生成的所有对象的引用。因此在应用重启过程的状态迁移中,哪怕一个很小的泄露,都可能会产生极大的影响。
那这些对开发人员来说意味着什么?它意味即使是普通的编译,构建,打包,部署,应用重启,这些琐事都会极大的分散你的注意力,影响你的开发效率。
本文试图揭秘对开发人员而言JRebel所带来的威力,看一下这个产品背后究竟有什么奥妙,以及深入了解下JVM的那些你可能会忽略的地方 。本文主要关注JRebel所试图要解决的那些问题。
认识类加载器
类加载器只是一个普通的Java对象
是的,它并不是什么了不起的东西,除了JVM的系统类加载器,剩下的全都是一个普通的Java对象而已!ClassLoader是一个抽象类,你可以自己创建一个类来实现它。下面是它的API:
public abstract class ClassLoader {
public Class loadClass(String name);
protected Class defineClass(byte[] b);
public URL getResource(String name);
public Enumeration getResources(String name);
public ClassLoader getParent(); }
看起来相当简单,对吧?我们来逐个看下这些方法。最核心的方法是loadClass,它接受一个String类型的类名,并且返回实际的Class对象。如果你之前用过类加载器的话,这可能是你最熟悉的一个方法了,因为你可能每天都会用到它。defineClass是一个final类型的方法,它接受一个来自文件或者网络的byte数组,返回的也是一个Class对象。
类加载器还会从类路径中加载资源。它的工作方式和loadClass方法差不多。类似的方法有好几个,比如getResource和getResources,它返回的是一个URL对象,或者是一个URL的Enumeration。这些URL指向的是方法参数name中对应的资源。
每个类加载器都会有一个父类加载器,getParent方法返回的就是这个父加载器,它和Java的继承没有什么关系,只是用一个链表将它们串联起来而已。后面我们会稍微深入的了解下它。
类加载器是懒加载模式的,因此类只有在运行时被请求加载的话才会被加载进来。类是由调用到它的对象加载的,因此在运行时一个类可能会被多个类加载器加载,这取决于具体是哪个类引用到了它们以及哪个类加载器加载了引用了它们的类。。。好吧,我自己都有点绕晕了。我们来看段代码吧。
public class A {
public void doSmth() {
B b = new B();
b.doSmthElse();
这里有一个A类,它在doSmth()方法里调用了B类的构造方法。实际上底层会触发这样的调用:
A.class.getClassLoader().loadClass(&B&);
加载了A类的类加载器会去加载B类。
类加载器是分层的,不过跟孩子们不一样,它们不会总听父母的话
每个类加载器都会有一个父加载器。当请求一个类加载器加载类时,它通常会先调父类加载器的loadClass方法,而它的父类加载器也会再去找自己的父加载器,这么一直下去。如果同一个父加载器下面有两个类加载器,它们又同时被请求加载同一个类,类加载器只会加载一次。如果两个类加载器分别加载了同一个类,事情就会变得非常麻烦,下面我们会看到这种情况。
Java应用服务器在实现Java EE规范的时候,有的实现是先委托给父加载器进行加载,有的实现则会先看下本地的Web应用类加载器底下有没有。我们来深入分析下这种情况,下面用图1作为例子。
在这个例子中,模块WAR1有自己的类加载器,它会优先用它来加载类,而不是委托给自己的双亲,也就是App1.ear的类加载器。这意味着不同的WAR模块,比如WAR1和WAR2,它们互相看不到对方的类。App1.ear模块有自己的类加载器,并且它是WAR1和WAR2类加载器的父加载器。当WAR1和WAR2的类加载器需要向上委派加载请求时,它会去请求App1.ear的类加载器,这意味着要加载的类在WAR类加载器的作用域外。如果某个类在WAR和app1中同时在在的话,WAR中的会覆盖掉APP的。最后EAR的类加载器的双亲就是容器的类加载器。EAR类加载器会把请求委派给容器的类加载器,不过它和WAR的做法并不一样,它会优先委派给父加载器。正如你所看到的,现在情况变得有点复杂了,这和普通的Java SE中的类加载行为并不一致。
那么在应用中如何重新加载类呢?
从前面的ClassLoader的API那可以知道,它只能用来加载类。也就是说,它没法用来卸载,或者重新加载类,因此如果要在运行时重新加载一个类的话,你得把现有的整个类结构体系全部扔掉,然后再重新加载使用,就像图2中那样。
如果你已经用过一段时间的Java了,你肯定会知道这要发生内存泄露了。一般的内存泄露是因为集合里面引用了许多需要要被清除的对象,但最终却没有被清理掉。类加载器也是这种情况,不过它更特殊一点。不幸的是,从Java平台的当前情况来看,这种情况不可避免并且开销极大。在经过几次重新部署后最终会抛出OutOfMemoryErrors异常。
每一个对象都会有一个指向自己对应类的引用,而这个类又会引用它的类加载器。关键在于类加载器又有它加载过的所有类的引用,每个类里面又会有一些静态的字段,像图3中那样。
这意味着:
如果类加载器泄露了,它所持有的所有类对象以及它们的静态字段也都会泄露。静态字段一般来说是些缓存,单例对象,以及不同的配置及应用状态信息。就算你的程序本身并没有任何大的静态缓存,这并不意味着你的框架不会替你缓存些什么东西(比如说log4j,它一般都在容器的类路径底下)。这同时也说明了为什么类加载器一旦泄露就会非常严重。
只要有一个对象泄露了,那么它对应的类的类加载器就会跟着一起泄露。尽管这个对象可能看起来占不了什么地方(它可能连一个字段都 没有),但它仍会引用到它自己的类加载器,最终引用到所有相关的应用状态信息。在应用重新部署的过程中,只要有一个地方发生了泄露,没有正确的清理掉,就会导致严重的泄露问题。通常一个应用中会有好几处类似会泄露的地方,由于一些第三方库本身构建的问题,有一些泄露的问题几乎无法解决。因此,类加载器的泄露十分常见。
这就是类加载器背后的技术难点,也就是说为了能在运行时刷新我们的代码,通常都得重新编译打包,部署甚至重启服务才能看到更新的代码。下篇文章中我们将会讲到Java中的这个难题的一些解决方案,包括使用Java 1.4中引入的一个类热插拔的框架,以及JRebel。
& 相关主题:
下一篇:没有了中国领先的IT技术网站
51CTO旗下网站
C++内存泄露检查的5个方法
在Linux平台上 有valgrind可以非常方便的帮助我们定位内存泄漏,因为Linux在开发领域的使用场景大多是跑服务器,再加上它的开源属性,相对而言,处理问题容 易形成“统一”的标准。而在Windows平台,服务器和客户端开发人员惯用的调试方法有很大不同。
作者:来源:ajie0112的博客| 16:23
在Linux平台上 有valgrind可以非常方便的帮助我们定位内存泄漏,因为Linux在开发领域的使用场景大多是跑服务器,再加上它的开源属性,相对而言,处理问题容 易形成&统一&的标准。而在Windows平台,服务器和客户端开发人员惯用的调试方法有很大不同。下面结合我的实际经验,整理下常见定位内存泄漏的方 法。
注意:我们的分析前提是Release版本,因为在Debug环境下,通过VLD这个库或者CRT库本身的内存泄漏检测函数能够分析出内存泄漏,相对而言比较简单。而服务器有很多问题需要在线上并发压力情况下才出现,因此讨论Debug版调试方法意义不大。
二、对象计数
方法:在对象构造时计数++,析构时&,每隔一段时间打印对象的数量
优点:没有性能开销,几乎不占用额外内存。定位结果精确。
缺点:侵入式方法,需修改现有代码,而且对于第三方库、STL容器、脚本泄漏等因无法修改代码而无法定位。
三、重载new和delete
方法:重载new/delete,记录分配点(甚至是调用堆栈),定期打印。
优点:没有看出
缺点:侵入式方法,需将头文件加入到大量源文件的头部,以确保重载的宏能够覆盖所有的new/delete。记录分配点需要加锁(如果你的程序是多线程),而且记录分配要占用大量内存(也是占用的程序内存)。
四、Hook Windows系统API
方法:使用微软的detours库,hook分配内存的系统Api:HeapAlloc/HeapRealloc/HeapFree(new/malloc的底层调用),记录分配点,定期打印。
优点:非侵入式方法,无需修改现有文件(hook api后,分配和释放走到自己的钩子函数中),检查全面,对第三方库、脚本库等等都能统计到。
缺点:记录内存需要占用大量内存,而且多线程环境需要加锁。
五、使用DiagLeak检测
微软出品的内存泄漏分析工具,原理同hookapi方式。配合LDGraph可视化展示内存分配数据,更方便查找泄漏。
1.在IDE工程选项里面配置Release版本也生成调试信息,发布时,将pdb文件和exe文件一起发布。
2.程序运行后,打开LeakDiag,设置Symbol path
3.定期Log下目标进程的内存分配情况,通过LDGraph打印分配增长情况,来发现内存泄漏。
优点:同hookapi方法,非侵入式修改,无需做任何代码改动。跟踪全面。可视化分析堆栈一览无余!
缺点:对性能有影响,hook分配加锁,遍历堆栈。但是不会占用目标进程的自身内存。
对于线上生产环境,建议大对象用计数来判断,定位快速准确,几乎无性能开销。在对外测试阶段,使用LeakDiag辅助分析,因为此时并发压力还不 是太大,性能开销还是可以承受。在线上大规模应用阶段,通过HookApi的方法,结合GM指令控制部分时间段的检测,这样可以把对玩家的影响(服务器性 能下降导致延迟)降到最低。【编辑推荐】【责任编辑: TEL:(010)】
大家都在看猜你喜欢
热点热点热点热点热点
24H热文一周话题本月最赞
讲师:22人学习过
讲师:7人学习过
讲师:12人学习过
精选博文论坛热帖下载排行
本书译自Grails项目负责人Graeme Keith Rocher所著的“The Definitive Guide to Grails”一书,着重介绍了如何在Grails框架下使用Groovy语...
订阅51CTO邮刊【调试记录】内存泄露调试心得(转) - STM32/STM8技术论坛 -
中国电子技术论坛 -
最好最受欢迎电子论坛!
后使用快捷导航没有帐号?
【调试记录】内存泄露调试心得(转)
助理工程师
13:56:59  
& &内存泄露在程序设计中是较难的一个问题,如果在平常的应用程序设计中(PC机),内存泄露相对来说容易点,至少是可以通过一些工具去查找问题,解决问题。但是,在相对低端的嵌入式系统里,可是无法查找,虽说是有硬件仿真工具,可是面对,大量的数据和一些复杂的系统也是很难去仿真的,那么只有一点一点去分析代码,一点点去理解,假设最终找出问题,解决问题。
& && &&&在嵌入式内存泄露的问题里,我个人以为,又有一些区别,一种是:因为一次内存泄露导致程序无法运行,这类问题是相对容易解决的;另一种是:一次内存泄露,不太影响整个程序,甚至可以得出正确数据,两次也可以,三次、、、、、,但是达到一定程序时,问题就会出现,那么要解决,必须把每个问题都解决了才能从根本上解决问题,所以首先必须要找到每个问题。对于这个问题也是最难的,因为或许从最开始就不知道是什么引起的问题。很蛋疼的是,第二种情况就是我遇到的。
& & 下面将详细介绍整个过程,整个系统如下:
硬件系统(第一次做板见笑了)软件系统问题描述:
1、能和上位机链接,可以执行上位机给下位机的命令,在读写时钟过程中很正常(20个字节左右)2、一些较实时的数据,需要经常更新,自然频率很高,读取出现个别错误;
3、从485里读出的数据有140个字节,通过以太网,向上位机发送,最终封装打包后的数据是212个字节,发送时出现out of memory的错误。
解决问题过程:起初还以为给以太网的内存不够,于是扩大之90多K(总128K),但是很奇怪的是依然不能解决。
因此怀疑是在移植的过程中出现问题。所以倒腾了几天还是没解决。
& & 项目的另一个人说是我的底层驱动出了错,因为从485读的数据,全是零,这点我从一开始就就否定了,因为我单独测试时也是0,指令正确,数据格式,校验都正确,数据就是零,那么必然正确(一定要坚持自己的观点),可是为什么全是0呢?当然是控制器的问题了(我心想),事实证明我说的正确,确实是控制器的问题,当然质量是没问题,是少插了测试模块,这点我不知道,因为刚来公司,对控制器不熟悉,我很惊讶,很大的 一个东西,竟然没发现,在我的提心下也没发现。后来换了一个带模块的控制器,他才发现。
& &也因此,我坚持我的看法,对协议栈进行测试,同事继续去研究驱动。
后来经过多次测试发现,在发送212个字节,开辟内存时,used为121,本来used只是内存的标记,只有1和0,怎么会出现这种问题呢?
想到肯定是内存问题于是对协议栈的配置更改,除了内存有所增加,used的还是121.所以认为或许是在初始化时影响到了。
& & 果不其然,原来在初始化协议栈之前,初始化了一个6K的空表,而且前面有许多无用的全局变量,(这部分代码是同事编写,写了三百多行代码,一行注释都没,纠结的看了2天才弄明白,主要是链表和定义过多,里面又有状态机什么的,所以注释很重要,尤其项目合作中,更重要是后期的维护,时间久了,代码又多就是自己写的,也搞不清,切记)代码如下:
int16_t AddCmd(char* rev,int len)
{
& & int16_
//& &int16_& &
& & if(rev[0]==0&&rev[1]==0xff&&rev[2]==0&&rev[3]==0xff)
& & {
& && &&&
& && &&&for(i=0;i&CMD_QUE;i++)
& && &&&{
& && && &if(CmdQue[0][i].used == 0)
& && && &{
& && && && &
& && && &}
& && && &else if(i==CMD_QUE-1)
& && && &{
& && && && & return -1;
& && && &}
& && &&&}
& && &&&CmdQue[0][i].used = 1;
& && &&&CmdQue[0][i].state = 1;
& && &&&CmdQue[0][i].pid = rev[4];
& && &&&CmdQue[0][i].nCode = rev[7];
& && &&&CmdQue[0][i].nFunction = rev[8];
& && &&&memcpy(CmdQue[0][i].data,rev+7,len-7);
//& && && &for(j=0;j&64;j++)
//& && && &{
//& && && && & printf(&CCAA%2X \n&,CmdQue[0][i].data[j]);&&//&&测试使用
//& && && &}
& && &&&return 0;
& & }
& & else
& & {
& && &&&return -1;
&&}
int16_t FindACmd(int8_t* num, int8_t state)
{
&&
& & for(i=0;i&CMD_QUE;i++)
& & {
& && &&&if(CmdQue[0][i].state == state)
& && &&&{
& && && && &*num =
& && && && &
& && &&&}
& & }
& & return 0;&&
}复制代码把这些搞完,下载程序查看,used虽不是1或0,但是变为30,121,55等等一些数据,有希望了,必然是内存的原因。
& &&&就这样纠结了几天依旧没解决,有种无能为力的感觉。
& & 所以看底层的代码, 突然发现定义了这样一个sbuf[ ],起初在没有联机时,以为接收的数据是不定长的,所以没给具体的值,现在至少可以确定其最大值,因此将sbuf[ ]改为sbuf[256 ],同样的其他几个也改正。(C语言仍需加强)& & 下载程序,一看,很神奇的问题终于没了 ,呜呼,终于解决了!真的很激动! 写到这里,不知道再写点什么了,平常感觉在技术上有许多想写的,可是正真要写时,感觉很难下笔,可是当真正写下整个过程时,发现对其理解更深了一层,所以做技术,写博客是很好的一种学习途径!
& &&&行百里者半九十,需要做的还很多,尤其项目的完善还需要一个过程!
& & 写的有点乱,想等项目结了好好的写点整个项目由电路图的设计,PCB板的设计,驱动代码的设计,以及整个系统的调试过程,以此来对整个项目有个更深的了解。
& &特别感谢在这个过程中鼓励和技术上帮助我的朋友和网友!
不足之处仍有许多,希望大家多多指正!附MODBUS协议:#include &modbus.h&
#include &usart.h&
#include &delay.h&
//********************************************************************************/& &&&
//modbus协议支持485总线
//修改日期:
//版本:V1.0
//Copyright(C) 象牙塔 All rights reserved
//Email:cronus_ QQ:
//********************************************************************************/
u8 MODBUS_SEND_SBUF[64];
u8 MODBUS_RECEIVE_SBUF[256];
u8 Defaultspec=0;
u8 baseAddress=0;
//字地址 0 - 255 (只取低8位)
//位地址 0 - 255 (只取低8位)
/* CRC 高位字节值表 */
const u8&&auchCRCHi[] = {
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
/* CRC低位字节值表*/
const u8&&auchCRCLo[] = {
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
/***************************CRC校验码生成函数 ********************************
*函数功能:生成CRC校验码
*本代码中使用查表法,以提高运算速度
****************************************************************************/
u16 crc16(u8 *puchMsg, u16 usDataLen)
u8 uchCRCHi = 0xFF ; /* 高CRC字节初始化 */
u8 uchCRCLo = 0xFF ; /* 低CRC 字节初始化 */
u16 uI /* CRC循环中的索引 */
while (usDataLen--) /* 传输消息缓冲区 */
&&uIndex = uchCRCHi ^ *puchMsg++ ; /* 计算CRC */
&&uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;
&&uchCRCLo = auchCRCLo[uIndex] ;
return (uchCRCLo && 8 | uchCRCHi) ;
/********************************读写线圈*********************************/
/***************主要功能码:读线圈,写单个线圈,写多个线圈****************/
/*************************************************************************
*SendReadCoilCommand();读线圈(0X01):最多读256个线圈
*SD:地址(1)+功能码(1)+起始地址(2)+线圈数量(2)+CRC 发送读线圈命令
*RD:地址(1)+功能码(1)+字节数N(1)+状态(N)+CRC& && && && &接受读线圈数据
*输入:StartingAddress:起始地址;CoilNumber:线圈数量
*输出:无
**************************************************************************/
//发送命令
//最多可读256个线圈
void SendReadCoilCommand(u8 StartingAddress,u8 CoilNumber)
{
& & u16 crcD
& & MODBUS_SEND_SBUF[0] = baseA//地址
& & MODBUS_SEND_SBUF[1] = 0X01;//功能码
& & MODBUS_SEND_SBUF[2] = 0X00;//读地址只有48个0X2C,远小于0XFF个
& & MODBUS_SEND_SBUF[3] = StartingA
& & MODBUS_SEND_SBUF[4] = 0X00;
& & MODBUS_SEND_SBUF[5] = CoilN
& & crcData = crc16(MODBUS_SEND_SBUF,6);
&&MODBUS_SEND_SBUF[6] = crcData & 0& &// CRC代码低位在前
&&MODBUS_SEND_SBUF[7] = crcData && 8;& && & //高位在后
& & RS485_Send_Data(MODBUS_SEND_SBUF,8);
& & // MODBUS_SEND_SBUF[]=0; 需不需要清零
}
//返回数据
u8 ReceiveReadCoilData(void)
{
& & u8 result=0;
& & u16 crcD
& & RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
& & crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
& & if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData && 8 ))
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&return result=0;
& & }
& & else
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&printf(&读线圈出错!&);
& && &&&result=0X01;& &
& && &&&
& && &&&
& & }
/***************************************************************************
*写单个线圈(0X05):&&可写线圈地址0~31
*SD: 地址(1)+功能码(1)+输出地址(2)+输出值(2)+CRC
*RD: 地址(1)+功能码(1)+输出地址(2)+输出值(2)+CRC
*输入:ExportAddress:输出地址;ExportData:输出值
*
***************************************************************************/
//发送命令
//ExportData 只能是0XFF 或 0X00
//地址范围0-31个
void SendWriteSingleCommand(u8 ExportAddress,u8 ExportData)
{
& & u16 crcD
& & u8
& & MODBUS_SEND_SBUF[0] = baseA//地址
& & MODBUS_SEND_SBUF[1] = 0X05;//功能码
& & MODBUS_SEND_SBUF[2] = 0X00;//读地址只有48个0X2C,远小于0XFF个
& & MODBUS_SEND_SBUF[3] = ExportA
& & MODBUS_SEND_SBUF[4]&&= ExportD
& & MODBUS_SEND_SBUF[5] = 0X00;
& &
& & crcData = crc16(MODBUS_SEND_SBUF,6);
&&MODBUS_SEND_SBUF[6] = crcData & 0& &// CRC代码低位在前
&&MODBUS_SEND_SBUF[7] = crcData && 8 ;& && & //高位在后
& & RS485_Send_Data(MODBUS_SEND_SBUF,8);
& & for(i=0;i&8;i++)
& & {
& && &&&printf(&\n%2X\n\r&,MODBUS_SEND_SBUF[i]);
& & }
& &
& & // MODBUS_SEND_SBUF[]=0; 需不需要清零
}
//返回数据
u8 ReceiveWriteSingleData(void)
{
& & u8 result=0;
& & u16 crcD
& & RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
& & crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
& & if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData && 8 ))
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&return result=0;
& & }
& & else
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&printf(&写单个线圈出错!&);
& && &&&result=0X05;& &
& && &&&
& & }
}
/***************************************************************************
*写多个线圈(0X0F):&&可写线圈地址0~31
*SD:地址(1)+功能码(1)+起始地址(2)+输出数量(2)+字节数量N(1)+输出值(N字节)+CRC
*RD: 地址(1)+功能码(1)+起始地址(2)+输出数量(2)+CRC
*查询——0X11 0X0F 0X00 0X13 0X00 0X0A 0X02 0XCD 0X01 0XBF 0X0B
*从机地址-功能码-寄存器地址高字节-寄存器地址低字节-寄存器数量高字节-寄存器数量
*低字节-字节数-数据1-数据2-CRC校验高字节-CRC校验低字节
*&&001AH&&0019H&&0018H&&0017H&&0016H&&0015H&&H
*& & 1& && &1& && &0& && &0& && &1& && &1& && &0& &&&1
*&&0022H&&0021H&&0020H&&001FH&&001EH&&001DH&&001CH 001BH
*& & 0& && &0& && &0& && &0& && &0& && &0& &&&0& &&&1
*传输的第一个字节CDH对应线圈为0013H到001AH,LSB(最低位)对应0013H
*输入:StartAddress:起始地址 ExportNumber:输出数量 ByteNumber:字节数量
*& && &ExportData:输出值(本代码里,最多4个字节)
***************************************************************************/
//发送命令
//地址范围0-31个,即最多32个地址,因此字节数是4Bit
void SendWriteMulCoilCommand(u8 StartAddress,u8 ExportNumber,u8 ByteNumber,u32 ExportData)
{
& & u16 crcD
& & u8
& & MODBUS_SEND_SBUF[0] = baseA//地址
& & MODBUS_SEND_SBUF[1] = 0X0F;//功能码
& & MODBUS_SEND_SBUF[2] = 0X00;//起始地址高,写地址只有32个0X2C,远小于0XFF个
& & MODBUS_SEND_SBUF[3] = StartA//起始地址低位
& & MODBUS_SEND_SBUF[4] = 0X00;&&//输出数量高位
& & MODBUS_SEND_SBUF[5] = ExportN//输出数量低位
& & MODBUS_SEND_SBUF[6] = ByteN//字节数
//& &&&if((ByteNumber&=0)&&(ByteNumber&8)){i=1;}
//& &&&else if((8&=ByteNumber)&&(ByteNumber&16)){i=2;}
//& &&&else if((16&=ByteNumber)&&(ByteNumber&24)){i=3;}
//& &&&else if((24&=ByteNumber)&&(ByteNumber&32)){i=4;}
//& &&&else{i=5;}
& & switch(ByteNumber)
&&{
& && &&&case 1:
& && && && && && && &MODBUS_SEND_SBUF[7]=ExportData&0XFF;
& && && && && && && &crcData = crc16(MODBUS_SEND_SBUF,8);
& && && && && && && &MODBUS_SEND_SBUF[8] = crcData & 0& &// CRC代码低位在前
& && && && && && && &MODBUS_SEND_SBUF[9] = crcData && 8 ;& && & //高位在后
& && && && && && && &RS485_Send_Data(MODBUS_SEND_SBUF,10);& &
& && && && && && &&&for(i=0;i&10;i++)
& && && && && && &&&{
& && && && && && && && &printf(&\n%2X\n\r&,MODBUS_SEND_SBUF[i]);
& && && && && && &&&}
& && && && && && &&&
& && && &case 2:
& && && && && && &&&MODBUS_SEND_SBUF[7]=ExportData&0XFF;
& && && && && && &&&MODBUS_SEND_SBUF[8]=(ExportData&0XFFFF)&&8;
& && && && && && &&&crcData = crc16(MODBUS_SEND_SBUF,9);
& && && && && && &&&MODBUS_SEND_SBUF[9] = crcData & 0& &// CRC代码低位在前
& && && && && && &&&MODBUS_SEND_SBUF[10] = crcData && 8 ;& && & //高位在后
& && && && && && &&&RS485_Send_Data(MODBUS_SEND_SBUF,11);& &
& && && && && && &&&for(i=0;i&11;i++)
& && && && && && &&&{
& && && && && && && && &printf(&\n%2X\n\r&,MODBUS_SEND_SBUF[i]);
& && && && && && &&&}
& && && && && && &&&
& && && && &case 3:
& && && && && && &&&MODBUS_SEND_SBUF[7]=ExportData&0XFF;
& && && && && && &&&MODBUS_SEND_SBUF[8]=(ExportData&0XFFFF)&&8;
& && && && && && &&&MODBUS_SEND_SBUF[9]=(ExportData&0XFFFFFF)&&16;
& && && && && && &&&crcData = crc16(MODBUS_SEND_SBUF,10);
& && && && && && &&&MODBUS_SEND_SBUF[10] = crcData & 0& &// CRC代码低位在前
& && && && && && &&&MODBUS_SEND_SBUF[11] = crcData && 8 ;& && & //高位在后
& && && && && && &&&RS485_Send_Data(MODBUS_SEND_SBUF,12);& &
& && && && && && &&&for(i=0;i&12;i++)
& && && && && && &&&{
& && && && && && && && &printf(&\n%2X\n\r&,MODBUS_SEND_SBUF[i]);
& && && && && && &&&}
& && && && && && &&&
& && && && & case 4:
& && && && && && &&&MODBUS_SEND_SBUF[7]=ExportData&0XFF;
& && && && && && &&&MODBUS_SEND_SBUF[8]=(ExportData&0XFFFF)&&8;
& && && && && && &&&MODBUS_SEND_SBUF[9]=(ExportData&0XFFFFFF)&&16;
& && && && && && &&&MODBUS_SEND_SBUF[10]=ExportData&&24;
& && && && && && &&&crcData = crc16(MODBUS_SEND_SBUF,11);
& && && && && && &&&MODBUS_SEND_SBUF[11] = crcData & 0& &// CRC代码低位在前
& && && && && && &&&MODBUS_SEND_SBUF[12] = crcData && 8 ;& && & //高位在后
& && && && && && &&&RS485_Send_Data(MODBUS_SEND_SBUF,13);& &
& && && && && && &&&for(i=0;i&13;i++)
& && && && && && &&&{
& && && && && && && && &printf(&\n%2X\n\r&,MODBUS_SEND_SBUF[i]);
& && && && && && &&&}
& && && && && && &&&
& && && && && & case 5:
& && && && && && &&&/***********************/
& && && && && && &&&
& && &&&}
& & // MODBUS_SEND_SBUF[]=0; 需不需要清零
}
//返回数据
u8 ReceiveWriteMulCoilData(void)
{
& & u8 result=0;
& & u16 crcD
& & RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
& & crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
& & if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData && 8 ))
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&return result=0;
& & }
& & else
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&printf(&写多线圈出错!&);
& && &&&result=0X0F;& &
& && &&&
& & }
}
/*****************************读写保持寄存器*********************************/
/*********主要功能码:读保持寄存器,写单个寄存器,写多个寄存器***************/
/****************************************************************************
*256套规范,每套规范预留256个寄存器,现有128个参数,每个参数2个字节。
*保持寄存器偏移量=规范号*256+参数号。
*规范号:0~255& && && &参数号:0~127
*0&=保持寄存器偏移量&=255*256+127=65407=0XFF7F
*0XFF7F=65535
*get_MN(),动态定义规范
*****************************************************************************/
//取值范围是0~255
u8 get_MN()
{
/***********测试使用************/
//& &&&printf(&\n%2X\n\r&,Defaultspec);
& & return D
& &
}
/*****************************************************************************
*读保持寄存器(0X03): 每次最多读取125个
*SD:地址(1)+功能码(1)+起始地址(2)+寄存器数量(2)+CRC
*RD:地址(1)+功能码(1)+字节数N(1)+寄存器值(N*2)+CRC
*输入:ParameterNum&&参数号&&RegNumber 寄存器数量
*****************************************************************************/
void SendReadRegCommand(u8 ParameterNum,u8 RegNumber)
{
& & u16 crcData,StartA
& & u8
& & StartAddress=256*mn+ParameterN
& & MODBUS_SEND_SBUF[0] = baseA//地址
& & MODBUS_SEND_SBUF[1] = 0X03;//功能码
& & MODBUS_SEND_SBUF[2] = StartAddress&&8;
& & MODBUS_SEND_SBUF[3] = StartAddress&0XFF;
& & MODBUS_SEND_SBUF[4] = 0X00;
& & MODBUS_SEND_SBUF[5] = RegN//不超过 7D(125)
& & crcData = crc16(MODBUS_SEND_SBUF,6);
&&MODBUS_SEND_SBUF[6] = crcData & 0& &// CRC代码低位在前
&&MODBUS_SEND_SBUF[7] = crcData && 8;& && & //高位在后
& & RS485_Send_Data(MODBUS_SEND_SBUF,8);
//& &MODBUS_SEND_SBUF[]=0; // 需不需要清零
//& &&&for(i=0;i&8;i++)
//& &&&{
//& && && &printf(&\n%2X\n\r&,MODBUS_SEND_SBUF[i]);
//& &&&}
}
u8 ReceiveReadRegData(void)
{
& & u8 result=0;
& & u16 crcD
& & RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
& & crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
& & if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData && 8 ))
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&return result=0;
& & }
& & else
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&printf(&读寄存器出错!&);
& && &&&result=0X03;& &
& && &&&
& & }
/*************************************************************************
*SendWriteRegisterCommand();写单个寄存器(0X06):
*SD:地址(1)+功能码(1)+寄存器地址(2)+寄存器值(2)+CRC
*RD:地址(1)+功能码(1)+寄存器地址(2)+寄存器值(2)+CRC
*输入:ParameterNum:寄存器地址;SinRegswitch:ON或OFF
*输出:无
**************************************************************************/
//发送命令
void SendWriteSinRegCommand(u8 ParameterNum,u8 SinRegswitch)
{
& & u16 crcData,RegisterA
& & u8
& & RegisterAddress=256*mn+ParameterN
& & MODBUS_SEND_SBUF[0] = baseA//地址
& & MODBUS_SEND_SBUF[1] = 0X06;//功能码
& & MODBUS_SEND_SBUF[2] = RegisterAddress&&8;
& & MODBUS_SEND_SBUF[3] = RegisterAddress&0XFF;
& & MODBUS_SEND_SBUF[4] = 0X00;
& & MODBUS_SEND_SBUF[5] = SinR
& & crcData = crc16(MODBUS_SEND_SBUF,6);
&&MODBUS_SEND_SBUF[6] = crcData & 0& &// CRC代码低位在前
&&MODBUS_SEND_SBUF[7] = crcData && 8;& && & //高位在后
& & RS485_Send_Data(MODBUS_SEND_SBUF,8);
& & // MODBUS_SEND_SBUF[]=0; 需不需要清零
& & for(i=0;i&8;i++)
& & {
& && &&&printf(&\n%2X\n\r&,MODBUS_SEND_SBUF[i]);
& & }
}
u8 ReceiveWriteSinRegData(void)
{
& & u8 result=0;
& & u16 crcD
& & RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
& & crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
& & if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData && 8 ))
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&return result=0;
& & }
& & else
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&printf(&写单个寄存器出错!&);
& && &&&result=0X06;& &
& && &&&
& & }
}
/*************************************************************************
*写多个寄存器(0X10): 每次最多写123个
*SD:地址(1)+功能码(1)+起始地址(2)+寄存器数量(2)+字节数N(1)+寄存器值(2*N)+CRC
*RD:地址(1)+功能码(1)+起始地址(2)+寄存器数量(2)+CRC
*输入:StartAddress:寄存器地址;SinRegswitch:ON或OFF
*输出:无
**************************************************************************/
// void SendWriteMulRegCommand( u8 ParameterNum, )
// {
//& &&&u16 crcData,StartA
//& &&&u8
//& &&&StartAddress=256*mn+ParameterN
//& &&&MODBUS_SEND_SBUF[0] = baseA//地址
//& &&&MODBUS_SEND_SBUF[1] = 0X10;//功能码
//& &&&MODBUS_SEND_SBUF[2] = StartAddress&&8;
//& &&&MODBUS_SEND_SBUF[3] = StartAddress&0XFF;
// }
/*************************************************************************
*读输入寄存器(0X04):读内存
*SD:地址(2)+命令(2)+起始地(2)址+寄存器数量(2)
*RD:地址(2)+命令(2)+字节数N(2)+数据内容(N*2)
*输入:StartAddress:起始地址0X0000~0XFFFF
*输入:RegisterNum:0XD(125)
*输出:无
**************************************************************************/
void SendReadEnterRegCommand( u16 StartAddress,u8 RegisterNum )
{
& & u16 crcD
& & MODBUS_SEND_SBUF[0] = baseA//地址
& & MODBUS_SEND_SBUF[1] = 0X04;//功能码
& & MODBUS_SEND_SBUF[2] = StartAddress&&8;
& & MODBUS_SEND_SBUF[3] = StartAddress&0XFF;
& & MODBUS_SEND_SBUF[4] = 0X00;
& & MODBUS_SEND_SBUF[5] = RegisterN
& & crcData = crc16(MODBUS_SEND_SBUF,6);
&&MODBUS_SEND_SBUF[6] = crcData & 0& &// CRC代码低位在前
&&MODBUS_SEND_SBUF[7] = crcData && 8;& && & //高位在后
& & RS485_Send_Data(MODBUS_SEND_SBUF,8);
}
u8 ReceiveReadEnterRegData(void)
{
& & u8 result=0;
& & u16 crcD
& & RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
& & crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
& & if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData && 8 ))
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&return result=0;
& & }
& & else
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&printf(&读输入寄存器出错!&);
& && &&&result=0X04;& &
& && &&&
& & }
}
/*************************************************************************
*读焊接历史记录(功能码:0X41)
*焊接记录,最多960条记录;记录读取方式只有0和1,1代表读记录( 从最早的记录
*开始读),0代表重读记录。每次最多读取3条记录。每个记录体包含64个字节(低
*字节在先)。
*SD: 地址(1)+功能码(1)+读取方式(1)+CRC
*RD: 地址(1)+功能码(1)+读取方式(1)+回复的记录个数(1)+记录体(64)+CRC
**************************************************************************/
void SendReadWeldingHistoryCommand( u8 ReadMode )
{
& & u16 crcD
//& &&&u8
& & MODBUS_SEND_SBUF[0] = baseA//地址
& & MODBUS_SEND_SBUF[1] = 0X41;//功能码
& & MODBUS_SEND_SBUF[2] = ReadM
& & crcData = crc16(MODBUS_SEND_SBUF,3);
&&MODBUS_SEND_SBUF[3] = crcData & 0& &// CRC代码低位在前
&&MODBUS_SEND_SBUF[4] = crcData && 8;& && & //高位在后
& & RS485_Send_Data(MODBUS_SEND_SBUF,5);
//& &&&for(i=0;i&5;i++)
//& &&&{
//& && && &printf(&\n%d\r&,MODBUS_SEND_SBUF[i]);
//& &&&}
}
u8 ReceiveWeldingHistoryData(void)
{
& & u8 result=0;
& & u16 crcD
& & RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
& & crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
& & if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData && 8 ))
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&return result=0;
& & }
& & else
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&printf(&读焊接历史记录错误!&);
& && &&&result=0X41;& &
& && &&&
& & }
}
void SendReadWriteTimeCommand(u8 TimeCommand , u8 TimeNumber[7] )
{
& &
& & u16 crcD
& & u8
& & MODBUS_SEND_SBUF[0] = baseA//地址
& & MODBUS_SEND_SBUF[1] = 0X44;//功能码
& &&&MODBUS_SEND_SBUF[2] = TimeC
& & if(TimeCommand==0)
& & {
& && &&&crcData = crc16(MODBUS_SEND_SBUF,3);
& & MODBUS_SEND_SBUF[3] = crcData & 0& &// CRC代码低位在前
& & MODBUS_SEND_SBUF[4] = crcData && 8;& && & //高位在后
& && &RS485_Send_Data(MODBUS_SEND_SBUF,5);
& & }
& & else
& & {
& && &&&delay_ms(5);
& && &&&for(i=0;i&7;i++)
& && && && &{
& && && && && & MODBUS_SEND_SBUF[3+i] = TimeNumber[i];
//& && && && && &&&printf(&\n\r%d\n\r&,TimeNumber[i]);
& && && && &}
& && &&&crcData = crc16(MODBUS_SEND_SBUF,10);
& & MODBUS_SEND_SBUF[10] = crcData & 0& &// CRC代码低位在前
& & MODBUS_SEND_SBUF[11] = crcData && 8;& && & //高位在后
& && &RS485_Send_Data(MODBUS_SEND_SBUF,12);
& && &&&
& & }
//& &&&MODBUS_SEND_SBUF[3] = RecordPointer&0XFF;
//& &&&MODBUS_SEND_SBUF[4] = RecordN
//& &&&
}
u8 ReceiveReadWriteTimeData( void )
{
& & u8 result=0;
& & u16 crcD
& & RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
& & crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
& & if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData && 8 ))
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&return result=0;
& & }
& & else
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&printf(&读写时钟错误!&);
& && &&&result=0X44;& &
& && &&&
& & }
}
/*************************************************************************
* 读设备标识(0X2B)
* 表:10:设备标识
* 对象ID& & 对象名称& & 类型& &  
* 0X00& & 厂商名称& & ASCII字符串& &  只读
* 0X01& & 产品代码& & ASCII字符串& &  只读
* 0X02& & 主次版本号& & ASCII字符串& &  只读
* SD:地址(1)+功能码(1)+MEI类型(1)+读设备ID码(1)+对象ID(1)
&&SD:&&**+2B+0E+01+00+CRC
* RD:地址(1)+功能码(1)+MEI类型(1)+设备ID码(1)+一致性等级[conformity
* level](1)+00(1)+下一个设备ID码(1)+对象数量n (1)+对象1 ID(1)+对象1
* 长度N(1)+对象内容(N)+… … +对象n ID(1)+对象n长度M(1)+对象内容(M)+CRC
*************************************************************************/
void SendReadDeviceIdentifineCommand( void )
{
& &
& & u16 crcD
& & MODBUS_SEND_SBUF[0] = baseA//地址
& & MODBUS_SEND_SBUF[1] = 0X2B;//功能码
& &&&MODBUS_SEND_SBUF[2] = 0X0E;
& & MODBUS_SEND_SBUF[3] = 0X01;
& & MODBUS_SEND_SBUF[4] = 0X00;
& & crcData = crc16(MODBUS_SEND_SBUF,5);
& & MODBUS_SEND_SBUF[5] = crcData & 0& &// CRC代码低位在前
& & MODBUS_SEND_SBUF[6] = crcData && 8;& && & //高位在后
& & RS485_Send_Data(MODBUS_SEND_SBUF,7);
}
u8 ReceiveDeviceIdentifineData( void )
{
& & u8 result=0;
& & u16 crcD
& & RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
& & crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
& & if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData && 8 ))
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&return result=0;
& & }
& & else
& & {
& && &&&RS485_RX_CNT=0; //长度清零
& && &&&printf(&读设备标识错误!&);
& && &&&result=0X2B;& &
& && &&&
& & }
}复制代码
Powered by

我要回帖

更多关于 explorer内存泄露调试 的文章

 

随机推荐