使用.Net Micro Framework 模拟器 – 基本原理
摘要:虚拟仪器(VI)技术在很多高成本的工业项目中扮演着越来越重要的角色,在电子产品的开发中,各种模拟器也为广大开发者方提供了便利,提高了生产力。本文介绍了微软最新的嵌入式开发框架.Net Micro Framework的模拟器。阐述了模拟器的启动过程,链接组件和注册信息等基本工作过程和原理。
Keywords
.Net Micro Framework, Hardware Emulator, Start Up, Component, Registry
.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研究—模拟器改造
http://blog.csdn.net/yefanqiu/archive/2007/10/31/1860020.aspx
Ø .Net Micro Framework研究—带IO的模拟器
http://blog.csdn.net/yefanqiu/archive/2007/12/30/2005462.aspx
Ø .Net Micro Framework研究—带AD的模拟器
http://blog.csdn.net/yefanqiu/archive/2007/12/31/2006197.aspx
Ø .Net Micro Framework研究—带I2C总线的模拟器
http://blog.csdn.net/yefanqiu/archive/2007/12/31/2006370.aspx
配置引擎(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我们可以看到它的内容如下:
{
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中我们可以看到这样一个函数:
/// 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还有一个比较常用的用途就是用于在初始化的时候检查必备的组件。例如以下代码所示:
{
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();
}
好了,今天暂且写到这里,关于模拟器还有很多值得探讨的地方,欢迎有兴趣的朋友与我交流。
参考资料: