代码改变世界

Unity3D串口处理

2014-04-28 14:05  追梦人001  阅读(12927)  评论(2编辑  收藏  举报

  最近公司用U3D开发一个应用,需要用到串口,便研究了两天串口编程,C#用SerialPort类实现串口编程,便开始使用SerialPort类编写代码。后来发现一个问题,Unity不支持DataReceived的方法。遇到这个问题很是棘手啊,后来在网上搜了一下,发现这个问题确实存在,解决的方法是开启两个线程,一个用来接收数据,一个用来处理数据。将两个线程分别放在FixedUpdate里面调用,这样便可以快速的读取和处理数据了。但是程序运行时,开启Open()方法时,会报System.IO.IOException这个异常。检察了一下应该是该串口被占用。之所以被占用,是因为上次没Close,而每次完成或退出时我确实写了SerialPort的Close()方法。后来在网上查了一下相关的资料,发现如果Close的时候正好赶上读取串口数据,这样两个线程便发生了矛盾,Close方法无法完成。但是代码也不会报错或是抛异常(据说这是微软的一个bug,是不是就不清楚了)。所以说问题就出在了这里。那解决该问题的方法是当启用Close的方法时,必须停止接受串口数据的线程。这样该问题就解决了,代码大致如下:

List<byte> liststr;//在ListByte中读取数据,用于做数据处理
List<byte> ListByte;//存放读取的串口数据
private Thread tPort;
private Thread tPortDeal;//这两个为两个线程,一个是读取串口数据的线程一个是处理数据的线程
bool isStartThread;//控制FixedUpdate里面的两个线程是否调用(当准备调用串口的Close方法时设置为false)
byte[] strOutPool = new byte[6];
SerialPort spstart;
void Start()
{
isStartThread=true;
liststr = new List<byte>();
ListByte = new List<byte>();
isStartThread = true;
spstart = new SerialPort("COM3", 57600, Parity.None, 8, StopBits.One);
try
{
spstart.Open();
}
catch (Exception e)
{
Debug.log(e.Tostring());
}
tPort = new Thread(DealData);
tPort.Start();
tPortDeal = new Thread(ReceiveData);
tPortDeal.Start();

}
void FixedUpdate()
{
if (isStartThread)
{
if (!tPortDeal.IsAlive)
{
tPortDeal = new Thread(ReceiveData);
tPortDeal.Start();
}
if (!tPort.IsAlive)
{
tPort = new Thread(DealData);
tPort.Start();

}
}

}
private void ReceiveData()
{
try
{
Byte[] buf = new Byte[1];
string sbReadline2str = string.Empty;
if (spstart.IsOpen)
{
tickcount++;
spstart.Read(buf, 0, 1);
}
if (buf.Length == 0)
{
return;
}
if (buf != null)
{
for (int i = 0; i < buf.Length; i++)
{
ListByte.Add(buf[i]);
}

}
}
catch (Exception e)
{
Debug.Log(e.ToString());
spantime = 0;
}
}


private void DealData()
{


liststr.Add(ListByte[0]);
ListByte.Remove(ListByte[0]);
if (liststr.Count == 6)//串口发过来的数据的位数(我的应用中位数为6)
{
{

//这个地方写处理的逻辑代码

}
}

liststr.Clear();

}

}

IEnumerator ClosePort()//该方法为关闭串口的方法,当程序退出或是离开该页面或是想停止串口时调用。
{

isStartThread=false;//停止掉FixedUpdate里面的两个线程的调用
yield return new WaitForSeconds(1);//等一秒钟,让两个线程确实停止之后在执行Close方法
spstart.Close();
}