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