計詳解)
簡介這是一份面向單片機課程設(shè)計的完整PDF報告圍繞LCD12864顯示屏實現(xiàn)十字路口交通信號燈的倒計時顯示與控制。報告源自湖南大學(xué)電氣與信息工程學(xué)院本科生課程設(shè)計適合電氣、自動化及相關(guān)專業(yè)學(xué)生作為課程設(shè)計參考也可幫助入門者學(xué)習(xí)ADuC848單片機、LCD12864接口編程、定時器與中斷應(yīng)用。內(nèi)容先從開發(fā)板硬件資源分布講起給出板上資源分布圖與ADuC848簡要介紹再詳細講解LCD顯示模塊的信號連接圖與關(guān)鍵引腳說明之后給出交通信號燈系統(tǒng)的原理分析、程序流程框圖、具體程序?qū)崿F(xiàn)、操作步驟和參考文獻覆蓋信號燈倒計時、三色燈切換、緊急事件處理等需求。資源為單個PDF文檔大小約1.5MB圖文并茂目錄結(jié)構(gòu)清晰。目前已有190人瀏覽學(xué)習(xí)對于正在準備單片機課程設(shè)計或想快速上手LCD12864顯示與交通燈控制邏輯的讀者是一份可直接對照參考的實用資料。1. 基于LCD12864的交通信號燈課程設(shè)計先想清楚做成什么如果只看課程設(shè)計報告的標題容易把“基于LCD12864的交通信號燈”理解成流水燈加一個液晶屏。真正拉開差距的是狀態(tài)切換與LCD12864的顯示節(jié)奏是否同步紅燈倒計時不能跳秒綠燈切黃燈時不能先滅一下南北方向禁行時屏幕上要給出和燈面一致的漢字提示。這個題目最穩(wěn)的做法是用51單片機做四狀態(tài)循環(huán)用定時器產(chǎn)生1秒時基再用ST7920主控的LCD12864以并行方式顯示狀態(tài)文字和剩余秒數(shù)。下文按這個骨架展開覆蓋Proteus仿真、C51代碼、配時調(diào)節(jié)和課程設(shè)計報告的收尾適合準備做51單片機交通燈課程設(shè)計的讀者參考。2. 交通信號燈控制器系統(tǒng)結(jié)構(gòu)與LCD12864接口選型2.1 交通信號燈四狀態(tài)循環(huán)綠、黃、紅怎么排交通信號燈的核心是一個有限狀態(tài)機。課程設(shè)計里最常要求的是十字路口東西方向為主干道南北方向為支路不能出現(xiàn)兩個方向同時綠燈。按照國標習(xí)慣放行順序是東西綠燈、東西黃燈、南北綠燈、南北黃燈四個狀態(tài)循環(huán)。南北紅燈和東西紅燈只是對側(cè)狀態(tài)的鏡像不需要額外定義。狀態(tài)東西方向燈面南北方向燈面默認配時典型調(diào)節(jié)范圍S1綠燈紅燈60秒30~99秒S2黃燈紅燈5秒3~10秒S3紅燈綠燈30秒20~60秒S4紅燈黃燈5秒3~10秒表格里的默認配時不是拍腦袋寫的。主干道車流大東西綠燈要比南北綠燈長黃燈時間由車速和路口寬度決定一般不低于3秒。答辯時如果老師問“為什么南北紅燈也是60秒”回答是南北紅燈時長等于東西綠燈加?xùn)|西黃燈也就是對側(cè)放行和緩沖的總和。在這個狀態(tài)下單片機要輸出6路信號正好占用一個8位IO口。常見做法是把6個LED接到P1口的低6位用低電平點亮的方式驅(qū)動。51單片機IO口的高電平驅(qū)動能力弱P1口灌電流能力比拉電流好所以LED陰極接IO口、陽極經(jīng)限流電阻接電源這是51單片機硬件設(shè)計里最基本的一條。sbit EW_GREEN P1^0; // 東西綠燈 sbit EW_YELLOW P1^1; // 東西黃燈 sbit EW_RED P1^2; // 東西紅燈 sbit NS_GREEN P1^3; // 南北綠燈 sbit NS_YELLOW P1^4; // 南北黃燈 sbit NS_RED P1^5; // 南北紅燈這段定義把燈面和IO位一一對應(yīng)。后面狀態(tài)機切換時只需要給P1口寫入一個字節(jié)就能同時改變6個燈。例如S1狀態(tài)對應(yīng)二進制1110 1011也就是P1 0xEB但不能把低電平信號理解成“熄滅”上面已經(jīng)說過是灌電流點亮所以在實際板子上還要用萬用表確認VCC到LED限流電阻這一路是否接通。2.2 LCD12864選型帶字庫ST7920與并行/串行兩種接法LCD12864的模塊型號很多課程設(shè)計里要優(yōu)先選帶中文字庫的ST7920主控模塊。ST7920內(nèi)部有GB2312中文漢字庫寫漢字時直接發(fā)送兩個字節(jié)的編碼不需要自己準備12864取模表。另一類以KS0108為控制器的LCD12864沒有字庫要想顯示狀態(tài)文字就得取模課堂演示時既要查編碼又要調(diào)試對齊沒必要給自己加難度。ST7920同時支持并行和串行接口。并行又分8位和4位串行是SPI式三線接口。如果是在Proteus仿真51單片機建議直接用8位并行如果是想把板子做得簡潔、少占用IO口再用串行。LCD12864引腳功能8位并行接法1VSS接GND2VCC接5V3V0接10k電位器中點調(diào)整對比度4RS接P2.55RW接P2.66E接P2.77~14DB0~DB7接P0口15LEDA接5V16LEDK接GND這里有個容易踩的坑DB0~DB7接到P0口后必須給P0口加上拉排阻否則LCD12864在仿真和實物上都可能出現(xiàn)花屏或白屏。P0口是開漏輸出內(nèi)部沒有上拉不接10k排阻時高電平不穩(wěn)定。Proteus仿真時很多人忽略這一點屏幕能亮但字亂跳原因就在這里。串行接法則簡單很多把模塊背面的PSB跳線接到低電平或直接接地SCLK接P2.5SID接P2.6CS接P2.7數(shù)據(jù)線和控制線共用最多占3個IO口。串行的缺點是初始化代碼和時序比較復(fù)雜調(diào)試時先看SCLK的波形是否符合要求比較費時間。如果報告篇幅有限并行接法寫起來更直觀如果板子引腳不夠用串行接法更合適。2.3 Proteus最小系統(tǒng)用AT89C52模型代替STC89C52RC常見做法在Proteus仿真51單片機課程設(shè)計時STC89C52RC往往沒有完全對應(yīng)的元件模型最常見的做法是用AT89C52代替引腳完全兼容晶振、復(fù)位電路和程序下載方式略有差別但仿真階段不影響邏輯驗證。放置元件時晶振選11.0592MHz兩個30pF電容接晶振兩端到地復(fù)位電路用10uF電容加10k電阻P0口放一個RESPACK-8排阻拉到VCC。LCD12864在Proteus中可以用元件庫里的LGM12864它的控制器模型兼容ST7920接線和實物一致。注意不要在仿真里改動其內(nèi)部Data Format否則漢字顯示會變成亂碼。啟動仿真后如果屏幕背光亮但無內(nèi)容先檢查V0電位器是否擰到了中間位置再檢查PSB是否接高電平最后確認清屏命令后面是否給了足夠延時。3. 用C51寫LCD12864交通信號燈狀態(tài)機與顯示驅(qū)動3.1 定時器0產(chǎn)生1秒時基中斷里只做累加交通信號燈最忌諱用空循環(huán)Delay來湊時間。LED是發(fā)光器件人眼對時序抖動很敏感LCD12864刷新又需要時間如果延時函數(shù)占住CPU按鍵會失靈屏幕更新也會卡頓。正確的做法是用定時器0產(chǎn)生固定時基在主循環(huán)里只做狀態(tài)判斷和顯示刷新。以11.0592MHz晶振、12T模式為例定時器0用方式116位計數(shù)每50ms產(chǎn)生一次中斷20次中斷累計1秒。初始化代碼和中斷服務(wù)函數(shù)如下#include reg52.h #include intrins.h typedef unsigned char uchar; typedef unsigned int uint; sbit LCD_RS P2^5; sbit LCD_RW P2^6; sbit LCD_EN P2^7; #define LCD_DATA P0 bit sec_flag 0; uint t50ms 0; uchar countdown 60; void Timer0_Init(void) { TMOD 0xF0; TMOD | 0x01; // 定時器0方式116位定時 TH0 0x4C; // 11.0592MHz下50ms初值 TL0 0x00; EA 1; ET0 1; TR0 1; } void Timer0_ISR(void) interrupt 1 { TH0 0x4C; TL0 0x00; if (t50ms 20) { // 50ms * 20 1s t50ms 0; sec_flag 1; } }代碼中TMOD 0xF0是為了保留定時器1的配置不覆蓋高4位。TH0 0x4C、TL0 0x00是11.0592MHz下50ms的初值換用12MHz晶振時要重新計算。STC新固件里如果選擇6T或1T模式這個初值也會變所以不要直接抄一份代碼就認為一定準。另一種更省事的方法是打開STC-ISP工具里的定時器計算器選擇定時器0、方式1、50ms間隔它會自動生成初始化代碼和中斷函數(shù)。3.2 交通燈狀態(tài)切換倒計時歸零才換狀態(tài)有了秒脈沖后主循環(huán)里通過sec_flag判斷是不是該走一次狀態(tài)機。狀態(tài)切換不能放在中斷里做否則LCD刷新會被打斷顯示會閃爍。下面這段代碼是把狀態(tài)機單獨抽成一個函數(shù)typedef enum { EW_GREEN_NS_RED 0, EW_YELLOW_NS_RED, EW_RED_NS_GREEN, EW_RED_NS_YELLOW } TrafficState; TrafficState state EW_GREEN_NS_RED; uchar time_ew_green 60; uchar time_ew_yellow 5; uchar time_ns_green 30; uchar time_ns_yellow 5; uchar countdown 60; void Traffic_Loop(void) { if (!sec_flag) return; sec_flag 0; countdown--; switch (state) { case EW_GREEN_NS_RED: if (countdown 0) { state EW_YELLOW_NS_RED; countdown time_ew_yellow; } break; case EW_YELLOW_NS_RED: if (countdown 0) { state EW_RED_NS_GREEN; countdown time_ns_green; } break; case EW_RED_NS_GREEN: if (countdown 0) { state EW_RED_NS_YELLOW; countdown time_ns_yellow; } break; case EW_RED_NS_YELLOW: if (countdown 0) { state EW_GREEN_NS_RED; countdown time_ew_green; } break; } OutPut_LED(state); LCD_ShowTraffic(state, countdown); }這段代碼的關(guān)鍵是“倒計時歸零才換狀態(tài)”。如果寫成countdown 1顯示上會感覺少了1秒因為秒脈沖先減1再判斷換狀態(tài)。這里統(tǒng)一把countdown解釋成“當(dāng)前狀態(tài)剩余秒數(shù)”當(dāng)前狀態(tài)剩余秒數(shù)減到0再切換邏輯最順。每切換一次狀態(tài)就把下一狀態(tài)的秒數(shù)賦給countdown整個循環(huán)不需要額外變量也不容易出現(xiàn)兩個方向配時串位。OutPut_LED和LCD_ShowTraffic兩個函數(shù)需要再定義。LED輸出就是給P1口寫一個狀態(tài)字節(jié)LCD顯示則是把狀態(tài)文字和剩余秒數(shù)寫進12864。如果嫌枚舉類型在C51里占用RAM資源可以直接用uchar state效果一樣但代碼可讀性會差一些。3.3 LCD12864初始化和漢字顯示寫指令、寫數(shù)據(jù)、清屏順序ST7920的LCD12864初始化有幾條固定指令順序不能亂。先發(fā)0x30把模塊設(shè)置成8位數(shù)據(jù)接口和基本指令集再發(fā)0x0C打開顯示但關(guān)閉光標接著清屏最后設(shè)置地址自動加1。清屏命令0x01執(zhí)行時間較長指令后面最好加一點延時否則后續(xù)數(shù)據(jù)可能被吞掉。void LCD_Delay(void) { uint i 100; while (i--); } void LCD_WriteCmd(uchar cmd) { LCD_RS 0; LCD_RW 0; LCD_DATA cmd; LCD_EN 1; _nop_(); _nop_(); LCD_EN 0; } void LCD_WriteData(uchar dat) { LCD_RS 1; LCD_RW 0; LCD_DATA dat; LCD_EN 1; _nop_(); _nop_(); LCD_EN 0; } void LCD12864_Init(void) { LCD_WriteCmd(0x30); LCD_Delay(); LCD_WriteCmd(0x0C); LCD_Delay(); LCD_WriteCmd(0x01); LCD_Delay(); LCD_WriteCmd(0x06); LCD_Delay(); }這里L(fēng)CD_RS0表示寫指令LCD_RS1表示寫數(shù)據(jù)。ST7920的數(shù)據(jù)口是P0所以必須配合P0口上拉排阻否則LCD_DATA cmd寫出去的電平可能不夠高模塊識別到的狀態(tài)就錯了。_nop_()來自intrins.h目的是給E引腳維持一點高電平時間實際板上如果速度不夠穩(wěn)定可以把這個延時加大。漢字顯示時ST7920的字庫地址按行分布12864屏幕橫向分成4行每行顯示8個漢字或16個字符數(shù)字。LCD12864的行址分別是0x80、0x90、0x88、0x98row * 0x10 col是簡化的地址計算公式。寫漢字時連續(xù)寫兩個字節(jié)模塊會從內(nèi)置字庫里找到對應(yīng)漢字void LCD_ShowChinese(uchar row, uchar col, const uchar *str) { LCD_WriteCmd(0x80 row * 0x10 col); while (*str) { LCD_WriteData(*str); LCD_WriteData(*str); } } void LCD_ShowNum(uchar row, uchar col, uchar num) { uchar tens num / 10; uchar ones num % 10; LCD_ShowChinese(row, col, 倒計時); LCD_WriteCmd(0x80 row * 0x10 col 4); LCD_WriteData(0 tens); LCD_WriteData(0 ones); }使用示例如下LCD_ShowChinese(0, 0, 東西綠燈)會把“東西綠燈”顯示在第一行前四個漢字位置。注意字符串里不能出現(xiàn)ASCII和漢字混排例如“東E綠”會顯示亂碼需要把狀態(tài)提示字符串統(tǒng)一寫成漢字。倒計時數(shù)字用ASCII字符顯示占8個半角字符位置因此數(shù)字部分在col4之后正好排在“倒計時”三個字后面。4. 交通信號燈配時參數(shù)、按鍵調(diào)節(jié)與LCD12864調(diào)試排錯4.1 配時參數(shù)集中到配置區(qū)狀態(tài)機里的60、30、5是魔法數(shù)字。課程設(shè)計報告里如果只貼程序老師看不出參數(shù)依據(jù)修改時也得一個一個找。更好的做法是把配時提取成配置參數(shù)放在程序頭部運行時用變量保存#define T_EW_GREEN 60 // 東西綠燈默認秒數(shù) #define T_EW_YELLOW 5 // 東西黃燈默認秒數(shù) #define T_NS_GREEN 30 // 南北綠燈默認秒數(shù) #define T_NS_YELLOW 5 // 南北黃燈默認秒數(shù)這幾個參數(shù)直接影響交通信號燈控制器的運行節(jié)奏。黃燈秒數(shù)不建議小于3否則車輛很難剎停南北綠燈也不宜過短否則行人過馬路時間不足。按下復(fù)位鍵后程序從頭開始執(zhí)行所有狀態(tài)回到初始值倒計時也回到60秒這樣調(diào)試時最容易觀察。4.2 按鍵調(diào)節(jié)配時與消抖課程設(shè)計要求里常有一條“可調(diào)配時”。實現(xiàn)方法很多最穩(wěn)的是在P3口接兩個獨立按鍵一個切換要調(diào)的狀態(tài)一個增加秒數(shù)。按鍵掃描放在主循環(huán)里每隔10ms掃描一次連續(xù)讀到低電平才認為按下避免機械抖動造成一次按擊觸發(fā)多次調(diào)整。sbit key_mode P3^2; sbit key_add P3^3; uchar adjust_mode 0; // 0:無調(diào)整1:調(diào)東西綠燈2:調(diào)南北綠燈 void Key_Scan(void) { if (key_mode 0) { adjust_mode; if (adjust_mode 2) adjust_mode 0; } if (key_add 0) { if (adjust_mode 1 time_ew_green 99) time_ew_green; if (adjust_mode 2 time_ns_green 99) time_ns_green; } }這里的key_mode和key_add是外部IO口讀到的電平。按鍵沒有按下時IO口被上拉電阻拉高按下后接地為低所以判斷條件寫成 0。按鍵消抖不能只靠一次讀取還要在兩次讀取之間加延時或者在循環(huán)里數(shù)連續(xù)低電平的周期。更嚴格的做法是把按鍵掃描放到定時器中斷里10ms調(diào)用一次不過把顯示和按鍵都塞進中斷會讓代碼變復(fù)雜主循環(huán)掃描已經(jīng)能滿足課程設(shè)計的演示需求。4.3 LCD12864白屏、花屏、缺字的三個典型原因LCD12864的調(diào)試問題集中在屏幕顯示上。第一類是白屏無顯示先調(diào)節(jié)V0對比度電位器把V0電壓調(diào)在1V左右屏幕才會出現(xiàn)黑色像素點。如果調(diào)了對比度仍然白屏檢查PSB引腳有沒有接錯并行模式需要接高電平。第二類是花屏通常是P0口沒有上拉電阻或者上下拉排阻虛焊?;ㄆ吝€會出現(xiàn)在程序運行中原因是LCD_WriteCmd后沒有留足穩(wěn)定時間可以適當(dāng)加大LCD_Delay但不要為了趕速度把_nop_()全刪掉。第三類是缺字多半是寫入地址不對。ST7920在漢字模式下一行只能放8個漢字如果寫入位置超過第8列后面的字就到了第二行如果字符串長度超過兩行也會出現(xiàn)亂碼。檢查0x80 row * 0x10 col是否寫錯特別是第二行和第四行地址不是連續(xù)遞增第二行是0x90第四行是0x98。4.4 Proteus仿真與Multisim仿真怎么選很多課程設(shè)計題目后面帶“仿真”兩個字交通信號燈multisim仿真也是經(jīng)常被搜索的組合。Multisim適合純數(shù)字電路例如用555定時器加計數(shù)器搭交通燈但要讓LCD12864顯示漢字Multisim的8051模型和液晶庫遠不如Proteus順手。我的建議是代碼和LCD12864相關(guān)的仿真用ProteusMultisim只用來做替代方案里的數(shù)字邏輯圖。Proteus中仿真速度如果太慢可以把定時器初值改小讓1秒變成0.2秒肉眼就能看到倒計時快速下降但報告里要說明這是預(yù)演模式實際參數(shù)以代碼配置區(qū)為準。下載到實物時STC系列單片機可以用STC-ISP軟件通過串口下載。下載線接P3.0和P3.1注意下載時單片機上電順序是先點下載再上電。如果板子上有LCD12864下載時它的數(shù)據(jù)線接在P0口不影響串口下載因為P0口沒有直接連接下載信號。5. 課程設(shè)計報告收尾流程圖、仿真截圖與答辯演示技巧5.1 報告里必須有的三張圖和一張表第一張是系統(tǒng)總體框圖畫出單片機、電源、晶振復(fù)位、按鍵、LED燈組、LCD12864的箭頭關(guān)系。第二張是狀態(tài)轉(zhuǎn)移圖圖里要標出每個狀態(tài)之間的轉(zhuǎn)移條件也就是“倒計時歸零”這個觸發(fā)詞。第三張是LCD12864顯示界面示意圖把“東西綠燈 南北紅燈 倒計時60”標出來。表格則放IO分配表交代哪個引腳接LED、哪個引腳接LCD審閱老師通常先看這張表。5.2 答辯演示前按順序檢查演示容易翻車的地方不是代碼邏輯而是硬件和仿真軟件的小問題。先按復(fù)位鍵確認LCD12864清屏后再顯示文字把V0調(diào)到字跡清晰不要在屏幕上留一整行花屏狀態(tài)再演示一次按鈕配時調(diào)整讓老師看到倒計時從調(diào)整后的秒數(shù)重新開始。如果演示時用的是Proteus仿真記得把仿真速度從Faster調(diào)成合適比例否則狀態(tài)切換快得看不清。最后留一個我常用的收尾習(xí)慣演示前把配時參數(shù)表貼在代碼配置區(qū)老師問“為什么是60秒”時打開LCD12864顯示界面直接指狀態(tài)轉(zhuǎn)移圖比臨時從原理開始推導(dǎo)更穩(wěn)。本文還有配套的精品資源點擊獲取