摘要:虛擬儀器(VI)技術在很多高成本的工業項目中扮演著越來越重要的角色,在電子產品的開發中,各種模擬器也為廣大開發者方提供了便利,提高了生產力。本文介 紹了微軟最新的嵌入式開發框架.Net Micro Framework的模擬器。闡述了模擬器的啟動過程,鏈接組件和注冊信息等基本工作過程和原理。
.Net MF模擬器簡介
.Net MF模擬器是一個.Net MF CLR的Windows 版的運行環境。運行時的核心部分(執行引擎,類型系統和GC等)和基礎類庫都和在實際設備上跑的MF一樣。兩者不同之 處在於HAL層(Hardware Abstract Layer)。模擬器的HAL層並不是實際的硬件設備驅動,而是由Windows提供的驅動代替。這裡充分體現了MF的架構中層與層之間高度分離的 作用,為模擬的真實性和准確性提供了基礎。
下圖說明了.Net Micro Framework模擬器的邏輯結構
模擬器組件(Emulator Components)是模擬器的組成元素,通過它們你可以開發出自定 義的硬件模擬器,這裡MVP劉洪峰有些不錯的自定義模擬器的例子:
Ø.Net Micro Framework研究—模擬器改造
Ø.Net Micro Framework研究—帶IO的模擬器
Ø.Net Micro Framework研究—帶AD的模擬器
Ø.Net Micro Framework研究—帶I2C總線的模擬器
配置引擎(Configuration Engine)用於在運行時把組件裝配在一起。它使用XML來描述硬件模型的各種配置參數。
啟動過程
在調試的時候,我們只需要在 項目屬性中選擇模擬器作為部署目標。在部署的時候,模擬器會自動被調用並運行我們的程序,但是這個過程掩蓋了模擬器啟動的細節。.Net Micro Framework的模擬器也 就是一個.Net(通常是Windows Form)應用程序,只不過模擬器加入了組件(Component)和XML配置文件(Emulator.config)的內容。當模擬器啟動的時候:
1. 加載默認 組件
模擬器的LoadDefaultComponents()虛方法被調用,它通過調用一系列的RegisterComponent方法來創建模擬器默認的基本組件。您也通過配置文件 (Emulator.config)對這些默認組建進行配置,替換或者刪除。在代碼裡面我們也可以通過Emulator類的一系列屬性來訪問這些默認組件。
2.讀取配置文件
默認的組件被構造完畢之後,Emulator的配置引擎會去讀取XML配置文件(Emulator.config)。配置文件中描述的組件會被按要求創建並注冊。此時調用了Emulator類的 Configure(System.Xml.XmlReader reader)方法來從XML配置文件中解析類型。
3.安裝組件
經過前兩步,所有組件都已經配置完畢了。這時候系統會去調用 Emulator和自定義組件類的SetupComponent方法來組裝之前創建好的組件。
4.初始化組件
在所有組件安裝完畢以後,Emulator類和EmulatorComponent類的 InitializeComponent方法會被調用,來對各個組件進行初始化。
5.加載.Net Micro Framework的程序集
6.運行模擬器
在實際的開發過程中,一般來 說,除非是要開發自己的模擬器組件,否則Configure, SetupComponent和InitializeComponent是不需要程序員在代碼裡面調用的。
注冊組件
通過編程注冊 模擬器組件需要調用RegisterComponent方法。前面已經提到,模擬器的配置引擎在會在config階段調用該方法。實際上在調用LoadDefaultComponents()的時候實際上就是在 內部調用了RegisterComponent方法。使用reflector我們可以看到它的內容如下:
protected virtual void LoadDefaultComponents() { this.RegisterComponent(new Hal()); this.RegisterComponent(new ComPortCollection()); this.RegisterComponent(new GpioCollection()); this.RegisterComponent(new SpiBus()); this.RegisterComponent(new MemoryManager()); this.RegisterComponent(new TimingServices()); this.RegisterComponent(new I2cBus()); this.RegisterComponent(new SerialPortCollection()); this.RegisterComponent(new NamedPipeServer()); }
實際上LoadDefaultComponents()就是在為默認的組件進行注冊。
如果是手動注冊,需要注意的是,如果一個組件包含於另一個組件那麼子組件注冊的同 時需要提供父組件給RegisterComponent的一個重載方法。在調用UnregisterComponent移除組件的時候,所有鏈接到它的子組件都會被自動刪除。例如MemoryManager組件, 它有兩個子組件RamManager 和 FlashManager。當我們移除MemoryManager時,RamManager 和 FlashManager也會被移除。
訪問已注冊組件
已注冊的模擬器組 件可以通過Emulator類的FindComponentById方法來訪問。我們經常會調用這個函數來對指定的組件進行初始化。例如在SDK提供的SampleEmulator中我們可以看到這樣一個 函數:
/**//// <summary> /// Helper method to initialize buttons. /// </summary> /// <param name="button">The button to be initialized.</param> /// <param name="componentId"> /// The string identifying the button, /// found in the config file /// </param> /// <param name="key">The key that this button should respond to.</param> private void InitializeButton(Button button, string componentId, Keys key) { button.Port = _emulator.FindComponentById(componentId) as Gpio.GpioPort; button.Key = key; }
FindComponentById還有一個比較常用的用途就是用於在初始化的時候檢查必備的組件。例如以下代碼所示:
public override void InitializeComponent() { base.InitializeComponent(); //以下兩行代碼即檢查了模擬器是否有名為“Pin_Down”的組件 //同時也檢查了該組件的類型是不是GpioPort if (FindComponentById("Pin_Down") as GpioPort == null) throw new Exception("The component 'Pin_Down' is required by the emulator."); // 啟動UI線程 Thread uiThread = new Thread(StartForm); uiThread.Start(); }
好了,今天暫且寫到這裡,關於模擬器還有很多值得探討的地方,歡迎有興趣的朋友與我交流。