實驗指導(dǎo):從點亮引腳到完整開發(fā)閉環(huán))
簡介這份實驗指導(dǎo)書面向高校電子信息、自動化等專業(yè)學(xué)生及嵌入式初學(xué)者系統(tǒng)梳理了嵌入式系統(tǒng)課程實驗的完整流程與規(guī)范要求。壓縮包內(nèi)為1個doc文檔約965KB內(nèi)容圍繞6個實驗展開從實驗?zāi)康?、設(shè)備、原理環(huán)境到內(nèi)容、步驟及報告格式均有明確說明。其中實驗一以Keil C51匯編語言為切入點講解工程建立、源文件添加、編譯連接與軟硬件仿真調(diào)試并給出在P1.0、P1.1輸出1Hz方波的具體任務(wù)后續(xù)實驗涉及十六進制與十進制轉(zhuǎn)換等順序結(jié)構(gòu)編程訓(xùn)練。文檔還細(xì)化了預(yù)習(xí)報告、程序清單與實驗總結(jié)的撰寫要求便于讀者按步驟完成實驗并規(guī)范記錄調(diào)試過程。目前已有338人學(xué)習(xí)下載適合作為課程配套練習(xí)與自學(xué)參考。1. 零基礎(chǔ)也能上手的嵌入式實驗從點亮一個引腳開始很多人第一次聽到“嵌入式系統(tǒng)”這四個字腦子里冒出來的畫面是電路板、示波器、一堆看不懂的匯編指令然后本能地覺得自己學(xué)不會。其實換個角度想你家里的洗衣機、微波爐、空調(diào)遙控器里面都藏著一顆小小的單片機它們做的事情無非是“讀一個輸入、做一個判斷、控制一個輸出”。這份《嵌入式系統(tǒng)實驗指導(dǎo)書6個實驗》就是把這件事拆成了六個可以動手的小任務(wù)從最簡單的“讓一個引腳輸出方波”開始一步步帶你摸到門道。它適合完全沒有接觸過單片機、但愿意動手敲幾行代碼的新手也適合剛開課、面對實驗箱不知道從哪按起的學(xué)生。你不需要先學(xué)完什么高深理論跟著實驗順序走第一個實驗就能讓你看到自己寫的程序真正跑起來。2. 為什么值得從這份指導(dǎo)書入手先弄懂它在練什么2.1 它練的不是“背指令”而是“建立完整開發(fā)閉環(huán)”很多新手學(xué)嵌入式容易陷入一個誤區(qū)抱著教材從指令系統(tǒng)開始背背了半個月連一個工程都沒建過。這份指導(dǎo)書的編排邏輯恰好相反它把每個實驗都拆成“預(yù)習(xí)寫程序 → 建工程編譯 → 軟件仿真 → 硬件運行 → 寫實驗報告”這樣一條完整的鏈路。你從實驗 1 開始就要走一遍這個流程走完六個實驗?zāi)隳X子里形成的是一套肌肉記憶拿到一個需求知道先建工程、選芯片、寫源文件、編譯排錯、再上硬件驗證。這個閉環(huán)比記住多少條匯編指令都值錢因為后面你換任何一款單片機流程都是同一套。實驗 1 用的是 Keil C51 環(huán)境加匯編語言目標(biāo)是在 P1.0 引腳上輸出一個 1Hz 的方波。聽起來簡單但它逼著你把“工程怎么建、芯片怎么選、晶振頻率怎么設(shè)、HEX 文件怎么生成”這些最瑣碎也最容易卡住新手的環(huán)節(jié)全部走一遍。我第一次學(xué)的時候光是在 Keil 里找到“生成 HEX 文件”那個勾選項就翻了半天菜單這種血淚經(jīng)驗只有親手做過才會變成自己的。2.2 六個實驗是一條遞進線不是六個孤立任務(wù)你如果只看單個實驗會覺得“十六進制轉(zhuǎn)十進制”“LED 動態(tài)顯示”“矩陣鍵盤”“A/D 轉(zhuǎn)換”各干各的。但順著做下來會發(fā)現(xiàn)實驗 3 寫的 LED 顯示子程序?qū)嶒?4 的鍵盤掃描程序要直接調(diào)用它實驗 5 的 A/D 轉(zhuǎn)換結(jié)果又要送到實驗 3 的顯示器上去顯示。也就是說這份指導(dǎo)書在悄悄教你一件事嵌入式程序是攢出來的子程序是可以復(fù)用的。實驗 4 的正文里明確寫了“顯示子程序使用實驗 3 中的顯示子程序”實驗 5 也寫了“顯示子程序可以用實驗 3 中的子程序”。這種復(fù)用意識是新手從“能跑一個例子”過渡到“能做一個東西”的關(guān)鍵一步。2.3 軟件仿真兜底沒有硬件也能先跑起來新手最怕的情況是手頭沒有實驗箱或者硬件連好了但不亮根本不知道是程序錯了還是線接錯了。這份指導(dǎo)書在實驗 1 的原理部分就講清楚了Keil C51 既可以連硬件跑也可以不連硬件、純在電腦上做虛擬仿真。實驗 2 更是直接注明“可以不用硬件連接以軟件仿真的方法即可進行”。這意味著你完全可以先在自己的電腦上把前兩個實驗跑通看到存儲器窗口里 30H、31H、32H、33H 的值隨著單步執(zhí)行而變化確認(rèn)程序邏輯沒問題再去碰硬件。這個“先軟后硬”的順序能幫你把大部分語法錯誤和邏輯錯誤擋在接電線之前少走很多彎路。3. 第一個例子怎么跑從建工程到 P1.0 輸出方波3.1 先把開發(fā)環(huán)境搭起來你需要兩樣?xùn)|西一臺裝了 Keil C51 的電腦以及可選一臺單片機實驗箱。如果暫時沒有實驗箱完全不影響你跑通第一個例子因為實驗 1 的前半段用軟件仿真就能驗證。打開 Keil 之后第一步是建工程點 Project → New Project選一個你記得住的文件夾比如 E:\TEST工程名起成 test。接著會彈出一個選芯片的窗口廠家選 Atmel型號選 89C51。選完之后在工程選項里把晶振頻率改成 12MHz并且在 Output 標(biāo)簽頁里勾上“生成 HEX 文件”。這兩步別漏晶振頻率影響后面延時的計算HEX 文件是燒到硬件上要用的格式。提示工程路徑盡量用英文不要帶空格和中文否則編譯時偶爾會報一些莫名其妙的錯這是很多人踩過的坑。3.2 寫第一段匯編程序新建一個源文件File → New把下面這段代碼敲進去。這段程序做的事情就是讓 P1.0 這個引腳的電平不停地翻轉(zhuǎn)翻轉(zhuǎn)之間加一段延時從而形成一個 1Hz 左右的方波。ORG 0000H ; 程序從 0000H 地址開始執(zhí)行 AGAIN: CPL P1.0 ; 對 P1.0 取反高變低、低變高 MOV R0, #10 ; 外層延時計數(shù)配合內(nèi)層循環(huán)湊出約 0.5 秒 LOOP1: MOV R1, #100 LOOP2: MOV R2, #250 DJNZ R2, $ ; R2 減到 0 之前原地循環(huán) DJNZ R1, LOOP2 DJNZ R0, LOOP1 SJMP AGAIN ; 跳回 AGAIN無限循環(huán) END逐行解釋一下關(guān)鍵點。ORG 0000H是告訴編譯器程序從地址 0 開始放這份指導(dǎo)書里每個實驗都要求起始地址為 0000H。CPL P1.0是核心動作把 P1.0 的電平取反原來高就變低、原來低就變高。后面三層MOV加DJNZ構(gòu)成一個嵌套循環(huán)靠消耗指令周期來“拖時間”讓每次翻轉(zhuǎn)之間隔大約 0.5 秒兩次翻轉(zhuǎn)就是一個完整周期所以頻率約 1Hz。SJMP AGAIN讓程序跑完一輪又跳回開頭永不停止。寫完之后用 File → Save As 存成 test.asm注意擴展名是 .asm不是 .txt。3.3 把源文件加進工程并編譯源文件存好了還不算工程的一部分得手動加進去。在左側(cè)工程窗口里點 Target 1 前面的加號看到 Source Group 1右鍵它選 Add Files to Group找到剛才的 test.asm 加進來然后關(guān)掉對話框。接著點 Project → Build target 開始編譯。如果代碼沒敲錯下面的輸出窗口會顯示 0 Error(s)并且生成 OBJ、LST、HEX 等文件。如果報錯最常見的原因是標(biāo)點符號用了中文全角比如把英文逗號敲成了中文逗號或者數(shù)字 0 和字母 o 混了。指導(dǎo)書的重要提示里專門強調(diào)了這一點別不當(dāng)回事這種錯編譯器給的提示往往很含糊找起來特別費勁。3.4 軟件仿真看結(jié)果編譯通過后點 Debug → Start/Stop Debug Session 進入仿真模式。這時候你可以單步執(zhí)行觀察 P1.0 的狀態(tài)怎么變。想看端口狀態(tài)打開 View → Memory Window在地址欄輸入p1或者直接看左側(cè)的寄存器/端口面板。每按一次單步CPL 執(zhí)行后 P1.0 就翻轉(zhuǎn)一次。雖然軟件仿真看不到真實的 LED 亮滅但你能確認(rèn)程序邏輯是對的引腳確實在翻轉(zhuǎn)延時循環(huán)確實在跑。這一步過了再連硬件就有底氣了。3.5 上硬件驗證有實驗箱的話把串口線連好、通電撥位開關(guān) K10 撥到 B 端用導(dǎo)線把 P1.0 接到 L00LED 指示燈P1.1 接到 L01。然后在工程選項的 Debug 標(biāo)簽頁里把調(diào)試方式改成硬件實時仿真重新進入調(diào)試連續(xù)運行就能看到 L00 對應(yīng)的燈以大約 1 秒的節(jié)奏亮滅。實驗 1 的最后一個內(nèi)容是讓你改程序讓 P1.0 和 P1.1 輸出頻率相同但電平相反的方波思路很簡單在 CPL P1.0 后面再加一條 CPL P1.1兩個引腳就會始終保持相反狀態(tài)。這個改動雖小但它是你第一次“改需求、改代碼、再驗證”的完整練習(xí)。4. 中間幾個實驗怎么跟顯示、按鍵與采樣的遞進練法4.1 實驗 2用單步調(diào)試看懂?dāng)?shù)據(jù)在內(nèi)存里怎么搬實驗 2 要求把片內(nèi) RAM 30H 里的一個兩位十六進制數(shù)轉(zhuǎn)換成三位十進制數(shù)按高位到低位放進 31H 到 33H。原理不復(fù)雜依次除以 100、除以 10商和最后的余數(shù)就是各位十進制數(shù)字。但這個實驗的真正價值在于它強制你用單步方式運行并且打開存儲器窗口觀察每個單元的變化。操作上先建工程、加入你預(yù)習(xí)時寫好的 .asm 文件然后 View → Memory Window 調(diào)出存儲器編輯窗口在地址欄輸入d:30H就能看到片內(nèi) RAM 從 30H 開始的內(nèi)容。右鍵某個數(shù)據(jù)選 Modify Memory at可以把 30H 改成你想測試的值比如 0FFH。然后在關(guān)鍵指令上設(shè)斷點Debug → Insert/Remove Breakpoint運行到斷點停下來再一步步單步走盯著 31H、32H、33H 的值怎么被寫進去。這個實驗不需要硬件純軟件仿真就能完成特別適合手頭沒設(shè)備的時候練手。它教你的其實是一個通用技能當(dāng)程序結(jié)果不對時不要靠猜打開存儲器窗口看數(shù)據(jù)到底變成了什么這是嵌入式調(diào)試?yán)镒顦闼氐摹昂蠡谒帯薄?.2 實驗 3動態(tài)掃描顯示理解“看起來同時亮”的真相實驗 3 是六個實驗里信息量最大的一個。實驗箱上有 6 位 8 段 LED 顯示器采用動態(tài)驅(qū)動同一時刻其實只有一位在亮但因為切換速度足夠快刷新頻率不低于 50Hz人眼看起來就是六位同時亮著。這個原理新手一定要先想通否則寫程序時會困惑“為什么我要一位一位地送數(shù)據(jù)”。硬件連接上8255 的 B 口驅(qū)動各段低電平發(fā)光A 口驅(qū)動各位低電平選中A、B、C、控制口的地址分別是片外 RAM 的 4000H、4001H、4002H、4003H。顯示子程序的入口約定是待顯示的數(shù)放在 20H 到 25H 這六個單元里每個單元的值是 00H 到 1FH對應(yīng)字形表里的 0 到 F、0. 到 F.、橫杠和滅。子程序要做的是取 20H 的值查字形表得到段碼送到 4001H同時把位選碼送到 4000H 選中第一位延時約 0.5 毫秒關(guān)掉所有位指針移到 21H位選碼左移一位選第二位重復(fù)直到六位都刷完一遍再返回。字形表在指導(dǎo)書里給全了比如數(shù)字 0 的段碼是 C0H數(shù)字 1 是 F9H照著抄進程序就行。主程序負(fù)責(zé)初始化 8255A 口、B 口方式 0 輸出然后反復(fù)調(diào)用顯示子程序。實驗內(nèi)容 3 還要求做一個“0 到 F 向左移動”的效果每過大約 1 秒把 20H 到 25H 的內(nèi)容整體左移一位最低位補一個新數(shù)字再繼續(xù)調(diào)用顯示。這里有個容易翻車的地方移動用的循環(huán)計數(shù)單元不能和顯示子程序里用到的 R0、R1、R2 沖突否則顯示會亂。指導(dǎo)書的重要提示里專門點了這一條我第一次做的時候就因為復(fù)用了 R1屏幕上的數(shù)字跳得毫無規(guī)律查了半天才發(fā)現(xiàn)是寄存器打架。4.3 實驗 4矩陣鍵盤把“按了哪個鍵”翻譯成數(shù)字實驗 4 的鍵盤是 4 行乘 4 列行接 8255 的 PC0 到 PC3列接 PC4 到 PC7。掃描思路是每次只讓一列為低電平、其余列為高然后讀行狀態(tài)如果某一行變成低電平說明那一行和當(dāng)前列交叉處的鍵被按下了。為了防抖讀到低電平后要延時 20 毫秒再讀一次兩次一致才認(rèn)。查鍵值子程序把“列驅(qū)動碼 行狀態(tài)”這個組合翻譯成 00H 到 0FH 的鍵值指導(dǎo)書里直接給了一段可用的 KEY20 子程序入口是累加器 A 高 4 位放列驅(qū)動碼、低 4 位放行狀態(tài)出口 A 里就是鍵值。主程序的結(jié)構(gòu)是初始化 8255A 口、B 口方式 0 輸出C 口高 4 位輸出、低 4 位輸入然后循環(huán)調(diào)用顯示子程序和鍵盤掃描子程序。掃描返回 FFH 表示沒按鍵返回其他值就把這個值移進顯示緩沖區(qū)的最右端實現(xiàn)“按一個鍵、顯示器右端移入一個數(shù)”的效果。這里的關(guān)鍵細(xì)節(jié)是判斷有沒有鍵按下看的是行狀態(tài)對應(yīng)位是不是 0讀行狀態(tài)直到鍵松開時只有低 4 位有效高 4 位是不定的必須濾掉否則會誤判成一直有鍵按著。4.4 實驗 5A/D 轉(zhuǎn)換把電壓變成看得懂的數(shù)字實驗 5 用 ADC0809 把電位器分壓出來的 0 到 5V 模擬電壓轉(zhuǎn)成數(shù)字量。接口參數(shù)很明確寫任意數(shù)到 8004H 啟動 IN4 通道的轉(zhuǎn)換讀 8004H 取結(jié)果轉(zhuǎn)換結(jié)束信號 EOC 經(jīng)過一個反相器接到 P1.0。所以程序流程是先寫 8004H 啟動轉(zhuǎn)換然后查詢 P1.0等它變成有效電平說明轉(zhuǎn)換結(jié)束再讀 8004H 拿到 8 位數(shù)字量。拿到之后套用實驗 2 的十六進制轉(zhuǎn)十進制思路把這個數(shù)拆成三位十進制送到顯示緩沖區(qū)第四到第六位用實驗 3 的顯示子程序顯示出來。這個實驗把前面幾個實驗的成果串在了一起轉(zhuǎn)換邏輯來自實驗 2顯示靠實驗 3是整份指導(dǎo)書里最能體現(xiàn)“攢程序”思路的一個。5. 新手最容易踩的坑排查清單5.1 編譯報一堆錯提示還看不懂現(xiàn)象Build 之后輸出窗口紅字一片提示的行號對不上或者根本看不懂。原因九成是標(biāo)點符號用了中文全角比如逗號、分號、冒號或者數(shù)字 0 和字母 o 混用。匯編器對這類字符非常敏感但報錯信息往往指向下一行讓人誤以為是別的地方錯了。解決把輸入法切到英文再敲代碼或者寫完之后全局搜一遍中文標(biāo)點養(yǎng)成習(xí)慣指令和操作數(shù)之間的分隔符一律用英文半角。5.2 改了程序硬件上跑出來還是舊結(jié)果現(xiàn)象明明改了代碼、編譯也通過了連上硬件運行效果和改之前一模一樣。原因改完程序后沒有退出硬件連接就重新編譯或者編譯了但沒有重新建立連接硬件里跑的還是上一次燒進去的舊程序。解決指導(dǎo)書的重要提示里反復(fù)寫了對程序修改后必須退出連接、重新編譯、再重新連接才能生效。養(yǎng)成“改代碼 → 退出調(diào)試 → 編譯 → 重新進調(diào)試”的固定動作別偷懶。5.3 顯示亂跳或者鍵盤不響應(yīng)現(xiàn)象LED 顯示的數(shù)字毫無規(guī)律地閃或者按鍵盤完全沒反應(yīng)。原因多半是寄存器或內(nèi)存單元沖突。顯示子程序占用了 R0、R1、R2主程序里的延時循環(huán)如果也用這幾個寄存器兩邊就會互相踩。鍵盤實驗里如果掃描程序和顯示程序共用同一個計數(shù)單元也會出現(xiàn)類似問題。解決給每個子程序劃定專用的工作寄存器組或內(nèi)存單元主程序的循環(huán)計數(shù)換用別的寄存器或者在調(diào)用子程序前把用到的寄存器壓棧保護。5.4 硬件連好了但燈不亮、屏不顯示現(xiàn)象程序編譯沒問題仿真也正常一接硬件就沒反應(yīng)。原因可能是接線沒接對比如 P0.0 到 P0.7 沒有和 JD0 到 JD7 對應(yīng)連上可能是撥位開關(guān) KC 沒有倒向上方也可能是連接前沒有按復(fù)位鍵 SS10。解決按指導(dǎo)書的步驟逐條核對接線確認(rèn) KC 的位置連接前和退出前都按一次 SS10 復(fù)位。硬件問題排查的順序永遠是先看線、再看開關(guān)、最后懷疑程序。5.5 延時時間對不上方波頻率差很多現(xiàn)象程序里按 12MHz 算的延時實際跑出來方波快很多或慢很多。原因晶振頻率在工程選項里沒有設(shè)成 12MHz或者延時循環(huán)的層數(shù)和初值是照抄的、沒有根據(jù)實際指令周期核算。解決回到 Project → Options for Target在 Target 標(biāo)簽頁確認(rèn)晶振頻率是 12MHz延時循環(huán)的初值可以先用仿真單步估算再上硬件微調(diào)別指望一次就精確。6. 做完六個實驗之后下一步往哪走六個實驗跟下來你手里其實已經(jīng)攢了一套能復(fù)用的東西一個顯示子程序、一個鍵盤掃描子程序、一個十六進制轉(zhuǎn)十進制的轉(zhuǎn)換思路、一個 A/D 采樣的查詢流程。下一步最值得做的事不是急著去學(xué)新的芯片而是把這幾個模塊拼成一個稍微完整一點的小系統(tǒng)。比如做一個“按鍵設(shè)定數(shù)值、顯示器實時顯示、電位器調(diào)節(jié)另一路數(shù)值”的小作品主程序里同時調(diào)用鍵盤掃描和顯示刷新中間用標(biāo)志位協(xié)調(diào)。這個練習(xí)會逼你面對一個新問題多個任務(wù)怎么在一個主循環(huán)里輪流跑而不互相卡死。這正是從“做實驗”過渡到“做項目”的門檻。具體的學(xué)習(xí)路徑可以這樣安排先把實驗 3 的顯示子程序改寫成帶參數(shù)入口的版本讓調(diào)用者指定顯示緩沖區(qū)的位置而不是寫死在 20H再把實驗 4 的鍵盤掃描改成帶返回值的函數(shù)式寫法主程序根據(jù)返回值做不同分支然后嘗試把實驗 5 的 A/D 采樣從“查詢等待”改成“定時采樣”用一個定時器中斷去觸發(fā)。每一步改動都不大但每一步都在把你從“照著指導(dǎo)書敲”往“自己設(shè)計程序結(jié)構(gòu)”推。我第一次帶新人做這些實驗的時候犯過一個很典型的錯誤總想著一次把主程序?qū)懲暾Y(jié)果調(diào)試時一出問題就不知道是哪一塊的鍋。后來我養(yǎng)成了一個習(xí)慣每加一個新模塊先單獨驗證它能跑再合進主程序合進去之后如果出問題至少能確定是新模塊引入的。從那以后我每次拼程序都強制走一遍“單獨驗證再合并”的流程翻車次數(shù)少了很多。希望這份指導(dǎo)書也能幫你把第一步邁穩(wěn)先從實驗 1 的那個方波開始跑通了再往下走別貪快。本文還有配套的精品資源點擊獲取