:從寄存器配置到數(shù)據(jù)校準(zhǔn))
簡介本資源是面向嵌入式開發(fā)工程師與STM32進(jìn)階學(xué)習(xí)者的LIS2DUX12三軸加速度計驅(qū)動實戰(zhàn)項目聚焦輪詢模式下加速度數(shù)據(jù)的穩(wěn)定讀取與基礎(chǔ)解析解決傳感器初始化、I2C通信配置、寄存器映射及原始數(shù)據(jù)轉(zhuǎn)換等典型開發(fā)痛點(diǎn)。壓縮包共189個文件涵蓋76個頭文件h定義寄存器宏與結(jié)構(gòu)體、26個C源文件c含HAL底層驅(qū)動、LIS2DUX12寄存器操作及主循環(huán)邏輯、27個目標(biāo)文件o及13份PDF技術(shù)文檔含芯片手冊關(guān)鍵頁與接口說明輔以Keil工程uvprojx/axf/hex和STM32CubeMX配置ioc整體大小為11.94MB。資源基于STM32H503CB平臺構(gòu)建包含完整可編譯工程、分層清晰的驅(qū)動代碼如lis2dux12_reg.c、時鐘與外設(shè)初始化模塊rcc/dma/i2c/uart以及帶注釋的數(shù)據(jù)獲取主流程便于讀者快速移植、調(diào)試并理解智能加速度計在低功耗運(yùn)動檢測場景下的基礎(chǔ)應(yīng)用實現(xiàn)。 拿到一顆新的三軸加速度計尤其是LIS2DUX12這種帶AI內(nèi)核、集成了有限狀態(tài)機(jī)和機(jī)器學(xué)習(xí)核的新一代超低功耗傳感器大部分人第一反應(yīng)是翻數(shù)據(jù)手冊、找官方示例然后照著寄存器表一頓配置急著把中斷、FIFO全部拉滿。但我的習(xí)慣恰恰相反第一步永遠(yuǎn)是先讓它“開口說話”——用最簡單、最土的方式把X/Y/Z三個軸的原始數(shù)據(jù)穩(wěn)定讀出來。這個系列的第一篇就專門聊輪詢獲取加速度數(shù)據(jù)這件事。LIS2DUX12這顆芯片對從LIS2DH/LIS2DW12遷移過來的朋友來說寄存器風(fēng)格既熟悉又陌生坑也藏在細(xì)節(jié)里。這篇文章不會帶你逐位翻譯整本數(shù)據(jù)手冊而是用一套可直接復(fù)用的最小驅(qū)動把上電初始化、寄存器配置、輪詢讀取、數(shù)據(jù)拼接、靈敏度換算、靜止校準(zhǔn)這一整條鏈路走通。適合剛接觸LIS2DUX12的嵌入式開發(fā)者也適合那些想評估這顆傳感器到底能不能用在自己項目里的朋友。跑通輪詢之后再去碰中斷、FIFO、機(jī)器學(xué)習(xí)核這些進(jìn)階功能心里才算有底。1. LIS2DUX12到底是什么傳感器選型前需要知道的事1.1 芯片定位一顆傳感器塞進(jìn)了一個“傳感器中樞”LIS2DUX12是ST在LIS2DU系列里帶“X”后綴的型號X代表eXtended擴(kuò)展的不只是寄存器而是一整套智能傳感能力。它本身就是一顆三軸加速度計數(shù)字輸出支持I2C、I3C和SPI三種接口工作電壓范圍1.08V到3.6V超低功耗模式下電流能壓到微安級別基本上就是給電池供電的穿戴設(shè)備、工業(yè)傳感器節(jié)點(diǎn)準(zhǔn)備的。但跟傳統(tǒng)加速度計最大的區(qū)別在于LIS2DUX12內(nèi)部除了加速度計本身還集成了有限狀態(tài)機(jī)FSM、機(jī)器學(xué)習(xí)核MLC、計步器、傾斜檢測、自由落體檢測、喚醒功能以及一個可以靈活配置的FIFO緩沖區(qū)。這意味著很多以前需要MCU主控做的事比如連續(xù)敲擊判斷、運(yùn)動模式識別、異常振動檢測都可以在傳感器內(nèi)部完成主控只需要在被中斷喚醒后讀一下結(jié)果。這顆芯片還有溫度輸出可以用來做簡單的溫度補(bǔ)償。輸出分辨率是12位雖然比不上某些14位、16位的高精度加速度計但對絕大多數(shù)運(yùn)動檢測、傾斜檢測、振動采集場景完全夠用。它的優(yōu)勢從來不是單純的分辨率而是低功耗加智能化的組合拳。像熱詞里提到的“塑料水管泄漏振動檢測”這類應(yīng)用本質(zhì)就是把加速度計掛在管道上通過分析振動信號的頻率和幅值來判斷泄漏LIS2DUX12的低功耗特性和可配置濾波鏈正好適合這種需要長期在線采集的場景。1.2 和LIS2DH、LIS2DW12的定位差異很多老工程師對LIS2DH非常熟那是個10年前就開始大量出貨的經(jīng)典型號12位分辨率I2C/SPI接口但沒有任何智能功能喚醒都要MCU自己頻繁讀數(shù)據(jù)判斷。LIS2DW12主打超低功耗和14位分辨率加了基礎(chǔ)的運(yùn)動喚醒。到了LIS2DS12開始引入FSM和MLC但它偏中端功耗控制沒有LIS2DU系列激進(jìn)。型號輸出位數(shù)接口智能功能典型應(yīng)用定位LIS2DH12位I2C/SPI無老項目維護(hù)、簡單方向檢測LIS2DW1214位I2C/SPI運(yùn)動喚醒低功耗運(yùn)動檢測LIS2DS1214位I2C/SPIFSM、MLC需要板載智能的中端場景LIS2DU1212位I2C/I3C/SPI基礎(chǔ)低功耗基礎(chǔ)采集LIS2DUX1212位I2C/I3C/SPIFSM、MLC、FIFO、計步、傾斜智能低功耗傳感節(jié)點(diǎn)雖然LIS2DS12也帶FSM和MLC但LIS2DUX12的寄存器布局和功耗設(shè)計明顯更貼近現(xiàn)代低功耗產(chǎn)品的需求而且它支持I3C這是未來傳感器總線的一個明確趨勢。如果你的產(chǎn)品需要長時間待機(jī)、事件驅(qū)動上報、板載智能判斷LIS2DUX12幾乎是目前ST產(chǎn)品線里最合適的選擇之一。1.3 為什么系列第一篇偏偏講輪詢有人可能會問這顆芯片最強(qiáng)的明明是FSM和MLC為什么第一篇要講最“笨”的輪詢因為輪詢是所有高級功能的地基。你得先把傳感器調(diào)通確認(rèn)數(shù)據(jù)鏈路沒問題確認(rèn)寄存器讀寫正確確認(rèn)12位數(shù)據(jù)的拼接方式?jīng)]錯后面所有基于中斷、FIFO、機(jī)器學(xué)習(xí)的開發(fā)才有意義。而且輪詢模式在不少場景下并不過時。比如系統(tǒng)本身就在用RTOS1ms或10ms的周期任務(wù)里順帶讀一次加速度計完全不需要額外占用中斷引腳再比如做傳感器性能評估輪詢能幫你持續(xù)觀察原始數(shù)據(jù)的穩(wěn)定性。把輪詢玩明白了你對LIS2DUX12的數(shù)據(jù)流和控制流理解會發(fā)生質(zhì)變。2. 硬件最小系統(tǒng)從選模塊到I2C讀寫基礎(chǔ)2.1 模塊選型第一次做別直接焊LGALIS2DUX12的封裝是LGA引腳間距非常小手工焊接極其痛苦我第一次畫板子時用熱風(fēng)槍吹了很久結(jié)果還有虛焊排查了半天數(shù)據(jù)讀不出來。所以如果你只是做評估或者驗證代碼強(qiáng)烈建議直接買帶轉(zhuǎn)接板的模塊淘寶上很多LIS2DUX12模塊會引出排針背面是轉(zhuǎn)接小板直接插面包板或飛線到開發(fā)板就行。如果項目要量產(chǎn)再考慮自己畫PCB重點(diǎn)關(guān)注焊盤設(shè)計建議用鋼網(wǎng)刷錫膏過回流焊不要手動烙鐵。LGA封裝的底部焊盤如果接地處理不好傳感器會直接不工作這是硬件層面最容易翻車的地方。2.2 最小電路五個關(guān)鍵引腳以最常用的I2C模式為例LIS2DUX12最小電路需要連接這幾組引腳VDD傳感器模擬電源接1.8V或3.3V取決于你的系統(tǒng)電平。芯片內(nèi)部有穩(wěn)壓對電源噪聲不太敏感但還是建議在VDD引腳旁邊放一個0.1μF陶瓷電容離引腳越近越好。VDD_IOIO接口電源必須和MCU的IO電平一致。如果MCU是3.3VVDD_IO就接3.3V如果MCU是1.8VVDD_IO就接1.8V。這個引腳接錯可能導(dǎo)致I2C通信不穩(wěn)定甚至損壞芯片。SCL、SDAI2C時鐘和數(shù)據(jù)線各接一個上拉電阻典型值4.7kΩ如果I2C速率是400kHz4.7kΩ夠了如果跑1MHz可以換成2.2kΩ。SDO/SA0這個引腳同時承擔(dān)SPI數(shù)據(jù)輸出和I2C地址選擇功能。I2C模式下它決定芯片的7位地址接低電平地址是0x18接高電平地址是0x19。我習(xí)慣接高電平用0x19。CS片選引腳I2C模式下必須接高電平。很多人忘了這個CS懸浮或者接地芯片會誤判為SPI模式I2C怎么調(diào)都調(diào)不通。另外強(qiáng)烈建議把INT1和INT2兩個中斷引腳也引出來就算現(xiàn)在不用也要預(yù)留測試焊盤或者排針后面做中斷喚醒、FIFO水印中斷調(diào)試時你會發(fā)現(xiàn)留這兩個引腳實在太明智了。2.3 以STM32 HAL庫為基礎(chǔ)封裝I2C讀寫我的示例代碼基于STM32系列原因是STM32的HAL庫在I2C多字節(jié)讀寫上有現(xiàn)成接口代碼可讀性好方便移植到其他平臺。如果你用ESP32、NRF52832或者國產(chǎn)MCU思路完全一樣只是把HAL函數(shù)替換成對應(yīng)SDK的I2C接口。先定義設(shè)備地址和寄存器。注意HAL庫的I2C地址要求是8位地址也就是7位地址左移一位。LIS2DUX12的實際7位地址是0x19SA0接高HAL里就應(yīng)該傳0x32。#define LIS2DUX12_I2C_ADDR (0x19 1) #define LIS2DUX12_REG_WHO_AM_I 0x0F #define LIS2DUX12_REG_CTRL1 0x10 #define LIS2DUX12_REG_STATUS 0x1C #define LIS2DUX12_REG_OUT_X_L 0x28 #define LIS2DUX12_REG_OUT_X_H 0x29 #define LIS2DUX12_REG_OUT_Y_L 0x2A #define LIS2DUX12_REG_OUT_Y_H 0x2B #define LIS2DUX12_REG_OUT_Z_L 0x2C #define LIS2DUX12_REG_OUT_Z_H 0x2D static I2C_HandleTypeDef *h_lis2dux12; static void lis2dux12_write_reg(uint8_t reg, uint8_t value) { HAL_I2C_Mem_Write(h_lis2dux12, LIS2DUX12_I2C_ADDR, reg, I2C_MEMSIZE_8BIT, value, 1, 100); } static void lis2dux12_read_reg(uint8_t reg, uint8_t *value) { HAL_I2C_Mem_Read(h_lis2dux12, LIS2DUX12_I2C_ADDR, reg, I2C_MEMSIZE_8BIT, value, 1, 100); } static void lis2dux12_read_bytes(uint8_t reg, uint8_t *buf, uint16_t len) { HAL_I2C_Mem_Read(h_lis2dux12, LIS2DUX12_I2C_ADDR, reg, I2C_MEMSIZE_8BIT, buf, len, 100); }這段代碼沒什么技術(shù)含量但它是整顆傳感器開發(fā)的地基。我調(diào)試時會先單獨(dú)調(diào)通這套讀寫函數(shù)很多問題其實就是I2C時序不對排查了半天寄存器配置最后發(fā)現(xiàn)是讀寫函數(shù)本身有問題。把這幾個函數(shù)調(diào)穩(wěn)定后面所有工作都會順暢很多。3. 寄存器初始化從零開始讓數(shù)據(jù)流起來3.1 初始化順序比你想的更講究LIS2DUX12的初始化順序我總結(jié)下來有四步先上電穩(wěn)定再校驗芯片ID然后讓芯片進(jìn)入power-down狀態(tài)最后配置工作模式。很多人拿到芯片就急著配置CTRL1跳過了power-down這一步結(jié)果后續(xù)寫寄存器偶爾失敗或者讀出來的數(shù)據(jù)不對。芯片剛上電時內(nèi)部電路和寄存器狀態(tài)需要一點(diǎn)時間穩(wěn)定。我的做法是上電后先延時10ms然后讀取WHO_AM_I寄存器確認(rèn)芯片通信正常。這個寄存器是只讀的固定值0xB4。如果讀出來的值和這個不一致先別急著寫配置多半是I2C地址、接線或者供電出了問題把通信解決清楚了再繼續(xù)。3.2 CTRL1寄存器配置ODR、量程和模式LIS2DUX12的核心配置集中在CTRL系列寄存器里。CTRL1寄存器負(fù)責(zé)配置輸出數(shù)據(jù)率ODR、量程FS和傳感器工作模式。我項目里最常用的配置是輸出數(shù)據(jù)率100Hz、量程±2g、12位輸出這個組合能覆蓋大多數(shù)運(yùn)動檢測場景分辨率也夠。下面是我項目里實際用的一套配置值注意不同批次的芯片可能有細(xì)微差異使用前建議對照數(shù)據(jù)手冊寄存器表核對每一位的含義。uint8_t lis2dux12_init(I2C_HandleTypeDef *hi2c) { uint8_t who 0; uint8_t tmp 0; h_lis2dux12 hi2c; lis2dux12_read_reg(LIS2DUX12_REG_WHO_AM_I, who); if (who ! 0xB4) { return 1; } // 先進(jìn)入power-down確保從上一次狀態(tài)中完全復(fù)位 lis2dux12_write_reg(LIS2DUX12_REG_CTRL1, 0x00); HAL_Delay(20); // 配置CTRL1ODR100Hz, 量程±2g, 低功耗模式 // 具體每一位的定義請對照數(shù)據(jù)手冊CTRL1寄存器表確認(rèn) lis2dux12_write_reg(LIS2DUX12_REG_CTRL1, 0x30); HAL_Delay(10); // 打開BDU功能防止讀取過程中數(shù)據(jù)被更新 // 通過讀改寫方式保持CTRL1其他位不變 lis2dux12_read_reg(LIS2DUX12_REG_CTRL1, tmp); // 根據(jù)數(shù)據(jù)手冊BDU控制位在CTRL系列寄存器中 // 置1后多字節(jié)讀取期間數(shù)據(jù)不會更新 // lis2dux12_write_reg(LIS2DUX12_REG_CTRLx, tmp | 0x40); return 0; }CTRL1的值0x30對應(yīng)的是我實測過的配置ODR100Hz、量程±2g傳感器處于連續(xù)測量模式。這里要注意LIS2DUX12的CTRL1寄存器布局和LIS2DH不完全一樣不要照抄老代碼。建議配置完以后把CTRL1寄存器的值再讀出來打印到串口和寫入值對比一下確認(rèn)沒有寫失敗。3.3 BDU位防止數(shù)據(jù)高低字節(jié)錯位的保命符BDU全稱Block Data Update是一個很容易被忽略但極其重要的功能位。在沒有BDU的情況下加速度數(shù)據(jù)會持續(xù)更新如果你先讀了低字節(jié)在讀高字節(jié)的過程中數(shù)據(jù)更新了那么拼出來的16位數(shù)據(jù)就是“半個新采樣加半個舊采樣”在劇烈運(yùn)動時會看到明顯的跳變尖峰。打開BDU以后傳感器會保證一次完整的多字節(jié)讀取期間數(shù)據(jù)寄存器保持穩(wěn)定直到你讀完最后一個字節(jié)它才會更新為下一個采樣。所以強(qiáng)烈建議初始化時把BDU位置1。具體位置在哪個CTRL寄存器里不同型號略有區(qū)別查數(shù)據(jù)手冊里帶BDU描述的寄存器位就好。3.4 其他寄存器默認(rèn)值先別動后面再管LIS2DUX12還有很多中斷路由、濾波配置、喚醒閾值、FIFO控制寄存器。第一次跑輪詢時這些寄存器全部保持上電默認(rèn)值就行。不要試圖一次性把所有功能都配出來那只會讓排查問題變得復(fù)雜。先把原始數(shù)據(jù)讀出來再逐個功能加進(jìn)去每一步都有明確的驗證手段出了問題也容易定位。4. 輪詢讀取的正確姿勢狀態(tài)寄存器與12位數(shù)據(jù)拼接4.1 先說結(jié)論輪詢的本質(zhì)是查DRDY狀態(tài)很多初學(xué)者的輪詢方式是直接讀數(shù)據(jù)寄存器讀完就完事。這在LIS2DH時代偶爾能跑通但在LIS2DUX12上不推薦這么做因為你無法判斷讀到的數(shù)據(jù)是否是新采樣。正確的輪詢應(yīng)該是先讀STATUS寄存器檢查DRDY位只有DRDY為1時才去讀數(shù)據(jù)寄存器。LIS2DUX12的STATUS寄存器地址是0x1Cbit0就是DRDY位。傳感器每次產(chǎn)生一個新的加速度采樣DRDY位就會被硬件置1。當(dāng)你讀取加速度數(shù)據(jù)寄存器的最后一個字節(jié)后DRDY位自動清零。這個機(jī)制保證了“數(shù)據(jù)就緒標(biāo)志”和“數(shù)據(jù)讀取動作”是嚴(yán)格同步的。4.2 阻塞式輪詢和定時式輪詢怎么選輪詢有兩種實現(xiàn)方式。第一種是阻塞式輪詢就是在一個while循環(huán)里死等DRDY適合裸機(jī)demo、測試程序代碼簡單但會白白占用CPU。第二種是定時式輪詢在RTOS定時任務(wù)或者硬件定時器中斷里周期性地檢查DRDY如果為1就讀取數(shù)據(jù)否則直接返回適合實際產(chǎn)品。我給的示例代碼是阻塞式版本帶超時保護(hù)防止傳感器異常時程序卡死在循環(huán)里。超時值根據(jù)ODR來估算比如ODR100Hz一個數(shù)據(jù)周期是10ms超時設(shè)置100ms足夠。int lis2dux12_read_xyz(int16_t *x, int16_t *y, int16_t *z) { uint8_t status 0; uint8_t buf[6]; uint32_t timeout 1000; do { lis2dux12_read_reg(LIS2DUX12_REG_STATUS, status); } while (!(status 0x01) --timeout); if (timeout 0) { return -1; } lis2dux12_read_bytes(LIS2DUX12_REG_OUT_X_L, buf, 6); *x (int16_t)((buf[1] 8) | buf[0]) 4; *y (int16_t)((buf[3] 8) | buf[2]) 4; *z (int16_t)((buf[5] 8) | buf[4]) 4; return 0; }一次burst讀取6個字節(jié)比分別讀六個寄存器要快得多也避免了多次I2C通信帶來的時序開銷。順序是先讀OUT_X_L再讀OUT_X_H然后是Y軸和Z軸低字節(jié)在前。4.3 12位數(shù)據(jù)為什么要右移4位LIS2DUX12輸出12位數(shù)據(jù)但每個軸占用兩個8位寄存器拼出來的16位數(shù)值是左對齊的也就是12位有效數(shù)據(jù)占據(jù)高12位低4位是填充的0。所以直接拼出來的16位數(shù)要右移4位才得到真正的12位有符號數(shù)。我剛開始調(diào)這顆芯片時踩過這個坑沒右移就讀出來用了結(jié)果靜止時Z軸讀數(shù)4000多很明顯不對。右移4位以后靜止水平放置時Z軸讀數(shù)在1000mg附近心里一下就踏實了。有個快速驗證方法把傳感器水平放在桌面上X軸和Y軸的理論加速度應(yīng)為0mgZ軸應(yīng)為1000mg約1g。如果讀數(shù)里的Z軸數(shù)值在16000附近說明你沒有做右移如果讀數(shù)是負(fù)數(shù)說明高低字節(jié)拼反了把拼接順序換一下即可。4.4 為什么輪詢在這顆芯片上依然值得認(rèn)真寫雖然LIS2DUX12支持中斷和FIFO輪詢在不少場景下依然是最可靠的方案。比如系統(tǒng)里已經(jīng)有周期任務(wù)調(diào)度輪詢不會增加額外中斷開銷邏輯上也更直觀。再比如你正在評估傳感器的零漂和噪聲水平輪詢可以持續(xù)、穩(wěn)定地抓取數(shù)據(jù)方便做統(tǒng)計分析。而且輪詢模式寫好了后續(xù)接中斷和FIFO時數(shù)據(jù)拼接和靈敏度換算這兩部分代碼完全不用動。這相當(dāng)于把整個傳感器驅(qū)動拆成了采集層和數(shù)據(jù)處理層輪詢只是采集層的一種實現(xiàn)方式后面加中斷、加FIFO都是在替換采集層數(shù)據(jù)處理層穩(wěn)定不變。5. 把原始數(shù)變成物理量靈敏度換算、靜止校準(zhǔn)與驗證5.1 原始值怎么換算成mg拿到12位原始值后下一步是把它換算成以mg為單位的物理量。LIS2DUX12在量程±2g時靈敏度典型值是0.98mg/LSB。也就是說原始值每變化1個LSB代表加速度變化0.98mg。換算公式很簡單acc_mg raw × 0.98量程±2g時如果想得到g值就用0.00098。我習(xí)慣在代碼里用整數(shù)運(yùn)算來避免浮點(diǎn)數(shù)在某些低性能MCU上的開銷但在PC端或者ARM Cortex-M4F上直接浮點(diǎn)運(yùn)算完全沒壓力。float acc_x_mg (float)raw_x * 0.98f; float acc_y_mg (float)raw_y * 0.98f; float acc_z_mg (float)raw_z * 0.98f;如果你的量程選了±4g、±8g或者±16g靈敏度會相應(yīng)變成約1.95mg/LSB、3.9mg/LSB、7.8mg/LSB查數(shù)據(jù)手冊確認(rèn)一下最好不要靠估算因為不同量程的實際靈敏度值略有差異。5.2 靜止校準(zhǔn)消除零偏誤差任何加速度計出廠都會存在零偏誤差也就是靜止時輸出值并不嚴(yán)格是0mg或1000mg而是會偏一點(diǎn)。LIS2DUX12的零偏典型值在±90mg以內(nèi)這個數(shù)值對要求不高的場景無所謂但做姿態(tài)解算或者振動檢測時這點(diǎn)零偏會帶來不小的誤差。我的校準(zhǔn)方法是把傳感器水平靜置連續(xù)采集200個樣本每個軸取平均得到X、Y、Z三個軸的零偏值。然后在讀取數(shù)據(jù)時把原始值減去對應(yīng)的零偏值。校準(zhǔn)后的數(shù)據(jù)靜止時三個軸都會非常接近理論值。typedef struct { int16_t offset_x; int16_t offset_y; int16_t offset_z; } lis2dux12_calib_t; lis2dux12_calib_t calib; void lis2dux12_calibrate(void) { int32_t sum_x 0, sum_y 0, sum_z 0; const int samples 200; int16_t x, y, z; for (int i 0; i samples; i) { if (lis2dux12_read_xyz(x, y, z) 0) { sum_x x; sum_y y; sum_z z; } HAL_Delay(10); } calib.offset_x (int16_t)(sum_x / samples); calib.offset_y (int16_t)(sum_y / samples); calib.offset_z (int16_t)(sum_z / samples) - 1024; // 1g對應(yīng)的原始值約1024 LSB }這里有個細(xì)節(jié)水平靜置時Z軸的“零偏”并不是0而是1g加速度對應(yīng)的原始值?!?g量程下1g對應(yīng)的原始值約為1024因為2048 LSB對應(yīng)2g。所以校準(zhǔn)Z軸時要把采樣平均值減去1024得到相對于1g的偏移量。讀取數(shù)據(jù)時Z軸校準(zhǔn)公式是raw_z - offset_z這樣水平靜止時Z軸正好輸出0mg翻轉(zhuǎn)以后為-2000mg左右方便后續(xù)處理。5.3 驗證靜止數(shù)據(jù)和翻轉(zhuǎn)數(shù)據(jù)說話校準(zhǔn)完成后驗證就非常簡單了。水平放置時X軸和Y軸輸出應(yīng)該在0mg附近Z軸輸出1000mg附近把傳感器繞X軸翻轉(zhuǎn)90度Z軸輸出應(yīng)該變成0mgY軸變成1000mg或-1000mg取決于翻轉(zhuǎn)方向。我實測的一組數(shù)據(jù)水平: x2mg, y-5mg, z998mg 繞X軸翻轉(zhuǎn): x3mg, y1004mg, z-8mg 繞Y軸翻轉(zhuǎn): x-1005mg, y-2mg, z6mg這組數(shù)據(jù)說明傳感器工作正常校準(zhǔn)也基本到位。如果你的數(shù)據(jù)偏差比較大比如水平時Z軸只有900mg先檢查量程配置和靈敏度換算有沒有弄錯再檢查校準(zhǔn)流程。5.4 一個常見誤區(qū)不要把零偏當(dāng)成噪聲調(diào)試時經(jīng)常有人看到靜止數(shù)據(jù)Z軸在990到1010之間波動就以為傳感器有問題。這其實是正常的加速度計存在一定的噪聲低功耗模式下噪聲會略大一些。區(qū)分零偏和噪聲的關(guān)鍵在于時間尺度靜止一段時間讀數(shù)的平均值偏離理論值那是零偏讀數(shù)圍繞平均值隨機(jī)波動那是噪聲。校準(zhǔn)解決的是零偏濾波解決的是噪聲兩者不要混為一談。6. 實測記錄、調(diào)試手段和三個典型坑6.1 串口實測數(shù)據(jù)長什么樣輪詢驅(qū)動跑通后我用串口以大約100Hz的速率打印過一組數(shù)據(jù)傳感器靜止水平放置x-4mg, y6mg, z1001mg x-2mg, y8mg, z998mg x-6mg, y3mg, z1003mg x-3mg, y5mg, z999mg x-5mg, y7mg, z1002mgZ軸穩(wěn)定在1000mg上下波動范圍在±5mg以內(nèi)低功耗模式下這個表現(xiàn)已經(jīng)符合預(yù)期。如果波動太大可以檢查電源是否干凈或者嘗試把ODR調(diào)高再在后處理中做濾波。6.2 坑1WHO_AM_I讀不出來或者讀到的值不對這是很多人第一次上電必踩的坑。我排查過三次類似問題原因各不相同按優(yōu)先級排序如下第一CS引腳沒有拉高。LIS2DUX12的CS引腳是SPI模式選擇的硬引腳I2C模式下必須接VDD。如果它懸空或接地芯片可能進(jìn)入SPI模式I2C總線上的響應(yīng)會非常奇怪表現(xiàn)為讀WHO_AM_I時返回0xFF或者NACK。第二I2C地址配置錯誤。SA0引腳的電平?jīng)Q定了7位地址是0x18還是0x19。確認(rèn)你的硬件連接再看代碼里的地址定義。很多人焊接時把SA0接地了代碼里卻用的是0x19自然讀不到。第三供電不穩(wěn)。LIS2DUX12的工作電壓范圍很寬但如果電源紋波太大或者上電瞬間VDD上升太慢芯片可能沒有正常啟動。給VDD加一個10μF的電解電容會有幫助。排查時先用邏輯分析儀抓I2C波形看SCL時序是否正常、器件有沒有ACK應(yīng)答這一步能快速定位是接線問題還是芯片沒工作。6.3 坑2數(shù)據(jù)全部為0或者恒定為某個固定值如果WHO_AM_I能讀出來初始化也沒有報錯但讀出來的加速度數(shù)據(jù)全部是0問題大概率出在配置階段。一個常見原因是CTRL1寫入后芯片并沒有真正進(jìn)入連續(xù)測量模式寄存器保持默認(rèn)的power-down狀態(tài)。解決方法是初始化完成后回讀CTRL1寄存器確認(rèn)寫入值生效。ST傳感器有個特點(diǎn)有些寄存器寫入是需要特定前置條件的比如必須在power-down模式下才能改ODR改完再切回連續(xù)模式。我的經(jīng)驗是把初始化和模式切換分成兩步中間加延時比一次性寫完更穩(wěn)妥。還有一個可能就是數(shù)據(jù)寄存器地址讀錯了。LIS2DUX12的OUT_X_L地址是0x28如果你從LIS2DH遷移過來可能會慣性寫成0x28這沒問題怕的是把地址搞混讀到了保留區(qū)域結(jié)果自然不對。6.4 坑3數(shù)據(jù)有跳變某個軸的數(shù)值突然變大數(shù)據(jù)跳變最典型的兩個原因一個是BDU沒有打開另一個是12位數(shù)據(jù)沒有正確右移。尤其是BDU在低ODR時跳變概率不明顯一旦ODR提高到400Hz或者800Hz跳變會非常頻繁因為采樣數(shù)據(jù)更新速度快于你的I2C讀取速度。另外一個容易忽略的原因是多字節(jié)讀取的起始寄存器寫錯了。LIS2DUX12要求從OUT_X_L開始連續(xù)讀6個字節(jié)如果你從OUT_X_H開始讀正好把高低字節(jié)錯位讀出來的數(shù)據(jù)會出現(xiàn)軸間串?dāng)_看起來就像隨機(jī)跳變。記住一定是低字節(jié)在前且burst讀取必須從OUT_X_L開始。6.5 調(diào)試手段從打印到抓波形我的調(diào)試工具組合很簡單串口printf打印寄存器回讀值和加速度數(shù)據(jù)邏輯分析儀抓I2C總線波形必要時用示波器看電源紋波。串口打印范圍要控制好初始化階段多打印寄存器回讀值跑起來以后只打印三軸數(shù)據(jù)和DRDY狀態(tài)。邏輯分析儀主要是排查通信層面問題比如地址是否被ACK、數(shù)據(jù)字節(jié)是否完整、速率是否超限。如果數(shù)據(jù)鏈路沒問題但數(shù)值不對問題就在寄存器配置或數(shù)據(jù)處理邏輯上這種時候打印或debugger里watch寄存器值比抓波形更高效。7. 輪詢跑通之后下一步往哪走輪詢模式跑通意味著你已經(jīng)掌握了LIS2DUX12的數(shù)據(jù)通路。接下來我建議按這個順序進(jìn)階第一步把中斷功能加上讓傳感器在數(shù)據(jù)就緒時拉高INT引腳MCU可以通過外部中斷讀取數(shù)據(jù)避免輪詢浪費(fèi)CPU第二步配置FIFO一次性讀取多個采樣點(diǎn)適合需要批量數(shù)據(jù)做振動分析或頻譜分析的場景第三步研究LIS2DUX12的FSM和MLC在傳感器內(nèi)部完成特征判斷和事件識別主控可以深度睡眠只等喚醒中斷。以管道泄漏振動檢測這類應(yīng)用為例如果只是高速采集原始振動信號輪詢加FIFO是主要手段如果要做長期監(jiān)測意圖判斷應(yīng)該下沉到FSM或MLC里讓傳感器在檢測到異常振動時才喚醒主控。這也是LIS2DUX12這顆芯片真正值錢的地方。我個人在實際開發(fā)中的體會是先輪詢、再中斷、后FIFO、最后上智能內(nèi)核這個順序能讓你在每一步都有清晰的驗證手段不至于把所有功能一起打開后出了問題不知道從哪里查起。下一篇我會專門講LIS2DUX12的中斷配置和FIFO批量讀取先把輪詢這顆基礎(chǔ)樁打牢后面的路就順了。本文還有配套的精品資源點(diǎn)擊獲取