TCS3200顏色識(shí)別與LCD1602顯示工程解析)
簡(jiǎn)介這是一套基于C51單片機(jī)的TCS3200顏色識(shí)別與LCD1602顯示完整軟件源碼面向正在學(xué)習(xí)單片機(jī)接口編程、傳感器數(shù)據(jù)采集與顯示驅(qū)動(dòng)的開發(fā)者尤其適合課程設(shè)計(jì)或電子競(jìng)賽的軟硬件聯(lián)調(diào)階段。代碼中main函數(shù)完成系統(tǒng)初始化后開啟總中斷INT0下降沿觸發(fā)計(jì)數(shù)定時(shí)器T0負(fù)責(zé)計(jì)時(shí)利用TCS3200輸出頻率與顏色分量的關(guān)系計(jì)算RGB數(shù)值再通過LCD1602顯示出對(duì)應(yīng)顏色信息中斷服務(wù)與主循環(huán)配合緊密適合理解傳感器測(cè)頻與顯示刷新的完整流程。壓縮包共16個(gè)文件容量?jī)H46KB主要包含C語言源文件、Keil工程文件uvproj/uvopt、編譯生成的HEX燒錄文件、LST列表文件及工程備份能夠直接打開工程重新編譯也可用HEX文件燒錄驗(yàn)證。另有PLG、M51等輔助文件便于查看鏈接映射和項(xiàng)目構(gòu)建日志。目前已有278人學(xué)習(xí)下載對(duì)于需要快速搭建TCS3200顏色識(shí)別顯示系統(tǒng)的讀者具有直接參考和復(fù)用價(jià)值。1. 手頭這份源代碼包到底在做什么手頭這份『基于C51單片機(jī)設(shè)計(jì)的TCS3200顏色識(shí)別LCD10602顯示的軟件源代碼.zip』是典型的單片機(jī)課程設(shè)計(jì)或軟件綜合實(shí)踐選題用8051系列單片機(jī)讀一顆顏色傳感器再把識(shí)別結(jié)果送到一塊字符屏上。標(biāo)題里的LCD10602按市面上能買到的器件習(xí)慣對(duì)應(yīng)的是最常見的LCD1602字符液晶下文的驅(qū)動(dòng)和接線都以LCD1602為準(zhǔn)。整包源碼的價(jià)值不在傳感器本身的電路而在C51工程里怎么把TCS3200輸出的頻率信號(hào)量化成RGB三個(gè)數(shù)再把這個(gè)過程嵌進(jìn)一個(gè)不卡死的顯示循環(huán)。適合正在做課程設(shè)計(jì)、剛接觸Keil C51的讀者也適合想把手頭的C51開發(fā)板快速接到TCS3200的老手。2. TCS3200顏色傳感器的頻率輸出原理與C51定時(shí)器計(jì)數(shù)2.1 引腳配置S0/S1決定頻率縮放S2/S3決定通道顏色TCS3200內(nèi)部把光電二極管排列成8x8陣列等效為16個(gè)紅色、16個(gè)綠色、16個(gè)藍(lán)色、16個(gè)無濾光通道。芯片對(duì)外只有一個(gè)頻率輸出引腳OUT頻率高低與所選通道的光強(qiáng)近似線性。選哪個(gè)通道由S2/S3兩個(gè)引腳決定頻率縮放倍率由S0/S1兩個(gè)引腳決定這四根線正好接在C51的四個(gè)普通IO上。S0S1輸出頻率縮放適用場(chǎng)景LL關(guān)斷不建議用于測(cè)量LH2%弱光環(huán)境、需要更精細(xì)的低頻測(cè)量HL20%常規(guī)室內(nèi)光源推薦起始檔HH100%強(qiáng)光、高速采樣S2S3選通通道LL紅色濾波通道HH綠色濾波通道LH藍(lán)色濾波通道HL無濾波通帶通道我一般把S0/S1設(shè)為20%檔起步。原因很直接C51的T0計(jì)數(shù)器是16位如果直接用100%檔強(qiáng)光下輸出頻率可達(dá)數(shù)百kHz10ms窗口內(nèi)計(jì)數(shù)可能逼近65535容易溢出2%檔又會(huì)讓暗色物體讀數(shù)太小。20%檔在室內(nèi)光照下通常落在10kHz到120kHz之間10ms窗口計(jì)數(shù)范圍是100到1200分辨率足夠判斷顏色大類。接線方式也很固定TCS3200的OUT接P3.4也就是T0外部計(jì)數(shù)輸入引腳S0接P1.0S1接P1.1S2接P1.2S3接P1.3。這樣做的原因是T0專門負(fù)責(zé)數(shù)脈沖T1專門負(fù)責(zé)開時(shí)間窗口兩個(gè)定時(shí)器分工不占用CPU計(jì)算頻率。2.2 T0外部計(jì)數(shù)加T1時(shí)間窗口的測(cè)量代碼C51測(cè)量頻率的常見做法是“定時(shí)計(jì)數(shù)”T1產(chǎn)生一個(gè)固定寬度的時(shí)間窗口T0在這個(gè)窗口內(nèi)對(duì)外部脈沖計(jì)數(shù)。窗口結(jié)束時(shí)讀出TH0和TL0拼接出來的計(jì)數(shù)值再除以窗口時(shí)間就得到頻率。下面這段是可運(yùn)行的骨架我會(huì)把它獨(dú)立成tcs3200_core.c。/* tcs3200_core.c - 適用于STC89C52/AT89S52等C51單片機(jī) */ #include reg52.h sbit S0 P1^0; sbit S1 P1^1; sbit S2 P1^2; sbit S3 P1^3; #define TCS_SCALE_20 0 /* S01,S10: 輸出頻率為原頻率的20% */ #define TCS_SCALE_2 1 /* S00,S11: 輸出頻率為原頻率的2% */ void Delay_Ms(unsigned int ms); void TCS_Init(unsigned char scale) { if (scale TCS_SCALE_20) { S0 1; S1 0; } else { S0 0; S1 1; } } unsigned int TCS_ReadChannel(unsigned char ch) { unsigned int cnt; /* 切換光電二極管通道: 0紅, 1綠, 2藍(lán), 3通帶 */ switch (ch) { case 0: S2 0; S3 0; break; /* 紅 */ case 1: S2 1; S3 1; break; /* 綠 */ case 2: S2 0; S3 1; break; /* 藍(lán) */ default: S2 1; S3 0; break; /* 無濾波 */ } Delay_Ms(2); /* 通道切換后留2ms讓輸出穩(wěn)定 */ /* T0作16位外部計(jì)數(shù), T1作16位定時(shí)器, 開一個(gè)10ms窗口 */ TMOD 0x15; /* 0001 0101: T116位定時(shí)器, T016位計(jì)數(shù)器 */ TH0 0; TL0 0; /* 11.0592MHz晶振, 12時(shí)鐘/機(jī)器周期, 10ms窗口需9216個(gè)計(jì)數(shù) */ TH1 (65536 - 9216) / 256; TL1 (65536 - 9216) % 256; TR0 1; TR1 1; while (TF1 0); /* 等待T1溢出, 10ms窗口結(jié)束 */ TR0 0; TR1 0; TF1 0; cnt ((unsigned int)TH0 8) | TL0; return cnt; }這段代碼的操作邏輯很直觀先切換通道等輸出穩(wěn)定清空T0給T1裝上10ms窗口的初值然后同時(shí)啟動(dòng)兩個(gè)定時(shí)器。T0在P3.4引腳上數(shù)外部脈沖T1在內(nèi)部按機(jī)器周期跑。T1溢出時(shí)窗口結(jié)束停止計(jì)數(shù)后把TH0和TL0拼成一個(gè)16位無符號(hào)整數(shù)返回。參數(shù)上要注意三個(gè)點(diǎn)。第一TMOD0x15的低4位0101表示T0是16位計(jì)數(shù)器高4位0001表示T1是16位定時(shí)器如果把T0配成定時(shí)器而T1配成計(jì)數(shù)器讀出來會(huì)是完全無規(guī)律的數(shù)字。第二9216這個(gè)數(shù)字只在11.0592MHz晶振、10ms窗口下成立換成12MHz晶振時(shí)要改成10000因?yàn)?2MHz的機(jī)器周期正好是1us。第三返回的cnt除以0.01秒就是OUT引腳的實(shí)際頻率單位Hz如果要做絕對(duì)光譜分析再除以0.2得到傳感器的原始頻率但單純比顏色時(shí)直接用cnt就夠。2.3 三通道輪詢與RGB歸一化顏色識(shí)別最少要讀紅綠藍(lán)三個(gè)通道。我一般把三個(gè)讀數(shù)放進(jìn)一個(gè)結(jié)構(gòu)體里然后按最大值歸一化到0到255這樣光源強(qiáng)弱變化時(shí)顏色比例仍然穩(wěn)定。typedef struct { unsigned char r; unsigned char g; unsigned char b; } RGB_Value; void TCS_ReadRGB(RGB_Value *rgb) { unsigned int cr, cg, cb, cmax; cr TCS_ReadChannel(0); cg TCS_ReadChannel(1); cb TCS_ReadChannel(2); /* 取三通道最大值做歸一化基準(zhǔn), 防止暗場(chǎng)下三個(gè)數(shù)都偏小 */ cmax (cr cg) ? cr : cg; cmax (cb cmax) ? cb : cmax; if (cmax 0) cmax 1; rgb-r (unsigned char)((unsigned long)cr * 255 / cmax); rgb-g (unsigned char)((unsigned long)cg * 255 / cmax); rgb-b (unsigned char)((unsigned long)cb * 255 / cmax); }為什么不除以固定的校準(zhǔn)值而用動(dòng)態(tài)最大值因?yàn)門CS3200讀到的絕對(duì)計(jì)數(shù)值受環(huán)境光強(qiáng)度影響很大同一個(gè)紅色物體在白天和晚上可能差三倍但紅綠藍(lán)之間的相對(duì)比例變化不大。按當(dāng)天當(dāng)前光源下的最大值歸一化相當(dāng)于把亮度信息丟掉只保留色度比例這對(duì)識(shí)別“偏紅還是偏綠”最有效。需要提醒的是三個(gè)通道不是同一時(shí)刻采樣的紅綠藍(lán)各占一個(gè)10ms窗口再加上通道切換延時(shí)一組數(shù)據(jù)實(shí)際跨越了大約40ms。對(duì)靜止物體這沒有問題對(duì)快速移動(dòng)的物體三通道會(huì)受運(yùn)動(dòng)模糊影響。更穩(wěn)的做法是每個(gè)通道連續(xù)讀三次取平均或者把窗口時(shí)間從10ms拉到50ms后者適合弱光但會(huì)降低刷新率。3. LCD1602LCD10602在C51上的4位驅(qū)動(dòng)與顯示格式3.1 4線接線與引腳分配LCD1602的標(biāo)準(zhǔn)接口有16個(gè)引腳但控制字符顯示只需要數(shù)據(jù)線、RS、RW、E四類。8位模式要占8根數(shù)據(jù)線4位模式只占4根而且速度差異在字符屏上完全感受不到。我一般直接省略RW把它接地這樣能再省一個(gè)IO只保留RS和E兩個(gè)控制腳。這里采用一組比較省線的分配LCD的D4到D7接P0.4到P0.7RS接P2.5RW接P2.6E接P2.7。P0口是開漏輸出必須外接10k上拉排阻否則高電平驅(qū)動(dòng)能力不足字符會(huì)忽亮忽暗。背光LED的限流電阻用一個(gè)100歐到330歐的貼片電阻視供電電壓而定。LCD引腳接C51引腳說明D4~D7P0.4~P0.74位數(shù)據(jù)線高4位有效RSP2.50寫命令1寫數(shù)據(jù)RWP2.6只寫模式接地EP2.7下降沿鎖存數(shù)據(jù)選4位模式還有一層原因C51的P0口經(jīng)常同時(shí)掛外部存儲(chǔ)器或其它外設(shè)8位全占用會(huì)讓系統(tǒng)擴(kuò)展性變得很差。4位模式只占半個(gè)端口剩下的引腳可以留給按鍵、蜂鳴器或者串口。3.2 4位初始化時(shí)序和讀寫函數(shù)LCD1602的4位初始化有一個(gè)固定套路先發(fā)三次0x30讓液晶無論處于什么狀態(tài)都回到8位模式的初始位置第四次發(fā)0x20切換成4位模式。之后才能用完整的命令字節(jié)配置顯示參數(shù)。我把它寫成一個(gè)單獨(dú)的lcd1602.c核心函數(shù)如下。/* lcd1602.c - 4位總線驅(qū)動(dòng), 適用于STC89C52/AT89S52 */ #include reg52.h #define LCD_DB P0 sbit LCD_RS P2^5; /* 寄存器選擇: 0命令 1數(shù)據(jù) */ sbit LCD_RW P2^6; /* 讀寫選擇: 0寫 1讀 */ sbit LCD_E P2^7; /* 使能脈沖 */ void Delay_Ms(unsigned int ms); static void LCD_WriteHalf(unsigned char dat) { LCD_DB (LCD_DB 0x0F) | (dat 0xF0); /* 只改高4位 */ LCD_E 1; Delay_Ms(1); LCD_E 0; } static void LCD_WriteByte(unsigned char dat) { LCD_WriteHalf(dat); /* 先高4位 */ LCD_WriteHalf(dat 4); /* 后低4位 */ } void LCD_WriteCmd(unsigned char cmd) { LCD_RS 0; LCD_RW 0; LCD_WriteByte(cmd); } void LCD_WriteData(unsigned char dat) { LCD_RS 1; LCD_RW 0; LCD_WriteByte(dat); } void LCD_Init(void) { Delay_Ms(20); LCD_RS 0; LCD_RW 0; /* 三次0x30握手進(jìn)入4位模式 */ LCD_WriteHalf(0x30); Delay_Ms(5); LCD_WriteHalf(0x30); Delay_Ms(1); LCD_WriteHalf(0x30); LCD_WriteHalf(0x20); /* 正式切到4位 */ LCD_WriteCmd(0x28); /* 4位, 2行, 5x7點(diǎn)陣 */ LCD_WriteCmd(0x0C); /* 開顯示, 不顯示光標(biāo) */ LCD_WriteCmd(0x01); /* 清屏 */ LCD_WriteCmd(0x06); /* 地址自動(dòng)加一 */ Delay_Ms(2); }LCD_WriteHalf每次只向P0的高4位寫數(shù)據(jù)低4位用原來的值兜住避免影響同一端口上其它外設(shè)。寫命令和寫數(shù)據(jù)唯一的區(qū)別就是RS的電平在命令狀態(tài)時(shí)RS拉低在數(shù)據(jù)狀態(tài)時(shí)RS拉高。E引腳用下降沿鎖存所以先拉高再拉低形成一個(gè)完整的使能脈沖。初始化時(shí)前三次0x30看起來是同一個(gè)值實(shí)際是在逐步確認(rèn)8位模式下的同步邊界。如果直接發(fā)0x28液晶在上電時(shí)序不完整時(shí)可能停留在2行模式的某一側(cè)導(dǎo)致后面每行只顯示半個(gè)字符。0x28把我前面發(fā)送的半個(gè)字節(jié)拆成兩次0x30中的一次然后在4位模式下正確配對(duì)。3.3 把RGB值轉(zhuǎn)成顏色名稱的映射判據(jù)歸一化之后接下來要做的不是打印三個(gè)數(shù)字而是給出一個(gè)人類能讀的顏色名。判據(jù)基于一個(gè)簡(jiǎn)單事實(shí)純紅色物體的r值會(huì)明顯大于g和b純綠色物體的g值會(huì)明顯大于另外兩個(gè)藍(lán)色同理。亮度很低的物體一律歸為黑色三個(gè)值接近且都偏大的歸為白色或灰色。unsigned char code *MapColor(unsigned char r, unsigned char g, unsigned char b) { unsigned char max; max (r g) ? r : g; max (b max) ? b : max; if (max 60) return BLACK; /* 三通道接近且都大于180, 視為白或淺灰 */ if (max 180 max - r 30 max - g 30 max - b 30) return WHITE; /* 用整數(shù)比較代替浮點(diǎn), 避免C51的浮點(diǎn)庫開銷 */ if (r max (unsigned int)g * 10 (unsigned int)max * 6 (unsigned int)b * 10 (unsigned int)max * 6) return RED; if (g max (unsigned int)r * 10 (unsigned int)max * 6 (unsigned int)b * 10 (unsigned int)max * 6) return GREEN; if (b max (unsigned int)r * 10 (unsigned int)max * 6 (unsigned int)g * 10 (unsigned int)max * 6) return BLUE; return MIX; }這里的閾值6/10表示次要通道不超過主通道的60%是室內(nèi)光源下比較寬松的判斷。如果你把物體放在暗處60%可能會(huì)把棕色誤判成紅色這時(shí)把閾值收緊到45%左右。比較時(shí)用g * 10 max * 6而不是g max * 0.6是因?yàn)镃51的浮點(diǎn)運(yùn)算要額外鏈接浮點(diǎn)庫在8位機(jī)上會(huì)拖慢顯示刷新。值得注意的是上面函數(shù)返回的字符串常量在C51里默認(rèn)放在code段所以返回類型寫成unsigned char code *。如果你的Keil工程報(bào)“指針類型不匹配”多半是因?yàn)樽址环旁赾ode段而函數(shù)聲明成了普通的char *這是C51和標(biāo)準(zhǔn)C差異最明顯的地方。4. 完整軟件工程的文件劃分與Keil5工程配置4.1 main.c、tcs3200.c、lcd1602.c的接口職責(zé)拿到一個(gè)軟件源代碼包先看文件劃分是否合理比先看代碼更重要。我一般會(huì)把C51工程拆成四個(gè)文件main.c負(fù)責(zé)主循環(huán)tcs3200.c負(fù)責(zé)傳感器采樣lcd1602.c負(fù)責(zé)顯示delay.c提供延時(shí)。頭文件只放函數(shù)聲明和宏定義不放全局變量定義。文件對(duì)外接口內(nèi)部職責(zé)main.cmain()初始化、輪詢、顯示刷新節(jié)奏tcs3200.cTCS_Init、TCS_ReadRGB通道切換、定時(shí)器窗口、歸一化lcd1602.cLCD_Init、LCD_ShowRGB4位驅(qū)動(dòng)、字符格式化delay.cDelay_Ms毫秒延時(shí)軟實(shí)現(xiàn)這樣拆分有個(gè)看得見的好處調(diào)試顏色識(shí)別時(shí)不需要翻液晶代碼調(diào)顯示時(shí)不需要碰傳感器。壓縮包里如果只有一個(gè)巨長(zhǎng)的main.c建議先按這個(gè)結(jié)構(gòu)拆開后再去改算法否則每一個(gè)小改動(dòng)都要重新編譯整個(gè)文件驗(yàn)證周期會(huì)拖長(zhǎng)。4.2 主循環(huán)的采樣節(jié)奏與刷新節(jié)流主循環(huán)不需要寫成復(fù)雜狀態(tài)機(jī)顏色識(shí)別和顯示天然是順序關(guān)系。為了讓屏幕上的數(shù)字可讀而不是飛快跳變我一般把每次完整刷新的間隔控制在300ms左右也就是大約3幀每秒。#include tcs3200.h #include lcd1602.h void main(void) { RGB_Value rgb; unsigned char code *color_name; Delay_Ms(100); LCD_Init(); TCS_Init(TCS_SCALE_20); LCD_WriteStr(0, 0, TCS3200 Ready); Delay_Ms(500); LCD_WriteCmd(0x01); Delay_Ms(2); while (1) { TCS_ReadRGB(rgb); color_name MapColor(rgb.r, rgb.g, rgb.b); LCD_ShowRGB(rgb, color_name); Delay_Ms(300); } }這段循環(huán)里TCS_ReadRGB本身會(huì)消耗三組10ms窗口加兩次通道切換延時(shí)大概40ms剩下的300ms是純顯示節(jié)流。如果你把Delay_Ms去掉屏幕會(huì)以每秒25幀以上刷新但由于LCD1602本身沒有硬件緩沖快速刷新時(shí)字符會(huì)閃爍人的眼睛反而不舒服。主循環(huán)里沒有中斷這是刻意為之。TCS3200的采樣靠定時(shí)器同步等待LCD1602的寫操作也依賴延時(shí)整個(gè)流程是順序的。如果給采樣加中斷反而會(huì)出現(xiàn)中斷里改通道、主循環(huán)里讀計(jì)數(shù)值的競(jìng)態(tài)問題。對(duì)這個(gè)項(xiàng)目來說輪詢比中斷更不容易踩坑。4.3 Keil5中添加C51芯片包與鏈接器控制文件Keil5默認(rèn)安裝時(shí)只勾選ARM工具鏈的話新建工程會(huì)找不到C51芯片。解決路徑不是重新下載整個(gè)IDE而是打開Pack Installer找到對(duì)應(yīng)芯片廠商的設(shè)備包后點(diǎn)擊Install或者在安裝Keil5時(shí)勾上C51 Compiler組件。同一臺(tái)機(jī)器上同時(shí)裝C51和ARMSTM32兩套工具鏈?zhǔn)峭耆R姷乃鼈児灿靡粋€(gè)uVision界面工程文件后綴都是.uvprojx創(chuàng)建工程時(shí)靠芯片型號(hào)區(qū)分編譯工具鏈。芯片選型后在Options for Target里有兩處必須確認(rèn)。第一是Memory Model一般選Small變量放data區(qū)這樣訪問速度快但data區(qū)總共只有128字節(jié)如果程序里RGB緩沖區(qū)不多Small完全夠用。第二是Code Rom Size選Large 64K避免鏈接器因?yàn)榇a超過默認(rèn)段而產(chǎn)生L55類錯(cuò)誤。Keil C51會(huì)為每個(gè)工程自動(dòng)生成一個(gè)鏈接器控制文件STARTUP.A51它負(fù)責(zé)在main之前把可重定位段清零、設(shè)置堆棧指針。如果程序里用了XDATA的大數(shù)組鏈接器會(huì)在映射文件.M51里報(bào)告XDATA段的起始地址和大小調(diào)試RAM溢出時(shí)先看這個(gè)文件。不要手動(dòng)去改STARTUP.A51里的堆棧位置除非你確定XDATA或IDATA的地址沖突改了反而容易把中斷向量頂?shù)簟?. 白平衡校準(zhǔn)與三處易錯(cuò)點(diǎn)定位5.1 黑白兩點(diǎn)標(biāo)定與歸一化公式動(dòng)態(tài)最大值歸一化能扛住光源強(qiáng)度波動(dòng)但扛不住光源色溫偏移。白熾燈偏黃、LED偏藍(lán)同樣一張白紙?jiān)诓煌庠聪伦x到的RGB比例會(huì)變。如果要讓顏色名稱穩(wěn)定我會(huì)上電后先做黑白兩點(diǎn)校準(zhǔn)白參考采白紙黑參考采鏡頭蓋或直接把傳感器貼近不透光物體。unsigned int cal_white[3] {200, 200, 200}; /* 白參考 */ unsigned int cal_black[3] {10, 10, 10}; /* 黑參考 */ unsigned char ScaleChannel(unsigned int raw, unsigned int white, unsigned int black) { unsigned long v; if (raw black) return 0; v (unsigned long)(raw - black) * 255 / (white - black); if (v 255) v 255; return (unsigned char)v; }ScaleChannel做的是線性拉伸把黑色讀數(shù)當(dāng)作0白色讀數(shù)當(dāng)作255中間的讀數(shù)按比例映射。這樣處理后即使換了一個(gè)色溫完全不同的環(huán)境只要重新校準(zhǔn)白參考和黑參考顏色名稱映射就能保持不變。校準(zhǔn)值建議放在main函數(shù)里在開機(jī)時(shí)提示用戶把白紙貼上傳感器延時(shí)兩秒后讀取并暫存。5.2 采樣穩(wěn)定不下來怎么辦如果屏幕上的數(shù)值在固定場(chǎng)景下仍然跳變優(yōu)先懷疑三個(gè)地方環(huán)境光頻閃、采樣窗口太短、物體距離遠(yuǎn)近變化。熒光燈每半個(gè)工頻周期亮度變化一次10ms窗口正好可能橫跨暗段和亮段解決辦法是把窗口從10ms改成50ms讓讀數(shù)覆蓋多個(gè)頻閃周期。距離問題更常見TCS3200沒有透鏡傳感器表面和被測(cè)物體距離超過2cm后雜散光會(huì)明顯抬高三個(gè)通道我一般盡量把傳感器貼在物體表面或者用黑色遮光套管固定1cm左右的工作距離。讀數(shù)徹底為零時(shí)先檢查OUT有沒有接到P3.4而不是P3.5再確認(rèn)S0/S1組合不是關(guān)斷狀態(tài)。5.3 字符顯示異常的定位順序LCD1602顯示亂碼或白屏按順序排查先量E引腳有沒有下降沿再確認(rèn)P0上拉到VCC的電阻已經(jīng)接好最后看初始化時(shí)序里三次0x30是否被精簡(jiǎn)成一次。只發(fā)一次0x30的代碼看起來省事但遇到上電慢的液晶屏就會(huì)漏同步表現(xiàn)為第一行正常、第二行錯(cuò)位或者全是方塊。另一個(gè)常見問題是RS和E的引腳定義和實(shí)際接線不一致代碼里寫P2^5板上卻接到了P2^4這類問題看代碼看不出直接拿示波器量引腳電平最快。如果只是刷新時(shí)新字符覆蓋殘留在寫第二行前先輸出16個(gè)空格清行再把顏色名居中就不會(huì)出現(xiàn)長(zhǎng)短名交替時(shí)末尾殘留的情況。本文還有配套的精品資源點(diǎn)擊獲取