Android Things:外设I/O接口-SPI

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/p106786860/article/details/71076450

一、接口简介


串行外围接口(Serial Peripheral Interface)设备通常需要快速的数据传输速率。SPI适合高带宽使用情况,如外部非易失性存储器和图形显示,许多传感器除了I2C也支持SPI。

SPI总线是一种同步的串行接口:这意味着它依赖于共享的时钟信号来同步设备之间的数据传输。控制时钟信号的设备被称为master。其它所有连接的外设被认为是Slaves。每个设备连接到同一组数据信号以形成总线。从理论上讲,SPI数据传输率是仅限于master切换时钟信号的快慢。时钟速度通常在16MHz到25MHz范围。高速共享时钟允许SPI外设更快的传输数据,比UART错误更少。

SPI支持全双工数据传输:意味着master和slave可以同时交换数据。为了支持全双工传输,总线必须提供下列单独的信号,使得SPI最少四线接口:

  • Master出Slave入(MOSI);
  • Mater入Slave出(MISO);
  • 共享时钟信号(CLK);
  • 共同的接地参考(GND);

SPI支持同一总线连接多个从设备:和I2C不同,slave设备使用硬件寻址。每个slave都需要外部芯片选择信号,来让master定位特定的设备作为数据传输的目标。如果仅仅使用一个slave这个信号就不必须。
这里写图片描述

二、接口使用


  1. 打开连接
    创建PeripheralManagerService对象,使用你想打开端口的名称,调用open()方法打开连接。

    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);
                }
            }
        }
    }
  2. 配置时钟和数据模式
    在一个SPI总线连接建立之后,配置数据传输速率和操作模式来匹配同一条总线上的Slave设备。为了成功的传输数据,在总线的所有设备必须具有相同的时钟和数据格式行为。

    设置SPI模式,定义了时钟信号的极性和相位。你选择的属性基于三个属性:
    这里写图片描述

    • 闲置级别:当没有数据传输的时候,时钟信号的级别(或高或低);
    • 前沿:每个时钟脉冲的前沿;
    • 后沿:每个时钟脉冲前沿相反过渡;

    支持以下模式:

    • MODE0-时钟信号闲置为低,数据传输在前时钟边沿;
    • MODE1-时钟信号闲置为高,数据传输在后时钟边沿;
    • MODE2-时钟信号闲置为低,数据传输在前时钟边沿;
    • MODE3-时钟信号闲置为高,数据传输在后时钟边沿;

    设置如下SpiDevice参数:

    • 频率:以Hz为单位指定共享时钟信号。时钟信号的能力在不同的设备之间有所不同。在设置这个值之前,你应该确认下你设备支持的频率。
    • xxx:指定在总线上传输的每个字节中比特的顺序,这也被称为数据的字节序。默认情况下,数据将会把最高有效位(MSB)首先发送。
    • 每个字的比特:配置一次传输的比特数,…,默认的值是8比特每字。
    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
    }
  3. 传输数据

    • SPI支持半双工和全双工数据传输。
    • 大多数应用程序应该都使用半双工write()或read()方法与Slave设备交换数据。
    // 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()方法。这个而方法接受读和写两个缓冲。写缓冲包含发送给Slave的数据,然而读缓冲是空的来接受Slave的数据。数据的长度必须小于或者等于数据缓冲的最小缓冲的大小。常见的全双工传输缓冲区大小是相等的。
    // 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);
        ...
    }
  4. 关闭连接
    当你完成和外部设备的通信,调用close()方法关闭连接并释放资源。此外在现有端口关闭之前,你不能打开相同端口的新连接。

    public class HomeActivity extends Activity {
        // SPI Device Name
        private static final String SPI_DEVICE_NAME = ...;
        private SpiDevice mDevice;
    
        @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);
                }
            }
        }
    }

三、案例演示


下面我们就通过spi接口,控制Max7219模块的显示来演示接口的使用。

  1. 硬件准备
    • 树莓派开发板 1块
    • 面包板 1块
    • Max7219点阵模块
    • 杜邦线(公对母)若干
  2. 电路搭建(注意,没有找个对应的元器件图,下图多一个引脚)
    这里写图片描述
  3. 代码编写
    SpiDemo\app\src\main\java\com\chengxiang\spidemo\MainActivity.java

    public class MainActivity extends AppCompatActivity {
        private static final String SPI_DEVICE_NAME = "SPI0.0";
    
        private static final byte OP_NOOP = 0;
        private static final byte OP_DIGIT0 = 1;
        private static final byte OP_DIGIT1 = 2;
        private static final byte OP_DIGIT2 = 3;
        private static final byte OP_DIGIT3 = 4;
        private static final byte OP_DIGIT4 = 5;
        private static final byte OP_DIGIT5 = 6;
        private static final byte OP_DIGIT6 = 7;
        private static final byte OP_DIGIT7 = 8;
        private static final byte OP_DECODEMODE = 9;
        private static final byte OP_INTENSITY = 10;
        private static final byte OP_SCANLIMIT = 11;
        private static final byte OP_SHUTDOWN = 12;
        private static final byte OP_DISPLAYTEST = 15;
    
        public static final byte[] ALIEN_FRAME_1 = new byte[]{
                (byte)0b00001000,
                (byte)0b00011000,
                (byte)0b00001000,
                (byte)0b00001000,
                (byte)0b00001000,
                (byte)0b00001000,
                (byte)0b00001000,
                (byte)0b00000000
        };
    
        public static final byte[] ALIEN_FRAME_2 = new byte[]{
                (byte)0b00011000,
                (byte)0b00100100,
                (byte)0b00100100,
                (byte)0b00000100,
                (byte)0b00001000,
                (byte)0b00010000,
                (byte)0b00111110,
                (byte)0b00000000
        };
        public static final byte[] ALIEN_FRAME_3 = new byte[]{
                (byte)0b00011000,
                (byte)0b00100100,
                (byte)0b00000100,
                (byte)0b00011000,
                (byte)0b00000100,
                (byte)0b00100100,
                (byte)0b00011000,
                (byte)0b00000000
        };
    
        public static final byte[][] FRAMES = new byte[][]{ALIEN_FRAME_1, ALIEN_FRAME_2, ALIEN_FRAME_3};
        private SpiDevice mDevice;
        private byte[] spidata = new byte[2];
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            try {
                //打开SPI接口连接
                PeripheralManagerService manager = new PeripheralManagerService();
                mDevice = manager.openSpiDevice(SPI_DEVICE_NAME);
    
                //设置SPI信号模式、频率、每个字比特数等
                mDevice.setMode(SpiDevice.MODE0);
                mDevice.setFrequency(1000000);
                mDevice.setBitsPerWord(8);
                mDevice.setBitJustification(false);
    
                //设置译码模式,0-用于驱动LED点阵屏
                spiTransfer(OP_DECODEMODE, 0);
                //设置扫描限制,显示7行
                spiTransfer(OP_SCANLIMIT, 7);
                //设置显示器检测,0-一般模式
                spiTransfer(OP_DISPLAYTEST, 0);
                //设置停机为false
                spiTransfer(OP_SHUTDOWN, 1);
                //设置显示强度为3
                spiTransfer(OP_INTENSITY, 15);
    
                while (true) {
                    for (int i = 0; i < FRAMES.length; i++) {
                        for (int j = 0; j < FRAMES[i].length; j++) {
                            spiTransfer((byte) (OP_DIGIT0 + j), FRAMES[i][j]);
                        }
                        Thread.sleep(500);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mDevice != null) {
                try {
                    mDevice.close();
                    mDevice = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private void spiTransfer(byte opcode, int data) throws IOException {
            spidata[0] = opcode;
            spidata[1] = (byte) data;
            mDevice.write(spidata, 2);
        }
    }

    代码库:https://github.com/ThingsDeveloper/spidemo

  4. 运行结果
    运行程序,显示器上闪烁显示1,2,3数字如下:
    这里写图片描述
    这里写图片描述

1.抛弃各种找元器件的烦恼,来“1024工场”旗舰店,一次性买到你所想要的:树莓派套装—专为Android Things打造。
树莓派套装
电脑用户,点击如下链接进入淘宝宝贝页面:
https://item.taobao.com/item.htm?spm=686.1000925.0.0.3f11c9ed68fPu7&id=549263158263
手机用户,打开淘宝客户端扫描二维码:
宝贝二维码
2.完整和持续更新的《使用Android打开物联网开发大门——Andoid Thigns开发》文档,欢迎大家阅读!
https://www.kancloud.cn/workshop1024/android_things_develop/360773
这里写图片描述
3.新技术,新未来!欢迎大家关注“1024工场”微信服务号,时刻关注我们的最新的技术讯息。(甭客气!尽情的扫描或者长按!)
服务号
4.加入“Android Things开发”QQ讨论群,一起学习一起Hi。(甭客气!尽情的扫描或者长按!)
qq群

猜你喜欢

转载自blog.csdn.net/p106786860/article/details/71076450