摘要:本文介紹了.Net Micro Framework中對硬件的簡單而獨特的訪問方式。涉及GPIO,RS232串口等。通過簡明的例程說明了如何創建IO口,如何發送接受數據的過程。
1.GPIO
通常來說,一塊MCU要與周邊環境進行交流,使用GPIO(General Purpose Input/Output)無疑是最常用的方式。一個GPIO口在被初始化之後可以被用於輸入或輸出的通道。一個GPIO口可以由兩種狀態來描述 - 低(約為0伏)和高(通常認為是3.3伏的正向電壓)。
在.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 }
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); }
另外,還有一個可以在運行時動態改變狀態地邏輯端口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); }
下面的例子演示了電平觸發的過程:
… 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);
讀數據的方法也和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); } }
3.管腳的使用和保留
保留CPU的管腳,你可以顯示的控制管腳的用途,避免管腳復用造成的沖突。通過注冊你的Hardware Provider(Microsoft.SPOT.HardwareProvider),你可以約定你的管腳使用情況。
串行通信不僅僅包括RS232的串口,還有SPI和I2C總線等方式。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占用