Modbus RTC 协议实现点表查询功能

Modbus 协议是常见的一种通信协议,那么在应用层软件上怎么去设计,才能使其能够快速的响应以及具有高度的可移植呢?在这里介绍一个我经常用到的一个Modbus协议软件设计框架。即通过查表的方式,快速的去响应主从机的动作。

Modbus RTC 协议的帧格式如下:

发送:

从站地址功能码起始地址线圈数量CRC
1字节(1~147)1字节(0x1)2字节(0x0000~0xFFFF)2字节(1~2000)2字节

接收:

从站地址功能码字节数线圈状态CRC
1字节(1~147)1字节(0x1)1字节(N)N字节2字节

根据协议的帧格式,点表中,需要添加的几个元素为:

1、寄存器首地址

2、寄存器个数

3、数据类型

4、占的比重

5、读写的数据

6、回调函数

通过代码实现如下:

/*数据类型*/
typedef enum
{
    MBDG_TYPE_U8,
    MBDG_TYPE_S8,
    MBDG_TYPE_U16,
	MBDG_TYPE_S16,
    MBDG_TYPE_U32,
	MBDG_TYPE_STR,
}T_Data_Type;
/* 读写属性定义 */
typedef enum
{
	READ_ONLY = 1,
	WRITE_ONLY,
	RW_BOTH,  
}E_Attribute;
typedef struct
{
	u16 uRegAddr;
	u16 uRegLen;
    u16 uType;
    u16 uRate;
    u16 uAttr;
	void *pData;
	void (*pFunc)();
}T_MB_Para;
#definePARALLEL_MODULE_PROTO_TYPE_ADR 0x00
u16 Proto_Type = 0;
/*点表数据*/
static const T_MB_Para tMBpara[] = {
{PARALLEL_MODULE_PROTO_TYPE_ADR,1,MBDG_TYPE_U16,1,READ_ONLY,&Proto_Type,NULL},}

#define MBDG_PARA_LEN   (sizeof(tMBpara) / sizeof(tMBpara[0]))

        在这里,tMBpara[]就是开头提到的点表,把需要读写是寄存器直接在tMBpara[]里面添加就行,通信时,直接去操作这个数组即可。

读动作:

s32 FullinModbusData(u16 StartAddr, u16 EndAddr, u16 i, u8 *Data)
{
    u8 Trans[4] = {0};
	u16 CopyStart, CopyEnd;
	u16 j;
    if(NULL == tMBpara[i].pData)
	{
		if(NULL != tMBpara[i].pFunc)
		{
            tMBpara[i].pFunc();
		}
		return -1;
	}
    if(StartAddr >= tMBpara[i].uRegAddr)
	{
		CopyStart = StartAddr;
	}
    else
	{
		CopyStart = tMBpara[i].uRegAddr;
	}
    if(EndAddr >= tMBpara[i].uRegAddr + tMBpara[i].uRegLen)
    {
        CopyEnd = tMBpara[i].uRegAddr + tMBpara[i].uRegLen;
    }
    else
	{
	    CopyEnd = EndAddr;
	}
    switch(tMBpara[i].uType)
	{
		case MBDG_TYPE_U8:
		case MBDG_TYPE_S8:
        for(j=CopyStart; j<CopyEnd; j++)
		{
            *(Data+(j - StartAddr)*2+1) = *((u8*)tMBpara[i].pData + 
                (j - tMBpara[i].uRegAddr))*tMBpara[i].uRate;
        }   
        break;
        case MBDG_TYPE_U16:
		case MBDG_TYPE_S16:
        for(j=CopyStart; j<CopyEnd; j++)
	    {
            *(u16*)Trans = *((u16*)tMBpara[i].pData+
								(j - tMBpara[i].uRegAddr))*tMBpara[i].uRate;
            *(Data+(j - StartAddr)*2) = Trans[1];
			*(Data+(j - StartAddr)*2+1) = Trans[0];
            Trans[0] = 0;
			Trans[1] = 0;
        }
        break;
        case MBDG_TYPE_U32:
		for(j=CopyStart; j<CopyEnd; j++)
		{    
            *(u32*)Trans = *((u32*)tMBpara[i].pData+
								(j - tMBpara[i].uRegAddr))*tMBpara[i].uRate;
            *(Data+(j - StartAddr)*2) = Trans[3];
			*(Data+(j - StartAddr)*2+1) = Trans[2];
            *(Data+(j - StartAddr)*2+2) = Trans[1];
			*(Data+(j - StartAddr)*2+3) = Trans[0]; 
            Trans[0] = 0;
			Trans[1] = 0;
			Trans[2] = 0;
			Trans[3] = 0;
        } 
        break;
        case MBDG_TYPE_STR:
        break;
    }
    return 0;
}

   写动作:

s32 ModbusRtuWriteData(u16 StartAddr, u16 EndAddr, u16 i, u8 *Data)
{
    u8 Trans[4] = {0};
	u16 CopyStart, CopyEnd;
	u16 j;
   
    if(StartAddr >= tMBpara[i].uRegAddr)
	{
		CopyStart = StartAddr;
	}
    else
	{
		CopyStart = tMBpara[i].uRegAddr;
	}
    if(EndAddr >= tMBpara[i].uRegAddr + tMBpara[i].uRegLen)
    {
        CopyEnd = tMBpara[i].uRegAddr + tMBpara[i].uRegLen;
    }
    else
	{
	    CopyEnd = EndAddr;
	}
    switch(tMBpara[i].uType)
	{
		case MBDG_TYPE_U8:
		case MBDG_TYPE_S8:
        
        break;

        case MBDG_TYPE_U16:
		case MBDG_TYPE_S16:
        for(j=CopyStart; j<CopyEnd; j++)
	    {
            Trans[0] = *(Data+((j - StartAddr)*2));
			Trans[1] = *(Data+((j - StartAddr)*2+1));
            *((u8*)tMBpara[i].pData + (j - tMBpara[i].uRegAddr)*2) = Trans[1];
            *((u8*)tMBpara[i].pData + (j - tMBpara[i].uRegAddr)*2+1) = Trans[0];
        }
        break;

        case MBDG_TYPE_U32:
		
        break;
       default:
        break;
    }
    if(NULL != tMBpara[i].pFunc)
	{
		tMBpara[i].pFunc();
	}
    return 0;
}

        通过调用这两个函数,可以实现对点表的读写操作。通过操作点表,Modbus协议使用更加灵活方便。面对不同的应用,只需要对点表的增删改查即可,不需要对整个软件框架进行修改,具有高度的可移植性。