SPI子系统

IO特性

SPI接口一般使用四条信号线通信:
SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)
MISO: 主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。
MOSI: 主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。
SCLK:串行时钟信号,由主设备产生。
CS/SS:从设备片选信号,由主设备控制。它的功能是用来作为“片选引脚”,也就是选择指定的从设备,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突

四种模式

Mode0:CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿(准备数据),(发送数据)数据发送是在下降沿。
Mode1:CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。
Mode2:CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。
Mode3:CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。
spi_bus
[图片]

SPI 总线设备驱动模型

在这里插入图片描述
设备树处理过程
在这里插入图片描述
我们可以下载安卓源码查看:
设备树在devices_tree,驱动在driver目录,

git clone https://android.googlesource.com/kernel/msm
MSM:指的是 Qualcomm 的 Mobile Station Modem 平台

在这里插入图片描述spi_master:是当前spi_dev的父节点
spi 设备树上的设备节点中的信息都会转化为spi_device中的成员值,呈一一对应关系。
解析spi设备树的核心函数为 of_register_spi_devices在这里插入图片描述
在of_register_spi_devices函数中解析设备树
访问 spi_master节点下的每一个子节点
在这里插入图片描述
解析子节点中的devices信息
在这里插入图片描述
spi_devices配置好后,使用spi_add_devices添加设备
在这里插入图片描述
spidev.c文件解析,spidev.c是spi通用的设备驱动文件,一些简单的spi设备可以直接使用spidev.c
spidev.c分析:https://blog.csdn.net/2301_77015050/article/details/132146419?ops_request_misc=&request_id=&biz_id=102&utm_term=spi%E9%A9%B1%E5%8A%A8&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-4-132146419.142v100pc_search_result_base2&spm=1018.2226.3001.4187

使用spidev.c操作spi_dac模块

在这里插入图片描述
这个电路图展示了一个使用 TLC5615 数模转换器(DAC)的电路。具体来说,电路图分成了两个主要部分:左边部分是连接到外部 SPI 接口的部分,右边部分是 DAC 输出和相应的指示电路。以下是详细的解释:
左边部分(H2 Header 与 SPI 接口)
H2 是一个 8 引脚的连接器,用于连接到外部 SPI 接口:

  1. 引脚 1 (5V): 提供5V电源。
  2. 引脚 2 (GND): 接地。
  3. 引脚 3 (MOSI): 主设备输出从设备输入,用于发送数据到 DAC(TLC5615)。
  4. 引脚 4 (MISO): 主设备输入从设备输出,这里未使用。
  5. 引脚 5 (SCLK): 串行时钟信号。
  6. 引脚 6 (CS): 片选信号,当低电平时,DAC 被选中。
  7. 引脚 7、8: 未使用。
    C1 和 C2 是电源去耦电容,用于滤除电源噪声:
  • C1 (22nF)
  • C2 (100nF)
    中间部分(TLC5615 数模转换器)
    U1 是 TLC5615 数模转换器,它将数字信号转换为模拟信号:
  1. 引脚 1 (DIN): 数据输入,连接到 MOSI。
  2. 引脚 2 (SCLK): 串行时钟输入。
  3. 引脚 3 (CS): 片选输入。
  4. 引脚 4 (DOUT): 数据输出,这里未使用。
  5. 引脚 5 (AGND): 模拟地。
  6. 引脚 6 (REFIN): 参考电压输入。
  7. 引脚 7 (VDD): 电源电压。
  8. 引脚 8 (OUT): 模拟信号输出。
    右边部分(输出信号处理和指示电路)
  9. 电容 C3 (100nF): 电源去耦电容。
  10. 电阻 R1 (5.1kΩ): 限流电阻,用于 LED1。
  11. LED1: 用于显示 DAC 输出状态。
  12. LM4040(U2): 精密电压参考,用于提供稳定的参考电压(2.048V)。
  13. 电阻 R2 (10kΩ): 拉低电阻。
  14. 电阻 R3 (5.1kΩ): 限流电阻,用于 LED2。
  15. LED2: 用于显示电源状态。
    工作原理
  16. 电源部分: H2 提供的 5V 电源通过 C1 和 C2 去耦之后,供给 TLC5615(U1)和 LM4040(U2)。
  17. SPI 通信部分: 外部设备通过 H2 的 MOSI, SCLK 和 CS 引脚与 TLC5615 进行 SPI 通信,发送的数字信号经 DAC 转换为模拟信号,从 U1 的 OUT 引脚输出。
  18. 参考电压部分: LM4040 提供稳定的 2.048V 参考电压给 TLC5615 的 REFIN 引脚,确保 DAC 输出的精度。
  19. 输出信号处理部分:
  • 从 U1 的 OUT 引脚输出的模拟信号通过 R1 限流并驱动 LED1,指示 DAC 的输出状态。
  • LED2 通过 R3 限流并一直亮起,指示电路板的电源状态。
    总体来说,这个电路实现了通过 SPI 接口控制 DAC 输出模拟信号,并通过 LED1 和 LED2 指示输出状态和电源状态。
    在这里插入图片描述
  • CS为低:
  • 在SCLK的上升沿,从DIN采集16位数据,存入上图中的16-Bit Shift Register
  • 在CS的上升沿,把16-Bit Shift Register中的10位数据传入10-Bit DAC Register,作为模拟量在OUT引脚输出
    注意:
  • 传输的16位数据中,高4位是无意义的
  • 中间10位才被转换为模拟量
  • 最低2位必须是0
  • 从DOUT出来的是上一次发送的数据
    在这里插入图片描述
    上机实验:
    修改设备树,在spi节点下添加子节点:
dac: dac@00 {
        compatible = "spidev";
        reg = <0>;
        spi-max-frequency = <20000000>;                //从DAC芯片手册获得
};        

编译设备树:

make dtbs 编译设备树
make modules 编译spidev.ko驱动
开启编译选项
make menuconfig

应用层代码:参考tools\spi\spidev_fdx.c
read/write只能单向读写所以要使用ioctl
ioctl的具体实现在spidev.c的spidev_ioctl函数中

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <linux/types.h>
#include <linux/spi/spidev.h>

/* dac_test /dev/spidevB.D <val> */

int main(int argc, char **argv)
{
        int fd;
        unsigned int val;
        struct spi_ioc_transfer        xfer[1];
        unsigned char tx_buf[2];
        unsigned char rx_buf[2];
        int        status;

        if(argc != 3) {
                printf("Usage: %s /dev/spidevB.D <val>\n", argv[0]);
                return 0;
        }


        fd = open(argv[1], O_RDWR);
        if (fd < 0) {
                printf("can not open %s\n", argv[1]);
                return 1;
        }

        val = strtoul(argv[2], NULL, 0);
        val <<= 2;                /* bit0, bit1 = 0b00 */
        val &= 0xFFC;        /* only reserve 10 bit */

        tx_buf[0] = (val>>8) & 0xff;
        tx_buf[1] = val & 0xff;

        memset(xfer, 0, sizeof xfer);

        xfer[0].len = 2;
        xfer[0].tx_buf = (unsigned long) tx_buf;
        xfer[0].rx_buf = (unsigned long) rx_buf;

        status = ioctl(fd, SPI_IOC_MESSAGE(1), xfer);//告诉内核我们要发送1个SPI消息。这部分中的1表示我们要发送的消息数量。
        if (status < 0) {
                printf("SPI_IOC_MESSAGE\n");
                return -1;
        }

        /* print result */
        val = (rx_buf[0] <<8)|(rx_buf[1]);
        val >>= 2;
        printf("pre val = %d\n", val);

        return 0;
}

在这里插入图片描述

  • unlocked_ioctl 是标准的 ioctl 处理函数,用于在设备文件上执行各种控制操作。它通常用于处理来自用户空间的 ioctl
    请求。

  • compat_ioctl 是用于处理 32 位用户空间应用程序在 64 位内核上运行时的兼容性 ioctl 请求。它确保 32
    位应用程序能够正确调用 ioctl,并处理 32 位和 64 位之间的数据结构差异

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值