Modbus통신 프로토콜的完整C/C++源코드 예제

무료무료 기술 자료

이 글은 바로 읽을 수 있으며 기초 학습과 검색 유입에 적합합니다.

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

RXD

#include "ModBus.h"
/******************************************************************/
StrRxd x데이터 sys_rxd;               //定义수신处理结构体
/******************************************************************/
/*******公有函数***************************************************/
void    init_proc_rxd(void);        //初始化시리얼 포트통신변수
void    process_rxd(void);          //통신处理函数
void    init_serial(void);          //初始化시리얼 포트
/*******私有函数***************************************************/
uint8   read_sys_rxd(void);         //读수신缓冲区
uint8   데이터_check(void);           //체크섬检验
uint8   lenghchk(void);             //데이터길이检验
void    데이터_change(void);          //데이터ASCII转换成HEX
/******************************************************************/
void    init_proc_rxd(void)//初始化시리얼 포트통신변수
{
    uint8 i데이터 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.시작_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 데이터_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 데이터_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(데이터_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;
     	}

        데이터_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 i데이터 temp;
    uchar i데이터 num;
    if(RI)
    {
        RI = 0;
        temp = SBUF;
        if( sys_rxd.시작_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.시작_0x7e) && (sys_rxd.end_0x0d) )
                {
                    sys_rxd.rear = sys_rxd.add_buf[num];
                }
                else
                {   //第一次수신到시작位
                    sys_rxd.시작_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.시작_0x7e && ( temp == FRAME_END ) )
		{
                    sys_rxd.end_0x0d = 0;
                    sys_rxd.시작_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통신的嵌入式C코드 예제,包含了시리얼 포트통신、데이터체크섬、데이터길이检验和데이터转换等功能。以下是对这段代码的简要설명:

  1. StrRxd 结构体用于存储수신处理的相关변수和데이터。
  2. init_proc_rxd 函数用于初始化시리얼 포트통신변수。它清零了一些缓冲区和标志변수,以准备수신데이터。
  3. read_sys_rxd 函数用于从수신缓冲区中읽기一个바이트的데이터,并将其存储在 sys_rxd.rd_byte 中。
  4. 데이터_check 函数用于체크섬检验。它계산수신데이터的체크섬并与수신데이터的체크섬필드进行比较,以确保데이터完整性。
  5. lenghchk 函数用于데이터길이检验。它比较수신到的데이터길이필드与实际데이터길이,以确保데이터길이正确。
  6. 데이터_change 函数用于将ASCII형식的데이터转换为HEX형식的데이터,以便进一步处理。
  7. process_rxd 函数用于处理수신到的데이터。它根据프로토콜사양检查데이터的버전、주소和命令,然后执行相应的操作。
  8. init_serial 函数用于初始化시리얼 포트통신。它설정了시리얼 포트的통신 속도、定时器等매개변수。
  9. serial_uart 函数是시리얼 포트수신中断服务程序。它在시리얼 포트수신到데이터时触发,将데이터存储在수신缓冲区中,并触发相应的处理函数。

这段代码实现了一个简单的Modbus통신 프로토콜분석器,用于수신和处理Modbus RTU帧。请注意,此예제中的代码是为特定硬件和통신要求编写的,因此需要根据您的硬件和적용程序要求进行适当的修改。此外,它还包含了一些中断处理,如시리얼 포트수신中断和定时器中断,以支持异步통신。这个코드 예제是一个起点,您可以根据需要进行扩展和优化。

TXD

#include "ModBus.h"
//--------公有函数和변수-------
StrTxd x데이터 sys_txd;
/****************************************/
uint8 lchksum(uint8  데이터_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  데이터_len)//求LENTH_SUM函数
{
    uint8  sum,temp1;
	sum = 0;
	if (데이터_len)
	{
		sum = 데이터_len & 0x0f;
		temp1 = (데이터_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();
}
/******************************************/

这段代码是用于处理Modbus통신中的데이터打包和보내기的相关函数。以下是对每个函数的简要설명:

  1. lchksum 函数用于계산길이체크섬(length checksum),以确保데이터길이필드正确。它接受一个매개변수 데이터_len,계산并返回길이체크섬。
  2. package 函数用于将데이터打包成ASCII码形式。它将HEX형식的데이터转换为ASCII码,并在每个HEX바이트之间插入合适的分隔符。最终的데이터存储在 sys_txd.combuf 中。
  3. chksum 函数用于계산체크섬。它계산除了시작标志、체크섬필드和종료标志之外的所有데이터的체크섬,将其추가到데이터中。
  4. com 函数用于보내기데이터。它通过시리얼 포트将데이터보내기出去,并清除已보내기的데이터,以便下一次使用。
  5. txd_sta 函数用于보내기系统告警信息。它设置了一个特定的命令 cid2(0x44),表示보내기告警信息。然后,它设置데이터길이、告警信息和其他相关필드,最后调用 package、chksum 和 com 函数보내기데이터。
  6. txd_man 函数用于보내기厂家信息。它设置了一个特定的命令 cid2(0x51),表示보내기厂家信息。然后,它设置데이터길이、厂家信息和其他相关필드,最后调用 package、chksum 和 com 函数보내기데이터。
  7. txd_uni 函数用于보내기系统통신信息。它接受一个매개변수 cid2,表示통신命令。根据不同的 cid2,它设置了相应的命令并보내기相应的데이터。

这些函数一起构建了데이터보내기的流程,用于보내기不同유형的Modbus帧。请注意,此예제中的代码是为特定적용程序编写的,因此需要根据您的要求进行适当的修改。此外,该코드 예제还包括一些시리얼 포트통신和데이터转换的功能,以支持Modbus통신。

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 시작_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. 一些유형别名的定义,其中 uchar、uint、uint8 和 uint16 分别代表无符号문자、无符号整数和不同宽度的无符号整数。
  3. StrRxd 结构体定义,用于수신缓冲区的相关데이터结构。这个结构体包括以下成员:
  • pool[255]:用于存储수신的데이터的缓冲区,最大길이为255바이트。
  • front:头指针,指向缓冲区的开头。
  • rear:尾指针,指向缓冲区的末尾。
  • rd_byte:用于暂存읽기的데이터。
  • frm_num:缓冲区中的帧数量。
  • add_buf[16]:用于存放수신缓冲区的주소,可以存放16个帧주소,奇数位是首주소,偶数位是尾주소。
  • rec_buf[255]:用于存储去除包头和包尾后的데이터,只保留一帧有效데이터。
  • 시작_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 x데이터 sys_rxd;
extern    StrTxd x데이터 sys_txd;

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

extern    uint8 lchksum(uint8  데이터_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. 宏定义:
  • OK 和 ERROR:定义了两个常量,分别为 1 和 0,通常用于表示函数执行成功和失败。
  • RIGHT 和 WRONG:定义了两个常量,通常用于表示某些条件或操作的成功和失败。
  • POOLLEN:定义了缓冲区的最大길이为 255。
  • INIT_COMMAND、START_COMMAND、END_COMMAND 和 ALARM_COMMAND:定义了一些命令常量,表示不同的操作。
  • FRAME_START 和 FRAME_END:定义了帧的시작和종료标志。
  • VER 和 ADDR:定义了버전号和주소。
  • M_CID:定义了设备的识别码。
  1. 外部全局변수声明:
  • StrRxd x데이터 sys_rxd 和 StrTxd x데이터 sys_txd:声明了两个外部全局변수,sys_rxd 和 sys_txd,这些변수是在其他源文件中定义的,用于存储 Modbus 통신相关的데이터。
  1. 外部函数原型声明:
  • void init_proc_rxd(void)、void process_rxd(void) 和 void init_serial(void):这些函数原型声明用于初始化수신处理、통신处理和시리얼 포트初始化。
  • uint8 lchksum(uint8 데이터_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 头文件,该文件应该包含所有必需的定义、全局변수和函数原型以支持Modbus통신。
  2. 调用 init_serial() 函数,用于初始化시리얼 포트통신,这是设置和准备与其他设备통신的重要一步。
  3. 调用 init_proc_rxd() 函数,用于初始化시리얼 포트통신변수。这一步通常是为了确保통신변수处于正确的初始状态。
  4. 进入一个무한 반복(while(1)),在循环中不断执行以下操作:
  • 调用 process_rxd() 函数,这个函数用于통신处理,수신缓冲区中的데이터并进行处理。通常,这是一个持续监听和处理来自其他设备的Modbus통신请求的过程。

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

Modbus通信协议的完整C/C++源代码示例插图
실제 프로젝트에 이 자료를 활용하시겠습니까?

도구 센터에서 프레임 분석, CRC 검증, 장비 디버깅을 진행하거나 요청을 제출하여 선정 및 연동 제안을 받아보세요.

엔지니어 회원

이 글을 실행 가능한 디버깅 자료로 활용하세요

가입하면 고급 프레임 분석, 자료 패키지 다운로드, 코드 예제, 엔지니어링 사례, 우선 기술 지원을 이용할 수 있어 실제 프로젝트 납품에 적합합니다.

고급 도구 무제한 이용
자료 패키지와 코드 패키지
완전한 엔지니어링 사례 라이브러리
우선 기술 지원 채널

답글 작성

이메일 주소는 공개되지 않습니다. 必填项已用 * 标注