GXHT30可兼容SHT30的Modbus协议RS485通讯温湿度变送器
一、文章摘要
二、技术参数
三、通信协议
四、相关测试
五、参考驱动
一、文章摘要
为解决IIC通信距离短,不可多节点组网使用的缺陷,为满足具有长远距离通信、总线上多节点应用场景需求,本文介绍使用中科银河芯GXHT30C温湿度芯片设计的Modbus协议RS485通讯温湿度变送器。
RS485优点:
● RS485的接口信号与其他接口信号相比降低了,因此不易损坏芯片
● 可以有效减少噪声信号的干扰
● 通信速度快、相对稳定
● 可以延长通信的距离,最大可达2000m
● 总线可实现多节点组网应用
产品应用场景:
● HVAC 环境控制
● 粮情测控
● 智慧农业
● 智能仓储、冷链
● 工业系统
● 物联网等众多温湿度环境监测领域
二、技术参数
2.1产品功能特点
● RS485 通信接口,标准 ModBus-RTU 协议,通信线最长可达 2000 米;
● CMOS + MEMS 集成工艺,保证产品性能、可长期稳定工作;
● 温度精度±0.5℃ 、湿度精度±3%RH,高精度、低漂移;
● 5-24V 宽电压范围供电,远距离集中供电仍能正常工作;
● 产品应用简单、可无需任何外围电路器件;
● 电源防接反保护功能,正负极接反不会烧坏设备;
● 灵敏度高、反应迅速,防水、防腐蚀,高可靠性。
2.2产品功能特点
2.3产品外观形态
三、通信协议
3.1基本通信参数
3.2数据帧格式及定义
3.3通讯示例说明
四、相关测试
4.1 ESD测试
GXHT30C温湿度变送器经过ESD测试,空气放电 ±12kV、接触式放电 ±8kV。
4.2 通讯测试
常温环境进行数据收发挂测,实测15万+ 条数据收发测试 0 掉包。
4.3 一致性测试
以下为高湿环境,进行数据一致性测试,测试截图如下。
4.4 成品展示
五、GXHT30C温湿度芯片驱动
5.1 GXHT30C.c
#include "GXHT30.h"
#include "stdlib.h"
#define write 0
#define read 1
uint16_t temp_buff[2] = {0};
uint16_t hum_buff[2] = {0};
void sys_delay_us(uint16_t time)
{
for(int i = 0; i < time; i++);
}
void GXHT30Reset(void);
//初始化IIC
void GXHT30_IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GXHT30_I2C_SCL_GPIO_RCC;
GXHT30_I2C_SDA_GPIO_RCC;//使能GPIO时钟
GPIO_InitStruct.Pin = GXHT30_I2C_SCL_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GXHT30_I2C_SCL_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GXHT30_I2C_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GXHT30_I2C_SDA_GPIO_PORT, &GPIO_InitStruct);
GXHT30_SDA_H;
GXHT30_SCL_H;
HAL_Delay(10); //延时一会等待初始化稳定
GXHT30Reset(); //复位芯片
}
static void GXHT30_SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GXHT30_I2C_SDA_GPIO_RCC; //使能GPIO时钟
GPIO_InitStruct.Pin = GXHT30_I2C_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GXHT30_I2C_SDA_GPIO_PORT, &GPIO_InitStruct);
}
static void GXHT30_SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GXHT30_I2C_SDA_GPIO_RCC; //使能GPIO时钟
GPIO_InitStruct.Pin = GXHT30_I2C_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GXHT30_I2C_SDA_GPIO_PORT, &GPIO_InitStruct);
}
//产生IIC起始信号
static void GXHT30_IIC_Start(void)
{
GXHT30_SDA_OUT(); //sda线输出
GXHT30_SDA_H;
GXHT30_SCL_H;
GXHT30_Delay_us(4);
GXHT30_SDA_L;//START:when CLK is high,DATA change form high to low
GXHT30_Delay_us(4);
GXHT30_SCL_L;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
static void GXHT30_IIC_Stop(void)
{
GXHT30_SDA_OUT();//sda线输出
GXHT30_SCL_L;
GXHT30_SDA_L;//STOP:when CLK is high DATA change form low to high
GXHT30_Delay_us(4);
GXHT30_SCL_H;
GXHT30_SDA_H;//发送I2C总线结束信号
GXHT30_Delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
static uint8_t GXHT30_IIC_Wait_Ack(void)
{
uint8_t ucErrTime=0;
GXHT30_SDA_IN(); //SDA设置为输入
GXHT30_SDA_H;
GXHT30_Delay_us(1);
GXHT30_SCL_H;
GXHT30_Delay_us(1);
while(GXHT30_READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
GXHT30_IIC_Stop();
return 1;
}
}
GXHT30_SCL_L;//时钟输出0
return 0;
}
//产生ACK应答
static void GXHT30_IIC_Ack(void)
{
GXHT30_SCL_L;
GXHT30_SDA_OUT();
GXHT30_SDA_L;
GXHT30_Delay_us(2);
GXHT30_SCL_H;
GXHT30_Delay_us(2);
GXHT30_SCL_L;
}
//不产生ACK应答
static void GXHT30_IIC_NAck(void)
{
GXHT30_SCL_L;
GXHT30_SDA_OUT();
GXHT30_SDA_H;
GXHT30_Delay_us(2);
GXHT30_SCL_H;
GXHT30_Delay_us(2);
GXHT30_SCL_L;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
static void GXHT30_IIC_Send_Byte(uint8_t txd)
{
uint8_t t;
GXHT30_SDA_OUT();
GXHT30_SCL_L;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
if((txd&0x80)>>7)
GXHT30_SDA_H;
else
GXHT30_SDA_L;
txd<<=1;
GXHT30_Delay_us(2);
GXHT30_SCL_H;
GXHT30_Delay_us(2);
GXHT30_SCL_L;
GXHT30_Delay_us(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
static uint8_t GXHT30_IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
GXHT30_SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
GXHT30_SCL_L;
GXHT30_Delay_us(2);
GXHT30_SCL_H;
receive<<=1;
if(GXHT30_READ_SDA)receive++;
GXHT30_Delay_us(1);
}
if (!ack)
GXHT30_IIC_NAck();//发送nACK
else
GXHT30_IIC_Ack(); //发送ACK
return receive;
}
/**
brief 检查GXHT30是否存在
param NONE
return 0存在 1不存在
*/
uint8_t GXHT30Check(void)
{
uint8_t ack=0;
GXHT30_IIC_Start();
GXHT30_IIC_Send_Byte(GXHT30_ADDRESS);
ack=GXHT30_IIC_Wait_Ack();
GXHT30_IIC_Stop();
return ack;
}
/**
brief GXHT30软复位
param NONE
return NONE
*/
void GXHT30Reset(void)
{
GXHT30_IIC_Start();
GXHT30_IIC_Send_Byte(GXHT30_ADDRESS << 1 | write);
GXHT30_IIC_Wait_Ack();
GXHT30_IIC_Send_Byte(0x30);
GXHT30_IIC_Wait_Ack();
GXHT30_IIC_Send_Byte(0xA2);
GXHT30_IIC_Wait_Ack();
GXHT30_IIC_Stop();
}
uint8_t GXHT30_CRC_8(uint8_t *Crc_ptr,uint8_t LEN)
{
uint8_t CRC_Value = 0xFF;
uint8_t i = 0,j = 0;
for(i=0;i<LEN;i++)
{
CRC_Value ^= *(Crc_ptr+i);
for(j=0;j<8;j++)
{
if(CRC_Value & 0x80)
CRC_Value = (CRC_Value << 1) ^ 0x31;
else
CRC_Value = (CRC_Value << 1);
}
}
return CRC_Value;
}
/**
brief 检查GXHT30读温湿度数据
param *temperature:需要读出的温度数据,uint16_t指针类型,精度范围+-0.3C
param *humidity:需要读出的湿度数据,uint16_t指针类型,精度范围+-2RH
return 1 读数据正常 0读数据失败
*/
float temperature_data;
float humidity_data;
uint8_t Read_GXHT30_Data(void)
{
uint16_t SRH=0,ST=0;
uint8_t databuff[6];
GXHT30_IIC_Start();
GXHT30_IIC_Send_Byte(GXHT30_ADDRESS << 1 | write);
GXHT30_IIC_Wait_Ack();
GXHT30_IIC_Send_Byte(0x2C);
GXHT30_IIC_Wait_Ack();
GXHT30_IIC_Send_Byte(0x06);
GXHT30_IIC_Wait_Ack();
GXHT30_IIC_Stop();
HAL_Delay(20);//延时一会等待数据读出
GXHT30_IIC_Start();
GXHT30_IIC_Send_Byte(GXHT30_ADDRESS << 1 | read);
if(GXHT30_IIC_Wait_Ack() ==0)
{
databuff[0]=GXHT30_IIC_Read_Byte(1);
databuff[1]=GXHT30_IIC_Read_Byte(1);
databuff[2]=GXHT30_IIC_Read_Byte(1);
databuff[3]=GXHT30_IIC_Read_Byte(1);
databuff[4]=GXHT30_IIC_Read_Byte(1);
databuff[5]=GXHT30_IIC_Read_Byte(0);
GXHT30_IIC_Stop();
if(GXHT30_CRC_8(databuff, 2) == databuff[2] && GXHT30_CRC_8(databuff + 3, 2) == databuff[5])
{
/* 转换温度数据 */
ST = (uint16_t)((0x0000|databuff[0])<<8|databuff[1]);
temperature_data = (-45 + 175*((float)ST/65535))*10;
/* 转换湿度数据 */
SRH = (uint16_t)((0x0000|databuff[3])<<8|databuff[4]);
humidity_data = 10 * (100 * ((float)SRH / 65535));
temp_buff[0] = ((uint16_t)(temperature_data)&0xff00)>>8; //将温度高八位存到temp_buff[0]
temp_buff[1] = ((uint16_t)(temperature_data))&0x00ff; //将温度低八位存到temp_buff[1]
hum_buff[0] = ((uint16_t)(humidity_data)&0xff00)>>8;//将湿度高八位存到hum_buf[0]
hum_buff[1] = ((uint16_t)(humidity_data))&0xff;//将湿度高八位存到hum_buf[1]
}
return 1;
}
return 0;
}
5.2 GXHT30C.h
#ifndef _GXHT30_H__
#define _GXHT30_H__
#include "stm32f0xx.h"
#include "delay.h"
extern uint16_t temp_buff[2];
extern uint16_t hum_buff[2];
void sys_delay_us(uint16_t time);
#define GXHT30_Delay_us(time) sys_delay_us(time)
#define GXHT30_Delay_ms(time) delay_ms(time)
#define GXHT30_ADDRESS 0x44
#define GXHT30_I2C_SCL_PIN GPIO_PIN_9
#define GXHT30_I2C_SCL_GPIO_PORT GPIOA
#define GXHT30_I2C_SCL_GPIO_RCC __HAL_RCC_GPIOA_CLK_ENABLE()
#define GXHT30_I2C_SDA_PIN GPIO_PIN_10
#define GXHT30_I2C_SDA_GPIO_PORT GPIOA
#define GXHT30_I2C_SDA_GPIO_RCC __HAL_RCC_GPIOA_CLK_ENABLE()
/* Private define ------------------------------------------------------------*/
#define GXHT30_SCL_H HAL_GPIO_WritePin(GXHT30_I2C_SCL_GPIO_PORT, GXHT30_I2C_SCL_PIN, GPIO_PIN_SET)
#define GXHT30_SCL_L HAL_GPIO_WritePin(GXHT30_I2C_SCL_GPIO_PORT, GXHT30_I2C_SCL_PIN, GPIO_PIN_RESET)
#define GXHT30_SDA_H HAL_GPIO_WritePin(GXHT30_I2C_SDA_GPIO_PORT, GXHT30_I2C_SDA_PIN, GPIO_PIN_SET)
#define GXHT30_SDA_L HAL_GPIO_WritePin(GXHT30_I2C_SDA_GPIO_PORT, GXHT30_I2C_SDA_PIN, GPIO_PIN_RESET)
#define GXHT30_READ_SDA HAL_GPIO_ReadPin(GXHT30_I2C_SDA_GPIO_PORT,GXHT30_I2C_SDA_PIN)
/*****************函数声明,对外接口******************/
void GXHT30_IIC_Init(void);
void GXHT30Reset(void);
extern uint8_t Read_GXHT30_Data(void);
#endif