的電子時(shí)鐘設(shè)計(jì):DS1302與數(shù)碼管動(dòng)態(tài)掃描全解析)
簡(jiǎn)介這是一份基于單片機(jī)MCU的簡(jiǎn)易電子時(shí)鐘畢業(yè)設(shè)計(jì)論文資源適合電子信息工程、計(jì)算機(jī)科學(xué)等專業(yè)的學(xué)生在課程設(shè)計(jì)、畢業(yè)設(shè)計(jì)或單片機(jī)入門學(xué)習(xí)時(shí)參考。文檔以AT89C系列單片機(jī)為核心詳細(xì)介紹了數(shù)字鐘的計(jì)時(shí)、自動(dòng)報(bào)時(shí)與自動(dòng)控制功能實(shí)現(xiàn)方案并結(jié)合數(shù)字邏輯電路、定時(shí)器與微處理器等知識(shí)展開設(shè)計(jì)能夠幫助讀者理解從需求分析、電路搭建到程序調(diào)試的完整流程。壓縮包內(nèi)含1個(gè)doc格式論文文檔大小僅456KB文件結(jié)構(gòu)包含中英文摘要、目錄、各章節(jié)設(shè)計(jì)方案及關(guān)鍵代碼邏輯說明便于直接查閱和二次編輯。目前已有129人學(xué)習(xí)使用是快速了解單片機(jī)數(shù)字鐘設(shè)計(jì)框架、撰寫畢業(yè)論文或準(zhǔn)備答辯的實(shí)用參考資料。文檔重點(diǎn)涵蓋單片機(jī)定時(shí)器應(yīng)用、數(shù)字集成電路技術(shù)以及時(shí)間調(diào)整與顯示模塊設(shè)計(jì)既有理論講解也有具體實(shí)現(xiàn)細(xì)節(jié)對(duì)初學(xué)者和需要快速完成設(shè)計(jì)任務(wù)的學(xué)生均具參考價(jià)值。1. 單片機(jī)電子時(shí)鐘一個(gè)看著簡(jiǎn)單、做起來全是細(xì)節(jié)的畢設(shè)題很多人第一次看到「基于單片機(jī)的簡(jiǎn)易電子時(shí)鐘設(shè)計(jì)」這個(gè)畢設(shè)題目第一反應(yīng)是「數(shù)碼管按鍵一個(gè)芯片能有多難」。實(shí)際做下來你會(huì)發(fā)現(xiàn)最花時(shí)間的不是把時(shí)間顯示出來而是讓時(shí)間「走得準(zhǔn)、掉電不丟、按鍵不抖、顯示不閃」。一個(gè)最簡(jiǎn)單的電子時(shí)鐘背后要同時(shí)處理定時(shí)器初值誤差、DS1302 三線時(shí)序、數(shù)碼管動(dòng)態(tài)掃描刷新率這三類問題任何一環(huán)沒處理好做出來的實(shí)物都會(huì)出現(xiàn)讓人抓狂的現(xiàn)象時(shí)間越走越慢、一斷電就歸零、數(shù)碼管忽明忽暗。這篇文章按我從選型到寫論文的完整路徑來寫先講方案為什么這么定再講電路怎么搭、代碼怎么寫最后把調(diào)試時(shí)最容易踩的幾個(gè)坑逐個(gè)拆開。讀者不管是準(zhǔn)備做這個(gè)畢設(shè)還是想快速搭一個(gè)能用的時(shí)鐘原型照著這個(gè)順序走基本能一次打通。新手可以按章節(jié)逐步復(fù)現(xiàn)做過的人也能在最后一章看到幾個(gè)平時(shí)不太會(huì)注意的精度和可靠性細(xì)節(jié)。2. 方案選型STC89C52、DS1302 與數(shù)碼管驅(qū)動(dòng)每一步都有取舍電子時(shí)鐘的「方案」聽起來簡(jiǎn)單其實(shí)包含三個(gè)獨(dú)立的選擇主控用什么、時(shí)間基準(zhǔn)從哪來、顯示用什么。這三個(gè)選擇互相影響牽一發(fā)而動(dòng)全身。先把它們定下來后面的電路和代碼才有討論的前提。2.1 主控為什么選 51 內(nèi)核成本、資料和答辯的平衡畢設(shè)選題里最穩(wěn)妥的主控還是 51 內(nèi)核的 STC89C52而不是 STM32 或 Arduino。理由很實(shí)在DIP 封裝的 STC89C52 可以直接插在洞洞板上不需要貼片焊接5V 供電引腳耐操焊錯(cuò)了兩根線也不容易燒芯片。從成本上說一片芯片幾塊錢整板物料不超過二十塊比起 STM32 最小系統(tǒng)板加調(diào)試器的成本低一個(gè)數(shù)量級(jí)。資料方面51 的教程、例程、參考設(shè)計(jì)多到看不完遇到問題搜索一下就能找到同類案例這對(duì)畢設(shè)進(jìn)度的保障非常重要。從答辯角度想51 內(nèi)核的每一個(gè)外設(shè)行為都是「透明」的定時(shí)器初值要自己算數(shù)碼管要自己掃描DS1302 時(shí)序要自己寫。老師問起來你能把每個(gè)寄存器的作用講清楚換用 Arduino庫(kù)函數(shù)把底層全封裝了問到底層實(shí)現(xiàn)就容易露怯。當(dāng)然 STC15 系列這類 1T 芯片也可以但要注意它的指令周期和傳統(tǒng) 51 不一樣網(wǎng)上很多延時(shí)函數(shù)和定時(shí)器初值不能直接套用。我的建議是畢設(shè)不要在這上面追求新傳統(tǒng) 12T 模式的 STC89C52 最省心。2.2 時(shí)鐘源之爭(zhēng)DS1302 硬件時(shí)鐘 vs 定時(shí)器軟件時(shí)鐘時(shí)鐘源是整個(gè)設(shè)計(jì)里最關(guān)鍵的一個(gè)決定。常見做法有三種純單片機(jī)定時(shí)器做軟件時(shí)鐘、外接 DS1302 實(shí)時(shí)時(shí)鐘芯片、外接 DS3231 高精度 RTC 模塊。我直接說結(jié)論畢設(shè)選 DS1302 最合適因?yàn)樗瑫r(shí)滿足「掉電保持」和「原理可講」這兩個(gè)硬需求。純定時(shí)器方案的最大問題是掉電即丟時(shí)間。單片機(jī)一斷電內(nèi)部 RAM 里的時(shí)分秒全部清零重新上電只能回到初始值。要做掉電保持就得額外加電池給芯片持續(xù)供電那還不如直接用帶雙電源引腳的 DS1302。另一個(gè)問題是精度軟件時(shí)鐘的時(shí)間基準(zhǔn)來自主晶振12MHz 晶振本身精度有限加上定時(shí)器重裝初值時(shí)的指令周期損耗一天慢幾秒是常態(tài)。DS1302 自帶 32.768kHz 晶振接口走時(shí)精度取決于這顆晶振比用主晶振做軟件計(jì)時(shí)更靠譜而且它內(nèi)部用 BCD 碼存儲(chǔ)時(shí)間讀寫邏輯很直觀寫論文時(shí)寄存器表一貼就是滿滿一頁(yè)。DS3231 精度確實(shí)高內(nèi)置溫補(bǔ)晶振年誤差能到幾分鐘級(jí)別但價(jià)格是 DS1302 的好幾倍接口也變成了 I2C。畢設(shè)用 DS3231 有點(diǎn)超綱成本解釋起來也麻煩。最終我的選擇是 DS1302加一顆 CR2032 紐扣電池做后備電源斷電后時(shí)間照走。2.3 顯示方案6 位數(shù)碼管動(dòng)態(tài)掃描更貼合「電子時(shí)鐘」顯示部分在數(shù)碼管和 LCD1602 之間選。LCD1602 能顯示字母和自定義字符做菜單、鬧鐘設(shè)置界面很方便但一個(gè)電子時(shí)鐘用液晶屏總感覺少了點(diǎn)「設(shè)備感」數(shù)碼管則是最經(jīng)典的時(shí)鐘顯示載體紅光數(shù)字一亮電子時(shí)鐘的味道就出來了。從電路復(fù)雜度看LCD1602 需要 11 根數(shù)據(jù)線數(shù)碼管動(dòng)態(tài)掃描只用一組 8 根段選線加若干位選線兩者差距不大但數(shù)碼管的驅(qū)動(dòng)邏輯更直觀也更容易在論文里畫圖說明。位數(shù)上我建議直接做 6 位時(shí)、分、秒各兩位。4 位只顯示時(shí)分秒的變化看不到時(shí)鐘「活著」的感覺少一半6 位能實(shí)時(shí)看到秒跳動(dòng)調(diào)試時(shí)也方便確認(rèn)程序在跑。動(dòng)態(tài)掃描的原理是用人眼視覺暫留逐位點(diǎn)亮每一位數(shù)碼管刷新頻率足夠快時(shí)看起來就是同時(shí)亮的。刷新率一般控制在每 2ms 切換一位6 位一輪下來約 12ms等效刷新率 80Hz 左右不會(huì)閃。段選用 P0 口經(jīng)限流電阻直接驅(qū)動(dòng)位選用 PNP 三極管 8550 做開關(guān)這個(gè)接法在下一章詳細(xì)展開。3. 電路設(shè)計(jì)最小系統(tǒng)、數(shù)碼管驅(qū)動(dòng)與 DS1302 外圍的接法電路部分不需要畫得多漂亮但接線關(guān)系必須清楚。我習(xí)慣先把每一塊電路的功能和連接關(guān)系列成清單再動(dòng)手焊洞洞板。這樣后續(xù)調(diào)試時(shí)每一個(gè)引腳是干什么的、出了問題查哪根線心里都有數(shù)。3.1 最小系統(tǒng)電源、12MHz 晶振與上電復(fù)位單片機(jī)要跑起來缺不了三樣?xùn)|西電源、晶振、復(fù)位電路。電源用 USB 供電或者 5V 適配器都行如果從 12V 變壓器取電中間要加 7805 穩(wěn)壓。晶振用 12MHz兩個(gè)引腳各接一個(gè) 22pF~30pF 的瓷片電容到地電容的作用是匹配晶振的負(fù)載電容讓振蕩電路更容易起振且頻率更穩(wěn)。復(fù)位電路是經(jīng)典的 10uF 電解電容加 10kΩ 電阻電容正極接 VCC負(fù)極接 RST 引腳RST 再經(jīng) 10kΩ 電阻下拉到地。上電瞬間電容充電RST 保持短暫高電平完成復(fù)位之后恢復(fù)低電平正常運(yùn)行。這個(gè)最小系統(tǒng)還有一件事要提前考慮程序下載。STC 系列用的是串口下載需要把單片機(jī)的 TXD 和 RXD 交叉接到 USB 轉(zhuǎn)串口模塊上下載時(shí)先點(diǎn)軟件的「下載」按鈕再給目標(biāo)板上電也就是常說的「冷啟動(dòng)」。這一步是后面調(diào)試中最容易翻車的點(diǎn)經(jīng)常出現(xiàn)「點(diǎn)下載沒反應(yīng)」絕大多數(shù)情況是冷啟動(dòng)順序不對(duì)或者串口線接反了。我一般會(huì)在 3.1 節(jié)就把下載口接好并測(cè)試一次空程序確認(rèn)下載鏈路通了再焊其他電路。3.2 數(shù)碼管驅(qū)動(dòng)共陽(yáng)接法、8550 位選與限流電阻6 位共陽(yáng)數(shù)碼管是畢設(shè)里最順手的顯示器件。所謂共陽(yáng)是每一位的 8 個(gè) LEDa~g 段加小數(shù)點(diǎn) dp的陽(yáng)極在內(nèi)部并在一起形成一個(gè)公共端。驅(qū)動(dòng)時(shí)分兩步段選決定這一位顯示什么數(shù)字位選決定哪一位被點(diǎn)亮。段選接法是 P0 口 8 根線各串一個(gè) 220Ω 限流電阻分別接到數(shù)碼管的 a~g 和 dp 引腳共陽(yáng)管的段選低電平有效也就是說某段引腳為低電平時(shí)該段 LED 導(dǎo)通點(diǎn)亮。位選接法是用 PNP 三極管 8550 做開關(guān)發(fā)射極接 VCC集電極接某一位的公共端基極經(jīng) 1kΩ 電阻接到單片機(jī)的 P2.x 引腳。IO 輸出低電平時(shí)三極管導(dǎo)通該位公共端被接上 VCC這一位才有條件點(diǎn)亮。這里最容易搞混的是共陽(yáng)和共陰的區(qū)別。共陰管公共端接地位選要用 NPN 三極管 8050IO 高電平選通段選也是高電平點(diǎn)亮。如果買錯(cuò)了管子或者買錯(cuò)了數(shù)碼管段碼表全得反過來。我建議直接鎖定共陽(yáng)加 8550 的組合段碼表用取反后的共陽(yáng)段碼網(wǎng)上現(xiàn)成的例程大多也是這個(gè)搭配。限流電阻的值也值得說一下220Ω 在 5V 供電下每段電流約 10mA 出頭6 位動(dòng)態(tài)掃描時(shí)每位只有 1/6 的占空比亮度剛好如果覺得暗可以換成 100Ω 或 150Ω但不要低于 100Ω否則長(zhǎng)期工作會(huì)超過 LED 額定電流。3.3 DS1302 外圍電路與按鍵蜂鳴器DS1302 的外圍電路不復(fù)雜但電源接法有講究。芯片的 VCC2 接系統(tǒng) 5VVCC1 接 CR2032 紐扣電池的正極兩路電源在芯片內(nèi)部自動(dòng)切換主電源 5V 存在時(shí)用主電源主電源掉電后自動(dòng)切換到電池保證時(shí)間寄存器不丟。電池負(fù)極和系統(tǒng) GND 共地電池正極最好串一個(gè) 1N4148 二極管再進(jìn) VCC1防止系統(tǒng)電源倒灌給電池充電。DS1302 的 32.768kHz 晶振直接接 X1 和 X2 引腳兩個(gè)引腳各接一個(gè) 6pF 左右的電容到地。這個(gè)晶振是圓柱形的很便宜但精度差異較大走時(shí)誤差主要就來自它。DS1302 的三根控制線 CE、SCLK、IO 分別接 P1.0、P1.1、P1.2IO 線和 SCLK 線各加一個(gè) 10kΩ 上拉電阻到 VCC。很多例程里沒加上拉仿真也能跑但實(shí)物焊接后數(shù)據(jù)經(jīng)常讀出來全是 0xFF原因就是 IO 線在輸入模式下處于懸空狀態(tài)電平不確定。按鍵部分用 4 個(gè)獨(dú)立輕觸按鍵分別做設(shè)置、加、減、確認(rèn)每個(gè)按鍵一端接 P3 口另一端接地P3 口內(nèi)部有上拉不需要外接電阻。蜂鳴器用有源 5V 蜂鳴器PNP 三極管驅(qū)動(dòng)基極接一個(gè) IO 口低電平響注意有源蜂鳴器內(nèi)部已經(jīng)帶了振蕩電路只需要給高低電平就能發(fā)聲別拿它當(dāng)無源的去寫 PWM 驅(qū)動(dòng)。4. 固件實(shí)現(xiàn)DS1302 讀寫時(shí)序、動(dòng)態(tài)掃描與 50ms 定時(shí)基準(zhǔn)電路焊好之后工作重心就轉(zhuǎn)到代碼上。固件部分我按模塊拆開DS1302 驅(qū)動(dòng)、數(shù)碼管掃描、定時(shí)器基準(zhǔn)、按鍵處理。每個(gè)模塊單獨(dú)調(diào)試全部通了再組合。這樣定位問題的時(shí)候不用在幾百行代碼里大海撈針。4.1 Keil 工程分模塊main、ds1302、display、key 各司其職Keil C51 的工程建議按源文件拆分main.c 放主循環(huán)和初始化ds1302.c 放 DS1302 讀寫函數(shù)display.c 放數(shù)碼管掃描和緩沖數(shù)組key.c 放按鍵掃描和消抖timer.c 放定時(shí)器初始化與中斷服務(wù)函數(shù)。別嫌模塊多畢設(shè)論文里「軟件設(shè)計(jì)」一章需要畫模塊結(jié)構(gòu)圖、寫各模塊功能說明工程結(jié)構(gòu)清晰能省不少寫文檔的時(shí)間。頭文件里統(tǒng)一聲明全局函數(shù)和變量注意 C51 的變量類型是 unsigned char 為主因?yàn)?51 是 8 位機(jī)int 運(yùn)算會(huì)慢很多但 DS1302 的地址和 BCD 碼轉(zhuǎn)換用 unsigned char 完全夠。還要注意 sbit 定義引腳時(shí)要和電路圖嚴(yán)格對(duì)應(yīng)比如sbit DS1302_CE P1^0;這種寫法定義錯(cuò)了整個(gè)時(shí)序全亂。我在工程里習(xí)慣把引腳定義全部集中在頭文件里這樣電路板改線時(shí)只需改頭文件不用去動(dòng)邏輯代碼。4.2 DS1302 驅(qū)動(dòng)代碼BCD 轉(zhuǎn)換、讀保護(hù)與三線時(shí)序DS1302 的核心操作是讀寫字寄存器寄存器地址低字節(jié)的 bit0 表示讀還是寫讀地址是寫地址加 1。比如秒寄存器地址是 0x80寫秒發(fā) 0x80讀秒發(fā) 0x81。時(shí)間數(shù)據(jù)在芯片內(nèi)部以 BCD 碼存放比如十進(jìn)制的 59 存成 0x59高四位是十位低四位是個(gè)位。所以在讀寫時(shí)間前先寫好十進(jìn)制和 BCD 之間的轉(zhuǎn)換函數(shù)。// 十進(jìn)制轉(zhuǎn) BCD例如 59 - 0x59 unsigned char DecToBcd(unsigned char dec) { return ((dec / 10) 4) | (dec % 10); } // BCD 轉(zhuǎn)十進(jìn)制例如 0x59 - 59 unsigned char BcdToDec(unsigned char bcd) { return (bcd 4) * 10 (bcd 0x0F); } // 向 DS1302 寫入一個(gè)字節(jié)CE 拉高后SCLK 上升沿逐位寫入 void DS1302_WriteByte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DS1302_IO dat 0x01; // 低位先出 DS1302_SCLK 1; // 上升沿芯片采樣數(shù)據(jù) dat 1; DS1302_SCLK 0; } } // 從 DS1302 讀出 8 位數(shù)據(jù)注意方向切換后數(shù)據(jù)在下降沿后穩(wěn)定 unsigned char DS1302_ReadByte(void) { unsigned char i, dat 0; for (i 0; i 8; i) { dat 1; if (DS1302_IO) dat | 0x80; DS1302_SCLK 1; DS1302_SCLK 0; } return dat; }邏輯說明DS1302 的寫時(shí)序是 CE 拉高后SCLK 上升沿把數(shù)據(jù)一位一位鎖存進(jìn)去低位在前讀時(shí)序則是在 SCLK 下降沿之后數(shù)據(jù)線上出現(xiàn)有效數(shù)據(jù)主機(jī)在下降沿后讀電平。代碼里讀寫用了同一條 IO 線所以讀數(shù)據(jù)之前必須切換方向我的習(xí)慣是在每次讀字節(jié)前先把 IO 拉高然后在下降沿之后采樣。參數(shù)說明DS1302_WriteByte是通用字節(jié)寫入命令字節(jié)和數(shù)據(jù)字節(jié)都通過它傳輸DS1302_ReadByte在讀出數(shù)據(jù)前不需要額外配置方向因?yàn)?C51 的準(zhǔn)雙向 IO 口天然支持輸入但前提是外接上拉電阻保證電平穩(wěn)定。接下來的設(shè)置時(shí)間和讀取時(shí)間函數(shù)要配合寫保護(hù)寄存器 0x8E 使用WP 位為 1 時(shí)禁止寫入。完整的設(shè)置時(shí)間流程是關(guān)寫保護(hù)→寫七個(gè)時(shí)間寄存器→開寫保護(hù)。// 設(shè)置 DS1302 時(shí)間參數(shù)為十進(jìn)制數(shù)值 void DS1302_SetTime(unsigned char hour, unsigned char minute, unsigned char second) { DS1302_WriteByte(0x8E); // 寫保護(hù)寄存器地址 DS1302_WriteByte(0x00); // WP0關(guān)閉寫保護(hù) DS1302_CE 1; DS1302_WriteByte(0x80); // 秒寄存器寫地址 DS1302_WriteByte(DecToBcd(second)); DS1302_WriteByte(0x82); // 分寄存器寫地址 DS1302_WriteByte(DecToBcd(minute)); DS1302_WriteByte(0x84); // 時(shí)寄存器寫地址 DS1302_WriteByte(DecToBcd(hour)); DS1302_WriteByte(0x8E); // 重新打開寫保護(hù) DS1302_WriteByte(0x80); DS1302_CE 0; }邏輯說明這里每一步操作之前都要先DS1302_CE 1但代碼里在關(guān)寫保護(hù)之后又置了一次 CE因?yàn)槲业腄S1302_WriteByte函數(shù)本身不操作 CE所以每次以 CE 拉高開始一段完整操作。設(shè)置時(shí)間的順序是秒→分→時(shí)三者在同一段 CE 高電平內(nèi)連續(xù)寫入最后把 WP 重新置 1。參數(shù)說明為什么要開寫保護(hù)調(diào)試過程中整板反復(fù)上下電DS1302 電源波動(dòng)時(shí)如果 WP0芯片可能把內(nèi)部誤判的數(shù)據(jù)寫進(jìn)時(shí)間寄存器導(dǎo)致時(shí)間亂跳。畢設(shè)做樣機(jī)時(shí)這個(gè)問題尤其常見。讀取時(shí)間則是每次先發(fā)讀地址再連續(xù)讀回秒、分、時(shí)注意讀操作完畢后 CE 必須拉低否則芯片會(huì)一直占用數(shù)據(jù)線。4.3 動(dòng)態(tài)掃描與定時(shí)器2ms 刷新和 50ms 基準(zhǔn)怎么配合數(shù)碼管顯示用定時(shí)器中斷驅(qū)動(dòng)主循環(huán)只負(fù)責(zé)刷新顯示緩沖數(shù)組的值這樣按鍵處理和顯示刷新互不干擾。我的做法是用定時(shí)器 1 做 2ms 中斷每次中斷切換掃描一位數(shù)碼管6 位數(shù)碼管 12ms 掃描一輪。顯示緩沖數(shù)組里存的是當(dāng)前要顯示的數(shù)字中斷服務(wù)函數(shù)從數(shù)組取數(shù)翻譯成段碼送出。// 共陽(yáng)段碼表0~9依次對(duì)應(yīng)段 a~g, dp 全滅為 0xFF unsigned char code SegCode[] {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90}; // 顯示緩沖下標(biāo) 0~5 依次為時(shí)十位、時(shí)個(gè)位、分十位、分個(gè)位、秒十位、秒個(gè)位 unsigned char dispBuf[6]; // 定時(shí)器 1 每 2ms 進(jìn)入一次切換掃描一位 void Timer1_ISR(void) interrupt 3 { static unsigned char pos 0; P0 0xFF; // 先消隱熄滅上一位的段 switch (pos) { case 0: P2 0xFE; break; // 位選低電平有效選中第 0 位 case 1: P2 0xFD; break; case 2: P2 0xFB; break; case 3: P2 0xF7; break; case 4: P2 0xEF; break; case 5: P2 0xDF; break; } P0 SegCode[dispBuf[pos]]; // 輸出當(dāng)前位段碼 pos (pos 1) % 6; }邏輯說明掃描順序是「先消隱、再選位、再送段碼」。消隱的目的是把上一位的段碼清掉否則切換位選的瞬間新位會(huì)短暫顯示上一位的筆畫產(chǎn)生拖影重影。位選 P2 口是低電平選中所以 0xFE、0xFD 這樣的值依次把 P2.0~P2.5 置低。段碼表用的是共陽(yáng)取反值顯示數(shù)字 0 時(shí) P0 輸出 0xC0對(duì)應(yīng) a~g 段低電平點(diǎn)亮、dp 熄滅。參數(shù)說明2ms 掃描一位意味著每位點(diǎn)亮?xí)r間 2ms6 位一輪 12ms等效刷新率約 83Hz肉眼看不出閃爍。如果掃描周期大于 4ms刷新率降到 40Hz 以下數(shù)碼管就會(huì)出現(xiàn)能察覺的閃爍感。想要顯示「時(shí):分:秒」中間的冒號(hào)閃爍效果可以單獨(dú)控制時(shí)個(gè)位的 dp 段每 500ms 翻轉(zhuǎn)一次它的狀態(tài)實(shí)現(xiàn)秒點(diǎn)跳動(dòng)。時(shí)間基準(zhǔn)用定時(shí)器 0 做 50ms 中斷每 20 次中斷累計(jì) 1 秒。這里有個(gè)細(xì)節(jié)12MHz 晶振下機(jī)器周期是 1us50ms 需要計(jì)數(shù) 50000 個(gè)機(jī)器周期初值是 65536-5000015536換算成十六進(jìn)制就是 0x3CB0。中斷服務(wù)函數(shù)里要先重裝初值再處理計(jì)數(shù)邏輯避免計(jì)數(shù)丟失。// 定時(shí)器 0 初始化模式 116 位定時(shí)50ms 中斷 void Timer0_Init(void) { TMOD | 0x01; // 定時(shí)器 0模式 1 TH0 (65536 - 50000) / 256; // 高 8 位初值 TL0 (65536 - 50000) % 256; // 低 8 位初值 ET0 1; // 使能定時(shí)器 0 中斷 TR0 1; // 啟動(dòng)定時(shí)器 EA 1; // 開啟總中斷 } // 定時(shí)器 0 中斷服務(wù)函數(shù)每 50ms 進(jìn)入一次 void Timer0_ISR(void) interrupt 1 { static unsigned int cnt 0; TH0 (65536 - 50000) / 256; // 重裝初值 TL0 (65536 - 50000) % 256; cnt; if (cnt 20) { // 20 次 1 秒 cnt 0; second; if (second 60) { second 0; minute; } if (minute 60) { minute 0; hour; } if (hour 24) { hour 0; } dispBuf[0] hour / 10; dispBuf[1] hour % 10; dispBuf[2] minute / 10; dispBuf[3] minute % 10; dispBuf[4] second / 10; dispBuf[5] second % 10; } }邏輯說明秒的遞增直接放在中斷里每 20 次中斷加一秒然后刷新顯示緩沖。hour / 10和hour % 10把十位和個(gè)位拆開這樣顯示模塊只需要從 dispBuf 取數(shù)即可。注意秒鐘到達(dá) 59 后再加一秒應(yīng)進(jìn)位到分并清零秒這里用 while 循環(huán)的連續(xù)判斷也能寫但用嵌套 if 更直觀代碼可讀性更好。參數(shù)說明50ms 定時(shí)的誤差來自中斷響應(yīng)到重裝初值之間損失的幾條指令時(shí)間約 2~3 個(gè)機(jī)器周期也就是 2~3us相當(dāng)于每個(gè) 50ms 周期實(shí)際比理論值長(zhǎng)一點(diǎn)一天累計(jì)誤差大概 4~6 秒。這個(gè)誤差在畢設(shè)論文里要如實(shí)記錄屬于「軟時(shí)鐘基準(zhǔn)不可避免的誤差」可以通過修正重裝初值來縮小但徹底消除得用硬件時(shí)鐘。這也從側(cè)面說明為什么第 2 章選了 DS1302 做主時(shí)鐘源——定時(shí)器只做顯示輔助和短時(shí)計(jì)時(shí)。5. 調(diào)試避坑時(shí)序、顯示、掉電與下載的四個(gè)高頻翻車點(diǎn)這一章內(nèi)容全是我實(shí)際調(diào)試時(shí)踩過的坑每條按「現(xiàn)象→原因→解決」寫。畢設(shè)做到最后你會(huì)發(fā)現(xiàn)焊板和寫代碼加起來的時(shí)間還不如調(diào)這些玄學(xué)問題花的時(shí)間多。5.1 DS1302 讀回 0xFF三線時(shí)序和上拉電阻的聯(lián)動(dòng)問題現(xiàn)象寫進(jìn)去的時(shí)間再讀出來全是 0xFF或者時(shí)間靜止不動(dòng)刷新顯示永遠(yuǎn)是上電初始值。原因最常見的有三個(gè)。一是 IO 線沒有上拉DS1302 讀模式下 IO 處于輸入態(tài)電平懸空讀回的數(shù)據(jù)隨機(jī)且偏 1二是 CE 時(shí)序不對(duì)CE 沒有先拉高再操作或者操作完后沒有拉低芯片沒有進(jìn)入正常通信狀態(tài)三是 SCLK 初始電平為高導(dǎo)致第一個(gè)數(shù)據(jù)位被誤采樣。解決硬件上給 IO 和 SCLK 各加一個(gè) 10kΩ 上拉到 VCC這個(gè)改動(dòng)解決了我遇到的大部分 0xFF 問題。軟件上嚴(yán)格按「CE 拉高→寫命令字節(jié)→讀數(shù)據(jù)→CE 拉低」的流程走命令字節(jié)發(fā)送完畢、方向切換到讀取之前先把 IO 拉高一個(gè) SCLK 周期釋放總線。注意每次讀寫操作結(jié)束后把 SCLK 拉低給下一次通信留一個(gè)確定的起始狀態(tài)。5.2 數(shù)碼管亮度不均、重影掃描周期與消隱順序現(xiàn)象第一位特別亮后面的位明顯暗數(shù)字邊緣有淡淡的拖影或者整個(gè)數(shù)碼管高頻閃爍眼睛看一會(huì)就累。原因亮度不均通常是位選掃描停留時(shí)間不一樣造成的比如某一位在處理中斷時(shí)被頻繁打斷點(diǎn)亮?xí)r間比別的位長(zhǎng)重影則幾乎都是位選切換瞬間沒有消隱導(dǎo)致的。閃爍一般是掃描周期太長(zhǎng)刷新率掉到了 50Hz 以下。解決確認(rèn)每位點(diǎn)亮?xí)r間一致我的做法是把「切換位選、送段碼」整個(gè)流程放在同一個(gè)定時(shí)器中斷里保證每位占用時(shí)間完全相等。消隱順序必須嚴(yán)格是「先 P00xFF 全滅→再切位選→再送新段碼」少一步都會(huì)在切換瞬間留下上一幀的殘影。刷新率不夠時(shí)把掃描中斷從 3ms 縮短到 2ms。還有一個(gè)容易忽略的點(diǎn)P0 口驅(qū)動(dòng)段選時(shí)需要加外部上拉否則 P0 開漏輸出高電平能力弱段碼輸出拉不高顯示會(huì)偏暗。5.3 斷電后時(shí)間歸零備用電池接法與寫保護(hù)現(xiàn)象斷電幾秒鐘再上電時(shí)間回到初始值DS1302 里的時(shí)間沒有保持住或者上電后時(shí)間偶爾變成不可信的亂碼。原因最常見的是 VCC1 沒接電池或者電池正負(fù)極接反其次是電池正極直接接了 VCC1但系統(tǒng) 5V 掉電瞬間電池端的電壓也被拉到異常值。還有一種情況是 DS1302 的 WP 寫保護(hù)沒啟用反復(fù)上下電時(shí)芯片把半路電壓下的隨機(jī)數(shù)據(jù)寫進(jìn)了寄存器。解決CR2032 電池正極接 VCC1負(fù)極接 GND中間串一個(gè) 1N4148 二極管防止倒灌。下載程序時(shí)整板會(huì)經(jīng)歷多次上電掉電建議代碼初始化時(shí)最后強(qiáng)制把 WP 置 1要修改時(shí)間時(shí)才臨時(shí)打開寫保護(hù)。這樣即使調(diào)電瞬間電平不穩(wěn)也不容易破壞寄存器內(nèi)容。調(diào)試時(shí)想驗(yàn)證掉電保持直接斷電 10 分鐘再上電看時(shí)間是否連續(xù)即可。5.4 下載失敗、晶振不起振冷啟動(dòng)與最小系統(tǒng)排查現(xiàn)象Keil 編譯通過點(diǎn)下載后進(jìn)度條一直不動(dòng)提示連接不上 MCU或者下載成功了但板子上電毫無反應(yīng)數(shù)碼管完全不亮。原因下載失敗九成是冷啟動(dòng)順序不對(duì)——必須先點(diǎn)下載再給單片機(jī)上電或者 TXD/RXD 交叉接反。上電無反應(yīng)則優(yōu)先懷疑晶振沒起振比如負(fù)載電容太大、晶振引腳虛焊、或者晶振旁邊鋪銅干擾。還有一種隱蔽情況復(fù)位電容太大導(dǎo)致上電復(fù)位時(shí)間過長(zhǎng)程序啟動(dòng)時(shí)電源還沒穩(wěn)定。解決下載問題先確認(rèn)串口號(hào)和波特率STC 下載器一般用 9600 或 2400 波特率更穩(wěn)點(diǎn)下載后 1~2 秒內(nèi)給板子上電動(dòng)作要干脆。晶振不起振就補(bǔ)焊晶振兩腳負(fù)載電容換成 22pF~30pF并盡量縮短晶振到單片機(jī) XTAL 引腳的走線距離不要在晶振底下鋪地銅。如果整板沒反應(yīng)先量電源、再量復(fù)位腳電平、再觀察晶振波形按這個(gè)順序排查不要一上來就懷疑程序?qū)戝e(cuò)。6. 從能亮到可靠驗(yàn)證記錄、論文組織與三個(gè)進(jìn)階技巧做到這一步板子基本能走、能顯、能按鍵設(shè)置。但畢設(shè)不能停在「能亮」的層面還得能證明它可靠并且把它講成一篇完整的論文。這一章說三件事怎么驗(yàn)證、怎么組織論文、怎么在細(xì)節(jié)上再進(jìn)一步。6.1 裸機(jī)驗(yàn)證清單與 24 小時(shí)走時(shí)測(cè)試拿到一塊焊好的板子按順序測(cè)上電顯示是否為預(yù)設(shè)初始時(shí)間按鍵設(shè)置能否逐位修改時(shí)分秒設(shè)置完成后時(shí)間是否正常走動(dòng)斷電 10 分鐘再上電時(shí)間是否保持鬧鐘到點(diǎn)蜂鳴器是否響。全部通過后做走時(shí)誤差記錄用標(biāo)準(zhǔn)時(shí)間校時(shí)每 2 小時(shí)記錄一次時(shí)間差持續(xù) 24 小時(shí)記錄表格式大致是「記錄時(shí)間、時(shí)鐘顯示、標(biāo)準(zhǔn)時(shí)間、誤差秒數(shù)」。這個(gè)數(shù)據(jù)表直接貼進(jìn)論文的調(diào)試章節(jié)很能說明問題。6.2 畢業(yè)論文的組織順序論文按「摘要→方案論證→硬件設(shè)計(jì)→軟件設(shè)計(jì)→系統(tǒng)調(diào)試→結(jié)論」的順序?qū)?。方案論證用表格對(duì)比定時(shí)器方案和 DS1302 方案硬件設(shè)計(jì)放電路圖和 BOM 表軟件設(shè)計(jì)放流程圖和關(guān)鍵代碼調(diào)試章節(jié)放仿真截圖、實(shí)物照片和誤差記錄表。畢設(shè)論文的關(guān)鍵不是代碼有多漂亮而是每一部分都能說清「為什么這么選、出了什么問題、怎么解決的」。6.3 三個(gè)進(jìn)階技巧誤差校正、掉電補(bǔ)償與抗干擾第一個(gè)技巧是定時(shí)器誤差校正。50ms 重裝初值會(huì)損失幾個(gè)機(jī)器周期一天慢幾秒。我的做法是在中斷里維護(hù)一個(gè)累加變量每個(gè) 50ms 周期累加損失的時(shí)間累積超過一個(gè)機(jī)器周期時(shí)把下一次重裝初值減 1相當(dāng)于每過一段時(shí)間給定時(shí)器「撥快」一點(diǎn)。這個(gè)補(bǔ)償算法能讓軟時(shí)鐘的日誤差從 4 秒左右壓到 1 秒以內(nèi)。第二個(gè)技巧是掉電瞬間保存現(xiàn)場(chǎng)。雖然 DS1302 自帶雙電源切換但如果主電源掉電時(shí)正在寫時(shí)間可能會(huì)出現(xiàn)半個(gè)字節(jié)的錯(cuò)誤。穩(wěn)妥做法是檢測(cè) 5V 電源電壓當(dāng)檢測(cè)到掉電瞬間暫停 DS1302 通信并把 WP 置 1等電源穩(wěn)定后再恢復(fù)。實(shí)際做下來這個(gè)檢測(cè)電路用一只穩(wěn)壓管加一個(gè) IO 口就能實(shí)現(xiàn)屬于成本低收益高的改動(dòng)。第三個(gè)技巧是抗干擾。按鍵消抖不要用延時(shí)循環(huán)阻塞主循環(huán)改用定時(shí)器 1ms 掃描狀態(tài)連續(xù)兩次一致才認(rèn)為按鍵有效數(shù)碼管掃描和按鍵掃描都用中斷驅(qū)動(dòng)主循環(huán)只做時(shí)間管理和緩沖刷新蜂鳴器驅(qū)動(dòng)三極管的感性負(fù)載兩端并聯(lián)一個(gè) 1N4148 續(xù)流二極管防止關(guān)斷瞬間的尖峰電壓干擾單片機(jī)復(fù)位。這些細(xì)節(jié)在論文里寫出來會(huì)讓答辯老師覺得你不只是拿了別人的例程改改而是真的在工程層面思考過可靠性。這些習(xí)慣我是在被干擾問題折磨過幾次之后養(yǎng)成的——第一次做時(shí)鐘時(shí)一按蜂鳴器按鍵數(shù)碼管就閃一下后來才發(fā)現(xiàn)是沒加續(xù)流二極管。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取