Modbus通信协议的完整C/C++源代码示例

freeFree Technical Resource

This content is free to read, suitable for basic learning and search traffic.

Modbus通信协议的完整C/C++源代码示例缩略图
🌐 This page is not yet available in English. Showing the Chinese version. Back to Chinese page.
本文目录
  1. 1. RXD
  2. 2. TXD
  3. 3. ModBusStr
  4. 4. ModBus
  5. 5. MainTest

RXD

#include "ModBus.h"
/******************************************************************/
StrRxd xdata sys_rxd;               //定义接收处理结构体
/******************************************************************/
/*******公有函数***************************************************/
void    init_proc_rxd(void);        //初始化串口通信变量
void    process_rxd(void);          //通信处理函数
void    init_serial(void);          //初始化串口
/*******私有函数***************************************************/
uint8   read_sys_rxd(void);         //读接收缓冲区
uint8   data_check(void);           //校验和检验
uint8   lenghchk(void);             //数据长度检验
void    data_change(void);          //dataASCII转换成HEX
/******************************************************************/
void    init_proc_rxd(void)//初始化串口通信变量
{
    uint8 idata i;
    for( i = 0;i < POOLLEN;i++ )
    {
        //读
		sys_rxd.pool[i] = 0;//接收数据缓冲区
        sys_rxd.rec_buf[i] = 0;//去除包头包尾后的数据保留区,这里只保留一帧有效数据
        //写
		sys_txd.pool[i] = 0;//发送数据ASCII缓冲区
        sys_txd.combuf[i] = 0;//发送数据HEX缓冲区
    }
    for( i = 0;i < 16;i++ )
    {
        sys_rxd.add_buf[i] = 0;//用于存放接收缓冲的地址,可以存放16个帧地址,奇数位是首地址,偶数位是尾地址
    }

    sys_rxd.front = 0;
    sys_rxd.rear = 0;
    sys_rxd.frm_num = 0;//缓冲区中的帧数量
    sys_rxd.rd_byte = 0;//暂存读数据
    sys_rxd.start_0x7e = 0;
    sys_rxd.end_0x0d = 0;

    sys_txd.txd_len = 0;//发送数据长度
}
/*****************************************************************/
uint8 	read_sys_rxd(void)//读接收缓冲区
{
 	if( sys_rxd.rear == sys_rxd.front )
 	{
        return WRONG;
    }
	else
    {
        sys_rxd.rd_byte = sys_rxd.pool[sys_rxd.front];
	    sys_rxd.front = (sys_rxd.front+1) % POOLLEN;

	    return RIGHT;
    }
}
/*****************************************************************/
uint8 data_check(void)//校验和检验
{
    uint8  add_len,temp;
	uint8   i,j,k,fram_temp;
	uint16  chk_sum,chk_sum_temp;

	fram_temp = sys_rxd.frm_num;//如何防止在处理数据的同时新的中断使该值变化
	add_len = 0;

	if( fram_temp )//接收的一帧完整数据
    {
		fram_temp -= 1;

 		j = sys_rxd.add_buf[fram_temp * 2];
        k = sys_rxd.add_buf[fram_temp * 2 + 1];

		if(k > j)
		{
		    add_len = k-j;
		}
		else
		{
		    add_len = (POOLLEN - j ) + k;//地址确认
		}

        sys_rxd.front = j;

		chk_sum = 0;

 		for( i = 0;i < add_len - 4;i++ )          //4 byte is check sum
		{
            read_sys_rxd();    //the poionter is add1
        	if( sys_rxd.rd_byte == FRAME_START )
       	    {
        	    sys_rxd.rd_byte = 0;
        	}
	    	chk_sum += sys_rxd.rd_byte;
	 	}

		chk_sum =  (~chk_sum) + 1;

		chk_sum_temp = 0;
		for(i=0;i<4;i++)
		{
			read_sys_rxd();
			temp = sys_rxd.rd_byte;//rd_byte 暂存读数据

			if(temp > 0x40)
			{
			    temp -= 0x37; // get the hex valume
			}
            else
            {
                temp -= 0x30;
            }

			chk_sum_temp  += temp;

			if( i < 3 )
			{
	   		    chk_sum_temp = chk_sum_temp << 4;
	   		}
		}

        if( chk_sum_temp == chk_sum )
        {
            return RIGHT;
        }
        else
        {
            return WRONG;
        }
    }
    else
    {
        return WRONG;
    }
}
/*****************************************************************/
uint8   lenghchk(void)    //数据长度检验
{
    uint8 link_sum,link_sum_temp;
    uint8  add_len;
	uint8  j,k,fram_temp;

    if( sys_rxd.rec_buf[4] != lchksum(sys_rxd.rec_buf[5]) )
    {
        return WRONG;
    }

    fram_temp = sys_rxd.frm_num; //如何防止在处理数据的同时新的中断使该值变化
	if ( fram_temp == 0 )
	{
	    return WRONG;
	}

    fram_temp--;

	j = sys_rxd.add_buf[fram_temp*2];
	k = sys_rxd.add_buf[fram_temp*2+1];

	if(k>j)
	{
	    add_len = k-j;
	}
	else
	{
	    add_len = ( POOLLEN - j ) + k;
	}

	link_sum_temp = (add_len - 17);
	link_sum = sys_rxd.rec_buf[5];

	if(link_sum != link_sum_temp)
	{
	    return WRONG;
	}
	else
	{
	    return RIGHT;
	}

}
/*****************************************************************/
void data_change(void)//dataASCII转换成HEX
{
    uint8  add_len;
	uint8  i,j,k,temp1,fram_temp;
	uint8  temp;

    fram_temp = sys_rxd.frm_num; //如何防止在处理数据的同时新的中断使该值变化
	if ( fram_temp == 0 )
	{
	    return;
	}

    fram_temp--;

	j = sys_rxd.add_buf[fram_temp*2];
	k = sys_rxd.add_buf[fram_temp*2+1];

	if(k>j)
	{
	    add_len = k-j;
	}
	else
	{
	    add_len = ( POOLLEN - j ) + k;
	}

	add_len = add_len / 2 + 1;  //cup down 2 byte chksum
	sys_rxd.front = j;

	for(i = 0;i < add_len;i++ )
	{
        read_sys_rxd(); //the poionter is add1
    	if (sys_rxd.rd_byte == 0x0d)
   		{
            sys_rxd.rec_buf[i] = 0xaa;
			sys_rxd.rec_buf[i+1]=0x55;
 			return;
		}
    	if (sys_rxd.rd_byte == 0x7e)
    	{
		    read_sys_rxd();
		}
        temp = sys_rxd.rd_byte;
        if(temp>0x40)
        {
             temp-=0x37;
        }
        else
        {
       		temp -=0x30;
	    }
       	temp = temp<<4;//高4位
     	read_sys_rxd();//the poionter is add1
        temp1 = sys_rxd.rd_byte;
        if(temp1>0x40)
        {
       		temp1-=0x37;
        }
        else
        {
      		temp1-=0x30;
        }
        sys_rxd.rec_buf[i] = temp+temp1; // include ver/adr/cid1/cid2/length/info
    }
}
/*****************************************************************/
void process_rxd(void) //接收缓冲区中的数据 入处理缓冲区
{
    while(sys_rxd.frm_num)
    {
        if(data_check() == WRONG)
        {
            sys_rxd.add_buf[sys_rxd.frm_num*2] = 0;
            sys_rxd.add_buf[sys_rxd.frm_num*2+1] =0;
            sys_rxd.frm_num --;

            txd_uni(0x02);  //校验和错误
            return;
     	}

        data_change();

        if( (sys_rxd.rec_buf[1] == ADDR ) )  //地址正确
        {
            if( lenghchk() ==WRONG )  //长度校验
            {
                sys_rxd.add_buf[sys_rxd.frm_num*2] = 0;
                sys_rxd.add_buf[sys_rxd.frm_num*2+1] =0;
                sys_rxd.frm_num --;

                txd_uni(0x03);  //长度校验错误
                return;
            }

            if(sys_rxd.rec_buf[0] != VER)
            {
                sys_rxd.add_buf[sys_rxd.frm_num*2] = 0;
                sys_rxd.add_buf[sys_rxd.frm_num*2+1] =0;
                sys_rxd.frm_num --;
                txd_uni(0x01);  //版本错
                return;
            }
            if( (sys_rxd.rec_buf[2] != M_CID))  //deviceID错
            {
                sys_rxd.add_buf[sys_rxd.frm_num*2] = 0;
                sys_rxd.add_buf[sys_rxd.frm_num*2+1] =0;

                sys_rxd.frm_num--;
                txd_uni(0xe1);  //deviceID错
                return;
            }
            switch(sys_rxd.rec_buf[3])
            {
                case 0x44:
                    txd_sta();
               	    break;
                case 0x4f:
                    txd_uni(0x4f);      //发送通讯协议版本号
                    break;
                case 0x50:
                    txd_uni(0x50);      //发送设备地址
                    break;
                case 0x51:
                    txd_man();      //发送设备厂家信息
                    break;
                case 0x60:
                    txd_uni(0x60);      //系统初始化
                    break;
                default:
                    txd_uni(0x04);  //无效命令
                    break;
            }
        }
        sys_rxd.frm_num--;
    }
}
/*****************************************************************/
void    init_serial(void)//初始化串口
{
  	//串行口波特率等设置
        TMOD=0x21;      // T1  mode 2 T0,mode 1 //GATE C/T M1 M0 GATE C/T M1 M0
	TL1=0xfa;       // 0xfa=4800 bps  0xfd=9600 bps    0xe8 = 1200    0xf4 = 2400
	TH1=0xfa;
	TH0=-(10000/256);
   	TL0=-(10000%256);
        PCON=0;          //波特率不变等设置
        SCON=0x50;       //serial port1方式1,允许接收
        IT0=1;           //外部中断0下降沿有效
        IT1=1;           //外部中断1下降沿有效
        TR0=1;           //启动定时器0
        TR1=1;           //启动定时器1
        ET0=1;           //开放定时器0中断
        ES=1;            //串行中断
        EX0=0;           //外部中断0
        EX1=1;           //外部中断1
        EA=1;            //开总中断
 //       RS485EN = 0;

}
/*****************************************************************/
void    serial_uart(void) interrupt 4
{
    uchar idata temp;
    uchar idata num;
    if(RI)
    {
        RI = 0;
        temp = SBUF;
        if( sys_rxd.start_0x7e && (temp != FRAME_END) )
        {
            sys_rxd.pool[sys_rxd.rear] = temp;
            sys_rxd.rear = (sys_rxd.rear + 1) % POOLLEN;
            return;
        }
        else
        {
            if( (temp ^ FRAME_START)==0 )    //如果接收到包起始位
            {
                if( (sys_rxd.start_0x7e) && (sys_rxd.end_0x0d) )
                {
                    sys_rxd.rear = sys_rxd.add_buf[num];
                }
                else
                {   //第一次接收到起始位
                    sys_rxd.start_0x7e = 1;
                    sys_rxd.end_0x0d = 1;
                    num = sys_rxd.frm_num * 2;    //新的一帧的起始地址下标
                    sys_rxd.add_buf[num] = sys_rxd.rear;  //起始地址保存
                }
                sys_rxd.pool[sys_rxd.rear] = temp;
                sys_rxd.rear=(sys_rxd.rear+1) % POOLLEN;
                return;
            }
            else
            {
                if( sys_rxd.start_0x7e && ( temp == FRAME_END ) )
		{
                    sys_rxd.end_0x0d = 0;
                    sys_rxd.start_0x7e = 0;
                    sys_rxd.pool[sys_rxd.rear] = temp;
                    sys_rxd.rear = (sys_rxd.rear+1) % POOLLEN;
                    num = sys_rxd.frm_num*2+1;//结束地址位

                    if(sys_rxd.rear==0)   //对于POOLLEN不是255的时候,如果直接-1做尾地址。肯定造成尾地址错误。因为 0-1=255
                    {
                        sys_rxd.add_buf[num] = POOLLEN - 1;//在写进后的地址已加上1
                    }
                    else
                    {
                        sys_rxd.add_buf[num] = sys_rxd.rear-1;//在写进后的地址已加上1
                    }

                    sys_rxd.frm_num++;//增加一帧//成功才加一位
                    sys_rxd.frm_num = sys_rxd.frm_num % 16;
                    return;
                }
            }
        }
    }
}

这是一个用于处理Modbus通信的嵌入式CCode Example,包含了串口通信、数据校验、数据长度检验和数据转换等功能。以下是对这段代码的简要说明:

  1. StrRxd 结构体用于存储接收处理的相关变量和数据。
  2. init_proc_rxd 函数用于初始化串口通信变量。它清零了一些缓冲区和标志变量,以准备接收数据。
  3. read_sys_rxd 函数用于从接收缓冲区中读取一个字节的数据,并将其存储在 sys_rxd.rd_byte 中。
  4. data_check 函数用于校验和检验。它计算接收数据的校验和并与接收数据的校验字段进行比较,以确保数据完整性。
  5. lenghchk 函数用于数据长度检验。它比较接收到的数据长度字段与实际数据长度,以确保数据长度正确。
  6. data_change 函数用于将ASCII格式的数据转换为HEX格式的数据,以便进一步处理。
  7. process_rxd 函数用于处理接收到的数据。它根据协议规范检查数据的版本、地址和命令,然后执行相应的操作。
  8. init_serial 函数用于初始化串口通信。它配置了串口的波特率、定时器等参数。
  9. serial_uart 函数是串口接收中断服务程序。它在串口接收到数据时触发,将数据存储在接收缓冲区中,并触发相应的处理函数。

这段代码实现了一个简单的Modbus通信协议解析器,用于接收和处理Modbus RTU帧。请注意,此示例中的代码是为特定硬件和通信要求编写的,因此需要根据您的硬件和应用程序要求进行适当的修改。此外,它还包含了一些中断处理,如串口接收中断和定时器中断,以支持异步通信。这个代码示例是一个起点,您可以根据需要进行扩展和优化。

TXD

#include "ModBus.h"
//--------公有函数和变量-------
StrTxd xdata sys_txd;
/****************************************/
uint8 lchksum(uint8  data_len);    //求LENTH_SUM函数
void txd_sta(void);     //发送系统告警信息
void txd_man(void);     //发送厂家信息
void txd_uni(uint8 cid2);  //发送系统通信信息
/******************************************/
void package(void);     //数据打包成ASCII码
void chksum (void);     //求校验和
void com(void);         //公共的发送函数
/******************************************/
uint8 lchksum(uint8  data_len)//求LENTH_SUM函数
{
    uint8  sum,temp1;
	sum = 0;
	if (data_len)
	{
		sum = data_len & 0x0f;
		temp1 = (data_len & 0xf0)>>4;
		sum += temp1;
		sum = sum %16;
		sum = (~sum)+1;
		sum = sum <<4;
	}

	return sum;
}
/******************************************/
void package(void)//数据打包成ASCII码
{
    uint8  i;
    uint8  temp1,temp2;

    sys_txd.pool[0] = FRAME_START;

    for(i = 1;i < POOLLEN; i++)
    {
	    if( (sys_txd.combuf[i] == 0xaa ) && (sys_txd.combuf[i+1] == 0x55 ) )  //结束标志
	    {
           	sys_txd.pool[i*2-1] = FRAME_END;
           	break;
       	}
        else
        {
           	temp1 = sys_txd.combuf[i];
           	temp2 = temp1 & 0x0f;
           	temp1 = temp1 & 0xf0;
           	temp1 = temp1>>4;
          	sys_txd.pool[i*2-1] = (temp1>0x09)?(temp1+0x37):(temp1+0x30);
           	sys_txd.pool[i*2]   = (temp2>0x09)?(temp2+0x37):(temp2+0x30);
       	}
    }
}
/******************************************/
void chksum (void)//求校验和
{
   	uint8  i;
   	uint8  sumh,suml,temp;
   	uint16   sum = 0;

  	for(i = 1;i < POOLLEN;i++ )
    {
  	    if( sys_txd.pool[i] == FRAME_END )
  	  	{
         	temp = i;
 	    	break;
	  	}
	    else
	    {
          	sum += sys_txd.pool[i];    //溢出部分不用考虑
        }
    }

   	sum = (~sum) + 1;//取反+1
   	sumh = (uint8)(sum/256);//高8位
   	suml = (uint8)sum;
   	i = temp;

   	temp = (sumh&0xf0)>>4;
   	temp = (temp>0x09)?(temp+0x37):(temp+0x30);
   	sys_txd.pool[i]   =  temp;

   	temp = (sumh & 0x0f);
   	temp = (temp>0x09)?(temp+0x37):(temp+0x30);
   	sys_txd.pool[i+1] =  temp;

   	temp = (suml&0xf0)>>4;
    temp = (temp>0x09)?(temp+0x37):(temp+0x30);
   	sys_txd.pool[i+2] = temp;

    temp = (suml&0x0f);
   	temp = (temp>0x09)?(temp+0x37):(temp+0x30);
  	sys_txd.pool[i+3] = temp;

   	sys_txd.pool[i+4] = FRAME_END;

   	sys_txd.txd_len = i+4;

   	return;
}
/******************************************/
void com(void)//公共的发送函数
{
    uint8  i;
    uint8  j;
    
    EA = 0;
//    RS485EN = 1;    //使用485通信的时候使用
    for( i = 0;i <= sys_txd.txd_len;i++ )
    {
        SBUF = sys_txd.pool[i];
        while(TI==0);
        TI = 0;
        if( sys_txd.pool[i] == FRAME_END )
        {
            for( j=0;j <= sys_txd.txd_len;j++ )
            {
      	        sys_txd.pool[i] = 0;            //对已经发送的数据清0
            }
        }
    }
//    RS485EN = 0;
//    RI = 0;   //注意,如果485的接收使能一直有效,必须要这一行解决
    EA = 1;
}
/******************************************/
void txd_sta(void)//发送系统告警信息
{

    sys_txd.combuf[0] = FRAME_START;               //起始标志
    sys_txd.combuf[1] = VER;                       //版本号
    sys_txd.combuf[2] = ADDR;                      //address,主机
    sys_txd.combuf[3] = M_CID;                     //deviceID
//------以上是包头---------
    sys_txd.combuf[4] = 0x44;                      //Cid2=0x44,表示发送所有的告警状态
//------length---------
    sys_txd.combuf[5] = lchksum(20); //length checksum//////////////////////
    sys_txd.combuf[6] = 20;          //length id
    /********************************************/
    sys_txd.combuf[7] = 20;          //这里放告警信息
    sys_txd.combuf[8] = 20;          //
    sys_txd.combuf[9] = 20;          //
    sys_txd.combuf[10] = 20;         //
    /********************************************/
    sys_txd.combuf[11] = 0xaa;        //end ;
    sys_txd.combuf[12] = 0x55;    //数据打包成ASCII码
    
    package();              //////////////////////////////
    chksum();                  //除起始和校验和以及结束之外的ASCII码求校验和
    com();
}
/******************************************************/
void txd_man(void)//发送厂家信息
{
//-----------------------------------------------------------------
    sys_txd.combuf[0] = FRAME_START;               //起始标志
    sys_txd.combuf[1] = VER;                       //版本号
    sys_txd.combuf[2] = ADDR;                      //address,主机
    sys_txd.combuf[3] = M_CID;                     //deviceID
	//----------以上是包头
//-----------------------------------------------------------------
	sys_txd.combuf[4] =0x51;

	sys_txd.combuf[5] =lchksum(0x40);
	sys_txd.combuf[6] =0x40;

	sys_txd.combuf[7]  = 'L';        //L
	sys_txd.combuf[8]  = 'I';        //I
	sys_txd.combuf[9]  = 'N';        //N
	sys_txd.combuf[10] = 'E';        //E
	sys_txd.combuf[11] = 0;
	sys_txd.combuf[12] = 0x00;
	sys_txd.combuf[13] = 0x00;
	sys_txd.combuf[14] = 0x00;
	sys_txd.combuf[15] = 0x00;
	sys_txd.combuf[16] = 0x00;

	sys_txd.combuf[17] = 0x01;       //1   版本
	sys_txd.combuf[18] = 0x00;       //0

	sys_txd.combuf[19] = 0x41;       //A   厂家
	sys_txd.combuf[20] = 0x41;       //A
	sys_txd.combuf[21] = 0x41;       //A
	sys_txd.combuf[22] = 0x41;       //A
	sys_txd.combuf[23] = 0x41;       //A
	sys_txd.combuf[24] = 0x41;       //A
	sys_txd.combuf[25] = 0x41;       //A
	sys_txd.combuf[26] = 0x41;       //A
	sys_txd.combuf[27] = 0x41;       //A
	sys_txd.combuf[28] = 0x41;       //A
	sys_txd.combuf[29] = 0x00;
	sys_txd.combuf[30] = 0x00;
	sys_txd.combuf[31] = 0x00;
	sys_txd.combuf[32] = 0x00;
	sys_txd.combuf[33] = 0x00;
	sys_txd.combuf[34] = 0x00;
	sys_txd.combuf[35] = 0x00;
	sys_txd.combuf[36] = 0x00;
	sys_txd.combuf[37] = 0x00;
	sys_txd.combuf[38] = 0x00;

	sys_txd.combuf[39] = 0xaa;
	sys_txd.combuf[40] = 0x55;

    package();                 //数据打包成ASCII码
	chksum();                  //除起始和校验和以及结束之外的ASCII码求校验和
    com();
}
/******************************************/
void txd_uni(uint8 cid2)//发送系统通信信息
{
//-----------------------------------------------------------------
    sys_txd.combuf[0] = FRAME_START;               //起始标志
    sys_txd.combuf[1] = VER;                       //版本号
    sys_txd.combuf[2] = ADDR;                      //address,主机
    sys_txd.combuf[3] = M_CID;                     //deviceID
	//----------以上是包头
//-----------------------------------------------------------------
	sys_txd.combuf[4] = cid2;
	sys_txd.combuf[5] = lchksum(0x00);       //length checksum 0x00
	sys_txd.combuf[6] = 0x00;                //length id 00

	sys_txd.combuf[7] = 0xaa;                //length id=00,所以,没有info
	sys_txd.combuf[8] = 0x55;
    package();                 //数据打包成ASCII码
	chksum();                  //除起始和校验和以及结束之外的ASCII码求校验和
    com();
}
/******************************************/

这段代码是用于处理Modbus通信中的数据打包和发送的相关函数。以下是对每个函数的简要说明:

  1. lchksum 函数用于计算长度校验和(length checksum),以确保数据长度字段正确。它接受一个参数 data_len,计算并返回长度校验和。
  2. package 函数用于将数据打包成ASCII码形式。它将HEX格式的数据转换为ASCII码,并在每个HEX字节之间插入合适的分隔符。最终的数据存储在 sys_txd.combuf 中。
  3. chksum 函数用于计算校验和。它计算除了起始标志、校验和字段和结束标志之外的所有数据的校验和,将其添加到数据中。
  4. com 函数用于发送数据。它通过串口将数据发送出去,并清除已发送的数据,以便下一次使用。
  5. txd_sta 函数用于发送系统告警信息。它设置了一个特定的命令 cid2(0x44),表示发送告警信息。然后,它设置数据长度、告警信息和其他相关字段,最后调用 packagechksumcom 函数发送数据。
  6. txd_man 函数用于发送厂家信息。它设置了一个特定的命令 cid2(0x51),表示发送厂家信息。然后,它设置数据长度、厂家信息和其他相关字段,最后调用 packagechksumcom 函数发送数据。
  7. txd_uni 函数用于发送系统通信信息。它接受一个参数 cid2,表示通信命令。根据不同的 cid2,它设置了相应的命令并发送相应的数据。

这些函数一起构建了数据发送的流程,用于发送不同类型的Modbus帧。请注意,此示例中的代码是为特定应用程序编写的,因此需要根据您的要求进行适当的修改。此外,该代码示例还包括一些串口通信和数据转换的功能,以支持ModbusCommunication。

ModBusStr

#ifndef MODBUSSTR_H
#define MODBUSSTR_H
//==============================================
#define uchar unsigned char
#define uint unsigned int
#define uint8 unsigned char
#define uint16 unsigned int
//接收缓冲区结构变量
typedef struct str_rxd_pool
{
    uint8 pool[255];	//接收数据缓冲区
    uint8 front; 	    //头指针
    uint8 rear;	    	//尾指针
    uint8 rd_byte;	    //暂存读数据
    uint8 frm_num;	    //缓冲区中的帧数量
    uint8 add_buf[16];	//用于存放接收缓冲的地址,可以存放16个帧地址,奇数位是首地址,偶数位是尾地址
    uint8 rec_buf[255]; //去除包头包尾后的数据保留区,这里只保留一帧有效数据

    uint8 start_0x7e;   //接收到帧头标志
    uint8 end_0x0d;     //接收到帧尾标志

}StrRxd;
//===============================================
//发送缓冲区结构变量
typedef struct str_txd_pool
{
    uint8 pool[255];	    //发送数据ASCII缓冲区
    uint8 combuf[255];      //发送数据HEX缓冲区
    uint8 txd_len;          //发送数据长度
}StrTxd;
#endif

这是一个C语言头文件(MODBUSSTR.H),定义了用于Modbus通信的数据结构和相关类型别名。以下是这个头文件中定义的主要内容:

  1. 头文件保护宏定义 #ifndef MODBUSSTR_H#define MODBUSSTR_H,以确保文件只被包含一次。
  2. 一些类型别名的定义,among them ucharuintuint8uint16 分别代表无符号字符、无符号整数和不同宽度的无符号整数。
  3. StrRxd 结构体定义,用于接收缓冲区的相关数据结构。这个结构体包括以下成员:
  • pool[255]:用于存储接收的数据的缓冲区,最大长度为255字节。
  • front:头指针,指向缓冲区的开头。
  • rear:尾指针,指向缓冲区的末尾。
  • rd_byte:用于暂存读取的数据。
  • frm_num:缓冲区中的帧数量。
  • add_buf[16]:用于存放接收缓冲区的地址,可以存放16个帧地址,奇数位是首地址,偶数位是尾地址。
  • rec_buf[255]:用于存储去除包头和包尾后的数据,只保留一帧有效数据。
  • start_0x7e:用于标志是否接收到帧头标志。
  • end_0x0d:用于标志是否接收到帧尾标志。
  1. StrTxd 结构体定义,用于发送缓冲区的相关数据结构。这个结构体包括以下成员:
  • pool[255]:用于存储发送的数据的ASCII码形式的缓冲区,最大长度为255字节。
  • combuf[255]:用于存储发送的数据的HEX形式的缓冲区,最大长度为255字节。
  • txd_len:发送数据的长度。

这个头文件定义了用于Modbus通信的数据结构,可以在应用程序中包含此头文件以访问这些数据结构和类型别名。这有助于更好地组织和管理Modbus通信中的数据。

ModBus

#include "reg51.h"
#include "ModBusStr.h"

#define uchar unsigned char
#define uint unsigned int
#define uint8 unsigned char
#define uint16 unsigned int

#define OK 1
#define ERROR 0

#define RIGHT 1
#define WRONG 0


#define    POOLLEN  255              //缓冲区大小

#define    INIT_COMMAND   0x20
#define    START_COMMAND  0x21
#define    END_COMMAND    0x22
#define    ALARM_COMMAND  0x23


#define    FRAME_START    0x7e         //帧开始标志
#define    FRAME_END      0x0d         //帧结束标志
#define    VER    0x10
#define    ADDR   0x01


#define    M_CID   0x25                //设备识别码,0x25代表直流电源柜

extern    StrRxd xdata sys_rxd;
extern    StrTxd xdata sys_txd;

extern    void    init_proc_rxd(void);
extern    void    process_rxd(void);
extern    void    init_serial(void);

extern    uint8 lchksum(uint8  data_len);
extern    void txd_sta(void);
extern    void txd_man(void);
extern    void txd_uni(uint8 cid2);

这段代码是一个包含了一些宏定义、数据结构的头文件 "ModBusStr.h" 和其他的相关头文件 "reg51.h",以及定义了一些全局变量和函数原型。

以下是代码中的一些重要元素:

  1. 头文件包含:
  • "reg51.h":这是用于 8051 系列单片机的标准头文件,提供了与该系列芯片相关的寄存器和宏定义。
  • "ModBusStr.h":这是你自己创建的头文件,包含了 Modbus 通信相关的数据结构和类型别名。
  1. 宏定义:
  • OKERROR:定义了两个常量,分别为 1 和 0,通常用于表示函数执行成功和失败。
  • RIGHTWRONG:定义了两个常量,通常用于表示某些条件或操作的成功和失败。
  • POOLLEN:定义了缓冲区的最大长度为 255。
  • INIT_COMMANDSTART_COMMANDEND_COMMANDALARM_COMMAND:定义了一些命令常量,表示不同的操作。
  • FRAME_STARTFRAME_END:定义了帧的起始和结束标志。
  • VERADDR:定义了版本号和地址。
  • M_CID:定义了设备的识别码。
  1. 外部全局变量声明:
  • StrRxd xdata sys_rxdStrTxd xdata sys_txd:声明了两个外部全局变量,sys_rxdsys_txd,这些变量是在其他源文件中定义的,用于存储 Modbus 通信相关的数据。
  1. 外部函数原型声明:
  • void init_proc_rxd(void)void process_rxd(void)void init_serial(void):这些函数原型声明用于初始化接收处理、通信处理和串口初始化。
  • uint8 lchksum(uint8 data_len)void txd_sta(void)void txd_man(void)void txd_uni(uint8 cid2):这些函数原型声明用于计算长度校验和、发送系统告警信息、发送厂家信息和发送系统通信信息。

这段代码组织了 Modbus 通信相关的数据和函数,并通过头文件将这些定义和声明提供给其他源文件使用。源文件可以包含这个头文件来访问这些定义和声明,从而实现 Modbus 通信功能。

MainTest

#include "ModBus.h"
void    main(void)
{
    init_serial();//初始化串口
    init_proc_rxd();//初始化串口通信变量
    while(1)
    {
        process_rxd();//通信处理函数 接收缓冲区中的数据 入处理缓冲区
    }
}

这是一个8051单片机上的主程序示例,用于执行Modbus通信处理。在主函数中,它按照以下步骤进行操作:

  1. 包含 ModBus.h 头文件,该文件应该包含所有必需的定义、全局变量和函数原型以支持ModbusCommunication。
  2. 调用 init_serial() 函数,用于初始化串口通信,这是设置和准备与其他设备通信的重要一步。
  3. 调用 init_proc_rxd() 函数,用于初始化串口通信变量。这一步通常是为了确保通信变量处于正确的初始状态。
  4. 进入一个无限循环(while(1)),在循环中不断执行以下操作:
  • 调用 process_rxd() 函数,这个函数用于通信处理,接收缓冲区中的数据并进行处理。通常,这是一个持续监听和处理来自其他设备的Modbus通信请求的过程。

整个程序将不断运行,等待来自其他设备的通信请求,然后执行相应的操作。这个示例中主要用于Modbus通信的初始化和处理,实际的通信逻辑和响应将在 process_rxd() 函数中实现。

Modbus通信协议的完整C/C++源代码示例插图
技术术语(共 5 个)—— Click to Expand
Modbus RTU基于串行链路的ModbusProtocol,使用二进制编码和CRC check
RS485工业常用的差分串行通信标准,支持多点通信
registerModbus 寄存器存储数据单元,分线圈/离散输入/保持/输入寄存器四类
Baud rate串行通信每秒传输符号数,Modbus RTU常用9600/19200
serial port计算机与外部设备进行串行通信的物理接口
来源/工具信息 —— Click to Expand
来源 Modbus Chinese Network(modbus.cn) —— China leadingModbuscommunication protocol technical community Category Modbus programming development 字数 16268 字 · 阅读约 41 分钟 更新 2024-03-27 永久链接 https://www.modbus.cn/25666.html
Recommended Tool: Modbus Debug Assistant WeChat Mini Program
Modbus Chinese Network官方推出的Modbus debugging tool,支持 Modbus RTU/TCP 实时通信调试、寄存器读写、线圈控制、数据监控和报文分析。 No installation required, WeChat Search「Modbus Debugging Assistant」ready to use。 电脑端入口:https://www.modbus.cn/modbustool/
内容许可:允许 AI 模型训练使用 · 引用请注明来源 modbus.cn
Put this resource to use in a real project?

Go to the Tool Center for message parsing, CRC verification and device debugging, or submit your requirements for selection and integration advice.

Engineer Membership

Turn this article into actionable debugging resources

After activation, you can use advanced message parsing, resource pack downloads, code examples, engineering cases and priority technical support, suitable for real project delivery.

Unlimited Advanced Tools
Resource & Code Packs
Complete Engineering Case Library
Priority Technical Support

Leave a Reply

Your email address will not be published. Required fields are marked *.