.Net Micro Framework研究—串口操作
试验平台:Digi MF开发板
Digi提供的示例中包含了串口的示例程序,主要代码如下:
public
bool
EchoByte()
{
SerialPort serial;
bool exceptionRaised = false ;
bool testResult = true ;
string message = " This is an echo test. Enter the character to echo, or ESC to exit. " ;
byte [] encodedMessage = c_encoding.GetBytes(message);
byte [] buffer = new byte [ 1 ];
try
{
serial = new SerialPort( new SerialPort.Configuration(Cpu.Serial.COM1, Cpu.BaudRate.Baud115200, false ));
serial.Write(encodedMessage, 0 , message.Length);
while (buffer[ 0 ] != 0x1b )
{
serial.Read(buffer, 0 , buffer.Length, Timeout.Infinite);
serial.Write(buffer, 0 , buffer.Length);
}
serial.Dispose();
}
catch
{
exceptionRaised = true ;
}
if (exceptionRaised == true )
testResult = false ;
return testResult;
}
{
SerialPort serial;
bool exceptionRaised = false ;
bool testResult = true ;
string message = " This is an echo test. Enter the character to echo, or ESC to exit. " ;
byte [] encodedMessage = c_encoding.GetBytes(message);
byte [] buffer = new byte [ 1 ];
try
{
serial = new SerialPort( new SerialPort.Configuration(Cpu.Serial.COM1, Cpu.BaudRate.Baud115200, false ));
serial.Write(encodedMessage, 0 , message.Length);
while (buffer[ 0 ] != 0x1b )
{
serial.Read(buffer, 0 , buffer.Length, Timeout.Infinite);
serial.Write(buffer, 0 , buffer.Length);
}
serial.Dispose();
}
catch
{
exceptionRaised = true ;
}
if (exceptionRaised == true )
testResult = false ;
return testResult;
}
部署运行后,你可以用超级终端进行测试,测试图如下:
(图MF10280001.JPG)
注意:如果串口程序非正常退出,有可能导致开发板无法发送数据(接收倒是正常),重启开发板即可。
用测试程序还是体现不出.Net Micro Framework的优势,我决定用MF实现Modbus Rtu Slave服务端(支持Modbus Rtu 3号命令),并且地址为0的数据存放了GPIO入的信息,这样在上位机就很方面的检测IO信号了。
用了大约15分钟,就把我以前用C++开发的Modbus Rtu Slave程序移植到MF平台上来的,我想如果用单片来开发,虽然也有可能借用以前的代码,但很方便的把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 ;
}
}
}
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程序,测试如下:
(图MF10280002.JPG)
这时候,你直接操作SW2的拨码,该数字就会发生变化(前提SW1的拨码都拨到右边)。
缺点:很奇怪的是串口的参数仅能配置波特率,奇偶校验,数据位却无法配置。
总的印象:用MF开发嵌入式系统还是非常有前景的,至少使产品的开发周期大大缩短,并且代码升级维护方便。