驅(qū)動(dòng)SHT30溫濕度傳感器:從I2C通信到實(shí)戰(zhàn)排坑)
簡(jiǎn)介面向STM32開(kāi)發(fā)者的SHT30溫濕度傳感器HAL庫(kù)例程包基于STM32F130ZET6平臺(tái)適合需要快速實(shí)現(xiàn)I2C溫濕度采集的中初級(jí)嵌入式開(kāi)發(fā)者也可作為智能家居、環(huán)境監(jiān)測(cè)等應(yīng)用場(chǎng)景的參考方案。SHT30具備±2%濕度與±0.3°C溫度精度搭配主頻72MHz、512KB閃存的STM32F130ZET6可應(yīng)對(duì)多數(shù)環(huán)境監(jiān)測(cè)需求。例程完整演示了SHT30的驅(qū)動(dòng)配置、數(shù)據(jù)讀取與解析流程涵蓋HAL庫(kù)I2C初始化、主從收發(fā)函數(shù)調(diào)用、溫濕度換算等關(guān)鍵環(huán)節(jié)可直接編譯運(yùn)行并觀察實(shí)時(shí)數(shù)據(jù)。資源包共231個(gè)文件、10.22MB以I2C驅(qū)動(dòng)、HAL庫(kù)源文件、定時(shí)器及串口模塊為主同時(shí)包含工程配置文件如uvprojx、ewp與編譯中間文件o、axf等便于在Keil或IAR環(huán)境下打開(kāi)調(diào)試。除核心源碼外包內(nèi)還提供初始化配置和完整參考工程有助于理解I2C通信時(shí)序、HAL庫(kù)API調(diào)用方式以及如何根據(jù)傳感器數(shù)據(jù)驅(qū)動(dòng)后續(xù)控制邏輯。同時(shí)代碼結(jié)構(gòu)清晰、注釋完整方便直接復(fù)用或二次開(kāi)發(fā)。已有1977人下載學(xué)習(xí)。 講實(shí)話我以前做環(huán)境監(jiān)測(cè)項(xiàng)目最早用的是DHT11后來(lái)?yè)Q成了SHT30中間還折騰過(guò)一陣子AHT20?,F(xiàn)在手頭固定板子型號(hào)是STM32F103ZET6用的是ST官方HAL庫(kù)。如果你正好在找一個(gè)能直接跑的SHT30例程又不想被網(wǎng)上那些標(biāo)準(zhǔn)外設(shè)庫(kù)辦法繞暈?zāi)沁@篇就是給你寫(xiě)的。我會(huì)把SHT30的I2C通信細(xì)節(jié)、HAL庫(kù)工程怎么配、驅(qū)動(dòng)代碼怎么拆解還有我實(shí)際調(diào)板子時(shí)踩過(guò)的坑一次性說(shuō)完。1. 先說(shuō)說(shuō)為什么棄用DHT11換到SHT30DHT11在很多入門(mén)教程里出現(xiàn)率極高因?yàn)樗阋?、接線簡(jiǎn)單、代碼也好寫(xiě)。但用過(guò)的都知道它的精度實(shí)在拿不出手——溫度精度正負(fù)2℃濕度精度正負(fù)5%有些批次甚至更離譜。放室內(nèi)看個(gè)大概還行一旦要做數(shù)據(jù)記錄、恒溫控制或者產(chǎn)品和儀器放一起DHT11的數(shù)據(jù)出來(lái)根本不敢用來(lái)做決策。SHT30是Sensirion的新一代數(shù)字溫濕度傳感器溫度精度能做到正負(fù)0.2℃典型值濕度精度正負(fù)2%注意是典型值。更關(guān)鍵的SHT30出廠就做了個(gè)體校準(zhǔn)不需要你手里攢一堆溫濕度計(jì)去逐點(diǎn)修正。單次測(cè)量的平均電流約15μA周期測(cè)量模式下平均電流甚至可以到1μA左右電池方案完全扛得住。這個(gè)精度和功耗差異就是我換到SHT30的根本原因。另外一個(gè)實(shí)際考量是通信接口。DHT11用單總線時(shí)序要求苛刻一旦中斷被嵌套或者系統(tǒng)忙時(shí)序就容易飄讀出來(lái)的數(shù)據(jù)是錯(cuò)的你都不知道錯(cuò)在哪只能一遍遍調(diào)延時(shí)。SHT30用的是標(biāo)準(zhǔn)I2C接口地址固定為0x44或0x45主機(jī)只要按I2C協(xié)議發(fā)命令、收數(shù)據(jù)就行時(shí)序由I2C控制器和內(nèi)部的晶振保證代碼上不需要精確卡微秒級(jí)延時(shí)。這在HAL庫(kù)環(huán)境下尤其省心——ST官方庫(kù)把I2C時(shí)序細(xì)節(jié)全封裝好了你只需要調(diào)用HAL_I2C_Master_Transmit和HAL_I2C_Master_Receive兩個(gè)函數(shù)。SHT30的封裝也友好有DFN-8小封裝也有帶PCB焊盤(pán)的貼片版本手工焊完全能做。如果你像我一樣用核心板擴(kuò)展找個(gè)SHT30模塊也行模塊上一般自帶上拉電阻和穩(wěn)壓電路接線就四根VCC、GND、SCL、SDA。板上沒(méi)有硬件I2C上拉的話我這里提醒一句SCL和SDA一定要各接一個(gè)10kΩ上拉到3.3V否則通信會(huì)間歇性失敗后面細(xì)講。2. SHT30不能只調(diào)包命令、時(shí)序與數(shù)據(jù)格式很多新手拿到SHT30第一反應(yīng)是找個(gè)現(xiàn)成例程復(fù)制粘貼。但驅(qū)動(dòng)這類(lèi)數(shù)字傳感器光會(huì)調(diào)庫(kù)函數(shù)是不夠的。SHT30手冊(cè)里其實(shí)就干了幾件事發(fā)命令、等測(cè)量、讀數(shù)據(jù)、解算。但每一環(huán)都有門(mén)道。先說(shuō)話命令。SHT30的單次測(cè)量命令是兩字節(jié)0x2C是高字節(jié)低字節(jié)決定重復(fù)性和時(shí)鐘拉伸行為。常用低字節(jié)有這么幾個(gè)命令低字節(jié)重復(fù)性時(shí)鐘拉伸測(cè)量時(shí)間上限0x06高使能15.5ms0x10高禁用15.5ms0x0D中使能6.5ms0x1E中禁用6.5ms0x13低使能2.5ms0x27低禁用2.5ms時(shí)鐘拉伸這里要多解釋一句。所謂時(shí)鐘拉伸就是從設(shè)備在測(cè)量期間主動(dòng)拉低SCL告訴主機(jī)我還沒(méi)準(zhǔn)備好你別往下走。這在單次測(cè)量、主機(jī)不等延時(shí)直接讀取的場(chǎng)景下很有用。但不巧STM32F103的HAL庫(kù)I2C在阻塞模式下對(duì)時(shí)鐘拉伸的支持并不順暢實(shí)際測(cè)下來(lái)用0x2C 0x06配合HAL_I2C_Master_Receive有時(shí)候會(huì)卡在等待SCL釋放的地方我后文會(huì)專門(mén)講這個(gè)坑。所以我做例程時(shí)默認(rèn)用0x2C 0x10——禁用時(shí)鐘拉伸高重復(fù)性然后在發(fā)完命令后主動(dòng)HAL_Delay(20)等它測(cè)量完再讀。20ms比15.5ms的上限多了余量哪怕芯片略有批次差異也足夠。你要是做低功耗可以把重復(fù)性降到中或低延時(shí)隨之縮短。接下來(lái)是讀取數(shù)據(jù)。SHT30會(huì)返回6字節(jié)結(jié)構(gòu)固定成字節(jié)索引內(nèi)容0溫度數(shù)據(jù)高位1溫度數(shù)據(jù)低位2溫度CRC8校驗(yàn)值3濕度數(shù)據(jù)高位4濕度數(shù)據(jù)低位5濕度CRC8校驗(yàn)值溫度和濕度原始值都是16位無(wú)符號(hào)數(shù)。溫度轉(zhuǎn)換公式是-45 175 * rawT / 65535濕度轉(zhuǎn)換公式是100 * rawH / 65535。這個(gè)公式手冊(cè)上有但你真寫(xiě)代碼時(shí)很容易把除數(shù)和被除數(shù)寫(xiě)反我犯過(guò)這毛病結(jié)果溫度直接飆到一百多度。注意rawT和rawH都是0到65535的整數(shù)65535就是滿量程。重點(diǎn)說(shuō)一下CRC8校驗(yàn)。兩個(gè)數(shù)據(jù)各跟一個(gè)校驗(yàn)字節(jié)已經(jīng)是很業(yè)界良心的做法了。多項(xiàng)式是0x31初始值0xFF和很多I2C傳感器用的CRC算法一致。比如SHT31、SGP30都是這套寫(xiě)一遍以后還能復(fù)用到別的傳感器上。千萬(wàn)別偷懶跳過(guò)校驗(yàn)數(shù)據(jù)線稍微長(zhǎng)一點(diǎn)或者供電紋波大偶發(fā)一個(gè)字節(jié)錯(cuò)誤是會(huì)很折磨人的有校驗(yàn)?zāi)苤苯咏o你報(bào)錯(cuò)重讀比你在串口上看到濕度98%但實(shí)際濕度60%強(qiáng)一百倍。還有一個(gè)容易忽略的細(xì)節(jié)SHT30上電后不要立刻發(fā)命令建議等100ms以上因?yàn)樗鼉?nèi)部的穩(wěn)壓器和晶振需要啟動(dòng)穩(wěn)定時(shí)間。我自己例程里在MCU初始化后先做了一次空讀操作權(quán)當(dāng)喚醒復(fù)位避免上電毛刺導(dǎo)致首幀數(shù)據(jù)異常。3. 搭建STM32F103ZET6的HAL庫(kù)工程標(biāo)題里的F130是筆誤先糾正一個(gè)事STM32家族里其實(shí)沒(méi)有F130ZET6這個(gè)型號(hào)ST的F1系列里是STM32F103ZET6。舍得查資料就會(huì)發(fā)現(xiàn)這個(gè)F130大概率是筆誤或者輸入法聯(lián)想出來(lái)的。我這邊板子用的就是F103ZET6動(dòng)不動(dòng)512KB Flash、64KB RAM的資源做傳感器采集綽綽有余。下面所有操作按F103ZET6進(jìn)行。我建工程用的是STM32CubeMX圖形化配置它生成HAL庫(kù)框架然后我再往里加驅(qū)動(dòng)。CubeMX版本不用太糾結(jié)我用的是6.x系列。打開(kāi)CubeMX新建工程時(shí)芯片型號(hào)選STM32F103ZET6調(diào)試接口要記得在SYS里勾上Serial Wire不然板子第一次下載完還能跑第二次就提示找不到芯片了這個(gè)坑不知道坑過(guò)多少人了。時(shí)鐘樹(shù)部分F103ZET6最高主頻72MHz。我外部晶振用的8MHz無(wú)源晶振配置HSE并倍頻到72MHzAPB1總線注意要二分頻到36MHz這不光是為了系統(tǒng)穩(wěn)定更關(guān)鍵的是I2C外設(shè)掛在APB1上而I2C想要400kHz快速模式前提就是APB1時(shí)鐘36MHz。你要是用默認(rèn)的72MHz給APB1I2C時(shí)序會(huì)完全不對(duì)通信基本廢掉。外設(shè)勾選這邊主要開(kāi)三樣I2C1我實(shí)際用PB6和PB7對(duì)應(yīng)I2C1的SCL和SDA外設(shè)使能后選Standard Mode也就是100kHz。實(shí)際測(cè)試100kHz最穩(wěn)SHT30數(shù)據(jù)手冊(cè)雖然支持400kHz但線路長(zhǎng)了之后400kHz很容易出現(xiàn)CRC校驗(yàn)失敗。USART1也就是PA9的TX和PA10的RX波特率115200用來(lái)把溫濕度打印到串口助手。GPIO留一個(gè)LED引腳做運(yùn)行指示我用的PD2你也可以用板載LED對(duì)應(yīng)的引腳。CubeMX生成完代碼后別忘了檢查一下I2C初始化函數(shù)中Timing參數(shù)。HAL庫(kù)在F1上I2C初始化時(shí)不需要像F4那樣手動(dòng)算I2C_TIMINGR寄存器而是由HAL庫(kù)根據(jù)APB1時(shí)鐘自動(dòng)計(jì)算這算是F1的一個(gè)優(yōu)勢(shì)。你只要保證I2C1的Clock Speed填100000就行。工程搭建完成后目錄里會(huì)有Inc/Src兩個(gè)主要文件夾。驅(qū)動(dòng)文件我會(huì)新建sht30.h和sht30.c放在Src和Inc目錄下。這里面有一個(gè)隱藏注意點(diǎn)CubeMX生成的代碼不吃手動(dòng)往main.c里塞上千行工程式代碼尤其每次你重新生成工程main.c會(huì)被覆蓋重寫(xiě)所以我的習(xí)慣是全部驅(qū)動(dòng)獨(dú)立成文件main.c里只留調(diào)用入口這樣重新生成工程時(shí)不需要心疼自己寫(xiě)的代碼丟失。4. 驅(qū)動(dòng)代碼逐段解析從單次測(cè)量到CRC校驗(yàn)下面這段驅(qū)動(dòng)就是我最常用、實(shí)測(cè)最穩(wěn)的版本。先說(shuō)清楚這個(gè)驅(qū)動(dòng)只做單次測(cè)量模式周期測(cè)量我放到最后擴(kuò)展里講。代碼思路很簡(jiǎn)單先發(fā)命令再延時(shí)再讀6字節(jié)最后解算。在sht30.h里這樣聲明#ifndef __SHT30_H #define __SHT30_H #include main.h uint8_t SHT30_Read_TH(float *temperature, float *humidity); #endifsht30.c的完整實(shí)現(xiàn)#include sht30.h #define SHT30_ADDR_W (0x44 1) // I2C寫(xiě)地址7位地址0x44左移 #define SHT30_ADDR_R (0x44 1) | 0x01 // I2C讀地址 #define SHT30_CMD_MS_H 0x2C // 單次測(cè)量命令高字節(jié) #define SHT30_CMD_MS_L 0x10 // 高重復(fù)性禁用時(shí)鐘拉伸 // CRC8校驗(yàn)多項(xiàng)式0x31初始值0xFF static uint8_t SHT30_CRC8(uint8_t *data, uint8_t len) { uint8_t crc 0xFF; while (len--) { crc ^ *data; for (uint8_t i 0; i 8; i) { crc (crc 0x80) ? (uint8_t)((crc 1) ^ 0x31) : (uint8_t)(crc 1); } } return crc; } uint8_t SHT30_Read_TH(float *temperature, float *humidity) { uint8_t cmd[2] {SHT30_CMD_MS_H, SHT30_CMD_MS_L}; uint8_t buf[6] {0}; if (HAL_I2C_Master_Transmit(hi2c1, SHT30_ADDR_W, cmd, 2, 100) ! HAL_OK) { return 1; } // 高重復(fù)性測(cè)量上限15.5ms給20ms余量 HAL_Delay(20); if (HAL_I2C_Master_Receive(hi2c1, SHT30_ADDR_R, buf, 6, 100) ! HAL_OK) { return 2; } // 分別校驗(yàn)溫度和濕度的CRC if (SHT30_CRC8(buf[0], 2) ! buf[2]) { return 3; } if (SHT30_CRC8(buf[3], 2) ! buf[5]) { return 4; } uint16_t rawT ((uint16_t)buf[0] 8) | buf[1]; uint16_t rawH ((uint16_t)buf[3] 8) | buf[4]; *temperature -45.0f 175.0f * (float)rawT / 65535.0f; *humidity 100.0f * (float)rawH / 65535.0f; return 0; }這段代碼有幾個(gè)關(guān)鍵點(diǎn)要展開(kāi)講。第一I2C地址為什么要左移。SHT30的7位地址是0x44焊盤(pán)ADDR接GND時(shí)為0x45。但在HAL庫(kù)的HAL_I2C_Master_Transmit里傳入的地址必須包含讀寫(xiě)位也就是8位地址所以得把0x44左移1位得到0x88作為寫(xiě)地址或最低位置1得到0x89作為讀地址。很多人剛開(kāi)始會(huì)直接把0x44填進(jìn)去然后發(fā)現(xiàn)I2C總線根本沒(méi)有ACK就是卡在這。第二為什么我不把HAL_Delay換成查詢狀態(tài)。SHT30支持一種變通發(fā)完命令直接進(jìn)入讀流程依靠時(shí)鐘拉伸等傳感器完成。前面提過(guò)這個(gè)思路在STM32F103 HAL庫(kù)下容易踩坑。所以我寧愿犧牲20ms延時(shí)換取確定不卡死的操作。在大多數(shù)采集場(chǎng)景20ms間隔完全夠用就算你要一秒采一次也沒(méi)影響。第三返回錯(cuò)誤碼代替直接停機(jī)。驅(qū)動(dòng)不管讀成功與否都返回一個(gè)狀態(tài)碼主程序可以根據(jù)狀態(tài)碼決定是打印串口信息還是亮LED報(bào)警。我習(xí)慣用1表示發(fā)送失敗2表示接收失敗3表示溫度CRC錯(cuò)誤4表示濕度CRC錯(cuò)誤。這樣一旦現(xiàn)場(chǎng)出了問(wèn)題看一眼錯(cuò)誤碼就能定位到通信的哪個(gè)環(huán)節(jié)。主程序調(diào)用就簡(jiǎn)單了我在while循環(huán)里每秒讀一次float temp 0.0f, humi 0.0f; uint8_t ret 0; char msg[64]; while (1) { ret SHT30_Read_TH(temp, humi); if (ret 0) { sprintf(msg, Temp: %.2f C, Humi: %.2f %%\r\n, temp, humi); HAL_UART_Transmit(huart1, (uint8_t *)msg, strlen(msg), 100); } else { sprintf(msg, SHT30 Error: %d\r\n, ret); HAL_UART_Transmit(huart1, (uint8_t *)msg, strlen(msg), 100); HAL_Delay(300); } HAL_Delay(1000); }這里注意sprintf格式化濕度百分比時(shí)我在格式串中用的是兩個(gè)百分號(hào)%因?yàn)閟printf里單個(gè)百分號(hào)會(huì)被識(shí)別成格式符不加兩個(gè)百分號(hào)打印出來(lái)數(shù)據(jù)就亂套了。這種細(xì)節(jié)調(diào)試時(shí)最浪費(fèi)時(shí)間。5. 燒進(jìn)去之后三個(gè)實(shí)測(cè)坑與排查方法代碼寫(xiě)完了上電實(shí)測(cè)階段往往才是真正耗時(shí)間的地方。我在三輪不同項(xiàng)目的調(diào)試中先后遇到這么幾個(gè)典型問(wèn)題每一個(gè)都值得拿出來(lái)復(fù)盤(pán)。第一個(gè)坑也是最常見(jiàn)的串口一直打Error Code 2或者3接收失敗或者CRC過(guò)不去。先別急著懷疑代碼拿出示波器或者邏輯分析儀抓I2C波形。如果沒(méi)有示波器就直接看SDA和SCL的對(duì)地電壓用萬(wàn)用表量正??臻e狀態(tài)都應(yīng)該是3.3V。如果是0V說(shuō)明總線被拉死大概率是模塊上拉電阻缺失。我之前用過(guò)一塊很便宜的SHT30模塊板子上沒(méi)焊上拉量下來(lái)SCL一直低電平。補(bǔ)兩個(gè)10k電阻到3.3V問(wèn)題立刻消失。如果你手頭正好沒(méi)有10k4.7k到47k的也可以用只是上拉太小會(huì)加重主機(jī)負(fù)載上拉太大則翻轉(zhuǎn)速度變慢別太離譜就行。第二個(gè)坑發(fā)完0x2C 0x06后HAL_I2C_Master_Receive卡死。這個(gè)坑我印象極深。我用0x2C 0x06配合HAL庫(kù)直接讀取結(jié)果程序死在接收函數(shù)里HardFault前反復(fù)卡在上面的超時(shí)查詢。仔細(xì)看SHT30手冊(cè)才發(fā)現(xiàn)0x06是使能時(shí)鐘拉伸傳感器測(cè)量期間會(huì)把SCL拉低HAL庫(kù)在阻塞模式下一直等待SCL釋放但SHT30既要拉低SCL又要回?cái)?shù)據(jù)兩者配合在F1的I2C模塊上就產(chǎn)生了死鎖現(xiàn)象。原理層面的東西不好深究但解決起來(lái)很簡(jiǎn)單命令改成0x10禁用時(shí)鐘拉伸然后自己延時(shí)20ms。改完之后這個(gè)問(wèn)題再?zèng)]出現(xiàn)過(guò)。這也讓我徹底認(rèn)清硬件I2C固然穩(wěn)定但必須匹配從設(shè)備的時(shí)序特性不是所有命令都合適。第三個(gè)坑是數(shù)據(jù)偶發(fā)跳變溫度濕度一會(huì)兒正常一會(huì)兒離譜地大。排查下來(lái)發(fā)現(xiàn)是我在測(cè)試時(shí)杜邦線飛了大概20厘米供電又從另一塊板子上取導(dǎo)致地回路不穩(wěn)。SHT30這類(lèi)高精度傳感器對(duì)供電很敏感我后來(lái)在VCC和GND之間加了一個(gè)100nF去耦電容并且把I2C線縮短到10厘米以內(nèi)跳變就消失了。這個(gè)現(xiàn)象在DHT11上不會(huì)那么明顯因?yàn)镈HT11本身精度就差跳變兩三個(gè)點(diǎn)你根本不在乎但SHT30精度高偶發(fā)偏差就會(huì)直接暴露出來(lái)。除了這三個(gè)坑還有幾個(gè)邊界情況要處理。硬件I2C偶爾會(huì)因從機(jī)異常進(jìn)入總線鎖死狀態(tài)表現(xiàn)為SCL/SDA長(zhǎng)期為低。我在工程里加了一個(gè)I2C總線恢復(fù)函數(shù)原理是把SCL對(duì)應(yīng)的引腳臨時(shí)配置為GPIO輸出手動(dòng)翻轉(zhuǎn)至少9個(gè)時(shí)鐘同時(shí)把SDA釋放為高電平這樣可以讓卡在錯(cuò)誤狀態(tài)的從機(jī)復(fù)位。這個(gè)函數(shù)平時(shí)用不上但傳感器熱插拔或者供電抖動(dòng)之后它能救你一把。另外關(guān)于讀取頻率SHT30單次測(cè)量模式下最小間隔可以做到幾十毫秒但我不建議無(wú)腦快速循環(huán)讀。一方面頻繁讀會(huì)增加功耗另一方面?zhèn)鞲衅鞅旧碛幸欢ǖ捻憫?yīng)時(shí)間讀數(shù)變化不會(huì)瞬時(shí)發(fā)生你要做數(shù)據(jù)平滑不如直接讀取間隔設(shè)置到500ms或1s以上。做產(chǎn)品尤其要注意這個(gè)別把CPU和I2C總線無(wú)限壓榨。6. 再往前一步周期測(cè)量模式與低功耗設(shè)計(jì)建議單次測(cè)量完全可以覆蓋大多數(shù)應(yīng)用但如果你做的是電池供電設(shè)備比如溫濕度記錄儀或者智能家居里的傳感器節(jié)點(diǎn)那SHT30的周期測(cè)量模式更值得研究。周期測(cè)量模式下傳感器內(nèi)部會(huì)以固定頻率自己完成測(cè)量?jī)煞N常見(jiàn)配置是每秒2次命令0x20 0x32和每秒10次命令0x20 0x22都是高重復(fù)性。設(shè)置好之后主機(jī)不用再發(fā)測(cè)量命令只要定時(shí)去讀6字節(jié)數(shù)據(jù)就行。這樣主控可以在兩次讀取之間進(jìn)入睡眠由傳感器自己維持測(cè)量節(jié)奏功耗優(yōu)勢(shì)非常明顯。實(shí)測(cè)下來(lái)2mps配置下SHT30的平均電流在數(shù)十微安級(jí)別一顆小鋰電池足夠撐很久。但周期模式也有個(gè)容易誤解的地方命令發(fā)一次后傳感器會(huì)一直按這個(gè)頻率測(cè)量如果你不切換到其他模式它永遠(yuǎn)不會(huì)自己停。所以代碼里的初始化邏輯要做好管理比如設(shè)備上電后發(fā)一次0x20 0x32后續(xù)循環(huán)里直接HAL_I2C_Master_Receive讀數(shù)據(jù)不要再重復(fù)發(fā)命令否則每次都會(huì)疊加一次配置重置影響讀時(shí)序。想退出周期模式時(shí)可以發(fā)送單次測(cè)量命令比如0x2C 0x10傳感器就會(huì)回到單次模式。還要留意周期模式下讀到的值并不一定每次都是新的。因?yàn)閿?shù)據(jù)緩存更新頻率和讀取頻率可能存在相位差連續(xù)讀兩次可能拿到同一個(gè)數(shù)據(jù)。這不算故障做數(shù)據(jù)記錄時(shí)去重或做一下時(shí)間戳即可。如果讀數(shù)長(zhǎng)期不變才需要懷疑傳感器卡在舊狀態(tài)可以用讀取狀態(tài)寄存器命令0xF3 0x2D檢查Bit15是否為零零表示正常狀態(tài)。低功耗設(shè)計(jì)的另一個(gè)細(xì)節(jié)是SHT30在每次測(cè)量完成后需要至少一個(gè)I2C停止條件才能進(jìn)入內(nèi)部空閑狀態(tài)。所以無(wú)論用HAL庫(kù)還是軟件模擬I2C讀取結(jié)束一定要讓總線產(chǎn)生STOP這也是HAL_I2C_Master_Receive天然做好的事情不需要額外處理。還有一個(gè)容易被忽略的小功能SHT30內(nèi)部帶加熱器可以通過(guò)命令0x30 0x0D開(kāi)啟0x30 0x00關(guān)閉。這個(gè)加熱器不是用來(lái)給環(huán)境加溫的而是用來(lái)在潮濕或者結(jié)露環(huán)境下烘干傳感器表面保證濕度測(cè)量不會(huì)因?yàn)閭鞲衅髡謨?nèi)積水而失真。如果你把設(shè)備用在戶外或者冷庫(kù)門(mén)口這類(lèi)濕度高的地方可以在初始化后周期性開(kāi)啟加熱器十幾秒然后關(guān)閉等穩(wěn)定再讀值。我自己很少用但在一個(gè)冷鏈記錄項(xiàng)目里測(cè)試過(guò)確實(shí)能把高濕工況下的數(shù)據(jù)漂移拉回來(lái)。7. 寫(xiě)在最后的一點(diǎn)實(shí)戰(zhàn)體會(huì)如果把整個(gè)SHT30驅(qū)動(dòng)過(guò)程濃縮成一條經(jīng)驗(yàn)?zāi)蔷褪菙?shù)字傳感器調(diào)試的核心不是把代碼寫(xiě)出來(lái)而是把通信鏈路上每一段電氣行為和時(shí)序行為都搞明白。DHT11的軟件時(shí)序讓你學(xué)會(huì)摳延時(shí)SHT30的HAL庫(kù)例程則讓你學(xué)會(huì)信協(xié)議、查CRC、管理總線狀態(tài)。這兩個(gè)方向并不沖突經(jīng)歷過(guò)一個(gè)再上手另一個(gè)上手速度會(huì)快很多。我個(gè)人在實(shí)際項(xiàng)目中的最終配置就是STM32F103ZET6作為主控硬件I2C1速率100kHzSHT30地址0x44命令固定0x2C 0x10加20ms延時(shí)讀取后帶CRC校驗(yàn)串口打印。這套組合我在至少三個(gè)不同環(huán)境下的設(shè)備上連續(xù)運(yùn)行過(guò)數(shù)月數(shù)據(jù)穩(wěn)定沒(méi)有一次總線鎖死或者臟數(shù)據(jù)。你照著搭大概率也能一次跑通。如果后續(xù)想擴(kuò)展建議去看Sensirion官方的嵌入式驅(qū)動(dòng)包里面還有狀態(tài)寄存器解析、加熱器控制、傳感器復(fù)位等接口邏輯清晰拿過(guò)來(lái)改改就能嵌套進(jìn)你的HAL工程。不過(guò)官方的抽象層次較高不建議第一版例程就引進(jìn)去先把單次測(cè)量跑通、把原始數(shù)據(jù)打印出來(lái)再逐步增加復(fù)雜度這才是最穩(wěn)妥的路徑。本文還有配套的精品資源點(diǎn)擊獲取