Modbus通信プロトコル完全に。C/C++源コード例

freeFree Technical Resource

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

Modbus通信プロトコル完全に。C/C++源コード例

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:定义了2つ常量,分别为 1 和 0,通常用于表示函数执行成功和失敗。
  • RIGHTWRONG:定义了2つ常量,通常用于表示某些条件或操作的成功和失敗。
  • POOLLEN:定义了バッファ的最大長さ为 255。
  • INIT_COMMANDSTART_COMMANDEND_COMMANDALARM_COMMAND:定义了一些命令常量,表示不同的操作。
  • FRAME_STARTFRAME_END:定义了フレーム的起始和结束标志。
  • VERADDR:定义了版本号和アドレス。
  • M_CID:定义了デバイス的识别码。
  1. 外部全局变量声明:
  • StrRxd xdata sys_rxdStrTxd xdata sys_txd:声明了2つ外部全局变量,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通信リクエスト的过程。

整1つの手続き将不断运行,待機中来自其他デバイス的通信リクエスト,然后执行相应的操作。这个サンプル中主要用于Modbus通信的初期化和処理,实际的通信逻辑和レスポンス将在 process_rxd() 函数実現しました。

Modbus通信プロトコル完全に。C/C++源コード例イラスト
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 *.