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协议使用更加灵活方便。面对不同的应用,只需要对点表的增删改查即可,不需要对整个软件框架进行修改,具有高度的可移植性。