串口通信(Serial Communications)是指外设和计算机间通过数据信号线、地线等按位(bit)进行传输数据的一种通信方式,属于串行通信方式,能够实现远距离通信,长度可达1200米。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。
串口通信的接口标准有很多,有 RS-232C、RS-232、RS-422A、RS-485 等。常用的就是 RS-232 和 RS-485。串口通信使用的大多都是 DB9 接口,如下图。
1 载波检测(DCD) 2 接受数据(RXD) 3 发出数据(TXD) 4 数据终端准备好(DTR) 5 信号地线(SG) 6 数据准备好(DSR) 7 请求发送(RTS) 8 清除发送(CTS) 9 振铃指示(RI)
这里我们以 RS-232 接口进行演示。
1、数据包格式定为(10bytes):
帧头(0xAA,0x55),命令字(1byte),地址位(2bytes),数据位(2bytes),校验位(1byte,和校验),帧尾(0xFE,0xFE)
地址位和数据位都是高位在前。
数据封装方法:
//数据打包 private byte[] DataPackage(byte cmd, int addr, int data) ? ? ? ? { ? ? ? ? ? ? byte[] package = new byte[10]; ? ? ? ? ? ? package[0] = 0xAA;//帧头 ? ? ? ? ? ? package[1] = 0x55; ? ? ? ? ? ? package[2] = cmd;//命令字 ? ? ? ? ? ? ? ? byte[] dataaddr = IntToByteArray(addr); ? ? ? ? ? ? package[3] = dataaddr[0];//地址高字节 ? ? ? ? ? ? package[4] = dataaddr[1];//地址低字节 ? ? ? ? ? ? byte[] value = IntToByteArray(data); ? ? ? ? ? ? package[5] = value[0];//数据高字节 ? ? ? ? ? ? package[6] = value[1];//数据低字节 ? ? ? ? ? ? package[7] = CheckSum(package);//校验位 ? ? ? ? ? ? package[8] = 0xFE;//帧尾 ? ? ? ? ? ? package[9] = 0xFE; ? ? ? ? ? ? return package; ? ? ? ? } ? ? ? ? ? //将int转换成2位数组 ? ? ? ? private static byte[] IntToByteArray(int value) ? ? ? ? { ? ? ? ? ? ? int hvalue = (value >> 8) & 0xFF; ? ? ? ? ? ? int lValue = value & 0xFF; ? ? ? ? ? ? byte[] arr = new byte[] { (byte)hvalue, (byte)lValue }; ? ? ? ? ? ? return arr; ? ? ? ? } ? ? ? ? ? //得到和校验码 ? ? ? ? private byte CheckSum(byte[] package) ? ? ? ? { ? ? ? ? ? ? byte checksum = 0; ? ? ? ? ? ? for (int i = 0; i < package.Length; i++) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? checksum += package[i]; ? ? ? ? ? ? } ? ? ? ? ? ? return checksum; ? ? ? ? }
2、串口调用封装类CommHelper.cs
internal class CommHelper ? ? { ? ? ? ? //委托 ? ? ? ? public delegate void EventHandle(byte[] readBuffer); ? ? ? ? public event EventHandle DataReceived; ? ? ? ? ? public SerialPort serialPort; ? ? ? ? private Thread thread; ? ? ? ? volatile bool _keepReading; ? ? ? ? ? public CommHelper() ? ? ? ? { ? ? ? ? ? ? serialPort = new SerialPort(); ? ? ? ? ? ?? ? ? ? ? ? ? thread = null; ? ? ? ? ? ? _keepReading = false; ? ? ? ? ? ? ? serialPort.ReadTimeout = -1; ? ? ? ? ? ? serialPort.WriteTimeout = 1000; ? ? ? ? } ? ? ? ? ? //获取串口打开状态 ? ? ? ? public bool IsOpen ? ? ? ? { ? ? ? ? ? ? get ? ? ? ? ? ? { ? ? ? ? ? ? ? ? return serialPort.IsOpen; ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? //开始读取 ? ? ? ? private void StartReading() ? ? ? ? { ? ? ? ? ? ? if (!_keepReading) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? _keepReading = true; ? ? ? ? ? ? ? ? thread = new Thread(new ThreadStart(ReadPort)); ? ? ? ? ? ? ? ? thread.Start(); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? //停止读取 ? ? ? ? private void StopReading() ? ? ? ? { ? ? ? ? ? ? if (_keepReading) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? _keepReading = false; ? ? ? ? ? ? ? ? thread.Join(); ? ? ? ? ? ? ? ? thread = null; ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? //读数据 ? ? ? ? private void ReadPort() ? ? ? ? { ? ? ? ? ? ? while (_keepReading) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? if (serialPort.IsOpen) ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? int count = serialPort.BytesToRead; ? ? ? ? ? ? ? ? ? ? if (count > 0) ? ? ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? ? ? byte[] readBuffer = new byte[count]; ? ? ? ? ? ? ? ? ? ? ? ? try ? ? ? ? ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? ? ? ? ? Application.DoEvents(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? serialPort.Read(readBuffer, 0, count); ? ? ? ? ? ? ? ? ? ? ? ? ? ? DataReceived?.Invoke(readBuffer); ? ? ? ? ? ? ? ? ? ? ? ? ? ? Thread.Sleep(100); ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? catch (TimeoutException) ? ? ? ? ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? //写数据 ? ? ? ? public void WritePort(byte[] send, int offSet, int count) ? ? ? ? { ? ? ? ? ? ? if (IsOpen) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? serialPort.Write(send, offSet, count); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? //打开 ? ? ? ? public void Open() ? ? ? ? { ? ? ? ? ? ? Close(); ? ? ? ? ? ? try ? ? ? ? ? ? { ? ? ? ? ? ? ? ? serialPort.Open(); ? ? ? ? ? ? ? ? serialPort.RtsEnable = true; ? ? ? ? ? ? ? ? if (serialPort.IsOpen) ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? StartReading(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? else ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? MessageBox.Show("串口打开失败!"); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? catch ? ? ? ? ? ? { ? ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? //关闭 ? ? ? ? public void Close() ? ? ? ? { ? ? ? ? ? ? StopReading(); ? ? ? ? ? ? serialPort.Close(); ? ? ? ? ? ? ? ? ? ? } ? ? }
3、调用 (模拟测试读写硬件版本号)
//地址存储信息定义 private static int HARD_VERSION = 0;?? ??? ? ? ?//硬件版本号 ? ? ? ? //命令定义 private static byte RD_HV = 0;?? ??? ??? ? ? ?//读硬件版本 private static byte WR_HV = 1;?? ??? ??? ? ? ?//写硬件版本 ? private CommHelper comm = new CommHelper(); private bool GetCorrect = false; //用来标识是否已经接收到返回值 ? private List<byte> buffer = new List<byte>(1024);//默认分配1M内存,限制不能超过 private byte[] binary_data = new byte[10];//指定格式的某个完整数据 ? //串口初始化 private void InitComm() ? ? ? ? { ? ? ? ? ? ? comm.serialPort.PortName = "COM1"; ? ? ? ? ? ? comm.serialPort.BaudRate = 19200; ? ? ? ? ? ? try ? ? ? ? ? ? { ? ? ? ? ? ? ? ? comm.Open(); ? ? ? ? ? ? } ? ? ? ? ? ? catch (Exception ex) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? MessageBox.Show(ex.Message); ? ? ? ? ? ? } ? ? ? ? ? ? if (comm.IsOpen) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? comm.DataReceived += new CommHelper.EventHandle(comm_DataReceived); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? //数据接收后处理 ? ? ? ? private void comm_DataReceived(byte[] readBuffer) ? ? ? ? { ? ? ? ? ? ? try ? ? ? ? ? ? { ? ? ? ? ? ? ? ? buffer.AddRange(readBuffer);//将数据放到缓存中 ? ? ? ? ? ? ? ? //完整性判断 ? ? ? ? ? ? ? ? while (buffer.Count >= 10) ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? //查找数据头 ? ? ? ? ? ? ? ? ? ? if (buffer[0] == 0xAA && buffer[1] == 0x55) ? ? ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? ? ? //和校验 ? ? ? ? ? ? ? ? ? ? ? ? byte checksum = 0; ? ? ? ? ? ? ? ? ? ? ? ? for (int i = 0; i < 7; i++) ? ? ? ? ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? ? ? ? ? checksum += buffer[i]; ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? if (checksum != buffer[7]) ? ? ? ? ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? ? ? ? ? buffer.RemoveRange(0, 10);//如果校验失败,从缓存中删除这一包数据 ? ? ? ? ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? buffer.CopyTo(0, binary_data, 0, 10);//复制一条完整的数据到具体的数据缓存 ? ? ? ? ? ? ? ? ? ? ? ? ? //读取硬件版本号 ? ? ? ? ? ? ? ? ? ? ? ? if (binary_data[2] == RD_HV && ByteArrayToInt(binary_data[3], binary_data[4]) == HARD_VERSION)//命令字为读RD_HV,地址HARD_VERSION ? ? ? ? ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? ? ? ? ? GetCorrect = true; ? ? ? ? ? ? ? ? ? ? ? ? ? ? string data = ByteArrayToString(binary_data[5], binary_data[6]); ? ? ? ? ? ? ? ? ? ? ? ? ? ? //这里可以处理数据更新界面展示 ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? buffer.RemoveRange(0, 10); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? else ? ? ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? ? ? GetCorrect = false; ? ? ? ? ? ? ? ? ? ? ? ? buffer.RemoveAt(0);//如果数据开始不是头,则删除数据 ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? catch ? ? ? ? ? ? { ? ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? //将2位数组转换成字符串 ? ? ? ? private static string ByteArrayToString(byte arr1, byte arr2) ? ? ? ? { ? ? ? ? ? ? int value = ByteArrayToInt(arr1, arr2); ? ? ? ? ? ? string str = Convert.ToString(value, 10); ? ? ? ? ? ? return str; ? ? ? ? } ? ? ? ? ? //将2位数组转换成10进制数 ? ? ? ? private static int ByteArrayToInt(byte arr1, byte arr2) ? ? ? ? { ? ? ? ? ? ? byte[] arr = new byte[] { arr1, arr2 }; ? ? ? ? ? ? int value = (int)((arr[0] & 0xFF) << 8) ? ? ? ? ? ? ? ? ? ? ? | (arr[1] & 0xFF); ? ? ? ? ? ? return value; ? ? ? ? } ? ? ? ? ? //读取硬件版本号 ? ? ? ? private void ReadHV() ? ? ? ? { ? ? ? ? ? ? GetCorrect = false; ? ? ? ? ? ? try ? ? ? ? ? ? { ? ? ? ? ? ? ? ? comm.WritePort(DataPackage(RD_HV, HARD_VERSION, 0), 0, 10);//发送RD_E2P命令,地址HARD_VERSION ? ? ? ? ? ? ? ? Delay(100); ? ? ? ? ? ? ? ? if (GetCorrect == true) ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? MessageBox.Show("硬件版本号读取成功!"); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? else ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? MessageBox.Show("硬件版本号读取失败!"); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? catch (Exception ex) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? MessageBox.Show(ex.Message); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? //写入硬件版本号 ? ? ? ? private void WriteHV() ? ? ? ? { ? ? ? ? ? ? if (tbVersion.Text.Length == 4 && isLegal(tbVersion.Text))//限定只能写入4个字符且输入合法 ? ? ? ? ? ? { ? ? ? ? ? ? ? ? try ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? int buf = Convert.ToInt32(tbVersion.Text); ? ? ? ? ? ? ? ? ? ? GetCorrect = false; ? ? ? ? ? ? ? ? ? ? comm.WritePort(DataPackage(WR_HV, HARD_VERSION, buf), 0, 10);//发送WR_HV命令,地址HARD_VERSION ? ? ? ? ? ? ? ? ? ? Delay(100); ? ? ? ? ? ? ? ? ? ? if (GetCorrect == true) ? ? ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? ? ? MessageBox.Show("硬件版本号写入成功!"); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? else ? ? ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? ? ? MessageBox.Show("硬件版本号写入失败!"); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? catch (Exception ex) ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? MessageBox.Show(ex.Message); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? else ? ? ? ? ? ? { ? ? ? ? ? ? ? ? MessageBox.Show("硬件版本号数据格式错误!请重新写入!"); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? //判断输入是否合法,必须是数字 ? ? ? ? private const string PATTERN = @"^[0-9]*$"; ? ? ? ? private bool isLegal(string hex) ? ? ? ? { ? ? ? ? ? ? return System.Text.RegularExpressions.Regex.IsMatch(hex, PATTERN); ? ? ? ? } ? ? ? ? ? //延时函数 ? ? ? ? [DllImport("kernel32.dll")] ? ? ? ? static extern uint GetTickCount(); ? ? ? ? static void Delay(uint ms) ? ? ? ? { ? ? ? ? ? ? uint start = GetTickCount(); ? ? ? ? ? ? while (GetTickCount() - start < ms) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? Application.DoEvents(); ? ? ? ? ? ? } ? ? ? ? }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。