zoukankan      html  css  js  c++  java
  • 内存泄露调试心得

      平常是懒得去写点东西,但是今天,决定写点调试心得,主要是因为这个问题正真用了一周时间才得以解决,记得这几年在我调程序的过程里,因为一个问题,最长的解决周期也就是四天,但是今天这个问题却是打破了自己的记录。
        内存泄露在程序设计中是较难的一个问题,如果在平常的应用程序设计中(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_t i;
    //   int16_t j;    
        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)
             {
                 break;
             }
             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 
    ",CmdQue[0][i].data[j]);  //  测试使用
    //         }
            return 0;
        }
        else
        {
            return -1;
      }
    
    }
    
    int16_t FindACmd(int8_t* num, int8_t state)
    {
      int i;
        for(i=0;i<CMD_QUE;i++)
        {
            if(CmdQue[0][i].state == state)
            {
                *num = i;
                return state;
            }
        }
        return 0;  
    }
      把这些搞完,下载程序查看,used虽不是1或0,但是变为30,121,55等等一些数据,有希望了,必然是内存的原因。
         就这样纠结了几天依旧没解决,有种无能为力的感觉。

        所以看底层的代码, 突然发现定义了这样一个sbuf[ ],起初在没有联机时,以为接收的数据是不定长的,所以没给具体的值,现在至少可以确定其最大值,因此将sbuf[ ]改为sbuf[256 ],同样的其他几个也改正。(C语言仍需加强)    下载程序,一看,很神奇的问题终于没了 ,呜呼,终于解决了!真的很激动!

     

    写到这里,不知道再写点什么了,平常感觉在技术上有许多想写的,可是正真要写时,感觉很难下笔,可是当真正写下整个过程时,发现对其理解更深了一层,所以做技术,写博客是很好的一种学习途径!
         行百里者半九十,需要做的还很多,尤其项目的完善还需要一个过程!
        写的有点乱,想等项目结了好好的写点整个项目由电路图的设计,PCB板的设计,驱动代码的设计,以及整个系统的调试过程,以此来对整个项目有个更深的了解。
       特别感谢在这个过程中鼓励和技术上帮助我的朋友和网友!

    不足之处仍有许多,希望大家多多指正!

    附MODBUS协议:

      1 #include "modbus.h"
      2 #include "usart.h"
      3 #include "delay.h"
      4 
      5 //********************************************************************************/     
      6 //modbus协议支持485总线
      7 //修改日期:2013/12/9
      8 //版本:V1.0
      9 //Copyright(C) 象牙塔 All rights reserved
     10 //Email:cronus_skl@163.com QQ:374199080
     11 //********************************************************************************/
     12 u8 MODBUS_SEND_SBUF[64];
     13 u8 MODBUS_RECEIVE_SBUF[256];
     14 
     15  u8 Defaultspec=0;
     16  u8 baseAddress=0;
     17 
     18 
     19 
     20 
     21 //字地址 0 - 255 (只取低8位)
     22 //位地址 0 - 255 (只取低8位)
     23 
     24 /* CRC 高位字节值表 */ 
     25 const u8  auchCRCHi[] = { 
     26 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0/**/, 
     27 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
     28 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
     29 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 
     30 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
     31 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 
     32 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
     33 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
     34 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
     35 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 
     36 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
     37 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 
     38 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
     39 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 
     40 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
     41 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 
     42 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
     43 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
     44 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
     45 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
     46 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
     47 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 
     48 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
     49 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
     50 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
     51 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 
     52 }; 
     53 /* CRC低位字节值表*/ 
     54 const u8  auchCRCLo[] = { 
     55 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06/**/, 
     56 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 
     57 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,           
     58 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 
     59 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 
     60 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 
     61 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 
     62 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 
     63 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 
     64 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 
     65 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 
     66 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 
     67 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 
     68 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 
     69 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 
     70 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 
     71 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 
     72 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 
     73 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 
     74 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 
     75 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 
     76 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 
     77 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 
     78 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 
     79 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 
     80 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 
     81 };
     82 
     83 /***************************CRC校验码生成函数 ********************************
     84 *函数功能:生成CRC校验码
     85 *本代码中使用查表法,以提高运算速度
     86 ****************************************************************************/
     87 u16 crc16(u8 *puchMsg, u16 usDataLen) 
     88 { 
     89 u8 uchCRCHi = 0xFF ; /* 高CRC字节初始化 */ 
     90 u8 uchCRCLo = 0xFF ; /* 低CRC 字节初始化 */ 
     91 u16 uIndex ; /* CRC循环中的索引 */ 
     92 while (usDataLen--) /* 传输消息缓冲区 */ 
     93 { 
     94   uIndex = uchCRCHi ^ *puchMsg++ ; /* 计算CRC */ 
     95   uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ; 
     96   uchCRCLo = auchCRCLo[uIndex] ; 
     97 } 
     98 return (uchCRCLo << 8 | uchCRCHi) ; 
     99 }
    100 
    101 /********************************读写线圈*********************************/
    102 /***************主要功能码:读线圈,写单个线圈,写多个线圈****************/
    103 /*************************************************************************
    104 *SendReadCoilCommand();读线圈(0X01):最多读256个线圈
    105 *SD:地址(1)+功能码(1)+起始地址(2)+线圈数量(2)+CRC 发送读线圈命令
    106 *RD:地址(1)+功能码(1)+字节数N(1)+状态(N)+CRC            接受读线圈数据
    107 *输入:StartingAddress:起始地址;CoilNumber:线圈数量
    108 *输出:无
    109 **************************************************************************/
    110 
    111 //发送命令
    112 //最多可读256个线圈
    113 void SendReadCoilCommand(u8 StartingAddress,u8 CoilNumber)
    114 {
    115     u16 crcData;
    116     MODBUS_SEND_SBUF[0] = baseAddress;//地址
    117     MODBUS_SEND_SBUF[1] = 0X01;//功能码
    118     MODBUS_SEND_SBUF[2] = 0X00;//读地址只有48个0X2C,远小于0XFF个
    119     MODBUS_SEND_SBUF[3] = StartingAddress;
    120     MODBUS_SEND_SBUF[4] = 0X00;
    121     MODBUS_SEND_SBUF[5] = CoilNumber;
    122     crcData = crc16(MODBUS_SEND_SBUF,6);
    123   MODBUS_SEND_SBUF[6] = crcData & 0xff;   // CRC代码低位在前
    124   MODBUS_SEND_SBUF[7] = crcData >> 8;       //高位在后
    125     RS485_Send_Data(MODBUS_SEND_SBUF,8);
    126     // MODBUS_SEND_SBUF[]=0; 需不需要清零
    127 }
    128 //返回数据
    129 u8 ReceiveReadCoilData(void)
    130 {
    131     u8 result=0;
    132     u16 crcData;
    133     RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
    134     crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
    135     if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData >> 8 ))
    136     {
    137         RS485_RX_CNT=0; //长度清零
    138         return result=0;
    139     }
    140     else
    141     {
    142         RS485_RX_CNT=0; //长度清零
    143         printf("读线圈出错!");
    144         result=0X01;    
    145         return result;
    146         
    147     }
    148 
    149 }
    150 
    151 
    152 /***************************************************************************
    153 *写单个线圈(0X05):  可写线圈地址0~31
    154 *SD: 地址(1)+功能码(1)+输出地址(2)+输出值(2)+CRC
    155 *RD: 地址(1)+功能码(1)+输出地址(2)+输出值(2)+CRC
    156 *输入:ExportAddress:输出地址;ExportData:输出值
    157 *
    158 ***************************************************************************/
    159 //发送命令
    160 //ExportData 只能是0XFF 或 0X00
    161 //地址范围0-31个
    162 void SendWriteSingleCommand(u8 ExportAddress,u8 ExportData)
    163 {
    164     u16 crcData;
    165     u8 i;
    166     MODBUS_SEND_SBUF[0] = baseAddress;//地址
    167     MODBUS_SEND_SBUF[1] = 0X05;//功能码
    168     MODBUS_SEND_SBUF[2] = 0X00;//读地址只有48个0X2C,远小于0XFF个
    169     MODBUS_SEND_SBUF[3] = ExportAddress;
    170     MODBUS_SEND_SBUF[4]  = ExportData;
    171     MODBUS_SEND_SBUF[5] = 0X00;
    172     
    173     crcData = crc16(MODBUS_SEND_SBUF,6);
    174   MODBUS_SEND_SBUF[6] = crcData & 0xff;   // CRC代码低位在前
    175   MODBUS_SEND_SBUF[7] = crcData >> 8 ;       //高位在后
    176     RS485_Send_Data(MODBUS_SEND_SBUF,8);
    177     for(i=0;i<8;i++)
    178     {
    179         printf("
    %2X
    
    ",MODBUS_SEND_SBUF[i]);
    180     }
    181     
    182     // MODBUS_SEND_SBUF[]=0; 需不需要清零
    183 }
    184 //返回数据
    185 u8 ReceiveWriteSingleData(void)
    186 {
    187     u8 result=0;
    188     u16 crcData;
    189     RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
    190     crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
    191     if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData >> 8 ))
    192     {
    193         RS485_RX_CNT=0; //长度清零
    194         return result=0;
    195     }
    196     else
    197     {
    198         RS485_RX_CNT=0; //长度清零
    199         printf("写单个线圈出错!");
    200         result=0X05;    
    201         return result;
    202     }
    203 
    204 }
    205 /***************************************************************************
    206 *写多个线圈(0X0F):  可写线圈地址0~31
    207 *SD:地址(1)+功能码(1)+起始地址(2)+输出数量(2)+字节数量N(1)+输出值(N字节)+CRC
    208 *RD: 地址(1)+功能码(1)+起始地址(2)+输出数量(2)+CRC
    209 *查询——0X11 0X0F 0X00 0X13 0X00 0X0A 0X02 0XCD 0X01 0XBF 0X0B
    210 *从机地址-功能码-寄存器地址高字节-寄存器地址低字节-寄存器数量高字节-寄存器数量
    211 *低字节-字节数-数据1-数据2-CRC校验高字节-CRC校验低字节
    212 *  001AH  0019H  0018H  0017H  0016H  0015H  0014H 0013H
    213 *    1      1      0      0      1      1      0     1
    214 *  0022H  0021H  0020H  001FH  001EH  001DH  001CH 001BH
    215 *    0      0      0      0      0      0     0     1
    216 *传输的第一个字节CDH对应线圈为0013H到001AH,LSB(最低位)对应0013H
    217 *输入:StartAddress:起始地址 ExportNumber:输出数量 ByteNumber:字节数量
    218 *      ExportData:输出值(本代码里,最多4个字节)
    219 ***************************************************************************/
    220 //发送命令
    221 //地址范围0-31个,即最多32个地址,因此字节数是4Bit
    222 void SendWriteMulCoilCommand(u8 StartAddress,u8 ExportNumber,u8 ByteNumber,u32 ExportData)
    223 {
    224     u16 crcData;
    225     u8 i;
    226     MODBUS_SEND_SBUF[0] = baseAddress;//地址
    227     MODBUS_SEND_SBUF[1] = 0X0F;//功能码
    228     MODBUS_SEND_SBUF[2] = 0X00;//起始地址高,写地址只有32个0X2C,远小于0XFF个
    229     MODBUS_SEND_SBUF[3] = StartAddress;//起始地址低位
    230     MODBUS_SEND_SBUF[4] = 0X00;  //输出数量高位
    231     MODBUS_SEND_SBUF[5] = ExportNumber;//输出数量低位
    232     MODBUS_SEND_SBUF[6] = ByteNumber;//字节数
    233 //     if((ByteNumber>=0)&&(ByteNumber<8)){i=1;}
    234 //     else if((8<=ByteNumber)&&(ByteNumber<16)){i=2;}
    235 //     else if((16<=ByteNumber)&&(ByteNumber<24)){i=3;}
    236 //     else if((24<=ByteNumber)&&(ByteNumber<32)){i=4;}
    237 //     else{i=5;}
    238     switch(ByteNumber)
    239   {
    240         case 1:
    241                      MODBUS_SEND_SBUF[7]=ExportData&0XFF;
    242                      crcData = crc16(MODBUS_SEND_SBUF,8);
    243                      MODBUS_SEND_SBUF[8] = crcData & 0xff;   // CRC代码低位在前
    244                      MODBUS_SEND_SBUF[9] = crcData >> 8 ;       //高位在后
    245                      RS485_Send_Data(MODBUS_SEND_SBUF,10);    
    246                     for(i=0;i<10;i++)
    247                     {
    248                         printf("
    %2X
    
    ",MODBUS_SEND_SBUF[i]);
    249                     }
    250                     break;
    251          case 2: 
    252                     MODBUS_SEND_SBUF[7]=ExportData&0XFF;
    253                     MODBUS_SEND_SBUF[8]=(ExportData&0XFFFF)>>8;
    254                     crcData = crc16(MODBUS_SEND_SBUF,9);
    255                     MODBUS_SEND_SBUF[9] = crcData & 0xff;   // CRC代码低位在前
    256                     MODBUS_SEND_SBUF[10] = crcData >> 8 ;       //高位在后
    257                     RS485_Send_Data(MODBUS_SEND_SBUF,11);    
    258                     for(i=0;i<11;i++)
    259                     {
    260                         printf("
    %2X
    
    ",MODBUS_SEND_SBUF[i]);
    261                     }
    262                     break;
    263             case 3: 
    264                     MODBUS_SEND_SBUF[7]=ExportData&0XFF;
    265                     MODBUS_SEND_SBUF[8]=(ExportData&0XFFFF)>>8;
    266                     MODBUS_SEND_SBUF[9]=(ExportData&0XFFFFFF)>>16;
    267                     crcData = crc16(MODBUS_SEND_SBUF,10);
    268                     MODBUS_SEND_SBUF[10] = crcData & 0xff;   // CRC代码低位在前
    269                     MODBUS_SEND_SBUF[11] = crcData >> 8 ;       //高位在后
    270                     RS485_Send_Data(MODBUS_SEND_SBUF,12);    
    271                     for(i=0;i<12;i++)
    272                     {
    273                         printf("
    %2X
    
    ",MODBUS_SEND_SBUF[i]);
    274                     }
    275                     break;
    276              case 4: 
    277                     MODBUS_SEND_SBUF[7]=ExportData&0XFF;
    278                     MODBUS_SEND_SBUF[8]=(ExportData&0XFFFF)>>8;
    279                     MODBUS_SEND_SBUF[9]=(ExportData&0XFFFFFF)>>16;
    280                     MODBUS_SEND_SBUF[10]=ExportData>>24;
    281                     crcData = crc16(MODBUS_SEND_SBUF,11);
    282                     MODBUS_SEND_SBUF[11] = crcData & 0xff;   // CRC代码低位在前
    283                     MODBUS_SEND_SBUF[12] = crcData >> 8 ;       //高位在后
    284                     RS485_Send_Data(MODBUS_SEND_SBUF,13);    
    285                     for(i=0;i<13;i++)
    286                     {
    287                         printf("
    %2X
    
    ",MODBUS_SEND_SBUF[i]);
    288                     }
    289                     break;
    290                 case 5:
    291                     /***********************/
    292                     break;
    293         }
    294     // MODBUS_SEND_SBUF[]=0; 需不需要清零
    295 }
    296 //返回数据
    297 u8 ReceiveWriteMulCoilData(void)
    298 {
    299     u8 result=0;
    300     u16 crcData;
    301     RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
    302     crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
    303     if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData >> 8 ))
    304     {
    305         RS485_RX_CNT=0; //长度清零
    306         return result=0;
    307     }
    308     else
    309     {
    310         RS485_RX_CNT=0; //长度清零
    311         printf("写多线圈出错!");
    312         result=0X0F;    
    313         return result;
    314     }
    315 
    316 }
    317 /*****************************读写保持寄存器*********************************/
    318 /*********主要功能码:读保持寄存器,写单个寄存器,写多个寄存器***************/
    319 /****************************************************************************
    320 *256套规范,每套规范预留256个寄存器,现有128个参数,每个参数2个字节。
    321 *保持寄存器偏移量=规范号*256+参数号。
    322 *规范号:0~255         参数号:0~127
    323 *0<=保持寄存器偏移量<=255*256+127=65407=0XFF7F
    324 *0XFF7F=65535
    325 *get_MN(),动态定义规范
    326 *****************************************************************************/
    327 //取值范围是0~255
    328 u8 get_MN()
    329 {
    330 /***********测试使用************/
    331 //     printf("
    %2X
    
    ",Defaultspec);
    332     return Defaultspec;
    333     
    334 }
    335 /*****************************************************************************
    336 *读保持寄存器(0X03): 每次最多读取125个
    337 *SD:地址(1)+功能码(1)+起始地址(2)+寄存器数量(2)+CRC
    338 *RD:地址(1)+功能码(1)+字节数N(1)+寄存器值(N*2)+CRC
    339 *输入:ParameterNum  参数号  RegNumber 寄存器数量
    340 *****************************************************************************/
    341 void SendReadRegCommand(u8 ParameterNum,u8 RegNumber)
    342 {
    343     u16 crcData,StartAddress;
    344     u8 i;
    345     StartAddress=256*mn+ParameterNum;
    346     MODBUS_SEND_SBUF[0] = baseAddress;//地址
    347     MODBUS_SEND_SBUF[1] = 0X03;//功能码
    348     MODBUS_SEND_SBUF[2] = StartAddress>>8;
    349     MODBUS_SEND_SBUF[3] = StartAddress&0XFF;
    350     MODBUS_SEND_SBUF[4] = 0X00;
    351     MODBUS_SEND_SBUF[5] = RegNumber;//不超过 7D(125)
    352     crcData = crc16(MODBUS_SEND_SBUF,6);
    353   MODBUS_SEND_SBUF[6] = crcData & 0xff;   // CRC代码低位在前
    354   MODBUS_SEND_SBUF[7] = crcData >> 8;       //高位在后
    355     RS485_Send_Data(MODBUS_SEND_SBUF,8);
    356 //   MODBUS_SEND_SBUF[]=0; // 需不需要清零
    357 //     for(i=0;i<8;i++)
    358 //     {
    359 //         printf("
    %2X
    
    ",MODBUS_SEND_SBUF[i]);
    360 //     }
    361 }
    362 u8 ReceiveReadRegData(void)
    363 {
    364     u8 result=0;
    365     u16 crcData;
    366     RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
    367     crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
    368     if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData >> 8 ))
    369     {
    370         RS485_RX_CNT=0; //长度清零
    371         return result=0;
    372     }
    373     else
    374     {
    375         RS485_RX_CNT=0; //长度清零
    376         printf("读寄存器出错!");
    377         result=0X03;    
    378         return result;
    379     }
    380 
    381 }
    382 
    383 /*************************************************************************
    384 *SendWriteRegisterCommand();写单个寄存器(0X06): 
    385 *SD:地址(1)+功能码(1)+寄存器地址(2)+寄存器值(2)+CRC
    386 *RD:地址(1)+功能码(1)+寄存器地址(2)+寄存器值(2)+CRC
    387 *输入:ParameterNum:寄存器地址;SinRegswitch:ON或OFF
    388 *输出:无
    389 **************************************************************************/
    390 
    391 //发送命令
    392 void SendWriteSinRegCommand(u8 ParameterNum,u8 SinRegswitch)
    393 {
    394     u16 crcData,RegisterAddress;
    395     u8 i;
    396     RegisterAddress=256*mn+ParameterNum;
    397     MODBUS_SEND_SBUF[0] = baseAddress;//地址
    398     MODBUS_SEND_SBUF[1] = 0X06;//功能码
    399     MODBUS_SEND_SBUF[2] = RegisterAddress>>8;
    400     MODBUS_SEND_SBUF[3] = RegisterAddress&0XFF;
    401     MODBUS_SEND_SBUF[4] = 0X00;
    402     MODBUS_SEND_SBUF[5] = SinRegswitch;
    403     crcData = crc16(MODBUS_SEND_SBUF,6);
    404   MODBUS_SEND_SBUF[6] = crcData & 0xff;   // CRC代码低位在前
    405   MODBUS_SEND_SBUF[7] = crcData >> 8;       //高位在后
    406     RS485_Send_Data(MODBUS_SEND_SBUF,8);
    407     // MODBUS_SEND_SBUF[]=0; 需不需要清零
    408     for(i=0;i<8;i++)
    409     {
    410         printf("
    %2X
    
    ",MODBUS_SEND_SBUF[i]);
    411     }
    412 }
    413 u8 ReceiveWriteSinRegData(void)
    414 {
    415     u8 result=0;
    416     u16 crcData;
    417     RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
    418     crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
    419     if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData >> 8 ))
    420     {
    421         RS485_RX_CNT=0; //长度清零
    422         return result=0;
    423     }
    424     else
    425     {
    426         RS485_RX_CNT=0; //长度清零
    427         printf("写单个寄存器出错!");
    428         result=0X06;    
    429         return result;
    430     }
    431 
    432 }
    433 /*************************************************************************
    434 *写多个寄存器(0X10): 每次最多写123个
    435 *SD:地址(1)+功能码(1)+起始地址(2)+寄存器数量(2)+字节数N(1)+寄存器值(2*N)+CRC
    436 *RD:地址(1)+功能码(1)+起始地址(2)+寄存器数量(2)+CRC
    437 *输入:StartAddress:寄存器地址;SinRegswitch:ON或OFF
    438 *输出:无
    439 **************************************************************************/
    440 // void SendWriteMulRegCommand( u8 ParameterNum, )
    441 // {
    442 //     u16 crcData,StartAddress;
    443 //     u8 i;
    444 //     StartAddress=256*mn+ParameterNum;
    445 //     MODBUS_SEND_SBUF[0] = baseAddress;//地址
    446 //     MODBUS_SEND_SBUF[1] = 0X10;//功能码
    447 //     MODBUS_SEND_SBUF[2] = StartAddress>>8;
    448 //     MODBUS_SEND_SBUF[3] = StartAddress&0XFF;
    449 // }
    450 /*************************************************************************
    451 *读输入寄存器(0X04):读内存
    452 *SD:地址(2)+命令(2)+起始地(2)址+寄存器数量(2)
    453 *RD:地址(2)+命令(2)+字节数N(2)+数据内容(N*2)
    454 *输入:StartAddress:起始地址0X0000~0XFFFF 
    455 *输入:RegisterNum:0X0001~0X007D(125)
    456 *输出:无
    457 **************************************************************************/
    458 void SendReadEnterRegCommand( u16 StartAddress,u8 RegisterNum )
    459 {
    460     u16 crcData;
    461     MODBUS_SEND_SBUF[0] = baseAddress;//地址
    462     MODBUS_SEND_SBUF[1] = 0X04;//功能码
    463     MODBUS_SEND_SBUF[2] = StartAddress>>8;
    464     MODBUS_SEND_SBUF[3] = StartAddress&0XFF;
    465     MODBUS_SEND_SBUF[4] = 0X00;
    466     MODBUS_SEND_SBUF[5] = RegisterNum;
    467     crcData = crc16(MODBUS_SEND_SBUF,6);
    468   MODBUS_SEND_SBUF[6] = crcData & 0xff;   // CRC代码低位在前
    469   MODBUS_SEND_SBUF[7] = crcData >> 8;       //高位在后
    470     RS485_Send_Data(MODBUS_SEND_SBUF,8);
    471 }
    472 u8 ReceiveReadEnterRegData(void)
    473 {
    474     u8 result=0;
    475     u16 crcData;
    476     RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
    477     crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
    478     if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData >> 8 ))
    479     {
    480         RS485_RX_CNT=0; //长度清零
    481         return result=0;
    482     }
    483     else
    484     {
    485         RS485_RX_CNT=0; //长度清零
    486         printf("读输入寄存器出错!");
    487         result=0X04;    
    488         return result;
    489     }
    490 }
    491 /*************************************************************************
    492 *读焊接历史记录(功能码:0X41)
    493 *焊接记录,最多960条记录;记录读取方式只有0和1,1代表读记录( 从最早的记录
    494 *开始读),0代表重读记录。每次最多读取3条记录。每个记录体包含64个字节(低
    495 *字节在先)。
    496 *SD: 地址(1)+功能码(1)+读取方式(1)+CRC
    497 *RD: 地址(1)+功能码(1)+读取方式(1)+回复的记录个数(1)+记录体(64)+CRC 
    498 **************************************************************************/
    499 void SendReadWeldingHistoryCommand( u8 ReadMode )
    500 {
    501     u16 crcData;
    502 //     u8 i;
    503     MODBUS_SEND_SBUF[0] = baseAddress;//地址
    504     MODBUS_SEND_SBUF[1] = 0X41;//功能码
    505     MODBUS_SEND_SBUF[2] = ReadMode;
    506     crcData = crc16(MODBUS_SEND_SBUF,3);
    507   MODBUS_SEND_SBUF[3] = crcData & 0xff;   // CRC代码低位在前
    508   MODBUS_SEND_SBUF[4] = crcData >> 8;       //高位在后
    509     RS485_Send_Data(MODBUS_SEND_SBUF,5);
    510 //     for(i=0;i<5;i++)
    511 //     {
    512 //         printf("
    %d
    ",MODBUS_SEND_SBUF[i]);
    513 //     }
    514 }
    515 
    516 u8 ReceiveWeldingHistoryData(void)
    517 {
    518     u8 result=0;
    519     u16 crcData;
    520     RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
    521     crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
    522     if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData >> 8 ))
    523     {
    524         RS485_RX_CNT=0; //长度清零
    525         return result=0;
    526     }
    527     else
    528     {
    529         RS485_RX_CNT=0; //长度清零
    530         printf("读焊接历史记录错误!");
    531         result=0X41;    
    532         return result;
    533     }
    534 }
    535 
    536 
    537 void SendReadWriteTimeCommand(u8 TimeCommand , u8 TimeNumber[7] )
    538 {
    539     
    540     u16 crcData;
    541     u8 i;
    542     MODBUS_SEND_SBUF[0] = baseAddress;//地址
    543     MODBUS_SEND_SBUF[1] = 0X44;//功能码
    544      MODBUS_SEND_SBUF[2] = TimeCommand;
    545     if(TimeCommand==0)
    546     {
    547         crcData = crc16(MODBUS_SEND_SBUF,3);
    548     MODBUS_SEND_SBUF[3] = crcData & 0xff;   // CRC代码低位在前
    549     MODBUS_SEND_SBUF[4] = crcData >> 8;       //高位在后
    550       RS485_Send_Data(MODBUS_SEND_SBUF,5);
    551     }
    552     else
    553     {
    554 
    555         delay_ms(5);
    556         for(i=0;i<7;i++)
    557             {
    558                 MODBUS_SEND_SBUF[3+i] = TimeNumber[i];
    559 //                 printf("
    
    %d
    
    ",TimeNumber[i]);
    560             }
    561         crcData = crc16(MODBUS_SEND_SBUF,10);
    562     MODBUS_SEND_SBUF[10] = crcData & 0xff;   // CRC代码低位在前
    563     MODBUS_SEND_SBUF[11] = crcData >> 8;       //高位在后
    564       RS485_Send_Data(MODBUS_SEND_SBUF,12);
    565         
    566     }
    567 //     MODBUS_SEND_SBUF[3] = RecordPointer&0XFF;
    568 //     MODBUS_SEND_SBUF[4] = RecordNumber;
    569 //     
    570 }
    571 
    572 u8 ReceiveReadWriteTimeData( void )
    573 {
    574     u8 result=0;
    575     u16 crcData;
    576     RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
    577     crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
    578     if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData >> 8 ))
    579     {
    580         RS485_RX_CNT=0; //长度清零
    581         return result=0;
    582     }
    583     else
    584     {
    585         RS485_RX_CNT=0; //长度清零
    586         printf("读写时钟错误!");
    587         result=0X44;    
    588         return result;
    589     }
    590 }
    591 /*************************************************************************
    592 * 读设备标识(0X2B)
    593 * 表:10:设备标识
    594 * 对象ID    对象名称    类型     
    595 * 0X00    厂商名称    ASCII字符串     只读
    596 * 0X01    产品代码    ASCII字符串     只读
    597 * 0X02    主次版本号    ASCII字符串     只读
    598 * SD:地址(1)+功能码(1)+MEI类型(1)+读设备ID码(1)+对象ID(1)
    599   SD:  **+2B+0E+01+00+CRC
    600 * RD:地址(1)+功能码(1)+MEI类型(1)+设备ID码(1)+一致性等级[conformity
    601 * level](1)+00(1)+下一个设备ID码(1)+对象数量n (1)+对象1 ID(1)+对象1
    602 * 长度N(1)+对象内容(N)+… … +对象n ID(1)+对象n长度M(1)+对象内容(M)+CRC 
    603 *************************************************************************/
    604 void SendReadDeviceIdentifineCommand( void )
    605 {
    606     
    607     u16 crcData;
    608     MODBUS_SEND_SBUF[0] = baseAddress;//地址
    609     MODBUS_SEND_SBUF[1] = 0X2B;//功能码
    610      MODBUS_SEND_SBUF[2] = 0X0E;
    611     MODBUS_SEND_SBUF[3] = 0X01;
    612     MODBUS_SEND_SBUF[4] = 0X00;
    613     crcData = crc16(MODBUS_SEND_SBUF,5);
    614     MODBUS_SEND_SBUF[5] = crcData & 0xff;   // CRC代码低位在前
    615     MODBUS_SEND_SBUF[6] = crcData >> 8;       //高位在后
    616     RS485_Send_Data(MODBUS_SEND_SBUF,7);
    617 }
    618 
    619 u8 ReceiveDeviceIdentifineData( void )
    620 {
    621     u8 result=0;
    622     u16 crcData;
    623     RS485_Receive_Data(MODBUS_RECEIVE_SBUF,&RS485_RX_CNT);
    624     crcData = crc16(MODBUS_RECEIVE_SBUF,RS485_RX_CNT-1);
    625     if(MODBUS_RECEIVE_SBUF[RS485_RX_CNT-1] == ( crcData&0xff ) || MODBUS_RECEIVE_SBUF[RS485_RX_CNT] == ( crcData >> 8 ))
    626     {
    627         RS485_RX_CNT=0; //长度清零
    628         return result=0;
    629     }
    630     else
    631     {
    632         RS485_RX_CNT=0; //长度清零
    633         printf("读设备标识错误!");
    634         result=0X2B;    
    635         return result;
    636     }
    637 }
  • 相关阅读:
    HDU 2236 无题Ⅱ
    Golden Tiger Claw(二分图)
    HDU 5969 最大的位或 (思维,贪心)
    HDU 3686 Traffic Real Time Query System (图论)
    SCOI 2016 萌萌哒
    Spring Boot支持控制台Banner定制
    构建第一个Spring Boot程序
    Spring Boot重要模块
    Java fastjson JSON和String互相转换
    BCompare 4 Windows激活方法【试用期30天重置】
  • 原文地址:https://www.cnblogs.com/skl374199080/p/3522115.html
Copyright © 2011-2022 走看看