总线学习3--SPI番外(TODO)

这次主要看看编程实现,在编程上对于SPI,有一个概念很重要,因为是全双工,所以RX有没有意义取决于slave。SPI没有单独read。永远同时收发。

1 SPIDEV_TEST

最基本用法

bash 复制代码
./spidev_test -D /dev/spidev0.0

输出

bash 复制代码
spi mode: 0
bits per word: 8
max speed: 500000 Hz
TX | FF FF FF FF
RX | 00 00 00 00

发送指定数据

bash 复制代码
./spidev_test -D /dev/spidev0.0 -p "\x9f\x00\x00" -v

接收

bash 复制代码
TX:
9f 00 00

RX:
ef 40 18

2 应用层

一般来说是基于spidev来编程,所有的接口都是IOCTL。

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

#define SPI_DEV "/dev/spidev14.0"
#define SPI_MODE    0
#define SPI_BITS    8
#define SPI_SPEED   1000000

static int spi_fd = -1;

// SPI单次传输通用函数
static int spi_xfer(uint8_t *tx, uint8_t *rx, uint32_t len)
{
    struct spi_ioc_transfer xfer = {
        .tx_buf        = (unsigned long)tx,
        .rx_buf        = (unsigned long)rx,
        .len           = len,
        .speed_hz      = SPI_SPEED,
        .bits_per_word = SPI_BITS,
        .delay_usecs   = 10,
    };
    return ioctl(spi, SPI_IOC_MESSAGE(1), &xfer);
}

int spi_init(void)
{
    spi_fd = open(SPI_DEV, O_RDWR);
    if (spi_fd < 0) {
        perror("open spi fail");
        return -1;
    }
    uint8_t mode = SPI_MODE;
    uint8_t bits = SPI_BITS;
    uint32_t speed = SPI_SPEED;
    ioctl(spi_fd, SPI_IOC_WR_MODE, &mode);
    ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    return 0;
}

int main(void)
{
    if (spi_init() < 0)
        return -1;

    // 第一包:0xF0 00 00 00 00,只发送,丢弃接收数据
    uint8_t tx1[] = {0xF0,0x00,0x00,0x00,0x00};
    uint8_t dump_rx[5] = {0};
    spi_xfer(tx1, dump_rx, sizeof(tx1));

    // 第二包:0xF1 00 00 00 00,发送同时读取设备返回数据
    uint8_t tx2[] = {0xF1,0x00,0x00,0x00,0x00};
    uint8_t rx_data[5] = {0};
    spi_xfer(tx2, rx_data, sizeof(tx2));

    printf("F1指令返回数据:");
    for(int i = 0; i < 5; i++) {
        printf("0x%02X ", rx_data[i]);
    }
    printf("\n");

    close(spi_fd);
    return 0;
}

3 驱动层

Linux标准SPI的API

函数原型 功能说明
void spi_message_init(struct spi_message *m); 初始化一个 SPI 事务 message,清空链表
void spi_message_add_tail(struct spi_transfer *xfer, struct spi_message *m); 把一段收发 transfer 挂载到 message 尾部,支持多段连续传输
int spi_sync(struct spi_device *spi, struct spi_message *m); 同步阻塞传输,等待整包收发完成才返回;线程上下文可用,中断禁止调用
int spi_write(struct spi_device *spi, const void *buf, size_t len); 简化单发,只发不收(rx=NULL)
int spi_read(struct spi_device *spi, void *buf, size_t len); 简化只读,tx=NULL,发空时钟读数据
int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx, void *rxbuf, unsigned n_rx); 先发一段,再收一段(完美适配你 F0/F1 两段指令需求)
函数原型 功能说明
int spi_async(struct spi_device *spi, struct spi_message *m); 异步发起传输,立即返回,完成后调用 complete 回调
void spi_message_complete_callback(struct spi_message *m); 传输完成回调函数原型

和上面效果一样的程序。

cpp 复制代码
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/of.h>

// 私有设备数据
struct fp_dev {
    struct spi_device *spi;
    u8 tx_buf[8];
    u8 rx_buf[8];
};

// SPI单次收发封装
static int fp_spi_xfer(struct fp_dev *priv, u8 *tx, u8 *rx, size_t len)
{
    struct spi_message msg;
    struct spi_transfer xfer = {
        .tx_buf = tx,
        .rx_buf = rx,
        .len = len,
        .delay_usecs = 10,
    };

    spi_message_init(&msg);
    spi_message_add_tail(&xfer, &msg);
    return spi_sync(priv->spi, &msg);
}

// 业务:先发F0指令,再发F1读返回数据
static int fp_read_data(struct fp_dev *priv)
{
    int ret;
    u8 tx1[] = {0xF0, 0x00, 0x00, 0x00, 0x00};
    u8 dump[5];

    // 第一帧 F0,丢弃接收
    ret = fp_spi_xfer(priv, tx1, dump, sizeof(tx1));
    if (ret)
        return ret;
    udelay(50); // 硬件时序间隔

    // 第二帧 F1,读取返回
    u8 tx2[] = {0xF1, 0x00, 0x00, 0x00, 0x00};
    ret = fp_spi_xfer(priv, tx2, priv->rx_buf, sizeof(tx2));
    return ret;
}

// probe:DTS compatible匹配后执行
static int fp_probe(struct spi_device *spi)
{
    struct fp_dev *priv;

    priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;
    priv->spi = spi;
    spi_set_drvdata(spi, priv);

    dev_info(&spi->dev, "Finger SPI driver probed\n");
    return 0;
}

static int fp_remove(struct spi_device *spi)
{
    dev_info(&spi->dev, "Finger SPI removed\n");
    return 0;
}

// DTS匹配表
static const struct of_device_id fp_of_match[] = {
    { .compatible = "vendor,finger-spi" },
    { /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, fp_of_match);

static struct spi_driver fp_spi_driver = {
    .probe = fp_probe,
    .remove = fp_remove,
    .driver = {
        .name = "fp-spi-driver",
        .of_match_table = fp_of_match,
    },
};

module_spi_driver(fp_spi_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Linux SPI peripheral minimal driver");

如果需要连续发送不拉CS,可以换成这个。

cpp 复制代码
// 一次性连续发送F0 + F1,CS全程保持低电平
static int fp_continuous_xfer(struct fp_dev *priv)
{
    struct spi_message msg;
    struct spi_transfer xfers[2];
    int ret;

    u8 tx1[] = {0xF0,0,0,0,0};
    u8 dump1[5];
    u8 tx2[] = {0xF1,0,0,0,0};
    u8 rx2[5];

    memset(xfers, 0, sizeof(xfers));
    // 第一段 F0
    xfers[0].tx_buf = tx1;
    xfers[0].rx_buf = dump1;
    xfers[0].len = 5;
    xfers[0].cs_change = 0; // 传输完不释放CS
    // 第二段 F1
    xfers[1].tx_buf = tx2;
    xfers[1].rx_buf = rx2;
    xfers[1].len = 5;
    xfers[1].cs_change = 1; // 最后一帧释放CS

    spi_message_init(&msg);
    spi_message_add_tail(&xfers[0], &msg);
    spi_message_add_tail(&xfers[1], &msg);

    ret = spi_sync(priv->spi, &msg);
    if (!ret)
        memcpy(priv->rx_buf, rx2, 5);
    return ret;
}
相关推荐
MartinYeung520 小时前
[论文学习]δ-STEAL:基于本地差分隐私的大语言模型窃取攻击
网络·学习·语言模型
paopaokaka_luck20 小时前
基于springboot3+vue3的文山民族文化资源展示与推广平台(协同过滤算法、Echarts 图形化分析)
java·前端·spring boot·学习·echarts
wuhuhuan1 天前
【JMeter 学习打卡 Day 4】多用户登录 + 动态 token 关联
学习·jmeter
Code额1 天前
Python asyncio 异步编程全套学习文档(零基础完整版)
python·学习·oracle·async·异步·asyncio
在线考试系统推荐1 天前
2026年7月最新实测:三大考试软件导入试题能力对比
人工智能·学习·系统架构
小弥儿1 天前
GitHub今日热榜 | 2026-08-20:Agent 上下文数据库接棒
数据库·学习·开源·github
sunoo-2291 天前
【C 语言标准 IO 入门】第二天学习笔记:文件操作核心函数 + 实战案例 + 踩坑合集
linux·笔记·vscode·学习
MartinYeung51 天前
[论文学习]MPMA:针对模型上下文协议的偏好操纵攻击
人工智能·学习·安全
我爱cope1 天前
【计算机网络 | 数据链路层8:CSMA/CD 与 CSMA/CA:有线以太网和 Wi-Fi 如何避免冲突?】
网络·学习·计算机网络
-To be number.wan1 天前
我的创作纪念日
学习