程序功能:
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结果演示
IIC (I2C,Inter-Integrated Circuit)即集成电路总线,是一种两线式串行总线,由PHILIPS公司开发用于连接微控制器及其外围设备。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。
I2C总线由数据线SDA和时钟线SCL构成通信线路,既可用于发送数据,也可接收数据。只有一根数据线,属于半双工通信方式。
标准模式:100Kbit/s 快速模式:400kbit/s 高速模式:3.4Mbit/s
I2C协议特点:
1、串行协议,多主机模式;
2、起始信号,SCL为高电平时,SDA下降沿,主机发出;
3、停止信号,SCL为高电平时,SDA上升沿,主机发出;
4、数据与地址按8位/字节传输(附带读写位),高位在前;
5、传输过程中,SCL为低电平时,SDA更改状态,SCL为高电平时,SDA状态需稳定;
6、每传输一个字节,接收器必须回应1个应答位(ACK)给发送器。
通过上面IIC协议的介绍,我们知道,通信是相互的,从机接收到读的命令,也会像主机发送数据,所以 I2C 器件一般采用开漏结构与总线相连,SCL 和 SDA 均需接上拉电阻。也正因此,当总线空闲时,这两条线路都处于高电平状态,当连到总线上的任一器件输出低电平,都将使总线拉低,即各器件的 SDA 及 SCL 都是“线与”关系。I2C 总线支持多主和主从两种工作方式。在主从工作方式中,主机启动数据的发送(发出启动信号)并产生时钟信号,数据发送完成后,发出停止信号。
SHT30工作电压范围:2.4v--5.5v,通信速率:最高1MHz,接口:提供两个用户可选择地址的IIC接口,精度:2%RH,0.3° C的典型精度。
该传感器在推荐的正常温度和湿度范围内分别为5-60° C和20-80%RH时显示出最佳的性能。 长期暴露在正常范围以外的条件下, 特别是在高湿度下, 可能会暂时抵消RH信号(例如在80%RH条件下60h后+3%RH)。 在恢复到正常的温度和湿度范围后, 传感器将自行缓慢地恢复到校准状态。 长期暴露在极端条件下可能会加速衰老。 为了确保湿度传感器的稳定运行, 必须满足文件“SMD包装的SHTxx组装” 中关于接触挥发性有机化合物的“储存和处理说明” 一节中所描述的条件。 请注意, 这不仅适用于运输和制造, 而且也适用于SHT3x-DIS的操作。
测量通信序列由START条件、 I2C写报头(7位I2C设备)组成地址加0(R)作为写位)和16位测量命令(高八位,低八位)。 每个字节的正确接收由传感器指示。 它将SDA引脚拉低(ACK位)后, 第8个SCL时钟的下降边缘 以指示接收。 描述了一个完整的测量周期。通过确认测量命令, SHT3x-DIS开始测量湿度和温度。
在这种模式下, 一个发出的测量命令触发一个数据对的获取。 每个数据对由一个16位温度
和一个16位湿度值(按此顺序)组成) 。 在传输期间, 数据对总是后面跟着CRC校验位, 请
在单周期模式下, 可以选择不同的测量命令。 16位命令显示在它们在重复性( 低、中、 高)和时钟拉伸(启用或禁用)方面不同) 。重复性设置影响传感器的测量持续时间和电流消耗。 传感器通常不响应任何I2C活动, 即不承认I2C读写标题(NACK)。 然而, 当发出带有时钟拉伸的命令时, 传感器用ACK响应读报头, 然后拉下SCL线。 将SCL线拉下, 直至测量完成。 测量完成后, 传感器释放SCL线并发送测量结果。图解如下:
在这种模式下, 一个发出的测量命令产生一个数据对流。 每个数据对由一个16位温度和一
个16位湿度值(按此顺序)组成) 。在周期模式下, 可以选择不同的测量命令。相应的16位命令显示在重复性(低、 中、 高)和数据采集频率(每秒0.5、 1、 2、 4和10次测量, MPS)方面)。 无法在此模式下选择时钟拉伸。数据采集频率和重复性设置影响传感器的测量持续时间和电流消耗
只需要配置gpio口味开漏输出就可以了,如下图所示:
在原有的基础上,新增I2C和SHT30源文件。如下图所示:
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);
}
枚举,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
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;
}
选择周期测量模式,因为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;
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