modbus协议通信时浮点数如何发送和接收处理的解决办法
在本次做项目的时候,发现需要威纶通屏幕与stm32芯片采用modbus通信,很多时候需要处理float小数。最开始一直在纠结大小端的问题,坑了一天,理出了下面的解决办法。
注意:主机发送给从机的小数是以32位的形式发送的,所以会占用2个保持寄存器。并且本次项目的威纶通屏幕作为主机时,在发送IEEE754格式的小数时,第一个保持寄存器存放的是小数的低16位,第二个保持寄存器存放的是小数的高16位。
小数转换步骤:
1、威纶通屏幕作为主机在发送浮点数时,是以IEEE754格式发送数据,需要用到2个保持寄存器,第一个保持寄存器存放小数的低16位,第二个保持寄存器存放小数的高16位
2、在从机保持寄存器接收到小数后,需要将数据按小端格式放入一个4字节的空间中,即低地址存放低字节,高地址存放高字节
3、获取小数的4个字节空间的首地址,得到小数值
一、如何将保持寄存器中存放的IEEE754格式的小数转换成float类型
一、方法1.0
此方法是采用1个4字节的u8类型的数组,去转换主机发给从机的小数。在stm32中小数存储方式是以小端模式存放:低地址存放低字节,高地址存放高字节。
/*
函数功能:将从机保持寄存器的值转换成对应小数
函数参数: HoldAddr ----从机保持寄存器接收到的小数存放的起始地址*/
float DataConvertFloat(u16 HoldAddr)
{
u8 str[4];
float temp;
str[0] = (u8 )(usSRegHoldBuf[HoldAddr]&0xff);//得到保持寄存器HoldAddr的低8位,也就是小数的0~7位
str[1] = (u8 )(usSRegHoldBuf[HoldAddr]>>8); //得到保持寄存器HoldAddr的高8位,也就是小数的8~15位
str[2] = (u8 )(usSRegHoldBuf[HoldAddr+1]&0xff);//得到保持寄存器(HoldAddr+1)的低8位,也就是小数的16~23位
str[3]= (u8 )(usSRegHoldBuf[HoldAddr+1]>>8);//得到保持寄存(HoldAddr+1)的高8位,也就是小数的24`31位
temp = *((float *)str);//得到小数,
return temp;
}
/*此处有用到保持寄存器的数组usSRegHoldBuf,这个我项目由于要多次使用到,我已经把
这个保持寄存器定义为了全局变量*/
#define S_REG_HOLDING_NREGS 500 //保持寄存器数量
USHORT usSRegHoldBuf[S_REG_HOLDING_NREGS] ;//保持寄存器
二、方法1.1
定义一个内联类型的变量,此内联类型比较特殊
/*浮点数与IEEE754格式转换*/
typedef union
{
float ul_Temp;
u8 uc_Buf[4];//小数转换成IEEE754格式的数组
u16 us_Buf[2];//用于modbus协议小数转换的数组
}un_DtformConver;
//此方式和1.0其实差不多,不过这个方式可以运用到的别的小数转换成IEEE754的场合
float DataConvertFloat(u16 HoldAddr)
{
un_DtformConver Data;
float temp;
Data.uc_Buf[0] = (u8 )(usSRegHoldBuf[HoldAddr]&0xff);//得到保持寄存器HoldAddr的低8位
Data.uc_Buf[1] = (u8 )(usSRegHoldBuf[HoldAddr]>>8);//得到保持寄存器HoldAddr的高8位
Data.uc_Buf[2] = (u8 )(usSRegHoldBuf[HoldAddr+1]&0xff);//保持寄存器(HoldAddr+1)的低8位
Data.uc_Buf[3]= (u8 )(usSRegHoldBuf[HoldAddr+1]>>8);//保持寄存器(HoldAddr+1)的高8位
temp = Data.ul_Temp;//得到小数
return temp;
}
三、方法1.3
float DataConvertFloat(u16 HoldAddr)
{
un_DtformConver Data;
float temp;
Data.us_Buf[0] = usSRegHoldBuf[HoldAddr];//获取从机接收到的小数的低16位
Data.us_Buf[1] = usSRegHoldBuf[HoldAddr+1];//获取从机接收到的小数的高16位
temp = Data.ul_Temp;//得到小数
return temp;
}
四、方法1.4
float DataConvertFloat(u16 HoldAddr)
{
float temp;
temp = *((float *)&usSRegHoldBuf[HoldAddr]);//直接获取地址转换就可以了,因为usSRegHoldBuf[HoldAddr+1]存放的正好是小数的高16位,满足stm32的小端模式
return temp;
}
二、如何将float类型的小数转换成IEEE754格式放入到从机的保持寄存器中
/*
函数功能:将小数转换成IEEE754数据类型,用于modbus协议传输数据
函数参数:value 小数值 Holdaddr 从机保持寄存器地址
*/
//方式1.0
void Float_To_U32(float value, u16 Holdaddr)
{
un_DtformConver Data;
Data.ul_Temp = value;//获取小数值
usSRegHoldBuf[Holdaddr] = Data.us_Buf[0];//获取低16位
usSRegHoldBuf[Holdaddr+1] = Data.us_Buf[1];//获取高16位
}
//方式1.1
void Float_To_U32(float value, u16 Holdaddr)
{
un_DtformConver Data;
u16 us_temp = 0;
Data.ul_Temp = value;//获取小数值
usSRegHoldBuf[Holdaddr] = Data.uc_Buf[0]; //获取最低8位
us_temp = Data.uc_Buf[1];//获取8~15位
usSRegHoldBuf[Holdaddr] |= us_temp <<8;//低16位保存到保持寄存器中
usSRegHoldBuf[Holdaddr+1] = Data.uc_Buf[2];//获取16~23位
us_temp = Data.uc_Buf[3];//获取24~31位
usSRegHoldBuf[Holdaddr+1] |= us_temp <<8;//高16位保存到保持寄存器中
}