字壓力傳感器設計:從信號鏈到標定與顯示的工程實踐)
簡介這份Word文檔面向電子信息、自動化及測控專業(yè)的學生與工程技術人員圍繞基于80C51單片機的數(shù)字壓力傳感器設計展開可用于課程設計、畢業(yè)設計或工業(yè)自控入門參考。內(nèi)容涵蓋電阻應變片與壓電傳感器兩種敏感元件的方案對比、全橋測量電路、三運放大電路、ADC0809模數(shù)轉換、LED數(shù)碼顯示以及RS-485通信接口與協(xié)議并給出電源電路、數(shù)據(jù)處理與溫度補償?shù)仍O計論證。資源包共1個doc文件約158KB為完整可編輯的Word版設計文檔結構清晰、圖文并茂便于直接查閱或二次修改。目前已有43人學習下載適合需要快速獲取完整設計方案、理解傳感器信號采集與單片機處理流程的讀者參考借鑒。1. 從一顆 80C51 到數(shù)字壓力表為什么老架構反而更適合入門很多人第一次接觸壓力測量腦子里冒出來的方案是買一個帶 I2C 輸出的數(shù)字壓力傳感器接上開發(fā)板讀寄存器就完事。但如果你手上只有一顆 80C51或者你面對的是一個需要長期穩(wěn)定運行、成本壓到極致的工業(yè)現(xiàn)場思路就完全不一樣了?;?80C51 單片機的數(shù)字壓力傳感器設計核心不是堆算力而是用最少的資源把模擬信號采準、把數(shù)字量算對、把顯示和通信做穩(wěn)。它解決的是“低成本、可標定、能長期跑”的問題適合剛入門的電子工程師、做課程設計的學生以及需要給老舊設備加數(shù)字顯示的現(xiàn)場維護人員。這顆芯片沒有硬件乘法器沒有浮點單元連 ADC 都要外掛但正因為資源緊你才會被迫把每一個時鐘周期和每一字節(jié)內(nèi)存都算清楚。我見過太多人一上來就選帶 24 位 ADC 的 MCU結果連基準電壓的溫漂都沒搞定數(shù)據(jù)跳得比心跳還快。80C51 方案反而逼你先搞明白信號鏈。2. 信號鏈怎么搭從壓阻橋到 80C51 能讀的數(shù)字2.1 壓力傳感器選型與模擬前端數(shù)字壓力傳感器的“數(shù)字”二字指的是最終輸出形式而不是敏感元件本身。絕大多數(shù)低成本方案用的還是壓阻式惠斯通電橋輸出差分電壓通常在幾十毫伏量級。以常見的表壓傳感器為例滿量程輸出可能是 50mV 到 100mV零點偏置卻可能有 ±20mV。這個信號直接進 ADC 會被噪聲淹沒所以模擬前端必須做兩件事差分放大和電平搬移。我一般會選一款儀表放大器比如 AD620 的國產(chǎn)替代型號增益電阻根據(jù)傳感器靈敏度和 ADC 參考電壓來算。假設傳感器靈敏度是 2mV/V供電 5V滿量程輸出就是 10mV。ADC 參考電壓 2.5V想要滿量程對應 2.0V增益就是 200 倍。增益電阻公式是 Rg 49.4kΩ / (G-1)算下來約 248Ω。這個電阻要用精度 1% 的金屬膜電阻溫漂越小越好。電平搬移用一只運放搭減法電路把差分放大后的信號抬到 ADC 量程中點。這里有個血淚經(jīng)驗不要用單電源運放直接處理接近地的信號輸出會失真。要么用雙電源要么選軌到軌輸入輸出的型號并且給負電源端加一個負壓產(chǎn)生電路。如果嫌麻煩可以直接選一款帶差分輸入和可編程增益的 ADC比如 HX711 這類專用于橋式傳感器的芯片它內(nèi)部就把放大和 ADC 做完了輸出直接是數(shù)字量80C51 只需要兩個 IO 口模擬時序讀取。但 HX711 的更新率低適合靜態(tài)壓力測量動態(tài)壓力就別想了。2.2 80C51 最小系統(tǒng)與 ADC 接口80C51 本身沒有 SPI 和 I2C 硬件模塊所有時序都要用 IO 口模擬。如果你選的是并行輸出的 ADC比如 ADC0809那就更方便直接掛在 P0 口和 P2 口上用外部中斷或者查詢方式讀。ADC0809 是 8 位、8 通道、逐次逼近型轉換時間約 100μs對于壓力測量綽綽有余。它的啟動信號、地址鎖存、輸出使能都需要用 IO 口控制。最小系統(tǒng)包括晶振電路、復位電路和電源。晶振選 11.0592MHz 還是 12MHz如果要做串口通信11.0592MHz 能產(chǎn)生更準的波特率誤差為 0。如果只做顯示和按鍵12MHz 計算定時器更方便。我一般用 11.0592MHz因為后期加串口上傳數(shù)據(jù)時不用改硬件。復位電路用 10μF 電容加 10kΩ 電阻上電復位時間約 100ms足夠 80C51 初始化。EA 引腳接高電平使用內(nèi)部程序存儲器。ADC 的參考電壓直接決定測量精度。不要用 7805 的輸出當基準它的溫漂和噪聲都很大。至少要用 TL431 這類可調(diào)基準源精度 0.5%溫漂 50ppm/℃。如果預算允許用 REF3025 這類專用基準初始精度 0.05%溫漂 15ppm/℃?;鶞孰妷旱姆€(wěn)定性比 ADC 位數(shù)更重要8 位 ADC 配 0.05% 基準有效精度能到 7 位以上16 位 ADC 配 5% 基準實際精度可能只有 8 位。2.3 采樣時序與數(shù)字濾波80C51 讀 ADC 的時序必須嚴格按數(shù)據(jù)手冊來。以 ADC0809 為例啟動轉換需要給 START 引腳一個正脈沖然后等待 EOC 變高再給 OE 高電平讀取數(shù)據(jù)。用查詢方式寫起來簡單但占用 CPU 時間。用中斷方式把 EOC 反相后接到 INT0 或 INT1轉換完成觸發(fā)中斷在中斷服務程序里讀數(shù)據(jù)。采樣頻率不用太高壓力變化通常很慢。我一般設 10Hz 到 100Hz 的采樣率用定時器 0 產(chǎn)生 10ms 中斷在中斷里啟動 ADC下次中斷讀結果。這樣 CPU 占用率很低還能留出時間做顯示和按鍵掃描。數(shù)字濾波是必須的。8 位 ADC 的原始數(shù)據(jù)跳動可能有 ±2LSB直接顯示會閃。最簡單的辦法是滑動平均開一個 8 個元素的環(huán)形緩沖區(qū)每次新數(shù)據(jù)覆蓋最舊數(shù)據(jù)然后求平均。求平均時不要用浮點80C51 做浮點除法很慢。用移位代替除法8 個數(shù)據(jù)求和后右移 3 位就是平均值。如果數(shù)據(jù)跳動還是大可以加中值濾波連續(xù)采 5 次去掉最大和最小剩下 3 個求平均。中值濾波對脈沖干擾特別有效比如電機啟停時產(chǎn)生的尖峰。// 滑動平均濾波8 點用移位代替除法 unsigned char filter_buf[8]; unsigned char filter_idx 0; unsigned int filter_sum 0; unsigned char filter_avg(unsigned char new_data) { filter_sum - filter_buf[filter_idx]; // 減去最舊數(shù)據(jù) filter_buf[filter_idx] new_data; // 存入新數(shù)據(jù) filter_sum new_data; // 累加新數(shù)據(jù) filter_idx (filter_idx 1) 0x07; // 環(huán)形索引0-7循環(huán) return (unsigned char)(filter_sum 3); // 除以8右移3位 }這段代碼的關鍵在于 filter_sum 用 unsigned int因為 8 個 8 位數(shù)據(jù)求和最大 2040不會溢出。filter_idx 用按位與代替取模因為 8 是 2 的冪這樣寫編譯出來的代碼更短。每次調(diào)用返回平均值主循環(huán)里直接拿去顯示或計算壓力值。注意初始化時要把緩沖區(qū)清零否則上電后前 8 次采樣會混入隨機值。3. 標定與壓力計算沒有浮點單元怎么算得準3.1 兩點標定法的實操步驟壓力傳感器的輸出和壓力值之間理論上是線性的但實際有零點偏置和靈敏度誤差。兩點標定是最實用的方法在零壓力和滿量程壓力下各采一組數(shù)據(jù)算出斜率和截距。具體操作是先不給壓力等讀數(shù)穩(wěn)定后記錄 ADC 值記為 adc_zero然后加滿量程標準壓力記錄 adc_full。壓力計算公式就是P (adc - adc_zero) * P_full / (adc_full - adc_zero)其中 P_full 是滿量程壓力值比如 100kPa。這個公式里有一次除法和一次乘法80C51 做 16 位除法大概需要幾十個機器周期對于 10Hz 的采樣率完全夠用。但要注意 adc_full - adc_zero 不能太小否則除法誤差大。如果傳感器靈敏度低放大后的滿量程 ADC 變化量至少要有 100 個碼值對應 8 位 ADC 就是 40% 量程。如果只有 20 個碼值標定出來的精度會很差這時候要么加大增益要么換 12 位 ADC。標定時要用標準壓力源不要用打氣筒加機械表湊合。機械表的精度通常只有 2.5% 滿量程標出來的數(shù)字表不會比它更準。如果實在沒有標準源可以用水柱法1 米水柱約 9.8kPa用卷尺量水位差精度能到 1%。但水柱法只適合低壓高壓還是得用活塞式壓力計。3.2 定點數(shù)運算與查表法80C51 沒有浮點單元用 float 類型會調(diào)用庫函數(shù)代碼體積大、執(zhí)行慢。我一般用定點數(shù)把壓力值放大 10 倍或 100 倍用 unsigned int 或 unsigned long 存儲。比如壓力范圍 0 到 100.0kPa就用 0 到 1000 表示單位是 0.1kPa。計算時先乘后除避免中間結果溢出。如果壓力范圍不大查表法更快。把 ADC 值 0 到 255 對應的壓力值預先算好存到 code 區(qū)運行時直接查表。表的大小是 256 字節(jié)對于 80C51 的 4KB 程序空間來說可以接受。查表法沒有計算誤差但標定后要重新生成表。生成表可以用 Excel 或者 Python 腳本把公式算好導出成 C 數(shù)組。// 定點數(shù)壓力計算壓力單位 0.1kPa滿量程 1000 unsigned int adc_zero 20; // 零點 ADC 值 unsigned int adc_full 230; // 滿量程 ADC 值 unsigned int p_full 1000; // 滿量程壓力 100.0kPa unsigned int calc_pressure(unsigned char adc) { unsigned long temp; if (adc adc_zero) return 0; if (adc adc_full) return p_full; temp (unsigned long)(adc - adc_zero) * p_full; temp temp / (adc_full - adc_zero); return (unsigned int)temp; }這段代碼里 temp 用 unsigned long因為 (adc - adc_zero) 最大 255乘以 1000 等于 255000超過 16 位范圍。除法用 32 位除以 16 位Keil C51 會調(diào)用庫函數(shù)執(zhí)行時間約 200 個機器周期在 11.0592MHz 下約 18μs對 10Hz 采樣率來說微不足道。注意 adc_zero 和 adc_full 要存在非易失存儲器里比如 AT24C02這樣標定一次就不用每次上電重來。3.3 溫度補償?shù)暮喕幚韷鹤枋綁毫鞲衅饔袦仄泓c溫漂和靈敏度溫漂都有。工業(yè)級傳感器內(nèi)部會做補償輸出已經(jīng)比較穩(wěn)定。但如果用的是裸片或者低成本傳感器溫漂可能達到 0.5%FS/℃。80C51 方案里加溫度補償最簡單的辦法是外掛一顆數(shù)字溫度傳感器比如 DS18B20測環(huán)境溫度然后在軟件里做一階線性補償。補償公式P_corrected P_raw K_zero * (T - T_ref) K_span * (T - T_ref) * P_rawK_zero 和 K_span 需要實驗測定。把傳感器放在溫箱里分別在 0℃、25℃、50℃ 下測零點和滿量程輸出算出系數(shù)。沒有溫箱的話可以用冰箱和熱水浴粗略測但精度沒法保證。如果溫度變化不大比如室內(nèi)環(huán)境 15℃ 到 35℃可以只做零點補償靈敏度補償忽略。DS18B20 用單總線80C51 模擬時序要小心讀寫時序的延時用 NOP 微調(diào)不同晶振頻率下要重新算。4. 顯示與通信把數(shù)字壓力值送出去4.1 數(shù)碼管驅動與動態(tài)掃描數(shù)字壓力表最直觀的顯示是 4 位數(shù)碼管顯示 0.0 到 100.0。共陽數(shù)碼管用 P0 口輸出段碼P2 口輸出位選。動態(tài)掃描頻率要大于 50Hz否則會閃。用定時器 1 產(chǎn)生 2ms 中斷每次中斷點亮一位4 位輪流刷新率 125Hz肉眼完全看不出閃爍。段碼表用 code 關鍵字存在程序存儲器共陽和共陰的段碼不一樣寫代碼前先用萬用表測一下。限流電阻每段一個220Ω 到 470Ω根據(jù)數(shù)碼管亮度調(diào)。位選三極管用 S8550 或 S8050注意共陽數(shù)碼管的位選是低電平有效三極管要接成射極跟隨器還是集電極開路取決于數(shù)碼管共陽端的接法。// 共陽數(shù)碼管段碼表0-9不帶小數(shù)點 unsigned char code seg_code[] { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90 }; // 顯示緩沖區(qū)4 位分別對應千、百、十、個 unsigned char disp_buf[4] {0, 0, 0, 0}; // 定時器1中斷服務程序2ms 一次 void timer1_isr() interrupt 3 { static unsigned char pos 0; P2 0xFF; // 先消隱關閉所有位 P0 seg_code[disp_buf[pos]]; // 輸出段碼 P2 ~(1 pos); // 打開當前位低電平有效 pos (pos 1) 0x03; // 0-3 循環(huán) }這段代碼里 P2 先寫 0xFF 消隱再寫段碼最后打開位選順序不能反否則會有鬼影。pos 用按位與循環(huán)和前面濾波代碼一個套路。disp_buf 里的數(shù)據(jù)在主循環(huán)里更新把壓力值拆成 4 位數(shù)字。拆位用除法和取余80C51 做 16 位除法稍慢但 10Hz 更新一次完全沒壓力。4.2 串口上傳與上位機協(xié)議如果需要把數(shù)據(jù)傳到電腦80C51 的串口是現(xiàn)成的。用定時器 1 做波特率發(fā)生器11.0592MHz 晶振下9600bps 的定時器初值是 0xFD。串口工作在模式 18 位數(shù)據(jù)可變波特率。發(fā)送用查詢方式就行壓力數(shù)據(jù)更新慢不用中斷。協(xié)議我一般用簡單的 ASCII 幀幀頭 0xAA然后 4 字節(jié)壓力值 ASCII 碼最后 0x55 結尾。上位機收到后解析。ASCII 的好處是調(diào)試方便用串口助手直接能看。二進制協(xié)議更緊湊但調(diào)試時得對著十六進制看容易出錯。// 串口發(fā)送一個字節(jié)查詢方式 void uart_send(unsigned char dat) { SBUF dat; while (!TI); // 等待發(fā)送完成 TI 0; // 清除發(fā)送標志 } // 發(fā)送壓力值格式0xAA 4字節(jié)ASCII 0x55 void send_pressure(unsigned int p) { uart_send(0xAA); uart_send(p / 1000 0); // 千位 uart_send(p / 100 % 10 0); // 百位 uart_send(p / 10 % 10 0); // 十位 uart_send(p % 10 0); // 個位 uart_send(0x55); }發(fā)送前要確保 p 不超過 9999否則會發(fā)亂。如果壓力值可能超過先做限幅。串口發(fā)送和數(shù)碼管掃描都在主循環(huán)里調(diào)用注意不要互相阻塞。數(shù)碼管掃描在定時器中斷里串口發(fā)送在主循環(huán)兩者不沖突。5. 避坑與排查80C51 壓力表最容易翻車的 5 個地方5.1 ADC 讀數(shù)跳動大顯示最后一位亂閃現(xiàn)象壓力穩(wěn)定時數(shù)碼管最后一位在 0 和 1 之間跳或者跳動超過 3 個碼值。原因基準電壓不穩(wěn)、模擬地和數(shù)字地混在一起、濾波不充分。80C51 的 IO 口翻轉時會產(chǎn)生電流尖峰通過地線耦合到模擬部分。解決基準源加 10μF 鉭電容和 0.1μF 陶瓷電容退耦位置盡量靠近基準芯片。模擬地和數(shù)字地單點連接在 ADC 下方用 0Ω 電阻或磁珠連接。軟件上把滑動平均從 8 點加到 16 點或者加中值濾波。如果還跳檢查傳感器信號線是否太長用屏蔽線并單端接地。5.2 標定后線性度差中間點誤差大現(xiàn)象零點和滿量程都準但 50% 壓力時誤差超過 2%。原因傳感器本身非線性或者放大器在中間電平處失真。也可能是 ADC 的積分非線性INL指標差。解決先查傳感器數(shù)據(jù)手冊的非線性指標如果傳感器本身非線性就 1%那系統(tǒng)不可能優(yōu)于 1%。如果是放大器問題檢查輸出是否在軌到軌范圍內(nèi)中間電平處是否進入非線性區(qū)。ADC 的 INL 通常 8 位的是 ±0.5LSB影響不大。如果要求高用三點標定把中間點也納入用分段線性插值。5.3 上電后數(shù)碼管顯示亂碼或全亮現(xiàn)象一上電所有段都亮或者顯示無意義的字符。原因80C51 上電復位時間不夠IO 口狀態(tài)不確定。或者 disp_buf 未初始化里面是隨機值。解決復位電路電容加大到 22μF確保復位時間大于 100ms。在 main 函數(shù)開頭先把 disp_buf 清零P0 和 P2 寫 0xFF 消隱。如果還亂檢查 EA 引腳是否接高程序是否從 0000H 開始執(zhí)行。有時候是晶振沒起振用示波器看 XTAL2 引腳有沒有波形。5.4 串口通信丟包或亂碼現(xiàn)象上位機收到的數(shù)據(jù)偶爾多一個字節(jié)或少一個字節(jié)或者全是亂碼。原因波特率誤差大、地線沒接、發(fā)送時被中斷打斷。解決11.0592MHz 晶振下 9600bps 誤差為 0如果用了 12MHz 晶振誤差約 8.5%會丟包。檢查串口線是否共地TX 和 RX 不要接反。發(fā)送函數(shù)里 while(!TI) 是死等如果發(fā)送時來了中斷TI 標志可能被意外清除。解決辦法是在發(fā)送前關中斷發(fā)送完再開中斷或者用中斷方式發(fā)送。5.5 長期運行后壓力值漂移現(xiàn)象跑幾個小時或幾天后零壓力時顯示不為零慢慢變大或變小。原因基準電壓溫漂、傳感器溫漂、放大器失調(diào)電壓漂移。也可能是電源紋波導致 ADC 參考不穩(wěn)。解決基準源換低溫漂型號傳感器如果溫漂大就加溫度補償。放大器選低失調(diào)漂移的比如 OP07 的失調(diào)漂移是 0.5μV/℃足夠用。電源加 LC 濾波7805 后面接 100μH 電感和 100μF 電容。軟件上可以加自動零點跟蹤如果壓力值連續(xù) 1 分鐘小于滿量程的 1%就認為當前是零點自動更新 adc_zero。但這個方法有風險如果實際壓力就是很小會被誤判為零點。我一般只在開機后前 10 秒做自動零點之后鎖定。6. 進階技巧用 80C51 的剩余資源做自診斷80C51 的 IO 口和定時器資源有限但做完壓力測量、顯示和串口后通常還能剩幾個 IO。我習慣留一個 IO 做傳感器斷線檢測一個 IO 做超量程報警。斷線檢測的原理很簡單在傳感器橋臂上并一個高阻值電阻正常時橋臂電阻低該點電壓被拉低斷線時橋臂開路電壓被高阻拉高。用 ADC 讀這個電壓超過閾值就報警。另一個技巧是用定時器 2 做看門狗。80C51 沒有內(nèi)置看門狗但可以用定時器 2 的溢出中斷模擬。在主循環(huán)里定時喂狗如果程序跑飛定時器 2 溢出后觸發(fā)復位。具體做法是定時器 2 設成 100ms 溢出溢出中斷里把 RST 引腳拉低 10μs。主循環(huán)里每 50ms 清一次定時器 2 的計數(shù)器。如果程序卡死定時器 2 溢出系統(tǒng)復位。// 定時器2看門狗初始化100ms溢出 void wdt_init() { RCAP2H 0x3C; // 11.0592MHz 下 100ms 初值 RCAP2L 0xB0; TH2 0x3C; TL2 0xB0; ET2 1; // 使能定時器2中斷 EA 1; TR2 1; // 啟動定時器2 } // 定時器2中斷溢出說明程序跑飛復位 void timer2_isr() interrupt 5 { TF2 0; // 清除溢出標志 // 拉低復位引腳需要硬件配合用一個IO口控制RST RST_PIN 0; // 等待復位 while(1); } // 主循環(huán)里喂狗 void wdt_feed() { TR2 0; // 停止定時器 TH2 0x3C; // 重裝初值 TL2 0xB0; TR2 1; // 重新啟動 }這個看門狗需要硬件上把 RST 引腳通過一個二極管或三極管接到某個 IO 口不能直接接因為 80C51 的 RST 是高電平復位而 IO 口拉低是復位不了的。正確做法是用一個 NPN 三極管基極接 IO 口集電極接 RST發(fā)射極接地。IO 口輸出高電平時三極管導通RST 被拉低但 80C51 的 RST 需要高電平復位所以這個電路反了。應該用 PNP 三極管發(fā)射極接 VCC基極接 IO 口集電極接 RSTIO 口輸出低電平時三極管導通RST 得到高電平。這個細節(jié)容易翻車畫板子前先用萬用表測一下。最后說一個我自己的習慣每次改完代碼先不接傳感器用信號發(fā)生器給 ADC 輸入一個已知電壓看顯示值對不對。確認信號鏈沒問題了再接傳感器標定。這樣能把硬件問題和軟件問題分開省得來回猜。希望幫到你。本文還有配套的精品資源點擊獲取