程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 具體解讀Java的串口編程

具體解讀Java的串口編程

編輯:關於JAVA

具體解讀Java的串口編程。本站提示廣大學習愛好者:(具體解讀Java的串口編程)文章只能為提供參考,不一定能成為您想要的結果。以下是具體解讀Java的串口編程正文


罕見成績

JavaComm 和 RxTX 裝置時有一些不同凡響的處所。激烈建議依照裝置解釋一點點的裝置。假如裝置解釋請求一個jar文件或一個同享庫必需在某一特定的文件夾下,那這就意味著須要嚴正看待。假如解釋請求一個特定的文件或裝備須要具有一個特定的一切權或拜訪權,這也意味著須要嚴正處置。許多裝置成績都只是由於沒有依照裝置解釋請求的去做而惹起的。


特殊要留意的是一些版本的JavaComm會帶有兩個裝置解釋。一個用於java 1.2及今後的版本,一個用於java 1.1版本。應用毛病的裝置解釋會招致不克不及任務的裝置成果。另外一方面,TxTx的一些版本/構件/包會包括不完整的解釋。在這類情形下須要取得相干的RxTx宣布的源碼,它包括了完全的裝置解釋。

別的要留意Windows的Jdk裝置法式會包括三個java虛擬機,是以會有三個擴大文件夾。

  1.     一個作為JDK的構成部門。
  2.     一個作為與運轉JDK對象的JDK一路的公有JRE的一部門。
  3.     一個作為與運轉運用法式的JDK一路的公共JRE的一部門。
  4. 更有甚者乃至會有第4個jre,它存在於\Windows的目次構造中。 JavaComm應當作為擴大被裝置到JDK和一切公共JRE中。

    Webstart

       JavaComm

    關於JavaComm和RxTx的一個罕見成績是它們不支撐經由過程Java WebStart停止裝置:JavaComm的污名昭著是由於須要將一個稱為javax.comm.properties的文件放到JDK lib目次下,而這是不克不及經由過程Java WebStart完成的。很使人懊喪的是,關於該文件的須要是JavaComm中一些不用要的設計/決議所招致的惡果,而JavaComm的設計者們可以很輕易地防止這類工作。Sun執拗地謝絕修改這個毛病,他們強調這個機制是必弗成少的。他們是在睜著眼說實話,特殊是當說起JavaComm時,由於Java在很長一段時光內具有一個專門用於此類意圖的辦事供給者架構。

    這個屬性文件中的內容只要一行,即供給當地驅動的java類稱號。

    driver=com.sun.comm.Win32Driver
    
    


    以下是一個可以經由過程Web Start安排JavaComm而疏忽誰人傷頭腦的屬性文件的技能。但它有嚴重的缺點,而且在安排較新的JavaComm時能夠會掉敗-假如Sun會做一個新版本的話。

    起首,封閉平安治理器(security manager)。Sun的一些蠢貨法式員認為一遍又一遍地檢討恐怖的javax.comm.properties文件的存在是很酷的工作,特殊是當它最後曾經被加載完成以後。這只是純真地檢討文件能否存在而不為其他緣由。

    System.setSecurityManager(null);
    
    

    然後,現在始化JavaComm API時,手動初始化驅動。

    String driverName = "com.sun.comm.Win32Driver"; // or get as a JNLP property
    CommDriver commDriver = (CommDriver)Class.forName(driverName).newInstance();
    commDriver.initialize();
    
    

    RxTx

    RxTx在某些平台上須要轉變串口裝備的一切權和拜訪權。這也是沒法經由過程WebStart完成的事。


    在法式啟動時你應當請求用戶作為超等用戶來履行需要的設置。特殊的,RxTx有一個形式婚配算法來驗證“正當”的串口裝備名。當或人想應用不尺度的裝備,例如USB轉串口轉換器(USB-to-serial converter)時,這常會把工作弄砸。這個機制可以被體系屬性屏障失落。概況參照RxTx的裝置解釋。
    JavaComm API
    引言

    Java官方串口通訊API是JavaComm API。這個API不是Java 2尺度版的構成部門,因此此API的完成須要零丁下載。不幸的是,JavaComm沒有取得Sun足夠的看重,現實的保護時光也不是很長。Sun只是偶然修復一些不主要的bug,卻沒有做過一些早已過時的主要檢驗。


    本節論述JavaComm API的根本操作。所供給的源碼堅持簡化以展現重點,在現實運用中應用須要完美。

    這章的源碼其實不是獨一可用的示例代碼。許多例子中都包括JavaComm下載。這些例子簡直包含比其API文檔更多的關於若何應用它的信息。不幸的是,Sun公司沒有任何真實的教程或一些解釋文檔。是以,要懂得這個API的機制,進修這些示例代碼是值得的,也仍須要進修這個API文檔。但最好的辦法是,進修這些例子並應用它們。因為缺乏易用的運用和懂得這些API的編程模子有艱苦,API平日備受鞭撻。比擬其名望和功效,這個API更好,但僅此罷了。


    該API采取回調機制告訴法式員有新數據到來。這也是進修這一機制的好主張,而不是依附訊問端口。不像Java中的其他回調接口(如:在圖形界面),這個接口只許可一個監聽器監聽事宜。假如多個監聽器要求監聽幾個事宜,主監聽器必需經由過程分配信息給其他二級監聽器的方法來完成。

    下載與裝置

    下載

    Sun公司的JavaComm網頁指向下載地址。在這個地址下,Sun以後(2007年)供給了支撐Solaris/SPARC、Solaris/x86曾經Linux x86的JavaComm 3.0版本。下載須要注冊一個Sun公司的賬戶。下載頁供給了注冊頁的鏈接。注冊的目標其實不清晰。在為注冊時,用戶可下載JDK和JREs,但關於這簡直眇乎小哉的JavaComm,Sun公司在軟件分銷和出口方面卻征引司法條則和當局限制。


    官方已不再供給JavaComm的Windows版本,而且Sun曾經違反了他們本身的產物逝世亡戰略-不克不及在Java產物集中下載。但仍可以從這下載2.0的Windows版本(javacom 2.0).
     

    裝置

    依照與下載一路的裝置解釋停止裝置。一些版本的JavaComm 2.0會包括兩個裝置解釋。這兩個解釋間最顯著的差別是毛病的誰人是用於陳舊的Java1.1情況的,而實用於Java 1.2(jdk1.2.html)的誰人才是准確的。

    Windows用戶能夠不會心識到他們在分歧的處所(普通是3到4個)裝置了統一個VM的正本。一些IDE和Java運用法式能夠也會帶有他們本身的公有JRE/JDK。所以JavaComm須要反復裝置到這些VM(JDK和JRE)中,如許能力夠開辟和履行串口運用法式。


    IDE 都有代表性的IDE的方法來得知一個新的庫(類和文檔)。平日一個庫想JavaComm不只須要被IDE辨認,並且每一個應用該庫的項目也應該辨認。浏覽IDE的文檔,應當留意老的JavaComm 2.0 版本和JavaDoc API文檔應用的是Java 1.0 的Java Doc 結構。一些古代的IDE曾經不再熟悉這些構造其實不能將JavaComm2.0的文檔集成到他們的贊助體系中了。在這類情形下須要一個內部的閱讀器來浏覽文檔(推舉運動)

    一旦軟件裝置完成,它便會推舉測試樣例和JavaDoc 目次。構建並運轉樣例運用來確認裝置能否准確時很有事理的。樣例法式平日須要一些小的調劑以便運轉在特殊的平台上(像改寫硬編碼的com端口標識符)。在運轉一個樣例法式時最好有一些串行硬件,想cabling,零調制解調器,接線盒,一個真實的貓,PABX和其他可用的裝備。

    Serial_Programming:RS-232 Connections 和Serial_Programming:Modems and AT Commands 供給了一些如何搭建串行運用開辟情況的信息。

    找到預期的串口

    當用JavaComm串行編程時起首要做的三件事

    1.     列舉JavaComm能拜訪的一切串口(端口標識)
    2.     從能拜訪的端口標識當選擇預期的端口標識
    3.     經由過程端口標識獲得端口
    4. 列舉和選擇希冀的端口標識在統一個輪回中完成:

      import javax.comm.*;
      import java.util.*;
      ...
      //
      // Platform specific port name, here= a Unix name
      //
      // NOTE: On at least one Unix JavaComm implementation JavaComm 
      //    enumerates the ports as "COM1" ... "COMx", too, and not
      //    by their Unix device names "/dev/tty...". 
      //    Yet another good reason to not hard-code the wanted
      //    port, but instead make it user configurable.
      //
      String wantedPortName = "/dev/ttya";
       //
      // Get an enumeration of all ports known to JavaComm
      //
      Enumeration portIdentifiers = CommPortIdentifier.getPortIdentifiers();
      //
      // Check each port identifier if 
      //  (a) it indicates a serial (not a parallel) port, and
      //  (b) matches the desired name.
      //
      CommPortIdentifier portId = null; // will be set if port found
      while (portIdentifiers.hasMoreElements())
      {
        CommPortIdentifier pid = (CommPortIdentifier) portIdentifiers.nextElement();
        if(pid.getPortType() == CommPortIdentifier.PORT_SERIAL &&
          pid.getName().equals(wantedPortName)) 
        {
          portId = pid;
          break;
        }
      }
      if(portId == null)
      {
        System.err.println("Could not find serial port " + wantedPortName);
        System.exit(1);
      }
      //
      // Use port identifier for acquiring the port
      //
      ...
      
      留意:
      
      JavaComm會從與其綁定的特定平台相干的驅動中取得一個默許的可拜訪串口標識列表。這個列表現實上不克不及經由過程JavaComm停止設置裝備擺設。辦法CommPortIdentifier.addPortName()是有誤導性的,由於驅動類是與平台相干的,並且它們的完成不是公共API的構成部門。依附於驅動,這個端口列表能夠會在驅動中停止設置裝備擺設/擴大。所以,假如JavaComm沒有找到某一特定端口,對驅動停止一些修改有時會有所贊助。
      
      某端口標識符一旦被找到,便可以用它獲得希冀的端口:
      
      //
      // Use port identifier for acquiring the port
      //
      SerialPort port = null;
      try {
        port = (SerialPort) portId.open(
          "name", // Name of the application asking for the port 
          10000  // Wait max. 10 sec. to acquire port
        );
      } catch(PortInUseException e) {
        System.err.println("Port already in use: " + e);
        System.exit(1);
      }
      //
      // Now we are granted exclusive access to the particular serial
      // port. We can configure it and obtain input and output streams.
      //
      ...
      
      


      初始化串口

      串口的初始化是很直不雅的。可以逐一地設置通訊參數(波特率,數據位,停滯位,奇偶校驗),也能夠應用便利的setSerialPortParams(...)辦法一下把他們弄定。

      作為初始化的一部門,通訊的輸出輸入流可以在以下的示例中設置裝備擺設。

      import java.io.*;
      ...
      //
      // Set all the params. 
      // This may need to go in a try/catch block which throws UnsupportedCommOperationException
      //
      port.setSerialPortParams(
        115200,
        SerialPort.DATABITS_8,
        SerialPort.STOPBITS_1,
        SerialPort.PARITY_NONE);
      //
      // Open the input Reader and output stream. The choice of a
      // Reader and Stream are arbitrary and need to be adapted to
      // the actual application. Typically one would use Streams in
      // both directions, since they allow for binary data transfer,
      // not only character data transfer.
      //
      BufferedReader is = null; // for demo purposes only. A stream would be more typical.
      PrintStream  os = null;
      try {
       is = new BufferedReader(new InputStreamReader(port.getInputStream()));
      } catch (IOException e) {
       System.err.println("Can't open input stream: write-only");
       is = null;
      }
      //
      // New Linux systems rely on Unicode, so it might be necessary to
      // specify the encoding scheme to be used. Typically this should
      // be US-ASCII (7 bit communication), or ISO Latin 1 (8 bit
      // communication), as there is likely no modem out there accepting
      // Unicode for its commands. An example to specify the encoding
      // would look like:
      //
      //   os = new PrintStream(port.getOutputStream(), true, "ISO-8859-1");
      //
      os = new PrintStream(port.getOutputStream(), true);
       //
      // Actual data communication would happen here
      // performReadWriteCode();
      //
      //
      // It is very important to close input and output streams as well
      // as the port. Otherwise Java, driver and OS resources are not released.
      //
      if (is != null) is.close();
      if (os != null) os.close();
      if (port != null) port.close();
      
      

      簡略數據傳輸
      簡略地寫入數據
       

      將數據寫入到串口與根本的java IO一樣簡略。但在你應用AT Hayes 協定時仍有一些留意事項:

      •     不要在輸入流(OutputStream)中應用prinln(或其他主動附加"\n"的辦法)。調制解調器的AT Hayes協定應用"\r\n"作為分隔符(而不考濾底層的操作體系)。
      •     寫入輸入流以後,假如調制解調器設置了回顯敕令行,輸出流的緩沖區會存有發送的指令的復述(有換行)和另外一個換行("AT"指令的呼應)。所以做為寫操作的一部門,要確保清算輸出流中的這類信息(現實上它可以用於查錯)。
      •     當應用Reader/Writer(不是個好主張)時,起碼要設置字符編碼為US-ASCII而不是應用體系平台的默許編碼,不然法式能夠不會運轉。
      •     由於應用調制解調器的重要操作是傳輸原始數據,與調制解調器的通訊應當應用輸出/輸入流,而不是Reader/Writer.

      Clipboard
       

      To do:

          說明若何在統一個流中混雜二進制與字符的輸出輸入


          修正示例法式使其應用流

      // Write to the output 
      os.print("AT");
      os.print("\r\n"); // Append a carriage return with a line feed
      
      is.readLine(); // First read will contain the echoed command you sent to it. In this case: "AT"
      is.readLine(); // Second read will remove the extra line feed that AT generates as output
      
      

      簡略的數據讀取(輪詢)

      假如你准確的應用了寫操作(如上所述),讀操作只需簡略的一條敕令。

      // Read the response
      String response = is.readLine(); // if you sent "AT" then response == "OK"
      
      

      簡略讀寫的成績

      上一節中演示的簡略串口讀寫有很嚴重的缺點。一切的操作都是經由過程壅塞I/O完成的。這意味著當沒有可讀數據時,或輸入緩沖區滿(裝備不克不及接收更多半據)時:

      讀寫辦法(在後面示例中的是os.print()或is.readLine())不會前往, 招致運用法式被暫停。更精確地說,讀寫線程被壅塞了。假如誰人線程是運用法式主線程的話,運用法式會停滯直到壅塞前提被釋放(即有可讀數據達到或裝備從新接收數據)。


      除非運用法式是最原始的那種,不然法式被壅塞是毫不許可的。例如,最最少也要能讓用戶撤消通訊操作。這須要應用非壅塞I/O或異步I/O。但是JavaComm是基於Java的尺度壅塞I/O體系(InputStream,OutputStream)的,但可以采取稍後展現的一個變形技能。

      所謂的"變形技能"是JavaComm經由過程事宜告訴機制為異步I/O供給的無限的支撐。但在Java中要在壅塞I/O的基本上完成非壅塞I/O的經常使用處理計劃是應用線程。關於串口寫操作這個計劃是實在可行的,激烈建議應用一個零丁的線程對串口停止寫操作-雖然曾經應用了事宜告訴機制,這稍後會做出說明。

      讀操作也應當在一個零丁的線程中停止處置,但假如采取了JavaComm的事宜告訴機制這也不是必需的。總結:

      讀操作應用事宜告訴和/或零丁線程;

      寫操作都要應用零丁線程,可選用事宜告訴機制。

      接上去的部門會引見一些其他細節。

      事宜驅動串行通訊
      引言

      JavaComm API供給了事宜告訴機制以戰勝壅塞I/O帶來的成績。但在這個典范的Sun方法中這個機制也有成績的。

      准繩上一個運用法式可以注冊事宜監聽器到一個特定的串口以吸收產生在這個端口上的主要事宜的告訴。讀寫數據的兩個最成心思的事宜類型是

          javax.comm.SerialPortEvent.DATA_AVAILABLE和  javax.comm.SerialPortEvent.OUTPUT_BUFFER_EMPTY.

      但這也帶來了兩個成績:

      1.     每一個串口只能注冊一個事宜監聽器。這會強迫法式員編寫"偉大"的監聽器,它以吸收到的事宜類型來辨別要停止的操作。
      2.     OUTPUT_BUFFER_EMPTY是一個可選的事宜類型。Sun在文檔中隱晦地提到JavaComm的完成紛歧建都會支撐發生這個事宜類型。
      3. 在停止具體評論辯論前,下一節將會演示完成和注冊一個串口事宜處置器的重要方法。要記住一個串口只能有一個事宜處置器,並且它要處置一切能夠的事宜。


        設置串行事宜處置器

         

        import javax.comm.*;
         
        /**
         * Listener to handle all serial port events.
         *
         * NOTE: It is typical that the SerialPortEventListener is implemented
         *    in the main class that is supposed to communicate with the
         *    device. That way the listener has easy access to state information
         *    about the communication, e.g. when a particular communication
         *    protocol needs to be followed.
         *
         *    However, for demonstration purposes this example implements a
         *    separate class.
         */ 
        class SerialListener implements SerialPortEventListener {
         
          /**
           * Handle serial events. Dispatches the event to event-specific
           * methods.
           * @param event The serial event
           */
          @Override
          public void serialEvent(SerialPortEvent event){
         
            //
            // Dispatch event to individual methods. This keeps this ugly
            // switch/case statement as short as possible.
            //
            switch(event.getEventType()) {
              case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
                outputBufferEmpty(event);
                break;
         
              case SerialPortEvent.DATA_AVAILABLE:
                dataAvailable(event);
                break;
         
        /* Other events, not implemented here ->
              case SerialPortEvent.BI:
                breakInterrupt(event);
                break;
         
              case SerialPortEvent.CD:
                carrierDetect(event);
                break;
         
              case SerialPortEvent.CTS:
                clearToSend(event);
                break;
         
              case SerialPortEvent.DSR:
                dataSetReady(event);
                break;
         
              case SerialPortEvent.FE:
                framingError(event);
                break;
         
              case SerialPortEvent.OE:
                overrunError(event);
                break;
         
              case SerialPortEvent.PE:
                parityError(event);
                break;
              case SerialPortEvent.RI:
                ringIndicator(event);
                break;
        <- other events, not implemented here */
         
            }
          }
         
          /**
           * Handle output buffer empty events.
           * NOTE: The reception of this event is optional and not
           *    guaranteed by the API specification.
           * @param event The output buffer empty event
           */
          protected void outputBufferEmpty(SerialPortEvent event) {
            // Implement writing more data here
          }
         
          /**
           * Handle data available events.
           *
           * @param event The data available event
           */
          protected void dataAvailable(SerialPortEvent event) {
            // implement reading from the serial port here
          }
        }
        

        監聽器一旦完成,便可用來監聽特定的串口事宜。要做到如斯,須要為串口添加一個監聽器實例。另外,每一個事宜類型的吸收須要停止零丁請求。
         

        SerialPort port = ...;
        ...
        //
        // Configure port parameters here. Only after the port is configured it
        // makes sense to enable events. The event handler might be called immediately
        // after an event is enabled.
        ...
         
        //
        // Typically, if the current class implements the SerialEventListener interface
        // one would call
        //
        //    port.addEventListener(this);
        //
        // but for our example a new instance of SerialListener is created:
        //
        port.addEventListener(new SerialListener());
         
        //
        // Enable the events we are interested in
        //
        port.notifyOnDataAvailable(true);
        port.notifyOnOutputEmpty(true);
         
        /* other events not used in this example ->
        port.notifyOnBreakInterrupt(true);
        port.notifyOnCarrierDetect(true);
        port.notifyOnCTS(true);
        port.notifyOnDSR(true);
        port.notifyOnFramingError(true);
        port.notifyOnOverrunError(true);
        port.notifyOnParityError(true);
        port.notifyOnRingIndicator(true);
        <- other events not used in this example */
        

        數據寫入

        應用零丁分別的過程停止數據寫入只要一個目標:防止全部運用法式塊因為某一個串口未預備好寫數據而鎖定。

        一個簡略的,線程平安的環形緩沖區完成

        應用一個自力於主法式線程的線程停止寫操作,注解須要某種方法將要寫入的數據從主運用線程(主線程)提交給寫線程。這可以采取一個同享的異步事宜緩沖區,例如一個byte數組。別的,主法式還須要某種方法決議能否可以往數據緩沖區中寫數據或許數據緩沖區能否曾經滿了。假如數據緩沖區已滿,注解串口還沒有預備好寫操作,而且要輸入的數據正在列隊。主法式須要在同享數據緩沖區中輪詢可用的新的余暇空間。但是,在主法式輪詢的間隙可以做些其他的事,例如更新用戶界面(GUI),供給一個可以加入發送數據的敕令提醒等等。


        乍一看PipedInputStream/PipedOutputStream關於這類通訊是一個不錯的主張。但假如管道流真的有效的話那Sun就不是Sun了。假如與之對應的PipedOutputStream沒有實時清算的話,PipedInputStream會產生壅塞,進而會壅塞運用法式線程。就算應用自力線程也防止不了。而java.nio.Pipe也有與此雷同的成績。它的壅塞行動與平台相干。而將JavaComm應用的傳統I/O改成NIO也不是很好。

        在本文中采取了一個很簡略的同步的環形緩沖區來停止線程間數據傳遞。在實際世界中的運用法式極可能會應用加倍龐雜的緩沖區完成。例如在一個實際世界的完成須要以輸出輸入流的視角操作緩沖區。


        如斯一個環形緩沖器並沒有甚麼特殊的,在線程處置方面,也沒有特殊的屬性。它只是用來這裡用來供給數據緩沖的一個簡略數據構造。這裡曾經完成了該緩沖器,以確保拜訪該數據構造是線程平安的。

         

        /**
         * Synchronized ring buffer. 
         * Suitable to hand over data from one thread to another.
         **/
        public class RingBuffer {
          /** internal buffer to hold the data **/
          protected byte buffer[];
          /** size of the buffer **/
          protected int size;
          /** current start of data area **/
          protected int start;
          /** current end of data area **/
          protected int end;
         
          /**
           * Construct a RingBuffer with a default buffer size of 1k.
           */
          public RingBuffer() {
             this(1024);
          }
          /**
           * Construct a RingBuffer with a certain buffer size.
           * @param size  Buffer size in bytes
           */
          public RingBuffer(int size) {
             this.size = size;
             buffer = new byte[size];
             clear();
          }
          /**
           * Clear the buffer contents. All data still in the buffer is lost.
           */
          public void clear() {
            // Just reset the pointers. The remaining data fragments, if any,
            // will be overwritten during normal operation.
            start = end = 0;
          }
          /**
           * Return used space in buffer. This is the size of the
           * data currently in the buffer.
           * <p>
           * Note: While the value is correct upon returning, it
           * is not necessarily valid when data is read from the 
           * buffer or written to the buffer. Another thread might
           * have filled the buffer or emptied it in the mean time.
           *
           * @return currently amount of data available in buffer
           */
          public int data() {
             return start <= end
                   ? end - start
                   : end - start + size;
          }
          /**
           * Return unused space in buffer. Note: While the value is
           * correct upon returning, it is not necessarily valid when
           * data is written to the buffer or read from the buffer.
           * Another thread might have filled the buffer or emptied
           * it in the mean time.
           *
           * @return currently available free space
           */
          public int free() {
             return start <= end
                   ? size + start - end
                   : start - end;
          }
          /**
           * Write as much data as possible to the buffer.
           * @param data  Data to be written
           * @return    Amount of data actually written
           */
          int write(byte data[]) {
            return write(data, 0, data.length); 
          }
          /**
           * Write as much data as possible to the buffer.
           * @param data  Array holding data to be written
           * @param off  Offset of data in array
           * @param n   Amount of data to write, starting from .
           * @return    Amount of data actually written
           */
          int write(byte data[], int off, int n) {
            if(n <= 0) return 0;
            int remain = n;
            // @todo check if off is valid: 0= <= off < data.length; throw exception if not
            int i = Math.min(remain, (end < start ? start : buffer.length) - end);
            if(i > 0) {
               System.arraycopy(data, off, buffer, end, i);
               off  += i;
               remain -= i;
               end  += i;
            }
            i = Math.min(remain, end >= start ? start : 0);
            if(i > 0 ) {
               System.arraycopy(data, off, buffer, 0, i);
               remain -= i;
               end = i;
            }
            return n - remain;
          }
         
          /**
           * Read as much data as possible from the buffer.
           * @param data  Where to store the data
           * @return    Amount of data read
           */
          int read(byte data[]) {
            return read(data, 0, data.length); 
          }
          /**
           * Read as much data as possible from the buffer.
           * @param data  Where to store the read data
           * @param off  Offset of data in array
           * @param n   Amount of data to read
           * @return    Amount of data actually read
           */
          int read(byte data[], int off, int n) {
            if(n <= 0) return 0;
            int remain = n;
            // @todo check if off is valid: 0= <= off < data.length; throw exception if not
            int i = Math.min(remain, (end < start ? buffer.length : end) - start);
            if(i > 0) {
               System.arraycopy(buffer, start, data, off, i);
               off  += i;
               remain -= i;
               start += i;
               if(start >= buffer.length) start = 0;
            }
            i = Math.min(remain, end >= start ? 0 : end);
            if(i > 0 ) {
               System.arraycopy(buffer, 0, data, off, i);
               remain -= i;
               start = i;
            }
            return n - remain;
          }
        }
        

        經由過程應用該環形緩沖器,你如今可以以一種可控的方法從一個線程提交數據到另外一個線程。固然,其他線程平安、非壅塞式的辦法異樣可以。這裡的症結點在於當緩沖區已滿或許緩沖區為空時,數據的讀寫不會形成梗塞。


        依據在 "樹立一個串口事宜處置器"末節演示的事宜處置器的輪廓,你可使用在"一個簡略的,線程平安的環形緩沖區完成"末節中引見的同享環形緩沖區以支撐OUTPUT_BUFFER_EMPTY事宜。不是一切的JavaComm完成都支撐這個事宜,所以這段代碼能夠永久也不會被挪用。但假如可以,它是確保最好數據吞吐量的一部門,由於它可使串口不會長時光處於余暇狀況。

        事宜監聽器的輪廓須要供給一個outputBufferEmpty()辦法,它的完成以下:

          RingBuffer dataBuffer = ... ;
          /**
          * Handle output buffer empty events.
          * NOTE: The reception is of this event is optional and not
          *    guaranteed by the API specification.
          * @param event The output buffer empty event
          */
          protected void outputBufferEmpty(SerialPortEvent event) {
          }
        
        

        上面的示例假定數據的目標地是某個文件。當數據達到時它會被從串口中掏出並寫入目標文件。這只是個精簡化的視圖,由於現實上你須要檢討數據的EOF標識以將調制解調器(平日稱為“貓”)重置為敕令形式。

        import javax.comm.*;
        ...
        InputStream is = port.getInputStream();
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("out.dat"));
        /**
         * Listen to port events
         */ 
        class FileListener implements SerialPortEventListener {
        
          /**
           * Handle serial event.
           */
          void serialEvent(SerialPortEvent e) {
            SerialPort port = (SerialPort) e.getSource();
        
            //
            // Discriminate handling according to event type
            //
            switch(e.getEventType()) {
            case SerialPortEvent.DATA_AVAILABLE:
        
              //
              // Move all currently available data to the file
              //
              try {
                 int c;
                 while((c = is.read()) != -1) {
                    out.write(c);
                 }
              } catch(IOException ex) {
                 ...
              }
              break;
            case ...:
              ...
              break;
            ...
            }
            if (is != null) is.close();
            if (port != null) port.close();
          }
        
        


        調制解調器掌握

        JavaComm重要關懷的是一個串口的處置和串口上數據的傳送。它不懂或許供給對高層協定的支撐,好比Hayes調制解調指令平日用來掌握客戶級的貓。這不是JavaComm的義務,也就不是一個bug。

        好像其他特殊的串行裝備,假如願望由JavaComm掌握一個貓,那末就得在JavaComm上寫需要的代碼。頁面"Hayes-compatible Modems and AT Commands"供給了處置Hayes貓的需要的根本信息。


        一些操作體系,像Windows或某一Linux關於若何設置裝備擺設一個特殊類型或牌子的貓的掌握敕令供給了一個或多或少尺度的方法。例如,Windows貓的“驅動”平日只是注冊進口,描寫一個個體的貓(真實的驅動是一個通用的串行調制解調驅動)。JavaComm沒法獲得如許的操作體系的詳細的數據。是以,要末必需供給一個零丁的java對象來許可用戶為應用個體的貓去設置裝備擺設一個運用,要末就添加一些響應平台的(當地的)代碼。

        RxTx
        概述與版本

        因為Sun沒無為Linux供給JavaComm的參考完成,人們為java和linux開辟了RxTx。後來RxTx被移植到了其他平台。最新版本的RxTx已知可運轉在100種以上平台,包含Linux, Windows, Mac OS, Solaris 和其他操作體系。

        RxTx可以自力於JavaComm API應用,也能夠作為所謂的Java Comm API辦事者。假如采取後者還須要一個稱為JCL的封裝包。JCL和RxTx平日與Linux/Java刊行版打包在一路,或許JCL完整與代碼集成在一路。所以,在一個個地下載他們之前,看一看Linux刊行版的CD是值得的。


        因為Sun對JavaComm的無限的支撐和不恰當的文檔,廢棄JavaComm API,轉而直接應用RxTx而不是經由過程JCL封裝包仿佛成了一種趨向。但是RxTx的文檔是很稀疏的。特殊是RxTx開辟者愛好將他們的版本和包內容弄得一團糟(例如應用或未應用集成的JCL)。從1.5版本開端,RxTx包括了公共JavaComm類的替換類。因為司法緣由,他們沒有在java.comm包中,而是在gui.io包下。但是現存的兩個版本的打包內容有很年夜差異。

        •     RxTx 2.0
        •     這個版本的RxTx 重要用作JavaComm供給者。它應當源自於RxRx 1.4,這是RxTx添加gui.io包之前的版本。
        •     RxTx 2.1
        •     這個版本的RxTx包括了一個完全的取代java.comm的gnu.io包。它應當源自於RxTx 1.5,這是支撐gnu.io的肇端版本。


        是以,假如你想對原始的JavaComm API 編程的話你須要

                Sun JavaComm 通用版。撰寫本文時現實上就是Unix包(包括對各類類Unix體系的支撐,像Linux或Solaris)即便在Windows上,這個Unix包也是須要用來供給java.comm的通用完成的。只用用Java完成那部門會被用到,但是Unix的當地庫會被疏忽的。

            RxTx 2.0, 為了能在JavaComm通用版本下有分歧的供給者,分歧於JavaComm包下的誰人。但是,假如你只想用gnu.io調換包,那末你只須要將一個JavaComm運用轉換成RxTx運用。

        假如你是對Sun公司廢棄使JavaComm支撐Windows這一行動覺得掉望的浩瀚成員中的一個,那末你應當將你的JavaComm運用轉到RxTx下去。如你在下面所看到的,這裡有兩種方法來完成這件事,假定你曾經裝置了RxTx的某一版本,那末上面的選項可選其一:

        •     應用RxTx 2.0作為JavaComm接口的完成
        •     將運用移植到RxTx 2.1情況上

        下面的第一項在後面曾經說明,第二項也相當簡略。關於須要將JavaComm運用移植到RxTx 2.1下去的人,只須要將運用源代碼中一切對“java.comm”包的援用換成“gnu.io”包,假如原始的JavaComm運用編寫適當,這裡就沒有其他的工作須要去做。

        在Unix平台上,RxTx 2.1乃至供給了對象“contrib/ChangePackage.sh”去在源代碼樹形構造中履行全局的調換,如許的調換在其他的平台很輕易應用支撐重構功效的IDE(集成開辟情況)來完成。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved