modbus绑定变量,并发送8位数据的办法

简单介绍一下modbus,在输入寄存器和保持寄存器的操作里,这里只能存储16位数据。如图:

需求一:

1.如果已经定义了一些变量,或者结构体了,那么我该如何去通过modbus,来维护这些变量呢?比如一个是开关变量,已经在项目中使用了。此时我添加modbus,我该如何去维护这个变量的变化呢?

方法一、写一个定时扫描的函数,来比对数组和变量的值,用来检查是否发生变化。

        这个方法本人认为,首先占用时间,其次对于数据的维护不安全,万一扫描的间隔中,数据发生变化,并且需要读取了,那么此刻读取变量了,那么变量的值就没有发生及时变化。

方法二、绑定变量,在接受时,直接修改变量。

        这个方法就是我采用的方法。

2.如果我需要发送8位数据,又不想影响数组和地址之间对应的关系,该怎么办?

方法一、所有的数据都以16位存放。超过16位的分成两个或更多。

        问题在于,如果这些变量已经定义好了呢?比如有些数据是bool类型,它已经定义好了,不能去修改。那么这时候该怎么办呢?

方法二、告知类型,根据类型,分类管理。

        这里就是我使用的方法。

分析函数:

       我主要就是解决上述两个需求。

首先我们看modbus的数据处理函数,以eMBRegHoldingCB为例:

eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
	eMBErrorCode    eStatus = MB_ENOERR;
	int             iRegIndex;

    /* it already plus one in modbus function method. */
    usAddress--;
	if((usAddress >= REG_HOLDING_START)&&\
		((usAddress+usNRegs) <= (REG_HOLDING_START + REG_HOLDING_NREGS)))
	{
		iRegIndex = (int)(usAddress - usRegHoldingStart);
		switch(eMode)
		{                                       
			case MB_REG_READ://读 MB_REG_READ = 0
        while(usNRegs > 0)
				{
					if(usRegHoldingBuf8[iRegIndex].type == REG_TYPE_U8){
						*pucRegBuffer++  = 0x00;
						*pucRegBuffer++  = (*(uint8_t*)usRegHoldingBuf8[iRegIndex].pData);
					
					}
					else if(usRegHoldingBuf8[iRegIndex].type == REG_TYPE_U16){
						*pucRegBuffer++ = (uint8_t)((*(uint16_t*)usRegHoldingBuf8[iRegIndex].pData) >> 8);            
						*pucRegBuffer++ = (uint8_t)((*(uint16_t*)usRegHoldingBuf8[iRegIndex].pData) & 0xFF); 
					}
          iRegIndex++;
          usNRegs--;					
				}                            
        break;
			case MB_REG_WRITE://写 MB_REG_WRITE = 0
				while(usNRegs > 0)
				{        
					if(usRegHoldingBuf8[iRegIndex].type == REG_TYPE_U8){
							uint8_t high = *pucRegBuffer++;   // 高字节(丢弃)
							uint8_t low  = *pucRegBuffer++;   // 低字节(有效数据)
							(void)high; // 避免编译器警告
							(*(uint8_t*)usRegHoldingBuf8[iRegIndex].pData) = low;
					}
					else if (usRegHoldingBuf8[iRegIndex].type == REG_TYPE_U16){
						(*(uint16_t*)usRegHoldingBuf8[iRegIndex].pData) =  *pucRegBuffer++ << 8;
						(*(uint16_t*)usRegHoldingBuf8[iRegIndex].pData)|= *pucRegBuffer++;
					}
          iRegIndex++;
          usNRegs--;
        }				
			}
	}
	else//错误
	{
		eStatus = MB_ENOREG;
	}	
	
	return eStatus;
}

        我们可以看到,这里它是先判断了我们读取的数据是否在地址范围内。接着通过

iRegIndex = (int)(usAddress - usRegHoldingStart);

        将起始地址找到。同时这个序号也是维护的数组的下标。这里我们就知道了,对于维护的数组。不管数据的大小,必须一个空间存放一个16位数据。

        接着看,发现后边是判断读取的模式和数据收发的方法。那么这里就是我们需要做修改的地方!

修改方法:

        首先:我们要绑定数据,那么一定是通过指针来找到数据。那么这就需要我们来修改存放的数组了。需要将数组变成存放指针的指针数组。同时,对于数据的不同大小(16位,32位无所谓,本身modbus对于这个处理就是正常的),尤其是8位数据,我们需要指明类型。ok这样我们就知道了修改的办法:

        首先我们定义一个结构体如下:

typedef enum {
    REG_TYPE_U8,
    REG_TYPE_U16,
} RegType;

typedef struct {
    RegType type;
    void *pData;
} RegMap;

        上边的枚举定义了数据的类型。这里不能有32或64位。因为正常的modbus,本身就是一个数组存放一个16位数据。我们这里主要处理的是8位和非8位数据的区别。因为两者需要不同的收发函数。

        紧接着修改数组类型

//输入寄存器内容 																															只读
//uint16_t usRegInputBuf[REG_INPUT_NREGS] = {0x1000,0x1001,0x1002,0x1003,0x1004,0x1005,0x1006,0x1007};
RegMap usRegInputBuf8[REG_INPUT_NREGS];
//输入寄存器起始地址
uint16_t usRegInputStart = REG_INPUT_START;

//保持寄存器内容																															可读可写
//uint16_t usRegHoldingBuf[REG_HOLDING_NREGS] = {0x147b,0x3f8e,0x147b,0x400e,0x1eb8,0x4055,0x147b,0x408e};
RegMap usRegHoldingBuf8[REG_HOLDING_NREGS];
//保持寄存器起始地址
uint16_t usRegHoldingStart = REG_HOLDING_START;

        我只用这两个,所以只对这两个做修改,同理其他的也是一样的。线圈部分则是会调用函数,将位一个个取出,重新变成8位数据发送而已。(注释的就是之前的样子,下边的就是现在修改后的样子。)

        接下来就是初始化这些变量了。因为我们数据是需要绑定的,还有类型也需要指定好。

简单的举个例子:

  typedef struct{
	 uint8_t data1;
	 uint16_t data2;
	 float data3;        
 }tempdata;
 tempdata temp;
void initdata()
{
uint16_t* pTemP16;
    usRegInputBuf8[0].pData = (uint8_t*)&temp.data1;
    usRegInputBuf8[0].type =  REG_TYPE_U8;
    usRegInputBuf8[1].pData = (uint16_t*)&temp.data2;
    usRegInputBuf8[1].type =  REG_TYPE_U16;
    
    pTemP16 = (uint16_t*)&temp.data3;
		usRegInputBuf8[2].pData = &pTemP16[0];
		usRegInputBuf8[2].type  = REG_TYPE_U16;
		usRegInputBuf8[3].pData = &pTemP16[1];
		usRegInputBuf8[3].type  = REG_TYPE_U16;
}

        原理很简单,对于8位数据,我们类型指定8位,对于16位,存16位。但是对于32位,我们拆成两个,分别存储前16位和后16位。那么以此类推,64位也是。

        接下来就是收发函数的修改:

eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
	eMBErrorCode    eStatus = MB_ENOERR;
	int             iRegIndex;

    /* it already plus one in modbus function method. */
    usAddress--;
	if((usAddress >= REG_HOLDING_START)&&\
		((usAddress+usNRegs) <= (REG_HOLDING_START + REG_HOLDING_NREGS)))
	{
		iRegIndex = (int)(usAddress - usRegHoldingStart);
		switch(eMode)
		{                                       
			case MB_REG_READ://读 MB_REG_READ = 0
        while(usNRegs > 0)
				{
					if(usRegHoldingBuf8[iRegIndex].type == REG_TYPE_U8){
						*pucRegBuffer++  = 0x00;
						*pucRegBuffer++  = (*(uint8_t*)usRegHoldingBuf8[iRegIndex].pData);
					
					}
					else if(usRegHoldingBuf8[iRegIndex].type == REG_TYPE_U16){
						*pucRegBuffer++ = (uint8_t)((*(uint16_t*)usRegHoldingBuf8[iRegIndex].pData) >> 8);            
						*pucRegBuffer++ = (uint8_t)((*(uint16_t*)usRegHoldingBuf8[iRegIndex].pData) & 0xFF); 
					}
          iRegIndex++;
          usNRegs--;					
				}                            
        break;
			case MB_REG_WRITE://写 MB_REG_WRITE = 0
				while(usNRegs > 0)
				{        
					if(usRegHoldingBuf8[iRegIndex].type == REG_TYPE_U8){
							uint8_t high = *pucRegBuffer++;   // 高字节(丢弃)
							uint8_t low  = *pucRegBuffer++;   // 低字节(有效数据)
							(void)high; // 避免编译器警告
							(*(uint8_t*)usRegHoldingBuf8[iRegIndex].pData) = low;
					}
					else if (usRegHoldingBuf8[iRegIndex].type == REG_TYPE_U16){
						(*(uint16_t*)usRegHoldingBuf8[iRegIndex].pData) =  *pucRegBuffer++ << 8;
						(*(uint16_t*)usRegHoldingBuf8[iRegIndex].pData)|= *pucRegBuffer++;
					}
          iRegIndex++;
          usNRegs--;
        }				
			}
	}
	else//错误
	{
		eStatus = MB_ENOREG;
	}	
	
	return eStatus;
}

        首先时在里边进行判断,如果时8位数据,那么我们就先写入0,后再写入8位数据。如果是16位数据,那么我们就写入高字节数据,再写入低字节数据。在这个之后,再将iRegIndex增加。这样就做到了一个数组内部,每一个对应一个数据。

(提一嘴,之前想的是把数组指针定uint8_t*来索引数据,也就是每一个数组来存8位数据,发送时候一次性发送两个,凑成一个16位数据。但是我发现这样还是有问题,与接收方是不对应的。同时你想要写数据的时候,如果是两个uint8_t数据,这时候,你一写,因为你写的必须给16位数据。那么就会将两个全部写掉。但是实际上你需要的只是写一个。这就导致了数据的不稳定。)

所以现在这个样子大体算是最好的方法了。

如果有好的方法,也可以分享。其他的也是照样子改,至于线圈部分,我就不做修改了。但是原理相同,我也修改过,但是感觉没必要。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值