程序功能:
1、软件模拟I2C协议与SHT30数字温湿度传感器通讯;
2、数码管显示环境温湿度;
3、串口打印环境温湿度。
目录
一、硬件电路
二、技术讲解
2.1IIC简介
2.2 IIC总线协议
2.2设备接入
三、SHT30数字温湿度传感器
3.1性能介绍
3.1.1湿度最优测量环境
3.1.2温度最优测量环境
3.1.3测量配置
3.2通讯介绍
3.2.1单周期数据采集模式
3.2.2周期采集
四、软件编程
4.1参数配置
4.2软件框架
4.3运行函数
4.4 I2C函数
4.4.1 iic结构体
4.4.2iic函数
4.5SHT30函数
4.5.1结构体函数
4.5.2SHT30函数
5结果演示
一、硬件电路
二、技术讲解
2.1IIC简介
IIC (I2C,Inter-Integrated Circuit)即集成电路总线,是一种两线式串行总线,由PHILIPS公司开发用于连接微控制器及其外围设备。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。
I2C总线由数据线SDA和时钟线SCL构成通信线路,既可用于发送数据,也可接收数据。只有一根数据线,属于半双工通信方式。
标准模式:100Kbit/s 快速模式:400kbit/s 高速模式:3.4Mbit/s
2.2 IIC总线协议
I2C协议特点:
1、串行协议,多主机模式;
2、起始信号,SCL为高电平时,SDA下降沿,主机发出;
3、停止信号,SCL为高电平时,SDA上升沿,主机发出;
4、数据与地址按8位/字节传输(附带读写位),高位在前;
5、传输过程中,SCL为低电平时,SDA更改状态,SCL为高电平时,SDA状态需稳定;
6、每传输一个字节,接收器必须回应1个应答位(ACK)给发送器。
2.2设备接入
通过上面IIC协议的介绍,我们知道,通信是相互的,从机接收到读的命令,也会像主机发送数据,所以 I2C 器件一般采用开漏结构与总线相连,SCL 和 SDA 均需接上拉电阻。也正因此,当总线空闲时,这两条线路都处于高电平状态,当连到总线上的任一器件输出低电平,都将使总线拉低,即各器件的 SDA 及 SCL 都是“线与”关系。I2C 总线支持多主和主从两种工作方式。在主从工作方式中,主机启动数据的发送(发出启动信号)并产生时钟信号,数据发送完成后,发出停止信号。
三、SHT30数字温湿度传感器
3.1性能介绍
SHT30工作电压范围:2.4v--5.5v,通信速率:最高1MHz,接口:提供两个用户可选择地址的IIC接口,精度:2%RH,0.3° C的典型精度。
3.1.1湿度最优测量环境
3.1.2温度最优测量环境
该传感器在推荐的正常温度和湿度范围内分别为5-60° C和20-80%RH时显示出最佳的性能。 长期暴露在正常范围以外的条件下, 特别是在高湿度下, 可能会暂时抵消RH信号(例如在80%RH条件下60h后+3%RH)。 在恢复到正常的温度和湿度范围后, 传感器将自行缓慢地恢复到校准状态。 长期暴露在极端条件下可能会加速衰老。 为了确保湿度传感器的稳定运行, 必须满足文件“SMD包装的SHTxx组装” 中关于接触挥发性有机化合物的“储存和处理说明” 一节中所描述的条件。 请注意, 这不仅适用于运输和制造, 而且也适用于SHT3x-DIS的操作。
3.1.3测量配置
3.2通讯介绍
测量通信序列由START条件、 I2C写报头(7位I2C设备)组成地址加0(R)作为写位)和16位测量命令(高八位,低八位)。 每个字节的正确接收由传感器指示。 它将SDA引脚拉低(ACK位)后, 第8个SCL时钟的下降边缘 以指示接收。 描述了一个完整的测量周期。通过确认测量命令, SHT3x-DIS开始测量湿度和温度。
3.2.1单周期数据采集模式
在这种模式下, 一个发出的测量命令触发一个数据对的获取。 每个数据对由一个16位温度
和一个16位湿度值(按此顺序)组成) 。 在传输期间, 数据对总是后面跟着CRC校验位, 请
在单周期模式下, 可以选择不同的测量命令。 16位命令显示在它们在重复性( 低、中、 高)和时钟拉伸(启用或禁用)方面不同) 。重复性设置影响传感器的测量持续时间和电流消耗。 传感器通常不响应任何I2C活动, 即不承认I2C读写标题(NACK)。 然而, 当发出带有时钟拉伸的命令时, 传感器用ACK响应读报头, 然后拉下SCL线。 将SCL线拉下, 直至测量完成。 测量完成后, 传感器释放SCL线并发送测量结果。图解如下:
3.2.2周期采集
在这种模式下, 一个发出的测量命令产生一个数据对流。 每个数据对由一个16位温度和一
个16位湿度值(按此顺序)组成) 。在周期模式下, 可以选择不同的测量命令。相应的16位命令显示在重复性(低、 中、 高)和数据采集频率(每秒0.5、 1、 2、 4和10次测量, MPS)方面)。 无法在此模式下选择时钟拉伸。数据采集频率和重复性设置影响传感器的测量持续时间和电流消耗
。
四、软件编程
4.1参数配置
只需要配置gpio口味开漏输出就可以了,如下图所示:
4.2软件框架
在原有的基础上,新增I2C和SHT30源文件。如下图所示:
4.3运行函数
static void Run()
{
float Temp_float = 0;
uint16_t Temp_uint = 0;
//周期性测量获取SHT30的温湿度
SHT30.Measure_Period_Mode();
//串口打印
printf("Wendu = %.1f¡æ\r\n",SHT30.fTemperature);
printf("shidu = %d%%RH\r\n\r\n",(uint16_t)SHT30.ucHumidity);
数码管显示
//温度
if(SHT30.fTemperature < 0) //负温
{
Temp_float = 0 - SHT30.fTemperature;
Display.Disp_Other(Disp_NUM_4,0x40,Disp_DP_OFF); //4号数码管
}
else
{
Temp_float = SHT30.fTemperature;
Display.Disp_Other(Disp_NUM_4,0x00,Disp_DP_OFF); //4号数码管
}
Temp_uint = (uint16_t)(Temp_float*10);
Display.Disp_HEX(Disp_NUM_3,Temp_uint/100,Disp_DP_OFF);
Display.Disp_HEX(Disp_NUM_2,Temp_uint%100/10,Disp_DP_ON);
Display.Disp_HEX(Disp_NUM_1,Temp_uint%10,Disp_DP_OFF);
//湿度
Display.Disp_HEX(Disp_NUM_6,SHT30.ucHumidity/10,Disp_DP_OFF);
Display.Disp_HEX(Disp_NUM_5,SHT30.ucHumidity%10,Disp_DP_OFF);
HAL_Delay(500);
}
4.4 I2C函数
4.4.1 iic结构体
枚举,ACK,和NACK,结构体封装,iic的起始,停止 读 写函数。
typedef enum
{
ACK = GPIO_PIN_RESET,//响应
NACK = GPIO_PIN_SET,//不响应
}ACK_Value_t;
//定义结构体类型
typedef struct
{
void (*Init)(void); //初始化
void (*Start)(void); //起始信号
void (*Stop)(void); //停止信号
ACK_Value_t (*Write_Byte)(uint8_t); //I2c写字节
uint8_t (*Read_Byte) (ACK_Value_t); //I2读字节
}I2C_Soft_t
4.4.2iic函数
1.将设置sda,scl引脚的端口,宏定义,方便代码编写,以及后期移植,
//置位与清零SCL管脚
#define SET_SCL HAL_GPIO_WritePin(SHT30_SCL_GPIO_Port,SHT30_SCL_Pin,GPIO_PIN_SET)
#define CLR_SCL HAL_GPIO_WritePin(SHT30_SCL_GPIO_Port,SHT30_SCL_Pin,GPIO_PIN_RESET)
//置位与清零SDL管脚
#define SET_SDA HAL_GPIO_WritePin(SHT30_SDA_GPIO_Port,SHT30_SDA_Pin,GPIO_PIN_SET)
#define CLR_SDA HAL_GPIO_WritePin(SHT30_SDA_GPIO_Port,SHT30_SDA_Pin,GPIO_PIN_RESET)
//读SDA管脚状态
#define READ_SDA HAL_GPIO_ReadPin(SHT30_SDA_GPIO_Port,SHT30_SDA_Pin)
2.初始化
将sda和scl均拉高
static void Init(void)
{
SET_SCL;
SET_SDA;
}
3起始
拉低sda,延时一会拉低scl等待发送
static void Start(void)
{
//SCL为该电平,SDA为下降沿IIC起始信号
SET_SDA;
SET_SCL;
I2C_Delay_us(1);
CLR_SDA;
I2C_Delay_us(10);
CLR_SCL;
I2C_Delay_us(1);
}
4停止
拉低SDA,拉高SCL再次拉高SDA 停止
static void Stop(void)
{
//SCL为高电平,SDA上升沿为IIC停止信号
CLR_SDA;
SET_SCL;
I2C_Delay_us(1);
I2C_Delay_us(10);
SET_SDA;
}
5写数据
BIT7为1,将数据位和他与 判断为高定电平,选择对应的SDA位。
static ACK_Value_t Write_Byte(uint8_t WR_Byte)
{
uint8_t i;
ACK_Value_t ACK_Rspond;
//SCL为低电平,SDA准备数据,接着SCL为高电平,读取SDA数据
//数据按八位传输,高位在前,利用for循环逐个接收
for(i=0;i<8;i++)
{
//SCL清零,主机SDA 准备数据
CLR_SCL;
I2C_Delay_us(1);
if((WR_Byte&BIT7) == BIT7)
{
SET_SDA;
}
else
{
CLR_SDA;
}
I2C_Delay_us(1);
//SCL置高,传输数据
SET_SCL;
I2C_Delay_us(10);
//准备发送下一个bit
WR_Byte <<= 1;
}
CLR_SCL;
//释放SDA,等待从机yingda
SET_SDA;
I2C_Delay_us(1);
SET_SCL;
I2C_Delay_us(10);
ACK_Rspond = (ACK_Value_t)READ_SDA;
CLR_SCL;
I2C_Delay_us(1);
//返回从机应答信号
return ACK_Rspond;
}
6读数据
static uint8_t Read_Byte(ACK_Value_t ACK_Value)
{
uint8_t RD_Byte = 0,i;
接收数据
//SCL位低电平,SDA准备数据,接着SCL为高电平,读取SDA数据
//数据传输八位,高位在前,利用for循环诸葛接收
for(i=0;i<8;i++)
{
//准备接收下一个bit
RD_Byte <<= 1;
//SCL清零,从机SDA准备数据
CLR_SCL;
I2C_Delay_us(10);
//SCL志高,获取数据
SET_SCL;
I2C_Delay_us(10);
RD_Byte |= READ_SDA;
}
//SCL清零,主机准备应答信号
CLR_SCL;
I2C_Delay_us(1);
//主机发送应答信号
if(ACK_Value == ACK)
{
CLR_SDA;
}
else
{
SET_SDA;
}
I2C_Delay_us(1);
SET_SCL;
I2C_Delay_us(10);
//Note:
//释放SDA数据线
//SCL先清零,在释放SDA,防止连续传输数据时。从机错将SDA释放。
CLR_SCL;
SET_SDA;
I2C_Delay_us(1);
//返回数据
return RD_Byte;
}
4.5SHT30函数
4.5.1结构体函数
选择周期测量模式,因为SHT30地址位是七位,所以需要左移 然后与读写位与或 加上读写位构成八位数据传输。
#define SHT30_ADDR (uint8_t)(0x44 << 1) //´传感器地址
//#define SHT30_ADDR (unsigned char)(0x45 << 1) //´另一种模式传感器地址
#define Write_CMD 0xFE
#define Read_CMD 0x01
//定义结构体类型
typedef struct
{
float fTemperature; //温度 -40 至 125 精度 百一
uint8_t ucHumidity; //湿度 0 - 100 精度 百一
void (*Measure_Period_Mode)(void); //周期测量模式
}SHT30_t;
4.5.2SHT30函数
1.信号输出的转换
测量数据总是以16位值( 无符号整数)的形式传输) 。 这些值已经线性化, 并补偿了温度
和电源电压的影响。 将这些原始值转换为物理标度可以使用以下公式实现。相对湿度换算公式(结果为%RH):
static void Measure_Period_Mode(void)
{
uint8_t temp_array[6] = {0};
uint16_t temp_uint = 0;
float temp_float = 0;
//启动周期性测量
I2C_Soft.Start();
I2C_Soft.Write_Byte(SHT30_ADDR & Write_CMD);
I2C_Soft.Write_Byte(0x27); //High repeat , mps = 10
I2C_Soft.Write_Byte(0x37);
Timer6.SHT30_Measure_Timeout = 0;
//发送接收数据命令
do
{
if(Timer6.SHT30_Measure_Timeout >= TIMER6_2S) //2s内没获取跳出
break;
I2C_Soft.Start();
I2C_Soft.Write_Byte(SHT30_ADDR & Write_CMD);
I2C_Soft.Write_Byte(0xE0);
I2C_Soft.Write_Byte(0x00);
I2C_Soft.Start();
}
while(I2C_Soft.Write_Byte(SHT30_ADDR | Read_CMD) ==NACK);
//开始接收测量数据,并计算
if(Timer6.SHT30_Measure_Timeout < TIMER6_2S)
{
temp_array[0] = I2C_Soft.Read_Byte(ACK);
temp_array[1] = I2C_Soft.Read_Byte(ACK);
temp_array[2] = I2C_Soft.Read_Byte(ACK);
temp_array[3] = I2C_Soft.Read_Byte(ACK);
temp_array[4] = I2C_Soft.Read_Byte(ACK);
temp_array[5] = I2C_Soft.Read_Byte(NACK);
I2C_Soft.Stop();
计算温度 精度0.1
if(CRC_8(temp_array,2) == temp_array[2]) //CRC-8 УÑé
{
temp_uint = temp_array[0]*256+temp_array[1];
temp_float = ((float)temp_uint)*0.267032-4500;
SHT30.fTemperature = temp_float*0.01;
}
计算湿度 1%RH
if(CRC_8(&temp_array[3],2) == temp_array[5]) //CRC-8 УÑé
{
temp_uint = temp_array[3]*256+temp_array[4];
temp_float = ((float)temp_uint)*0.152590;
temp_float = temp_float*0.01;
SHT30.ucHumidity = (unsigned char)temp_float;
}
}
}
校验
static uint8_t 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;
}
5结果演示