Android Things SDK用法之SPI篇
1.SPI
串行外设接口(SPI)设备通常都使用在要求快速的数据传输速率场景上。SPI非常适合于高带宽使用情况,如外部非易失性存储器和图形显示。许多传感器设备除了支持I2C,也支持SPI。
SPI是一个同步串行接口,两个SPI设备间,依赖于共享时钟信号同步来进行数据传输。主设备控制时钟信号的触发,主控设备的SPI接口上所有其他连接的外设被称为从设备。每个设备连接到同一组数据信号以形成总线。
从理论上讲,SPI的数据传输速率仅受限于主控设备能以多快的速率切换时钟信号。时钟速率通常在16MHz到25MHz范围之间。相对于UART通信方式,这种高速共享时钟机制允许SPI外设传输数据更快和更少的错误率。
SPI支持全双工数据传输,这意味着主控设备和从设备可以同时交换信息。支持全双工传输,总线必须提供以下不同的信号,能进行SPI通信最少需要四根线接口:
- 主设备输出从设备输入(MOSI)
- 从设备输入主设备输出(MISO)
- 共享时钟信号(CLK)
- 地线(接地)
SPI支持从同一总线连接的多个从设备。与I2C不同,从设备使用硬件寻址。对于总线上多个从设备中的某个从设备作为数据传输的目标设备,允许主设备通过地址映射到特定设备上来说,一个外部的片选信号是必须的。如果总线上只有一个设备,这个信号是没有必要的。
1.2 设备连接管理
如果连接到指定的SPI从设备,需要知道总线上从从设备名称。在初始化阶段,或在应用程序移植到新的硬件,可以通过peripheralmanagerservice类的 getSpiBusList()方法发现设备上所有可用的
SPI从设备名称:
PeripheralManagerService manager
=
new
PeripheralManagerService();
List<String>
deviceList = manager.getSpiBusList();
if
(deviceList.isEmpty())
{
Log.i(TAG,
"No SPI bus available on this device.");
}
else
{
Log.i(TAG,
"List of available devices: "
+ deviceList);
}
SPI控制器将报告每个可用的从端口作为一个单独的设备名称。例如,一个开发版在相同的SPI总线上,如果支持CS0,CS1和CS2,通过调用getspibuslist()方法将返回的名称为“spi0.0”、“spi0.1”、和“spi0.2”。
一旦确定了目标端口的名字,使用peripheralmanagerservice来连接目标设备。当主设备与目标设备的完成通信后,需关闭连接来释放资源。此外,在现有连接关闭之前,是无法打开一个新的连接。通过调用close() 方法来关闭连接。
public
class
HomeActivity
extends
Activity
{
// SPI Device Name
private
static
final
String SPI_DEVICE_NAME
=
...;
private
SpiDevice mDevice;
@Override
protected
void onCreate(Bundle
savedInstanceState)
{
super.onCreate(savedInstanceState);
// Attempt to access the SPI device
try
{
PeripheralManagerService manager
=
new
PeripheralManagerService();
mDevice
= manager.openSpiDevice(SPI_DEVICE_NAME);
}
catch
(IOException
e)
{
Log.w(TAG,
"Unable to access SPI 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 SPI device",
e);
}
}
}
}
1.3 配置时钟和数据模式
在SPI总线上建立一个连接后,去配置数据传输速率和操作模式去匹配一个从设备。为了保证数据传输成功,总线上的所有的设备必须以一个相同的时钟和数据格式进行传输。
注意:一些从设备无法配置SPI操作模式,所以在选择外围设备时请参考硬件文档。
(1)设置SPI模式,它定义时钟信号的极性和相位。您选择的模式基于三个属性:
- 空闲电平:当没有数据传输时,时钟信号的电平(低电平或高电平)。
- 前沿:每个时钟脉冲的前沿。
- 后缘:在每个时钟脉冲的前沿相反的过渡。
支持以下模式被:
- MODE0 -时钟信号空闲为低电平,传输的数据在时钟的前沿
- MODE1-时钟信号空闲为低电平,数据传输在后时钟边缘
- MODE2-时钟信号空闲为高电平,传输的数据在时钟的前沿
- MODE3-时钟信号空闲为高电平,数据传输在后时钟边缘
(2)设置spidevice参数为:
- 频率- 指定共享时钟信号赫兹。不同设备硬件的时钟信号能力将有所不同。在设置此值之前,应验证指定设备的支持频率。
- 说明-指定每个字节的每个字节的顺序,因为它们是通过总线传输。这也被称为数据的字节序。默认情况下,数据将以最高有效位(MSB)首先进行发送。
- 字节位-配置在一次触发片选线之间传输的比特数给定的奴隶。默认值是每字节8bit位。
下面的代码配置模式0(MODE0),16MHz时钟,每个字8位,MSB高位首先传送:
public
void configureSpiDevice(SpiDevice
device)
throws
IOException
{
// Low clock, leading edge transfer
device.setMode(SpiDevice.MODE0);
device.setFrequency(16000000);
// 16MHz
device.setBitsPerWord(8);
// 8 BPW
device.setBitJustification(false);
// MSB first
}
1.4 数据传输
SPI既可支持半双工和全双工数据传输。大多数应用程序都应该使用半双工write()或read()方法与从设备的进行数据交换。
// Half-duplex data transfer
public
void sendCommand(SpiDevice
device,
byte[]
buffer)
throws
IOException
{
// Shift data out to slave
device.write(buffer,
buffer.length);
// Read the response
byte[]
response =
new
byte[32];
device.read(response,
response.length);
...
}
如果要使用全双工方式进行数据交换,得使用transfer()方法代替write()方法。此方法接受读写两个缓冲区。写入缓冲区包含要发送给从设备的数据,而读取缓冲区是空的,并接受来自从设备的数据。
数据长度必须小于或等于最小缓冲区的大小。与全双工传输的缓冲区大小相等是很普遍的。
// Full-duplex data transfer
public
void sendCommand(SpiDevice
device,
byte[]
buffer)
throws
IOException
{
byte[]
response =
new
byte[buffer.length];
device.transfer(buffer,
response, buffer.length);
...
}