Android之SPI

在Android中,SPI(Serial Peripheral Interface)是一种重要的串行通信协议,广泛应用于嵌入式系统和硬件控制。以下是详细介绍:

📡 SPI协议基础

SPI协议简介

SPI (Serial Peripheral Interface) 是由摩托罗拉公司开发的全双工同步串行通信协议,用于短距离通信。

主要特点:
  • 🔄 全双工通信:可以同时发送和接收数据
  • 高速传输:支持高达几十MHz的时钟频率
  • 🏗️ 主从架构:一个主设备控制多个从设备
  • 📍 简单协议:没有复杂的寻址机制

SPI信号线:

SCLK (Serial Clock)     - 时钟信号线
MOSI (Master Out Slave In) - 主设备输出,从设备输入
MISO (Master In Slave Out) - 主设备输入,从设备输出
SS/CS (Slave Select/Chip Select) - 片选信号线

🔧 Android中SPI的实现层次

1. 硬件抽象层(HAL)

// hardware/interfaces/spi/1.0/ISpi.hal
interface ISpi {
    open(string device) generates (int32_t fd);
    transfer(int32_t fd, vec<uint8_t> txData) generates (vec<uint8_t> rxData);
    setMode(int32_t fd, uint32_t mode);
    setSpeed(int32_t fd, uint32_t speed);
    close(int32_t fd);
};

2. 内核驱动层

// Linux SPI子系统
/dev/spidev0.0, /dev/spidev0.1, /dev/spidev1.0...  // SPI设备节点

// 内核中的SPI控制器
struct spi_controller {
    struct device *dev;
    s16 bus_num;
    u16 num_chipselect;
    u16 dma_alignment;
    // ...
};

3. 应用层接口

// Android Framework层
public class SpiManager {
    public SpiDevice openSpiDevice(String deviceName, int chipSelect);
    public void closeSpiDevice(SpiDevice device);
}

📱 Android中SPI的应用场景

1. 传感器通信

// 加速度计、陀螺仪、压力传感器
public class SensorSpiManager {
    private static final String SPI_DEVICE = "/dev/spidev0.0";
    private static final int ACCELEROMETER_CS = 0;
    
    public void readAccelerometerData() {
        // 通过SPI读取加速度计数据
    }
}

2. 显示屏控制

// TFT LCD、OLED显示屏控制
public class DisplaySpiController {
    private static final String SPI_DEVICE = "/dev/spidev1.0";
    
    public void sendDisplayCommand(byte[] command) {
        // 通过SPI发送显示命令
    }
    
    public void sendDisplayData(byte[] data) {
        // 通过SPI发送显示数据
    }
}

3. 存储设备

// SPI Flash、SD卡等存储设备
public class StorageSpiManager {
    private static final String SPI_DEVICE = "/dev/spidev2.0";
    
    public void writeFlash(int address, byte[] data) {
        // 通过SPI写入Flash存储
    }
    
    public byte[] readFlash(int address, int length) {
        // 通过SPI读取Flash数据
        return null;
    }
}

🛠️ Android SPI编程实现

方法一:使用Android Things API(已废弃)

public class SpiDeviceManager {
    private static final String SPI_DEVICE_NAME = "SPI0.0";
    private static final int SPI_FREQUENCY = 1000000; // 1MHz
    
    private PeripheralManager peripheralManager;
    private SpiDevice spiDevice;
    
    public void initSpi() {
        try {
            peripheralManager = PeripheralManager.getInstance();
            spiDevice = peripheralManager.openSpiDevice(SPI_DEVICE_NAME);
            
            // 配置SPI参数
            configureSpi();
            
        } catch (IOException e) {
            Log.e("SPI", "初始化SPI设备失败", e);
        }
    }
    
    private void configureSpi() throws IOException {
        // 设置SPI模式
        spiDevice.setMode(SpiDevice.MODE0);
        
        // 设置频率
        spiDevice.setFrequency(SPI_FREQUENCY);
        
        // 设置位宽
        spiDevice.setBitsPerWord(8);
        
        // 设置字节序
        spiDevice.setBitJustification(SpiDevice.BIT_JUSTIFICATION_MSB_FIRST);
    }
    
    public byte[] transferData(byte[] txData) {
        try {
            byte[] rxData = new byte[txData.length];
            spiDevice.transfer(txData, rxData, txData.length);
            return rxData;
            
        } catch (IOException e) {
            Log.e("SPI", "SPI数据传输失败", e);
            return null;
        }
    }
    
    public void writeData(byte[] data) {
        try {
            spiDevice.write(data, data.length);
        } catch (IOException e) {
            Log.e("SPI", "SPI写入数据失败", e);
        }
    }
    
    public byte[] readData(int length) {
        try {
            byte[] buffer = new byte[length];
            spiDevice.read(buffer, length);
            return buffer;
        } catch (IOException e) {
            Log.e("SPI", "SPI读取数据失败", e);
            return null;
        }
    }
    
    public void closeSpi() {
        if (spiDevice != null) {
            try {
                spiDevice.close();
            } catch (IOException e) {
                Log.e("SPI", "关闭SPI设备失败", e);
            }
        }
    }
}

方法二:通过JNI调用Linux SPI接口

Java层:
public class NativeSpiManager {
    static {
        System.loadLibrary("spi_native");
    }
    
    // 本地方法声明
    public native int openSpiDevice(String devicePath, int mode, int speed, int bitsPerWord);
    public native int transferSpiData(int fd, byte[] txData, byte[] rxData, int length);
    public native int writeSpiData(int fd, byte[] data, int length);
    public native int readSpiData(int fd, byte[] buffer, int length);
    public native void closeSpiDevice(int fd);
    
    private int deviceFd = -1;
    
    public boolean initSpi(String devicePath, int mode, int speed, int bitsPerWord) {
        deviceFd = openSpiDevice(devicePath, mode, speed, bitsPerWord);
        return deviceFd >= 0;
    }
    
    public byte[] transfer(byte[] txData) {
        if (deviceFd < 0) return null;
        
        byte[] rxData = new byte[txData.length];
        int result = transferSpiData(deviceFd, txData, rxData, txData.length);
        
        return result >= 0 ? rxData : null;
    }
    
    public boolean write(byte[] data) {
        if (deviceFd < 0) return false;
        
        return writeSpiData(deviceFd, data, data.length) >= 0;
    }
    
    public byte[] read(int length) {
        if (deviceFd < 0) return null;
        
        byte[] buffer = new byte[length];
        int result = readSpiData(deviceFd, buffer, length);
        
        return result >= 0 ? buffer : null;
    }
}
C/C++层(JNI实现):
#include <jni.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include <android/log.h>

#define TAG "SPI_JNI"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)

// 打开SPI设备
JNIEXPORT jint JNICALL
Java_com_example_NativeSpiManager_openSpiDevice(JNIEnv *env, jobject thiz,
                                                jstring device_path, jint mode,
                                                jint speed, jint bits_per_word) {
    const char *path = (*env)->GetStringUTFChars(env, device_path, 0);
    
    // 打开SPI设备文件
    int fd = open(path, O_RDWR);
    if (fd < 0) {
        LOGE("无法打开SPI设备: %s", path);
        (*env)->ReleaseStringUTFChars(env, device_path, path);
        return -1;
    }
    
    // 设置SPI模式
    if (ioctl(fd, SPI_IOC_WR_MODE, &mode) < 0) {
        LOGE("设置SPI模式失败: %d", mode);
        close(fd);
        (*env)->ReleaseStringUTFChars(env, device_path, path);
        return -1;
    }
    
    // 设置SPI速度
    if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0) {
        LOGE("设置SPI速度失败: %d", speed);
        close(fd);
        (*env)->ReleaseStringUTFChars(env, device_path, path);
        return -1;
    }
    
    // 设置位宽
    if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits_per_word) < 0) {
        LOGE("设置SPI位宽失败: %d", bits_per_word);
        close(fd);
        (*env)->ReleaseStringUTFChars(env, device_path, path);
        return -1;
    }
    
    LOGI("成功打开SPI设备: %s, fd: %d", path, fd);
    (*env)->ReleaseStringUTFChars(env, device_path, path);
    return fd;
}

// SPI数据传输
JNIEXPORT jint JNICALL
Java_com_example_NativeSpiManager_transferSpiData(JNIEnv *env, jobject thiz,
                                                  jint fd, jbyteArray tx_data,
                                                  jbyteArray rx_data, jint length) {
    jbyte *tx_buf = (*env)->GetByteArrayElements(env, tx_data, 0);
    jbyte *rx_buf = (*env)->GetByteArrayElements(env, rx_data, 0);
    
    // 构建SPI传输结构
    struct spi_ioc_transfer transfer = {
        .tx_buf = (unsigned long)tx_buf,
        .rx_buf = (unsigned long)rx_buf,
        .len = length,
        .delay_usecs = 0,
        .speed_hz = 0,
        .bits_per_word = 0,
    };
    
    // 执行SPI传输
    int result = ioctl(fd, SPI_IOC_MESSAGE(1), &transfer);
    
    (*env)->ReleaseByteArrayElements(env, tx_data, tx_buf, JNI_ABORT);
    (*env)->ReleaseByteArrayElements(env, rx_data, rx_buf, 0);
    
    if (result < 0) {
        LOGE("SPI传输失败");
        return -1;
    }
    
    return length;
}

// SPI写入数据
JNIEXPORT jint JNICALL
Java_com_example_NativeSpiManager_writeSpiData(JNIEnv *env, jobject thiz,
                                               jint fd, jbyteArray data, jint length) {
    jbyte *buf = (*env)->GetByteArrayElements(env, data, 0);
    
    // 构建SPI传输结构(只写)
    struct spi_ioc_transfer transfer = {
        .tx_buf = (unsigned long)buf,
        .rx_buf = 0,
        .len = length,
        .delay_usecs = 0,
        .speed_hz = 0,
        .bits_per_word = 0,
    };
    
    // 执行SPI写入
    int result = ioctl(fd, SPI_IOC_MESSAGE(1), &transfer);
    
    (*env)->ReleaseByteArrayElements(env, data, buf, JNI_ABORT);
    
    if (result < 0) {
        LOGE("SPI写入失败");
        return -1;
    }
    
    return length;
}

// SPI读取数据
JNIEXPORT jint JNICALL
Java_com_example_NativeSpiManager_readSpiData(JNIEnv *env, jobject thiz,
                                              jint fd, jbyteArray buffer, jint length) {
    jbyte *buf = (*env)->GetByteArrayElements(env, buffer, 0);
    
    // 构建SPI传输结构(只读)
    struct spi_ioc_transfer transfer = {
        .tx_buf = 0,
        .rx_buf = (unsigned long)buf,
        .len = length,
        .delay_usecs = 0,
        .speed_hz = 0,
        .bits_per_word = 0,
    };
    
    // 执行SPI读取
    int result = ioctl(fd, SPI_IOC_MESSAGE(1), &transfer);
    
    (*env)->ReleaseByteArrayElements(env, buffer, buf, 0);
    
    if (result < 0) {
        LOGE("SPI读取失败");
        return -1;
    }
    
    return length;
}

// 关闭SPI设备
JNIEXPORT void JNICALL
Java_com_example_NativeSpiManager_closeSpiDevice(JNIEnv *env, jobject thiz, jint fd) {
    if (fd >= 0) {
        close(fd);
        LOGI("SPI设备已关闭");
    }
}

🔍 SPI模式和配置

SPI工作模式:

public class SpiModes {
    // SPI模式定义
    public static final int MODE_0 = 0; // CPOL=0, CPHA=0
    public static final int MODE_1 = 1; // CPOL=0, CPHA=1
    public static final int MODE_2 = 2; // CPOL=1, CPHA=0
    public static final int MODE_3 = 3; // CPOL=1, CPHA=1
    
    // 时钟极性和相位
    // CPOL: 时钟极性 (Clock Polarity)
    // CPHA: 时钟相位 (Clock Phase)
}

配置参数:

public class SpiConfiguration {
    private int mode = 0;           // SPI模式
    private int speed = 1000000;    // 时钟频率 (Hz)
    private int bitsPerWord = 8;    // 每个字的位数
    private int delay = 0;          // 传输延迟 (μs)
    
    public void configureSpi(SpiDevice device) throws IOException {
        device.setMode(mode);
        device.setFrequency(speed);
        device.setBitsPerWord(bitsPerWord);
    }
}

🔧 实际应用示例

1. 温度传感器读取

public class TemperatureSensor {
    private NativeSpiManager spiManager;
    private static final String SPI_DEVICE = "/dev/spidev0.0";
    
    public void initSensor() {
        spiManager = new NativeSpiManager();
        spiManager.initSpi(SPI_DEVICE, 0, 1000000, 8);
    }
    
    public float readTemperature() {
        // 发送读取命令
        byte[] command = {0x03, 0x00}; // 读取温度寄存器
        byte[] response = spiManager.transfer(command);
        
        if (response != null && response.length >= 2) {
            // 解析温度数据
            int rawTemp = ((response[0] & 0xFF) << 8) | (response[1] & 0xFF);
            return rawTemp * 0.0625f; // 转换为摄氏度
        }
        
        return Float.NaN;
    }
}

2. LED矩阵控制

public class LedMatrixController {
    private NativeSpiManager spiManager;
    private static final String SPI_DEVICE = "/dev/spidev1.0";
    
    public void initMatrix() {
        spiManager = new NativeSpiManager();
        spiManager.initSpi(SPI_DEVICE, 0, 10000000, 8); // 10MHz
    }
    
    public void displayPattern(byte[][] pattern) {
        for (int row = 0; row < pattern.length; row++) {
            // 发送行地址
            byte[] rowCmd = {(byte)(0x01 + row), pattern[row][0]};
            spiManager.write(rowCmd);
            
            // 短暂延迟
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

3. Flash存储操作

public class SpiFlashManager {
    private NativeSpiManager spiManager;
    private static final String SPI_DEVICE = "/dev/spidev2.0";
    
    // Flash命令
    private static final byte CMD_READ = 0x03;
    private static final byte CMD_WRITE = 0x02;
    private static final byte CMD_ERASE = 0x20;
    private static final byte CMD_WRITE_ENABLE = 0x06;
    
    public void initFlash() {
        spiManager = new NativeSpiManager();
        spiManager.initSpi(SPI_DEVICE, 0, 25000000, 8); // 25MHz
    }
    
    public void writeFlash(int address, byte[] data) {
        // 写使能
        spiManager.write(new byte[]{CMD_WRITE_ENABLE});
        
        // 构建写命令
        byte[] writeCmd = new byte[4 + data.length];
        writeCmd[0] = CMD_WRITE;
        writeCmd[1] = (byte)((address >> 16) & 0xFF);
        writeCmd[2] = (byte)((address >> 8) & 0xFF);
        writeCmd[3] = (byte)(address & 0xFF);
        System.arraycopy(data, 0, writeCmd, 4, data.length);
        
        // 执行写入
        spiManager.write(writeCmd);
    }
    
    public byte[] readFlash(int address, int length) {
        // 构建读命令
        byte[] readCmd = {
            CMD_READ,
            (byte)((address >> 16) & 0xFF),
            (byte)((address >> 8) & 0xFF),
            (byte)(address & 0xFF)
        };
        
        // 发送读命令并接收数据
        byte[] fullCmd = new byte[4 + length];
        System.arraycopy(readCmd, 0, fullCmd, 0, 4);
        
        byte[] response = spiManager.transfer(fullCmd);
        
        if (response != null && response.length > 4) {
            byte[] data = new byte[length];
            System.arraycopy(response, 4, data, 0, length);
            return data;
        }
        
        return null;
    }
}

⚠️ 注意事项和最佳实践

1. 权限配置

<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.ACCESS_HARDWARE_PERIPHERALS" />

<!-- SELinux权限配置 -->
<!-- sepolicy/device.te -->
allow untrusted_app spi_device:chr_file { read write open ioctl };

2. 错误处理

public class RobustSpiManager {
    private static final int MAX_RETRY_COUNT = 3;
    
    public byte[] transferWithRetry(byte[] txData) {
        for (int i = 0; i < MAX_RETRY_COUNT; i++) {
            try {
                byte[] result = transfer(txData);
                if (result != null) {
                    return result;
                }
            } catch (Exception e) {
                Log.w("SPI", "传输失败,重试 " + (i + 1) + "/" + MAX_RETRY_COUNT, e);
                
                // 短暂延迟后重试
                try {
                    Thread.sleep(10);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
        return null;
    }
}

3. 线程安全

public class ThreadSafeSpiManager {
    private final Object spiLock = new Object();
    
    public byte[] transfer(byte[] txData) {
        synchronized (spiLock) {
            return performTransfer(txData);
        }
    }
    
    public boolean write(byte[] data) {
        synchronized (spiLock) {
            return performWrite(data);
        }
    }
}

4. 资源管理

public class SpiResourceManager implements AutoCloseable {
    private NativeSpiManager spiManager;
    
    public SpiResourceManager(String devicePath, int mode, int speed, int bitsPerWord) {
        spiManager = new NativeSpiManager();
        if (!spiManager.initSpi(devicePath, mode, speed, bitsPerWord)) {
            throw new RuntimeException("Failed to initialize SPI device");
        }
    }
    
    @Override
    public void close() {
        if (spiManager != null) {
            spiManager.closeSpi();
            spiManager = null;
        }
    }
    
    // 使用try-with-resources确保资源释放
    public static void example() {
        try (SpiResourceManager manager = new SpiResourceManager("/dev/spidev0.0", 0, 1000000, 8)) {
            // 使用SPI设备
        } catch (Exception e) {
            Log.e("SPI", "操作失败", e);
        }
    }
}

Android中的SPI通信为高速数据传输和硬件控制提供了可靠的解决方案,特别适用于传感器数据采集、显示控制、存储设备访问等应用场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值