多任務(wù)系統(tǒng)設(shè)計(jì):從ADC采集到時(shí)間片調(diào)度的綜合測(cè)量實(shí)戰(zhàn))
1. 項(xiàng)目背景與核心挑戰(zhàn)從賽題到實(shí)戰(zhàn)的跨越最近在整理過(guò)往的參賽資料翻到了第十一屆藍(lán)橋杯單片機(jī)國(guó)賽的這道“時(shí)間電壓光照強(qiáng)度測(cè)量”題目。這道題可以說(shuō)是當(dāng)年國(guó)賽的一個(gè)經(jīng)典縮影它沒(méi)有追求花哨的算法或復(fù)雜的通信協(xié)議而是扎扎實(shí)實(shí)地考察了單片機(jī)系統(tǒng)中最基礎(chǔ)、也最核心的幾項(xiàng)能力多任務(wù)調(diào)度、模擬信號(hào)采集、人機(jī)交互以及系統(tǒng)穩(wěn)定性設(shè)計(jì)。很多同學(xué)初次接觸這類(lèi)綜合題目時(shí)容易陷入“功能堆砌”的誤區(qū)把每個(gè)模塊單獨(dú)調(diào)通就以為萬(wàn)事大吉結(jié)果一上系統(tǒng)就各種沖突、數(shù)據(jù)跳變、顯示卡頓。今天我就以這道賽題為藍(lán)本結(jié)合我當(dāng)年備賽和后來(lái)帶學(xué)生參賽的經(jīng)驗(yàn)拆解一下如何構(gòu)建一個(gè)穩(wěn)定、精準(zhǔn)且實(shí)時(shí)性好的綜合測(cè)量系統(tǒng)。這不僅僅是完成一道題目更是理解一個(gè)嵌入式產(chǎn)品雛形的設(shè)計(jì)思路。這道題目的核心是要求我們?cè)O(shè)計(jì)并實(shí)現(xiàn)一個(gè)能夠同步測(cè)量并顯示時(shí)間、電壓和光照強(qiáng)度的單片機(jī)系統(tǒng)。聽(tīng)起來(lái)功能明確但魔鬼藏在細(xì)節(jié)里。你需要一個(gè)實(shí)時(shí)時(shí)鐘來(lái)管理時(shí)間至少兩個(gè)ADC通道來(lái)采集電壓和光照傳感器信號(hào)一個(gè)顯示設(shè)備通常是LCD1602或數(shù)碼管來(lái)呈現(xiàn)數(shù)據(jù)還需要按鍵進(jìn)行設(shè)置和切換。所有這些功能要在一個(gè)主循環(huán)里和諧共處不能因?yàn)椴杉妷憾寱r(shí)鐘走慢也不能因?yàn)樗⑿嘛@示而丟失按鍵響應(yīng)。這就是典型的裸機(jī)多任務(wù)協(xié)同問(wèn)題也是嵌入式開(kāi)發(fā)從“點(diǎn)亮LED”到“做個(gè)小產(chǎn)品”的關(guān)鍵一步。2. 系統(tǒng)架構(gòu)設(shè)計(jì)與核心器件選型面對(duì)這樣一個(gè)多輸入、單處理核心、多輸出的系統(tǒng)第一步不是急著寫(xiě)代碼而是進(jìn)行清晰的系統(tǒng)架構(gòu)設(shè)計(jì)。一個(gè)好的架構(gòu)能讓你在后續(xù)開(kāi)發(fā)和調(diào)試中事半功倍。2.1 核心控制器與開(kāi)發(fā)平臺(tái)藍(lán)橋杯單片機(jī)競(jìng)賽指定使用CT107D開(kāi)發(fā)板其核心是經(jīng)典的IAP15F2K61S2單片機(jī)可以看作是增強(qiáng)型的8051內(nèi)核。選型理由很直接賽題指定。但我們要理解它的能力邊界。這款單片機(jī)運(yùn)行頻率可達(dá)35MHz擁有8路10位ADC、2個(gè)定時(shí)器/計(jì)數(shù)器、硬件SPI和I2C接口資源對(duì)于本題目是綽綽有余的。它的優(yōu)勢(shì)在于我們對(duì)51架構(gòu)非常熟悉開(kāi)發(fā)工具Keil C51成熟底層寄存器操作直觀。劣勢(shì)在于它是單核的所有任務(wù)都需要我們手動(dòng)分時(shí)調(diào)度。開(kāi)發(fā)環(huán)境我依然推薦Keil uVision 5配合官方的STC-ISP下載工具。在項(xiàng)目配置時(shí)一個(gè)關(guān)鍵細(xì)節(jié)是正確設(shè)置內(nèi)存模型。由于我們需要多個(gè)全局變量存儲(chǔ)時(shí)間、電壓值、光照值、顯示緩沖區(qū)等建議使用“Large: variables in XDATA”模式即把變量分配到外部RAM雖然這款單片機(jī)片內(nèi)XRAM有2KB但邏輯上算外部這樣可以避免緊湊模式Small下默認(rèn)數(shù)據(jù)區(qū)DATA/IDATA128字節(jié)很快用盡的窘境。2.2 傳感器與信號(hào)調(diào)理電路設(shè)計(jì)題目中的“電壓”和“光照強(qiáng)度”測(cè)量本質(zhì)都是將模擬量轉(zhuǎn)換為數(shù)字量。1. 電壓測(cè)量通常開(kāi)發(fā)板上會(huì)預(yù)留一個(gè)“ADC輸入”接口連接到一個(gè)可調(diào)電位器如Rb2。測(cè)量板載電壓信號(hào)是最簡(jiǎn)單的。但賽題有時(shí)會(huì)要求測(cè)量外部輸入電壓這就需要我們?cè)O(shè)計(jì)前端電路。直接測(cè)量0-5V范圍如果確信輸入電壓在0-5V之間且內(nèi)阻很小可以直接接入單片機(jī)的ADC引腳如P1.0。但務(wù)必在引腳前串聯(lián)一個(gè)100-200歐姆的小電阻并接一個(gè)0.1uF的電容到地形成簡(jiǎn)單的RC濾波可以抑制高頻干擾。分壓測(cè)量高于5V若要測(cè)量更高電壓如12V必須使用電阻分壓網(wǎng)絡(luò)。例如用100k和20k電阻串聯(lián)將12V分壓至2V輸入ADC。計(jì)算關(guān)系為V_adc V_in * (R2/(R1R2))。這里的關(guān)鍵是阻抗匹配和精度。分壓電阻的阻值不宜過(guò)小耗電也不宜過(guò)大易受漏電流影響百kΩ級(jí)別比較合適。同時(shí)應(yīng)選用1%精度的金屬膜電阻否則分壓比不準(zhǔn)會(huì)引入系統(tǒng)誤差。電壓跟隨器高阻抗信號(hào)源如果被測(cè)電壓源內(nèi)阻很大如某些傳感器輸出直接接入會(huì)導(dǎo)致ADC采樣時(shí)因抽取電流而使被測(cè)電壓跌落。此時(shí)需要在ADC輸入端前加一個(gè)電壓跟隨器使用運(yùn)放如LM324構(gòu)成。它的輸入阻抗極高輸出阻抗極低可以完美地將高內(nèi)阻信號(hào)“緩沖”給ADC。這是很多初學(xué)者容易忽略但在實(shí)際電子測(cè)量中至關(guān)重要的一環(huán)。2. 光照強(qiáng)度測(cè)量最常用的器件是光敏電阻或光敏二極管/三極管。開(kāi)發(fā)板上通常集成的是光敏電阻如GL5528。它的阻值隨光照增強(qiáng)而減小。我們通常將它連接成一個(gè)分壓電路光敏電阻串聯(lián)一個(gè)固定電阻如10kΩ接在Vcc和GND之間從中間節(jié)點(diǎn)引出電壓信號(hào)到ADC。光照越強(qiáng)光敏電阻阻值越小其分得的電壓就越低ADC讀取的值就越小。這里固定電阻的選值很重要它決定了測(cè)量范圍的線(xiàn)性度和中心點(diǎn)。通常選擇與光敏電阻在常用光照下的阻值相近的阻值可以使電壓變化范圍更寬。更精確的方案是使用集成光照傳感器如BH1750數(shù)字I2C接口它直接輸出lux值精度和穩(wěn)定性遠(yuǎn)勝模擬方案但賽題一般規(guī)定使用板載模擬器件。2.3 人機(jī)交互模塊顯示與輸入顯示部分LCD1602是最佳選擇。它能同時(shí)顯示兩行16個(gè)字符非常適合同時(shí)展示時(shí)間、電壓、光照這三類(lèi)信息。驅(qū)動(dòng)它需要6-7根IO口4位或8位數(shù)據(jù)模式編寫(xiě)好底層寫(xiě)命令、寫(xiě)數(shù)據(jù)、初始化函數(shù)后上層只需關(guān)心往顯示緩沖區(qū)送什么字符串即可。一個(gè)提升用戶(hù)體驗(yàn)的技巧是固定顯示格式。例如Time: 12:30:45 U: 3.28V L: 650 Lux這樣信息布局清晰。注意電壓和光照值通常是浮點(diǎn)數(shù)需要先轉(zhuǎn)換為字符串。避免在中斷或高頻循環(huán)中使用sprintf它比較耗時(shí)??梢宰约簩?xiě)一個(gè)輕量級(jí)的float_to_str函數(shù)或者將數(shù)值放大成整數(shù)再處理。輸入部分開(kāi)發(fā)板上有獨(dú)立按鍵和矩陣鍵盤(pán)。對(duì)于設(shè)置時(shí)間這類(lèi)需要“加、減、確認(rèn)、切換”的操作4個(gè)獨(dú)立按鍵就足夠了。按鍵處理的核心是消抖和狀態(tài)機(jī)。我強(qiáng)烈推薦使用定時(shí)器中斷如2ms一次進(jìn)行按鍵掃描采用“狀態(tài)機(jī)消抖法”可以準(zhǔn)確識(shí)別按下、釋放、長(zhǎng)按等事件且不影響主程序運(yùn)行。3. 軟件核心裸機(jī)多任務(wù)調(diào)度與時(shí)間片管理這是本項(xiàng)目的靈魂所在。如何讓計(jì)時(shí)、采集、顯示、按鍵響應(yīng)這四個(gè)任務(wù)在一顆單核CPU上流暢運(yùn)行答案就是基于定時(shí)器中斷的時(shí)間片輪詢(xún)調(diào)度。3.1 定時(shí)器中斷作為系統(tǒng)心跳我們啟用一個(gè)定時(shí)器如Timer0設(shè)置為10ms產(chǎn)生一次中斷。這10ms就是我們的系統(tǒng)時(shí)間片。在中斷服務(wù)程序ISR里我們不做復(fù)雜操作只設(shè)置一系列的標(biāo)志位Flag就像給各個(gè)任務(wù)發(fā)“令牌”。bit flag_10ms 0; // 10ms到標(biāo)志 bit flag_100ms 0; // 100ms到標(biāo)志 bit flag_500ms 0; // 500ms到標(biāo)志 unsigned int ms_count 0; // 毫秒計(jì)數(shù)器 void Timer0_ISR() interrupt 1 { TH0 ...; TL0 ...; // 重裝初值保證10ms中斷 flag_10ms 1; ms_count; if(ms_count % 10 0) { flag_100ms 1; } // 每100ms if(ms_count % 50 0) { flag_500ms 1; } // 每500ms if(ms_count 1000) { ms_count 0; } // 秒計(jì)時(shí)在main中處理 }3.2 主循環(huán)中的任務(wù)分發(fā)主函數(shù)main()是一個(gè)無(wú)限循環(huán)其核心就是檢查這些標(biāo)志位并執(zhí)行相應(yīng)的任務(wù)。void main() { Sys_Init(); // 系統(tǒng)初始化定時(shí)器、ADC、LCD、按鍵等 while(1) { // 任務(wù)110ms級(jí)任務(wù) - 按鍵掃描狀態(tài)機(jī)消抖 if(flag_10ms) { flag_10ms 0; Key_Scan(); // 在函數(shù)內(nèi)部處理消抖和狀態(tài)更新 } // 任務(wù)2100ms級(jí)任務(wù) - ADC采集電壓和光照 if(flag_100ms) { flag_100ms 0; voltage Get_ADC_Value(ADC_CH0); // 假設(shè)P1.0是電壓 light Get_ADC_Value(ADC_CH1); // 假設(shè)P1.1是光照 // 這里可以進(jìn)行簡(jiǎn)單的濾波如一階滯后濾波 voltage_filtered 0.2 * voltage 0.8 * voltage_filtered; light_filtered 0.2 * light 0.8 * light_filtered; } // 任務(wù)3500ms級(jí)任務(wù) - 顯示刷新 if(flag_500ms) { flag_500ms 0; // 1. 更新時(shí)間每秒更新一次這里用500ms標(biāo)志不影響 if(sec 60) { sec0; if(min60)... } // 簡(jiǎn)易時(shí)鐘邏輯 // 2. 格式化字符串 sprintf(display_buf, T:%02d:%02d:%02d, hour, min, sec); LCD_Write_String(0,0, display_buf); sprintf(display_buf, U:%4.2fV L:%4d, voltage_filtered*scale, (int)light_filtered); LCD_Write_String(0,1, display_buf); } // 任務(wù)4隨時(shí)響應(yīng)的任務(wù) - 按鍵事件處理 Key_Process(); // 根據(jù)Key_Scan()設(shè)置的狀態(tài)執(zhí)行加、減、切換模式等操作 } }這種架構(gòu)的優(yōu)點(diǎn)非常明顯確定性每個(gè)任務(wù)的執(zhí)行周期是固定的不會(huì)因?yàn)槟硞€(gè)任務(wù)卡住而影響其他任務(wù)。比如ADC轉(zhuǎn)換需要幾十個(gè)微秒但它每100ms才執(zhí)行一次完全不影響10ms一次的按鍵掃描。模塊化每個(gè)任務(wù)都是獨(dú)立的函數(shù)耦合度低便于調(diào)試和維護(hù)。資源友好CPU大部分時(shí)間處于空循環(huán)功耗低。只有標(biāo)志位置起時(shí)才工作。3.3 時(shí)間管理的進(jìn)階RTC模擬與閏年處理題目要求的時(shí)間通常不需要連接外部RTC芯片我們用軟件模擬一個(gè)即可。在1秒標(biāo)志到達(dá)時(shí)可以用ms_count判斷更新秒、分、時(shí)、日、月、年。這里有一個(gè)大坑月份天數(shù)和閏年判斷。一個(gè)健壯的日期更新函數(shù)應(yīng)該如下// 月份天數(shù)表索引1-12 const u8 month_day[13] {0,31,28,31,30,31,30,31,31,30,31,30,31}; void Update_Date(void) { u8 days_of_month; // 判斷閏年 if((year%40 year%100!0) || (year%4000)) { month_day[2] 29; } else { month_day[2] 28; } days_of_month month_day[month]; day; if(day days_of_month) { day 1; month; if(month 12) { month 1; year; } } }務(wù)必在每次日期更新前重新判斷閏年因?yàn)樵路輒onth可能剛跨過(guò)2月。4. 模擬信號(hào)采集的精度提升與軟件濾波ADC采集的原始值只是數(shù)字要變成有意義的物理量電壓值、勒克斯值需要經(jīng)過(guò)校準(zhǔn)和濾波。4.1 ADC基準(zhǔn)電壓與校準(zhǔn)IAP15F2K61S2的ADC參考電壓Vref默認(rèn)是電源電壓Vcc通常是5V。這意味著ADC結(jié)果N與輸入電壓Vin的關(guān)系是Vin (N / 1023) * Vref。這里有個(gè)關(guān)鍵點(diǎn)Vcc不一定精確等于5.00V。USB供電可能是4.8V穩(wěn)壓芯片輸出可能有±1%的誤差。這直接導(dǎo)致所有電壓測(cè)量產(chǎn)生系統(tǒng)誤差。校準(zhǔn)技巧在程序初始化時(shí)或者提供一個(gè)“校準(zhǔn)模式”測(cè)量一個(gè)已知的、精確的基準(zhǔn)電壓比如開(kāi)發(fā)板上自帶的TL431基準(zhǔn)源通常是2.5V或3.3V。根據(jù)測(cè)量值反算出實(shí)際的Vref值并保存到EEPROM中。后續(xù)所有電壓計(jì)算都使用這個(gè)校準(zhǔn)后的Vref_real。公式為Vref_real V_known * 1023 / N_measured。這是提升測(cè)量精度的最有效手段。4.2 軟件濾波算法ADC采集的值會(huì)因電源噪聲、電磁干擾等產(chǎn)生跳動(dòng)。直接顯示原始值會(huì)閃爍不停。必須進(jìn)行軟件濾波。均值濾波連續(xù)采樣N次如8次求和后取平均。簡(jiǎn)單有效但會(huì)引入N個(gè)采樣周期的延遲。適合變化緩慢的信號(hào)。#define FILTER_N 8 unsigned int adc_buf[FILTER_N]; unsigned char buf_index 0; unsigned int adc_filtered 0; // 在ADC中斷或循環(huán)中 adc_buf[buf_index] ADC_Result; buf_index (buf_index 1) % FILTER_N; adc_filtered 0; for(int i0; iFILTER_N; i) { adc_filtered adc_buf[i]; } adc_filtered / FILTER_N;一階滯后濾波低通濾波這是我更推薦的方法計(jì)算量小效果平滑。公式為Y(n) α * X(n) (1-α) * Y(n-1)。其中α是濾波系數(shù)0α1α越小濾波效果越強(qiáng)但響應(yīng)越慢。通常取0.1~0.3。float voltage_now, voltage_filtered 0; float alpha 0.2; // 濾波系數(shù) // 每次得到新采樣值voltage_now后 voltage_filtered alpha * voltage_now (1-alpha) * voltage_filtered;這種方法只有一個(gè)狀態(tài)變量voltage_filtered無(wú)需數(shù)組非常適合單片機(jī)。4.3 光照傳感器的標(biāo)定光照測(cè)量比電壓更“虛”因?yàn)樗鼪](méi)有像“伏特”這樣的絕對(duì)單位。光敏電阻的輸出電壓與光照強(qiáng)度勒克斯Lux通常是指數(shù)或?qū)?shù)關(guān)系而非線(xiàn)性。直接套用線(xiàn)性公式Lux k * Vadc b誤差會(huì)很大。更靠譜的做法是進(jìn)行兩點(diǎn)標(biāo)定在完全黑暗的環(huán)境下用黑布蓋住記錄ADC值A(chǔ)DC_dark此時(shí)光照約為0 Lux。在一個(gè)已知標(biāo)準(zhǔn)光照環(huán)境下比如用手機(jī)的光照計(jì)APP測(cè)出500 Lux記錄ADC值A(chǔ)DC_500。假設(shè)關(guān)系近似線(xiàn)性在有限范圍內(nèi)可行則計(jì)算斜率k 500 / (ADC_500 - ADC_dark)。實(shí)際光照Lux k * (ADC_now - ADC_dark)。當(dāng)然如果追求精度應(yīng)該采集多個(gè)點(diǎn)如0, 100, 500, 1000 Lux繪制曲線(xiàn)在程序里用查表法或分段線(xiàn)性插值來(lái)計(jì)算。對(duì)于國(guó)賽題目?jī)牲c(diǎn)標(biāo)定加上一句“測(cè)量結(jié)果僅供參考如需精確測(cè)量需進(jìn)行多點(diǎn)標(biāo)定”的說(shuō)明就足夠體現(xiàn)你的工程思維了。5. 系統(tǒng)整合、調(diào)試與性能優(yōu)化當(dāng)各個(gè)模塊都調(diào)通后整合起來(lái)才是真正的挑戰(zhàn)。最常見(jiàn)的問(wèn)題是資源沖突和時(shí)序錯(cuò)亂。5.1 調(diào)試中常見(jiàn)的“坑”與排查坑1顯示亂碼或閃爍。原因LCD寫(xiě)數(shù)據(jù)太快未等待其內(nèi)部操作完成。或者在中斷服務(wù)程序里調(diào)用了LCD寫(xiě)函數(shù)打斷了正常的寫(xiě)時(shí)序。解決確保每次寫(xiě)命令或數(shù)據(jù)后都有足夠的延時(shí)檢查忙標(biāo)志或簡(jiǎn)單延時(shí)。所有LCD操作必須在主循環(huán)或低優(yōu)先級(jí)任務(wù)中完成絕對(duì)避免在ADC中斷、定時(shí)器中斷中直接調(diào)用LCD_Write???按鍵反應(yīng)遲鈍或連擊。原因消抖時(shí)間設(shè)置不當(dāng)太長(zhǎng)則遲鈍太短則連擊或者按鍵處理Key_Process函數(shù)被其他耗時(shí)任務(wù)阻塞。解決使用狀態(tài)機(jī)消抖將鍵值檢測(cè)放在定時(shí)中斷中只設(shè)置“鍵按下”、“鍵釋放”等事件標(biāo)志。主循環(huán)中的Key_Process函數(shù)應(yīng)非常短小只根據(jù)標(biāo)志位執(zhí)行相應(yīng)動(dòng)作如時(shí)間值1立即返回???電壓/光照值偶爾跳變到一個(gè)極大或極小值。原因ADC轉(zhuǎn)換期間受到干擾或者讀取ADC結(jié)果時(shí)高低字節(jié)讀取不同步。解決硬件上ADC輸入引腳靠近MCU處加濾波電容0.1uF。軟件上讀取ADC結(jié)果時(shí)應(yīng)先讀低字節(jié)再讀高字節(jié)根據(jù)數(shù)據(jù)手冊(cè)順序或者連續(xù)讀取兩次舍棄第一次的結(jié)果。加入軟件濾波如前所述的一階滯后濾波能有效平滑野值???系統(tǒng)運(yùn)行一段時(shí)間后時(shí)間明顯變慢。原因定時(shí)器中斷重裝值計(jì)算有誤或者中斷服務(wù)程序執(zhí)行時(shí)間過(guò)長(zhǎng)導(dǎo)致定時(shí)器中斷不能精確發(fā)生。解決計(jì)算定時(shí)器初值要精確。對(duì)于12T模式的51單片機(jī)若晶振為12MHz機(jī)器周期為1us。要產(chǎn)生10ms中斷需計(jì)數(shù)10000次。初值TH0 (65536-10000)/256; TL0 (65536-10000)%256;。同時(shí)用示波器或邏輯分析儀測(cè)量一個(gè)IO口在定時(shí)器中斷里翻轉(zhuǎn)的波形看周期是否是準(zhǔn)確的20ms進(jìn)出中斷各一次以此驗(yàn)證定時(shí)精度。5.2 性能優(yōu)化與代碼風(fēng)格減少全局變量盡量使用局部變量并將頻繁訪(fǎng)問(wèn)的全局變量聲明為idata或pdata加快訪(fǎng)問(wèn)速度。避免浮點(diǎn)運(yùn)算51單片機(jī)處理浮點(diǎn)數(shù)非常慢??梢詫㈦妷褐涤谜麛?shù)表示單位是毫伏mV。例如3.28V在程序中存儲(chǔ)為3280。顯示時(shí)再除以1000得到整數(shù)部分取模1000得到小數(shù)部分。光照強(qiáng)度同理用整數(shù)表示。狀態(tài)機(jī)編程對(duì)于按鍵、顯示界面切換如查看歷史數(shù)據(jù)、系統(tǒng)模式運(yùn)行/設(shè)置等使用狀態(tài)機(jī)switch-case結(jié)構(gòu)可以讓邏輯無(wú)比清晰避免一堆if-else嵌套。注釋與模塊化為每個(gè).c文件編寫(xiě)清晰的頭部注釋說(shuō)明功能、作者、版本。函數(shù)名使用動(dòng)詞名詞形式如ADC_GetVoltage()變量名使用有意義的名稱(chēng)如g_system_mode。6. 功能擴(kuò)展與賽題思考完成基本功能后可以思考一些擴(kuò)展點(diǎn)這往往是國(guó)賽題目拉開(kāi)差距的地方數(shù)據(jù)記錄與回看利用單片機(jī)內(nèi)部的EEPROM如IAP15有2KB每隔一段時(shí)間如每分鐘存儲(chǔ)一次電壓和光照的均值。然后通過(guò)按鍵切換到一個(gè)“歷史數(shù)據(jù)”界面進(jìn)行翻看。這里要注意EEPROM的壽命約10萬(wàn)次擦寫(xiě)需要做均衡寫(xiě)入處理。超限報(bào)警設(shè)置電壓和光照的上下限閾值當(dāng)測(cè)量值超限時(shí)讓蜂鳴器響或LED閃爍。閾值可以通過(guò)按鍵設(shè)置并保存到EEPROM。上位機(jī)通信通過(guò)串口將實(shí)時(shí)數(shù)據(jù)發(fā)送到電腦用串口助手或自己寫(xiě)的上位機(jī)軟件顯示曲線(xiàn)。這涉及到串口通信協(xié)議設(shè)計(jì)比如定義簡(jiǎn)單的幀頭、數(shù)據(jù)、校驗(yàn)、幀尾格式。低功耗設(shè)計(jì)如果題目有電池供電的要求可以考慮在無(wú)操作時(shí)讓單片機(jī)進(jìn)入空閑模式Idle由定時(shí)器中斷喚醒。關(guān)閉不用的外設(shè)如ADC、比較器以降低功耗?;剡^(guò)頭看這道賽題它考察的絕不僅僅是某個(gè)外設(shè)的驅(qū)動(dòng)。它考察的是系統(tǒng)思維如何將多個(gè)看似獨(dú)立的功能模塊通過(guò)合理的軟硬件架構(gòu)整合成一個(gè)穩(wěn)定、可靠、實(shí)時(shí)響應(yīng)的整體。它要求你對(duì)MCU的資源IO、定時(shí)器、中斷、ADC有全局的掌控對(duì)模擬電路傳感器接口、抗干擾有基本的理解對(duì)軟件工程模塊化、狀態(tài)機(jī)、調(diào)度有初步的實(shí)踐。把這些點(diǎn)都做好你就從一個(gè)只會(huì)調(diào)庫(kù)的“程序員”向一個(gè)能解決實(shí)際問(wèn)題的“工程師”邁進(jìn)了一大步。在調(diào)試這樣一個(gè)系統(tǒng)時(shí)我最深的體會(huì)是耐心和模塊化測(cè)試。一定要確保每一個(gè)子模塊比如單獨(dú)的ADC采集程序、單獨(dú)的LCD顯示程序都完全正確后再?lài)L試將它們組合起來(lái)。用printf通過(guò)串口輸出調(diào)試信息雖然51上實(shí)現(xiàn)稍麻煩是無(wú)比強(qiáng)大的工具能幫你快速定位問(wèn)題所在。最后代碼的可讀性和可維護(hù)性在項(xiàng)目復(fù)雜度上升時(shí)會(huì)讓你受益匪淺別為了省幾行代碼而寫(xiě)出只有自己能看懂的“天書(shū)”。