怎样监听或者拦截串口上的数据

接口/总线/驱动

1145人已加入

描述

  串口全称为串行接口,一般指COM接口,是采用串行通信方式的扩展接口。其特点是数据位的传送按位顺序进行,最少只需一根传输线即可完成,成本低但传送速度慢。由于串口(COM)不支持热插拔及传输速率较低,目前部分新主板和大部分便携电脑已取消该接口。现在串口多用于工业控制和测量设备以及部分通信设备中。

  根据美国电子工业协会(EIA: Electronic Industry Association)制定的标准,串口可以分为RS-232、RS-422以及RS-485等种类,其中以RS-232类型的接口最为典型和常见,如图 1所示,是RS-232类型9针串口的实物示意图。RS-232类型9针串口每一个引脚的作用说明如图 2所示。

  

  串口

  当需要编程操纵硬件时会遇到过这样的问题,就是通过串口来接收硬件发来的数据,或是通过串口向硬件发送某种格式的命令。在C#平台上,可以通过 System.IO.Ports 命名空间下的SerialPort 类来实现。

  下面是我做过的一个简单的示例,首先获取本机关联的串行端口列表,然后获取配置文件中配置的COM端口,检查是否在本机串行端口列表中,若在列表中则进一步实例化串口对象,并为串口对象指定数据接收事件来实现监听,示例代码如下:

  using System.IO.Ports;

  namespace SerialTest

  {

  public class SerialTest

  {

  #region 串口监听

  private SerialPort serialPort = null;

  /// 《summary》

  /// 开启串口监听

  /// 《/summary》

  private void StartSerialPortMonitor()

  {

  List《string》 comList = GetComlist(false); //首先获取本机关联的串行端口列表

  if (comList.Count == 0)

  {

  DialogForm.Show(“提示信息”, “当前设备不存在串行端口!”);

  System.Environment.Exit(0); //彻底退出应用程序

  }

  else

  {

  string targetCOMPort = ConfigurationManager.AppSettings[“COMPort”].ToString();

  //判断串口列表中是否存在目标串行端口

  if (!comList.Contains(targetCOMPort))

  {

  DialogForm.Show(“提示信息”, “当前设备不存在配置的串行端口!”);

  System.Environment.Exit(0); //彻底退出应用程序

  }

  serialPort = new SerialPort();

  //设置参数

  serialPort.PortName = ConfigurationManager.AppSettings[“COMPort”].ToString(); //通信端口

  serialPort.BaudRate = Int32.Parse(ConfigurationManager.AppSettings[“BaudRate”].ToString()); //串行波特率

  serialPort.DataBits = 8; //每个字节的标准数据位长度

  serialPort.StopBits = StopBits.One; //设置每个字节的标准停止位数

  serialPort.Parity = Parity.None; //设置奇偶校验检查协议

  serialPort.ReadTimeout = 3000; //单位毫秒

  serialPort.WriteTimeout = 3000; //单位毫秒

  //串口控件成员变量,字面意思为接收字节阀值,

  //串口对象在收到这样长度的数据之后会触发事件处理函数

  //一般都设为1

  serialPort.ReceivedBytesThreshold = 1;

  serialPort.DataReceived += new SerialDataReceivedEventHandler(CommDataReceived); //设置数据接收事件(监听)

  try

  {

  serialPort.Open(); //打开串口

  }

  catch (Exception ex)

  {

  DialogForm.Show(“提示信息”, “串行端口打开失败!具体原因:” + ex.Message);

  System.Environment.Exit(0); //彻底退出应用程序

  }

  }

  }

  /// 《summary》

  /// 串口数据处理函数

  /// 《/summary》

  /// 《param name=“sender”》《/param》

  /// 《param name=“e”》《/param》

  public void CommDataReceived(Object sender, SerialDataReceivedEventArgs e)

  {

  try

  {

  //Comm.BytesToRead中为要读入的字节长度

  int len = serialPort.BytesToRead;

  Byte[] readBuffer = new Byte[len];

  serialPort.Read(readBuffer, 0, len); //将数据读入缓存

  //处理readBuffer中的数据,自定义处理过程

  string msg = encoding.GetString(readBuffer, 0, len); //获取出入库产品编号

  DialogForm.Show(“接收到的信息”, msg);

  }

  catch(Exception ex)

  {

  DialogForm.Show(“提示信息”, “接收返回消息异常!具体原因:” + ex.Message);

  }

  }

  /// 《summary》

  /// 关闭串口

  /// 《/summary》

  private void Stop()

  {

  serialPort.Close();

  }

  /// 《summary》

  /// 获取本机串口列表

  /// 《/summary》

  /// 《param name=“isUseReg”》《/param》

  /// 《returns》《/returns》

  private List《string》 GetComlist(bool isUseReg)

  {

  List《string》 list = new List《string》();

  try

  {

  if (isUseReg)

  {

  RegistryKey RootKey = Registry.LocalMachine;

  RegistryKey Comkey = RootKey.OpenSubKey(@“HARDWAREDEVICEMAPSERIALCOMM”);

  String[] ComNames = Comkey.GetValueNames();

  foreach (String ComNamekey in ComNames)

  {

  string TemS = Comkey.GetValue(ComNamekey).ToString();

  list.Add(TemS);

  }

  }

  else

  {

  foreach (string com in SerialPort.GetPortNames()) //自动获取串行口名称

  list.Add(com);

  }

  }

  catch

  {

  DialogForm.Show(“提示信息”, “串行端口检查异常!”);

  System.Environment.Exit(0); //彻底退出应用程序

  }

  return list;

  }

  #endregion 串口监听

  }

  }

  从串口读数据

  从串口COM11发送的数据最终将到达与其连通的串口COM21,如果COM21处于可用状态,则到达的数据将被缓存,等待程序的读取。从串口读入数据有多种模式,本文将介绍“轮询模式”和事件监听模式。

  “轮询模式”是指程序(线程)每隔固定的时间就对串口进行一次扫描,如果扫描发现串口中有可用数据,则进行读取。Com21PollingListener类使用“事件监听模式”读取串口COM21接收到的数据:

  Com21PollingListener.java

  package com.serialPort.listener;

  import java.io.IOException;

  import java.io.InputStream;

  import javax.comm.CommPortIdentifier;

  import javax.comm.NoSuchPortException;

  import javax.comm.PortInUseException;

  import javax.comm.SerialPort;

  /**

  * Com21PollingListener类使用“轮训”的方法监听串口COM21,

  * 并通过COM21的输入流对象来获取该端口接收到的数据(在本文中数据来自串口COM11)。

  */

  public class Com21PollingListener {

  public static void main(String[] args){

  //1.定义变量

  CommPortIdentifier com21 = null;//未打卡的端口

  SerialPort serialCom21 = null;//打开的端口

  InputStream inputStream = null;//端口输入流

  try{

  //2.获取并打开串口COM21

  com21 = CommPortIdentifier.getPortIdentifier(“COM21”);

  serialCom21 = (SerialPort) com21.open(“Com21Listener”, 1000);

  //3.获取串口的输入流对象

  inputStream = serialCom21.getInputStream();

  //4.从串口读入数据

  //定义用于缓存读入数据的数组

  byte[] cache = new byte[1024];

  //记录已经到达串口COM21且未被读取的数据的字节(Byte)数。

  int availableBytes = 0;

  //无限循环,每隔20毫秒对串口COM21进行一次扫描,检查是否有数据到达

  while(true){

  //获取串口COM21收到的可用字节数

  availableBytes = inputStream.available();

  //如果可用字节数大于零则开始循环并获取数据

  while(availableBytes 》 0){

  //从串口的输入流对象中读入数据并将数据存放到缓存数组中

  inputStream.read(cache);

  //将获取到的数据进行转码并输出

  for(int j = 0;j 《 cache.length && j 《 availableBytes; j++){

  //因为COM11口发送的是使用byte数组表示的字符串,

  //所以在此将接收到的每个字节的数据都强制装换为char对象即可,

  //这是一个简单的编码转换,读者可以根据需要进行更加复杂的编码转换。

  System.out.print((char)cache[j]);

  }

  System.out.println();

  //更新循环条件

  availableBytes = inputStream.available();

  }

  //让线程睡眠20毫秒

  Thread.sleep(20);

  }

  }catch(InterruptedException e){

  e.printStackTrace();

  }catch (NoSuchPortException e) {

  //找不到串口的情况下抛出该异常

  e.printStackTrace();

  } catch (PortInUseException e) {

  //如果因为端口被占用而导致打开失败,则抛出该异常

  e.printStackTrace();

  } catch (IOException e) {

  //如果获取输出流失败,则抛出该异常

  e.printStackTrace();

  }

  }

  }

  “事件监听模式”是为串口注册一个事件监听类,当有数据到达串口的时候就会触发事件,在事件的响应方法中读取串口接收到的数据。Com21EventListener类使用“事件监听模式”读取串口COM21接收到的数据:

  Com21EventListener.java

  package com.serialPort.listener;

  import java.io.IOException;

  import java.io.InputStream;

  import java.util.TooManyListenersException;

  import javax.comm.CommPortIdentifier;

  import javax.comm.NoSuchPortException;

  import javax.comm.PortInUseException;

  import javax.comm.SerialPort;

  import javax.comm.SerialPortEvent;

  import javax.comm.SerialPortEventListener;

  /**

  * Com21EventListener类使用“事件监听模式”监听串口COM21,

  * 并通过COM21的输入流对象来获取该端口接收到的数据(在本文中数据来自串口COM11)。

  * 使用“事件监听模式”监听串口,必须字定义一个事件监听类,该类实现SerialPortEventListener

  * 接口并重写serialEvent方法,在serialEvent方法中编写监听逻辑。

  */

  public class Com21EventListener implements SerialPortEventListener {

  //1.定义变量

  CommPortIdentifier com21 = null;//未打卡的端口

  SerialPort serialCom21 = null;//打开的端口

  InputStream inputStream = null;//输入流

  //2.构造函数:

  //实现初始化动作:获取串口COM21、打开串口、获取串口输入流对象、为串口添加事件监听对象

  public Com21EventListener(){

  try {

  //获取串口、打开窗串口、获取串口的输入流。

  com21 = CommPortIdentifier.getPortIdentifier(“COM21”);

  serialCom21 = (SerialPort) com21.open(“Com21EventListener”, 1000);

  inputStream = serialCom21.getInputStream();

  //向串口添加事件监听对象。

  serialCom21.addEventListener(this);

  //设置当端口有可用数据时触发事件,此设置必不可少。

  serialCom21.notifyOnDataAvailable(true);

  } catch (NoSuchPortException e) {

  e.printStackTrace();

  } catch (PortInUseException e) {

  e.printStackTrace();

  } catch (IOException e) {

  e.printStackTrace();

  } catch (TooManyListenersException e) {

  e.printStackTrace();

  }

  }

  //重写继承的监听器方法

  @Override

  public void serialEvent(SerialPortEvent event) {

  //定义用于缓存读入数据的数组

  byte[] cache = new byte[1024];

  //记录已经到达串口COM21且未被读取的数据的字节(Byte)数。

  int availableBytes = 0;

  //如果是数据可用的时间发送,则进行数据的读写

  if(event.getEventType() == SerialPortEvent.DATA_AVAILABLE){

  try {

  availableBytes = inputStream.available();

  while(availableBytes 》 0){

  inputStream.read(cache);

  for(int i = 0; i 《 cache.length && i 《 availableBytes; i++){

  //解码并输出数据

  System.out.print((char)cache[i]);

  }

  availableBytes = inputStream.available();

  }

  System.out.println();

  } catch (IOException e) {

  e.printStackTrace();

  }

  }

  }

  //在main方法中创建类的实例

  public static void main(String[] args) {

  new Com21EventListener();

  }

  }

  读写程序的联合运行

  串口能接收到数据的前提是该串口处于打开(可用)状态,如果串口处于关闭状态,那么发送到该串口的数据就会丢失。所以在实验的过程中,如果使用铜线连接同一个串口的引脚2和引脚3,一定要注意的是千万不能在向串口发送完数据之后关闭该串口,然后再次打开串口去读取数据,一定要让串口始终处于打开状态直到程序运行结束。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分