的出租車計價器:從脈沖計數(shù)到狀態(tài)機(jī)實現(xiàn))
簡介一套基于51單片機(jī)的出租車計價器完整開發(fā)資料包面向單片機(jī)課程設(shè)計、電子設(shè)計競賽及畢業(yè)設(shè)計人群。方案覆蓋硬件原理、軟件編程、仿真驗證與答辯匯報全流程包含原理圖、源程序、Proteus仿真、實物演示視頻、設(shè)計論文和答辯技巧等內(nèi)容可直接作為項目模板或課設(shè)藍(lán)本。資源共181個文件壓縮包約90.26MB主要文件類型包括sch/pcb/dsn原理圖、c/h/hex程序文件、doc/docx/pdf論文文檔、png/jpg截圖與txt說明等其中原理圖與仿真文件用于硬件電路設(shè)計和驗證源程序便于修改調(diào)試論文與答辯文檔可輔助撰寫報告。項目功能完善1602液晶實時顯示時間、日期、行駛距離、當(dāng)前金額與總價按鍵支持設(shè)置時間、日期、起步金額及歷史數(shù)據(jù)查看通過霍爾傳感器測量電機(jī)轉(zhuǎn)速并換算里程計費邏輯清晰貼近真實出租車計價場景。已有2819人學(xué)習(xí)下載適合具備51單片機(jī)基礎(chǔ)、需要系統(tǒng)完成課設(shè)或畢設(shè)的同學(xué)使用。1. 出租車計價器為什么難在“算得準(zhǔn)”乘客從上車到下車的幾公里里計價器干的事遠(yuǎn)不止“顯示一個錢數(shù)”它要實時接收車輪轉(zhuǎn)速信號把脈沖數(shù)換算成里程再按“起步價超程單價等待費”這套規(guī)則不斷刷新金額。很多人在 51 單片機(jī)課程設(shè)計里把這個題簡化成“按鍵加數(shù)碼管”結(jié)果實訓(xùn)現(xiàn)場才發(fā)現(xiàn)在真實場景下最難的不是算錢而是輪速忽快忽慢時還能計得穩(wěn)、掉電后不丟數(shù)據(jù)。這篇文章按一個工程落地順序來講先把計價器的功能拆成可實現(xiàn)的模塊再給出硬件接線和軟件狀態(tài)機(jī)的核心代碼最后補(bǔ)充 Proteus 仿真和實物調(diào)試?yán)镒钊菀撞鹊目?。新手可以跟著把模塊接起來跑通有幾年經(jīng)驗的工程師也能從脈沖濾波、EEPROM 掉電存儲和晶振誤差校準(zhǔn)這幾處看到可復(fù)用的細(xì)節(jié)。2. 基于 51 單片機(jī)的計價器方案從脈沖到里程換算2.1 計價器的功能拆解與核心器件選型一臺完整的出租車計價器前端是傳感器中間是單片機(jī)后面是顯示和存儲。51 單片機(jī)在這個場景里任務(wù)并不重但信號鏈路和計費邏輯必須分清優(yōu)先級脈沖采集來自霍爾傳感器或干簧管的輪速信號必須用外部中斷不能靠主循環(huán)掃描計時基準(zhǔn)計價器要算等待費、夜晚加價需要一個穩(wěn)定的秒基準(zhǔn)用定時器中斷產(chǎn)生系統(tǒng)節(jié)拍人機(jī)交互至少要有“空車/載客”“白天/夜間”“單價設(shè)置”這幾個鍵LCD 顯示里程和金額掉電保存營運中突然斷電當(dāng)前金額、累計里程不能丟失需要一片 EEPROM 或者單片機(jī)內(nèi)置的 Flash 模擬存儲。器件選型沒有唯一答案下面這組是我在多輪課程設(shè)計和小型工程里常用、Proteus 里也能直接找到模型的組合。模塊推薦器件選型理由備注主控STC89C52RC 或 AT89S5251 內(nèi)核學(xué)校實驗箱/開發(fā)板覆蓋面廣資料多12T 模式的指令周期是 1μs12MHz輪速傳感器霍爾開關(guān) A3144有源輸出直接出數(shù)字電平抗抖動比干簧管好車輪上貼小磁鋼或裝帶齒圓盤顯示LCD1602能同時顯示里程、單價、金額仿真和實物都穩(wěn)定存儲AT24C02掉電不丟數(shù)據(jù)I2C 時序簡單2KB 夠存金額、總里程、單價晶振11.0592MHz串口調(diào)試波特率能整除定時器計數(shù)也能用純做計價器 12MHz 也 OK要注意51 單片機(jī)的外部中斷是在每個機(jī)器周期采樣一次引腳電平的輪速太低不會漏但必須在硬件上做好整形和濾波否則一個抖動就能讓里程跳一大截。2.2 輪速脈沖換算成里程的公式與定點數(shù)處理計價器的里程計算本質(zhì)是一元函數(shù)行駛里程 累計脈沖數(shù) × 單位脈沖對應(yīng)的里程。單位脈沖里程由車輪周長和每圈脈沖數(shù)決定。假設(shè)車輪滾動一圈周長 $C$米每圈產(chǎn)生 $N$ 個脈沖每個脈沖對應(yīng)$$meter_per_pulse \frac{C}{N}$$例如周長 2.0 米、每圈 1 個脈沖那么每個脈沖就是 2.0 米。實際工程里更推薦每圈多裝幾個磁鋼這樣低速時也能在幾百毫秒內(nèi)感知到運動避免“車走了一秒沒跳數(shù)”的錯覺。軟件里不直接用浮點數(shù)原因很簡單51 的浮點庫又大又慢運算時間還會影響中斷響應(yīng)。常見做法是先把金額需要的精度放大到“厘”或“分”里程放大到“米”或“0.1 米”全程用 unsigned long 做整數(shù)運算顯示時再縮小回去。在實際計價器里我會這樣定義參數(shù)#define PULSES_PER_REV (1) // 每圈脈沖數(shù) #define WHEEL_CIRC_MM (2000) // 車輪周長單位 mm #define PER_PULSE_MM (WHEEL_CIRC_MM / PULSES_PER_REV) // 每脈沖 2000mm volatile unsigned long pulse_cnt; // 外部中斷累計的脈沖數(shù) unsigned long total_dist_mm; // 總行駛距離單位 mm代碼邏輯解釋外部中斷每來一次pulse_cnt主循環(huán)定時把pulse_cnt與計數(shù)上次采樣值的差值乘上PER_PULSE_MM累加到total_dist_mm。用“mm”而不是“m”做單位是為了在 16 位定時器溢出不頻繁的前提下盡量保留精度。顯示層再去掉小數(shù)點。起步價內(nèi)行駛里程可以直接從total_dist_mm判斷不需要單獨另存跳表用的也是這個變量但要小心“脈沖經(jīng)過 2 米才累計一次”帶來的誤差下文狀態(tài)機(jī)里會專門處理若車輪打滑、急剎車導(dǎo)致脈沖異常增多需要在算法層做“短時間內(nèi)位移超過上限則不累加”的保護(hù)。2.3 計費規(guī)則用狀態(tài)機(jī)而不是用 if 堆加價規(guī)則一多用 if 層層嵌套會把自己繞暈。計價器常見的規(guī)則有三條起步價內(nèi)比如 2 公里只收起步價超過起步里程后按每跳多少米加收一檔停車等待或低速行駛時按等待時長計費。這三條規(guī)則會同時生效而且互相之間要疊加我最常用的結(jié)構(gòu)是“狀態(tài)機(jī) 變量快照”。狀態(tài)機(jī)上只需要三個主狀態(tài)IDLE空車待客不計費顯示空車牌RUNNING載客行駛中隨時累計里程、等待、金額ARRIVED到達(dá)結(jié)清金額顯示應(yīng)收款等待司機(jī)清零。狀態(tài)遷移條件按下“載客”鍵從IDLE進(jìn)入RUNNING按下“到達(dá)”鍵從RUNNING進(jìn)入ARRIVED按下“清零”鍵回到IDLE。在這個基礎(chǔ)上白天/夜間切換只是修改單價變量不影響狀態(tài)遷移這樣代碼就不容易寫亂。3. 硬件電路搭建脈沖輸入、LCD1602、EEPROM 怎么接3.1 最小系統(tǒng)與電源設(shè)計要點51 單片機(jī)最小系統(tǒng)三件套電源、晶振、復(fù)位。計價器項目里還有個長期被忽略的點電源紋波會直接影響中斷引腳的電平判斷所以去耦電容比優(yōu)化代碼優(yōu)先級更高。電源入口車載電瓶 12V經(jīng) 7805 降到 5V7805 后面并聯(lián)一個 470μF 電解電容和一個 0.1μF 瓷片電容單片機(jī) VCC 引腳旁邊再加一個 0.1μF離引腳越近越好晶振用 11.0592MHz兩個 30pF 負(fù)載電容分別接到晶振兩端復(fù)位電路用 10μF 電容串聯(lián) 10kΩ 電阻到地高電平復(fù)位。有串口調(diào)戲需求時11.0592MHz 非常香因為 9600 波特率誤差為 0純做計價器也可以換成 12MHz定時器初值更好算但后續(xù)串口打印調(diào)試信息會麻煩一點。如果條件允許直接在硬件設(shè)計階段留一個串口下載和調(diào)試口后面校準(zhǔn)里程會省很多力氣。3.2 霍爾脈沖輸入與整形電路A3144 霍爾開關(guān)是集電極開路輸出必須加上拉電阻否則輸出高電平的時候引腳是懸空的單片機(jī)會讀到隨機(jī)電平。規(guī)范接法如下A3144 的 VCC 接 5VGND 接 GND輸出引腳 OUT 接一個 10kΩ 上拉電阻到 VCC再串聯(lián)一個 1kΩ 電阻接到單片機(jī)外部中斷 0 輸入引腳 P3.2P3.2 和 GND 之間并聯(lián)一個 0.1μF 電容構(gòu)成簡單的 RC 低通濾波吸收高頻毛刺。如果現(xiàn)場環(huán)境電磁干擾強(qiáng)烈可以在中間加一級 74HC14 施密特觸發(fā)器整形。施密特觸發(fā)器會把緩慢變化的邊沿修整成陡峭的數(shù)字電平同時還有一定滯回可以抑制輸出電壓在閾值附近反復(fù)抖動。實物測試時用手轉(zhuǎn)動車輪如果在 LCD 上的里程跳字一次跳好幾個數(shù)說明硬件濾波不夠不要急著改軟件。Proteus 仿真時則不用操心這些直接用信號發(fā)生器或者“DCLOCK”原件模擬霍爾輸出注意把脈沖周期調(diào)到 10ms 到 100ms 之間去接近真實輪速。仿真環(huán)境下最容易犯的錯誤是忘記給 A3144 輸出端加上拉電阻邏輯分析儀看波形發(fā)現(xiàn)一直是低電平。3.3 LCD1602 的 4 線接法與 Proteus 仿真的差異LCD1602 是這次項目里顯示的不二選擇數(shù)據(jù)總線可以做成 8 位也可以做成 4 位。我通常用 4 位模式省下來的引腳可以給矩陣鍵盤或者 EEPROM 用。原理是每次傳一個字節(jié)時先傳高 4 位再傳低 4位速度足夠顯示更新并不頻繁。LCD1602 引腳接到單片機(jī)說明RSP2.6寄存器選擇0 命令1 數(shù)據(jù)RWP2.5接地只寫模式簡化讀寫時序ENP2.7使能信號高脈沖觸發(fā)D4P0.4數(shù)據(jù)位高 4 位D5P0.5由于 P0 口內(nèi)部無上拉必須外接 10kΩ 排阻D6P0.6同上D7P0.7同上V0電位器中間抽頭調(diào)節(jié)對比度不接屏?xí)馨悼床坏絻?nèi)容在 Proteus 里要特別注意仿真庫的 LCD1602 引腳標(biāo)號可能與實物不同部分版本會直接使用DB0~DB7實際是用 8 位模式。用 4 位模式前先在器件屬性里確認(rèn)是否有D4~D7引腳或者干脆在仿真里統(tǒng)一用 8 位模式實物里再切 4 位這樣能少一個折騰點。另外Proteus 仿真里無論怎么調(diào)對比度V0 都不能直接接 GND否則很多版本的屏幕根本不亮正確的做法是放一個 1kΩ 電位器中間抽頭接 V0兩邊分別接 5V 和 GND。3.4 AT24C02 掉電存儲與 I2C 接口連接計價器要存的數(shù)據(jù)不多總金額、總里程、當(dāng)前單價、上次運營狀態(tài)。AT24C02 容量 2Kbit即 256 字節(jié)存這些綽綽有余。連線固定為SCL 接 P2.1SDA 接 P2.0兩個引腳各通過 4.7kΩ 上拉電阻到 VCCA0、A1、A2 全部接地器件地址固定為 0xA0寫/ 0xA1讀。寫 AT24C02 有兩點容易因為看起來很合理而犯錯第一I2C 時序必須嚴(yán)格按照起始、停止、應(yīng)答信號來寫軟件模擬時每一小段之間加上微秒級延時第二EEPROM 寫一個字節(jié)后需要最多 5ms 的內(nèi)部寫入時間期間不能發(fā)下一個字節(jié)否則數(shù)據(jù)會丟失通常做法是在每頁寫入后直接延時 10ms。實物調(diào)試時可以用邏輯分析儀抓 SCL/SDA 波形看有沒有應(yīng)答位沒有邏輯分析儀就串行打印回讀校驗一次寫 A 地址、一次讀回來對比這是幾十行代碼能解決的可靠性問題。4. 軟件實現(xiàn)定時器中斷、脈沖計數(shù)與計價狀態(tài)機(jī)4.1 用定時器 1 產(chǎn)生 50ms 系統(tǒng)節(jié)拍計價器需要一個穩(wěn)定的時間基準(zhǔn)用來計算等待費、定時刷新顯示以及對脈沖做滑動檢測。51 單片機(jī)定時器 1 工作方式 1 是 16 位定時器在 11.0592MHz、12T 模式下機(jī)器周期為 1.085μs定時 50ms 需要計數(shù)值 46080初值即65536-4608019456十六進(jìn)制為0x4C00。volatile unsigned int sys_tick_ms; // 累計的毫秒數(shù)用于等待計費 void Timer1_Init(void) { TMOD 0x0F; // 清空定時器1的相關(guān)位 TMOD | 0x10; // 定時器1工作方式116位計數(shù) TH1 0x4C; // 高8位初值11.0592MHz下50ms中斷 TL1 0x00; // 低8位初值 ET1 1; // 開定時器1中斷 TR1 1; // 啟動定時器1 EA 1; // 開總中斷 } void Timer1_ISR(void) interrupt 3 { TH1 0x4C; // 重裝初值消除累計誤差 TL1 0x00; sys_tick_ms 50; // 每進(jìn)一次中斷增加50ms }初值重裝是在進(jìn)中斷后立即進(jìn)行的這個順序很重要如果先處理業(yè)務(wù)邏輯再裝初值會導(dǎo)致下一次定時短于 50ms長時間運行后秒基準(zhǔn)越來越快。在此基礎(chǔ)上計算等待費的邏輯立刻變得簡單在狀態(tài)機(jī)里用一個變量記錄“最近一次位移發(fā)生的時刻”當(dāng)sys_tick_ms與它的差連續(xù)超過 60 秒就按每分鐘等待費累加一次金額。這個判斷放在主循環(huán)里做不需要額外開一個定時器。4.2 外部中斷 0 的脈沖計數(shù)與防抖策略脈沖信號接在 P3.2/INT0 上設(shè)置為下降沿觸發(fā)。每次脈沖到來觸發(fā)一次interrupt 0服務(wù)函數(shù)累加計數(shù)。代碼如下volatile unsigned long pulse_total; // 總脈沖數(shù)用于里程累計 volatile unsigned long pulse_last_ms; // 上次脈沖發(fā)生時的系統(tǒng)時間 void Ext0_ISR(void) interrupt 0 { unsigned long now sys_tick_ms; if (now - pulse_last_ms 3) // 間隔小于3ms認(rèn)為是抖動或干擾 return; // 丟棄本次脈沖 pulse_total; pulse_last_ms now; }這里的防抖思路是時間窗口過濾輪速對應(yīng)的脈沖間隔遠(yuǎn)大于 3ms而接點抖動、電磁毛刺的時間通常遠(yuǎn)小于 3ms。用一個 50ms 定時器的累計值來當(dāng)作毫秒時間戳不需要另外加延時程序也不阻塞中斷響應(yīng)。要注意pulse_last_ms也放在中斷里更新這保證了計數(shù)點與時間基準(zhǔn)同步。如果把這個判斷放到主循環(huán)里查高速旋轉(zhuǎn)時主循環(huán)可能還沒來得及處理計數(shù)就已經(jīng)丟失了。真實輪速下脈沖頻率上限可以這樣估算車輪周長 2m最高車速 120km/h 即每秒 33.3m對應(yīng)每秒約 17 個脈沖即便多裝幾個磁鋼外部中斷也遠(yuǎn)遠(yuǎn)扛得住。51 單片機(jī)的瓶頸不在這里反而在低速時所以大力度優(yōu)化中斷函數(shù)意義不大把精力放在防抖和換算才更值。4.3 計價狀態(tài)機(jī)起步價、超程單價與等待時間狀態(tài)機(jī)的實現(xiàn)用一個enum加一個switch就足夠了enum { IDLE, RUNNING, ARRIVED } state; unsigned long total_money_cents; // 金額單位分 unsigned long total_dist_mm; // 本次行程累計里程單位mm unsigned int wait_seconds; // 本次行程累計等待秒數(shù) // 參數(shù) #define START_PRICE_CENTS 800 // 起步價 8.00 元 #define START_DIST_MM 2000 // 起步里程 2km #define PER_STEP_DIST_MM 500 // 每跳 500m #define PER_STEP_PRICE 100 // 每跳加 1.00 元 #define WAIT_PER_MIN 100 // 等待 1 分鐘加 1.00 元 void state_machine(void) { static unsigned long last_total; static unsigned long last_wait_point; switch (state) { case IDLE: total_money_cents 0; total_dist_mm 0; wait_seconds 0; last_total 0; last_wait_point 0; break; case RUNNING: // 里程累加由主循環(huán)周期計算這里只做跳表判斷 if (total_dist_mm - last_total PER_STEP_DIST_MM) { if (total_dist_mm START_DIST_MM) { total_money_cents PER_STEP_PRICE; } last_total total_dist_mm; } // 等待計費整個1分鐘窗口內(nèi)沒有新脈沖 if (pulse_total last_wait_point) { wait_seconds; if (wait_seconds 60) { wait_seconds - 60; total_money_cents WAIT_PER_MIN; } } else { wait_seconds 0; last_wait_point pulse_total; } break; case ARRIVED: // 顯示最終金額等待司機(jī)按下“清零”回到 IDLE break; } }兩個細(xì)節(jié)在真實場景里很值得保留。跳表判斷用的是total_dist_mm - last_total而不是直接比較total_dist_mm這防止了起步價邊界附近反復(fù)加跳等待計費則用一個“整分鐘沒有脈沖”的窗口來判斷比用低俗速度閾值更簡單也更貼近實際計價邏輯。如果現(xiàn)場要求“車速低于 12km/h 才算等待”只需要把“沒有脈沖”換成“每秒脈沖數(shù)低于 2”就能擴(kuò)展不會推翻現(xiàn)有狀態(tài)機(jī)架構(gòu)。另一個常見錯誤是自動跳表時沒有處理上電瞬間的last_total 0。解決辦法是把last_total設(shè)為靜態(tài)變量在IDLE狀態(tài)下隨時置零這樣每次載客從同一個起點開始不會把上一次的里程帶到這一次的跳表判斷里。4.4 在 LCD1602 上顯示里程、單價與金額LCD 顯示在每個計價周期結(jié)束時刷新一次即可不需要在中斷里操作。4 位模式初始化需要按固定順序發(fā)三次0x03、再發(fā)0x02進(jìn)入 4 位模式然后設(shè)置顯示方向和光標(biāo)。關(guān)鍵的初始化代碼是這樣void Lcd1620_Init(void) { Lcd_Write_Cmd(0x03); delay_us(5000); Lcd_Write_Cmd(0x03); delay_us(1000); Lcd_Write_Cmd(0x03); delay_us(1000); Lcd_Write_Cmd(0x02); delay_us(1000); // 切換為4位總線 Lcd_Write_Cmd(0x28); // 4線、兩行、5x7 Lcd_Write_Cmd(0x0C); // 開顯示、關(guān)光標(biāo) Lcd_Write_Cmd(0x06); // 寫入后地址加一 Lcd_Write_Cmd(0x01); // 清屏 }顯示內(nèi)容按兩行排布第一行顯示“里程 00.0km”第二行顯示“金額 8.00元”。由于數(shù)據(jù)是整數(shù)運算格式化時把整數(shù)除法和取模分別拿到整數(shù)部分和小數(shù)部分不要調(diào)用sprintf去用浮點轉(zhuǎn)換那樣代碼體積會暴漲。void Lcd_Show_Run_Info(void) { unsigned int km_int, km_dec; km_int total_dist_mm / 1000; km_dec (total_dist_mm % 1000) / 100; // 顯示一位小數(shù) Lcd_Set_Cursor(0, 0); Lcd_Print_Str(Dist ); Lcd_Print_Char(0 km_int/10); Lcd_Print_Char(0 km_int%10); Lcd_Print_Char(.); Lcd_Print_Char(0 km_dec); Lcd_Print_Str(km); Lcd_Set_Cursor(0, 1); Lcd_Print_Str(Fee ); Lcd_Print_Num(total_money_cents / 100); // 元 Lcd_Print_Char(.); Lcd_Print_Num((total_money_cents % 100) / 10); // 角 Lcd_Print_Num(total_money_cents % 10); // 分 Lcd_Print_Str(yuan); }這套顯示函數(shù)配合狀態(tài)機(jī)時要注意在ARRIVED狀態(tài)不要再實時刷新第二行的金額否則司機(jī)還沒看清單價就被下一次跳表覆蓋了。ARRIVED下的顯示應(yīng)該固定“金額 xx.x 元”不變只等下一次載客時清零。5. 校準(zhǔn)實測與仿真排錯讓里程表和時鐘都可信5.1 Proteus 仿真里最容易忽略的三個點用 Proteus 調(diào)試時觀察目標(biāo)不要只盯著 LCD 上有沒有數(shù)而是拆成數(shù)據(jù)源、處理、輸出三層分別驗證。第一信號源用DPULSE或外加一個方波發(fā)生器頻率從 1Hz 慢慢調(diào)高到 20Hz看外部中斷的計數(shù)是否跟隨第二定時器中斷是否準(zhǔn)確直接在中斷里翻轉(zhuǎn)一個引腳接示波器既可以看到周期也能確認(rèn)進(jìn)中斷的頻率第三LCD1602 的 V0 電位器電平是否在 1V 到 3V 之間仿真中對比度不對時屏幕的方塊格會非常明顯。跳表邏輯在仿真里可以用加速數(shù)據(jù)驗證把等待計時和里程跳表參數(shù)臨時縮小十倍避免在實際運行完時間和長度上都等待太久。驗證完成后把參數(shù)恢復(fù)原值這種臨時調(diào)參的方法比設(shè)計一大堆復(fù)雜的測試點要實用許多。5.2 里程系數(shù)現(xiàn)場校準(zhǔn)的辦法烙鐵焊完實物后不要以為公式算出來的每脈沖 2.000 米就能直接用。輪胎氣壓不同、磁鋼位置磨損、輪轂打滑都會影響實際行駛距離。常見做法是找一段已知 500 米的路面推著車走一個完全來回讀取實際累計脈沖數(shù)然后反推real_per_pulse_mm (500000) / (pulse_count/2); // 注意來回除2把這個值寫回 EEPROM作為修正系數(shù)。后續(xù)運行時不用改程序修改存儲區(qū)的參數(shù)即可。軟件里單位精度不高、放大倍數(shù)不足時修正以后誤差仍然存在。所以強(qiáng)烈建議里程內(nèi)部單位統(tǒng)一為 mm外部系數(shù)能精確到小數(shù)點后一位時總里程誤差在幾百米級別的長運營里就會被積累出來。校準(zhǔn)前先檢查晶振是否準(zhǔn)否則后期的采樣時間基準(zhǔn)就是錯的改輪徑系數(shù)也救不回來。5.3 時鐘漂移的影響與補(bǔ)償方向51 單片機(jī)內(nèi)部定時器的計時誤差來源主要是晶振負(fù)載電容偏差和環(huán)境溫度變化一天下來漂移幾秒到幾十秒都有可能。計價器的等待費按分鐘計這種漂移在長時間運營里會讓金額出現(xiàn)可感知的偏差。工程上常用的處理辦法是加 DS1302 或 PCF8563 外部 RTC但很多課程設(shè)計不允許額外芯片。折中方案是在外部中斷 1 上接一個 32768Hz 晶振和 CD4060 分頻讓秒基準(zhǔn)不依賴內(nèi)部定時器或者用 50Hz 市電過零信號做秒基準(zhǔn)。這兩種方案都會增加硬件復(fù)雜度算上 PCB 面積和成本我更推薦直接換帶內(nèi)置 RTC 的單片機(jī)比用軟件補(bǔ)償可靠得多。如果受限于現(xiàn)狀只能用內(nèi)部定時器那就把補(bǔ)償值做成一個可配置項例如每 24 小時偏差 N 毫秒由司機(jī)或維修人員在調(diào)試菜單里修改單片機(jī)每秒自動調(diào)整中斷重裝值。這個思路雖然不是真正的無源校準(zhǔn)但在工程現(xiàn)場足夠?qū)嵱靡卜奖阕鲵炇諟y試。5.4 一組可復(fù)用的測試用例驗收計價器時不要只看“數(shù)字會跳”要有明確判定標(biāo)準(zhǔn)。下面這套案例覆蓋主干路徑和邊界條件實測時可直接套用。序號測試場景輸入設(shè)置預(yù)期結(jié)果1空車狀態(tài)不跳表按下載客鍵前給 100 個脈沖金額保持 0.00 元2起步價內(nèi)不計超程載客后累計 1.5km顯示起步價 8.00 元3超程跳表載客后累計 2.5km金額變?yōu)?9.00 元4等待計費載客后 1 分鐘無脈沖金額增加 1.00 元5掉電保存載客 1km 后斷電重新上電金額和里程恢復(fù)為斷電前值6脈沖抖動連續(xù)輸入 1kHz 毛刺 1 秒里程增加不超過 1 個脈沖對應(yīng)值最后一行的抖動測試正是硬件 RC 濾波和軟件時間窗口共同作用的結(jié)果。把這一組用例全部跑通計價器的核心邏輯才算真正可信剩下要做的就是把單價、夜間加價這類參數(shù)改成實際應(yīng)用的版本代碼結(jié)構(gòu)和狀態(tài)機(jī)都不需要再動。本文還有配套的精品資源點擊獲取