.Net Micro Framework研究—串口操作
試驗平台:Digi MF開發板
Digi提供的示例中包含了串口的示例程序,主要代碼如下:
部署運行後,你可以用超級終端進行測試,測試圖如下:
(圖MF10280001.JPG)
注意:如果串口程序非正常退出,有可能導致開發板無法發送數據(接收倒是正常),重啟開發板即可。
用測試程序還是體現不出.Net Micro Framework的優勢,我決定用MF實現Modbus Rtu Slave服務端(支持Modbus Rtu 3號命令),並且地址為0的數據存放了GPIO入的信息,這樣在上位機就很方面的檢測IO信號了。
值得一提的是VS2005 的調試功能非常強大,很容易添加斷點及監控當前變量的值,同時用debug.print()命令也非常好使,這樣調試程序絕對比調試單片舒服。
下面貼出我寫的Modbus RtuSlave代碼
using System;
using Microsoft.SPOT;
using System.Threading;
using Microsoft.SPOT.Hardware;
namespace MFModbus
{
public class ModbusRtu
{
private Thread m_worker;
private bool m_RunFlag;
private byte bytRtuDataFlag=0;
private byte bytRtuDataIdx;
private byte[] bytRtuData = new byte[8];
//設備地址,默認為1
private byte ModbusAddr = 1;
//數據區(注意,Modbus讀寫是以字(雙字節)為單位的)
private byte[] DataBuff = new byte[128];
SerialPort serial = null;
InputPort[] input = new InputPort[5];
Cpu.Pin[] pin = new Cpu.Pin[5] { (Cpu.Pin)0, (Cpu.Pin)1, (Cpu.Pin)2, (Cpu.Pin)5, (Cpu.Pin)6 };
public ModbusRtu(byte mModbusAddr)
{
ModbusAddr=mModbusAddr;
for (int i = 0; i < 5; i++)
{
input[i] = new InputPort(pin[i], false, Port.ResistorMode.PullUp);
}
}
~ModbusRtu()
{
Stop();
}
//CRC16校驗
private UInt16 GetCheckCode(byte[] buf, int nEnd)
{
UInt16 crc = (UInt16)0xffff;
int i, j;
for (i = 0; i < nEnd; i++)
{
crc ^= (UInt16)buf[i];
for (j = 0; j < 8; j++)
{
if ((crc & 1) != 0)
{
crc >>= 1;
crc ^= 0xA001;
}
else
crc >>= 1;
}
}
return crc;
}
//啟動Modbus服務
public void Run()
{
try
{
//僅有波特率選項,竟然沒有奇偶校驗控制
serial = new SerialPort(new SerialPort.Configuration(Serial.COM1, BaudRate.Baud9600, false));
Debug.Print("Open Serial OK");
m_worker = new Thread(new ThreadStart(this.ModbusThreadProc));
m_RunFlag = true;
m_worker.Start();
}
catch
{
Debug.Print("Serial Error");
}
}
//停止Modbus服務
public void Stop()
{
m_RunFlag = false;
if (serial != null)
serial.Dispose();
}
//Modbus Slave服務
private void ModbusThreadProc()
{
Debug.Print("Start Modbus Slave");
byte[] bytData=new byte[1];
while (m_RunFlag)
{
serial.Read(bytData, 0, bytData.Length, Timeout.Infinite);
RtuSlave(bytData[0]);
}
}
//串口數據處理
private void RtuSlave(byte bytData)
{
//Debug.Print(bytRtuDataIdx.ToString() + " - " + bytData.ToString());
if (bytRtuDataFlag == 0)
{
//如果數據為首地址
if (bytData == ModbusAddr)
{
bytRtuDataFlag = 1;
bytRtuDataIdx = 0;
bytRtuData[bytRtuDataIdx++] = bytData;
}
}
else
{
bytRtuData[bytRtuDataIdx++] = bytData;
if (bytRtuDataIdx >= 8)
{
//信息處理
UInt16 intCRC16 = GetCheckCode(bytRtuData, 8 - 2);
//Debug.Print("CRC:" + bytRtuData[8 - 2].ToString() + " " + ((byte)(intCRC16 & 0xFF)).ToString() +"|" + bytRtuData[8 - 1].ToString() + " " + ((byte)((intCRC16 >> 8) & 0xff)).ToString());
//CRC16校驗檢驗
if (bytRtuData[8 - 2] == (intCRC16 & 0xFF) && bytRtuData[8 - 1] == ((intCRC16 >> 8) & 0xff))
{
byte[] bytSendData = new byte[255];
byte bytErrorFlag = 0;
byte bytErrorNo = 1;
//Debug.Print("CRC OK");
//讀數據
if (bytRtuData[1] == 3)
{
UInt16 lngDataAddr = bytRtuData[2];
lngDataAddr = (UInt16)((lngDataAddr << 8) + bytRtuData[3]); //地址
UInt16 lngDataNum = bytRtuData[4];
lngDataNum = (UInt16)((lngDataNum << 8) + bytRtuData[5]); //數量
if (lngDataAddr * 2 + lngDataNum * 2 > 1024 || lngDataNum > 120)
{
bytErrorNo = 2;
bytErrorFlag = 0;
}
else
{
bytSendData[0] = bytRtuData[0];
bytSendData[1] = bytRtuData[1];
bytSendData[2] = (byte)(lngDataNum * 2);
//讀GPIO信號
DataBuff[0]
;= 0;
DataBuff[1] = (byte)((input[0].Read() ? 1 : 0) | (input[1].Read() ? 2 : 0) | (input[2].Read() ? 4 : 0) | (input[3].Read() ? 8 : 0) | (input[4].Read() ? 16 : 0));
for (int i = 0; i < bytSendData[2]; i++)
{
bytSendData[3 + i] = DataBuff[lngDataAddr * 2 + i];
}
intCRC16 = GetCheckCode(bytSendData, 3 + lngDataNum * 2);
bytSendData[3 + lngDataNum * 2] = (byte)(intCRC16 & 0xFF); //CRC校驗低位
bytSendData[4 + lngDataNum * 2] = (byte)((intCRC16 >> 8) & 0xff); //CRC校驗高位
//發送數據
int intRet=serial.Write(bytSendData, 0, 5 + lngDataNum * 2);
//Debug.Print("SendData OK " + intRet.ToString() );
bytErrorFlag = 1;
}
}
if (bytErrorFlag == 0)
{
//協議不支持
bytSendData[0] = bytRtuData[0];
bytSendData[1] = (byte)(bytRtuData[1] | 0x80);
bytSendData[2] = bytErrorNo;
intCRC16 = GetCheckCode(bytSendData, 3);
bytSendData[3] = (byte)(intCRC16 & 0xFF); //CRC校驗低位
bytSendData[4] = (byte)((intCRC16 >> 8) & 0xff); //CRC校驗高位
//發送數據
serial.Write(bytSendData, 0, 5);
}
}
bytRtuDataFlag = 0;
}
}
return;
}
//串口號
public static class Serial
{
public const SerialPort.Serial COM1 = (SerialPort.Serial)0;
public const SerialPort.Serial COM2 = (SerialPort.Serial)1;
}
//串口波特率
public static class BaudRate
{
public const SerialPort.BaudRate Baud4800 = (SerialPort.BaudRate)4800;
public const SerialPort.BaudRate Baud9600 = (SerialPort.BaudRate)9600;
public const SerialPort.BaudRate Baud19200 = (SerialPort.BaudRate)19200;
public const SerialPort.BaudRate Baud38400 = (SerialPort.BaudRate)38400;
public const SerialPort.BaudRate Baud57600 = (SerialPort.BaudRate)57600;
public const SerialPort.BaudRate Baud115200 = (SerialPort.BaudRate)115200;
public const SerialPort.BaudRate Baud230400 = (SerialPort.BaudRate)230400;
}
}
}
程序部署運行後,直接用標准的Modbus Rtu客戶端程序測試即可,我用的是我以前編寫的Modbus Rtu ClIEnt程序,測試如下:
這時候,你直接操作SW2的撥碼,該數字就會發生變化(前提SW1的撥碼都撥到右邊)。
缺點:很奇怪的是串口的參數僅能配置波特率,奇偶校驗,數據位卻無法配置。
總的印象:用MF開發嵌入式系統還是非常有前景的,至少使產品的開發周期大大縮短,並且代碼升級維護方便。