Android Things(二)-外部I/O接口协议

目录

  • 概述
  • GPIO: 简单0/1状态协议
  • PWM:方波信号接口
  • I2C:低速同步串行接口
  • UART:异步串行接口

1.概述

Android Things 提供了几种对外I/O接口协议来连接各种外部设备。通过这些协议Android Things和外部设备就能互相交流。Android Things像一个躯干、一个核心,我们通过这些协议给他安装各种肢体和外延功能。这里介绍4种Android Things支持的协议:GPIO是一个最简单的协议,只能读写高/低两种电平信号;PWM只能简单的对外发出方波信号;I2C、UART是串行接口协议,能连续的读写大量数据,用于比较复杂的设备。下面具体介绍。

2.GPIO

参考官方文档

GPIO可用一句话概括:每次只可以读或写一个高电平或低电平信号。一般用于简单的外部设备,比如开关、LED灯。
要获得一个GPIO端口需要知道端口的唯一端口名,PeripheralManagerService 的getGpioList()方法可以获取所有当前的GPIO端口名,有了这个唯一名称就可以获取这个端口:

PeripheralManagerService manager = new PeripheralManagerService();
List<String> portList = manager.getGpioList();
if (portList.isEmpty()) {
    Log.i(TAG, "No GPIO port available on this device.");
} else {
    Log.i(TAG, "List of available ports: " + portList);
}

...

Gpio mGpio = manager.openGpio(GPIO_NAME);

获得Gpio对象后,就可以操作这个端口:包括1. 设置读写方向,即设置此端口的功能是读还是写;2. 设置读写类型,即指定Gpio对像读/写的值代表高电平还是低电平,下面是读的例子:

// 设置方向:DIRECTION_IN为读,DIRECTION_OUT为写
    mGpio.setDirection(Gpio.DIRECTION_IN);
// 设置类型:指定getValue()==true或setValue(true)的意义,ACTIVE_HIGH即true代表高电平,ACTIVE_LOW即true代表低电平
    mGpio.setActiveType(Gpio.ACTIVE_HIGH);

    ...

    if (mGpio.getValue()) {
        //读到了高电平
    } else {
        // 读到了低电平
    }

为了能够监听电平高低的变化,我们可以设置监听回调:

public void configureInput(Gpio gpio) throws IOException {
   //设置监听类型:EDGE_NONE:不回调;EDGE_RISING:低->高回调;EDGE_FALLING:高->低回调;EDGE_BOTH:高低变化都回调
    gpio.setEdgeTriggerType(Gpio.EDGE_BOTH);
    gpio.registerGpioCallback(mGpioCallback);
}
private GpioCallback mGpioCallback = new GpioCallback() {
    @Override
    public boolean onGpioEdge(Gpio gpio) {
        //根据读类型读取状态
        if(gpio.getValue()){...}
           
        // 返回true则继续监听,false则不再监听
        return true;
    }

    @Override
    public void onGpioError(Gpio gpio, int error) {
        Log.w(TAG, gpio + ": Error event " + error);
    }
};

3.PWM

参考官方文档

PWM是用来发出方波控制信号的,而且只能发出方波信号,不能读取。先看一下方波信号,如下图,高低电平周期出现:

pwm-signal

这个波形可以通过设置周期和占空比进行调节,占空比即每个周期高电平占的比例。

获取一个PWM端口也是通过唯一名称的,依然是PeripheralManagerService 的 getPwmList()方法:

PeripheralManagerService manager = new PeripheralManagerService();
List<String> portList = manager.getPwmList();
if (portList.isEmpty()) {
    Log.i(TAG, "No PWM port available on this device.");
} else {
    Log.i(TAG, "List of available ports: " + portList);
}

...

Pwm mPwm = mPeripheralManager.openPwm(PWM_NAME);

//设置频率
mPwm .setPwmFrequencyHz(120);
//设置占空比
mPwm .setPwmDutyCycle(25);
 //设置PWM有效
mPwm .setEnabled(true);

4. I2C

参考官方文档

I2C是同步串行接口,使用共享同步时钟同步数据,适合数据量较小的外部设备。
I2C接口有三根线,分别是

  1. SCL:同步时钟信号线
  2. SDA:数据传输线
  3. GND:地线

由于I2C只有一根数据线,所以只能是半双工的。

I2C可以同时接入多个设备,如图:


i2c-connections

连接的每个设备都对应一个唯一地址。

和上面两个协议相同,I2C也可以通过PeripheralManagerService 的getI2CBusList()获取I2C端口列表,由于一个I2C端口链接多个设备,所以还需要一个地址来定位某个设备。获取设备的代码如下:

PeripheralManagerService manager = new PeripheralManagerService();
I2cDevice mDevice = manager.openI2cDevice(I2C_DEVICE_NAME, I2C_ADDRESS);

I2C协议可以读写设备的寄存器,使用下图的数据帧格式进行读写,其中前面两个地址定位哪个设备的哪个寄存器,后面的一个地址一个数据代表要在这个设备的这个寄存器读写的内容。

i2c-smbus

具体函数如下:

  • 字节数据:readRegByte()和writeRegByte()来读或者写一个单独的8位寄存器数据。

  • 字数据:readRegWord()和writeRegWord()以一个16位的字来读或者写两个连续寄存器的值。第一个寄存器的地址被翻译为字中的最小有效字节(LSB),其次是最重要的字节(MSB)。

  • 块数据:readRegBuffer()和writeRegBuffer()读或者写最多32个连续寄存器的值作为一个数组。

I2C还支持从数据线读写原始数据,数据帧格式如下:

i2c-raw

需要注意的是:打开一个设备的连接后,不可以同时再打开另一个,需要先关闭一个才能连接另一个。关闭调用I2cDevice 的 close()方法.

5. UART

参考官方文档

UART一般用作和外部设备交换原始数据,它和其他的几个协议不同之处在于数据传输速度和数据格式都可以自定义,而且它是异步传输数据的,是没有同步时钟信号的,设备会收集所有进来的数据到一个先进先出的缓存里,直到你的应用来读取。

UART是全双工的,读数据和写数据各用一根线。由于读和写可以同时进行,一般它比I2C要快,但是需要两边的设备都遵循一个传输速率以防止数据错误,而且只能连接一个设备。如下是设备连接图:

uart-connections

打开一个UART端口也是同样需要知道唯一的端口名称,类似的也是PeripheralManagerService的getUartDeviceList()方法可以获得UART端口列表,然后openUartDevice(UART_DEVICE_NAME)即可获得UartDevice类型的对象。

UART传输的数据帧格式如下图:

uart-frame

Start位:发送数据前,数据线被拉起 1 bit的固定的时间间隔来指明真正要发送的数据的开始。
Data部分:要传输的数据,可以传5-9 bit数据,数据位少,传输的数据就少,但是可以提高速率。
Parity位:校验位,如果UART设置奇偶校验,那数据帧就会加上这一位,当然也可以不设置,则没有这一位。
Stop位:所有数据传输完毕后,数据线会被重置一段时间表明数据传输结束,这段时间可以是1-2bit的时间。

默认的数据帧一般是1bit start、8bit数据和1bit stop位,没有校验位。

UART的传输速率称为波特率,单位是 bit/秒,接收端和发送端必须使用相同的波特率。

代码中实际使用时一般如下:

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);     //设置stop位大小
}

UART有一种五根线的设备,如下图:

uart-flow-control

多出的两根线能保证较高速度传输的情况下更少的传输失败,可以称为FlowControl功能。

可以使用如下代码开启或关闭着两根线:

public void setFlowControlEnabled(UartDevice uart, boolean enable) throws IOException {
    if (enable) {
        // 打开 FlowControl功能
        uart.setHardwareFlowControl(UartDevice.HW_FLOW_CONTROL_AUTO_RTSCTS);
    } else {
        // 取消FlowControl功能
        uart.setHardwareFlowControl(UartDevice.HW_FLOW_CONTROL_NONE);
    }
}

UART的读写功能也比较简单如下代码:

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

//读取数据一般在回调里监听
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) {
            // 开始读取
            try {
                readUartBuffer(uart);
            } catch (IOException e) {
                Log.w(TAG, "Unable to access UART device", e);
            }

            // 返回true则继续监听,false则不再继续监听
            return true;
        }

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

到这里这几种协议就介绍完了,利用这几种协议的特性并搭配好的想法和电路,就可以创造出各种有趣或实用的功能。

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

推荐阅读更多精彩内容