外部存儲(chǔ)器擴(kuò)展:27C512與6264實(shí)戰(zhàn))
簡(jiǎn)介這是一份面向單片機(jī)初、中級(jí)學(xué)習(xí)者的Keil C51與Proteus仿真擴(kuò)展存儲(chǔ)器教學(xué)案例重點(diǎn)演示如何用C語(yǔ)言驅(qū)動(dòng)27C512EPROM和6264SRAM進(jìn)行外部存儲(chǔ)器擴(kuò)展覆蓋地址譯碼、總線連接、讀寫(xiě)時(shí)序等核心知識(shí)點(diǎn)。資源共18個(gè)文件壓縮包僅53KB其中既有Proteus仿真電路DSN文件、C語(yǔ)言源文件、A51啟動(dòng)代碼和編譯生成的HEX文件也有LST、OBJ、M51等中間文件還附有工程配置、備份文件與歷史記錄文件類(lèi)型較齊全便于直接打開(kāi)項(xiàng)目從源代碼、原理圖到調(diào)試信息進(jìn)行完整鏈路學(xué)習(xí)。目前已有898人學(xué)習(xí)下載。通過(guò)該案例學(xué)習(xí)者可以獲得C語(yǔ)言讀寫(xiě)外部存儲(chǔ)器的完整程序并在Proteus中實(shí)際驗(yàn)證地址譯碼、片選信號(hào)與總線時(shí)序直觀理解EPROM和SRAM在單片機(jī)系統(tǒng)中的應(yīng)用方式。作為教材配套實(shí)例它適合電子、自動(dòng)化等專(zhuān)業(yè)學(xué)生結(jié)合理論學(xué)習(xí)動(dòng)手實(shí)踐提升單片機(jī)系統(tǒng)設(shè)計(jì)與Proteus仿真調(diào)試的綜合能力。1. 項(xiàng)目整體思路拆解為什么要在Proteus里折騰外部存儲(chǔ)器先把這個(gè)項(xiàng)目說(shuō)透。很多朋友學(xué)51單片機(jī)用的都是內(nèi)部ROM和內(nèi)部RAM代碼量小、數(shù)據(jù)量少根本接觸不到外部存儲(chǔ)器擴(kuò)展。但現(xiàn)實(shí)中的產(chǎn)品尤其是做數(shù)據(jù)采集、儀器儀表、工控板卡的外部程序存儲(chǔ)器和數(shù)據(jù)存儲(chǔ)器太常見(jiàn)了。27C512是64KB的EPROM6264是8KB的SRAM這兩顆芯片在Proteus里搭一個(gè)仿真工程用Keil C51寫(xiě)C語(yǔ)言程序驅(qū)動(dòng)讀寫(xiě)本質(zhì)上是在模擬“單片機(jī)外部存儲(chǔ)器”這套經(jīng)典硬件組合。為什么推薦用Proteus而不是直接買(mǎi)開(kāi)發(fā)板兩個(gè)原因。第一Proteus里你能直接看到總線時(shí)序、片選信號(hào)的走向邏輯分析儀一掛哪根線拉高了、哪根線沒(méi)使能一目了然。第二27C512這種EPROM實(shí)體芯片是要紫外線擦除的買(mǎi)回來(lái)寫(xiě)錯(cuò)了就得照紫外線燈折騰半天。仿真里點(diǎn)幾下鼠標(biāo)就能重新加載程序效率完全不是一個(gè)量級(jí)。這個(gè)項(xiàng)目適合誰(shuí)三類(lèi)人。一是剛學(xué)完單片機(jī)基礎(chǔ)、想更進(jìn)一步理解總線與存儲(chǔ)器映射的學(xué)生二是準(zhǔn)備做畢業(yè)設(shè)計(jì)、需要擴(kuò)展程序存儲(chǔ)器和數(shù)據(jù)存儲(chǔ)器的工程師三是純粹想搞懂“Keil C51生成hex文件之后仿真時(shí)程序到底跑到哪去了”的好奇黨。三個(gè)角色的需求點(diǎn)不同但底層都繞不開(kāi)三個(gè)核心問(wèn)題地址怎么分配、片選怎么接、數(shù)據(jù)怎么讀寫(xiě)。整套方案的核心思路其實(shí)很樸素用P0口做低8位地址/數(shù)據(jù)分時(shí)復(fù)用用P2口的高位地址線做存儲(chǔ)器的地址輸入再用74LS373鎖存低8位地址最后把讀信號(hào)、寫(xiě)信號(hào)、片選信號(hào)按存儲(chǔ)器的時(shí)序要求接好。剩下的活兒就是寫(xiě)C代碼告訴Keil“xdata區(qū)域在哪、怎么訪問(wèn)”。這里要先建立一個(gè)認(rèn)知51單片機(jī)的程序存儲(chǔ)器和數(shù)據(jù)存儲(chǔ)器是分開(kāi)編址的。程序存儲(chǔ)器用PSEN外部程序讀選通信號(hào)激活地址空間從0000H到FFFFH共64KB數(shù)據(jù)存儲(chǔ)器用RD和WR信號(hào)訪問(wèn)同樣有64KB的地址空間。這兩個(gè)64KB是獨(dú)立的所以27C512接到程序存儲(chǔ)器側(cè)6264接到數(shù)據(jù)存儲(chǔ)器側(cè)互不沖突。硬件上唯一的坑在于外部程序存儲(chǔ)器的代碼由PSEN讀取外部數(shù)據(jù)存儲(chǔ)器由RD/WR讀寫(xiě)兩者不會(huì)同時(shí)訪問(wèn)所以片選信號(hào)看似可以共用實(shí)際必須嚴(yán)格按照訪問(wèn)類(lèi)型來(lái)區(qū)分否則會(huì)出亂子。2. 硬件方案與芯片選型27C512和6264的本質(zhì)區(qū)別2.1 27C512掉電不丟的程序倉(cāng)庫(kù)27C512是一顆512Kbit也就是64KB的紫外線可擦除EPROM數(shù)據(jù)總線8位地址線需要16根A0-A15。51單片機(jī)的程序地址空間正好也是64KB理論上可以直接把16根地址線全接上但實(shí)際工程里很少有人這么干因?yàn)閱纹瑱C(jī)程序一般用不了64KB那么大通常的做法是把最高位地址線A15直接接地只用低32KB或者用P2口的某些引腳做簡(jiǎn)單譯碼。Proteus仿真里同樣可以簡(jiǎn)化A15接地其余15根地址線接單片機(jī)的P2.0-P2.7和74LS373的輸出Q0-Q7。為什么選27C512而不是更常見(jiàn)的28C648KB EEPROM因?yàn)?7C512能直觀展示“大容量程序存儲(chǔ)器”的概念。當(dāng)程序超過(guò)內(nèi)部4KB/8KB的限制時(shí)你會(huì)發(fā)現(xiàn)自己寫(xiě)的代碼真的被放到了外部ROM里此時(shí)PSEN引腳會(huì)不停跳變邏輯分析儀能看到清晰的取指脈沖。這個(gè)體驗(yàn)比小容量芯片強(qiáng)烈得多。在Proteus里27C512的使用有個(gè)關(guān)鍵操作雙擊芯片符號(hào)在“Advanced Properties”或“Initial Contents”中加載hex文件。很多新手掛在第一步——仿真一運(yùn)行程序根本執(zhí)行不了單片機(jī)始終處于復(fù)位狀態(tài)就是因?yàn)闆](méi)給27C512填程序。這一步本質(zhì)上是模擬“用編程器把hex文件寫(xiě)進(jìn)EPROM”的過(guò)程Proteus把編程器功能做成了可視化加載但很多教程沒(méi)講透。2.2 62648KB的臨時(shí)數(shù)據(jù)倉(cāng)庫(kù)6264是8KB的CMOS靜態(tài)RAM需要A0-A12共13根地址線數(shù)據(jù)線8根。它的引腳里有兩個(gè)使能端CE片選使能低有效和OE輸出使能低有效一個(gè)寫(xiě)使能WE低有效。讀操作時(shí)CE和OE都拉低WE拉高寫(xiě)操作時(shí)CE拉低WE拉低OE可以拉高。6264和27C512的功耗等級(jí)不同27C512的讀取電流在30mA左右6264在CMOS模式下只有幾微安但這些都是靜態(tài)參數(shù)仿真里不用太關(guān)心實(shí)際畫(huà)板子時(shí)需要考慮電源去耦。仿真里反而要注意的是6264上電后內(nèi)容是隨機(jī)的讀出來(lái)可能是0x00可能是0xFF也可能是亂七八糟的值。這和在真實(shí)硬件上的行為一致所以調(diào)試時(shí)不要指望RAM上電就是干凈的程序里要主動(dòng)初始化。2.3 Proteus里的連線設(shè)計(jì)74LS373是關(guān)鍵樞紐沒(méi)有74LS373P0口既當(dāng)?shù)刂酚之?dāng)數(shù)據(jù)是沒(méi)法工作的。P0口在ALE地址鎖存使能為高時(shí)輸出低8位地址ALE由高變低時(shí)74LS373鎖存這8位地址并穩(wěn)定輸出隨后P0口切換為數(shù)據(jù)總線傳輸8位數(shù)據(jù)。這個(gè)“先地址、后數(shù)據(jù)”的過(guò)程就是教科書(shū)上反復(fù)強(qiáng)調(diào)的“分時(shí)復(fù)用”。Proteus中的連線建議P0.0-P0.7 → 74LS373的D0-D7同時(shí) → 27C512和6264的D0-D7ALE → 74LS373的G鎖存使能74LS373的Q0-Q7 → 27C512的A0-A7、6264的A0-A7P2.0-P2.7 → 27C512的A8-A15其中A15接地也可以P2.0-P2.4 → 6264的A8-A12P2.5-P2.7 → 譯碼邏輯或直接做片選注意系統(tǒng)性設(shè)計(jì)這里有個(gè)細(xì)節(jié)容易被忽略6264的A12接哪根地址線決定了它的映射地址范圍。如果你把P2.4接到A12那么6264占用的地址空間是P2.7-P2.5為任意值P2.41時(shí)的區(qū)間如果P2.5-P2.7懸空或做譯碼不當(dāng)可能出現(xiàn)多個(gè)地址都指向同一顆芯片。Proteus不會(huì)報(bào)錯(cuò)但邏輯上很危險(xiǎn)。3. Keil C51工程配置從裸機(jī)到外部存儲(chǔ)器的關(guān)鍵設(shè)置3.1 創(chuàng)建工程時(shí)的芯片選擇與內(nèi)存模型打開(kāi)Keil C51新建工程芯片型號(hào)選擇Atmel 89C52或者AT89C51我習(xí)慣用AT89C52因?yàn)閮?nèi)部RAM有256字節(jié)比89C51多128字節(jié)的間接尋址區(qū)IDATA調(diào)試時(shí)更從容。但這個(gè)內(nèi)部容量對(duì)項(xiàng)目來(lái)說(shuō)完全不夠用所以重點(diǎn)在Target選項(xiàng)卡里設(shè)置Memory Model選擇“Large: variables in XDATA”這樣默認(rèn)變量類(lèi)型如果未指定存儲(chǔ)區(qū)編譯器就會(huì)分配到外部RAM。Code Rom Size選擇“Large: 64K program”這樣代碼可以超過(guò)2KB、8KB的限制指示鏈接器把代碼放到外部程序存儲(chǔ)器的可用空間。Off-chip Code memory填入程序存儲(chǔ)器的起始地址一般從0x0000開(kāi)始大小視27C512的映射而定。如果A15接地實(shí)際可用的程序空間是0x0000-0x7FFF共32KB在“Start”填0x0000“Size”填0x8000。Off-chip Xdata memory填入6264的起始地址。如果P2.4接A12且P2.5-P2.7都為0時(shí)選中6264那起始地址是0x0000大小0x20008KB。注意不要以為填了地址Keil就會(huì)自動(dòng)生成片選邏輯Keil只負(fù)責(zé)生成尋址指令真正的片選信號(hào)是硬件電路決定的。你填的地址必須和硬件的譯碼結(jié)果一致否則程序運(yùn)行后訪問(wèn)的存儲(chǔ)器和你想的完全不是一回事。3.2 使用xdata關(guān)鍵字控制變量的存儲(chǔ)位置C51里訪問(wèn)外部RAM可以顯式聲明變量到xdata段unsigned char xdata buffer[256]; // 分配到外部RAM也可以使用指針訪問(wèn)任意絕對(duì)地址#define SRAM_BASE 0x0000 void write_sram(unsigned int addr, unsigned char dat) { unsigned char xdata *p (unsigned char xdata *)(SRAM_BASE addr); *p dat; } unsigned char read_sram(unsigned int addr) { unsigned char xdata *p (unsigned char xdata *)(SRAM_BASE addr); return *p; }這樣寫(xiě)的好處是直觀但連續(xù)多次調(diào)用會(huì)生成比較冗長(zhǎng)的匯編代碼。更高效的方式是用Keil提供的絕對(duì)地址訪問(wèn)宏#include absacc.h // 向6264的0x0000地址寫(xiě)一個(gè)字節(jié) XBYTE[0x0000] 0x55; // 從6264的0x0100地址讀一個(gè)字節(jié) unsigned char val XBYTE[0x0100];XBYTE宏展開(kāi)后就是通過(guò)DPTR間接訪問(wèn)Keil會(huì)針對(duì)xdata訪問(wèn)自動(dòng)選擇MOVX指令不需要你手動(dòng)控制DPTR高低字節(jié)省事很多。但要注意訪問(wèn)xdata變量時(shí)Keil的優(yōu)化器可能改變?cè)L問(wèn)順序如果要求嚴(yán)格的時(shí)序比如連續(xù)寫(xiě)多個(gè)寄存器需要在關(guān)鍵位置加volatile聲明或直接寫(xiě)內(nèi)嵌匯編。3.3 跟外部存儲(chǔ)器的第一次親密接觸掃描與驗(yàn)證在Proteus里成功運(yùn)行前建議先寫(xiě)一個(gè)最簡(jiǎn)單的存儲(chǔ)器掃描程序確認(rèn)地址映射完全正確。下面這段代碼把整個(gè)6264逐字節(jié)寫(xiě)入特定模式再讀出來(lái)校驗(yàn)一旦出錯(cuò)就能精準(zhǔn)定位到是地址線接錯(cuò)還是數(shù)據(jù)線接錯(cuò)#include reg52.h #include absacc.h // 6264大小8KB起始地址0x0000 #define SRAM_SIZE 0x2000 void check(signed char pattern) { unsigned int i; bit error_flag 0; for (i 0; i SRAM_SIZE; i) { XBYTE[i] pattern; } for (i 0; i SRAM_SIZE; i) { if (XBYTE[i] ! pattern) { error_flag 1; P1 i 0xFF; // 出錯(cuò)地址的低8位輸出到P1 while (1); // 停在這里便于觀察 } } if (!error_flag) { P1 pattern; // 全部通過(guò)P1輸出當(dāng)前模式值 } } void main() { // 三個(gè)經(jīng)典模式0x00、0xFF、0xAA分別能排查不同的接線問(wèn)題 check(0x00); // 全0模式如果數(shù)據(jù)線短路到地可能誤判通過(guò) check(0xFF); // 全1模式 check(0xAA); // 0101模式能測(cè)出相鄰數(shù)據(jù)線的短路 while (1); }這個(gè)程序雖然簡(jiǎn)單但在Proteus里跑一遍你能直觀看到P1口的變化。如果程序停在中間的while(1)里說(shuō)明出現(xiàn)了寫(xiě)入和讀出不一致的地址這時(shí)候就要檢查你配置的Keil地址范圍是否和硬件的片選邏輯匹配。4. 實(shí)操過(guò)程與關(guān)鍵代碼把27C512和6264同時(shí)用起來(lái)4.1 仿真中給27C512加載代碼的正確流程這一步是很多教程不愿意細(xì)講的但恰恰是最容易卡住初學(xué)者的。在Proteus里直接雙擊27C512芯片會(huì)彈出一個(gè)屬性對(duì)話框。在“Memory Contents”區(qū)域點(diǎn)擊文件夾圖標(biāo)選擇Keil編譯生成的hex文件。如果看不到hex文件多半是Keil沒(méi)有在Output選項(xiàng)卡里勾選“Create HEX File”。關(guān)鍵提示加載hex文件前先確認(rèn)Keil工程中“Off-chip Code memory”的起始地址設(shè)置的和27C512在仿真里的接法一致。比如P2.7、P2.6、P2.5被用作片選而實(shí)際A15接了P2.7那么你的代碼地址范圍可能是0x0000-0x7FFF也可能是從0x8000開(kāi)始的某段。Proteus只會(huì)把hex文件里的代碼按地址放進(jìn)ROM模型如果你的代碼段地址超過(guò)了硬件允許的范圍仿真時(shí)指令就會(huì)跑飛現(xiàn)象是單片機(jī)不斷復(fù)位或者程序跳到一個(gè)莫名其妙的地址。Proteus的51模型加載hex文件后上電復(fù)位的動(dòng)作和真實(shí)芯片幾乎一樣PC從0x0000開(kāi)始先讀取外部ROM的第一個(gè)字節(jié)執(zhí)行跳轉(zhuǎn)指令然后進(jìn)入你的main函數(shù)。所以只要hex文件沒(méi)問(wèn)題、連線沒(méi)問(wèn)題仿真就能跑起來(lái)。4.2 程序代碼結(jié)構(gòu)按“讀ROM、寫(xiě)RAM、回顯”三個(gè)步驟組織我完整跑過(guò)的示例工程主邏輯就分三段。第一段循環(huán)讀出27C512中的一串固定字符事先在Proteus的Memory Contents里手工填入通過(guò)串口發(fā)送到虛擬終端第二段把接收到的串口數(shù)據(jù)寫(xiě)入6264第三段再讀回來(lái)輸出到LCD1620或虛擬終端顯示。用代碼說(shuō)話#include reg52.h #include absacc.h #include string.h #define ROM_BASE 0x0000 #define RAM_BASE 0x0000 sfr16 DPTR 0x82; // 定義16位DPTR // 串口初始化9600bps12MHz晶振 void uart_init() { SCON 0x50; // 模式1允許接收 TMOD 0x20; // 定時(shí)器1工作在模式2 TH1 0xFD; // 波特率9600 TL1 0xFD; TR1 1; } void uart_send(unsigned char c) { SBUF c; while (!TI); TI 0; } // 從27C512中讀取一個(gè)字節(jié) unsigned char read_rom(unsigned int addr) { unsigned char code *p (unsigned char code *)(ROM_BASE addr); return *p; } // 向6264寫(xiě)一個(gè)字節(jié) void write_ram(unsigned int addr, unsigned char dat) { unsigned char xdata *p (unsigned char xdata *)(RAM_BASE addr); *p dat; } // 也可以直接用XBYTE宏 #define WRITE_RAM(addr, dat) XBYTE[(addr)] (dat) #define READ_RAM(addr) XBYTE[(addr)] void main() { unsigned char i; unsigned char ch; uart_init(); // 假設(shè)27C512從0x0020開(kāi)始存放了一個(gè)字符串 for (i 0; i 16; i) { ch read_rom(0x0020 i); uart_send(ch); WRITE_RAM(i, ch); // 復(fù)制到6264中 } // 從6264讀回來(lái)驗(yàn)證 for (i 0; i 16; i) { ch READ_RAM(i); uart_send(ch); } while (1); }重點(diǎn)說(shuō)一下read_rom函數(shù)。這里用了一個(gè)code指針指向外部程序存儲(chǔ)器地址。C51對(duì)code指針有專(zhuān)門(mén)的取指指令MOVC所以從27C512讀數(shù)據(jù)是完全透明的編譯器負(fù)責(zé)處理。而write_ram/READ_RAM用的是MOVX指令對(duì)應(yīng)外部數(shù)據(jù)存儲(chǔ)器。這種“一個(gè)項(xiàng)目里同時(shí)用MOVC和MOVX”的寫(xiě)法正是理解51總線結(jié)構(gòu)的最好練習(xí)。4.3 把FreeRTOS思想和存儲(chǔ)器擴(kuò)展結(jié)合起來(lái)如果你想把實(shí)驗(yàn)往更接近產(chǎn)品的方向推可以做一個(gè)微型任務(wù)調(diào)度器用6264作為任務(wù)控制塊的存儲(chǔ)區(qū)typedef struct { unsigned char id; unsigned char state; unsigned int counter; } TCB; TCB xdata task_table[8]; void task_scheduler() { unsigned char i; for (i 0; i 8; i) { task_table[i].state 0x01; task_table[i].counter 0; } // 模擬一輪“任務(wù)遍歷與自增” while (1) { for (i 0; i 8; i) { task_table[i].counter; } } }TCB數(shù)組分配在xdata區(qū)硬件上就是6264。你可以在Proteus中右鍵點(diǎn)擊6264在“Memory Contents”中實(shí)時(shí)觀察這些字節(jié)的變化。這個(gè)操作比單純用debugger看變量有趣得多也更能直觀理解外部RAM和內(nèi)部RAM的差別。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 仿真一運(yùn)行程序“原地踏步”不動(dòng)現(xiàn)象點(diǎn)擊運(yùn)行后程序計(jì)數(shù)器PC一直顯示0x0000或者重復(fù)執(zhí)行第一條跳轉(zhuǎn)指令。排查思路確認(rèn)27C512里有沒(méi)有加載hex。雙擊芯片查看Memory Contents如果是空的肯定跑不起來(lái)。確認(rèn)ALE信號(hào)是否正常。在Proteus里添加數(shù)字探針觀察P0口和74LS373的輸出。如果ALE一直是低電平鎖存器不工作地址就永遠(yuǎn)無(wú)效。確認(rèn)PSEN引腳有沒(méi)有脈沖。如果沒(méi)有脈沖說(shuō)明單片機(jī)根本沒(méi)嘗試讀外部ROM可能是EA引腳被拉高導(dǎo)致內(nèi)部ROM訪問(wèn)優(yōu)先。芯片89C52內(nèi)部的4KB/8KB ROM空間如果不為空并且程序小于內(nèi)部容量單片機(jī)根本不會(huì)去讀外部ROM。解決辦法把EA引腳接地強(qiáng)制從外部ROM啟動(dòng)。這里必須強(qiáng)調(diào)一個(gè)反直覺(jué)的坑Proteus里AT89C52默認(rèn)內(nèi)部程序存儲(chǔ)器有代碼比如一個(gè)空的main函數(shù)如果你生成的hex文件小于4KB編譯器會(huì)把它放到內(nèi)部ROM區(qū)Keil不會(huì)自動(dòng)用外部ROM。要想驗(yàn)證27C512被外部讀取必須把EA引腳拉低。5.2 6264寫(xiě)入沒(méi)問(wèn)題但讀出全是0xFF這個(gè)現(xiàn)象在Proteus里很常見(jiàn)原因是6264的OE和WE信號(hào)沒(méi)有正確區(qū)分或者片選信號(hào)接反了。檢查以下幾點(diǎn)OE是否接到單片機(jī)的RD引腳P3.7。WE是否接到單片機(jī)的WR引腳P3.6。CE是否接到了有效的片選信號(hào)如果是直接接地那6264始終被選中讀寫(xiě)邏輯不會(huì)出錯(cuò)如果通過(guò)譯碼器控制確認(rèn)譯碼器的輸出極性正確。還有一種可能是Keil的目標(biāo)配置里外部數(shù)據(jù)存儲(chǔ)器大小沒(méi)填。如果Off-chip Xdata memory的Size設(shè)置成0編譯器會(huì)認(rèn)為xdata區(qū)不存在所有xdata訪問(wèn)都會(huì)報(bào)錯(cuò)或生成錯(cuò)誤代碼。正確配置是Start: 0x0000Size: 0x2000。5.3 程序運(yùn)行正常但往6264寫(xiě)數(shù)據(jù)后讀回來(lái)的數(shù)據(jù)錯(cuò)位這是地址線接錯(cuò)導(dǎo)致的典型癥狀。比如你把6264的A12接到了P2.3原本期望從0x0000開(kāi)始訪問(wèn)實(shí)際上6264的內(nèi)部地址是0x0000-0x1FFF但由于P2.3參與尋址你訪問(wèn)0x0000時(shí)可能需要P2.30而訪問(wèn)0x2000時(shí)P2.31于是在0x1000和0x3000都可能映射到同一個(gè)物理單元。遇到這種錯(cuò)位問(wèn)題最快的排查方法是用我上面給的掃描程序把0x00、0xFF、0xAA分別寫(xiě)入全地址再讀回。如果錯(cuò)誤地址非常有規(guī)律比如每隔0x1000錯(cuò)一次基本上就是某一根高位地址線的問(wèn)題。在Proteus中可以用鼠標(biāo)拖拽連線快速調(diào)整重新仿真比焊板子快太多了。5.4 程序里有XDATA變量編譯卻報(bào)錯(cuò)“RAMSIZE”Keil C51的Target選項(xiàng)卡里有個(gè)“Use on-chip ROM”的選項(xiàng)旁邊的“RAMSIZE”必須設(shè)置為256字節(jié)對(duì)于89C52或128字節(jié)對(duì)于89C51。如果這個(gè)值小于你的內(nèi)部RAM使用量編譯就會(huì)報(bào)錯(cuò)。但要注意這個(gè)設(shè)置只管內(nèi)部RAM跟外部6264沒(méi)關(guān)系外部RAM的容量是在Off-chip Xdata memory里單獨(dú)定義的。很多新手在這里填錯(cuò)把RAMSIZE當(dāng)成外部RAM大小造成編譯錯(cuò)誤或內(nèi)存覆蓋。5.5 虛擬終端顯示亂碼串口波特率對(duì)不上Proteus里的虛擬終端不會(huì)自動(dòng)跟隨Keil的波特率必須手動(dòng)在虛擬終端屬性里設(shè)置。常見(jiàn)的坑是晶振頻率Keil代碼里用12MHz晶振計(jì)算波特率但Proteus的AT89C52默認(rèn)晶振可能是11.0592MHz。11.0592MHz能精確分頻出9600波特率12MHz則會(huì)產(chǎn)生3%左右的誤差這個(gè)誤差在真實(shí)硬件上可能還能工作但在Proteus里有時(shí)也會(huì)導(dǎo)致亂碼。推薦統(tǒng)一使用11.0592MHz晶振。6. 工具選型與版本差異Keil C51 v9.61和Proteus 8.x的使用細(xì)節(jié)Keil C51現(xiàn)在最常見(jiàn)的版本是v9.61這個(gè)版本對(duì)Windows 10/11的兼容性都很好編譯51系列完全夠用。需要注意的一點(diǎn)是v9.61安裝包本身是評(píng)估版代碼量限制在4KB以?xún)?nèi)但寄存器窗口、調(diào)試器功能都是完整的。如果你要編譯超過(guò)4KB的代碼要么用破解補(bǔ)丁不推薦這里不多說(shuō)要么換用其他家的免費(fèi)工具鏈。好消息是本文里的示例代碼量都不大用評(píng)估版也完全跑得動(dòng)。Proteus方面7.x和8.x在仿真51核心時(shí)邏輯模型基本一致。區(qū)別在于8.x版本支持更現(xiàn)代的界面和更流暢的仿真并且對(duì)存儲(chǔ)器加載hex文件的操作方式略有不同。7.8版本在Windows 10上偶爾會(huì)有界面閃爍問(wèn)題建議直接上8.x以上版本。Proteus的元件搜索關(guān)鍵詞AT89C52、27C512、6264、74LS373、RESPACK-8、LOGICPROBE、VIRTUAL TERMINAL。有一個(gè)Proteus特有的操作技巧連線時(shí)按CtrlW可以在兩個(gè)點(diǎn)之間畫(huà)自動(dòng)布線這在復(fù)雜總線連線時(shí)能省下大量時(shí)間。另外右鍵點(diǎn)擊導(dǎo)線選擇“Wire Label”可以給總線命名。強(qiáng)烈建議給地址總線和數(shù)據(jù)總線分別命名A0-A15、D0-D7而不是依靠物理連線。命名后即使物理連線位置有偏差只要標(biāo)簽一致電氣上就是連通的這樣可以極大降低連線的視覺(jué)復(fù)雜度。7. 仿真之外這個(gè)實(shí)驗(yàn)在生產(chǎn)環(huán)境里的真實(shí)對(duì)應(yīng)很多人覺(jué)得Proteus仿真就是“玩”實(shí)際工作中根本用不上。但我要說(shuō)這個(gè)實(shí)驗(yàn)的思路和現(xiàn)代嵌入式系統(tǒng)的啟動(dòng)流程是高度一致的?,F(xiàn)代MCU內(nèi)部集成Flash和SRAM但外部擴(kuò)展存儲(chǔ)器的應(yīng)用場(chǎng)景依然大量存在——比如用SPI NOR Flash存固件鏡像、用并行SRAM做圖像緩沖、用NAND Flash做文件系統(tǒng)這些都是27C512和6264的“現(xiàn)代升級(jí)版”。理解地址映射、片選譯碼、總線時(shí)序是理解這些復(fù)雜系統(tǒng)的底層基礎(chǔ)。更進(jìn)一步如果你把27C512看作一個(gè)“外部Boot ROM”把6264看作“共享內(nèi)存”那這套架構(gòu)就可以類(lèi)比成早期的計(jì)算機(jī)主板總線結(jié)構(gòu)。CPU通過(guò)地址總線尋址、數(shù)據(jù)總線傳輸、控制總線協(xié)調(diào)和51單片機(jī)訪問(wèn)外部存儲(chǔ)器的原理如出一轍。在實(shí)際硬件調(diào)試中我吃過(guò)一次大虧。當(dāng)時(shí)畫(huà)了一塊板子外擴(kuò)了一顆64KB的SRAM片選信號(hào)用了138譯碼器。結(jié)果在調(diào)試時(shí)發(fā)現(xiàn)程序能運(yùn)行但RAM的某個(gè)地址范圍出現(xiàn)的全是0x00折騰了兩天最后查出來(lái)是譯碼器的一個(gè)使能端接到了單片機(jī)的PSEN上。PSEN在讀外部RAM時(shí)不活動(dòng)導(dǎo)致譯碼器一直沒(méi)有輸出那片RAM實(shí)際上從未被真正選中。仿真里這個(gè)問(wèn)題很容易發(fā)現(xiàn)因?yàn)镻roteus會(huì)如實(shí)反映邏輯電平但如果你想省事用“或門(mén)”邏輯直接拼片選很容易犯類(lèi)似錯(cuò)誤。所以我個(gè)人的建議是務(wù)必在Proteus里把片選邏輯完整搭出來(lái)不要圖省事直接把CE接地。雖然CE接地在只有一顆RAM的仿真里也能跑通但這樣一來(lái)你對(duì)“為什么存儲(chǔ)器會(huì)映射到某個(gè)地址范圍”的理解就完全建立不起來(lái)。只有親手接上譯碼器或門(mén)電路看著地址線變化時(shí)片選信號(hào)的電平跳變你才會(huì)對(duì)總線地址空間有肌肉記憶般的理解。最后再分享一個(gè)小技巧。Proteus的邏輯分析儀Logic Analyzer不是只能接數(shù)字信號(hào)你可以把ALE、PSEN、RD、WR、P2.7都拖進(jìn)去運(yùn)行時(shí)直接觀察時(shí)序關(guān)系。我見(jiàn)過(guò)很多人調(diào)這個(gè)項(xiàng)目時(shí)對(duì)著代碼發(fā)呆其實(shí)打開(kāi)邏輯分析儀看一眼ALE和P0口的數(shù)據(jù)關(guān)系基本就能定位90%的問(wèn)題。這比反復(fù)改代碼重新編譯高效得多。本文還有配套的精品資源點(diǎn)擊獲取