配時方案)
簡介這份資源面向嵌入式初學(xué)者、電子信息類專業(yè)學(xué)生及課程設(shè)計開發(fā)者提供一套完整的智能交通燈控制器實現(xiàn)方案涵蓋Keil程序代碼、Proteus仿真圖與使用說明可用于學(xué)習(xí)微控制器控制邏輯、交通流調(diào)度算法與電路聯(lián)合仿真。壓縮包共17個文件約86KB包含C語言源程序、Keil工程與編譯產(chǎn)物、Proteus電路設(shè)計文件、HEX燒錄文件及使用說明文本覆蓋從代碼編寫、編譯調(diào)試到虛擬仿真的完整流程。項目實現(xiàn)正常、緊急與智能三種工作模式通過按鍵切換并涉及傳感器數(shù)據(jù)處理、紅綠燈時序動態(tài)調(diào)整與用戶交互設(shè)計等知識點。已有4701人學(xué)習(xí)下載適合作為課程設(shè)計參考或嵌入式入門練手項目幫助讀者在無實物硬件條件下完成程序驗證與功能優(yōu)化快速掌握Keil與Proteus聯(lián)合仿真的基本方法。1. 從路口配時說起這套智能交通燈控制器方案到底解決什么問題做過路口信號配時的人都知道最讓人頭疼的不是燈不亮而是燈“太死板”。固定周期配時在車流均勻時看著還行一旦某個方向排隊溢出、另一個方向空放整個路口的通行效率就崩了。這套智能交通燈控制器方案核心就是解決“按需配時”的問題用單片機做本地決策根據(jù)車流檢測結(jié)果動態(tài)調(diào)整紅綠燈時長而不是死守一個寫死的周期表。它適合兩類人——正在做單片機課程設(shè)計、需要一套完整可仿真可燒錄方案的學(xué)生以及想快速驗證路口控制邏輯、不想從零搭硬件平臺的工程師。方案交付物包括 Keil 工程源碼、Proteus 仿真圖和一份使用說明拿到手就能跑仿真、看波形、改參數(shù)。2. 需求拆解與技術(shù)選型為什么是 51 單片機加 Proteus 這套組合2.1 功能需求到底該列幾條很多同學(xué)一上來就寫“實現(xiàn)智能控制”這等于沒寫。我一般會把需求拆成可驗收的條目第一支持標準四相位東西直行、東西左轉(zhuǎn)、南北直行、南北左轉(zhuǎn)或簡化兩相位控制第二每個相位的基礎(chǔ)綠燈時長可獨立設(shè)定且能在運行中根據(jù)檢測信號延長或縮短第三具備黃燈過渡和全紅清空時間防止相位切換時沖突第四至少兩路車流檢測輸入比如東西向和南北向各一個用于判斷是否有車等待第五數(shù)碼管或 LCD 顯示當前相位倒計時第六支持緊急模式比如手動強制全紅或某方向常綠。這六條寫進任務(wù)書答辯時老師一看就知道你思路清楚。2.2 為什么選 51 而不是 STM32這不是技術(shù)落后而是場景匹配。交通燈控制器的邏輯復(fù)雜度不高主要就是定時器中斷、IO 掃描和狀態(tài)機切換。51 單片機比如 AT89C51 或 STC89C52的定時器資源足夠IO 口數(shù)量剛好而且 Proteus 自帶的 51 模型仿真速度快、兼容性好。用 STM32 當然也能做但你會花大量時間在時鐘樹配置和 HAL 庫移植上而仿真圖里 STM32 模型加載慢、外設(shè)行為偶爾和實物有差異反而增加調(diào)試成本。對于課程設(shè)計或方案驗證階段51 加 Proteus 是性價比最高的組合。通常的做法是先用 Proteus 把邏輯跑通確認狀態(tài)機沒問題再考慮要不要換平臺做實物。2.3 Proteus 仿真圖里必須有的幾個部分仿真圖不是隨便擺幾個元件就行。一個能跑通、能演示的交通燈仿真至少包含單片機最小系統(tǒng)晶振、復(fù)位電路、紅黃綠 LED 燈組每個方向一組共四組或兩組、兩位數(shù)碼管用于倒計時顯示、按鍵輸入模擬車流檢測或緊急模式、以及必要的限流電阻。如果要做車流檢測可以用按鍵手動觸發(fā)也可以用 Proteus 里的信號發(fā)生器模擬脈沖。使用說明里通常會標注每個元件在仿真圖里的位號和連接關(guān)系改參數(shù)時對著看就行。3. 總體設(shè)計狀態(tài)機、定時器與 IO 分配的協(xié)同3.1 用狀態(tài)機描述相位切換交通燈的本質(zhì)是一個有限狀態(tài)機。我把狀態(tài)定義為S0 東西綠、S1 東西黃、S2 全紅清空、S3 南北綠、S4 南北黃、S5 全紅清空然后回到 S0。每個狀態(tài)有一個基礎(chǔ)持續(xù)時間比如東西綠 20 秒、黃 3 秒、全紅 1 秒。智能的地方在于在 S0 和 S3 狀態(tài)里如果檢測到另一個方向有車等待且當前方向綠燈已經(jīng)達到最小綠燈時間就提前結(jié)束當前相位進入黃燈過渡。這個邏輯用 switch-case 寫最清晰每個 case 里處理倒計時和切換條件。3.2 定時器中斷怎么分配51 單片機通常用 Timer0 做 1ms 或 10ms 基準中斷在中斷里遞減倒計時變量。我一般用 Timer0 做 50ms 中斷因為交通燈的時間精度要求不高50ms 一次既不會太頻繁影響主循環(huán)又能保證倒計時顯示不閃爍。中斷服務(wù)函數(shù)里只做一件事遞減當前狀態(tài)的剩余時間減到 0 就置一個標志位主循環(huán)檢測到標志位再執(zhí)行狀態(tài)切換。這樣中斷短、主循環(huán)邏輯清晰不會出現(xiàn)中斷里做復(fù)雜判斷導(dǎo)致時序混亂的問題。3.3 IO 口分配表功能端口說明東西紅P1.0低電平點亮東西黃P1.1低電平點亮東西綠P1.2低電平點亮南北紅P1.3低電平點亮南北黃P1.4低電平點亮南北綠P1.5低電平點亮數(shù)碼管段選P0共陰數(shù)碼管數(shù)碼管位選P2.0/P2.1兩位倒計時車流檢測輸入P3.2/P3.3按鍵模擬低有效緊急模式按鍵P3.4低有效這個分配不是唯一的但原則是LED 和數(shù)碼管分開端口避免掃描時互相干擾檢測輸入用外部中斷引腳或普通 IO 都行用普通 IO 輪詢更簡單。使用說明里會給出完整的引腳對應(yīng)關(guān)系改仿真圖時照著連即可。4. 關(guān)鍵模塊實現(xiàn)從倒計時到智能延時的代碼落地4.1 定時器初始化與中斷服務(wù)#include reg52.h unsigned char phase 0; // 當前相位 0-5 unsigned int remain_ms 0; // 當前相位剩余時間(ms) bit time_up 0; // 倒計時歸零標志 void Timer0_Init(void) { TMOD 0xF0; TMOD | 0x01; // Timer0 模式116位定時 TH0 (65536 - 50000) / 256; // 50ms 定時初值(12MHz晶振) TL0 (65536 - 50000) % 256; ET0 1; // 允許 Timer0 中斷 EA 1; // 開總中斷 TR0 1; // 啟動 Timer0 } void Timer0_ISR(void) interrupt 1 { TH0 (65536 - 50000) / 256; // 重裝初值 TL0 (65536 - 50000) % 256; if (remain_ms 0) { remain_ms - 50; // 每次中斷減 50ms if (remain_ms 0) { time_up 1; // 置位倒計時結(jié)束標志 } } }這段代碼里remain_ms是當前相位的剩余毫秒數(shù)主循環(huán)根據(jù)time_up標志決定是否切換狀態(tài)。50ms 中斷意味著倒計時精度是 50ms對于交通燈來說完全夠用。注意TH0和TL0的計算12MHz 晶振下一個機器周期是 1us50ms 需要 50000 個機器周期所以初值是 65536 減去 50000。如果你用的是 11.0592MHz 晶振初值要重新算使用說明里一般會注明晶振頻率。4.2 相位切換與智能延時邏輯#define MIN_GREEN 10000 // 最小綠燈 10s #define MAX_GREEN 30000 // 最大綠燈 30s #define YELLOW_TIME 3000 // 黃燈 3s #define ALL_RED 1000 // 全紅 1s void phase_switch(void) { switch(phase) { case 0: // 東西綠 if (time_up || (remain_ms MIN_GREEN car_wait_ns())) { phase 1; remain_ms YELLOW_TIME; time_up 0; } break; case 1: // 東西黃 if (time_up) { phase 2; remain_ms ALL_RED; time_up 0; } break; case 2: // 全紅 if (time_up) { phase 3; remain_ms MAX_GREEN; time_up 0; } break; case 3: // 南北綠 if (time_up || (remain_ms MIN_GREEN car_wait_ew())) { phase 4; remain_ms YELLOW_TIME; time_up 0; } break; case 4: // 南北黃 if (time_up) { phase 5; remain_ms ALL_RED; time_up 0; } break; case 5: // 全紅 if (time_up) { phase 0; remain_ms MAX_GREEN; time_up 0; } break; } }這段邏輯的關(guān)鍵在case 0和case 3當剩余時間小于等于最小綠燈時間并且另一個方向有車等待時提前結(jié)束當前綠燈。car_wait_ns()和car_wait_ew()是檢測函數(shù)讀 P3.2 和 P3.3 的電平。注意remain_ms MIN_GREEN這個條件它保證綠燈至少亮了 10 秒避免頻繁切換導(dǎo)致路口混亂。使用說明里會解釋每個宏定義的含義改參數(shù)時直接改宏就行。4.3 數(shù)碼管倒計時顯示unsigned char code seg_code[] {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; void display(unsigned int ms) { unsigned char sec ms / 1000; P2_0 0; P2_1 1; // 選通十位 P0 seg_code[sec / 10]; delay_ms(2); P0 0x00; // 消影 P2_0 1; P2_1 0; // 選通個位 P0 seg_code[sec % 10]; delay_ms(2); P0 0x00; }數(shù)碼管顯示用動態(tài)掃描兩位分別顯示秒數(shù)的十位和個位。seg_code是共陰數(shù)碼管的段碼表如果你的仿真圖用的是共陽數(shù)碼管段碼要取反。delay_ms(2)是位間延時太長會閃爍太短會亮度不均。使用說明里會標注數(shù)碼管型號照著改段碼表就行。5. 避坑與排查仿真跑不通時先查這五個地方5.1 現(xiàn)象LED 全亮或全滅不受程序控制原因通常是 IO 口初始狀態(tài)不對。51 單片機復(fù)位后 P1、P2、P3 都是高電平如果你的 LED 是低電平點亮復(fù)位后應(yīng)該全滅。但如果仿真圖里 LED 接的是高電平點亮那復(fù)位后就會全亮。解決方法是檢查仿真圖里 LED 的接法確認是共陽還是共陰然后在程序開頭把對應(yīng)端口置為正確電平。我一般會在main函數(shù)第一行就把所有 LED 端口置高滅燈狀態(tài)再進入主循環(huán)。5.2 現(xiàn)象倒計時不走或跳變先看晶振頻率設(shè)置。Proteus 仿真圖里單片機的晶振頻率屬性必須和程序里定時器初值計算時用的頻率一致。如果程序按 12MHz 算仿真圖里設(shè)的是 11.0592MHz倒計時就會偏慢約 8%。另一個常見原因是中斷里沒有重裝初值或者重裝初值的語句被其他邏輯跳過。檢查Timer0_ISR里TH0和TL0是否在每次中斷都重新賦值。5.3 現(xiàn)象相位切換卡死在某一個狀態(tài)多半是time_up標志沒有被清零。在phase_switch里每次切換狀態(tài)后都要把time_up 0否則下一次進入這個狀態(tài)時time_up還是 1會立刻觸發(fā)切換看起來就像卡死或亂跳。另外檢查remain_ms是否在切換時被正確賦值如果賦了一個 0那time_up會立刻置位狀態(tài)機就會空轉(zhuǎn)。5.4 現(xiàn)象按鍵檢測不靈敏或誤觸發(fā)按鍵沒有消抖。機械按鍵在按下和釋放時會有 5-10ms 的抖動如果直接在主循環(huán)里讀電平可能會一次按下被識別成多次。解決方法是加 10ms 左右的延時確認或者用定時器做狀態(tài)機消抖。在仿真里Proteus 的按鍵模型抖動比實物小但養(yǎng)成消抖習(xí)慣沒壞處。我一般會在檢測函數(shù)里加delay_ms(10)后再讀一次電平。5.5 現(xiàn)象Proteus 仿真報錯或元件不工作先看仿真圖里有沒有“電源”和“地”符號。Proteus 的數(shù)字電路仿真需要明確的電源網(wǎng)絡(luò)LED 和數(shù)碼管的公共端必須接電源或地。另外51 單片機的 EA 引腳在仿真里通常默認接高電平如果你用的是外部程序存儲器EA 要接地但課程設(shè)計一般用內(nèi)部程序存儲器EA 接高電平即可。使用說明里會給出完整的仿真圖截圖和網(wǎng)絡(luò)標號對照檢查就行。6. 驗收與答辯怎么證明這套控制器真的“智能”驗收不是看燈亮不亮而是看邏輯對不對。我一般會準備三組測試用例第一組兩個方向都沒有車觀察是否按最大綠燈時間運行驗證基礎(chǔ)配時第二組東西方向有車、南北方向沒車觀察東西綠燈是否延長到最大時間南北綠燈是否縮短到最小時間第三組東西綠燈期間突然觸發(fā)南北檢測觀察是否在最小綠燈結(jié)束后立即切換。每組用例記錄相位切換時間和倒計時顯示和設(shè)計值對比誤差在 5% 以內(nèi)就算合格。答辯時老師最愛問兩個問題一是“你的智能體現(xiàn)在哪里”二是“如果兩個方向同時有車怎么辦”。第一個問題用第二組測試用例回答展示動態(tài)調(diào)整前后的時間對比。第二個問題要說明優(yōu)先級策略通常按“先到先服務(wù)”或“主干道優(yōu)先”處理如果同時到達就按固定順序輪轉(zhuǎn)避免死鎖。你可以提前在代碼里加一個priority變量答辯時現(xiàn)場改參數(shù)演示。還有一個技巧把 Proteus 仿真速度調(diào)慢讓老師能看清相位切換過程。在 Proteus 的“系統(tǒng)”菜單里把“動畫速度”調(diào)到 50% 左右倒計時和燈切換會變得肉眼可追蹤。另外使用說明里通常有“參數(shù)速查表”把最小綠燈、最大綠燈、黃燈時間、全紅時間列在一起答辯時直接指著表說“這些參數(shù)都可以在代碼開頭的宏定義里改”比翻代碼找半天強得多。從那以后我每次做仿真類項目都會在驗收前把三組測試用例跑一遍截圖存檔答辯時直接翻圖說話。希望幫到你。本文還有配套的精品資源點擊獲取