IIC协议之SHT30温湿度采样
1.IIC协议介绍
1.1 协议简介
IIC由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成(总线空闲时是高电平)。IIC是一种主从结构总线,在IIC总线上,每一个设备都可以作为主设备或者从设备,但一个IIC总线上一般只有一个主设备,多个从设备。主设备来产生时钟信号,一般主设备是CPU,而从设备只是用来相应主设备的通信请求,一般从设备是传感器。
1.2 SHT30设备地址
一个IIC总线上可以挂载多个从设备,所以主设备就需要通过从设备的地址来确定与哪个设备进行通信。在IIC总线上每个从设备都有唯一确定的地址,设备地址在芯片内部就固定好了,可以从芯片的datasheet上找到。因为IIC地址全为0时为广播地址,所以IIC总线上最多可以挂载2^7-1=127个从设备。
有时候IIC总线上需要挂载多个同一芯片,为了实现地址的不同,芯片引出了一个或者多个引脚,来使同一芯片具有不同的地址。如SHT30温湿度传感器,芯片引出一个ADDR引脚,当ADDR引脚接VSS时,其7bit地址为0x44,ADDR接VDD时地址为0x45。
2.协议时序
2.1 IIC通信时序图
下面这个图片更好的看出数据的传输。
2.2 起始信号
IIC通信以起始信号开始传输数据,所以在发送数据之前,需要先给个起始信号。由时序图可以知道,总线空闲时SCL和SDA都是高电平,时钟线先拉低,然后数据线拉低,这样就产生了一个起始信号。起始信号是在SCL为高电平时,SDA由高电平向低电平转变,产生一个下降沿,即为起始信号。
2.3 结束信号
IIC通信以结束信号停止传输数据。当收到结束信号后,主从设备停止传输数据。由时序图可以知道,当SCL为高电平时,SDA由低电平向高电平跳转,SDA产生一个上升沿,产生一个结束信号。
2.4 应答信号
在8bit数据传输的后面,紧跟着一个应答信号,用来判断是不是要结束传输数据或者继续传输数据。
当主设备连接从设备时,发送一个地址信号,当在总线上从设备接收到和自己地址相同的地址信息后,给出一个应答信号,表示自己与主设备建立通信。
当主设备接收到从设备的一个字节的数据后,给出自己的应答或者非应答信号,来表示是否需要继续传输数据,给的应答信号,“应答”为低电平,“非应答”为高电平。
2.5 数据位收发
发数据:
先把SCL拉低,然后把SDA拉高,再把SCL拉高,这样就发送一个bit的“1”。
先把SCL拉低,然后把SDA拉低,再把SCL拉高,这样就发送一个bit的“0”。
为什么要这样做?因为数据传输是在SCL为高电平时传输,并且前面讲到起始信号和终止信号的时候,在SCL为高电平时,只要有上升沿或者下降沿的到来,那么就会认为是起始信号或者结束信号。所以在传输bit的时候,得在SCL为低电平时改变SDA的电平,然后在SCL为高电平时,保证SDA的电平稳定,这样才能保证正确发送“0”,“1”。
收数据:同样,接收数据也是在SCL为高电平时接收,因为数据的传输在SCL为高电平,所以在接收数据前,把SCL拉高,然后读取SDA的电平即可。
3.协议代码
3.1 起始信号
void IIC_Start()//产生开始信号
{
IIC_SDA_On();//输出模式
SCL_H();
SDA_H();
delay_us(2);
SDA_L();
delay_us(2);
SCL_L();
}
3.2 结束信号
void IIC_Stop()//产生结束信号
{
IIC_SDA_On();//输出模式
SCL_L();
SDA_L();
delay_us(2);
SCL_H();
delay_us(2);
SDA_H();
delay_us(2);
}
3.3 应答信号
void IIC_Ack()//应答模式,SCL高电平时,SDA=0为应答
{
SCL_L();
IIC_SDA_On();//输出模式
SDA_L();
/*SCL__---
*
*SDA_____
*传输数据0
*/
delay_us(2);
SCL_H();
delay_us(2);
SCL_L();
}
3.4 非应答信号
void IIC_NAck()
{
SCL_L();
IIC_SDA_On();//输出模式
SDA_H();
/*SCL__---
*
*SDA-----
*传输数据1
*/
delay_us(2);
SCL_H();
delay_us(2);
SCL_L();
}
3.5 等待从机应答
从机无应答时,应结束连接,所以发送IIC终止信号
uint8_t IIC_Wait_Ack()
{
uint8_t ucErrTime=0;
IIC_SDA_In();
SCL_H();
while(SDA_Read())
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
SCL_L();
return 0;
}
3.6 主机发送一个字节
void IIC_Send_Byte(uint8_t data)
{
uint8_t i,txd;
IIC_SDA_On();
SCL_L();//拉低时钟线,选择传输的SDA电平,因为数据传输需要在SCL低电平时改变,高电平时输出
for(i=0;i<8;i++)
{
txd=(data&0x80)>>7;//把最高位取出来
data<<=1;
if(txd)
SDA_H();
else
SDA_L();
delay_us(2);
SCL_H();//开始传输
delay_us(2);
SCL_L();
delay_us(2);
}
}
3.7 接收从机一个字节
在接收从机数据的时候,需要给应答或者非应答信号,来决定是否需要继续传输数据。
uint8_t IIC_Receive_Byte(uint8_t ack)//方便理解,逻辑和从机应答相反,1为应答 0为非应答
{
uint8_t i,recevie=0;
IIC_SDA_In();
for(i=0;i<8;i++)
{
SCL_L();
delay_us(2);
SCL_H();
recevie<<=1;
recevie+=SDA_Read();
delay_us(2);
}
if(ack)
IIC_Ack();
else
IIC_NAck();
return recevie;
}
4.SHT30代码
4.1 SHT30初始化
发送测量指令,让SHT30开始循环采集温湿度。我采用的是SHT30的周期性数据采集模式的测量指令。
void Sht30_Init()
{
IIC_Start();
IIC_Send_Byte(0x44<<1|0);//0为写
IIC_Wait_Ack();
IIC_Send_Byte(0x21);
IIC_Wait_Ack();
IIC_Send_Byte(0x30);
IIC_Wait_Ack();
IIC_Stop();
HAL_Delay(150);
}
4.2 SHT30获取温湿度
周期模式测量结果的读出。
在温度湿度后面会紧跟一个检验和数据CRC。
void Read_IIC_Data(float *Temperature,float *Humidity)
{
uint16_t data[6];
uint16_t temp,humi;
IIC_Start();
IIC_Send_Byte(0x44<<1|0);//0为写
IIC_Wait_Ack();
IIC_Send_Byte(0xe0);
IIC_Wait_Ack();
IIC_Send_Byte(0x00);
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0x44<<1|1);
IIC_Wait_Ack();
//温度数据
data[0]=IIC_Receive_Byte(1);
data[1]=IIC_Receive_Byte(1);
//CRC
data[2]=IIC_Receive_Byte(1);
//湿度数据
data[3]=IIC_Receive_Byte(1);
data[4]=IIC_Receive_Byte(1);
data[5]=IIC_Receive_Byte(0);
IIC_Stop();
//校验和就不计算了
temp=(data[0]<<8)|data[1];
humi=(data[3]<<8)|data[4];
/*转换实际温度*/
*Temperature=(175.0*(float)temp/65535.0-45.0) ;// T = -45 + 175 * tem / (2^16-1)
*Humidity=(100.0*(float)humi/65535.0);// RH = hum*100 / (2^16-1)
}