)
最近在做一個設備狀態(tài)監(jiān)測的振動采集前端。傳感器選型時沒怎么糾結ST的IIS3DWB后綴IIS3DWB10IS基本上是繞不開的一顆料。它是一顆三軸數(shù)字振動傳感器10kHz平坦帶寬專門為工業(yè)狀態(tài)監(jiān)測和預測性維護設計。搭配STM32C5系列這顆Cortex-M33內(nèi)核的MCU用SPI接口讀取振動原始數(shù)據(jù)再在MCU上做簡單FFT或者把數(shù)據(jù)喂給上位機是很典型的邊緣采集方案。這篇東西就把我實際調(diào)通這套組合的過程完整記錄下來從為什么選SPI、CubeMX里怎么配到寄存器讀寫代碼和常見坑一步一步來。適合手里有STM32C5開發(fā)板或者正準備用IIS3DWB做振動采集的工程師參考。1. 項目背景與選型邏輯1.1 IIS3DWB這顆傳感器強在哪普通消費級加速度計比如LIS2DW12或者MPU6050帶寬通常只有幾百赫茲做傾斜檢測、計步?jīng)]問題但拿來做工業(yè)振動采集就不夠用了。旋轉機械的軸承故障特征頻率、齒輪嚙合頻率很容易到幾千赫茲早期故障信號往往藏在高頻成分里傳感器的帶寬不夠這些信號就全被濾掉了后面算法做得再漂亮也是白搭。IIS3DWB的10kHz平坦帶寬意思是10kHz以內(nèi)幅頻響應幾乎沒有衰減這在MEMS加速度計里相當少見所以它在軸承故障診斷、齒輪箱監(jiān)測、風機預測性維護這類場景下特別合適。IIS3DWB量程有±2g、±4g、±8g、±16g四檔可調(diào)內(nèi)置FIFO支持最高26.7kHz的輸出數(shù)據(jù)速率SPI和I2C接口都有。很多人一看到FIFO、26.7kHz這些參數(shù)覺得這傳感器肯定很復雜其實它的寄存器模型和普通加速度計差別不大。核心就是把CTRL1設置好然后按SPI協(xié)議讀數(shù)據(jù)本質(zhì)上跟讀一塊W25Q64 Flash沒有太大區(qū)別只是命令格式稍有點特殊。還有一點必須說清楚IIS3DWB雖然叫數(shù)字振動傳感器本質(zhì)還是三軸MEMS加速度計。所謂“震動計”在工業(yè)上更準確的叫法是“振動傳感器”它輸出的是三個軸向的瞬時加速度值單位是g不是位移也不是速度。如果需要位移或者速度得在MCU端對加速度做一次積分、兩次積分這是后話。但前提是先得把原始加速度數(shù)據(jù)穩(wěn)定讀出來本文做的就是這一步。1.2 為什么選STM32C5系列STM32C5是ST面向主流市場推出的新一代MCUCortex-M33內(nèi)核帶FPU和DSP指令主頻最高250MHzFlash和RAM的容量在同類產(chǎn)品里給得比較足。對振動采集這個場景Cortex-M33的DSP指令尤其有用。加速度數(shù)據(jù)讀進來之后要做FFT、帶通濾波、RMS計算這些運算不用外掛DSPMCU自己就能跑主頻和算力都夠用。而且C5系列定位偏工業(yè)和電機控制CAN-FD、高級定時器、模擬外設、各種通信接口都很齊全后續(xù)想在這個平臺上疊加電機控制、設備綜合監(jiān)測都不用換芯片。開發(fā)工具鏈方面STM32C5在CubeMX和CubeIDE里直接支持HAL庫代碼生成非常方便。如果你以前用過STM32F4或者G4切到C5基本沒有學習成本寄存器模型、SPI外設、DMA、定時器這些周邊都保持了ST一貫的風格工程遷移過來改動很小。我這次用的是Nucleo開發(fā)板板載ST-Link調(diào)試器、虛擬串口、時鐘都集成好了接線也方便很適合快速驗證這類傳感器方案。1.3 通信方式SPI為什么是剛需IIS3DWB同時支持SPI和I2C但真正做振動采集SPI幾乎可以說是剛需。算一筆賬三軸加速度每軸16位讀一次至少6個字節(jié)如果ODR開到26.7kHz就算只讀原始數(shù)據(jù)鏈路上的有效數(shù)據(jù)流量也到每秒1.8Mbit以上這還沒算SPI命令字節(jié)和CS切換的時間。I2C標準模式最高400kHz額定帶寬直接差一個數(shù)量級根本帶不動。實際中就算只讀單軸數(shù)據(jù)I2C也會非常緊張更別提還要處理寄存器地址、ACK這些協(xié)議開銷。SPI的優(yōu)勢不只是速率高全雙工通信讓主機發(fā)命令字節(jié)的同時就能從機收數(shù)據(jù)協(xié)議開銷極小。I2C每個字節(jié)都要帶地址、ACK位時序要求也嚴格在高速采樣場景下很容易成為瓶頸。另外SPI的CS片選是顯式的物理電平軟件拉低拉高非常直觀排查問題也比I2C方便。所以這個項目里我沒有猶豫直接放棄I2C全部走SPI。后面所有驅動代碼也都是按SPI來寫I2C只作為備選項留在心里實際不啟用。2. 硬件準備與接線細節(jié)2.1 硬件清單與模塊選型這套東西需要的硬件非常少我列一下STM32C5系列Nucleo開發(fā)板一塊帶SPI1和板載調(diào)試器IIS3DWB模塊建議買帶轉接板的版本原廠封裝的引腳間距很小手工焊接容易連錫轉接板會省很多事杜邦線若干盡量短一點后面會解釋為什么如果不放心模塊上的供電準備一個3.3V LDO和幾顆陶瓷電容。模塊到手后先看絲印確認SPC、SDI、SDO、CS這幾個信號定義。不同賣家的絲印可能不一樣有的直接用SPI通用命名SCK/MOSI/MISO/CS有的用ST原廠命名SPC/SDI/SDO/CS。對應關系其實很清晰SPC就是SCKSDI就是MOSISDO就是MISOCS就是片選。務必確認模塊供電不能接5VIIS3DWB供電范圍是1.8V到3.6V接5V就燒了。2.2 SPI引腳連接與對照表我這里用的是STM32C5的SPI1默認引腳選PA5當SCK、PA7當MOSI、PA6當MISO片選CS用PA4純GPIO軟件控制。需要特別說明一句不同C5型號或者不同Nucleo板子的絲印可能不一樣最終引腳號以CubeMX里看到的外設映射為準不要照抄別人的表格。功能STM32C5引腳IIS3DWB模塊引腳VDD3.3VVDDGNDGNDGNDSCKPA5 (SPI1_SCK)SPCMOSIPA7 (SPI1_MOSI)SDIMISOPA6 (SPI1_MISO)SDOCSPA4 (GPIO輸出)CS接線時最容易翻車的點是SDO和SDI。SDO是傳感器的串行數(shù)據(jù)輸出必須接到MCU的MISOSDI是傳感器輸入必須接到MCU的MOSI。這兩個接反SPI回讀永遠得到0xFF或者亂碼而且排查起來第一反應往往是懷疑程序不會想到是這種低級硬件錯誤。我這次雖然沒踩但見太多人踩過了。2.3 電源與去耦等不容忽視的細節(jié)新手接線往往只關注信號線電源部分很隨意。振動傳感器的測量精度受電源噪聲影響很大IIS3DWB這種高分辨率傳感器對VDD紋波尤其敏感。模塊上如果自帶LDO和去耦電容還好如果只是裸芯片轉接板一定在模塊電源引腳附近加一顆0.1uF陶瓷電容有條件再并一顆10uF。我實測過供電紋波大的時候靜態(tài)數(shù)據(jù)上會疊一層明顯的噪聲毛刺加電容之后會干凈很多這是最立竿見影的改善。另外傳感器和MCU必須共地。用Nucleo開發(fā)板供電時沒什么問題如果模塊是外部電源單獨供電負極端子一定要和開發(fā)板的GND連在一起否則SPI電平?jīng)]有參考地通信不可能正常。還有一點IIS3DWB模塊盡量靠近MCU放杜邦線不要太長。SPI速率高的時候幾十厘米的杜邦線就是一根天線信號完整性會變差數(shù)據(jù)毛刺、誤碼都會冒出來這個問題后面還會再提到。3. CubeMX工程配置3.1 芯片選擇與時鐘配置打開CubeMX在MCU Selector里選STM32C5系列根據(jù)手頭板子選具體型號比如C5系列的Nucleo。雙擊進入工程配置后第一步先把調(diào)試接口打開。Nucleo板載ST-Link走SWD在System Core - SYS里把Debug設為Serial Wire否則代碼下載一次之后板子可能識別不到只能按住復位再下載很麻煩。時鐘配置建議直接在Clock Configuration頁面里讓CubeMX自動生成。C5主頻最高250MHzSPI1一般掛在APB2上時鐘樹里能看到APB2對應的頻點。配置原則很簡單讓SPI外設時鐘最終分頻出來的SCK不超過10MHz最好先落在2MHz到5MHz這個區(qū)間。我這次是250MHz系統(tǒng)時鐘APB2保持250MHzSPI分頻選64得到約3.9MHz的SCKIIS3DWB在這個時鐘頻率下工作非常穩(wěn)定。別一上來就拉滿到10MHz穩(wěn)定跑通之后再提速不遲。3.2 SPI外設參數(shù)設置在Connectivity - SPI1里做如下配置ModeTransmit Receive Master全雙工主機模式Hardware NSS SignalDisable片選用軟件GPIO控制Frame FormatMotorolaData Size8 BitsFirst BitMSB FirstPrescaler64SCK約3.9MHzCPOLLowCPHA1 Edge。CPOL和CPHA是很多新手搞不清楚的地方。SPI有四種種模式區(qū)別就是空閑時SCK是高還是低、數(shù)據(jù)在哪個邊沿采樣。IIS3DWB支持模式0CPOL0CPHA1Edge和模式3CPOL1CPHA2Edge兩種我這里選模式0也就是CPOLLow、CPHA1Edge這是ST傳感器最常用的配置。如果配置成了其他模式大概率讀到0xFF或者數(shù)據(jù)錯位最典型的現(xiàn)象就是WHO_AM_I讀不出規(guī)定的0xD3。另外數(shù)據(jù)位這里提醒一句IIS3DWB寄存器讀寫是按8位一字節(jié)的Data Size必須設成8 Bits。有人看到傳感器輸出16位數(shù)據(jù)就把SPI配置成16 Bits這是概念搞混了。傳感器的16位加速度值是放在兩個連續(xù)8位寄存器里的處理數(shù)據(jù)階段再去拼接SPI字節(jié)寬度始終是8位。3.3 片選GPIO與中斷引腳配置CS片選我在CubeMX里配成普通GPIO輸出。在System Core - GPIO里選PA4配置為GPIO_Output初始電平設為High。為什么不用硬件NSS因為硬件NSS在多設備和復雜時序下不夠靈活軟件片選可以精確控制拉低拉高的時機CS波形干凈并且你完全清楚每次SPI事務從哪開始、到哪結束排查問題非常方便。硬件片選省一根線不假但帶來的調(diào)試成本遠大于那點收益。如果后面要做數(shù)據(jù)就緒中斷可以把傳感器的INT1引腳接到另一個GPIO在CubeMX里配置為外部中斷。但我的建議是第一版不要開中斷先老老實實用輪詢方式讀STATUS寄存器等整個鏈路跑通、數(shù)據(jù)穩(wěn)定了再考慮改成EXTI觸發(fā)讀取。這樣減少調(diào)試變量出了問題更容易定位。中斷和DMA都屬于優(yōu)化階段的事不要一開始就堆上去。3.4 生成工程的幾個小提醒CubeMX生成代碼之前Project Manager里確認工具鏈選的是你本機裝的IDEMDK-ARM或者STM32CubeIDE都行。生成之后代碼骨架會自動包含SPI1和GPIO的初始化函數(shù)主頻配置也一并生成好了不需要手動去改寄存器。我通常拿到工程先編譯一次再動代碼確認工具鏈沒有報錯。如果編譯報錯多半是CubeMX和IDE版本不匹配先升級CubeMX或者統(tǒng)一版本再重新生成別浪費時間在版本兼容上。生成后的工程里HAL_SPI_MspInit函數(shù)會完成SPI引腳的復用配置GPIO_Init會按照你設置的CS引腳類型生成初始化代碼。這些都不用改直接在main.c里加業(yè)務代碼就行。注意CubeMX默認生成的代碼塊之間標了USER CODE BEGIN和USER CODE END你寫的代碼最好放在這些標記里面這樣下次重新生成代碼不會丟。4. 驅動代碼從寄存器讀寫到振動數(shù)據(jù)解析4.1 SPI讀寫函數(shù)與命令字格式開始寫驅動前必須先理解IIS3DWB的SPI命令格式。它和普通SPI從設備不太一樣寄存器的讀寫不是在MOSI上先發(fā)一個裸的寄存器地址而是要拼一個命令字節(jié)。這個命令字節(jié)的最高位bit7是讀寫標志1表示讀0表示寫bit5到bit1是寄存器地址bit0固定為0另外bit6這個位置和地址自動遞增相關。很多新手直接發(fā)寄存器地址結果就是通信總是不對。正確做法是讀命令首字節(jié)等于0x80 | (寄存器地址 1)寫命令首字節(jié)等于寄存器地址 1。舉個例子讀WHO_AM_I寄存器它的地址是0x0F左移一位是0x1E加上0x80就是0x9E。這個左移一位的細節(jié)非常容易忽略我見過不少人在這里卡了半天。下面是基礎代碼用的是STM32 HAL庫/* SPI句柄和CS宏定義 */ extern SPI_HandleTypeDef hspi1; #define IIS3DWB_CS_LOW() HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET) #define IIS3DWB_CS_HIGH() HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET) /* 讀單字節(jié)寄存器 */ uint8_t IIS3DWB_ReadReg(uint8_t reg) { uint8_t cmd 0x80 | (reg 1); /* 讀命令bit71 */ uint8_t val 0; IIS3DWB_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 10); HAL_SPI_Receive(hspi1, val, 1, 10); IIS3DWB_CS_HIGH(); return val; } /* 寫單字節(jié)寄存器 */ void IIS3DWB_WriteReg(uint8_t reg, uint8_t value) { uint8_t buf[2]; buf[0] reg 1; /* 寫命令bit70 */ buf[1] value; IIS3DWB_CS_LOW(); HAL_SPI_Transmit(hspi1, buf, 2, 10); IIS3DWB_CS_HIGH(); } /* 連續(xù)讀多個寄存器 */ void IIS3DWB_ReadMultReg(uint8_t reg, uint8_t *buf, uint16_t len) { uint8_t cmd 0x80 | (reg 1); IIS3DWB_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 10); HAL_SPI_Receive(hspi1, buf, len, 10); IIS3DWB_CS_HIGH(); }代碼邏輯不復雜但有幾個細節(jié)必須注意。CS低電平要包住整個SPI事務不能拉低之后又沒有及時發(fā)送命令更不能在發(fā)送過程中讓CS抖動從機靠CS來同步狀態(tài)機CS中途變化會導致時序錯亂。另外這里HAL_SPI_Transmit和HAL_SPI_Receive分開調(diào)用在STM32的HAL庫中是可行的因為SPI底層是全雙工外設接收時會在MOSI上發(fā)送0x00從機不會因此誤判。如果你對這點不放心也可以用HAL_SPI_TransmitReceive一次完成命令和數(shù)據(jù)的交換效果一樣。4.2 讀WHO_AM_I驗證通信驅動寫完第一件事不是配置傳感器而是讀WHO_AM_I。WHO_AM_I地址是0x0FIIS3DWB的固定返回值是0xD3。這一步是整個通信鏈路的自檢相當于確認SPI接線、時鐘極性、CS控制全部正確。把這個寄存器讀通后面初始化配置才有意義。uint8_t id IIS3DWB_ReadReg(0x0F); if (id ! 0xD3) { printf(WHO_AM_I error: 0x%02X\r\n, id); } else { printf(IIS3DWB detected, id0x%02X\r\n, id); }如果這里能打印出0xD3說明通信基本沒問題。如果讀不到先把工程放一放回頭檢查硬件接線和SPI模式別急著往下寫初始化否則后面寫再多配置也無法驗證。有時候是上電時序的問題傳感器剛上電還沒準備好MCU就已經(jīng)去讀了所以代碼里在初始化前先加一個20ms左右的延時能避免很多莫名其妙的首次讀取失敗。4.3 初始化寄存器配置通信驗證通過后按下面順序初始化傳感器。首先是上電穩(wěn)定延時然后配置CTRL3使能地址自動遞增最后配置CTRL1設置輸出數(shù)據(jù)速率和量程。void IIS3DWB_Init(void) { HAL_Delay(20); /* 上電穩(wěn)定 */ /* CTRL3 (0x22): 使能寄存器地址自動遞增 IF_INC1 */ IIS3DWB_WriteReg(0x22, 0x80); /* CTRL1 (0x20): ODR26.7kHz(0x0C)量程±4g(0x04) */ IIS3DWB_WriteReg(0x20, 0xC4); }CTRL1寄存器的高4位決定輸出數(shù)據(jù)速率bit3和bit2決定量程。我這次配的是26.7kHz、±4g也就是0xC4。如果只是測靜態(tài)傾斜或者低頻振動ODR可以降下來功耗更低數(shù)據(jù)也更穩(wěn)如果是測電機或者齒輪箱的高頻振動就盡量保持最高ODR。量程選擇要和ODR一起考慮工業(yè)現(xiàn)場振動沖擊大±2g雖然靈敏度最高但一個瞬時沖擊就可能讓數(shù)據(jù)飽和波形直接削頂。0.122mg/LSB的分辨率對絕大多數(shù)振動監(jiān)測夠用了后續(xù)算法里再做歸一化處理就行沒必要為了高靈敏度去冒險用±2g。4.4 讀取三軸數(shù)據(jù)并換算成加速度初始化完成之后讀取振動數(shù)據(jù)就很簡單了。三軸加速度數(shù)據(jù)連續(xù)存放在0x28到0x2D這6個寄存器里順序是X低、X高、Y低、Y高、Z低、Z高。因為前面已經(jīng)使能了地址自動遞增所以發(fā)一個命令字節(jié)后可以連續(xù)讀回6個字節(jié)非常高效。uint8_t buf[6]; int16_t raw_x, raw_y, raw_z; IIS3DWB_ReadMultReg(0x28, buf, 6); raw_x (int16_t)((buf[1] 8) | buf[0]); raw_y (int16_t)((buf[3] 8) | buf[2]); raw_z (int16_t)((buf[5] 8) | buf[4]); /* 量程±4g時靈敏度0.122mg/LSB轉換為g */ float ax raw_x * 0.122f / 1000.0f; float ay raw_y * 0.122f / 1000.0f; float az raw_z * 0.122f / 1000.0f;數(shù)據(jù)是16位補碼所以組裝完之后必須強轉成int16_t否則負數(shù)會變成很大的正數(shù)后面換算全部出錯。靈敏度系數(shù)根據(jù)量程對應如下±2g用0.061±4g用0.122±8g用0.244±16g用0.488單位都是mg/LSB。這個系數(shù)不要去背用時查表就行但必須和數(shù)據(jù)手冊保持一致否則后續(xù)所有分析數(shù)據(jù)的單位都是錯的振動幅值、頻譜峰值全會偏差。4.5 主循環(huán)與數(shù)據(jù)輸出主循環(huán)采用輪詢STATUS寄存器的方式。STATUS寄存器地址是0x1Ebit0為1表示X/Y/Z三軸都有新數(shù)據(jù)可用。只有等到就緒位置1再去讀數(shù)據(jù)才能避免重復讀舊數(shù)據(jù)也能防止讀到寄存器正在更新時的半新半舊數(shù)據(jù)。while (1) { if (IIS3DWB_ReadReg(0x1E) 0x01) { IIS3DWB_ReadMultReg(0x28, buf, 6); /* 解析并輸出到串口 */ printf(%d,%d,%d\r\n, raw_x, raw_y, raw_z); } }這里的printf建議先通過串口重定向到板載虛擬串口PC端用串口助手或者Python腳本接收數(shù)據(jù)。有一點必須提醒ODR開到26.7kHz時每秒將近3萬個樣本115200波特率的串口根本扛不住。簡單調(diào)試建議把ODR降到1.6kHz或者更低或者只打印單軸數(shù)據(jù)。等業(yè)務邏輯成熟后再用DMA加緩沖的方式把整段波形搬運出來那是后話。5. 實測驗證與數(shù)據(jù)觀察5.1 靜態(tài)基準先看重力分量代碼燒進去串口開始打印數(shù)據(jù)第一個測試不搞花活就把開發(fā)板平放在桌面上觀察。理論上水平放置的時候X軸和Y軸應該接近0gZ軸應該接近1g因為Z軸承受了全部重力加速度。實際由于傳感器安裝不可能絕對水平X/Y通常有幾毫g的偏置這完全正常。如果靜態(tài)數(shù)據(jù)顯示X/Y/Z都非常接近0g那多半是量程或靈敏度系數(shù)沒寫對如果某個軸穩(wěn)定在1g附近說明單位換算沒問題。通過靜態(tài)數(shù)據(jù)還能看出整個系統(tǒng)的噪聲水平。正常情況下數(shù)據(jù)在小數(shù)點后第三位輕微跳動是正常的畢竟MEMS傳感器不可能做到紋絲不動。如果看到數(shù)據(jù)大幅度來回跳先別懷疑傳感器查供電和接線大概率是電源噪聲或者SPI速率太高導致的采樣錯位。我調(diào)試時遇到過一組數(shù)據(jù)毛刺特別大最后發(fā)現(xiàn)就是面包板供電線太細、壓降和紋波疊加在一起造成的。5.2 敲擊與振動測試靜態(tài)驗證沒問題之后用手指輕輕敲一下桌面觀察數(shù)據(jù)波形。敲擊瞬間加速度會在幾十毫秒內(nèi)產(chǎn)生一個明顯的沖擊尖峰然后衰減振蕩。如果在串口數(shù)據(jù)里能看到這個沖擊說明傳感器動態(tài)響應是通的。我建議這時候拿個小喇叭放一段固定頻率的正弦波音頻音量開到中等放在傳感器旁邊觀察該頻率下數(shù)據(jù)是否有明顯波動這個辦法用來驗證傳感器對不同頻率振動的響應非常直觀。需要注意敲擊測試不要用力太猛避免超過量程導致數(shù)據(jù)削頂。如果看到波形頂部被削成平頭說明數(shù)據(jù)飽和了要么減小敲擊力度要么把量程從±4g調(diào)到±8g。另外傳感器的固定方式也影響測量結果用雙面膠貼在桌面上和用手捏著采到的波形是完全不一樣的手捏著會引入人體自身的低頻抖動和肌肉微顫所以測試時盡量把傳感器固定好再采集。5.3 數(shù)據(jù)可視化與異常判斷串口打印原始數(shù)值肉眼觀察效率太低建議把數(shù)據(jù)用串口助手記錄成CSV然后丟進Python里畫個波形非常直觀。這一步不需要在MCU端做復雜信號處理先把原始數(shù)據(jù)可視化對理解整個采集鏈路非常有幫助。很多振動問題光看時域波形就能初步判斷。如果波形是一條直線加細噪聲說明當前環(huán)境沒有明顯振動系統(tǒng)工作正常如果波形出現(xiàn)周期性脈沖可能是設備某個旋轉部件有問題如果波形整體隨機且幅度很大可能是結構共振或者環(huán)境干擾。等確認數(shù)據(jù)鏈路可靠了再往MCU里加FFT、RMS、包絡分析這些算法這才是預測性維護真正要做的內(nèi)容。順便提一句做FFT之前先把直流分量去掉否則頻譜圖上0Hz那個尖峰會把其他頻率成分都壓得看不見。6. 常見問題與避坑指南6.1 讀WHO_AM_I總返回0xFF或0x00這個現(xiàn)象十有八九是接線或者SPI模式問題。返回0xFF通常意味著MISO上一直接高電平也就是傳感器根本沒有響應優(yōu)先排查MOSI和MISO是否接反CS是否真的被拉低傳感器供電是否正常。返回0x00則可能是傳感器沒完成上電初始化、供電沒穩(wěn)定、或者CS拉低的時間太短。還有一種很隱蔽的情況模塊上CS引腳和VDD之間本來就接了上拉電阻這時候如果你的GPIO配置成了開漏輸出可能根本拉不到低電平換成推挽輸出就能解決。如果硬件看起來都對就上邏輯分析儀抓一下SPI引腳波形看SCK在傳輸期間是否有時鐘輸出看CS是否在整段傳輸期間保持低電平看MOSI上的命令字節(jié)是不是0x9E。沒有邏輯分析儀就把SPI速率降到1MHz以內(nèi)再試排除信號完整性影響。這種問題多半是某一個簡單的配置不對不要反復改代碼先冷靜把時序抓出來看。6.2 數(shù)據(jù)全零或數(shù)值不更新WHO_AM_I能讀對說明通信通了但數(shù)據(jù)一動不動問題基本出在初始化配置上。最常見的原因是CTRL1沒寫成功或者ODR配置位不對傳感器根本沒有進入連續(xù)測量模式。寫寄存器之后最好回讀一下確認寫入值生效養(yǎng)成這個習慣能省很多排查時間。我寫過太多傳感器驅動幾乎每個都會遇到回讀不一致的情況原因可能是上電時序、SPI寫入太快、或者從機還在復位中。另外有一個隱藏坑CTRL3的IF_INC沒有使能的時候地址自動遞增是不生效的。如果你用連續(xù)讀方式一次讀6字節(jié)后面的Y/Z數(shù)據(jù)全是無效數(shù)據(jù)。每次讀到X軸正常、Y/Z卻固定不變很可能就是這個原因?;刈xCTRL3確認是0x80之后再繼續(xù)排查。6.3 振動數(shù)據(jù)毛刺多數(shù)據(jù)毛刺多首先懷疑電源。在傳感器VDD引腳旁邊加上0.1uF和10uF電容再測一次靜態(tài)噪聲通常會有明顯改善。其次是SPI速率如果我跑5MHz沒問題但你用的是更長的杜邦線或者更大的環(huán)境干擾可以降到1MHz試試確認一下是不是信號完整性問題。第三檢查每個SPI事務之間CS是否完全拉高如果CS沒有徹底釋放從機狀態(tài)會錯亂多讀幾次就會出現(xiàn)掉拍表現(xiàn)就是毛刺。還要注意強振動環(huán)境下模塊本身的機械共振也可能被采進來。IIS3DWB的10kHz帶寬意味著所有在這個頻帶內(nèi)的信號它都會忠實記錄數(shù)據(jù)里如果有明顯的高頻分量先確認是不是傳感器固定方式引起的偽振動比如雙面膠沒貼牢、模塊懸空、外殼共振等等。傳感器本身沒問題但安裝不對采出來的數(shù)據(jù)一樣是錯的。6.4 幾個提升穩(wěn)定性的調(diào)試習慣最后分享幾個我個人調(diào)試過程中總結的習慣。第一所有寄存器讀寫操作都封裝成函數(shù)后續(xù)排查時可以加打印不要到處寫裸的HAL_SPI調(diào)用。第二傳感器初始化寫成帶返回值的函數(shù)WHO_AM_I不對就返回錯誤碼不要讓主程序在通信異常的情況下繼續(xù)跑否則后面所有數(shù)據(jù)都是臟的出了問題很難定位。第三串口輸出調(diào)試信息時格式固定成“寄存器名值”方便在串口工具里過濾和搜索。還有一個經(jīng)驗不要一開始就追求高性能配置。第一次跑通時SPI降到1MHzODR降到1kHz左右先把整條數(shù)據(jù)鏈路跑通再逐步往上加性能。每一步只改動一個變量出問題就能立刻定位。這比一次把所有性能參數(shù)拉滿然后面對一堆變量猜原因要高效得多。這套STM32C5加IIS3DWB的組合底層通信一旦穩(wěn)定后續(xù)擴展DMA、FIFO、中斷采集都只是在這個骨架上做加法不會再被底層通信的問題反復折騰。