Android Things 外设I/O-UART

写在前面的话:由于外设I/O涉及到GPIO、PWM、和串行通信三部分,而串行通信有讲了I2C(IIC)、SPI、UART,这样导致本文的篇幅过长不便于阅读,特此将本文分成几部分来方便阅读

  1. Android Things 外设I/O-GPIO
  2. Android Things 外设I/O-PWM
  3. Android Things 外设I/O-I2C(IIC)
  4. Android Things 外设I/O-SPI
  5. Android Things 外设I/O-UART

串行通信

使用这些API在连接在同一本地总线上的两个或多个智能设备之间传输更大的数据有效负载。 下表概述了每个支持的串行协议的基本属性:

协议 传输类型 电线数量 外围设备数量 传输速度
I2C 同步 2 Up to 127 Low
SPI 同步 4+ Unlimited High
UART 异步 2 or 4 1 Medium

UART

复杂的外围设备,如GPS模块,LCD显示器和XBee无线电通常使用通用异步收发器(UART)端口(通常简称为串行端口)进行通信。
  UART是用于与外围设备交换原始数据的通用接口。 它是通用的,因为数据传输速度和数据字节格式都是可配置的。 它是异步的,因为不存在用于同步两个设备之间的数据传输的时钟信号。 设备硬件以先进先出(FIFO)缓冲区收集所有传入数据,直到应用程序读取为止。
  UART数据传输是全双工的,意味着数据可以同时发送和接收。 它通常比I2C快,但缺乏共享时钟意味着两个设备必须达成一个共同的数据传输速率,每个设备可以独立遵守与最小的定时误差。

UART连接

UART外设通常有两种类型:

  • 3线端口包括数据接收(RX),数据发送(TX)和接地参考(GND)信号。
  • 5线端口添加用于硬件流控制的请求发送(RTS)和清除发送(CTS)信号。 流控制允许接收设备指示其FIFO缓冲器临时满,并且发送设备在发送任何更多数据之前应该等待。

与SPI和I2C不同,UART仅支持两个器件之间的点对点通信。

管理连接

为了打开到特定UART的连接,您需要知道唯一的端口名称。 在开发的初始阶段或将应用程序移植到新硬件时,可以通过 getUartDeviceList()从PeripheralManagerService找到所有可用的设备名称:

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设备名称
    private static final String UART_DEVICE_NAME = ...;

    private UartDevice mDevice;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 尝试访问UART设备
        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发送的每个字符都包裹在一个数据帧中,该数据帧包含以下组件:

UART框架
  • 启动位 - 在发送数据之前,该行保持活动状态,持续1位持续时间的固定时间间隔,以指示新字符的开始。
  • 数据位 - 表示数据字符的各位。 UART可以配置为在5-9个数据位之间发送以表示字符。 较少的位减少了数据的范围,但可以增加有效的数据传输速率。
  • 奇偶校验位 - 可选错误校验值。 如果UART配置为偶校验或奇校验,则会在帧中添加一个额外的位,以指示数据位的内容是否为偶数或奇数值。 将此设置为none会从帧中删除该位。
  • 停止位 - 在传输所有数据之后,线路被重置一个可配置的时间间隔,以指示该字符的结束。 这可以配置为保持空闲1或2位持续时间。

注:大多数UART器件的默认配置为8个数据位,无奇偶校验和1个停止位(8N1)。

UART上的数据传输速率称为波特率。 它表示接收和发送的速度(以位/秒为单位)。 由于在通过UART连接的两个器件之间没有共享时钟,因此必须提前配置这两个器件,才能使用相同的波特率正确解码数据。
  常用波特率包括9600,19200,38400,57600,115200和921600.该速率包括数据帧(开始,停止和奇偶校验位)的开销,因此有效数据传输速率将略低,并且根据 您配置的帧位数。
  以下代码将UART连接配置为以115200波特,8个数据位,无奇偶校验和1个停止位(8N1)操作:

public void configureUartFrame(UartDevice uart) throws IOException {
    // 配置UART端口
    uart.setBaudrate(115200);
    uart.setDataSize(8);
    uart.setParity(UartDevice.PARITY_NONE);
    uart.setStopBits(1);
}

注意:选择不常见的波特率可能导致数据传输中的高错误率。 您应该始终验证您选择的波特率是否得到设备硬件的良好支持。

硬件流控制

如果您的设备支持5线UART端口,则启用硬件流控制可以提高数据传输的可靠性。 通常这也意味着您可以安全地使用更快的波特率,而丢失传入数据的机会更低。

UART流控制

在硬件流控制使能的情况下,当器件上的接收缓冲器已满并且不能接受任何更多数据时,UART将发送请求发送(RTS)信号。 一旦缓冲液被排空,信号将被清除。 类似地,UART监视清除发送(CTS)信号,并且如果它看到由外围设备断言的线,则将暂停发送数据。
  要启用硬件流控制,请使用带有HW_FLOW_CONTROL_AUTO_RTSCTS的setHardwareFlowControl()方法。 默认值为HW_FLOW_CONTROL_NONE,表示禁用流控制。

public void setFlowControlEnabled(UartDevice uart, boolean enable) throws IOException {
    if (enable) {
        // 启用硬件流控制
        uart.setHardwareFlowControl(UartDevice.HW_FLOW_CONTROL_AUTO_RTSCTS);
    } else {
        // 禁用流量控制
        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 {
    // 一次读取的最大数据量
    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();
        // 开始监听中断事件
        mDevice.registerUartDeviceCallback(mUartCallback);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // 中断事件不再需要
        mDevice.unregisterUartDeviceCallback(mUartCallback);
    }

    private UartDeviceCallback mUartCallback = new UartDeviceCallback() {
        @Override
        public boolean onUartDeviceDataAvailable(UartDevice uart) {
            // 从UART设备读取可用数据
            try {
                readUartBuffer(uart);
            } catch (IOException e) {
                Log.w(TAG, "Unable to access UART device", e);
            }

            // 继续侦听更多中断
            return true;
        }

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

onUartDeviceDataAvailable()回调返回一个布尔值,指示回调是否应该在接收未来中断事件时自动取消注册。 此处返回true,以便在每次UART FIFO中显示数据时继续接收事件。


参考文献 https://developer.android.google.cn/things/sdk

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容