使用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驅動程式碼是借鑑的正點原子的例程,溫溼度計算部分參考了這篇部落格:
首先是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;
}
}
}