C# 串口通信总结
在C#串口通信开发过程中有的产家只提供通信协议,这时候开发人员要自己根据协议来封装方法,有的产家比较人性化提供了封装好的通信协议方法供开发人员调用。
1、只提供通信协议(例如,今年早些时候开发的出钞机):
如:
/// <summary>
/// 出钞 DISPENSE(0x45)
/// </summary>
public void Chuchao()
{
log("出钞设备出钞");
if (!sp.IsOpen)
{
sp.Open();
}
try
{
var send = new byte[] { Eot, Id, Stx, Dipsense, 48, 51, Etx, 0x01 };
int bcc = 0;
for (int i = 0; i < send.Length - 1; i++)
bcc ^= send[i];
send[send.Length - 1] = (byte)bcc;
sp.Write(send, 0, send.Length);
log("操作命令:" + GetBytesString(send, 0, send.Length, " "));
for (int i = 0; i < 3; i++)
{
var r = sp.ReadByte();
log("发送命令,收到的反馈。" + r);
if (r != Ack)
{
sp.Write(send, 0, send.Length);
}
else
{
break;
}
}
byte[] recive = new byte[14];
for (int i = 0; i < 3; i++)
{
bcc = 0;
recive = GetData(recive.Length);
for (int j = 0; j < recive.Length - 1; j++)
{
bcc ^= recive[j];
}
if (bcc != recive[recive.Length - 1])
{
sp.Write(new byte[] { Nck }, 0, 1);
log("收到信息:" + GetBytesString(recive, 0, recive.Length, " ") + ",接收标识:NCK:失败");
}
else
{
sp.Write(new byte[] { Ack }, 0, 1);
log("收到信息:" + GetBytesString(recive, 0, recive.Length, " ") + ",接收标识:ACK:成功");
break;
}
}
Error errorCode = new Error();
errorCode.Code = recive[9];
log("接收到的命令:" + recive[3] + ",接收到的错误码:" + errorCode.Code + "--" + errorCode.ErrorMsg);
if (recive[3] != Dipsense || errorCode.Code > 0x31)
{
throw new Exception("出钞出错");
}
}
catch (Exception e)
{
log(e.ToString());
//throw;
}
finally
{
if (sp.IsOpen)
{
sp.Close();
}
}
}
2、产家提供通信协议方法(例如,今年早些时候开发的银行卡支付机):
这时候你只要在你的项目bin/Debug下面添加产家提供的dll,然后,再这样:
/// <summary> /// 打开串口 /// </summary> /// <param name="port">串口号字符串</param> /// <returns> /// 串口文件句柄 /// 备注:必须先调用此函数,获得指定串口的串口文件句柄,才可调用其他函数。 /// 可以同时打开多个串口,获得多个串口文件句柄,但不能多次打开同一个串口。 /// 使用完毕后,必须调用CommClose()关闭串口。 /// </returns> [DllImport("CRT_310.dll", EntryPoint = "CommOpen", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern IntPtr CommOpen(string port); /// <summary> /// 按指定的波特率打开串口 (该函数完成的功能= CommOpen 函数+ CommSetting 函数) /// </summary> /// <param name="port">串口号字符串</param> /// <param name="data">指定波特率 /// 波特率=1200,2400,4800,9600,19200,38400。 /// 例如:CommOpen("Com1",9600); /// </param> /// <returns>串口文件句柄 /// 备注:必须先调用此函数,获得指定串口的串口文件句柄,才可调用其他函数。 /// 可以同时打开多个串口,获得多个串口文件句柄,但不能多次打开同一个串口。 /// 使用完毕后,必须调用CommClose()关闭串口。</returns> [DllImport("CRT_310.dll", EntryPoint = "CommOpenWithBaut", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern IntPtr CommOpenWithBaut(string port, uint data);
然后就可以像这样调用了:
public string CardBoxPositionToRead() { var comHandle = new IntPtr(); var cardStates = new byte[2]; var recordInfo = new byte[200]; int? data; try { comHandle = PackageK100Dll.M100A_CommOpenWithBaud(ComPort, BaudRate); if (comHandle.ToInt32() == 0) { PackageK100Dll.M100A_CommClose(comHandle); return "打开串口失败!"; } data = PackageK100Dll.M100A_CheckCardPosition(comHandle, false, 0, cardStates, recordInfo); if (data != 0) { PackageK100Dll.M100A_CommClose(comHandle); return "读取卡片位置失败"; } switch (cardStates[0]) { //通道无卡 case 48: break; case 49: case 50: data = PackageK100Dll.M100A_MoveCard(comHandle, false, 0, 0x34, recordInfo); if (data != 0) { PackageK100Dll.M100A_CommClose(comHandle); return "移动卡片位置失败"; } break; //通道有卡 default: PackageK100Dll.M100A_CommClose(comHandle); return "请取走卡片或者业务正在办理,请稍候!"; } switch (cardStates[1]) { case 48: PackageK100Dll.M100A_CommClose(comHandle); return "卡箱无卡"; default: //将卡槽卡片移动到读写卡位置 data = PackageK100Dll.M100A_MoveCard(comHandle, false, 0, 0x30, recordInfo); if (data != 0) { PackageK100Dll.M100A_CommClose(comHandle); return "移动卡片位置失败"; } PackageK100Dll.M100A_CommClose(comHandle); return "true"; } } catch (Exception) { PackageK100Dll.M100A_CommClose(comHandle); return "发生异常"; } }