目前可以成为.NET MF开发板调试口的信道有,串口、USB和网口,一般情况下,一旦具体设定哪个信道,在不更换固件的情况下,是无法动态改变的。
所谓调试口,就是供windows平台上的VS2008/VS2010调试的信道,当然MFDeploy和我编写的工具YFAccessFlash也是通过这个信道和开发板实现通信的。不过,这些通信过程,作为运行到开发板上的应用程序是无从知道的,这些基于开发板底层的通信,完全由TinyCLR进行全权管理。
我以前写了一篇关于这个信道的通信协议的介绍文章《Micro Framework WireProtocol协议介绍》,文章中介绍,WireProtocol协议是上位机MFDeploy或VS2008程序在诊断、部署、调试.Net Micro Framework设备及相关应用程序时的通信协议。该协议与具体的硬件链路无关,目前支持的物理连接有串口、网口、USB等。该协议为点对点协议,协议中没有设备地址的概念,在同一时间同一物理通道仅能调试一台设备。协议格式分两部分,帧头和负荷(Payload)(一帧命令可以不包含Payload)。目前,通信命令一共71个,其中1个已经过时、6个已经被注销(V4.1版本又增加了几个命令)。
有了以上知识,我们很自然地想到,借助这个通信协议,我们是否可以扩展一下,自定义我们所需要的通信命令,这样借助WireProtocol协议通道,我们就可以传输任意长度的数据了。
不过,说起来容易,做起来难,这就像练武之人想打通任督二脉一样,打通的意义虽然重大,但是实现起来却环节众多。考虑到复杂性,所以我这里不打算详细介绍所有实现的细节,由点带面,简要说一下实现的过程,我们需要修改或创建的文件(或代码)如下:
PC 平台:改造MFDeployEngine.dll和Microsoft.SPOT.Debugger.dll文件,创建UsbStream类。
MF 平台:修改TinyCLR_Debugging.h和Debugger.CPP文件,添加c_Monitor_Custom命令,此外再实现一个P/Invoke接口,让运行在开发板上的用户程序可以访问该通道。
P/Invoke接口中的UsbStream类的定义(.NET Micro Framework代码)如下:
public class UsbStream
{
public UsbStream();
public int BytesToRead { get; }
public int BytesToWrite { get; }
public event UsbStreamDataReceivedEventHandler DataReceived;
public void Close();
public void DiscardInBuffer();
public void DiscardOutBuffer();
public bool Open();
public int Read(byte[] buffer, int offset, int count);
public int Write(byte[] buffer, int offset, int count);
}
和串口的定义类似,不过不用设置通信参数。
运行在PC机上的UsbStream类的定义(.NET Framework代码)如下:
public class UsbStream
{
public UsbStream(YFDeploy deploy);
public int BytesToRead { get; }
public int BytesToWrite { get; }
public void Close();
public void DiscardInBuffer();
public void DiscardOutBuffer();
public bool Open();
public int Read(byte[] buffer, int offset, int count);
public int Write(byte[] buffer, int offset, int count);
}
二者代码几乎相同,不过后者构造函数中有参数,并且没有事件函数。
我们做一个Demo,来演示我们所实现的功能,很简单的做法,就是PC向开发板发送数据,开发板原样返回。
PC机上的主要代码如下:
private void btnSend_Click(object sender, EventArgs e)
{
if (txtOutput.Text.Length <= 0) return;
if (usb == null)
{
usb = new UsbStream(deploy);
if (!usb.Open())
{
txtOutput.BackColor = Color.Red;
usb = null;
return;
}
else
{
txtOutput.BackColor = Color.White;
}
trmRead.Enabled = true;
}
byte[] inData = System.Text.ASCIIEncoding.UTF8.GetBytes(txtOutput.Text);
if (usb.Write(inData, 0, inData.Length)>0)
{
txtOutput.BackColor = Color.White;
}
else
{
txtOutput.BackColor = Color.Red;
}
}
private void trmRead_Tick(object sender, EventArgs e)
{
if (usb != null)
{
if (usb.BytesToRead > 0)
{
byte[] bytData = new byte[usb.BytesToRead];
usb.Read(bytData, 0, bytData.Length);
txtInput.Text += new String(System.Text.ASCIIEncoding.UTF8.GetChars(bytData));
}
}
}
开发板上的代码如下:
public static void Main()
{
UsbStream usb = new UsbStream();
if (usb.Open())
{
Debug.Print("open ok");
Graphics.Print("open ok\r\n");
}
usb.DataReceived += new UsbStreamDataReceivedEventHandler(usb_DataReceived);
while (true)
{
Thread.Sleep(100);
}
//usb.Close();
}
static void usb_DataReceived(object sender, EventArgs e)
{
UsbStream usb = (UsbStream)sender;
string strInfo = "[" + usb.BytesToRead.ToString()+"]";
if (usb.BytesToRead > 0)
{
byte[] bytData=new byte[usb.BytesToRead];
usb.Read(bytData,0,bytData.Length);
for (int i = 0; i < bytData.Length; i++)
{
strInfo += bytData[i].ToString() + " ";
}
usb.Write(bytData, 0, bytData.Length);
Graphics.Print(new string(System.Text.UTF8Encoding.UTF8.GetChars(bytData)));
}
Debug.Print(strInfo);
}
代码都非常简单,这里就不过多介绍了,我们看一下效果图。
PC机上的程序,类似串口调试工具,可以发送和接收数据。
开发板的LCD屏上显示从PC接收的数据。
调试串口输出的调试信息,可以看到开发板接收的字节数据。
以上功能的实现还是非常有意义的,特别是我们做一个产品的时候,我们可以很方便的通过PC配置我们产品的参数,并且可以和该产品进行通信。这样就不需要通过其它的通信口,来完成该功能了。并且难能可贵的是,实现了私有通信,但是并没有牺牲掉调试口。
本文源码:http://www.sky-walker.com.cn/yefan/MFV40/SourceCode/YFAccess.rar
MF快速参考: .NET Micro Framework 快速入门