動(dòng)開(kāi)發(fā)實(shí)戰(zhàn):從SPI時(shí)序到量產(chǎn)校準(zhǔn))
簡(jiǎn)介這份AD7190驅(qū)動(dòng)程序壓縮包面向嵌入式硬件開(kāi)發(fā)者與傳感器接口設(shè)計(jì)人員針對(duì)高精度數(shù)據(jù)采集場(chǎng)景提供與24位Σ-Δ型ADC AD7190通信所需的底層驅(qū)動(dòng)支持。AD7190具備低功耗、多通道差分輸入、內(nèi)置可編程增益放大器與數(shù)字濾波器等特性常用于工業(yè)與醫(yī)療領(lǐng)域的溫度、壓力、電流監(jiān)測(cè)本驅(qū)動(dòng)幫助開(kāi)發(fā)者快速完成初始化配置、SPI數(shù)據(jù)讀取、中斷與錯(cuò)誤處理等關(guān)鍵環(huán)節(jié)。資源共21個(gè)文件約57KB以C源碼與頭文件為核心輔以編譯生成的obj、lst、map、hex等中間與燒錄文件以及Keil工程相關(guān)配置結(jié)構(gòu)緊湊便于直接集成到嵌入式工程。目前已有700余人學(xué)習(xí)下載適合需要縮短AD7190調(diào)試周期、參考SPI時(shí)序與寄存器配置思路的開(kāi)發(fā)者借鑒使用。1. AD7190 驅(qū)動(dòng)程序.zip從裸機(jī)到量產(chǎn)一個(gè) 24 位 ADC 的驅(qū)動(dòng)到底要寫什么如果你手頭正捏著一份 AD7190 驅(qū)動(dòng)程序.zip大概率不是想研究它的寄存器手冊(cè)有多厚而是想盡快讓這顆 24 位 Σ-Δ 型 ADC 在自己的板子上把數(shù)讀出來(lái)。這顆芯片在精密稱重、壓力變送、熱電偶測(cè)溫里出現(xiàn)頻率極高原因很直接內(nèi)置 PGA、內(nèi)置基準(zhǔn)緩沖、可選斬波、RMS 噪聲低到能分辨微伏級(jí)信號(hào)。但它的驅(qū)動(dòng)也是出了名的“玄學(xué)”——SPI 時(shí)序差半個(gè)周期讀出來(lái)全是 0xFFFFFF配置寄存器寫錯(cuò)一位轉(zhuǎn)換結(jié)果直接跳成噪聲。這份驅(qū)動(dòng)包真正要解決的是把“能讀到數(shù)”推進(jìn)到“讀得穩(wěn)、讀得準(zhǔn)、能過(guò) EMI 和溫漂測(cè)試”。適合誰(shuí)看適合已經(jīng)能點(diǎn)亮 STM32 或同類 MCU 的 SPI、但被 AD7190 的連續(xù)轉(zhuǎn)換模式和狀態(tài)機(jī)繞暈的嵌入式工程師。下面按我實(shí)際調(diào)通的順序把驅(qū)動(dòng)拆開(kāi)講。2. 先搞懂 AD7190 的通信骨架SPI 模式、時(shí)序和寄存器映射2.1 為什么 AD7190 的 SPI 不能照搬 Flash 的配置很多新手翻車的第一步是把 AD7190 當(dāng)成普通 SPI Flash 來(lái)配CPOL0、CPHA0、隨便找個(gè) 1 MHz 時(shí)鐘就開(kāi)讀。AD7190 的 SPI 接口對(duì)空閑電平和采樣邊沿有明確要求手冊(cè)里寫的是 SCLK 空閑高電平、數(shù)據(jù)在 SCLK 下降沿被主機(jī)采樣。也就是說(shuō)MCU 側(cè)通常要配成 CPOL1、CPHA1 或者 CPOL1、CPHA0具體取決于你用的是硬件 SPI 還是軟件模擬。我一般會(huì)先用軟件 SPI 把時(shí)序跑通確認(rèn)讀到的 ID 寄存器是 0x0X低四位固定再切硬件 SPI 提速。另一個(gè)容易被忽略的點(diǎn)是AD7190 的每次寄存器訪問(wèn)都是“通信寄存器打頭”。你不能直接讀數(shù)據(jù)寄存器必須先寫通信寄存器告訴它下一步是讀還是寫、操作哪個(gè)寄存器、是單次還是連續(xù)。這個(gè)機(jī)制決定了驅(qū)動(dòng)里必須有一個(gè)狀態(tài)機(jī)而不是簡(jiǎn)單的 read_reg/write_reg 兩個(gè)函數(shù)。2.2 通信寄存器的位定義與驅(qū)動(dòng)里的狀態(tài)機(jī)通信寄存器 8 位實(shí)際用到的就幾位WEN寫使能、R/W讀寫、RS2~RS0寄存器地址。寫 0x08 表示下一步寫配置寄存器寫 0x58 表示下一步連續(xù)讀數(shù)據(jù)寄存器。驅(qū)動(dòng)里我習(xí)慣用一個(gè)枚舉把常用操作固化下來(lái)/* AD7190 通信寄存器命令字按實(shí)際使用場(chǎng)景預(yù)定義 */ typedef enum { AD7190_CMD_WRITE_CONFIG 0x08, /* 寫配置寄存器 */ AD7190_CMD_READ_CONFIG 0x10, /* 讀配置寄存器 */ AD7190_CMD_WRITE_MODE 0x09, /* 寫模式寄存器 */ AD7190_CMD_READ_MODE 0x11, /* 讀模式寄存器 */ AD7190_CMD_READ_DATA 0x58, /* 連續(xù)讀數(shù)據(jù)寄存器 */ AD7190_CMD_READ_STATUS 0x40, /* 讀狀態(tài)寄存器 */ AD7190_CMD_RESET 0xFF /* 復(fù)位連續(xù)寫 40 個(gè) 1 */ } ad7190_cmd_t;這段枚舉的價(jià)值在于它把“通信寄存器 后續(xù)操作”綁成一個(gè)原子命令調(diào)用方不用每次去翻位表。參數(shù)上唯一要留意的是 RESET 命令它不是寫一次就生效而是要求 SCLK 連續(xù)打 40 個(gè) 1少一個(gè)都可能復(fù)位不干凈。我見(jiàn)過(guò)有人只發(fā) 8 個(gè) 1 就往下走結(jié)果配置寄存器寫不進(jìn)去查了一下午。2.3 復(fù)位、讀 ID、寫配置的最小驗(yàn)證流程拿到一份 AD7190 驅(qū)動(dòng)程序.zip我建議先別急著跑連續(xù)轉(zhuǎn)換按下面三步做最小驗(yàn)證第一步硬復(fù)位。拉低 RESET 引腳至少 100 ns或者用軟件發(fā) 40 個(gè) 1。第二步讀狀態(tài)寄存器確認(rèn) RDY 位和 ERR 位正常。第三步寫配置寄存器把增益、極性、斬波、基準(zhǔn)選擇一次性配好再回讀比對(duì)。/* 最小驗(yàn)證復(fù)位后回讀配置寄存器確認(rèn)寫入生效 */ uint8_t ad7190_min_probe(void) { uint8_t cfg[3] {0x00, 0x00, 0x10}; /* 示例增益 1雙極性內(nèi)部基準(zhǔn) */ ad7190_write_reg(AD7190_CMD_WRITE_CONFIG, cfg, 3); uint8_t rd[3] {0}; ad7190_read_reg(AD7190_CMD_READ_CONFIG, rd, 3); /* 逐字節(jié)比對(duì)任何一位不一致都說(shuō)明時(shí)序或命令字有問(wèn)題 */ if (rd[0] ! cfg[0] || rd[1] ! cfg[1] || rd[2] ! cfg[2]) { return 1; /* 配置回讀失敗 */ } return 0; }邏輯說(shuō)明寫配置和讀配置都走通信寄存器打頭寫的時(shí)候 WEN1讀的時(shí)候 WEN0。參數(shù)上配置寄存器 24 位里高 8 位決定斬波和時(shí)鐘源中間 8 位決定增益和極性低 8 位決定基準(zhǔn)和緩沖?;刈x比對(duì)是最便宜的后悔藥能在 10 秒內(nèi)告訴你 SPI 到底通沒(méi)通。3. 把連續(xù)轉(zhuǎn)換跑起來(lái)模式寄存器、RDY 引腳和采樣節(jié)拍3.1 連續(xù)轉(zhuǎn)換模式與單次轉(zhuǎn)換模式怎么選AD7190 的模式寄存器決定它怎么干活。單次轉(zhuǎn)換模式適合低功耗輪詢場(chǎng)景每次轉(zhuǎn)換完自動(dòng)進(jìn)空閑你讀一次數(shù)據(jù)它就停連續(xù)轉(zhuǎn)換模式適合高速采集轉(zhuǎn)換完立刻開(kāi)始下一次RDY 引腳會(huì)周期性拉低告訴你數(shù)據(jù)好了。我一般這樣選采樣率低于 10 SPS 用單次省電且邏輯簡(jiǎn)單高于 10 SPS 用連續(xù)避免每次重新啟動(dòng)的建立時(shí)間。模式寄存器里還有一個(gè)關(guān)鍵位時(shí)鐘源選擇。用內(nèi)部 4.92 MHz 時(shí)鐘最省事但如果你板子上有溫漂要求外部晶振更穩(wěn)。另外斬波使能位會(huì)影響有效分辨率和建立時(shí)間稱重場(chǎng)景通常開(kāi)斬波因?yàn)橐獕?1/f 噪聲。3.2 RDY 引腳的兩種接法和中斷配置RDY 是開(kāi)漏輸出低電平表示數(shù)據(jù)就緒。接法有兩種上拉到 MCU 的 GPIO 做輪詢或者直接接外部中斷引腳做邊沿觸發(fā)。輪詢簡(jiǎn)單但占 CPU中斷高效但要注意消抖——AD7190 在連續(xù)模式下 RDY 會(huì)周期性翻轉(zhuǎn)如果中斷里讀數(shù)據(jù)太慢下一次轉(zhuǎn)換完成時(shí)你還在處理上一次就會(huì)丟數(shù)。/* RDY 中斷回調(diào)讀 24 位數(shù)據(jù)并存入環(huán)形緩沖 */ void HAL_GPIO_EXTI_Callback(uint16_t pin) { if (pin ! AD7190_RDY_PIN) return; uint8_t buf[3] {0}; ad7190_read_reg(AD7190_CMD_READ_DATA, buf, 3); /* 拼成 24 位有符號(hào)數(shù)注意 AD7190 是二進(jìn)制補(bǔ)碼輸出 */ int32_t raw ((int32_t)buf[0] 16) | ((int32_t)buf[1] 8) | buf[2]; if (raw 0x800000) { raw | 0xFF000000; /* 符號(hào)擴(kuò)展 */ } ringbuf_push(adc_ring, raw); }邏輯說(shuō)明讀數(shù)據(jù)寄存器必須用連續(xù)讀命令 0x58讀 3 個(gè)字節(jié)。參數(shù)上符號(hào)擴(kuò)展不能省否則負(fù)電壓讀出來(lái)會(huì)變成接近滿量程的正數(shù)。環(huán)形緩沖的大小取決于你的采樣率和上層處理速度我一般留 64 個(gè)樣本夠上層做一次滑動(dòng)平均。3.3 采樣率、濾波器和建立時(shí)間的三角關(guān)系A(chǔ)D7190 的輸出數(shù)據(jù)率由模式寄存器的 FS 字決定公式大致是 f_out 4.92 MHz / (1024 × FS)。FS 越小采樣越慢但噪聲越低。這里有個(gè)血淚經(jīng)驗(yàn)改變 FS 之后必須等至少一個(gè)完整的建立周期再讀數(shù)據(jù)否則第一次讀到的可能是舊值或未穩(wěn)定的值。建立時(shí)間通常是 1/FS 的若干倍具體看斬波是否開(kāi)啟。參數(shù)典型值影響FS 字1~1023決定輸出速率越小越慢越穩(wěn)斬波使能開(kāi)/關(guān)開(kāi)則噪聲低但建立時(shí)間長(zhǎng)增益1~128增益越高有效分辨率越低基準(zhǔn)內(nèi)部/外部外部基準(zhǔn)溫漂更小這張表我一般貼在調(diào)試筆記第一頁(yè)改任何一個(gè)參數(shù)前先看另外三個(gè)會(huì)不會(huì)被連帶影響。4. 驅(qū)動(dòng)里的避坑與排查那些讓讀數(shù)跳成噪聲的細(xì)節(jié)4.1 現(xiàn)象讀出來(lái)全是 0xFFFFFF 或 0x000000原因SPI 時(shí)序相位不對(duì)或者通信寄存器命令字寫錯(cuò)。AD7190 在 SCLK 空閑高電平時(shí)采樣如果你配成 CPOL0數(shù)據(jù)線在錯(cuò)誤邊沿被采樣讀回來(lái)就是全 1 或全 0。解決用示波器看 SCLK 和 DIN 的相位關(guān)系確認(rèn)數(shù)據(jù)在下降沿穩(wěn)定。沒(méi)有示波器就先用軟件 SPI 把時(shí)鐘降到 100 kHz逐位翻轉(zhuǎn)驗(yàn)證。4.2 現(xiàn)象配置寄存器回讀正確但數(shù)據(jù)寄存器一直不更新原因模式寄存器沒(méi)進(jìn)連續(xù)轉(zhuǎn)換或者 RDY 引腳沒(méi)接對(duì)。AD7190 上電默認(rèn)是空閑模式你不寫模式寄存器它不會(huì)自己開(kāi)始轉(zhuǎn)換。解決寫模式寄存器時(shí)把 MD2~MD0 設(shè)成連續(xù)轉(zhuǎn)換對(duì)應(yīng)的值然后確認(rèn) RDY 引腳在 100 ms 內(nèi)有一次拉低。如果 RDY 一直高檢查是不是被外部上拉電阻拉太死或者引腳配置成了推挽輸出。4.3 現(xiàn)象讀數(shù)周期性跳動(dòng)幅度遠(yuǎn)超噪聲預(yù)期原因基準(zhǔn)電壓不穩(wěn)或者模擬輸入端有共模干擾。AD7190 的基準(zhǔn)輸入對(duì)電源紋波很敏感如果你用內(nèi)部基準(zhǔn)檢查 AVDD 的退耦電容是不是離引腳太遠(yuǎn)。解決在 REFIN 引腳旁邊放 0.1 μF 和 10 μF 并聯(lián)模擬輸入加 RC 濾波截止頻率設(shè)在采樣率的 1/10 以下。4.4 現(xiàn)象切換增益后第一組數(shù)據(jù)明顯偏大或偏小原因PGA 建立時(shí)間不夠。增益越高內(nèi)部 PGA 的建立越慢如果你切完增益立刻讀拿到的是未穩(wěn)定的值。解決切增益后丟棄前 3 個(gè)樣本或者等 RDY 連續(xù)拉低 3 次再開(kāi)始記錄。這個(gè)坑在多點(diǎn)稱重校準(zhǔn)里特別常見(jiàn)表現(xiàn)為校準(zhǔn)點(diǎn)之間線性度很差。4.5 現(xiàn)象低采樣率下讀數(shù)緩慢漂移原因斬波使能后內(nèi)部會(huì)周期性交換輸入極性來(lái)抵消失調(diào)但如果你的輸入濾波電容太大交換瞬間的電荷注入會(huì)導(dǎo)致漂移。解決減小輸入濾波電容或者在斬波模式下把采樣率再降一檔給內(nèi)部節(jié)點(diǎn)足夠的恢復(fù)時(shí)間。5. 從能讀到能用校準(zhǔn)、溫漂補(bǔ)償和量產(chǎn)驗(yàn)證的實(shí)操技巧驅(qū)動(dòng)跑通只是第一步真正讓 AD7190 在量產(chǎn)里站住腳靠的是校準(zhǔn)和驗(yàn)證。我一般把校準(zhǔn)分成三步零點(diǎn)校準(zhǔn)、滿量程校準(zhǔn)、溫漂抽檢。零點(diǎn)校準(zhǔn)最簡(jiǎn)單短接模擬輸入讀 100 個(gè)樣本取平均把這個(gè)值存進(jìn) MCU 的 Flash 作為 offset。滿量程校準(zhǔn)需要一個(gè)已知的基準(zhǔn)源比如高精度電阻分壓出來(lái)的電壓讀 100 個(gè)樣本取平均算出增益系數(shù)。這兩個(gè)系數(shù)在每次上電時(shí)加載能消掉大部分芯片間的個(gè)體差異。溫漂補(bǔ)償稍微麻煩一點(diǎn)。AD7190 內(nèi)部基準(zhǔn)的溫漂典型值是幾個(gè) ppm/°C如果你做的是 0.01% 精度的稱重這個(gè)漂移不能忽略。我的做法是在恒溫箱里跑三個(gè)溫度點(diǎn)25°C、40°C、60°C每個(gè)溫度點(diǎn)記錄零點(diǎn)讀數(shù)和滿量程讀數(shù)擬合出一條一階線性補(bǔ)償曲線。代碼里用一個(gè)簡(jiǎn)單的查表加插值就能實(shí)現(xiàn)/* 溫漂補(bǔ)償根據(jù)當(dāng)前溫度插值出零點(diǎn)和增益修正系數(shù) */ typedef struct { float temp; float offset; float gain; } cal_point_t; static const cal_point_t cal_table[3] { {25.0f, 0.0f, 1.0000f}, {40.0f, -12.5f, 0.9998f}, {60.0f, -30.2f, 0.9995f} }; float ad7190_compensate(float raw, float temp) { /* 簡(jiǎn)化版線性插值實(shí)際項(xiàng)目建議用二分查找定位區(qū)間 */ if (temp cal_table[0].temp) { return (raw - cal_table[0].offset) * cal_table[0].gain; } if (temp cal_table[2].temp) { return (raw - cal_table[2].offset) * cal_table[2].gain; } for (int i 0; i 2; i) { if (temp cal_table[i].temp temp cal_table[i 1].temp) { float ratio (temp - cal_table[i].temp) / (cal_table[i 1].temp - cal_table[i].temp); float off cal_table[i].offset ratio * (cal_table[i 1].offset - cal_table[i].offset); float gain cal_table[i].gain ratio * (cal_table[i 1].gain - cal_table[i].gain); return (raw - off) * gain; } } return raw; }這段代碼的關(guān)鍵參數(shù)是 cal_table 里的三個(gè)溫度點(diǎn)和對(duì)應(yīng)的 offset、gain。offset 單位是 ADC 碼值gain 是無(wú)量綱系數(shù)。實(shí)際產(chǎn)線里我會(huì)把每個(gè)模塊的校準(zhǔn)值單獨(dú)寫進(jìn)它的 Flash而不是用同一張表因?yàn)樾酒g的失調(diào)差異遠(yuǎn)大于溫漂。量產(chǎn)驗(yàn)證階段我習(xí)慣做兩件事一是連續(xù)跑 24 小時(shí)每 10 分鐘記錄一次零點(diǎn)和滿量程讀數(shù)看有沒(méi)有階躍或緩慢漂移二是做一次電源波動(dòng)測(cè)試把 AVDD 在 ±5% 范圍內(nèi)掃一遍確認(rèn)讀數(shù)變化在允許誤差內(nèi)。這兩步能篩掉大部分焊接不良和退耦電容選型錯(cuò)誤的問(wèn)題。最后說(shuō)一個(gè)我自己的習(xí)慣每次改完驅(qū)動(dòng)先不接真實(shí)傳感器用信號(hào)源給一個(gè) 1 mV 的直流信號(hào)看讀數(shù)的峰峰值。如果峰峰值超過(guò)數(shù)據(jù)手冊(cè)里對(duì)應(yīng)采樣率的 RMS 噪聲的 6 倍說(shuō)明驅(qū)動(dòng)里還有時(shí)序或配置問(wèn)題別急著往下做算法。這個(gè)習(xí)慣幫我省過(guò)至少三次“以為是算法問(wèn)題、其實(shí)是驅(qū)動(dòng)沒(méi)配對(duì)”的返工。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取