在.Net Micro Framework中访问硬件 - part1
在.Net Micro Framework中访问硬件 - part1
摘要:本文介绍了.Net Micro Framework中对硬件的简单而独特的访问方式。涉及GPIO,RS232串口等。通过简明的例程说明了如何创建IO口,如何发送接受数据的过程。
Keywords:
.Net Micro framework, GPIO, RS232, Embedded, C#
1.GPIO
在.Net Micro Framework中,GPIO的状态被定义为布尔型,false->低, true->高。
Tips这里说的低(0伏)和高(3.3伏)是指你在设置GPIO的时候,实际加在GPIO口的电压。而在考虑输入的时候一般1v以下会被认为是逻辑低,1.7~5.5伏通常认为是逻辑高。超过5.5的电压是如果不加保护电路通常是会损坏你的硬件的。
1.1输出
在Microsoft.SPOT.Hardware命名空间下,你可以找到OutputPort类,它继承自Microsoft.SPOT.Hardware.Port---一个用于描述GPIO的基础类。
定义OutputPort一般都会初始化一个默认值(true代表高,false表示低)。
OutputPort outputPort = new OutputPort(MyPins.StatusLED, true);
/*第一个参数是枚举类型Microsoft.SPOT.Hardware.Cpu.Pin,不过为了使你的代码更灵活,非常建议你使用自己封装的类来绑定CPU的管脚名称和GPIO口的编号。*/
接着,outputPort的Write和Read方法就可以使用了,Write方法控制了管脚的电平状态,Read方法用于返回当前状态,也即上一次设置的状态。
下面的例子用于让自定义pin的led按1hz的频率闪烁(实际上是亮暗各0.5s左右)。
OutputPort outputPort = new OutputPort(MyPins.StatusLED, true);
while (true)
{
Thread.Sleep(500);
outputPort.Write(!outputPort.Read()); //toggle port
}
while (true)
{
Thread.Sleep(500);
outputPort.Write(!outputPort.Read()); //toggle port
}
1.2 输入
和OutputPort相类似,Microsoft.SPOT.Hardware.InputPort扮演了输入的功能。它同样继承自Microsoft.SPOT.Hardware.Port。下面的例子演示了如何在一个无限循环中轮询inputPort的状态。
using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
…
InputPort inputPort = new InputPort(Cpu.Pin.GPIO_Pin2,//管脚号
false,//是否过滤毛刺信号
Port.ResistorMode.PullDown);//电阻模式,这里设为下拉即合上开关(按下按钮)时GPIO状态为高
while (true)
{
bool state = inputPort.Read(); //polling of port state
Debug.Print("GPIO input port at pin " + inputPort.Id +
" is " + (state ? "high" : "low"));
//留一点时间让设备或者模拟器能够对VS反应
Thread.Sleep(10);
}
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
…
InputPort inputPort = new InputPort(Cpu.Pin.GPIO_Pin2,//管脚号
false,//是否过滤毛刺信号
Port.ResistorMode.PullDown);//电阻模式,这里设为下拉即合上开关(按下按钮)时GPIO状态为高
while (true)
{
bool state = inputPort.Read(); //polling of port state
Debug.Print("GPIO input port at pin " + inputPort.Id +
" is " + (state ? "high" : "low"));
//留一点时间让设备或者模拟器能够对VS反应
Thread.Sleep(10);
}
另外,还有一个可以在运行时动态改变状态地逻辑端口Tristate Port。听名字似乎是三态端口,不过事实上目前它只能在高和低之间切换。
1.3 中断
如果你仅仅只是想等待一个按键的按下,那么使用前面那种使用无限循环的方式来读取输入口的状态的办法,无疑显得太耗了点,我们不并希望Cpu总是处于这么忙碌的状态,比如某些状况下(例如电池)。我们只是希望某些外部消息和请求以中断的形式告诉Cpu,在.Net MF中我们使用InterruptPort类来实现相应地功能,中断可以理解为硬件之间的事件。如果MCU除了等待某个GPIO口的事件而没有其他事情做的话,该处理器就可以放心的进入省电模式了。当一个信号的改变发生在相应的输入管脚的时候,MCU会“醒”来,然后相应的中断服务程序(ISR)会被执行
你可以把中断端口设置为上升沿触发或者下降沿触发。或者两者都触发,也可以是高低电平触发。如下程序演示了如何在一个脉冲的上升沿和下降沿都引起中断,中断服务程序的入口点由相应的委托OnInterrupt来指派。
using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
…
InterruptPort port =
new InterruptPort(Cpu.Pin.GPIO_Pin3,
false, //不过滤毛刺
Port.ResistorMode.PullDown,
Port.InterruptMode.InterruptEdgeBoth);//中断模式设为双边沿触发
port.OnInterrupt += new GPIOInterruptEventHandler(port_OnInterrupt);//指定中断服务程序
Thread.Sleep(Timeout.Infinite);
…
private static void port_OnInterrupt(Cpu.Pin port,
bool state,
TimeSpan time)
{
Debug.Print("Pin=" + port + " State=" + state + " Time=" + time);
}
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
…
InterruptPort port =
new InterruptPort(Cpu.Pin.GPIO_Pin3,
false, //不过滤毛刺
Port.ResistorMode.PullDown,
Port.InterruptMode.InterruptEdgeBoth);//中断模式设为双边沿触发
port.OnInterrupt += new GPIOInterruptEventHandler(port_OnInterrupt);//指定中断服务程序
Thread.Sleep(Timeout.Infinite);
…
private static void port_OnInterrupt(Cpu.Pin port,
bool state,
TimeSpan time)
{
Debug.Print("Pin=" + port + " State=" + state + " Time=" + time);
}
下面的例子演示了电平触发的过程:
…
interruptPort =
new InterruptPort(Cpu.Pin.GPIO_Pin2,false, Port.ResistorMode.PullUp,
Port.InterruptMode.InterruptEdgeLevelLow);//中断模式设置为低电平触发
interruptPort.OnInterrupt +=
new GPIOInterruptEventHandler(port_OnInterrupt);
Thread.Sleep(Timeout.Infinite);//除了等待中断以外,该线程休眠
…
private static void port_OnInterrupt(Cpu.Pin port,
bool state,
TimeSpan time)
{
Debug.Print("Pin=" + port + " State=" + state + " Time=" + time);
//清除当前中断端口的状态以便下一次指定电平到来时仍能触发中断
interruptPort.ClearInterrupt();
}
interruptPort =
new InterruptPort(Cpu.Pin.GPIO_Pin2,false, Port.ResistorMode.PullUp,
Port.InterruptMode.InterruptEdgeLevelLow);//中断模式设置为低电平触发
interruptPort.OnInterrupt +=
new GPIOInterruptEventHandler(port_OnInterrupt);
Thread.Sleep(Timeout.Infinite);//除了等待中断以外,该线程休眠
…
private static void port_OnInterrupt(Cpu.Pin port,
bool state,
TimeSpan time)
{
Debug.Print("Pin=" + port + " State=" + state + " Time=" + time);
//清除当前中断端口的状态以便下一次指定电平到来时仍能触发中断
interruptPort.ClearInterrupt();
}
2 串口(RS-232)
尽管如今USB盛行,串口设备仍然有着他们自己的生存空间,比如一些测量仪器,GPS接收器等等。
和.NET Framework以及.NET Compact Framework中的类型相应的,.Net Micro Framework中也有SerialPort类,只不过它被放在了Microsoft.SPOT.Hardware程序集中而不是它的前辈们的System.IO.Ports下。使用前需要先对SerialPort.Configuration进行配置。
下面的例子演示了如何通过串口发送数据:
using System;
using System.Text;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
…
SerialPort.Configuration config =
new SerialPort.Configuration(SerialPort.Serial.COM1,
SerialPort.BaudRate.Baud9600,
false);//不做任何流控制
SerialPort serialPort = new SerialPort(config);
byte[] outBuffer = Encoding.UTF8.GetBytes("Hello World!"r"n");
serialPort.Write(outBuffer, 0, outBuffer.Length);//MF中无需先Open再写入
serialPort.Dispose();
//keeps the emulator running to see results
Thread.Sleep(Timeout.Infinite);
using System.Text;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
…
SerialPort.Configuration config =
new SerialPort.Configuration(SerialPort.Serial.COM1,
SerialPort.BaudRate.Baud9600,
false);//不做任何流控制
SerialPort serialPort = new SerialPort(config);
byte[] outBuffer = Encoding.UTF8.GetBytes("Hello World!"r"n");
serialPort.Write(outBuffer, 0, outBuffer.Length);//MF中无需先Open再写入
serialPort.Dispose();
//keeps the emulator running to see results
Thread.Sleep(Timeout.Infinite);
读数据的方法也和PC上差不多:
SerialPort.Configuration config =
new SerialPort.Configuration(SerialPort.Serial.COM1,
SerialPort.BaudRate.Baud115200,
false);
SerialPort serialPort = new SerialPort(config);
byte[] inBuffer = new byte[32];
while (true)
{
int count = serialPort.Read(inBuffer, 0, inBuffer.Length, 0);//读的时候需要指定buffer
if (count > 0) //minimum one byte read
{
char[] chars = Encoding.UTF8.GetChars(inBuffer);
string str = new string(chars, 0, count);
Debug.Print(str);
}
}
new SerialPort.Configuration(SerialPort.Serial.COM1,
SerialPort.BaudRate.Baud115200,
false);
SerialPort serialPort = new SerialPort(config);
byte[] inBuffer = new byte[32];
while (true)
{
int count = serialPort.Read(inBuffer, 0, inBuffer.Length, 0);//读的时候需要指定buffer
if (count > 0) //minimum one byte read
{
char[] chars = Encoding.UTF8.GetChars(inBuffer);
string str = new string(chars, 0, count);
Debug.Print(str);
}
}
3.管脚的使用和保留
保留CPU的管脚,你可以显示的控制管脚的用途,避免管脚复用造成的冲突。通过注册你的Hardware Provider(Microsoft.SPOT.HardwareProvider),你可以约定你的管脚使用情况。
串行通信不仅仅包括RS232的串口,还有SPI和I²C总线等方式。HardwareProviderHardwareProvider也不只为RS232提供硬件信息服务。当你自定义HWP的时候,你需要从HWP继承过来,并重写GetI2CPins, GetSerialPins, and GetSpiPins三个虚方法中的至少一个,它们在缺省状况下均返回Cpu.Pin.GPIO_NONE.,之后需要调用静态方法Register()来完成注册。
下面是一个自定义的HardwareProvider用以设定RS232串口的保留管脚。它实现了GetSerialPins方法,返回的是CPU的输入输出管脚号。
internal sealed class MyHardwareProvider : HardwareProvider
{
public override void GetSerialPins(SerialPort.Serial com,
out Cpu.Pin rxPin,
out Cpu.Pin txPin)
{ switch (com)
{
case SerialPort.Serial.COM1:
rxPin = Cpu.Pin.GPIO_Pin0;
txPin = Cpu.Pin.GPIO_Pin1;
break;
case SerialPort.Serial.COM2:
rxPin = Cpu.Pin.GPIO_Pin2;
txPin = Cpu.Pin.GPIO_Pin3;
break;
default:
rxPin = Cpu.Pin.GPIO_NONE;
txPin = Cpu.Pin.GPIO_NONE;
break;
}
}
}
//注册HWP
HardwareProvider.Register(new MyHardwareProvider());
SerialPort.Configuration config =
new SerialPort.Configuration(SerialPort.Serial.COM1,
SerialPort.BaudRate.Baud9600,
false);
//如果此时serialPortB从 config创建时,serialPortA尚未释放的话,会导致运行时错误。
SerialPort serialPortA = new SerialPort(config);
//SerialPort serialPortB = new SerialPort(config); //出错,因为这时COM1被A占用
{
public override void GetSerialPins(SerialPort.Serial com,
out Cpu.Pin rxPin,
out Cpu.Pin txPin)
{ switch (com)
{
case SerialPort.Serial.COM1:
rxPin = Cpu.Pin.GPIO_Pin0;
txPin = Cpu.Pin.GPIO_Pin1;
break;
case SerialPort.Serial.COM2:
rxPin = Cpu.Pin.GPIO_Pin2;
txPin = Cpu.Pin.GPIO_Pin3;
break;
default:
rxPin = Cpu.Pin.GPIO_NONE;
txPin = Cpu.Pin.GPIO_NONE;
break;
}
}
}
//注册HWP
HardwareProvider.Register(new MyHardwareProvider());
SerialPort.Configuration config =
new SerialPort.Configuration(SerialPort.Serial.COM1,
SerialPort.BaudRate.Baud9600,
false);
//如果此时serialPortB从 config创建时,serialPortA尚未释放的话,会导致运行时错误。
SerialPort serialPortA = new SerialPort(config);
//SerialPort serialPortB = new SerialPort(config); //出错,因为这时COM1被A占用
推荐资源:
- MS .Net Micro Framework team
- Windows Embedded blog From china
- MVP 刘洪峰的blog
- .Net MF国内的资源站点Winbile.Net(正在改版)
- [书籍]Embedded Programming with the .Net Micro Framework
- [书籍]Expert .Net Micro Framework
My first .net mf app on Freescale iMX!! 图中是我的第一个mf程序,在iMX上显示了一张它的照片(240*320):
enjoy it!
黄季冬
posted on 2008-07-09 23:05 J.D Huang 阅读(3229) 评论(10) 编辑 收藏 举报