動實戰(zhàn):I2C讀寫與CRC校驗)
簡介面向STM32嵌入式開發(fā)者的SHT30溫濕度傳感器驅(qū)動資源演示如何通過模擬IIC在STM32F407上完成溫濕度讀取適合需要掌握IIC時序、傳感器寄存器配置的初學(xué)者和項目開發(fā)者。壓縮包共3個文件包含SHT30.c/H驅(qū)動源碼與溫濕度.pdf說明文檔整體僅858KB其中C/H文件提供可直接移植的驅(qū)動接口覆蓋GPIO模擬SDA/SCL、啟動/停止信號、讀寫地址與數(shù)據(jù)校驗等關(guān)鍵環(huán)節(jié)PDF則詳細(xì)梳理SHT30的7位地址0x44/0x45、單次/周期測量模式配置及IIC完整通信步驟便于對照理解。目前已有1595人學(xué)習(xí)下載。借助該資源可快速跑通模擬IIC通信流程省去自行閱讀數(shù)據(jù)手冊和反復(fù)調(diào)試時序的精力也能為后續(xù)接入其他IIC傳感器提供清晰的代碼范式與排錯思路尤其適合在環(huán)境監(jiān)測、智能家居等場景中快速落地。 解壓SHT30溫濕度傳感器stm32f4.zip這個壓縮包時我心里大概有數(shù)這又是一套STM32環(huán)境采集的常規(guī)工程。但把代碼完整過了一遍后發(fā)現(xiàn)這個工程包做得比想象中完整從STM32F407的I2C初始化、SHT30的寄存器讀寫、CRC校驗到串口輸出、Keil5燒錄配置都有不是那種只丟一個main.c的碎片工程。這個項目能解決的問題很直接用一塊Cortex-M4單片機穩(wěn)定讀取SHT30溫濕度傳感器數(shù)據(jù)通過I2C總線把原始數(shù)據(jù)變成真正的溫度濕度數(shù)值。整個過程不依賴現(xiàn)成模塊庫代碼邏輯完全可控特別適合兩類人——一是剛把STM32跑起來、想快速上手一個真實傳感器的初學(xué)者二是手里有項目需求、想把SHT30低成本集成進現(xiàn)有固件的老手。1. 這個工程包到底做了什么1.1 SHT30環(huán)境采集里非常能打的選手SHT30是瑞士Sensirion盛思銳出的數(shù)字溫濕度傳感器和傳統(tǒng)濕敏電阻方案最大的區(qū)別在于它直接輸出經(jīng)過校準(zhǔn)的數(shù)字信號溫度和濕度都是16bit精度不需要自己做校準(zhǔn)和多點標(biāo)定。精度方面溫度典型值±0.3°C濕度典型值±2%RH做環(huán)境監(jiān)測、氣象站、冷鏈運輸記錄、溫室大棚控制都綽綽有余。同時它的接口是I2C最高支持1MHz快速模式單次測量命令一次讀回6個字節(jié)溫度高字節(jié)、低字節(jié)、溫度CRC、濕度高字節(jié)、低字節(jié)、濕度CRC數(shù)據(jù)干凈利落很省心。我在項目里橫向?qū)Ρ冗^DHT11、AM2302和SHT30。DHT11勝在便宜、程序簡單但精度和一致性只能說“湊合能用”AM2302某種程度上是DHT11的升級版測量范圍大一些但時序敏感、每次讀取必須嚴(yán)格按照起始信號來稍不注意就超時。SHT30貴一點但基本不用調(diào)上電就能拿到穩(wěn)定數(shù)據(jù)長期漂移也小很多。如果是做量產(chǎn)產(chǎn)品SHT30的性價比其實更高省掉了大量后期標(biāo)定和測試時間。1.2 為什么主控選STM32F4而不選其他這個工程包選擇STM32F4核心原因不外乎三點性能充裕、外設(shè)豐富、生態(tài)成熟。STM32F407主頻168MHz跑I2C這種低速總線完全是殺雞用牛刀但正是這種性能冗余給后期擴展留了空間比如同時掛EMMC存數(shù)據(jù)、用SPI1接磁編碼器MT6701、再接一塊LCD屏CPU也不會卡頓。另外STM32F4的I2C、SPI、USART、SDIO接口齊全在一顆芯片上就能完成“傳感器采集數(shù)據(jù)存儲對外通信”的閉環(huán)。相比用51單片機或者F1系列F4有更大的Flash和RAM工程里即便加上FatFS文件系統(tǒng)、Modbus協(xié)議棧資源也不會捉襟見肘。對初學(xué)者來說STM32F4還有一層優(yōu)勢網(wǎng)上資料特別多。而且工程用Keil5開發(fā)圖形化界面配好燒錄器之后點一下Load就能下載程序調(diào)試門檻比Linux板卡低得多。這套方案放在2025年來看依然是最穩(wěn)妥的入門路線不會被各種新架構(gòu)帶偏。1.3 工程包的代碼結(jié)構(gòu)長什么樣解壓之后里面的文件路徑大概是這樣的SHT30_STM32F4/ ├── Core/ │ ├── Inc/ │ │ ├── main.h │ │ └── sht30.h │ └── Src/ │ ├── main.c │ └── sht30.c ├── Drivers/ │ ├── CMSIS/ │ └── STM32F4xx_HAL_Driver/ ├── MDK-ARM/ │ ├── Project.uvprojx │ └── ... └── Readme.txt這個結(jié)構(gòu)是從STM32CubeMX生成的工程模板改過來的把SHT30驅(qū)動單獨拆成sht30.c和sht30.h和主業(yè)務(wù)邏輯解耦。好處很明顯以后換傳感器只要替換驅(qū)動文件不用動main.c里的業(yè)務(wù)代碼代碼給別人看也是先看接口再看實現(xiàn)理解成本低很多。2. 硬件連接和電路設(shè)計里容易忽略的細(xì)節(jié)2.1 引腳定義與I2C地址的坑SHT30的接口很簡單常用引腳就5個VDD、GND、SCL、SDA、ADDR。默認(rèn)情況下ADDR接地器件7位地址是0x44如果ADDR接VDD地址變成0x45。我第一次調(diào)這塊芯片就栽在地址上——HAL庫的I2C函數(shù)要求傳的是8位地址也就是說要對左移一位寫地址是0x88、讀地址是0x89很多人直接填0x44結(jié)果總線一直NACK查了整整半天。STM32F4這邊常用的I2C1引腳是PB6SCL和PB7SDAI2C2是PB10/PB11I2C3是PA8/PC9。選哪一路不影響驅(qū)動邏輯但要注意引腳是否和其他外設(shè)沖突。這個工程用的是I2C1接法如下表SHT30引腳STM32F4引腳說明VDD3.3V供電GNDGND地SCLPB6I2C1時鐘線SDAPB7I2C1數(shù)據(jù)線ADDRGND默認(rèn)地址0x442.2 上拉電阻和電源去耦I(lǐng)2C總線是開漏結(jié)構(gòu)必須在SCL和SDA上接上拉電阻。很多人第一次畫板子會漏掉這一步然后發(fā)現(xiàn)I2C通信時好時壞。上拉電阻阻值取4.7kΩ比較保守如果總線上設(shè)備多或者線比較長可以降到2.2kΩ反過來如果I2C速率高、走線短10kΩ也能工作。SHT30手冊給出的范圍是1kΩ到10kΩ推薦從4.7kΩ起步。還有一個容易被忽略的點傳感器供電紋波。SHT30在3.3V下工作如果電源來自DC-DC模塊且紋波偏大濕度數(shù)據(jù)會出現(xiàn)隨機跳動。最好在VDD和GND之間放一個100nF陶瓷電容空間允許再加一個10μF鉭電容效果會穩(wěn)很多。此外SHT30這類傳感器對VDD爬坡速度也有要求上電太慢可能導(dǎo)致初始化失敗穩(wěn)定供電是前提。2.3 一塊板子上掛多個I2C設(shè)備的地址規(guī)劃I2C總線天然支持多設(shè)備掛載但SHT30的地址選擇只有0x44和0x45兩檔一個I2C總線上最多掛兩顆。如果項目需要測多個點位的溫濕度就得用TCA9548A這類I2C多路復(fù)用器把總線擴展成8路每路再掛SHT30。這個工程雖然只驗證了一顆傳感器但驅(qū)動代碼里用了地址宏定義換成0x45或者動態(tài)傳地址都很方便。規(guī)劃階段就把地址位分配好后面聯(lián)調(diào)省太多事。3. 驅(qū)動代碼拆解從I2C到溫濕度數(shù)據(jù)3.1 I2C底層用硬件還是軟件模擬SHT30本身只要求標(biāo)準(zhǔn)I2C時序STM32F4的硬件I2C完全夠用。用HAL庫的話幾行代碼就能完成一次讀取我建議新手直接上硬件I2C省心且不占CPU。不過要提醒一句STM32F4的硬件I2C偶爾會出現(xiàn)BUSY標(biāo)志卡死的情況這時候強制拉一下SCL或者在初始化里調(diào)用__HAL_I22C_CLEAR_FLAG(hi2c1, I2C_FLAG_BUSY)就能解掉。如果習(xí)慣把I2C引腳換成任意GPIO或者想徹底避開硬件I2C的時序問題也可以自己寫軟件I2C。方式就是用兩個普通GPIO模擬開漏輸出配合延時翻轉(zhuǎn)電平。這種方案移植性最強換個單片機只要改引腳定義就能跑。缺點是CPU占用高、抗干擾依賴軟件延時準(zhǔn)確性所以量產(chǎn)代碼我更推薦硬件I2C。3.2 SHT30核心命令與讀取流程SHT30的命令分單次測量和周期測量兩大類。單次測量適合“需要時才測一次”的場景周期測量適合持續(xù)監(jiān)控。工程里用的是單次測量命令是0x2C 0x06含義是高重復(fù)性、時鐘延展模式。讀取流程四步走主機向0x88地址發(fā)送測量命令0x2C 0x06等待芯片完成測量高重復(fù)性模式下約15ms主機從0x89地址讀取6個字節(jié)解析溫度和濕度原始值并做CRC校驗核心代碼#define SHT30_ADDR_W 0x88 // I2C寫地址 #define SHT30_ADDR_R 0x89 // I2C讀地址 #define SHT30_CMD_MS_H 0x2C // 單次測量高重復(fù)性 #define SHT30_CMD_MS_L 0x06 uint8_t sht30_read_data(float *temp, float *hum) { uint8_t buf[6]; uint8_t cmd[2] {SHT30_CMD_MS_H, SHT30_CMD_MS_L}; // 發(fā)送測量命令 if (HAL_I2C_Master_Transmit(hi2c1, SHT30_ADDR_W, cmd, 2, 100) ! HAL_OK) return 1; HAL_Delay(20); // 等待測量完成留點余量 // 讀取6字節(jié)數(shù)據(jù) if (HAL_I2C_Master_Receive(hi2c1, SHT30_ADDR_R, buf, 6, 100) ! HAL_OK) return 2; // 校驗CRC if (SHT30_CRC8(buf[0], 2) ! buf[2] || SHT30_CRC8(buf[3], 2) ! buf[5]) return 3; // 轉(zhuǎn)換溫度、濕度 uint16_t rawT (buf[0] 8) | buf[1]; uint16_t rawH (buf[3] 8) | buf[4]; *temp -45.0f 175.0f * rawT / 65535.0f; *hum 100.0f * rawH / 65535.0f; return 0; }一個細(xì)節(jié)測量完成前絕對不要發(fā)讀命令否則讀出來的是上上次的舊數(shù)據(jù)。工程里用HAL_Delay(20)是簡單粗暴但有效的方式如果對時序有嚴(yán)格要求可以改成讀取前的SCL電平輪詢或者用SHT30的“測量后查詢”模式那個不需要延時就知道了測量狀態(tài)。3.3 CRC校驗到底在防什么SHT30返回的6個字節(jié)里每個16bit數(shù)據(jù)后面都跟了1個8bit CRC由芯片內(nèi)部CRC-8引擎生成多項式0x31初始值0xFF。這個設(shè)計專門用來檢測通信鏈路是否受干擾。I2C走線如果比較長或者環(huán)境里有電機、開關(guān)電源之類的干擾源數(shù)據(jù)線上會出現(xiàn)毛刺收到的原始值可能發(fā)生位翻轉(zhuǎn)。沒有CRC校驗時一個反了的bit可能讓溫度從25°C跳到-15°C后果很嚴(yán)重。所以驅(qū)動里的CRC校驗不是可選項而是必選項。我見過很多工程把CRC驗證注釋掉靠平均值濾波來兜底這種方式在實驗室里沒問題放到工業(yè)現(xiàn)場遲早翻車。CRC代碼也不復(fù)雜一個循環(huán)就寫完uint8_t SHT30_CRC8(uint8_t *data, uint8_t len) { uint8_t crc 0xFF; for (uint8_t i 0; i len; i) { crc ^ data[i]; for (uint8_t bit 0; bit 8; bit) { if (crc 0x80) crc (crc 1) ^ 0x31; else crc 1; } } return crc; }3.4 溫濕度計算公式與精度取舍SHT30的原始數(shù)據(jù)是16bit范圍0~65535換算成物理量的公式非常固定溫度T -45 175 * rawT / 65535濕度RH 100 * rawH / 65535這個公式直接來自芯片手冊套用就好。有一點要注意濕度公式算出來是相對濕度RH和絕對濕度、露點溫度不是一回事。如果要算露點需要用Magnus公式結(jié)合溫度和相對濕度近似計算。工程里只輸出了溫濕度但代碼里留了擴展位加一個露點計算函數(shù)也就十來行。在STM32F4上用浮點運算計算溫濕度毫無壓力因為F4帶硬件單精度浮點單元FPU。如果換成Cortex-M0這類沒有FPU的芯片就要考慮把浮點換成定點數(shù)比如溫度用擴大100倍的整數(shù)表示輸出時再除100能顯著降低CPU開銷。這種對精度的取舍做嵌入式要時刻記在心里。4. Keil5燒錄STM32F4工程全流程4.1 工程模板與文件組織用Keil5打開工程后左側(cè)Project欄會看到Target下的分組Application里是main.c和sht30.cDrivers里是HAL庫源碼和CMSIS啟動文件。工程用的是STM32F407VET6啟動文件startup_stm32f407xx.s不能選錯選成F103的啟動文件芯片直接跑不起來。系統(tǒng)時鐘配置這里也要留意F407默認(rèn)25MHz外部晶振如果用8MHz晶振必須改PLL配置否則串口波特率會偏差到?jīng)]法用。4.2 燒錄前必須檢查的5個設(shè)置我把這個工程里燒錄踩過的坑整理成5個檢查點芯片型號Options for Target - Device選擇STMicroelectronics - STM32F4 Series - 對應(yīng)型號。型號不匹配時Flash燒錄算法和內(nèi)存地址映射都會出錯。宏定義C/C - Define必須寫USE_HAL_DRIVER,STM32F407xx。USE_HAL_DRIVER告訴HAL庫啟用STM32F407xx告訴庫你的具體型號缺一個編譯直接報一大堆未定義錯誤。調(diào)試器選擇Debug - Use - ST-Link Debugger或J-LINKSettings里選擇SWD模式不建議用JTAG。SWD只占兩根線省出來的引腳能干別的。Flash燒錄算法Debug - Settings - Flash Download確認(rèn)Flash Size和實際芯片一致。報“No Algorithm”就是沒選對Flash要把STM32F4xx Flash添加進來。Reset and RunFlash Download里務(wù)必勾選Reset and Run這樣程序燒完自動復(fù)位執(zhí)行。沒勾也沒關(guān)系手動按復(fù)位鍵就行但調(diào)試效率會低不少。配置完成后Rebuild編譯再點Load下載。正常情況下Build Output窗口會顯示“Application running”板上的LED開始閃爍。4.3 串口打印調(diào)試技巧這個工程把溫濕度數(shù)據(jù)通過USART1打印波特率115200。用printf重定向時需要重寫fputc函數(shù)int fputc(int ch, FILE *f) { while (!(USART1-SR USART_FLAG_TXE)); USART1-DR (uint8_t)ch; return ch; }打開串口助手后把顯示格式設(shè)為字符串模式不然看到的全是亂碼。輸出數(shù)據(jù)盡量不要做太多格式化字符串越長MCU和串口助手兩邊的配合越容易出問題。我一般輸出CSV格式方便直接記錄到Excel里畫溫濕度曲線26.32,48.51 26.31,48.49 26.31,48.50這樣用串口助手的“保存到文件”功能就能低成本攢下一整天的溫濕度數(shù)據(jù)后續(xù)處理也方便。5. 實測中的典型問題與排查記錄5.1 傳感器無應(yīng)答讀回全是0xFF這是調(diào)試SHT30時最常遇到的問題?,F(xiàn)象是HAL_I2C_Master_Transmit返回值錯誤或者讀到的6個字節(jié)全是0xFF。排查順序一般是這樣的先用萬用表測VDD和GND有沒有電電壓是不是3.3V。遇到過開發(fā)板USB口供電不足電壓掉到2.8V傳感器就是不回應(yīng)。再查SCL、SDA有沒有接反。SHT30封裝比較小手工焊接時焊反很常見。確認(rèn)上拉電阻存在。用示波器看SCL/SDA如果空閑電平不是高電平多半是上拉電阻沒接或沒焊好。最后查代碼里的地址。前面強調(diào)過HAL庫要用0x88/0x89而不是0x44/0x45。5.2 溫濕度數(shù)值跳變、漂移如果CRC校驗已經(jīng)通過但數(shù)據(jù)還是會偶爾跳一下問題多半出在電源或布線上。經(jīng)驗是先給傳感器單獨加100nF去耦電容再把I2C導(dǎo)線從電機、繼電器等干擾源旁挪開最后在上層加一個簡單滑動平均濾波比如采5次取中間值。這些招數(shù)組合下來數(shù)據(jù)基本就穩(wěn)了。另外傳感器在通電初期濕度值會有一個“爬坡”過程這不是芯片壞了而是內(nèi)部高分子感濕膜在重新平衡環(huán)境濕度。從上電到穩(wěn)定一般需要幾分鐘尤其是剛從干燥倉儲環(huán)境拿出來時不要一上電就急著拿數(shù)據(jù)去驗收。5.3 Keil5下載時連接失敗Keil5點擊Load后提示“Cannot access target”我把它整理成一個速查表現(xiàn)象可能原因處理辦法連接失敗ST-Link驅(qū)動沒裝好重裝STSW-LINK009驅(qū)動連接失敗SWDIO/SWCLK接線松檢查杜邦線或PCB焊盤連接失敗芯片讀保護被使能用STM32CubeProgrammer去除讀保護連接失敗板子沒供電確認(rèn)VDD檢查調(diào)試器是否給板子供了電下載后不運行Flash Download沒勾Reset and Run勾上或者手動復(fù)位有一個情況要特別提醒如果之前往芯片里燒過讀保護代碼芯片會鎖定SWD口此時Keil完全連不上。解決方法是按住板子復(fù)位鍵點擊Download在出現(xiàn)燒錄進度前松開復(fù)位鍵通常能救回來救不回來就上STM32CubeProgrammer用整片擦除恢復(fù)。5.4 問題排查速查表癥狀大概率原因先查什么I2C讀取返回HAL_ERROR地址錯/上拉缺失/接線錯地址、波形讀回字節(jié)全為0xFFSDA線沒上拉/接線斷開上拉、連線數(shù)據(jù)偶爾跳變電源紋波/布線干擾去耦電容、走線溫濕度值離譜公式用錯/原始數(shù)據(jù)錯位CRC、公式CRC校驗一直失敗I2C速率太快/軟件I2C時序不對降速、延時長一點溫度正常濕度偏高傳感器受潮或靠近熱源烘干、隔離6. 后續(xù)擴展SHT30和EMMC、SPI外設(shè)的聯(lián)動6.1 用SDIO把溫濕度存進EMMC工程基礎(chǔ)功能跑通后一個很自然的擴展方向是把溫濕度數(shù)據(jù)存進存儲介質(zhì)做一個獨立的環(huán)境記錄儀。STM32F407的SDIO接口可以直接驅(qū)動eMMC芯片因為eMMC的接口協(xié)議和MMC/SD是兼容的通過SDIO總線就能訪問內(nèi)部的控制邏輯和NAND Flash。不過用eMMC不能只接四根線就完事有幾個點必須處理eMMC通常需要VCC和VCCQ兩路電源VCCQ電平看芯片是1.8V還是3.3V板子上要保證完整的初始化時序包括CMD0、CMD1、CMD2這些命令軟件層建議直接掛FatFS文件系統(tǒng)讓數(shù)據(jù)以文件形式保存主機端讀起來方便。如果不用文件系統(tǒng)就得自己實現(xiàn)壞塊管理和均衡磨損工作量成倍增加。在工程里可以每完成一次SHT30讀取就把溫濕度記錄追加到一個文本文件里再配上RTC時間戳一個冷鏈運輸記錄儀的原型就成了。6.2 SPI1驅(qū)動MT6701磁編碼器另一個相關(guān)的熱詞是“stm32f4配置spi1驅(qū)動mt6701”。MT6701是一款磁編碼器芯片輸出絕對角度機器人關(guān)節(jié)和云臺上經(jīng)常用它做位置反饋。它支持SPI、ABZ、PWM多種接口如果走SPI掛到STM32F4的SPI1主機接口即可SCK、MOSI、MISO、CS四根線軟件上按芯片手冊配好時鐘極性和相位讀一次就能拿到14位角度數(shù)據(jù)0到16383對應(yīng)360度。這樣組合下來一個系統(tǒng)里既有SHT30做環(huán)境監(jiān)測又有MT6701做運動位置反饋再加上EMMC做數(shù)據(jù)記錄本質(zhì)上就是“環(huán)境感知運動感知數(shù)據(jù)存儲”的完整方案。在需要長期無人值守的設(shè)備上這種組合很常見。6.3 擴展時要注意的資源沖突擴展外設(shè)之前先翻芯片數(shù)據(jù)手冊確認(rèn)引腳有沒有打架。比如SPI1如果用了PB3、PB4、PB5而I2C1用了PB6、PB7表面看不沖突但JTAG的默認(rèn)引腳正好也是PB3、PB4要把調(diào)試本文還有配套的精品資源點擊獲取