Modbus通信プロトコルの完全なC/C++ソースコード例

無料無料技术资料

这篇内容可直接阅读,适合用于基础学习和検索引流。

Modbus通信プロトコルの完全なC/C++ソースコード例

RXD

#include "modBus.h"
/** ****************************************************************/
StrRxd xdata_rxd;               //受信処理構造体を定義する
/** ****************************************************************/
/** **** パブリック関数 ***************************************************
void void    _proc_rxd        //シリアル通信変数の初期化
void void    _rxd          //通信処理機能
void void    _serial          シリアルの初期化。
/** **** プライベート関数 ***************************************************
Uint 8より   read_sys_rxd void;         //読み取り受信バッファ
Uint 8より   data_check void;           //チェックサム検査
Uint 8より   lenghchk void;             //データ長検定
void void    data_change void;          //ASCIIデータをHEXに変換
/** ****************************************************************/
void void    init_proc_rxd(void)//シリアル通信変数の初期化
{
    Uint 8 Idata i。
    for i = 0;i POLEN;i++
    {
        //Read ing
		sys_rxd.pool i] = 0;//受信データバッファ
        sys_rxd.rec_buf i] = 0;//パケットの末尾の後のデータ予約を削除します。有効なデータは1フレームのみを保持します。
        //write
		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_0x7 e = 0;
    sys_rxd.end_0x0 d = 0;

    sys_txd.txd_len = 0;//送信データ長
}
/** ***************************************************************/
Uint 8より 	read_sys_rxd void//受信バッファの読み取り
{
 	if_rxd.rear == sys_rxd.front
 	{
        WRONGへの復帰
    }
	elseさん
    {
        sys_rxd. rd_byte = sys_rxd.pool sys_rxd.front];
	    sys_rxd.front = sys_rxd.front +1% POOLLEN;

	    リターンライト;
    }
}
/** ***************************************************************/
uint8 data_check void//チェックサム検証
{
    uint8 add_len temp;
	Uint 8より   i、j、k、fram_;
	uint16 chk_chk__;

	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_- 4;i++          //4バイトは合計をチェックする
		{
            read__rxd;    //チャンピオンはアド1です。
        	if sys_rxd. rd_byte == FRAME_START
       	    {
        	    sys_rxd.rd_byte = 0;
        	}
	    	chk_+=_rxd._byte;
	 	}

		chk_=~chk_1;

		chk_sum_temp = 0;
		for i=0; i4;i++
		{
			read__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.fram_num--;

            txd_uni 0 x 02; //チェックサムエラー
            リターン;
     	}

        data_change;

        if sys_rxd.rec_buf1] == 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 0 x 03; //長さチェックエラー
                リターン;
            }

            if sys_rxd.rec_buf 0] != VER 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 0 x 01; //エラーのバージョン
                リターン;
            }
            if sys_rxd.rec_buf2] != M_//デバイス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が正しくない
                リターン;
            }
            switch sys_rxd.rec_buf3]
            {
                case 0x44
                    txd_sta;
               	    休憩を取る。
                case 0x4f
                    txd_uni 0x4f;      //通信プロトコルバージョン番号の送信
                    休憩を取る。
                ケース0x50
                    txd_uni 0x50;      //デバイスアドレスを送信
                    休憩を取る。
                ケース0x51
                    txd_man;      //機器メーカー情報の送信
                    休憩を取る。
                case 0 x 60
                    txd_uni 0x60;      システムの初期化
                    休憩を取る。
                default:
                    txd_uni 0 x 04; //無効なコマンド
                    休憩を取る。
            }
        }
        sys_rxd.fram_num--;
    }
}
/** ***************************************************************/
void void    init_serial(void)//シリアルの初期化
{
  	//シリアルポートボーレートなどの設定
        TMOD= 0 x 21。      // T1 mode 2 T0 mode 1 //GATE C/T M1 M0 GATE C/T M1 M0
	TL 1 = 0 x fa       //0 xfa =4800 bps 0 xfd =9600 bps    0 x 8 = 1200    0 x f 4 = 2400
	Th 1 = 0 x fa
	TH0=-10000/256;
   	TL0=-10000% 256;
        PCON=0。          //ボーレート変更なし
        SCON= 0 x 50。       //シリアル1モード1、受信可能
        T 0 =1。           //外部割込み0立下りエッジ有効
        T 1 =1。           //外部割り込み1下降エッジ有効
        TR 0 =1。           //タイマーの起動0
        TR 1 =1。           タイマーの起動1
        T 0 =1。           //オープンタイマー 0割り込み
        ES=1。            //シリアル割り込み
        EX 0 =0;           //外部割り込み0
        EX 1 =1。           //外部割り込み1
        A =1。            //オープン·ブレーク
 //       RS 485 EN = 0

}
/** ***************************************************************/
void void    serial_uart void interrupt4
{
    Uchar idata temp;
    uchar idata num;
    if RI
    {
        Ri = 0。
        temp = SBUF;
        if sys_rxd._0x7e!= ME_END
        {
            sys_rxd.pool sys_rxd.rear] = temp;
            sys_rxd.rear = sys_rxd.rear + 1% POOLLEN;
            リターン;
        }
        elseさん
        {
            if ^ME_START ==0    //パケット開始ビットを受信した場合
            {
                if sys_rxd.start_0x7e sys_rxd.end_0x0d
                {
                    sys_rxd.rear = sys_rxd._buf
                }
                elseさん
                {   //最初に開始ビットを受信
                    sys_rxd.start_0x7e = 1;
                    sys_rxd.end_0x0 d = 1;
                    =    //新しいフレームの開始アドレスインデックス
                    sys_rxd. add_buf num] = sys_rxd.rear; //開始アドレス保存
                }
                sys_rxd.pool sys_rxd.rear] = temp;
                sys_rxd.rear = sys_rxd.rear +1% POOLLEN;
                リターン;
            }
            elseさん
            {
                if sys_rxd._0x7e == ME_END
		{
                    sys_rxd.end_0x0 d = 0;
                    sys_rxd.start_0x7 e = 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= 25 5
                    {
                        sys_rxd.add_buf num] = POOLLEN-1;//書き込み後のアドレスに1を追加
                    }
                    elseさん
                    {
                        sys_rxd.add_buf num] = sys_rxd.rear-1;//書き込み後のアドレスに1が追加されます
                    }

                    sys_rxd.fram_num ++;//フレームを追加する//成功を1桁追加する
                    sys_rxd.frm_num = sys_rxd.frm_num % 16;
                    リターン;
                }
            }
        }
    }
}

これは、シリアルポート通信、データ検証、データ長チェック、データ変換などの機能を含むModbus通信を処理するための組み込みCコード例です。以下は、このコードの簡単な説明です。

  1. StrRxd構造体は、受信処理に関連する変数やデータを格納するために使用される。
  2. init_proc_rxdシリアルポート通信変数の初期化に使用される関数。データを受信する準備をするために、バッファとフラグ変数の一部をクリアします。
  3. read_sys_rxd関数は、受信バッファから1バイトのデータを読み込み、に格納するために使用されます。sys_rxd.rd_byte中で。
  4. data_checkチェックサム検査用の関数。受信データのチェックサムを計算し、受信データのチェックフィールドと比較して、データの整合性を確保します。
  5. lenghchk関数はデータ長のチェックに使用されます。受信したデータ長フィールドと実際のデータ長を比較して、データ長が正しいことを確認します。
  6. data_change関数は、ASCII形式のデータをHEX形式のデータに変換し、さらに処理するために使用します。
  7. process_rxd関数は受信したデータを処理する。プロトコル仕様に基づいてデータのバージョン、アドレス、コマンドをチェックし、適切なアクションを実行します。
  8. init_serialシリアル通信を初期化する関数。シリアルポートボーレート、タイマなどのパラメータを設定します。
  9. serial_uart関数はシリアルインタフェース受信割込みサービスプログラムです。シリアルポートがデータを受信したときにトリガし、データを受信バッファに格納し、対応する処理関数をトリガします。

このコードは、Modbus RTUフレームの受信と処理のためのシンプルなModbus通信プロトコルパーサを実装しています。この例のコードは、特定のハードウェアおよび通信要件用に書かれているため、ハードウェアおよびアプリケーション要件に応じて適切な修正が必要です。さらに、非同期通信をサポートするために、シリアルポート受信割込みやタイマ割込みなどの割込み処理を備えています。このコード例は出発点であり、必要に応じて拡張および最適化することができる。

TXD

#include "modBus.h"
//-----------------
StrTxd xdata sys_txd;
/** **************************************/
uint8lchksum uint8data_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;         //共通の送信関数
/** ****************************************/
uint8lchksum uint8data_len//LENTH_SUM関数を求める
{
    uint8sum temp1;
	sum = 0です。
	if data_
	{
		sum = data_len 0 x 0 f;
		temp1 = data_0xf0 4;
		sum += temp 1。
		sum = sum % 16;
		=
		sum = sum 4。
	}

	リターン·サム;
}
/** ****************************************/
void package(void)//データをASCIIでパッケージ化
{
    Uint 8 i。
    uint8 temp1、temp2;

    sys_txd.pool 0] = FRAME_START;

    for i = 1;i POLEN; i++
    {
	    if_txd.combuf i] == 0xaa_txd.combuf i+1] == 0x55//終了フラグ
	    {
           	sys_txd.pool i*2-1] = FRAME_END;
           	休憩を取る。
       	}
        elseさん
        {
           	temp1 = sys_txd.combuf i];
           	temp2 = temp1 0 x 0 f;
           	temp1 = temp1 0 xf0;
           	temp1 = temp1 4;
          	sys_txd.pool i*2-1] = temp1 0 x 09 temp1 + 0x37
           	sys_txd.pool i*2]   =(temp 2 0 x 0 9))temp2 + 0x37
       	}
    }
}
/** ****************************************/
void chksum void//チェックサムを求める
{
   	Uint 8 i。
   	uint8 sumh suml;
   	UINT 16   sum = 0です。

  	for i = 1;i POLEN;i++
    {
  	    if sys_txd.pool i] == FRAME_END
  	  	{
         	temp = i;
 	    	休憩を取る。
	  	}
	    elseさん
	    {
          	+=    オーバーフロー部分は考慮しない。
        }
    }

   	sum =~sum + 1;//逆+1を取る
   	sumh = uint8 sum/256;//上位8ビット
   	suml = uint8;
   	T = temp;

   	temp = sumh 0xf0 4;
   	temp =(temp 0 x 09)(temp+ 0x37 temp+0x30;
   	sys_txd.pool i]   = tempです。

   	temp = sumh 0x0f;
   	temp =(temp 0 x 0 9)(temp+ 0x37 temp+0x30;
   	sys_txd.pool i+1] = temp;

   	temp = suml 0xf0 4;
    temp =(temp 0 x 09)(temp+ 0x37 temp+0x30;
   	sys_txd.pool i+2] = temp;

    temp = suml 0x0f;
   	temp =(temp 0 x 0 9)(temp+ 0x37 temp+0x30;
  	sys_txd.pool i+3] = temp;

   	sys_txd.pool i+4] = FRAME_END;

   	sys_txd.txd_len = i+4;

   	リターン;
}
/** ****************************************/
void com void//公開の送信関数
{
    Uint 8 i。
    J 8の略。
    
    E = 0;
//    RS 485 EN = 1。    //485通信を使用する場合
    for i = 0;i =_txd.txd_;i++
    {
        SBUF = sys_txd.pool i];
        while TI===0;
        TI = 0;
        if sys_txd.pool i] == FRAME_END
        {
            for j=0;j =_txd.txd_;j++
            {
      	        sys_txd。プールi] = 0;            //送信済みのデータを削除する0
            }
        }
    }
//    RS 485 EN = 0
//    Ri = 0。   //485の受信イネーブルが有効な場合は、この行を解決する必要があります。
    A = 1。
}
/** ****************************************/
void txd_sta(void)//システムアラート情報を送信
{

    sys_txd.combuf 0] = FRAME_START;               //スタートサイン
    sys_txd.combuf 1] = VER;                       //version number
    sys_txd.combuf 2] = ADDR;                      アドレス,ホスト
    sys_txd.combuf 3] = M_CID;                     デバイスID/デバイスID
//-----------------------
    sys_txd.combuf 4] = 0x44;                      //Cid2= 0 x 44すべてのアラートステータスを送信する
//-----------------------
    sys_txd.combuf 5] = lchksum 20; //length checksum/////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] = 0 xaa;        //end ;
    sys_txd.combuf 12] = 0x55;    データをASCIIコードにまとめます。
    
    package package nCloth;              //////////////////////////////
    chk;                  //開始と終了以外のASCIIコードのチェックサム
    com;
}
/** ****************************************************/
void txd_man(void)//メーカー情報の送信
{
//-----------------------------------------------------------------
    sys_txd.combuf 0] = FRAME_START;               //スタートサイン
    sys_txd。combuf 1] = VER;                       //version number
    sys_txd.combuf 2] = ADDR;                      アドレス,ホスト
    sys_txd.combuf 3] = M_CID;                     デバイスID/デバイスID
	//-------------上記は包頭です
//-----------------------------------------------------------------
	sys_txd.combuf 4] =0x51;

	sys_txd.combuf 5] =lchksum 0x40;
	sys_txd.combuf 6] =0x40;

	sys_txd.combuf 8] = 'L';        //L
	sys_txd.combuf 8] = '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] = 0 x 41;       //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] = 0 x 00;
	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] = 0 x 0 0;
	sys_txd.combuf 38] = 0x00;

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

    package package nCloth;                 データをASCIIコードにまとめます。
	chk;                  //開始と終了以外のASCIIコードのチェックサム
    com;
}
/** ****************************************/
void txd_uni uint8 cid2//システム通信情報の送信
{
//-----------------------------------------------------------------
    sys_txd.combuf 0] = FRAME_START;               //スタートサイン
    sys_txd.combuf 1] = VER;                       //version number
    sys_txd.combuf 2] = ADDR;                      アドレス,ホスト
    sys_txd.combuf 3] = M_CID;                     デバイスID/デバイスID
	//-------------上記は包頭です
//-----------------------------------------------------------------
	sys_txd.combuf 4] = cid2;
	sys_txd.combuf 5] = lchksum 0x00;       //length checksum 0 x 0 0
	sys_txd.combuf 6] = 0x00;                //長さID 0 0

	sys_txd.combuf 7] = 0xaa;                //length id=00なので、infoはありません。
	sys_txd.combuf 8] = 0x55;
    package package nCloth;                 データをASCIIコードにまとめます。
	chk;                  //開始と終了以外の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(0 x 44)アラートを送信します。次に、データ長、アラート情報、およびその他の関連フィールドを設定し、最後に呼び出します。package、chksumそして、comデータを送信する機能
  6. txd_man関数はメーカー情報を送信するために使用する。特定のコマンドを設定します。cid2(0 x 51)はメーカー情報を送信することを示す。次に、データ長、メーカー情報、およびその他の関連フィールドを設定し、最後に呼び出します。package、chksumそして、comデータを送信する機能
  7. txd_uniシステム通信情報を送信する機能。パラメータを受け取ります。cid2通信命令を参照。別のものに基づくcid2適切なコマンドを設定し、適切なデータを送信します。

これらの関数は、異なる種類のModbusフレームを送信するデータ送信フローを構築します。この例のコードは特定のアプリケーション用に書かれているため、要件に応じて適切な修正が必要です。さらに、コード例には、Modbus通信をサポートするためのシリアルポート通信とデータ変換機能も含まれています。

ModBusStr

#ifndef MODBUSSTR_H
#define MODBUSSTR_H
//==============================================
#unsigned charの定義
#define uint unsigned int
#define uint 8 unsigned char
#16 unsigned intの定義
//受信バッファ構造変数
typedef struct str_rxd_pool
{
    [8]プール25 5]	//受信データバッファ
    Uint 8フロント。 	    //頭のポインタ
    Uint 8 rear;	    	//最後のポインタ
    uint 8 rd_byte;	    //データの読み取り
    uint 8 fram_num;	    //バッファのフレーム数
    uint8 add_buf 16];	//受信バッファを格納するために使用されるアドレスは、16個のフレームアドレスを格納でき、奇数ビットが先頭アドレス、偶数ビットが末尾アドレスです。
    uint8 rec_buf 255; //パケットの末尾のデータ予約を削除します。有効なデータは1フレームのみを保持します。

    uint8 start_0x7e;   //フレームヘッダーフラグを受信
    uint 8 end_0 x 0 d;     //フレームテールフラグを受信

)STRXD;
//===============================================
//バッファ構造体変数の送信
typedef struct str_txd_pool
{
    [8]プール25 5]	    //データを送信するASCIIバッファ
    (25 5);      //送信データHEXバッファ
    uint 8 txd_len;          送信データの長さ
)Strtxd;
#endif

これはC言語のヘッダファイルです(MODBUSSTR.HModbus通信に使用されるデータ構造と関連する型エイリアスを定義する。以下は、このヘッダファイルで定義されている主な内容です。

  1. ヘッダ·ファイル保護マクロ定義{{へっだふぁいるろくろていぎ}}#ifndef MODBUSSTR_Hそして、#define MODBUSSTR_Hファイルが一度だけ含まれるようにします。
  2. いくつかの型エイリアスの定義、uchar、uint、uint8そして、uint16符号なし文字、符号なし整数、および幅の異なる符号なし整数をそれぞれ表します。
  3. StrRxd受信バッファに使用される関連するデータ構造体定義。この構造体には以下のメンバーが含まれる。
  • pool[255]:受信したデータを格納するバッファで、最大長は255バイトです。
  • front:バッファの先頭を指すヘッダーポインタ。
  • rear::バッファの最後へのポインタ。
  • rd_byte:スクラッチ読み取りに使用されるデータ。
  • frm_num:バッファ内のフレーム数。
  • add_buf[16]受信バッファを格納するために使用されるアドレスは、16個のフレームアドレスを格納でき、奇数ビットが先頭アドレス、偶数ビットが末尾アドレスです。
  • rec_buf[255]:ヘッダーと末尾を削除したデータを格納し、有効なデータの1フレームのみを保持します。
  • 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"

#unsigned charの定義
#define uint unsigned int
#define uint 8 unsigned char
#16 unsigned intの定義

#define OK 1
#define ERROR 0

#Define RIGHT 1
#define WRONG 0


#define    プーレン25 5              //バッファサイズ

#define    _COMMAND   0 x 20
#define    START_COMMAND 0 x 21
#define    END_COMMAND    0 x 22
#define    ALARM_COMMAND 0 x 23


#define    FRAME_START    0 x 7 e         //フレーム開始フラグ
#define    ME_END      0 x 0 d         //フレーム終了フラグ
#define    VER VER VER    0 x 10
#define    ADDRは   0 x 0 1


#define    M_CID   0 x 25                //デバイス識別コード、0 x 25はDC電源キャビネットを表します。

externさん    StrRxd xdata_rxd;
externさん    StrTxd xdata sys_txd;

externさん    void void    _proc_rxd
externさん    void void    _rxd
externさん    void void    _serial

externさん    uint8lchksum uint8data_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ファミリのMCUの標準ヘッダファイルであり、ファミリのチップに関連するレジスタとマクロの定義を提供します。
  • "ModBusStr.h"これは、Modbus 通信に関連するデータ構造と型エイリアスを含む独自のヘッダーです。
  1. マクロの定義:
  • OKそして、ERROR関数の成功と失敗を表すために通常使用される2つの定数1と0を定義します。
  • RIGHTそして、WRONG::特定の条件や操作の成功と失敗を表すために通常使用される2つの定数を定義します。
  • POOLLENバッファの最大長を255と定義します。
  • INIT_COMMAND、START_COMMAND、END_COMMANDそして、ALARM_COMMAND異なる操作を表すコマンド定数を定義します。
  • FRAME_STARTそして、FRAME_END:フレームの開始フラグと終了フラグを定義します。
  • VERそして、ADDR:バージョン番号とアドレスを定義します。
  • M_CID:デバイスの識別コードを定義します。
  1. 外部グローバル変数宣言
  • StrRxd xdata sys_rxdそして、StrTxd xdata sys_txd2つの外部グローバル変数を宣言する。sys_rxdそして、sys_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 void    main
{
    init_serial();//シリアルの初期化
    init_proc_rxd();//シリアル通信変数の初期化
    while 1
    {
        process_rxd();//通信ハンドラはバッファ内のデータを処理バッファに入力する
    }
}

これはModbus通信処理を実行する8051 MCU上のメインプログラムの例です。メイン機能では、次の手順に従います。

  1. 含む。ModBus.hModbus通信をサポートするために必要なすべての定義、グローバル変数、関数プロトタイプを含むヘッダファイル。
  2. コールアウトinit_serial()シリアルポート通信を初期化する機能は、他のデバイスとの通信をセットアップして準備する上で重要なステップです。
  3. コールアウトinit_proc_rxd()シリアルポート通信変数を初期化する関数。このステップは通常、通信変数が正しい初期状態にあることを確認するために行われます。
  4. 無限ループに入る(while(1)を使用して、ループ内で次の操作を継続的に実行します。
  • コールアウトprocess_rxd()関数:通信処理に使用され、バッファ内のデータを受信して処理を行います。通常、これは他のデバイスからのModbus通信要求を継続的にリッスンして処理するプロセスです。

プログラム全体が継続的に実行され、他のデバイスからの通信要求を待ってから、適切なアクションを実行します。この例では、主にModbus通信の初期化と処理に使用され、実際の通信ロジックと応答は次のとおりです。process_rxd()機能で実装。

Modbus通信プロトコルの完全なC/C++ソースコード例插图
この資料を実際のプロジェクトに活用しませんか?

ツールセンターで電文解析、CRCチェック、デバイスデバッグを行うか、要件を送信して選定と接続のアドバイスを受けましょう。

エンジニア会員

この記事を実行可能なデバッグ資料に

登録すると、高度な電文解析、資料パックのダウンロード、コード例、エンジニアリングケース、優先技術サポートが利用可能に。実際のプロジェクト納品に最適です。

高度なツール無制限
資料パックとコードパック
完全なエンジニアリングケースライブラリ
優先技術サポート窓口

发表回复

あなたのメールアドレスアドレス不会被公开。 必須項目已用 * 标注