使用STM32F103采集Si7021温湿度传感器数据

使用STM32F103采集Si7021温湿度传感器数据

此采集程序是跑在STM32F103C8T6上的,模拟IIC接口读取数据,每50ms采集一次数据,每隔1s通过串口1发送一次数据,完整的Keil5工程文件和传感器驱动代码可在此下载:Si7021温湿度STM32F1读取程序

一、传感器电路连接

Si7021的外围电路非常简单,如图1所示:


图1    Si7021传感器外围电路

Si7021采用的是IIC接口,最高支持400KHz的通信速率,0~100%RH的湿度量程和最大-40℃~+125℃的温度量程,150μA低功耗,超小体积。可提供精确,低功耗,工厂校准的数字解决方案,适用于测量湿度,露点和温度,适用于从HVAC / R和资产跟踪到工业和消费者平台的各种应用。

二、时序和协议分析

首先看看Si7021的IIC通信时序,图2展示了Si7021的IIC时序图和各个时序时间的参数表。


图2    传感器IIC通信时序

其实这个IIC时序我个人认为没啥特别的,感觉就是一个比较常见的通信时序。

不过,这个传感器在读写协议上有个比较特殊的也是我有点搞不懂的地方。它的IIC读写其实是有两个模式的,一个叫保持主模式(Hold Master Mode),另一个叫无保持主模式(No Hold Master Mode),这两种模式又分别有两种测量指令相对应。

按照数据手册的说法,无保持主模式下在从机应答了测量指令后,主机要重新发送一次起始信号Sr,然后连续发送三次从机地址之后开才始读取数据;而在保持主模式下,传感器应答主机的读指令之后从机会把时钟线SCL强制拉高,直到温湿度转换完成。图3和图4分别为Si7021无保持主模式和保持主模式下的IIC读写协议。


图3    无保持主模式读写协议


图4    保持主模式读写协议

这个读写协议我就有点看不懂了,而且最尴尬的是,按照手册上的无保持主模式读写根本读不出数据,全是0xFF,而且不知道是不是我的驱动有问题,用保持主模式的时候拿逻辑分析仪测信号,发现SCL也并没有强制拉高。最后实在没办法,就用了无保持的测量指令,然后在STM32等到ACK之后延时20ms再读数据,才正常的。但是这样又跟数据手册说的完全不一样了。

反正当时真是把我给搞懵逼了,还请各位大佬能点拨指导一下。

三、测量程序编写

既然已经能读到数据了,那就直接开始编写测量程序好了。我的测量代码的底层IIC驱动代码是借鉴的正点原子的例程,温湿度计算部分参考了这篇博客:家庭IOT监测之温湿度SI7021及上传ONENET

首先是IO口的初始化,我使用了PB8(SCL)和PB9(SDA)。

void IIC_Init(void)//IIC初始化
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	//使能GPIOB时钟
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;//PB8->SCL, PB9->SDA
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
 
	IIC_SCL=1;//初始电平设为高
	IIC_SDA=1;
}

然后是多字节读取传感器函数:

//函数名称:Multiple_read_Si7021
//函数功能:多字节读取传感器
//参数描述:
//返 回 值:
void Multiple_read_Si7021(u8 REG_address, u16 *value)
{
	u8 Si7021_BUF[2]={0};//缓存数组定义
	
	IIC_Start();//IIC起始信号
	
	IIC_Send_Byte((SLAVE_ADDR<<1)|0);//将7位器件地址左移一位再加上一位写操作
	IIC_Wait_Ack();//等待应答
	
	IIC_Send_Byte(REG_address);
	IIC_Wait_Ack();//等待应答
	
	delay_ms(19);//等待温湿度转换完成,至少应该大于18ms
	
	IIC_Start();
	IIC_Send_Byte((SLAVE_ADDR<<1)|1);//将7位器件地址左移一位再加上一位读操作
	IIC_Wait_Ack();	
	Si7021_BUF[0] = IIC_Read_Byte(1);//读传感器高8位数据并发送应答信号
	Si7021_BUF[1] = IIC_Read_Byte(0);//读传感器低8位数据并发送非应答信号
	
	IIC_Stop();//停止信号
	
	*value=((Si7021_BUF[0]<<8)+Si7021_BUF[1]);//将高低8位数据合成为16位数据
}

接着是温湿度测量函数:

//函数名称:measure_si7021
//函数功能:NO HOLD MASTER模式下读取温湿度 
//参数描述:无
//返 回 值:无
void measure_Si7021(void)
{
	//缓存变量定义
	u16 TEMP,HUMI;
	u8 curI;
	
	//读取温度
	Multiple_read_Si7021(TEMP_NOHOLD_MASTER,&TEMP);//NOHOLD_MASTER模式下读取温度
	si7021.temp=(((((float)TEMP)*175.72f)/65536.0f) - 46.85f);//将原始温度数据计算为实际温度数据并传递给缓存区,单位 ℃
//	TEMP_buf=(((((float)TEMP)*175.72f)/65536.0f) - 46.85f);
	
	Multiple_read_Si7021(HUMI_NOHOLD_MASTER,&HUMI);//NOHOLD_MASTER模式下读取湿度
	si7021.humi=(((((float)HUMI)*125.0f)/65535.0f) - 6.0f);//将原始湿度数据计算为实际湿度数据并传递给缓存区,单位 %RH
//	Humi_buf=(((((float)HUMI)*125.0f)/65535.0f) - 6.0f);
	
	//下面其实是我抄的机智云智能宠物屋温湿度测量的一个平均值滤波算法,循环储存10次的数据,调用一次measure_Si7021()就存一次
	if(MEAN_NUM > si7021_filter.curI)//当MEAN_NUM==10时,完成10次读取
	{
		si7021_filter.tBufs[si7021_filter.curI] = si7021.temp;
		si7021_filter.hBufs[si7021_filter.curI] = si7021.humi;

		si7021_filter.curI++;
	}
	else
	{
		si7021_filter.curI = 0;

		si7021_filter.tBufs[si7021_filter.curI] = si7021.temp;
		si7021_filter.hBufs[si7021_filter.curI] = si7021.humi;

		si7021_filter.curI++;
	}
	
	if(MEAN_NUM <= si7021_filter.curI) 
    {
        si7021_filter.thAmount = MEAN_NUM;
    }

	//判断是否初次循环
    if(0 == si7021_filter.thAmount) 
    {
        //计算采集第10次数据之前的平均值
        for(curI = 0; curI < si7021_filter.curI; curI++)
        {
            si7021.temp += si7021_filter.tBufs[curI];
            si7021.humi += si7021_filter.hBufs[curI];
        }

        si7021.temp = si7021.temp / si7021_filter.curI;
        si7021.humi = si7021.humi / si7021_filter.curI; 
        
        TEMP_buf = si7021.temp;
        Humi_buf = si7021.humi;
    }
    else if(MEAN_NUM == si7021_filter.thAmount) 
    {
        //计算采集第10次数据之后的平均值
        for(curI = 0; curI < si7021_filter.thAmount; curI++) 
        {
            si7021.temp += si7021_filter.tBufs[curI];
            si7021.humi += si7021_filter.hBufs[curI];
        }

        si7021.temp = si7021.temp / si7021_filter.thAmount; 
        si7021.humi = si7021.humi / si7021_filter.thAmount; 
        
        TEMP_buf = si7021.temp; 
        Humi_buf = si7021.humi; 
    }
}

最后,主函数每50ms调用一次测量函数,但每隔1s才发送一次数据给串口:

#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "Si7021.h"

int main(void)
{
    u8 i=0;

	delay_init();			//延时函数初始化	
	uart_init(115200);		//串口1初始化波特率为115200
	IIC_Init();				//IIC初始化 


	while(1)
	{	//每50ms读取一次数据
		measure_Si7021();


		i++;
		delay_ms(50);
		if(i==20)
		{	//串口每1s打印一次数据
			printf("\r\nTemp:%.2f\r\n",TEMP_buf);//打印温度数据,保留两位小数
			printf("Humi:%.2f\r\n",Humi_buf);//打印湿度数据,保留两位小数	
			i=0;
		}
	}
}

猜你喜欢

转载自blog.csdn.net/zzz_xxj/article/details/80931856

相关文章