1. 程式人生 > >Android Things物聯網開發--外圍I/O介面之UART(七)

Android Things物聯網開發--外圍I/O介面之UART(七)

UART(Universal Asynchronous Receiver Transmitter)


UART是通用非同步收發器(Universal Asynchronous Receiver Transmitter)的縮寫。複雜的外設比如GPS模組,LCD顯示器以及收音機通常使用UART埠來通訊。

UART是用於與一個裝置交換原始資料的通用介面。說它通用是因為它的資料傳輸速率和資料位元組格式都可以配置。說它非同步是因為沒有時鐘訊號用於同步兩個裝置之間的資料傳輸。裝置硬體會用先進先出(FIFO)緩衝區來收集所有傳入的資料集直到你的app讀取。

UART資料傳輸是全雙工的,這意味著資料可以在同一時刻被髮送或者接受。它顯然要比

I2C 快得多,但是缺少共享鐘意味著兩個裝置必須有統一的資料傳輸速率,這樣每個裝置可以以最小的時序誤差最為獨立的宿主機。
這裡寫圖片描述
UART外設通常有以下兩種:

  • 3線纜埠:包含資料接收(RX),資料發射(TX)以及地線(GND)
  • 5線纜埠:相比3線埠添加了請求傳送(RTS)和清除發射(CTS)訊號用於硬體流控制。流控制允許接收裝置去表明它的FIFO緩衝區暫時滿了,而此時發射裝置應該在傳送更多資料之前等待。

不像 SPI I2C ,UART僅僅支援兩個裝置間的點到點(point-to-point)之間的通訊。

管理連線

為了開啟一個到特定UART裝置的連線,你需要知道唯一的埠名。在開發的前期階段,後者移植一個app到一個新裝置時,使用PeripheralManagerService

getUartDeviceList() 方法獲取所用可用的裝置名是很有幫助的。

PeripheralManagerService manager = new PeripheralManagerService();
List<String> deviceList = manager.getUartDeviceList();
if (deviceList.isEmpty()) {
    Log.i(TAG, "No UART port available on this device.");
} else {
    Log.i(TAG, "List of available devices: "
+ deviceList); }

一旦你知道了目標名稱,使用 PeripheralManagerService 來連線到該裝置。當你完成與外設的通訊後,要斷開連線釋放資源。另外,你不能在同一個裝置連線關閉之前再次開啟一個新的連線。使用 close() 方法來關閉連線:

public class HomeActivity extends Activity {
    // UART Device Name
    private static final String UART_DEVICE_NAME = ...;

    private UartDevice mDevice;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Attempt to access the UART device
        try {
            PeripheralManagerService manager = new PeripheralManagerService();
            mDevice = manager.openUartDevice(UART_DEVICE_NAME);
        } catch (IOException e) {
            Log.w(TAG, "Unable to access UART device", e);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (mDevice != null) {
            try {
                mDevice.close();
                mDevice = null;
            } catch (IOException e) {
                Log.w(TAG, "Unable to close UART device", e);
            }
        }
    }
}

配置埠引數

當一個連線建立後,配置資料傳輸速率和幀格式來匹配連線上的外部裝置。

資料幀

每個通過UART傳送的字元都被一個數據幀包裹,資料幀包括以下組成部分:
這裡寫圖片描述

  • 開始位(Start Bit)–在傳送資料之前,該行保持1位持續時間的固定時間間隔,用來指示新字元的開始。
  • 資料位(Data Bits)–表示資料字元的各個位。UART會被配置傳送5到9位資料來表示一個字元。位數越少表示的資料的範圍越小,但是可以提高資料傳輸的效率。
  • 校驗位(Parity Bit)–可選錯誤檢查值。如果UART被配置為奇校驗或者偶校驗,額外的一位資料會被新增到幀中用於表明資料位的內容總和為偶數還是奇數值。設定它為none來從資料幀中移除這一位。
  • 停止位(Stop Bit)–在所有資料都傳輸完成之後,該行被重置為可配置的時間間隔以指示該字元的結尾。這可以配置為在1或2位持續時間內保持空閒。

注意:大多數UART裝置預設配置8位資料位,不需要檢驗,1位停止位(8N1)。

UART上的資料傳輸速率被稱為波特率(Baud rate)。它代表了接收和傳送的速度(單位為:位/秒)。

由於通過UART連線的兩個裝置之間沒有共享時鐘,所以必須提前配置,以便使用相同的波特率來正確的解碼資料。

通用的波特率包括9600,19200,38400,57600,115200和921600。這些速率包括資料幀(開始,停止,奇偶校驗為)的開銷,所以有效的資料傳輸速率將會略低並且根據你配置的幀位數量而有所不同。

下面的程式碼配置UART連線的波特率為115200,8位資料位,沒有校驗,1個停止位(8N1):

public void configureUartFrame(UartDevice uart) throws IOException {
    // Configure the UART port
    uart.setBaudrate(115200);
    uart.setDataSize(8);
    uart.setParity(UartDevice.PARITY_NONE);
    uart.setStopBits(1);
}

注意:選擇不通用的波特率在傳輸過程中可能造成較高的錯誤率。你應該總是確認你選擇的波特率是否是你的裝置硬體支援的。

硬體流控制(Hardware Flow Control)

如果你的裝置支援 5線纜 UART埠,啟用硬體流控制可以增加資料傳輸的可靠性。這也意味著你可以安全的使用高波特率傳輸,並且丟失資料的機會更少。

這裡寫圖片描述

當你激活了硬體流控制,當裝置上的接收緩衝區滿並且不再接收更多資料時UART會發送請求傳送(RTS)訊號。一旦緩衝區被清空,該訊號會被清除。同樣,UART會監聽清除傳送(CTS)訊號,and will pause transmitting data if it sees that line asserted by the peripheral device.(並且如果得知外圍裝置斷言的線路它會停止發射資料。【不知assert如何翻譯才好,求各位指教。】)

要啟用硬體流控制,使用 setHardwareFlowControl() 方法並且傳遞引數 HW_FLOW_CONTROL_AUTO_RTSCTS 。預設值是 HW_FLOW_CONTROL_NONE ,也就是說硬體流控制在預設情況下是不可用的:

public void setFlowControlEnabled(UartDevice uart, boolean enable) throws IOException {
    if (enable) {
        // Enable hardware flow control
        uart.setHardwareFlowControl(UartDevice.HW_FLOW_CONTROL_AUTO_RTSCTS);
    } else {
        // Disable flow control
        uart.setHardwareFlowControl(UartDevice.HW_FLOW_CONTROL_NONE);
    }
}

傳送輸出資料

要通過UART將資料緩衝區傳輸到外設,使用write() 方法:

public void writeUartData(UartDevice uart) throws IOException {
    byte[] buffer = {...};
    int count = uart.write(buffer, buffer.length);
    Log.d(TAG, "Wrote " + count + " bytes to peripheral");
}

注意:一個Java位元組佔8位,如果你使用setDataSize() 方法配置一個較小的資料,那麼每個位元組的高位會被截斷。

監聽輸入資料

使用read() 方法從UART的FIFO緩衝區中拉取資料到應用程式。這個方法接收一個空的緩衝用於填充輸入的資料以及一個最大能夠讀取的位元組數作為引數。UART讀取是非阻塞的,如果FIFO緩衝區中沒有可用資料,它將立即返回。

UartDevice 在讀取的時候將會返回FIFO緩衝區可用的資料位元組數。為了確保所有的資料都接收到,在UART上迴圈直到當前沒有更多可讀資料:

public void readUartBuffer(UartDevice uart) throws IOException {
    // Maximum amount of data to read at one time
    final int maxCount = ...;
    byte[] buffer = new byte[maxCount];

    int count;
    while ((count = uart.read(buffer, buffer.length)) > 0) {
        Log.d(TAG, "Read " + count + " bytes from peripheral");
    }
}

為了在緩衝區空時避免沒有必要的輪訓UART,使用 UartDevice 註冊一個UartDeviceCallback 。這個回撥在存在可讀資料時將會呼叫onUartDeviceDataAvailable() 方法。在你的應用程式不再監聽輸入資料時你應該取消回撥函式的註冊。

public class HomeActivity extends Activity {
    private UartDevice mDevice;
    ...

    @Override
    protected void onStart() {
        super.onStart();
        // Begin listening for interrupt events
        mDevice.registerUartDeviceCallback(mUartCallback);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Interrupt events no longer necessary
        mDevice.unregisterUartDeviceCallback(mUartCallback);
    }

    private UartDeviceCallback mUartCallback = new UartDeviceCallback() {
        @Override
        public boolean onUartDeviceDataAvailable(UartDevice uart) {
            // Read available data from the UART device
            try {
                readUartBuffer(uart);
            } catch (IOException e) {
                Log.w(TAG, "Unable to access UART device", e);
            }

            // Continue listening for more interrupts
            return true;
        }

        @Override
        public void onUartDeviceError(UartDevice uart, int error) {
            Log.w(TAG, uart + ": Error event " + error);
        }
    };
}

onUartDeviceDataAvailable() 方法返回一個boolean值用於表明回撥是否應該從接受未來的中斷事件中自動取消註冊。返回true表明在UART的FIFO緩衝區中每次出現數據時繼續接收事件。