AndroidThings系列之五:I2C通信
其实AndroidThings的API已将底层的细节大大抽象了,正如Arduino简化了底层细节一样,这样一来将大大提高生产效率。本例的复杂之处在LCD的具体操作上,所以除了掌握AndroidThings基础知识,要想在Maker领域中取得更好的效果,学会看数据手册也算是基本功之一,如果实在啃不动数据手册,也可以参考别人的代码来改,不过适用性就差了一点点。
本文将介绍如何通过I2C通信来实现屏幕点亮和显示。
先给小白讲下I2C,大神直接略过就好。
I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。 主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件.在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件.然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下.主机负责产生定时时钟和终止数据传送。
I2C器件使用3线接口连接,包括:
• 共享时钟信号(SCL)
• 共享数据线(SDA)
• 公共地参考(GND)
I2C支持沿同一总线连接的多个从设备。与SPI不同,从设备使用I2C软件协议进行寻址。每个设备都使用唯一的地址进行编程,并仅响应主设备发送到该地址的传输。即使总线仅包含一个从站,每个从站设备也必须具有一个地址。
i.MX7D的I2C接口如下
要在AndroidThings中使用I2C设备,需要启用如下权限
<uses-permission android:name="com.google.android.things.permission.USE_PERIPHERAL_IO" />
正如上一节介绍的一样,如果想要查看I2C接口列表,可以使用PeripheralManager来实现,下面是具体的代码及输出
Log.d(TAG, "I2C: " + PeripheralManager.getInstance().getI2cBusList());
在LogCat窗口会看到如下的输出:
/com.example.netlh.lcd1602 D/AndroidThings: I2C: [I2C1, I2C2]
实现I2C通信需要I2C设备,这里我们使用的是Grove RGB LCD显示屏,这是一个1602显示屏,支持RGB三色背光。该屏包括两个I2C设备,一个地址为0x62(RGB地址),另一个为0x3e(LCD地址)。
我们知道在Linux下,可以使用 i2c-tools来方便的调试I2C设备,该工具包括i2cdetect,i2cdump等常用命令,可以方便的检测并操作i2c设备。
使用i2c-tools源码,可以编译出android可用的版本,具体编译方法请自行参考。
利用adb命令将编译后的工具上传到i.MX7D,下面是在i.MX7D上的用法
adb root
adb push i2cdetect /data
...
adb shell
imx7d_pico:/ #
总而言之,就是将i2cdetect等工具上传到开发板,然后其它的附加操作例如添加执行权限等都省略不提,接下来使用i2cdetect来检测下设备是否在线
127|imx7d_pico:/ # /data/i2cdetect -l
i2c-3 i2c 30a50000.i2c I2C adapter
i2c-1 i2c 30a30000.i2c I2C adapter
i2c-0 i2c 30a20000.i2c I2C adapter
imx7d_pico:/ # /data/i2cdetect -y 0
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: 03 -- -- -- -- -- -- UU -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 3e --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- 62 -- -- -- -- -- -- -- -- -- -- -- -- --
70: 70 -- -- -- -- -- -- --
imx7d_pico:/ #
设备地址已检测,接下来就开始写代码了。
LCD的数据手册请参考官方给出的文档,这里我们专门定义了一个类Lcd1602,代码内容如下
package com.example.netlh.lcd1602;
import com.google.android.things.pio.I2cDevice;
import com.google.android.things.pio.PeripheralManager;
import java.io.IOException;
public class Lcd1602 {
private final static int LCD_I2C_ADDRESS = 0x3E;
private final static int RGB_I2C_ADDRESS = 0x62;
public final static String DEBUG_TAG = "GROVE LCD RGB";
private final static String I2C_NAME = "I2C1";
private I2cDevice mLcdDevice, mRgbDevice;
public void setColor(int r, int g, int b) throws IOException {
mRgbDevice.writeRegByte(0, (byte)1);
mRgbDevice.writeRegByte(1, (byte) 0);
mRgbDevice.writeRegByte(0x08, (byte) 0xaa);
mRgbDevice.writeRegByte(4, (byte) r);
mRgbDevice.writeRegByte(3, (byte) g);
mRgbDevice.writeRegByte(2, (byte) b);
}
private void textCommand(int cmd) throws IOException{
mLcdDevice.writeRegByte(0x80, (byte)cmd);
}
private void setPosition(int row, int col) throws IOException {
if(row == 0) {
textCommand(0x80 + col);
}
if(row == 1) {
textCommand(0xC0 + col);
}
}
public void setText(int row, int col, byte[] msg) throws IOException {
//textCommand(0x01);
textCommand(0x08 | 0x04); //display on, no cursor
textCommand(0x28); //two lines
setPosition(row, col);
for(int i = 0; i < msg.length; i ++) {
mLcdDevice.writeRegByte(0x40, msg[i]);
}
}
public void clear() throws IOException {
textCommand(0x01); //clear screen
}
public void setup() {
PeripheralManager peripheralManager = PeripheralManager.getInstance();
try {
mLcdDevice = peripheralManager.openI2cDevice(I2C_NAME, LCD_I2C_ADDRESS);
mRgbDevice = peripheralManager.openI2cDevice(I2C_NAME, RGB_I2C_ADDRESS);
} catch (IOException e) {
e.printStackTrace();
}
}
public void close(){
if(mLcdDevice != null) {
try {
mLcdDevice.close();
mLcdDevice = null;
} catch (IOException e) {
e.printStackTrace();
}
}
if(mRgbDevice != null) {
try {
mRgbDevice.close();
mRgbDevice = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
该类的内容比较简单,只提供一些简单的操作功能,如设备背景,显示字串内容,以及关闭设备等操作。这里包括的大部分命令项比如0x01, 0x04需要查询LCD的数据手册。
主要的功能实现如下
public class MainActivity extends Activity {
private static final String TAG = "AndroidThings";
private Lcd1602 mLcd1602;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "I2C: " + PeripheralManager.getInstance().getI2cBusList());
mLcd1602 = new Lcd1602();
mLcd1602.setup();
//byte[] msg = "For more complex peripherals, look for an existing user-space driver".getBytes();
Log.d(Lcd1602.DEBUG_TAG, "onCreate: " + PeripheralManager.getInstance().getUartDeviceList());
try {
mLcd1602.clear();
mLcd1602.setColor(33, 28, 28);
mLcd1602.setText(0, 0, "ABCDEFGHIJKLMNOP".getBytes());
mLcd1602.setText(1, 1, "TEMP: 11 C".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(Lcd1602.DEBUG_TAG, "onDestroy: ");
if(mLcd1602 != null) {
mLcd1602.close();
mLcd1602 = null;
}
}
}
整体代码框架和led工程的差不多,首先在onCreate里面初始化设备,设置RGB的背光色,然后显示一些简单的文字信息。
运行的结果,倒也不是很复杂。
其实AndroidThings的API已将底层的细节大大抽象了,正如Arduino简化了底层细节一样,这样一来将大大提高生产效率。本例的复杂之处在LCD的具体操作上,所以除了掌握AndroidThings基础知识,要想在Maker领域中取得更好的效果,学会看数据手册也算是基本功之一,如果实在啃不动数据手册,也可以参考别人的代码来改,不过适用性就差了一点点。
下一节来个PWM式点灯。