簡單介紹微控制器、電腦處理器原理
全文原創,轉載請標明出處
如果您發現我寫錯了、不明白我寫的內容或者能提出建設性意見,那麼懇請您在評論區發表高見
本文的定位只是讓具備基本電學、數制知識的讀者明白裸機工作的大致流程,並不針對某款特定的晶片,不講程式設計,全程幼兒園化
先講解一些基本的數位電子技術知識,再讓讀者嘗試打草稿"設計"一款微控制器,然後延伸到何為軟體何為硬體,最終完結撒花
1.晶片
電訊號是目前最容易處理的訊號,我們能用類比電路、數位電路來處理電訊號
電路能被整合在一塊晶片裡成為積體電路(IC,integrated circuit),類比電路、數位電路都能被整合
微控制器核心、電腦處理器核心都是數位電路,所以如果你認為晶片就是電腦CPU、顯示卡、記憶體條等等,那就錯了,你忽略了廣大的模擬積體電路,還有架起類比電路和數位電路的橋樑的模數轉換器(ADC)、數模轉換器(DAC)
2.電晶體
類比電路、數字電路里都有大量的電晶體(transistor)。電晶體是大名鼎鼎的貝爾實驗室的幾位工程師發明的,電晶體是20世紀最偉大的發明之一,它奠定了資訊時代的硬體基礎,奠定資訊時代的軟體基礎的是夏農(Claude Elwood Shannon)的資訊理論。發明電晶體的幾位工程師都獲得了諾貝爾物理學獎,發明積體電路的基爾比(Jack Kilby)也獲得了諾貝爾物理學獎
電晶體在模擬電路里放大訊號,在數字電路里當開關
電晶體學起來還是非常複雜的,學過類比電子技術的同學肯定會贊同我,這裡就簡短地介紹一下電晶體,不講深奧了:
上面是常見的電晶體的電路符號
電晶體可以被看作受控電源,從上面的幾種電晶體裡抽出一個,它的各個引腳的名稱如下圖所示
對於上圖這種電晶體,它的Gate的電壓高於Source的電壓VGS能控制Drain到Source的電流IDS,人話版:下圖電路中只有2條電流通路,分別被用橙色、藍色箭頭標出。對於上圖中的這種電晶體,電流無法從Gate流到Drain、無法從Source流到Drain,VGS能控制Drain和Source之間的導電通道的"寬度",越"寬"的話則允許流過的最大電流越大
在模擬電路里,"寬度"可能的取值非常多,多得讓你對學習喪失信心
在數字電路里,"寬度"的取值範圍只限於2個值:0和最大
3.邏輯閘、電平
下圖電路中電池負極接著地(GND),當然這個"地"不一定是地球表面,GND是用來定義電路中電壓為0V的點的
高中物理學過"定義無窮遠處電勢為0V"或"定義大地電勢為0V",下圖電路也類似地定義電池負極電壓為0V
如果說某個點的電壓是多少伏,那就是在說那個點的電壓相對於0V點的電壓;
如果說某個元件比如下圖中電阻的電壓,那就是在說這個電阻兩端各自對0V點的電壓的差值
讓上圖電路中的訊號源輸出電壓夠小比如0V,那麼"寬度"就能取到0,藍箭頭指示的電流就不存在了,那麼Drain的電壓等於電池的正極電壓3.3V;
讓訊號源輸出電壓夠大比如3.3V,那麼"寬度"就能取到最大,藍箭頭指示的電流也能達到最大,那麼這時Drain的電壓就接近電池負極的電壓0V
這樣就實現了通過訊號源的電壓來控制藍色箭頭指示的電流通路的通斷,電晶體的作用是不是很像開關呢
這個電路實現了將輸入取反的功能,這樣的數位電路叫非門,上圖的非門存在一些問題比如輸出0V時有電流流過電阻,那麼電阻會消耗電能
以後就用下面這個符號來表示非門,左邊輸入,右邊輸出
在上面的非門的例子中也可以把0V稱作低電平或者用二進位制數0表示,把3.3V稱作高電平或者用二進位制數1表示
實際上"不會導致數位電路錯誤工作"的低電平、高電平的電壓值並不固定,而是在一定範圍內,比如上面的非門的低電平範圍可能是-0.3V~+0.5V、高電平範圍可能是2.8V~3.5V
如果把電晶體換成耐壓值更高的、電池換成5V的,那麼低電平、高電平的範圍也可能變化,可能高電平的範圍變成4.5V~5.2V
低電平是0、高電平是1的數字邏輯稱作正邏輯,否則稱作反邏輯,大多數情況下選用正邏輯,如果用反邏輯,得特別說明
對於正邏輯,高電平的電壓範圍是這種電平規則的特徵,比如高電平是3.3V的電平規則叫CMOS電平,高電平是5V的電平規則叫TTL電平
如果要實現不同電平的互聯,可能需要進行電平轉換,否則可能燒燬電晶體或者電晶體不認前級發來的高電平
類似地,還能用電晶體搭建出與門、或門、異或門等等,這些電路叫邏輯閘電路。這裡沒必要畫出其它邏輯閘的電路圖了吧,就算畫了,你也不一定看,總之你知道確實有這些邏輯閘就行,你設計不出來不代表那些天才工程師、科學家們設計不出來
與門:可以多輸入,所有輸入都為1才輸出1
或門:可以多輸入,只要有輸入為1就輸出1
異或門:只能2輸入,2個輸入不同則輸出1,否則輸出0
這些簡單的邏輯閘電路能構成功能更復雜的電路,比如加法電路:可能有2組輸入、1組輸出,每組輸入可能是8bit,輸出可能也是8bit,輸入2個二進位制數,自動輸出相加結果
你可能會問如果8bit不夠表示相加結果怎麼辦?怎麼算減法?怎麼計算負數的運算?這些問題並不影響你理解本文後面的部分,你要是有興趣的話,那就稍後去自行搜尋這些問題吧
既然是用邏輯閘構成的,那麼這個加法電路的輸入、輸出也是0、1,這種情況下用二進位制表示數具備天然的優勢
你知道能用邏輯閘搭出加法器就行,你是否能搭建出加法器並不影響你認識微控制器
類似地,用邏輯閘還能設計出資料選擇器、編碼器、譯碼器等等邏輯電路
4.組合邏輯電路、時序邏輯電路
上面提到的數位電路的輸出都只與輸入有關,這樣的數位電路叫組合邏輯電路
還有一類數位電路,它下一時刻的輸出不僅與當前時刻的輸入有關,還與當前時刻的輸出有關。舉個例子比如秒錶,它在計時的時候:
- 這個時刻顯示的秒數是3,那麼1秒後它顯示的秒數得是4,再下1秒後顯示的秒數得是5,這是下一時刻的輸出與這個時刻的輸出有關;
- 如果你按下了清零鍵,那麼下一時刻它顯示的秒數就是0了,這是下一時刻的輸出與這個時刻的輸入有關
秒錶電路需要知道到底有沒有"過了1秒",可以每隔1秒發一個高脈衝或低脈衝或上升沿或下降沿讓電路知道過了1秒,這個週期為1秒的訊號就是這個時序邏輯電路的時鐘訊號
這種下一時刻的輸出與這一時刻的輸入輸出都有關的邏輯電路叫時序邏輯電路
時鐘訊號由振盪電路產生,時序邏輯電路的內部構造比組合邏輯電路的更復雜,設計振盪電路也有一定難度。這裡就不打擊大家的閱讀興趣了
現在你具備了一定的知識儲備,那麼來嘗試設計你的微控制器。會設計微控制器的話,也能大致知道設計電腦處理器的流程
5.最小系統
設計微控制器
- 肯定會需要用到時鐘訊號,因為你的微控制器的輸出肯定不能只與輸入訊號有關,否則功能就太簡單了
- 肯定具備復位功能,比如上電覆位,否則每次上電後你無法知道微控制器裡的這堆數位電路到底處在什麼狀態、這個狀態是否能正確執行指令
- 肯定有電源
上面提到的時鐘訊號、復位訊號、電源,再加上微控制器,就能組成微控制器的最小系統
可以這麼直觀地理解最小系統:用你設計的單片機制作產品,無論產品是什麼,時鐘訊號、復位訊號、電源、微控制器是肯定會被包含在產品裡的,這4個都具備時產品才可能正常工作,只要缺少一個,那麼產品總有個時候會不能正常工作;任何產品都能被看作是在最小系統上繼續搭建而成的
6.指令集
你得讓微控制器獲取你下達的指令並執行:這個"指令"是什麼?以什麼形式存在?怎麼向微控制器下達"指令"?
你的微控制器的輸出是由微控制器內部的數位電路處理得到的,微控制器裡可以有很多具備不同功能的數位電路比如加法器、定時器等等,完成一件工作比如計算一個算式1+2×3+2×5肯定會用到一些步驟,計算這個算式的步驟肯定有先後順序,微控制器可能會這麼做:
- 把3送入加法器的輸入1
- →把3送入加法器的輸入2
- →暫存加法器的輸出,記為數A
- →把5送入加法器的輸入1
- →把5送入加法器的輸入2
- →暫存加法器的輸出,記為數B
- →把數A送入加法器的輸入1
- →把數B送入加法器的輸入2
- →暫存加法器的輸出,記為數C
- →把數C送入加法器的輸入1
- →把1送入加法器的輸入2
- →加法器的輸出即為計算結果
你得設計一個"聽話"的電路來完成上面的指令,還得設計一些儲存器來暫存中間結果
你向微控制器傳達指令肯定是用二進位制串,因為數位電路只認二進位制串,假設你設定0000表示算加法,那麼你希望這個"聽話"的電路:
- 收到0000,知道要把接下來的2個輸入都扔給加法器了
- →時鐘訊號來了個上升沿,於是把這次收到的二進位制串扔給加法器的輸入1
- →時鐘訊號又來了個上升沿,於是把這次收到的二進位制串扔給加法器的輸入2
- →時鐘訊號又來了個上升沿,於是把加法器的輸出取出來放到某個儲存器裡
是不是覺得這個"聽話"的電路難以設計?沒事,你不會設計不代表天才的工程師們、科學家們不會設計,假設你請來一位大神同學Q,向Ta講述你的要求:
- 收到0000之後把再收到的2個二進位制串扔給加法器的2個輸入,然後把加法器的輸出存在一個地方
- 收到0001之後,再收到第1個二進位制數假設是A,再收到第2個二進位制數假設是B,把A連加B次,算完後把結果存在一個地方
- 收到0010之後把再收到的第1個二進位制數假設是A扔給加法器的輸入1,把再收到的第2個二進位制數假設是B進行某種操作後扔給加法器的輸入2,加法器的輸出必須是A-B,把結果存到一個地方
- 收到0011之後……
- ……
看你剛才提的要求:收到0000、0001等之後就做某某事,這就是你設計的一套指令集架構(Instruction Set Architecture,簡稱ISA)
7.核心、微架構
你如果請另一個大神設同學W也設計一個"聽話"的電路來實現你的指令集,那麼同學W設計的電路不一定和同學Q設計的完全相同,甚至W設計的電路可能效率更高,有些指令執行所需的時鐘週期比Q設計的電路需要的更少
Q和W萌生了獨自創業的想法,他們都認為用這個電路繼續搭建出完整的微控制器還得燒更多錢,不如以後只設計這種電路算了,把設計好的方案賣給其它設計微控制器的公司,這樣雙方都能專注於各自的領域,最終產品的效能可以更優越,雙方也都能節省精力
為了方便微控制器公司,Q和W把他們設計的電路繼續完善,比如整合加法器等等
Q和W完善之後的東西叫核心(Core),就是微控制器核心、電腦處理器的核心,他們實現核心的方式叫微架構(Microarchitecture),他們的核心都能實現你設計的指令集,他們給自己設計的核心設定獨一無二的名字
現實世界中大名鼎鼎的ARM公司就像大神Q、W,ARM公司專注於設計核心,把核心設計方案賣給大名鼎鼎的ST、ADI、TI、NXP、華為海思、高通、蘋果等等晶片設計公司
ARM公司現在設計的核心的名字都是Cortex ??,第1個問號是"A"、"R"或"M",第2個問號是十進位制數,可能是1位也可能是2位十進位制數,以後可能會是更長的十進位制數。例如上圖中的Cortex A53、Cortex M3
高通買來Cortex A53的授權,開發出驍龍625;ST買來Cortex M3的授權,開發出STM32F1系列
驍龍625常被稱為片上系統(System on Chip,簡稱SoC)或者微處理器(Micro Processor),常被用於手機等等;STM32F1系列常被成為微控制器(Siingle Chip),被廣泛應用於家電、玩具等等
由於A53和M3的效能、功耗、成本的差異,人們對它們構成的晶片的稱呼不同,晶片的用途也不同
發現上圖中的Cortex A53、M3裡有很多東西是你不認識的?沒關係,你總會認識的
也有公司從核心到晶片全都自行設計,比如大名鼎鼎的Intel、AMD、Nvidia
回來繼續設計你的微控制器:
8.儲存器
你使用核心時,要是得全程手動地
- 把核心的各個輸入撥到合適的電平
- →按一下時鐘訊號按鈕,告訴指令譯碼器你傳完指令了
- →再把核心的各個輸入撥到合適的電平
- →再按一下時鐘訊號按鈕,告訴核心你傳完第1個操作數了
- →再把核心的輸入撥到合適的電平
- →……
這不得煩死你,這樣算算式比你手算還慢
於是你想把指令存起來,讓時鐘訊號能自己不斷地跳變,你只需把指令編好存起來就行
你需要開發儲存器,你再找來一位大神E,向Ta提要求:
- 儲存器能儲存海量的0、1狀態
- 有1個讀寫控制引腳,比如高電平表示寫資料,低電平表示讀資料
- 每8個0、1狀態為一個儲存單元,給儲存單元們編號
- 有7根地址線,給它們輸入7個bit就能選中這7個bit組成的二進位制數編號的儲存單元
- 有8根資料線,讀資料時8根資料線輸出選通的儲存單元的8個bit,寫資料時儲存器把8根資料線上的8個bit寫入儲存單元
- 輸入儲存單元的編號到地址線,給讀寫控制訊號輸入讀,那麼儲存器的資料線輸出指定儲存單元裡的內容
- 輸入儲存單元的編號到地址線,給讀寫控制訊號輸入寫,輸入你想寫入的二進位制串到資料線,那麼儲存器的指定儲存單元的內容就更新成你寫入的二進位制串
-
有1個高電平使能引腳(使能(enable),可以理解為使……能……,比如高電平使能,那麼使能腳為高電平時則儲存器能工作,否則不工作,高電平就叫使能腳的有效電平)
-
使能腳電平無效時儲存器的資料線呈高阻態(高阻態也稱Hi-Z,如果你認識"高"的英文單詞、知道電學用Z表示阻抗,就不難理解Hi-Z就是高阻態。稍後讓你直觀理解高阻態)
可以這麼理解高阻態:
上圖電路中如果電阻阻值越大,那麼測試點的電壓就越接近訊號源的輸出電壓,當阻值無窮大比如開路時,測試點的電壓就是訊號源的輸出電壓
儲存器不工作時資料線呈高阻態的好處:
假設一個電路中儲存器A、B都有8根資料線D0~D7,把它們各自的Dx(x是0~7的整數)接在一起,當只有一塊儲存器假設是儲存器A工作時,那麼Dx相連的點的電壓就是儲存器A的資料線的輸出電壓
至於為什麼要這麼連線儲存器A、B,這是大神Q、W給你建議的,他們把核心和儲存器聯調成功後,你就知道為什麼要這麼接儲存器A、B了
大神E經過不斷研究發現(現實世界中也是如此):
掉電後資料會丟失的儲存器的讀寫速度非常快而且造價高昂,可以每次只讀寫1個儲存單元,想讀寫哪個單元就讀寫哪個單元。這樣的儲存器被命名為隨機訪問儲存器(Random Access Memory,簡稱RAM)。大神E的初代產品叫靜態隨機訪問儲存器(SRAM),是個組合邏輯電路;後來大神E又研發出容量更大、速度更快的SDRAM、DDR等,都是時序邏輯電路
掉電後資料不丟失的儲存器有的只能寫一次,有個可以用紫外線照很久後擦掉內容後再寫,有的可以用電飛快地擦除後再寫;
大神E對掉電後不丟失資料、可以用電擦除內容的儲存器的研究成果如下:
命名 | 特性 |
EEPROM (Electrical Erasable Programable Read Only Memory) |
可讀寫任一儲存單元 整合度很低 速度慢 |
NOR Flash |
可只讀任一儲存單元 儲存區分塊,比如每4096個儲存單元為一個塊,各塊內部連續、外部相鄰 如果要寫入的儲存單元的內容是0xFF就能直接寫,否則要把儲存單元所在的塊的所有儲存單元寫為0xFF再繼續操作 整合度高些 成本低些 |
NAND Flash |
不能讀寫任一儲存單元,每次得至少讀或寫一定數量個儲存單元 整合度非常高 成本非常低 |
你可能會問:EEPROM是可寫的,為什麼叫ROM?等下你會分析該如何把掉電不丟失資料的儲存器用在你的微控制器裡,分析的時候你就知道了
這麼一看,NOR Flash、NAND Flash也屬於EEPROM的行列,但是現在EEPROM一般指上表中能隨意讀寫任一儲存單元的低整合度的儲存器
你肯定需要用到掉電不丟失內容的儲存器,但那些只能寫1次、需要紫外線擦除好久的肯定不用
如果採用EEPROM,微控制器可能無法小巧玲瓏
如果採用NAND Flash來儲存你的指令、資料,那麼會增加核心的複雜度、嚴重浪費這樣的儲存器的儲存空間、嚴重拉低核心的執行效率
那就用NOR Flash吧,能做到讀任一儲存單元,雖然做不到寫任一儲存單元,但你可以讓微控制器在執行時儘量只讀取NOR Flash的內容而不寫入(現在知道為什麼叫ROM了吧),讓需要被不斷讀寫的資料躺在RAM裡。SRAM控制簡單,就用它了
把這些儲存器塞到微控制器裡面,這樣才"單片"嘛
9.匯流排
現在Q、W修改他們設計的核心,讓核心能自動地取你儲存的指令和資料、執行完後就繼續取下一條指令及其資料
Q、W對核心做出如下修改:
- 伸出8根線用於選擇儲存器裡的儲存單元,把這8根線命名為地址匯流排
- 伸出8根線用於讀寫儲存器裡的儲存單元,把這8根線命名為資料匯流排
假設你選用的NOR Flash、SRAM的資料線都有8根、地址線都有7根,還各有1個名為EN的使能引腳(假設高電平有效),像下圖這樣連線核心和儲存器
如果用十六進位制數表示核心的地址總線上的值,高位在前低位在後,那麼顯然核心訪問地址0000 0000B~0111 1111B就能訪問到SRAM裡的內容、訪問地址1000 0000B~1111 1111B就能訪問到NOR Flash裡的內容
這樣做的好處顯而易見:核心的8條資料線最多能訪問28=256個不同的地址,上圖的連線方式把核心的地址匯流排的效能榨乾了
現在你知道為什麼要讓儲存器在使能電平無效時讓資料線呈高阻態了吧
Q、W修改復位功能,讓核心在微控制器掉電再上電後從地址0x80開始取指令、取資料、執行、取指令、取資料、執行……
可以這麼實現:
- 用幾bit的儲存器(假設命名為IP(Instruction Point),指令指標)記錄核心正在執行的二進位制所在的地址
- →核心讀出儲存器記錄的地址裡的二進位制串並執行,讀出的二進位制串可能是指令,也可能是指令需要的資料
- →每來一個時鐘週期就讓鎖存器記錄的地址+1,但是核心正在執行指令時不能讓鎖存器記錄的內容變化,否則核心會漏執行、錯執行指令
你以後把編寫好的程式從NOR Flash的首地址開始儲存,核心就能按照你的意願工作了
前面列舉算加法的步驟時用了幾個儲存器來暫存加法的計算結果,這裡又用幾個儲存器來指定核心執行的指令的地址,這些儲存器起到了反映、控制電路的執行狀態的作用。把這樣的儲存器稱為暫存器
10.外設
如果想讓微控制器能具備更多功能呢?那就塞入更多電路到微控制器裡
- 塞入IO口控制器,這樣可以控制微控制器的任一引腳的功能,比如用來輸出高低電平來控制LED的亮滅
- 塞入模數轉換器ADC,這樣微控制器就能感知外面的物理世界的變化
- 塞入儲存控制器,這樣就能非常方便地使用外接的更大的儲存器
- ……
這些被塞入的電路模組叫外設,由於它們都在微控制器內部、在核心外部,所以也叫片核心外外設
有的外設比如LED,它並不在微控制器內部,所以叫片外外設或者板載外設
有的外設在核心裡,比如加速核心執行指令用的一些外設,這樣的外設叫核內外設
11.暫存器
微控制器核心怎麼控制眾多的外設呢?給它們每個分配一個核心?這樣太複雜了,外設需要實現的功能一般很單一,那就給外設們分配一些暫存器吧
- 讓核心的地址匯流排能定位到外設們的暫存器
- →把"用於控制外設的暫存器"的值作為“控制外設的邏輯電路”的輸入
- →外設也能把它的計算結果、工作狀態寫入為它分配的資料暫存器、狀態暫存器
- →核心讀取外設的資料暫存器、狀態暫存器就能知道外設的工作結果和狀態
- 有的板載外設也有暫存器,但微控制器核心可能無法直接用地址匯流排找到板載外設的暫存器,可以和板載外設做個約定:
- →微控制器以某種規則把板載外設的暫存器的地址、微控制器想讀還是寫板載外設的暫存器發給板載外設
- →板載外設知道微控制器想訪問的暫存器地址
- →微控制器通過這種方式訪問板載外設的暫存器
至此你再次知道了那個非常重要的思想:暫存器能反映、控制微控制器的執行狀態
這裡的片內外設暫存器掛在核心的地址總線上,但是暫存器並不總是需要掛在地址總線上,比如我們用的電腦的處理器Intel、AMD公司的CPU,它的核心的很多暫存器就沒掛在核心的地址總線上,它的一些指令能讀寫這樣的暫存器
拿大名鼎鼎的STM32微控制器來舉例:
取自STM32F103x8、F103xB的資料手冊Rev. 17第34頁的記憶體對映圖(memory map)
STM32的地址線有32根,這32根地址線能訪問到232個不同的地址,上圖指出了每個地址範圍對應哪個外設的暫存器、哪個儲存器的內容,比如0x20000000對應微控制器內部的NOR Flash的首地址,0x40012400~0x40012800對應ADC1的暫存器
232個地址並非每個都會被用到,那些沒被用到的地址被標記為Reserved(保留)
如果要了解某個外設的暫存器的每一bit的意義、名稱,那就去查參考手冊上描述那個外設的暫存器的每一bit的功能的部分
取自STM32F101、2、3、5、7 參考手冊Rev. 20第237頁
- 上圖中的暫存器有32bit,STM32的32bit的暫存器會佔用32÷8=4個地址(即STM32把每8bit作為一個儲存空間來編址)
- 這32bit組成的暫存器的名字是ADC_SR
- bit31~bit5是Reserved的,不使用
- bit4~bit0都有各自的名稱和用途
- 圖的上部標註了Reset value(復位值),即微控制器復位後這32bit暫存器的值
- 圖的上部有個Address offset(地址偏移)
可以這麼理解Address offset:
- 假設晶片設計者把地址範圍0x10~0x1F總計16個地址分配給某個外設的暫存器,那麼0x10就是這個外設的暫存器地址基址
- →這16個地址中的第3、4個地址分別是0x12、0x13,假設這2個地址上的暫存器被命名為REG
- →那麼名為REG的暫存器的Address offset就是0x12-0x10=0x02
編寫程式讀寫外設的暫存器,就能控制外設
12.中斷
假設ADC等一眾外設工作得遠比核心慢,那麼核心想要實時瞭解各外設的工作進度和結果,該怎麼操作呢?不斷地讀外設們的暫存器?這樣確實可以,但是太浪費時間了,核心寶貴的效能都被浪費在不斷查詢外設們的暫存器
能不能讓外設主動告訴核心並讓核心迅速處理?當然可以,這種技術叫中斷
可以這麼實現:
- 外設們伸一根中斷訊號線到核心
- →外設遇到需要核心來處理的事情比如ADC發現根本沒有需要轉換的模擬訊號輸入給它時就在它伸向核心的中斷訊號線上產生有效的訊號比如上升沿
- →於是核心迅速記錄手頭的工作所在的地址並停下手頭的工作
- →核心去處理ADC那邊的事(事的內容、地址由程式設計人員指定)
- →處理完ADC的事之後接著做剛才被打斷的工作
Q和W也覺得中斷技術非常重要,於是把中斷控制器整合到了他們設計的核心中
13.燒錄/下載
你可以設計一個專門用來連線電腦和微控制器的NOR Flash的外設放到微控制器裡,比如很多同學學習微控制器知識用的第1塊微控制器STC89C51就在微控制器裡集成了名為通用非同步收發器(Universal Asynchronous Receiver Transmitter,簡稱UART)的外設,它負責微控制器與微控制器之間、微控制器與電腦之間以符合UART的規則的方式通訊。STC公司開發了執行在Windows的專門用來向STC微控制器寫入程式的軟體STC-ISP,STC微控制器的UART外設收到某串二進位制後就產生一箇中斷告訴核心把稍後UART接到的二進位制串寫入NOR Flash
這個過程叫燒錄或者下載
在很久以前,給微控制器的儲存器寫入程式需要高壓,一不小心就燒燬了微控制器,所以有了"燒錄"這個名稱
微控制器從電腦獲取指令,像不像你通過因特網從伺服器下載遊戲?所以給微控制器的儲存器寫入程式也可以叫"下載"
14.組合語言
有沒有覺得總是用二進位制串給微控制器下達指令非常麻煩?不如用助記符代替這些二進位制指令吧,於是組合語言產生了
核心肯定不認識你創造的助記符,得用軟體把助記符轉成核心能直接執行的二進位制,實現這個功能的軟體叫彙編器(assembler)
組合語言有個很大的缺陷:過於依賴核心的工作流程、儲存器的佈局
你用匯編語言寫程式的話,得先打草稿來劃分儲存空間,比如地址0x12~0x21用來暫存計算的中間結果,地址0x80~0x8F用來存實現某個功能的指令,你希望掉電後依然能儲存使用者設定的一些變數,那麼得在Flash裡找一塊空間用來存這些變數……
把用匯編語言寫的程式移植到其它核心、其它儲存器佈局上可是個天大的工程,不如創造一種接近人類的語言、不依賴於核心和儲存器的程式語言吧,C語言就是這種語言的代表之一
15.C語言
核心更不可能認識C語言,所以需要用軟體把C語言程式碼轉成核心能直接執行的二進位制,需要多個軟體來實現:
編譯器:把C語言程式碼轉成微控制器能理解的二進位制,還沒完:都說了C語言可以不依賴儲存器佈局、你可以在C語言程式碼裡不寫任何一個儲存器地址,編譯器得到的結果並不包含任何一個儲存器地址,所以微控制器肯定不能執行編譯器的產物
聯結器:
- 告訴聯結器哪個地址範圍用來存指令及其資料,哪個地址範圍用來存變數,聯結器來完成地址分配的工作。你也可以在C語言程式碼中告訴聯結器把某些資料或指令放到哪個或哪段地址上
- 你寫的C程式碼可能用到了一些迴圈、跳轉,聯結器還得想辦法連接出這樣一份完整的二進位制,微控制器一條條地執行這份二進位制時的效果就像你用C語言寫的那樣能迴圈、跳轉
- C語言程式碼裡可能有很多指令並不會被執行,可能有很多資料並不會被用到,聯結器可以只挑出會被執行的指令、會被用到的資料來連線,這樣最終生成的一份二進位制的體積就能儘量小
如果你在Windows作業系統上用Visual Studio、Code::Blocks、Dev C++等IDE寫C語言程式,那麼聯結器得連線處能讓Windows作業系統呼叫的可執行(executable,Windows下這種檔案的副檔名是.exe)檔案,這個步驟與連線處微控制器能直接執行的一份二進位制有所不同,你去了解一下Windows作業系統執行EXE檔案的步驟就能大致猜出到底哪裡"有所不同"了
有了C語言,開發、除錯微控制器程式就輕鬆多了
16.除錯
假設寫了個非常長的程式,雖然微控制器能正常執行,但是執行的最終結果卻是錯的。程式設計人員特別想讓微控制器慢點執行,以便觀察到微控制器執行完關鍵步驟後的結果是否正確,這些"結果"可能在暫存器,可能在SRAM,也可能是某個板載外設的工作狀態
上述的過程叫除錯(debug)
Q和W也認為除錯功能非常重要,於是把除錯系統整合到他們設計的核心裡
再拿大名鼎鼎的STM32微控制器來舉例:
有一種叫硬體偵錯程式的電子系統,支援STM32的硬體偵錯程式有很多種,比如ST-Link、J-LInk、CMSIS DAP-Link等等
硬體偵錯程式能對接微控制器和電腦,通過硬體偵錯程式,能下載程式到微控制器,電腦也能控制微控制器控制微控制器
- 一步步地執行指令(即單步執行)
- 執行到某條指令前停住(即設定斷點)
- 實時檢視和修改某些暫存器、變數的值
- 檢視C程式碼被編譯成的二進位制程式碼及其彙編程式碼形式
- ……
上圖是用Keil μVision MDK-ARM(這是個整合開發環境Integrated Development Environment,簡稱IDE,包含程式碼編輯器、偵錯程式、編譯器、聯結器等等)+ST-Link硬體除錯STM32F407ZG的介面,上半部分是反彙編(Disassembly),下半部分是原始碼
- 看上圖中更靠上的青色、綠色、紅色方框:
- 地址0x0800 7318和0x0800 7319處的2個位元組0xB530被核心當作一條指令,這條二進位制指令對應的助記符是PUSH (r4-r5,lr)
- 再看圖中的紫色方框:
- 圖中下半部分對應一個C原始檔,其第135行的內容出現在了紫色方框中,
- 再看上圖中更靠下的青色、綠色、紅色方框:
- 剛才說的C程式碼對應的二進位制指令被儲存在地址0x0800 7310~0x0800 7320+(4-1)即0x0800 7323
Keil μVision MDK-ARM的偵錯程式還有很多很方便的工具,比如能實時檢視某個變數的值得Watch、能檢視正在執行的指令是怎麼被一路呼叫來的呼叫棧Call Stack等等
現在你的微控制器已經很完善了,成功面世
但是有客戶在用你的微控制器設計產品時遇到了一些問題:
- 微控制器做某些事情非常慢
- 你的微控制器的暫存器太多,如果要用到很多片內外設,那麼在初始化外設時得寫非常多的暫存器,容易寫錯
- 微控制器同時處理多件事時不夠流暢
17.軟體實現、硬體實現、通用計算、ASIC、超頻
那些讓微控制器工作得很慢的程式碼可能頻繁用到一系列操作,可能會讓微控制器執行很多條指令才能完成這樣的1個操作,比如沒有內建乘法器電路的微控制器算整數的乘法得用連加
連加就是對乘法的軟體實現,用乘法器電路算乘法就是硬體實現
再比如某個板載外設需要以某種規定與微控制器通訊,程式設計人員可以分析那個板載外設的通訊規則之後,編寫程式讓微控制器的某些引腳在適當的時刻產生適當的電平來與板載外設進行通訊,程式設計人員得非常仔細地分析這種通訊規則,一個細節都不能出錯
如果這種通訊規則非常常用,那麼微控制器廠商可以用電路來實現這種通訊規則。把這個電路做成一個片內外設,微控制器核心給這個片內外設的控制暫存器寫入控制字,之後核心只需把要傳送的內容傳給這個外設,這個外設就能在不需要核心干預的情況下正確地將資訊傳送出去
事實上有很多通訊規則已經被硬體化了,比如SPI、SAI、I2C、I2S、USART、USB、HDMI、PCIe、SATA等等
用硬體電路實現通訊協議的優勢有:
- 最大通訊速率更快
- 程式設計人員的程式設計、除錯代價更小,如果程式設計人員水平不足,那麼這條可能不成立
用軟體實現的優勢當然是金錢成本更低
但是硬體實現並非總是優於軟體實現,例如機械按鍵的延時消抖,學過微控制器程式設計的入門例程的同學有感觸,沒學過的話以後就去學吧
除錯也能不用到硬體偵錯程式,而純軟體實現,這樣的除錯叫模擬器除錯
如果你對電腦有點研究,而且還必須要玩高畫質、高幀率的遊戲,那麼你買打遊戲用的電腦時肯定會選帶獨立顯示卡的
打遊戲時,獨立顯示卡專注於計算遊戲裡炫酷的畫面,CPU專注於其它事情
舉個例子:
- 假設把大型3D遊戲的畫質調到最低的等級,和敵人激戰時獨立顯示卡和CPU的負載率都很低,都不到10%
- 如果禁用獨立顯示卡,那麼這個遊戲的畫質、幀率嚴重下降,玩遊戲時CPU的佔用率也極高
在這個例子中,CPU拼死也幹不好獨立顯示卡輕輕鬆鬆能幹好的事
獨立顯示卡設計者畫大力氣用硬體電路實現計算顯示畫面的演算法,可能獨立顯示卡一條指令能做好的事CPU需要成千上萬條指令才能做好
在上面的例子中,有4個核心的CPU就像是4個博士畢業生,有640個(真的可以有這麼多)核心的獨立顯示卡就像640個小學生
- 如果他們比賽做640道10以內的加減法,那麼640個小學生確實可以秒殺4個博士畢業生
- 如果他們比做博士畢業生的專業課試題,那麼1個博士畢業生就能秒殺640個小學生
電腦CPU的定位是做通用計算,通過支援複雜的指令集,讓程式設計人員編寫複雜的程式能實現複雜的功能
獨立顯示卡是計算顯示畫面(其實不限於計算顯示畫面)的專用積體電路(Application Specific Integrated Circuit,簡稱ASIC),通過硬體電路實現了可以非常高效地實現某些功能,而在其它功能的實現上並不一定強於CPU
剛才的例子也能說明有的工作通過並行執行能極大地提升效率,比如算10以內的加減法
但有的工作卻不能,假設你和你的一個同學都喜歡玩第一人稱射擊遊戲,但你們的技術都不行,於是你們合作:一個人控制滑鼠,一個人控制鍵盤,你們"合體"的效果可能並沒多少提升甚至更差。在這個例子中提升玩第一人稱射擊遊戲水平的方法就一個人控制一臺電腦不斷練習槍法、走位、意識、隊友配合、技能Combo等等,類比到微控制器和電腦處理器就是提升執行速度,最常見的方法之一就是超頻
核心是被時鐘訊號指揮的,如果時鐘訊號跳變得更快,那麼核心就會執行得更快。核心收到的時鐘訊號的頻率叫核心的主頻,把主頻提升到高於廠家建議的最高主頻即超頻,但是不能無限制提超頻,超頻會讓核心執行得更不穩定甚至燒燬核心
現在的電腦CPU已經很智慧了,比如Intel的酷睿CPU能睿頻,即任務繁重時提升主頻,任務少時降低主頻來降低功耗
18.庫函式
通過寫微控制器眾多的暫存器來設定片內外設確實容易出錯還費時費力,可以把配置暫存器這種繁瑣的工作用函式來實現
例如ST公司為STM32系列微控制器編寫了豐富的庫函式,使用者可以用C語言呼叫庫函式來配置STM32的外設暫存器
庫函式的優勢就是優秀的C語言程式碼的優勢:可以用英語把程式碼讀出來
劣勢就是執行效率更低,你試試把用庫函式初始化微控制器的C程式碼反彙編就知道了:如果寫暫存器只需要幾行彙編指令,那麼用庫函式,得先用好多好多個RAM儲存單元來暫存外設的某個功能的標誌位,最終再把這些標誌位做運算後賦值給暫存器
STM32的效能足夠,所以程式碼量不大時庫函式並沒有顯著降低微控制器的執行效率
19.作業系統——儲存管理、併發……
剛才說過,用匯編語言寫程式碼需要打草稿劃分儲存空間,用C語言寫直接執行在微控制器核心上的程式碼雖不用打剛才的草稿,但聯結器需要打這個草稿,這個草稿的存在使得最終得到的二進位制程式碼不一定能正常執行在儲存器地址分佈與你的微控制器的不同的微控制器上
剛才還說過,核心的工作就是不斷地順序地取指令、取資料、執行,還能響應中斷,如果有多件事需要微控制器同時做,那麼微控制器只得不斷地交替做這些事情,導致有些很閒的事浪費微控制器的大量資源,而有些很忙的事無法被微控制器及時響應
作業系統就能解決上面的2個問題
作業系統能動態地管理儲存器,能合理地分配核心資源到不同任務,對作業系統的詳細描述已超出本文討論的範圍,你知道作業系統的優勢就行
完結撒花,做個重點總結:
- 電晶體能構成邏輯閘,進而構成功能複雜的組合邏輯電路和時序邏輯電路
- 核心能從儲存器的某個地址開始不斷地取指令、取資料、執行
- 指令集規定某個二進位制串讓核心實現什麼功能
- 不同架構的核心能實現相同的指令集
- 核心內部、核心外部微控制器內部、微控制器外部都有外設
- 核心可以通過匯流排訪問、控制儲存器、外設等等
- 暫存器能反映、控制電路的執行狀態
- 外設可以通過中斷來打斷核心的工作、讓核心去執行其它工作、做完其它工作後返回執行剛才被中斷的工作。中斷技術提升了對核心效能的利用率
- 組合語言、C語言極大地方便了開發微控制器程式
- 除錯功能能幫助你檢查程式存在的邏輯錯誤
- 功能可以被硬體實現或軟體實現,各有優劣
- 通用計算和專用計算各有優劣
- 實際上微控制器、處理器的匯流排位數、儲存器地址線資料線位數等等並不一定都與本文中我讓你用的方案一致,本文中的方案只是示例,以你實際使用的晶片的資料手冊為準