zoukankan      html  css  js  c++  java
  • 工作日记-LED串口开发

    背景

    公司最近的一个项目中需要使用LED显示屏实时显示一些数据,经过调研,项目经理选择了泰美泉公司的产品,前几日硬件设备到了之后,笔者负责的中间件组就马不停蹄的开始了实际的调研与测试工作,因为之前有过对接LED设备的经验,所以对这次的调研还是比较有把握的。LED调研最核心的要解决,这个LED是否可以满足我方的项目需求。

    项目需求的核心点如下:

    • 实时性
    • 稳定性

    具体的展示UI是否可以满足也是需要考虑的方面,但是既然已经采购,UI就只能在使用这个LED的基础下尽可能的满足显示需求,所以本次测试不考虑UI设计。

    针对不同的关注点,笔者分配组员进行了相关的调研测试。

    泰美泉LED调研-开发模式的确定

    设备连接与启动

    泰美泉LED是针对我公司定制的,与服务器的通信方式为串口无线方式,具体如下

    • LED端:串口转无线模块(内部封装好了,只保留了一根天线暴露在外)
    • 服务器端:USB转串口无线模块

    win10笔记本电脑连接上usb串口无线模块后,可以自动安装驱动,安装完驱动后,打开设备管理器,可以看到端口部分这个设备正常显示了出来。

    右键属性,可以设置串口的波特率和串口号。设置好属性后,需要检查下LED端是否正常启动,检查方法:LED端使用移动电源供电,移动电源开关打开后,屏幕会闪烁一下,代表正常启动。

    设备基本调试

    设备和无线模块都连接好后,使用串口工具发送指令来查看LED设备是否可以正常工作。串口工具使用的是sscom串口调试助手。打开后显示如下:

    选择端口号为无线模块对应的端口号,波特率对应修改,点击打开串口就可以开始测试了。

    具体依次发送的测试内容如下:

    1. !#001%ZD00$$ //先清除所有区域,执行一次就行
    2. !#001%ZI01%ZC0000000000960064%ZA01%ZS03%ZH0050%F16%C1%AH1%AV1欢迎使用北京泰美泉2000型中长跑测试仪$$

    发现串口正确回复,并且LED屏幕显示了测试文字-欢迎使用北京泰美泉2000型中长跑测试仪。设备基本调试步骤成功。

    动态库环境调试

    上面的步骤是直接使用串口,调用字库协议来发送数据,此外,LED还提供了另外一种方式,就是调用dll动态库的方式来控制LED屏幕。这种方式需要搭建Java开发环境,并加载泰美泉提供的demo程序进行测试。

    组员在进行测试时,发现虽然已经正确修改了配置文件,但是Demo程序测试时会反馈失败,咨询对方技术人员后得知,数据未从串口发出,后来对接了另外一个动态库的技术人员,远程后,发现原来使用的Demo程序版本太低,使用对方的Demo程序就可以正常显示。

    工作小启示:

    很多时候与我们对接的是设备的销售商或者商务,对方提供的资料未必是最新的资料,尽量找对方的一线人员获取最新的资料数据。

    动态库调试最核心的步骤是修改配置文件信息,配置文件信息如下:

    [地址:0]
    CardType=21
    CardAddress=0
    CommunicationMode=0
    ScreemHeight=64
    ScreemWidth=96
    SerialBaud=9600
    SerialNum=3
    NetPort=5005
    IpAddress0=192
    IpAddress1=168
    IpAddress2=1
    IpAddress3=236
    ColorStyle = 1
    

    测试节目模式和实时模式后,均无法满足我们对于实时性更新的要求,所以本次开发不会选用动态库模式。

    字库模式调试

    既然动态库模式行不通,我们就开始测试字库模式,字库模式的协议比较简单,但是也比较受限,经过测试,虽然可以满足实时性的需求,但是针对目前的UI设计就无法满足,只能重新设计UI。

    最终确定的UI如下:

    阶段总结

    经过上述的一番测试,我们组最终确定了使用字库模式进行开发。具体开发计划是使用Java语言,调用串口工具类,直接发送协议字符串给LED进行实时显示。这个阶段的目标已经达到。


    泰美泉LED调研-Java串口环境

    串口环境搭建和工具类

    关于java串口环境的搭建的主题,网络上已经有很多优秀的文章,笔者主要参考了Java程序与串口的通信实现及调试 这篇文章进行了串口环境的搭建。

    核心步骤:

    1. 工程中创建lib文件夹,并放入RXTXcomm.jar,使用Maven构建项目的要在pom文件中添加下面的代码,使maven可以管理我们自己的jar包。

      <dependency>
      		<groupId>rxtx</groupId>
      		<artifactId>rxtx</artifactId>
      		<version>0.1</version>
      		<scope>system</scope>
      		<systemPath>${project.basedir}/lib/RXTXcomm.jar</systemPath>
      </dependency>
      
    2. JAVA_HOME/jre/bin 目录下放入两个dll

      • rxtxParallel.dll
      • rxtxSerial.dll
    3. 使用我整理好的工具类即可

      package com.icomp.tpft.util;
      
      import gnu.io.*;
      import org.apache.log4j.Logger;
      
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.OutputStream;
      import java.util.Enumeration;
      import java.util.TooManyListenersException;
      import java.util.concurrent.BlockingQueue;
      import java.util.concurrent.LinkedBlockingQueue;
      
      
      /**
       * 串口发送工具类,实现中会自动启动一个线程来处理接收数据并打印,无业务处理
       * (工具类可以直接用于不关注接收数据的场景)
       */
      public class ComUtil extends Thread implements SerialPortEventListener {
      
          private static final Logger logger = Logger.getLogger(ComUtil.class);
      
          // 监听器,我的理解是独立开辟一个线程监听串口数据
          private static CommPortIdentifier portId; // 串口通信管理类
          private static Enumeration<?> portList; // 有效连接上的端口的枚举
          private InputStream inputStream; // 从串口来的输入流
          private static OutputStream outputStream;// 向串口输出的流
          private static SerialPort serialPort; // 串口的引用
          // 堵塞队列用来存放读到的数据
          private BlockingQueue<String> msgQueue = new LinkedBlockingQueue<String>();
      
          /**
           * 接收串口数据方法
           *
           * @param serialPortEvent
           */
          @Override
          public void serialEvent(SerialPortEvent serialPortEvent) {
              switch (serialPortEvent.getEventType()) {
                  case SerialPortEvent.BI:
                  case SerialPortEvent.OE:
                  case SerialPortEvent.FE:
                  case SerialPortEvent.PE:
                  case SerialPortEvent.CD:
                  case SerialPortEvent.CTS:
                  case SerialPortEvent.DSR:
                  case SerialPortEvent.RI:
                  case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
                      break;
                  case SerialPortEvent.DATA_AVAILABLE:// 当有可用数据时读取数据
                      byte[] readBuffer = new byte[200];
                      try {
                          int numBytes = -1;
                          while (inputStream.available() > 0) {
                              numBytes = inputStream.read(readBuffer);
      
                              if (numBytes > 0) {
                                  msgQueue.add(":收到的数据为:"
                                          + new String(readBuffer));
                                  readBuffer = new byte[200];// 重新构造缓冲对象,否则有可能会影响接下来接收的数据
                              } else {
                                  msgQueue.add("本轮次没有读到数据");
                              }
                          }
                      } catch (IOException e) {
                          logger.error("出现异常", e);
                      }
                      break;
              }
          }
      
      
          /**
           * 打开串口方法
           * comName 端口号
           *
           * @return 返回1 表示端口打开成功,返回 0表示端口打开失败
           */
          public int startComPort(String comName) {
              // 通过串口通信管理类获得当前连接上的串口列表
              portList = CommPortIdentifier.getPortIdentifiers();
      
              while (portList.hasMoreElements()) {
      
                  // 获取相应串口对象
                  portId = (CommPortIdentifier) portList.nextElement();
      
                  logger.info("设备类型:--->" + portId.getPortType());
                  logger.info("设备名称:---->" + portId.getName());
                  // 判断端口类型是否为串口
                  if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
                      // 判断如果COM4串口存在,就打开该串口
                      if (portId.getName().equals(comName)) {
                          try {
                              // 打开串口名字为COM_3(名字任意),延迟为2毫秒
                              serialPort = (SerialPort) portId.open(comName, 2000);
      
                          } catch (PortInUseException e) {
                              logger.error("出现异常", e);
                              return 0;
                          }
                          // 设置当前串口的输入输出流
                          try {
                              inputStream = serialPort.getInputStream();
                              outputStream = serialPort.getOutputStream();
                          } catch (IOException e) {
                              logger.error("出现异常", e);
                              return 0;
                          }
                          // 给当前串口添加一个监听器
                          try {
                              serialPort.addEventListener(this);
                          } catch (TooManyListenersException e) {
                              logger.error("出现异常", e);
                              return 0;
                          }
                          // 设置监听器生效,即:当有数据时通知
                          serialPort.notifyOnDataAvailable(true);
      
                          // 设置串口的一些读写参数
                          try {
                              // 比特率、数据位、停止位、奇偶校验位
                              serialPort.setSerialPortParams(9600,
                                      SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
                                      SerialPort.PARITY_NONE);
                          } catch (UnsupportedCommOperationException e) {
                              logger.error("出现异常", e);
                              return 0;
                          }
                          return 1;
                      }
                  }
              }
              return 0;
          }
      
          @Override
          public void run() {
              try {
                  logger.info("--------------任务处理线程运行了--------------");
                  while (true) {
                      Thread.sleep(1, 10);
                      // 如果堵塞队列中存在数据就将其输出
                      if (msgQueue.size() > 0) {
                          logger.info(msgQueue.take());
                      }
                  }
              } catch (InterruptedException e) {
                  logger.error("出现异常", e);
              }
          }
      
          /**
           * 发送指令方法(未初始化自动初始化)
           *
           * @param command
           */
          public static void writeCommand(String command, String comName) {
              if (outputStream == null) {
                  logger.info("当前串口未初始化,无法写入数据!即将开始初始化!");
                  ComUtil cRead = new ComUtil();
                  int i = cRead.startComPort(comName);
                  if (i == 1) {
                      logger.info("当前串口初始化成功!");
                      // 启动线程来处理收到的数据
                      cRead.start();
                      logger.info("读取线程启动!");
                      doWriteCommand(command);
                  } else {
                      logger.info("当前串口初始化失败!");
                      return;
                  }
              } else {
                  doWriteCommand(command);
              }
          }
      
          private static void doWriteCommand(String command) {
              try {
                  //String st = "!#001%ZI01%ZC0000000000960064%ZA01%ZS03%ZH0050%F16%C1%AH1%AV1101:10'00 01202:20'00 02303:30'00 03404:40'00 04$$";
                  String st = command;
                  //logger.info("发出字节:" + HexStringUtils.toHexString(st.getBytes("gbk")));
                  outputStream.write(st.getBytes("gbk"), 0,
                          st.getBytes("gbk").length);
              } catch (IOException e) {
                  logger.error("出现异常", e);
              }
          }
      
          public static void main(String[] args) throws InterruptedException {
              for (int i = 0; i < 10; i++) {
                  ComUtil.writeCommand("!#001%ZI01%ZC0000000000960064%ZA01%ZS03%ZH0050%F16%C1%AH1%AV1101:10'00 01202:20'00 02303:30'00 03404:40'00 04$$", "COM3");
                  Thread.sleep(1000);
              }
          }
      }
      
      
    4. 使用main方法测试即可。注意,要修改对应的串口参数,以及修改串口具体指令

    业务方法

    具体的业务代码这里就不更新在这里了,业务代码的核心会使用

    ComUtil.writeCommand
    

    这个方法来调用驱动层发送。


    总结

    本次测试,测试了LED的实时性,并且成功的搭建了JAVA的串口开发环境,我们项目组对于LED硬件对接又多了一些经验。

  • 相关阅读:
    1.5寻找倒数第k个元素
    MySQL基础之分组函数
    MySQL基础之单行函数
    MySQL基础查询(一)
    gem install redis Fetching: redis-4.1.3.gem (100%) ERROR: Error installing redis: redis requires Ruby version >= 2.3.0.
    SQL语句
    使用kill无法杀死mysql进程
    Ansible学习笔记
    rsync报错:rsync: chgrp ".hejian.txt.D1juHb" (in backup) failed: Operation not permitted (1)
    Linux磁盘管理
  • 原文地址:https://www.cnblogs.com/ging/p/13750309.html
Copyright © 2011-2022 走看看