串口通信

using system.IO.Ports;

SerialPort类

1.获取机器的所有串口

/*获得设备的所有串口*/
//类SerialPort的静态方法GetPortNames()返回一台设备所有的串口名字
//如果机器无串口,返回一个空的字符串数组的引用【不是null】
string[] DevideAllports = SerialPort.GetPortNames();
if(DevideAllports.Length == 0) {
    Console.WriteLine("本机无串口");
}
else {
    foreach (var item in DevideAllports) {
        Console.WriteLine(item);
    }
}

2.操纵设备的一个串口

两台设备之间实现串口通信,二者使用的串口的(1)数据位 (2)停止位  (3)奇偶校验方式 (4)波特率 必须相同。

//1.创建一个实例
SerialPort DeciveSp = new SerialPort();
//2.绑定串口
DeciveSp.PortName = "COM1";
//3.设置通信参数
DeciveSp.DataBits = 8;//设置数据位
DeciveSp.StopBits = StopBits.One;//设置停止位
DeciveSp.BaudRate = 9600;//设置波特率
DeciveSp.Parity = Parity.Even;//设置奇偶校验方式

3.打开和关闭一个串口

/*打开串口*/
if (!DeciveSp.IsOpen) {
    DeciveSp.Open();
}
/*关闭串口*/
if (DeciveSp.IsOpen) {
    DeciveSp.Close();
}

4.发送数据

public void Write (string str);
public void Write (byte[] buffer, int offset, int count);
public void Write (char[] buffer, int offset, int count);
public void WriteLine (string str);

5.接收数据

public delegate void SerialDataReceivedEventHandler(object sender, SerialDataReceivedEventArgs e);

public event SerialDataReceivedEventHandler DataReceived;
C#中SerialPort类中DataReceived事件,当机器收到数据时会激发中断处理程序,在辅助线程上引发 DataReceived 事件;
device.ReceivedBytesThreshold = 1;表示机器串口硬件缓存收到一个字节,就触发DataReceived事件,但是C#并不能保证接收到的每一个字节都会触发一次DataReceived事件。
所以:
(1)触发一次,可以使用BytesToRead循环获取缓冲区内接收到的所有数据。
(2)由于Read()是在辅助线程上执行的,不是在主线程上执行的,所以在DataRecived事件绑定的方法中,必须使用Control.Invoke()方法修改UI;

应用场景一:通信双方发送的消息不长(低于1024 bits,约512个字符),各个命令的执行必须是互斥的,一发一答的过程不能被其他命令中断。

class MySerialPort {
    private SerialPort device = new SerialPort();
    private AutoResetEvent waitRsp = new AutoResetEvent(false);
    private object locker = new object(); //用于同步触发串口接收数据的函数
    byte[] bufferReceived = null;//接收缓存
    public MySerialPort() {
        device.ReadBufferSize = 1024;//设置接收缓存的大小,一般容纳下最长消息即可
        device.DataReceived += new SerialDataReceivedEventHandler(device_ReceiveData);
    }
    private void device_ReceiveData(object sender,SerialDataReceivedEventArgs e) {
        if(device.BytesToRead > 0) {
            lock (locker) {
                if(device.BytesToRead > 0) {
                    while (device.BytesToRead > 0) {
                        bufferReceived = new byte[device.BytesToRead];
                        device.Read(bufferReceived, 0, device.BytesToRead);
                    }
                    waitRsp.Set();  //告诉处理命令的方法,已经收到回复信息,请继续往下执行
                }
            }
        }
    }
}

串口编程注意事项:

(1)DataReceived事件中,最高效的做法是指缓存数据,然后异步的去分析数据。但是,这样较复杂,在效率要求不是很高的情况下(大多数情况),可以在DataReceived事件中缓存数据后,立刻进行数据完整性检查,有效性检查,分析数据,以及更新界面。但这么做有一个隐患,底层串口操作的效率依赖于数据分析和界面更新,任何一个环节频繁耗时过长,都会造成数据的堆积。文章只假设都不拖时间的情况。

(2)一答一回的指令可以使用AutoResetEvent在发完数据后,在DataReceived事件中set()来同步

(3)相互独立的指令交互过程,可以使用Interlocked锁进行同步,这样在用户多个线程调用指令时,也不会发生错误。

(4)发送指令不能太快,否则可能造成设备处理不及,可以在发送前,判断一下(当前时间 - 上一次收到数据的时间)是否满足设备的响应时长。

(5)考虑重发问题。

posted @ 2018-12-20 13:37  十七岁的天空  阅读(349)  评论(0编辑  收藏  举报