当前位置: 首页 > news >正文

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 = 0while(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 = 0while(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 = 0while(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 = 0while(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位数据。那么就会将两个全部写掉。但是实际上你需要的只是写一个。这就导致了数据的不稳定。)

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

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

http://www.dtcms.com/a/345086.html

相关文章:

  • Vue中的methods 和 computed
  • Linux-Makefile
  • 网络编程6(JVM)
  • 【Redis】哨兵模式和集群模式
  • 红帽认证升级华为openEuler证书活动!
  • 【学习记录】c完整线程池实现
  • 未来已来?AI 预测技术在气象、金融领域的应用现状与风险警示
  • MySQL视图详解:从基础概念到实战案例
  • 人工智能-python-深度学习-软件安装阶段
  • 第2章 cmd命令基础:执行动态链接库(rundll32)
  • 大视协作码垛机器人:定制圆形吸盘破解桶型码垛难题
  • HEVC(H.265)与HVC1的关系及区别
  • 【C初阶】数据在内存中的存储
  • 【LeetCode 热题 100】139. 单词拆分——(解法一)记忆化搜索
  • Vue 插槽(Slots)全解析1
  • 所做过的笔试真题
  • 阿里云RDS MySQL数据归档全攻略:方案选择指南
  • (LeetCode 面试经典 150 题) 124. 二叉树中的最大路径和 (深度优先搜索dfs)
  • 大麦盒子DM4036刷包推荐
  • 停车场道闸的常见形式
  • 【会议跟踪】Model-Based Systems Engineering (MBSE) in Practice 2025
  • 场景题:内存溢出 和 内存泄漏 有啥区别?
  • Python-UV
  • Android夜间模式切换及自定义夜间模式切换按钮实现快速适配夜间模式
  • LeetCode Hot 100 第一天
  • 《器件在EMC中的应用》---TVS在EMC中的应用
  • 中国大学MOOC--C语言第十一周结构类型
  • 开源版CRM客户关系管理系统源码包+搭建部署教程
  • 3D打印小批量低成本打印玩具工艺品模型-中科米堆CASAIM
  • MTK Linux DRM分析(十三)- Mediatek KMS实现mtk_drm_drv.c(Part.1)