.Net Micro Framework研究—串口操作
试验平台:Digi MF开发板
Digi提供的示例中包含了串口的示例程序,主要代码如下:
public
bool
EchoByte()
{
SerialPortserial;
bool
exceptionRaised
=
false
;
bool
testResult
=
true
;
string
message
=
"
Thisisanechotest.Enterthecharactertoecho,orESCtoexit.
"
;
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
Threadm_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
];
SerialPortserial
=
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
UInt16GetCheckCode(
byte
[]buf,
int
nEnd)
{
UInt16crc
=
(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(
"
OpenSerialOK
"
);
m_worker
=
new
Thread(
new
ThreadStart(
this
.ModbusThreadProc));
m_RunFlag
=
true
;
m_worker.Start();
}
catch
{
Debug.Print(
"
SerialError
"
);
}
}
//
停止Modbus服务
public
void
Stop()
{
m_RunFlag
=
false
;
if
(serial
!=
null
)
serial.Dispose();
}
//
ModbusSlave服务
private
void
ModbusThreadProc()
{
Debug.Print(
"
StartModbusSlave
"
);
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
)
{
//
信息处理
UInt16intCRC16
=
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("CRCOK");
//
读数据
if
(bytRtuData[
1
]
==
3
)
{
UInt16lngDataAddr
=
bytRtuData[
2
];
lngDataAddr
=
(UInt16)((lngDataAddr
<<
8
)
+
bytRtuData[
3
]);
//
地址
UInt16lngDataNum
=
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("SendDataOK"+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.SerialCOM1
=
(SerialPort.Serial)
0
;
public
const
SerialPort.SerialCOM2
=
(SerialPort.Serial)
1
;
}
//
串口波特率
public
static
class
BaudRate
{
public
const
SerialPort.BaudRateBaud4800
=
(SerialPort.BaudRate)
4800
;
public
const
SerialPort.BaudRateBaud9600
=
(SerialPort.BaudRate)
9600
;
public
const
SerialPort.BaudRateBaud19200
=
(SerialPort.BaudRate)
19200
;
public
const
SerialPort.BaudRateBaud38400
=
(SerialPort.BaudRate)
38400
;
public
const
SerialPort.BaudRateBaud57600
=
(SerialPort.BaudRate)
57600
;
public
const
SerialPort.BaudRateBaud115200
=
(SerialPort.BaudRate)
115200
;
public
const
SerialPort.BaudRateBaud230400
=
(SerialPort.BaudRate)
230400
;
}
}
}
程序部署运行后,直接用标准的Modbus Rtu客户端程序测试即可,我用的是我以前编写的Modbus Rtu Client程序,测试如下:
(图MF10280002.JPG)
这时候,你直接操作SW2的拨码,该数字就会发生变化(前提SW1的拨码都拨到右边)。
缺点:很奇怪的是串口的参数仅能配置波特率,奇偶校验,数据位却无法配置。
总的印象:用MF开发嵌入式系统还是非常有前景的,至少使产品的开发周期大大缩短,并且代码升级维护方便。