?? firmware.html
字號:
<html><head><title>FirmWare</title></head><body><p align="center"><font color="#FF0000"><b>MiniNurse(MN)固件源代碼及其分析</b></font></p><blockquote> <blockquote><p><br>對于像c51這樣簡單的芯片,其固件就相當于操作系統,一個相當原始和粗糙的操作系統。下面是一個"Hello World"性質的固件片斷(C for51),我做了一些注釋:</p> <blockquote> <p>void main (void) <font color="#808080">/* 芯片加電后PC為0,轉到這里執行。 */</font><br> {<br> <font color="#808080">/* 這里可能會有中斷、定時器、等的初始化代碼 */</font><br> while(1) { <font color="#808080">/* 注意!一個死循環,這正是固件成為操作系統的原因。 */</font><br> <font color="#808080">/* 如果沒有這個循環執行完后面的代碼后,芯片會處于空閑狀態。只有重置才能喚醒它。 */</font><br> P1 ^0= 0x01; <font color="#808080">/* 這兩行就是我們的示例代碼,它們是唯一的“進程”,一直被執行。 */</font><br> printf ("Hello World\n"); <font color="#808080">/* printf()通常會從51的串口輸出。*/</font><br> }<br> }</p> </blockquote><p>MN的固件要比這復雜。它的固件是分層的,這對固件的可理解性、可移植性以及健壯性都非常重要。比如,如果換用Motorola或者其它種類的單片機,只需要修改最底層的代碼就可以了,這個工作量是非常小的。又如要擴展MN的功能,只要添加相應的廠商請求部分以及可能的主循環(mainloop.c)代碼,這個工作量會因為擴展的功能而異,但如果不是分層,代碼的維護將會是可怕的。<br> <br>MN的固件代碼,分成以下幾個層次:<a href="#硬件抽象層:base_io.c base_io.h">硬件抽象層</a>,<a href="#D12命令接口:D12_comm_if.c D12_comm_if.h">D12命令接口</a>,<a href="#中斷服務例程:isr.c">中斷服務例程</a>,<a href="#USB標準請求:usb_standard_request.c usb_standard_request.h">USB標準請求</a>、<a href="#USB廠商請求:usb_usr_request.c usb_usr_request.h">廠商請求</a>,<a href="#主循環:mainloop.c mainloop.h">主循環</a>等。</p> </blockquote></blockquote><p> </p><blockquote> <p><font color="#000080"><b><a name="硬件抽象層:base_io.c base_io.h">硬件抽象層:base_io.c base_io.h</a></b></font></p> <blockquote><p>該層定義了C51和D12通訊的方法。base_io.h中聲明了兩個函數(復用方式):</p> <blockquote> <p> void outportb(unsigned int Addr,unsigned char Data);<br> unsigned char inportb(unsigned int Addr);</p> </blockquote><p>outportb()為發送信息到D12,inportb()相反。<br> <br>51和D12之間的信息有數據和地址之分。在51看來,它連接的是一個特殊的擴展RAM,這個RAM只有兩個地址狀態。這個兩個地址狀態對于D12來說則意味著下次到達的信息是數據還是對它的控制命令。D12的手冊上說,偶數地址為數據,奇數地址為命令。參考outportb()的實現:</p> <blockquote> <p>void outportb(unsigned int Addr,unsigned char Data) {<br> *((unsigned char xdata *)Addr)=Data;<br> }</p> </blockquote><p>在base_io.h中,有如下兩行:</p> <blockquote> <p>#define D12_Command 0xff03<br> #define D12_Data 0xff02</p> </blockquote><p>在更高層的代碼中,會經常看到類似如下的調用:</p> <blockquote> <p>outportb(D12_Command, 0xFD);</p> </blockquote><p>這條語句說明0xFD是對D12的一個命令(該命令讀取芯片ID)。實現中的xdata是C for 51的擴展,被這個關鍵詞修飾的變量存儲在外部RAM中,前面說過,D12就相當與51的一個擴展RAM,所以這里就是在D12中。C for 51還有其它的擴展,可以參閱具體的編譯器文檔,它們之間有細微差別,本文只在必要的時候做必要的解釋。這里給出一種編譯器的擴展規則,僅供參考:</p> </blockquote></blockquote><p align="center"><img border="0" src="../../Set%20of%20Docutments/FIRMWARE_Explicitly%20Declared%20Memory%20Types.png" width="648" height="276"><br>截取自C51</p><blockquote> <blockquote><p>inportb()的實現方法與outportb()絕類,只是方向相反。<br><br> D12除了支持上面使用的這種所謂“復用方式”外,還支持非復用方式。這種方式通過控制D12上的A0管腳區分指令和數據。具體的參看D12手冊和硬件設計文檔。這種方式下需要修改outportb()和inportb()的實現,代碼和注釋如下。不過這種方式并沒有試驗成功,目前還不確定是否是固件的問題,僅示意!語言順序和那些看上去沒有意義的語言成份都是試圖符合D12的時序要求。</p> <blockquote> <p>void outportb(unsigned int Addr,unsigned char Data) //send to D12<br> {<br> switch (Addr) //in fact, 'Addr' should be described as 'Command or Data select word'<br> {<br> case D12_Command: //send command-byte to D12<br> A0c=D12_Command;<br> break;<br> case D12_Data: //send data-byte to D12<br> A0c=D12_Data;<br> break;<br> }<br> <br> WRc=0; //Write immediately<br> do {if (1+1==2);} while(0); //重復執行上行語句可能是更好的延時方法!<br> P0=Data;<br> do {if (1+1==2);} while(0); //重復執行上行語句可能是更好的延時方法!<br> WRc=1; //enclose<br> }<br> <br> unsigned char inportb(unsigned int Addr) //receive from D12<br> {<br> unsigned char tmpData;<br> switch (Addr) //in fact, 'Addr' should be described as 'Command or Data select word'<br> {<br> case D12_Command:<br> A0c=D12_Command;<br> break;<br> case D12_Data:<br> A0c=D12_Data;<br> break;<br> }<br> <br> RDc=0; //Read immediately<br> do {if (1+1==2);} while(0); //重復執行上行語句可能是更好的延時方法!<br> tmpData=P0;<br> do {if (1+1==2);} while(0); //重復執行上行語句可能是更好的延時方法!<br> RDc=1; //enclose<br> return tmpData;<br> }</p> </blockquote> <p align="center"><img border="0" src="../../Set%20of%20Docutments/FIRMWARE_D12_timing.PNG" width="743" height="468"><br> 截取自Philips PDIUSBD12手冊</p> <p align="center"> </p> <p align="center"> </p> </blockquote> <p><b><font color="#000080"><a name="D12命令接口:D12_comm_if.c D12_comm_if.h">D12命令接口:D12_comm_if.c D12_comm_if.h</a></font></b></p> <blockquote><p>D12向外提供了十數條指令,芯片手冊上有詳細的列表和語法說明。D12_comm_if.h聲明了它們中的大多數,因為有幾條指令并沒有多少用處。D12_comm_if.c中的實現完全根據芯片手冊的定義而寫,沒有什么可以說的。仍以讀芯片ID的例子做示范性說明,這個命令似乎也沒有實際的用途,不過卻可以知道D12是否正常工作。<br> <br>讀芯片ID的命令號是0xFD,這個剛才看過了。該指令后跟兩個八位數據,方向是D12-->51。在實現中,可以看到發送命令后,讀了兩次數據。第一次讀到的是低位,第二次是高位,做了位操作后拼裝成一個16位整型返回。(是的,16位。8位單片機還能怎樣?)為了方便,將代碼再列下:</p> <blockquote> <p>unsigned short D12_ReadChipID(void)<br> {<br> unsigned short tmpi,tmpj;<br> <br> if(bEPPflags.bits.in_isr == 0)<br> DISABLE;<br> <br> outportb(D12_Command, 0xFD);<br> tmpi=inportb(D12_Data);<br> tmpj=inportb(D12_Data);<br> tmpi += (tmpj<<8);<br> <br> if(bEPPflags.bits.in_isr == 0)<br> ENABLE;<br> return tmpi;<br> }</p> </blockquote> <p> </p> </blockquote>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -