Complete C/C++source code example of Modbus communication protocol

freeFree Technical Resource

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

Complete C/C++source code example of Modbus communication protocol

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);          //数据ASCII转换成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)//数据ASCII转换成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))  //设备ID错
            {
                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);  //设备ID错
                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;       //串口1方式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;
                }
            }
        }
    }
}

This is an embedded C code example for handling Modbus communication, which includes functions such as serial communication, data verification, data length checking, and data conversion. Here is a brief explanation of this code: The

  1. StrRxdStructures are used to store variables and data related to receiving and processing.
  2. init_proc_rxdThe function is used to initialize serial communication variables. It cleared some buffer and flag variables in preparation for receiving data.
  3. read_sys_rxdfunction is used to read one byte of data from the receive buffer and store it insys_rxd.rd_byte. The
  4. data_checkfunction is used for checksum verification. It calculates the checksum of the received data and compares it with the checksum field of the received data to ensure data integrity. The
  5. lenghchkfunction is used for data length verification. It compares the received data length field with the actual data length to ensure that the data length is correct. The
  6. data_changefunction is used to convert ASCII format data to HEX format data for further processing. The
  7. process_rxdfunction is used to process the received data. It checks the version, address, and command of data according to protocol specifications, and then performs corresponding operations. The
  8. init_serialfunction is used to initialize serial communication. It is configured with parameters such as baud rate and timer for the serial port. The
  9. serial_uartfunction is a serial port receive interrupt service program. It triggers when data is received through the serial port, stores the data in the receive buffer, and triggers the corresponding processing function. This code implements a simple Modbus communication protocol parser for receiving and processing Modbus RTU frames. Please note that the code in this example is written for specific hardware and communication requirements, so it needs to be appropriately modified according to your hardware and application requirements. In addition, it also includes some interrupt handling, such as serial port receive interrupts and timer interrupts, to support asynchronous communication. This code example is a starting point that you can extend and optimize as needed.

This code implements a simple Modbus communication protocol parser for receiving and processing Modbus RTU frames. Please note that the code in this example is written for specific hardware and communication requirements, so it needs to be appropriately modified according to your hardware and application requirements. In addition, it also includes some interrupt handling, such as serial port receive interrupts and timer interrupts, to support asynchronous communication. This code example is a starting point that you can extend and optimize as needed.

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;                      //地址,主机
    sys_txd.combuf[3] = M_CID;                     //设备ID
//------以上是包头---------
    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;                      //地址,主机
    sys_txd.combuf[3] = M_CID;                     //设备ID
	//----------以上是包头
//-----------------------------------------------------------------
	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;                      //地址,主机
    sys_txd.combuf[3] = M_CID;                     //设备ID
	//----------以上是包头
//-----------------------------------------------------------------
	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();
}
/******************************************/

This code is used to handle the functions related to data packaging and transmission in Modbus communication. The following is a brief description of each function:

  1. lchksumThe function is used to calculate the length checksum (length checksum) to ensure that the data length field is correct. It takes a parameterdata_len, calculates and returns a length checksum. The
  2. packagefunction is used to package data into ASCII code format. It converts HEX format data into ASCII code and inserts appropriate separators between each HEX byte. The final data is stored insys_txd.combuf. The
  3. chksumfunction is used to calculate the checksum. It calculates the checksum of all data except for the start flag, checksum field, and end flag, and adds it to the data. The
  4. comThe function is used to send data. It sends data out through the serial port and clears the already sent data for the next use.
  5. txd_staThe function is used to send system alarm information. It sets a specific commandcid2 (0x44) , Indicates sending alarm information. Then, it sets the data length, alarm information and other related fields, and finally callspackage, chksum And comFunction sends data.
  6. txd_manThe function is used to send manufacturer information. It sets a specific commandcid2 (0x51) , Indicates sending manufacturer information. Then, it sets the data length, manufacturer information and other related fields, and finally calls thepackage, chksum And comFunction sends data.
  7. txd_uniThe function is used to send system communication information. It accepts a parametercid2, represents a communication command. Depending on thecid2It sets corresponding commands and sends corresponding data.

These functions together construct the data transmission process for sending different types of Modbus frames. Please note that the code in this example is written for a specific application, so it needs to be modified appropriately according to your requirements. In addition, the code example also includes some serial communication and data conversion functions to support Modbus communication.

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

This is a C language header file (MODBUSSTR.H) that defines data structures and related type aliases for Modbus communication. The following is the main content defined in this header file:

  1. Header file protection macro definition#ifndef MODBUSSTR_H And #define MODBUSSTR_HTo ensure that the file is only included once.
  2. Some definitions of type aliases, includinguchar, uint, uint8 And uint16Representing unsigned characters, unsigned integers, and unsigned integers of different widths.
  3. StrRxdStructure definition, used to receive the relevant data structures of the buffer. This structure includes the following members:
  • pool[255]: a buffer used to store received data, with a maximum length of 255 bytes.
  • front: Head pointer, pointing to the beginning of the buffer.
  • rear: Tail pointer, pointing to the end of the buffer.
  • rd_byte: Used for temporarily storing read data.
  • frm_num: The number of frames in the buffer.
  • add_buf[16]: Used to store the address of the receive buffer, which can hold 16 frame addresses, with odd bits being the first address and even bits being the last address.
  • rec_buf[255]: Used to store data after removing packet headers and tails, retaining only one frame of valid data.
  • start_0x7e: Used to indicate whether the frame header flag has been received.
  • end_0x0d: Used to indicate whether the frame tail flag has been received.
  1. StrTxdStructure definition, used to send data structures related to the buffer. This structure includes the following members:
  • pool[255]: a buffer in ASCII format used to store sent data, with a maximum length of 255 bytes.
  • combuf[255]: A HEX format buffer used to store sent data, with a maximum length of 255 bytes.
  • txd_len: The length of the transmitted data. This header file defines the data structures used for Modbus communication and can be included in applications to access these data structures and type aliases. This helps to better organize and manage data in Modbus communication. This code is a header file containing some macro definitions, data structures, and other related header files, as well as defining some global variables and function prototypes.

This header file defines the data structures used for Modbus communication and can be included in applications to access these data structures and type aliases. This helps to better organize and manage data in Modbus communication.

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);

This code is a header file that contains some macro definitions and data structures"ModBusStr.h"And other related header files"reg51.h", as well as defining some global variables and function prototypes.

The following are some important elements in the code:

  1. The header file contains:
  • "reg51.h": This is a standard header file for 8051 series microcontrollers, providing register and macro definitions related to this series of chips.
  • "ModBusStr.h": This is a header file that you created yourself, containing data structures and type aliases related to Modbus communication.
  1. Macro Definition:
  • OK And ERROR: It defines two constants, 1 and 0, which are usually used to indicate the success or failure of function execution.
  • RIGHT And WRONG: Two constants are defined, typically used to represent the success and failure of certain conditions or operations.
  • POOLLEN: The maximum length of the buffer is defined as 255.
  • INIT_COMMAND, START_COMMAND, END_COMMAND And ALARM_COMMAND: Defined some command constants to represent different operations.
  • FRAME_START And FRAME_END: Defined the start and end flags for frames.
  • VER And ADDR: Defined version number and address.
  • M_CID: Defined the identification code of the device.
  1. External Global Variable Declaration:
  • StrRxd xdata sys_rxd And StrTxd xdata sys_txd: Declares two external global variables,sys_rxd And sys_txd, which are defined in other source files and used to store Modbus communication related data.
  1. External Function Prototype Declaration:
  • void init_proc_rxd(void), void process_rxd(void) And void init_serial(void): These function prototype declarations are used for initializing receive processing, communication processing, and serial port initialization.
  • uint8 lchksum(uint8 data_len), void txd_sta(void), void txd_man(void) and void txd_uni(uint8 cid2): These function prototypes are declared for calculating length checksum, sending system alarm information, sending manufacturer information, and sending system communication information.

This code organizes the data and functions related to Modbus communication, and provides these definitions and declarations to other source files for use through header files. The source file can contain this header file to access these definitions and declarations, thereby enabling Modbus communication functionality.

MainTest

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

This is an example of the main program on an 8051 microcontroller, used to perform Modbus communication processing. In the main function, it operates according to the following steps:

  1. contains theModBus.hheader file, which should contain all necessary definitions, global variables, and function prototypes to support Modbus communication.
  2. Call theinit_serial()function to initialize serial communication, which is an important step in setting up and preparing for communication with other devices.
  3. Call theinit_proc_rxd()function to initialize serial communication variables. This step is usually taken to ensure that the communication variables are in the correct initial state.
  4. enters an infinite loop (while(1)) where the following operations are continuously performed:
  • calls theprocess_rxd()function, which is used for communication processing, receiving data from the buffer and processing it. Usually, this is a process of continuously monitoring and processing Modbus communication requests from other devices.

The entire program will run continuously, waiting for communication requests from other devices, and then perform the corresponding operations. This example is mainly used for initialization and processing of Modbus communication, and the actual communication logic and response will beprocess_rxd()function.

Complete C/C++source code example of Modbus communication protocolFigure
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 *.