Vollständige C / C + + - Quellcodebeispiele für das Modbus-Kommunikationsprotokoll

kostenlosKostenloses technisches Material

Dieser Inhalt ist direkt lesbar und eignet sich für das Grundlagenlernen und die Suche.

Vollständige C / C + + - Quellcodebeispiele für das Modbus-Kommunikationsprotokoll

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

Dies ist ein Beispiel für ein eingebettetes C-Code zur Verarbeitung der Modbus-Kommunikation mit Funktionen wie serieller Port-Kommunikation, Datenüberprüfung, Datenlängeüberprüfung und Datenkonvertierung. Hier ist eine kurze Beschreibung dieses Codes:

  1. StrRxdStrukturen werden verwendet, um relevante Variablen und Daten für die Empfangsverarbeitung zu speichern.
  2. init_proc_rxdDie Funktion wird verwendet, um die Variablen der seriellen Kommunikation zu initialisieren. Es löscht einige Puffer - und Flag-Variablen auf Null, um die Daten vorzubereiten.
  3. read_sys_rxdDie Funktion wird verwendet, um ein Byte von Daten aus dem Empfangspuffer zu lesen und es in einemsys_rxd.rd_bytezu speichern.
  4. data_checkDie Funktion wird für die Überprüfung von Summen verwendet. Es berechnet die Prüfsumme der empfangenen Daten und vergleicht sie mit dem Prüfschutzfeld der empfangenen Daten, um die Datenintegrität zu gewährleisten.
  5. lenghchkDie Funktion wird für den Datenlänge-Test verwendet. Es vergleicht das empfangene Datenlänge Feld mit der tatsächlichen Datenlänge, um sicherzustellen, dass die Datenlänge korrekt ist.
  6. data_changeDie Funktion wird verwendet, um Daten im ASCII-Format in Daten im Hex-Format für die weitere Verarbeitung zu konvertieren.
  7. process_rxdDie Funktion wird verwendet, um die empfangenen Daten zu verarbeiten. Es prüft die Version, die Adresse und die Befehle der Daten anhand der Protokollspezifikation und führt dann entsprechende Aktionen aus.
  8. init_serialDie Funktion wird verwendet, um die serielle Kommunikation zu initialisieren. Es konfiguriert Parameter wie die Baudrate und den Timer des seriellen Anschlusses.
  9. serial_uartDie Funktion ist ein serieller Port-Empfang Interrupt-Dienstprogramm. Es wird ausgelöst, wenn Daten am seriellen Anschluss empfangen werden, speichert die Daten im Empfangspuffer und löst die entsprechende Verarbeitung Funktion aus.

Dieser Code implementiert einen einfachen Modbus-Protokoll - Parser zum Empfangen und Verarbeiten von Modbus-RTU - Frames. Beachten Sie, dass der Code in diesem Beispiel für spezifische Hardware - und Kommunikationsanforderungen geschrieben wurde und daher entsprechend Ihren Hardware - und Anwendungsanforderungen angemessen geändert werden muss. Darüber hinaus enthält es einige Interruptionsverarbeitung, wie Serien-Empfang - Interrupts und Timer-Interrupts, um asynchrone Kommunikation zu unterstützen. Dieses Code-Beispiel ist ein Ausgangspunkt, den Sie je nach Bedarf erweitern und optimieren können.

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();
}
/******************************************/

Dieser Code ist eine Funktion, die das Verpacken und Senden von Daten in der Modbus-Kommunikation verarbeitet. Hier finden Sie eine kurze Beschreibung jeder Funktion: Die Funktion

  1. lchksumwird verwendet, um die Länge-Checksumme (length checksum) zu berechnen, um sicherzustellen, dass das Datenlänge-Feld korrekt ist. Es akzeptiert einen Parameterdata_len, berechnet und gibt eine Prüfsumme der Länge zurück.
  2. packageFunktionen werden verwendet, um Daten in ASCII-Format zu verpacken. Es konvertiert Daten im HEX-Format in ASCII-Code und fügt geeignete Trennzeichen zwischen jedes HEX-Byte ein. Die endgültigen Daten werden in dersys_txd.combufgespeichert.
  3. chksumDie Funktion wird zur Berechnung der Prüfsumme verwendet. Es berechnet die Prüfsumme aller Daten außer dem Start-Flag, dem Prüfsummefeld und dem End-Flag und fügt diese zu den Daten hinzu.
  4. comFunktionen werden verwendet, um Daten zu senden. Es sendet Daten über einen seriellen Anschluss und löscht die gesendeten Daten für die nächste Verwendung.
  5. txd_staDie Funktion wird verwendet, um Systemwarnmeldungen zu senden. Er gibt einen bestimmten Befehl.cid2(0x44) ein, der den Senden einer Warnmeldung angibt. Anschließend wird die Datenlänge, die Warnmeldung und andere relevante Felder festgelegt und schließlich die FunktionpackagechksumcomFunktionen senden Daten.
  6. txd_manDie Funktion wird verwendet, um Herstellerinformationen zu senden. Er gibt einen bestimmten Befehl.cid2(0x51): Anzeigt, dass die Hersteller-Informationen gesendet werden. Anschließend wird die Datenlänge, die Herstellerinformationen und andere relevante Felder festgelegt und schließlich die Funktionpackagechksumcomaufgerufen, um die Daten zu senden.
  7. txd_uniDie Funktion wird verwendet, um Systemkommunikationsinformationen zu senden. Es akzeptiert einen Parametercid2, der den Kommunikationsbefehl darstellt. Abhängig voncid2setzt er den entsprechenden Befehl und sendet die entsprechenden Daten.

Diese Funktionen erstellen zusammen den Prozess des Datensendens, um verschiedene Modbus-Frame - Arten zu senden. Beachten Sie, dass der Code in diesem Beispiel für eine spezifische Anwendung geschrieben wurde und daher entsprechend Ihren Anforderungen angepasst werden muss. Darüber hinaus enthält das Codebeispiel einige Funktionen für die serielle Kommunikation und Datenkonvertierung zur Unterstützung der Modbus-Kommunikation.

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

Dies ist eine C-Headerdatei (MODBUSSTR.H), die die Datenstrukturen und die zugehörigen Typen-Aliases für die Modbus-Kommunikation definiert. Hier sind die wichtigsten Elemente, die in dieser Header-Datei definiert sind:

  1. Header-Datei - Schutz Makro-Definition#ifndef MODBUSSTR_H#define MODBUSSTR_H, um sicherzustellen, dass die Datei nur einmal enthalten wird.
  2. Definition für einige Typen von Alias, wobeiucharuintuint8uint16unsignierte Zeichen, unsignierte Ganzzahl und unsignierte Ganzzahl unterschiedlicher Breite darstellt.
  3. StrRxdStruktur-Definition, die für den Empfang der zugehörigen Datenstruktur des Puffers verwendet wird. Diese Struktur umfasst folgende Mitglieder:
  • pool[255]: Der Puffer zum Speichern empfangener Daten mit einer maximalen Länge von 255 Bytes.
  • front: Der Kopfzeiger zeigt auf den Anfang des Puffers.
  • rear: Endzeiger, der auf das Ende des Puffers zeigt.
  • rd_byte: Speichern von gelesenen Daten.
  • frm_num: Die Anzahl der Frames im Puffer.
  • add_buf[16]: Die Adresse des Empfangspuffers kann 16 Frame-Adressen gespeichert werden, ungerade Bits sind die Anfangs-Adresse, gerade Bits sind die End-Adresse.
  • rec_buf[255]: Speichert die Daten nach dem Entfernen von Header und End und behält nur einen Frame der gültigen Daten.
  • start_0x7e: Indiziert, ob ein Frame-Header - Flag empfangen wurde.
  • end_0x0d: Zeigt an, ob ein Frame-End - Flag empfangen wurde.
  1. StrTxdStruktur-Definition, die für die Übertragung der zugehörigen Datenstrukturen des Puffers verwendet wird. Diese Struktur umfasst folgende Mitglieder:
  • pool[255]: Puffer in Form von ASCII-Codes für die Speicherung der gesendeten Daten, maximal 255 Byte lang.
  • combuf[255]: Puffer in Hexform für die Speicherung der gesendeten Daten, maximal 255 Byte lang.
  • txd_len: Die Länge der übertragenen Daten.

Diese Header-Datei definiert die Datenstrukturen, die für die Modbus-Kommunikation verwendet werden, und Sie können diese Header-Dateien in Ihre Anwendung enthalten, um auf diese Datenstrukturen und Typ-Alias zuzugreifen. Dies hilft bei der besseren Organisation und Verwaltung von Daten in der Modbus-Kommunikation.

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

Dieser Code ist eine Header-Datei, die einige Makro-Definitionen, Datenstrukturen enthält."ModBusStr.h"für Makro-Definitionen, Datenstrukturen und andere verwandte Headerdateien"reg51.h"sowie globale Variablen und Funktionsprototypen.

Hier sind einige wichtige Elemente im Code: Die

  1. Header enthält:
  • "reg51.h": Dies ist eine Standard-Headerdatei für die 8051 - Serie von Single-Chip - Computern, die die Definitionen von Registeren und Makros für die Chips der Serie enthält.
  • "ModBusStr.h": Dies ist eine Header-Datei, die Sie selbst erstellt haben und enthält die Datenstrukturen und Typen-Alias, die für die Modbus-Kommunikation relevant sind.
  1. Definition von Macro:
  • OKERROR: definiert zwei Konstanten, 1 und 0, die normalerweise für den Erfolg und Misserfolg einer Funktion verwendet werden.
  • RIGHTWRONG: definiert zwei Konstanten, die häufig zum Erfolg oder Misserfolg bestimmter Bedingungen oder Vorgänge verwendet werden
  • POOLLEN: Die maximale Länge des Puffers ist 255 definiert.
  • INIT_COMMANDSTART_COMMANDEND_COMMANDALARM_COMMAND: Definiert Befehlkonstanten, die verschiedene Operationen darstellen.
  • FRAME_STARTFRAME_END: Die Beginn - und End-Fahlen des Frames werden definiert.
  • VERADDR: Die Versionsnummer und die Adresse werden festgelegt.
  • M_CID: Der ID-Code des Geräts wird definiert.
  1. Externe globale Variablendeklaration:
  • StrRxd xdata sys_rxdStrTxd xdata sys_txd: Zwei externe globale Variablen,sys_rxdsys_txdDiese Variablen werden in anderen Quelldateien definiert und werden für die Speicherung von Daten im Zusammenhang mit der Modbus-Kommunikation verwendet.
  1. Externe Funktionsprototypdeklaration:
  • void init_proc_rxd(void)void process_rxd(void)void init_serial(void): Diese Funktionsprototypendeklarationen werden für die Initialisierung der Empfangsverarbeitung, Kommunikationsverarbeitung und der Initialisierung der seriellen Ports verwendet.
  • uint8 lchksum(uint8 data_len)void txd_sta(void)void txd_man(void)void txd_uni(uint8 cid2): Diese Funktionsprototypen deklarieren die Längensprüfsumme, das Senden von Systemwarnmeldungen, das Senden von Herstellerinformationen und das Senden von Systemkommunikationsinformationen.

Dieser Code organisiert die Daten und Funktionen, die für die Modbus-Kommunikation relevant sind, und stellt diese Definitionen und Deklarationen anderen Quelldateien über eine Headerdatei zur Verfügung. Die Quelldatei kann diese Header-Datei enthalten, um auf diese Definitionen und Deklarationen zuzugreifen, um die Modbus-Kommunikationsfunktionen zu implementieren.

MainTest

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

Dies ist ein Beispiel für das Hauptprogramm auf einem 8051 - MCU, das die Modbus-Kommunikationsverarbeitung ausführt. In der Hauptfunktion verfolgt sie folgende Schritte:

  1. enthält dieModBus.h- Headerdatei, die alle notwendigen Definitionen, globalen Variablen und Funktionsprototypen enthält, um die Modbus-Kommunikation zu unterstützen.
  2. Aufruft dieinit_serial()- Funktion zur Initialisierung der seriellen Anschlusskommunikation, ein wichtiger Schritt zur Einrichtung und Vorbereitung der Kommunikation mit anderen Geräten.
  3. Aufruft dieinit_proc_rxd()- Funktion zur Initialisierung der Serienport-Kommunikationsvariablen. Dieser Schritt dient normalerweise dazu, sicherzustellen, dass sich die Kommunikationsvariablen im richtigen Ausgangszustand befinden.
  4. tritt in eine unendliche Schleife (while(1)) ein, in der die folgenden Aktionen fortgesetzt werden:
  • ruft dieprocess_rxd()- Funktion auf, die für die Kommunikationsverarbeitung verwendet wird, die Daten aus dem Puffer empfängt und verarbeitet. In der Regel handelt es sich um einen Prozess, der kontinuierlich Modbus-Kommunikationsanfragen von anderen Geräten überwacht und verarbeitet.

Das gesamte Programm läuft kontinuierlich, wartet auf die Kommunikationsanfrage von anderen Geräten und führt dann die entsprechenden Aktionen aus. In diesem Beispiel wird hauptsächlich die Initialisierung und Verarbeitung der Modbus-Kommunikation verwendet, wobei die tatsächliche Kommunikationslogik und die Antwort in der Funktionprocess_rxd()implementiert werden.

Vollständige C / C + + - Quellcodebeispiele für das Modbus-KommunikationsprotokollAbbildung
Relevante Etiketten
Sollte man diese Informationen für ein echtes Projekt verwenden?

Gehen Sie zum Tool Center, um Nachrichten zu analysieren, CRC-Prüfungen und Geräte-Debugging zu erledigen, oder senden Sie Anforderungen für Auswahl - und Zugangsvorschläge.

Ingenieur Mitglied

Verwandeln Sie diesen Artikel in ein umsetzbares Debuggermaterial

Erweiterte Nachrichtenanalyse, Paket-Downloads, Codebeispiele, Engineering-Szenarien und Priority-Technischer Support sind für die Real-Projekt - Bereitstellung möglich.

Unbegrenzte Werkzeuge
Datenpaket und Codepaket
Vollständige Engineering Case-Basis
Vorrangiger Zugang zum technischen Support

Antwort veröffentlichen

Ihre E-Mail - Adresse wird nicht öffentlich gemacht. Erforderliche Elemente wurden verwendet * markiert.