SPI使用参考


REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 4/19/2024

    1. 概述

    1.1 SPI

    Serial Peripheral Interface (串行外围设备接口),简称SPI, 是 Motorola 公司推出的一种高速同步串行接口技术,主要应用在EEPROM,Flash,实时时钟(RTC),数模转换器(ADC),数字信号处理器(DSP) 等设备。

    SPI驱动简要框架如上图,Sigmaster driver向SPI Core注册控制器及设备,用户APP通过SPI device创建的文件接口与底层硬件的交互,进而实现读写功能。


    图1-1 spi brief framework

    1.2 MSPI

    MSPI,即Master spi device,是SigmaStar提供的专门作为spi通信主设备的IP,可实现与各种外接的spi slave device通讯,满足绝大部分spi通讯协议设备的需求。

    当前平台设备节点和硬件组别的关系如下表所示:

    MSPI Bus bank addr cs_num
    0 1110H 2
    1 1111H 2

    2. 关键字说明

    • MISO / SDO:Master input slave output 主机输入,从机输出(数据来自从机)

    • MOSI / SDI:Master output slave input 主机输出,从机输入(数据来自主机)

    • SCLK / SCK : Serial Clock 串行时钟信号,由主机产生发送给从机

    • CS / SS:Slave Select 片选信号,由主机发送,以控制与哪个从机通信,通常是低电平有效信号

    • CPOL / CKP:时钟极性,表示时钟的默认状态下的电平高低状态

    • CPHA / CKE:时钟相位,表示采集数据时是在时钟信号的具体相位

    • SPI Mode :SPI的时钟极性和相位的配置可以输出4种SPI模式

      SPI Mode CPOL CPHA
      0 [00] 0 0
      1 [01] 0 1
      2 [10] 1 0
      3 [11] 1 1

    3. 硬件功能

    • MSPI(Master SPI)只能作为master device,不能作为slave device

    • 硬件支持6组MSPI,支持Motorola SPI标准时序

    • FIFO mode下支持全双工读写/半双工读写,DMA mode下仅支持半双工读写

    • 支持4线通讯(MISO + MOSI + SCLK + CS)和3线通讯(MOSI + SCLK + CS)

    • 8字节读写缓冲区(FIFO mode),字节传输1bit到15bit可配置位宽度

    • 硬件预设的片选设定,mspi0预设2路cs,driver当中提供额外cs扩展,需要从dtsi当中添加,详细方法见7.2.3. cs-ext章节

    • Source Clock:12M、54M、108M、144M,实际SPI的通讯频率可选档位则是每种Source Clock分频的8个档位,共计32个档位,因此SPI通讯频率范围是46875K~72MHz,目标频率可通过赋值struct mspi_setup结构体中的max_speed_hz变量进行设定(参考7.3.1.用户态读写SPI章节);而实际的通讯频率会选择最接近目标频率但小于目标频率的档位,以Source Clock为12Mhz举例,如下表,频率档位被分为8档,如果目标频率是80000HZ,那么实际通讯频率为46875HZ。

      NO. SPI Clock Rate formula
      1 46875 Source Clock / 256
      2 93750 Source Clock / 128
      3 187500 Source Clock / 64
      4 375000 Source Clock / 32
      5 750000 Source Clock / 16
      6 1500000 Source Clock / 8
      7 3000000 Source Clock / 4
      8 6000000 Source Clock / 2

    4. 硬件连接介绍

    MSPI是多主从架构,顾名思义一路总线上面可以有多个主设备与多个从设备,从设备选择信号线,常称为片选信号线,也称为CS、SS,以下用 SS 表示。 当有多个 SPI 从设备与 SPI 主机相连时,设备的其它信号线 SCLK、MOSI 及 MISO 同时并联到相同的 SPI 总线上,即无论有多少个从设备,都共同只使用这 3 条总线;而每个从设备都有独立的这一条 SS 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。 SPI 协议中没有设备地址,它使用 SS 信号线来寻址,当主机要选择从设备时,把该从设备的 SS 信号线设置为低电平(默认为低电平),该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。所以SPI 通讯以 SS 线置低电平为开始信号,以 SS 线被拉高作为结束信号。


    图4-1 mspi_connect

    5. PADMUX配置

    MSPI在Uboot及Kernel环境下的padmux配置方法一致,只需要根据选择的引脚在对应的padmux.dtsi中加入如下所示的代码,以MSPI0举例,每种PADMUX模式都需要配置4个引脚:

    // MODE 1
    <PAD_SPI0_CK PINMUX_FOR_SPI0_MODE_1 MDRV_PUSE_SPI0_CK>;
    <PAD_SPI0_DI PINMUX_FOR_SPI0_MODE_1 MDRV_PUSE_SPI0_DI>;
    <PAD_SPI0_DO PINMUX_FOR_SPI0_MODE_1 MDRV_PUSE_SPI0_DO>;
    <PAD_SPI0_CZ PINMUX_FOR_SPI0_MODE_1 MDRV_PUSE_SPI0_CZ>;
    
    // MODE 2
    <PAD_GPIO5  PINMUX_FOR_SPI0_MODE_2 MDRV_PUSE_SPI0_CK>;
    <PAD_GPIO6  PINMUX_FOR_SPI0_MODE_2 MDRV_PUSE_SPI0_DI>;
    <PAD_GPIO7  PINMUX_FOR_SPI0_MODE_2 MDRV_PUSE_SPI0_DO>;
    <PAD_GPIO4  PINMUX_FOR_SPI0_MODE_2 MDRV_PUSE_SPI0_CZ>;
    

    第一列为引脚索引号,可以在/drivers/sstar/inlcude/{chipname}/gpio.h中查到;

    第二列为模式定义,在/drivers/sstar/gpio/{chipname}/hal_pinmux.c中m_hal_gpio_st_padmode_info_tbl数组里,罗列了所有引脚的复用关系,查询该引脚支持哪些复用功能可以查询该数组;

    第三列为引脚及搭配模式的索引名称,可在/drivers/sstar/include/drv_puse.h里查询。

    6. Uboot用法介绍

    6.1. Uboot Config配置

    在编译Uboot时需要选择的配置如下:

    SigmaStar drivers->
    
        <*> SigmaStar MSPI->
    
    Device drivers->
    
        Generic Driver Options->
    
            <*> Enable Driver Model
    
    Device drivers->
    
        <*> SPI Support->
    
            <*> Enable Driver Model for SPI drivers
    
    Command line interface->
    
        Device access commands->
    
            <*> sspi - Command to access spi device
    

    6.2. Uboot DTS配置

        spi0: spi0@1F222000 {
            compatible = "sstar,mspi";
            reg = <0x1F222000 0x200>;
            mspi-group = <0>;
            use-dma = <0>;
            cs-num = <2>;
            cs-ext = <PAD_UNKNOWN>;
            4to3-mode;
            clk-out-mode = <27000000>;
            status = "okay";
        };
    
    属性 描述 备注
    compatible 用于匹配驱动进行驱动注册,需与代码中一致 禁止修改
    reg 用于指定SPI寄存器bank的地址 不需要修改
    mspi-group 用于指定SPI外设编号序列号 不需要修改
    use-dma 用于指定是否使能DMA模式 可根据需要修改
    cs-num 用于指定Engine自带的cs pad的数量 和cs-ext配合使用
    cs-ext 用于指定除Engine自带的cs外要使用的pad index 可根据需要修改
    4to3-mode 用于指定是否开启4Wires作为3Wires使用 可根据需要修改
    clk-out-mode 用于指定是否开启clk-out-mode 可根据需要修改
    status 用于选择是否使能SPI master驱动 可根据需要修改

    6.3. Uboot cmd参数说明及实例截图

    参数 设定值 说明
    1 sspi 命令名
    2 0:0.1@750000 SPI总线:CS:SPI模式:频率
    3 16 发送的数据长度为16bit
    4 55 发送的16进制数据0x55

    7. Kernel用法介绍

    7.1. Kernel Config配置

    在编译Kernel时需要选择的配置如下:

    Device drivers->
    
        [*] SStar SoC platform drivers->
    
            <*> SStar MSPI driver
    
    Device drivers->
    
        [*] SPI support->
    
            <*>   User mode SPI device driver support
    

    7.2. dtsi节点说明

        spi0: spi@1f222000 {
                compatible = "sstar,mspi";
                mspi-group = <0>;
                clocks = <&CLK_mspi0>;
                reg = <0x1F222000 0x200>;
                interrupts = <GIC_SPI INT_IRQ_MSPI_0 IRQ_TYPE_LEVEL_HIGH>;
                dma-enable;
                cs-num = <1>;
                //cs-ext = <PAD_UNKNOWN>;
                //4to3-mode;
                //clk-out-mode = <27000000>;
                status = "ok";
            };
    
        spi1: spi@1f222200 {
                compatible = "sstar,mspi";
                mspi-group = <1>;
                clocks = <&CLK_mspi1>;
                reg = <0x1F222200 0x200>;
                interrupts = <GIC_SPI INT_IRQ_MSPI_1 IRQ_TYPE_LEVEL_HIGH>;
                dma-enable;
                cs-num = <1>;
                //cs-ext = <PAD_UNKNOWN>;
                //4to3-mode;
                //clk-out-mode = <27000000>;
                status = "ok";
            };
    

    SPI master驱动中支持配置的属性如下:

    属性 描述 备注
    compatible 用于匹配驱动进行驱动注册,需与代码中一致 禁止修改
    reg 用于指定SPI寄存器bank的地址 不需要修改
    interrupts 用于指定使用的硬件中断号及属性 不需要更改
    clocks 用于指定使用的时钟源 不需要更改
    mspi-group 用于指定SPI外设编号序列号 不需要修改
    dma-enable 用于指定是否使能DMA模式 可根据需要修改
    clk-out-mode 用于指定是否开启clk-out-mode 可根据需要修改
    cs-num 用于指定Engine自带的cs pad的数量 和cs-ext配合使用
    cs-ext 用于指定除Engine自带的cs外要使用的pad index 可根据需要修改
    4to3-mode 用于指定是否开启4Wires作为3Wires使用 可根据需要修改
    status 用于选择是否使能SPI master驱动 可根据需要修改

    7.2.1. dma mode

    SigmaStar SPI Master支持两种基本通信模式:buffer mode和dma mode。当SPI Master处于buffer mode工作模式时,SPI Master驱动将需要发送的数据写入SPI Master的发送buffer中,同时将接收到的数据从接收buffer中读出。发送buffer和接收buffer为SPI Master外设中的寄存器,发送buffer和接收buffer的容量为各8 * 2-bytes。由于SPI Master工作在buffer mode时,需要软件参与发送buffer和接收buffer的操作,所以波形会受到软件调度的影响,在每次发送的之间会产生一定间隔。

    当SPI Master处于dma mode工作模式时,驱动只需将需要发送的数据的地址和接收的数据需要存放的地址设置到SPI Master DMA相关的寄存器中后,SPI Master会自动连续发送和接收数据,此过程不需要软件参与。因此当SPI Master工作在dma mode时,SPI的波形连续性较好。

    当SPI Master和SPI Device通讯时发送或接收的数据量较少,可以考虑使用buffer mode,工作效率较高。当SPI Master和SPI Device通讯时发送或接收的数据量较大,如接收SPI Sensor数据等等,此类大量数据通讯如果使用buffer mode会使系统的CPU占用率很高,影响系统效率,在大量数据通讯的场景下可以考虑使用dma mode。

    [注意] 当前SigmaStar SPI Master dma mode只支持半双工(half-duplex)的工作模式,SPI Master的发送和接收共用同一个dma channel。当设备需要使用全双工(full-duplex)通讯时,只能使用buffer mode的工作模式。use-dma属性指定的是工作在半双工模式下使用buffer mode或者dma mode,当Device驱动需要使用全双工通讯时,Master驱动会自动切换到buffer mode,可以在dma mode打开时,通过传输的参数来启用全双工,所以如有希望使用全双工模式时,也可以指定use-dma属性。

    7.2.2. cs-num

    最大值为硬件预设的cs数量,如cs0,cs1,若不需要启用cs1,则可以直接cs-num = 1,当打开CONFIG_SPI_SPIDEV时,driver中会根据cs-num的数量与cs-ext加起来的数量注册设备节点,注意如果某个dtsi节点下含有spi子节点,那么,会按照子节点的数量优先配置给kernel space用,相应的user space用的设备节点注册就会失败。

    如上面dtsi代码所示,如果spi0节点下没有lsm6dso@0子节点,那么当我们开启CONFIG_SPI_SPIDEV时,就可以在/dev/目录下,看到spidev0.0(mspi0 cs0),spidev0.1(mspi0 cs1),spidev1.0(mspi1 cs0),spidev1.1(mspi1 cs1)四个节点,即spidev##bus.##cs。而添加了这个子节点之后,spidev0.0在注册给user space的时候就会失败,我们可以看到log打印:

    chipselect x already in use
    

    此时/dev目录下的spidev0.0就没有了。

    7.2.3. cs-ext

    由driver实现的额外添加cs引脚的功能。当硬件预设cs引脚不够用时,我们可以在dtsi当中配置额外cs引脚来作为扩充,配置方法为打开dtsi节点下的cs-ext属性并配置所要作为扩充为cs的pad id,同时,建议在xxx-padmux.dtsi当中也把对应的pad配置为GPIO MODE,如下为把PAD_SAR_ADC17作为cs的扩充:

        <PAD_SPI0_CK            PINMUX_FOR_SPI0_MODE_1        MDRV_PUSE_SPI0_CK>,
        <PAD_SPI0_DI            PINMUX_FOR_SPI0_MODE_1        MDRV_PUSE_SPI0_DI>,
        <PAD_SPI0_DO            PINMUX_FOR_SPI0_MODE_1        MDRV_PUSE_SPI0_DO>,
        <PAD_SPI0_CZ            PINMUX_FOR_SPI0_MODE_1        MDRV_PUSE_SPI0_CZ>,
        <PAD_PM_INTOUT          PINMUX_FOR_SPI0_CZ1_MODE_1    MDRV_PUSE_SPI0_CZ2>,
        <PAD_I2C0_SCL           PINMUX_FOR_GPIO_MODE          MDRV_PUSE_SPI0_CZ3>,
    

    7.2.4. clk-out-mode

    clk-out-mode为SigmaStar SPI Master所支持的一种特殊模式。当SPI Master处于clk-out-mode时,SPI Master的MOSI和MISO不再按给定的数据发送或接收数据,SPI Master的Clock信号会不断输出方波。此功能的应用场景为使用SPI Master输出的clock作为其他Device的工作Clock。clk-out-mode属性的值用于指定输出clock的频率,配置clk-out-mode只需要在dtsi当中把对那个的clk-out-mode属性打开并配置相应要输出的波形频率就可以:

    clk-out-mode = <3750000>; //使能clk-out-mode,输出3.75M频率波形
    

    7.2.5. 4 to 3 mode

    某些平台的SPI Master 3-wires mode不支持dma mode,如果此时有3-wires dma mode的通讯需求时可以开启4 to 3 mode模式。硬件上将Master的MOSI和MISO短接并连接至Device的SDAT,如下图:


    图7-1 4to3 mode

    当驱动在读数据时,会将MOSI自动切换为GPIO Input Mode从而不干扰MISO的波形输入,从而达到将四线模式用作三线模式。如果要使用此功能,只要把dtsi节点当中的4to3-mode属性打开就可以,即

        4to3-mode; //使能4to3-mode
    

    7.3. 模块使用介绍

    7.3.1. 用户态读写SPI

    内核有自带一套用于测试MSPI Master的Device Driver 以及用户程序:drivers/spi/spidev.c 和tools/spi/spidev_test.c。要使用内核自带测试程序,需要修改配置文件打开 CONFIG_SPI_SPIDEV 选项。CONFIG_SPI_SPIDEV选项在menuconfig中:

        Device Drivers --->
    
            [*] SPI Support --->
    
                <*> User mode SPI device driver support
    

    开启CONFIG_SPI_SPIDEV选项后,内核会在/dev文件夹下生成spidevX.X文件,详细可参考7.2.2. cs-num章节。用户程序可以通过操作这几个文件操作对应的spi master。对这几个文件的操作可以参考tools/spi/spidev_test.c。

    spidev_test的生成可以通过在tools/目录下执行make spi获取,生成的文件在:tools/spi/spidev_test,将spidev_test拷贝到开发板上运行,就可以通过命令行操作MSPI Master了。spidev_test核心发送/接收函数示例如下:

     static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
    {
        int ret;
        int out_fd;
        struct spi_ioc_transfer tr = {
            .tx_buf = (unsigned long)tx,
            .rx_buf = (unsigned long)rx,
            .len = len,
            .delay_usecs = delay,
            .speed_hz = speed,
            .bits_per_word = bits,
            };
    
        if (mode & SPI_TX_OCTAL)
            tr.tx_nbits = 8;
        else if (mode & SPI_TX_QUAD)
            tr.tx_nbits = 4;
        else if (mode & SPI_TX_DUAL)
            tr.tx_nbits = 2;
        if (mode & SPI_RX_OCTAL)
            tr.rx_nbits = 8;
        else if (mode & SPI_RX_QUAD)
            tr.rx_nbits = 4;
        else if (mode & SPI_RX_DUAL)
            tr.rx_nbits = 2;
        if (!(mode & SPI_LOOP)) {
        if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL))
                tr.rx_buf = 0;
        else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL))
                tr.tx_buf = 0;
        }
    
        ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
        if (ret < 1)
            pabort("can't send spi message");
    
        if (verbose)
            hex_dump(tx, len, 32, "TX");
    
        if (output_file) {
            out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
            if (out_fd < 0)
                pabort("could not open output file");
    
            ret = write(out_fd, rx, len);
            if (ret != len)
                pabort("not all bytes written to output file");
    
            close(out_fd);
        }
    
        if (verbose)
            hex_dump(rx, len, 32, "RX");
    }
    

    当struct spi_ioc_transfer tr中.rx_buff不为null,.tx_buff为null时,MSPI Master执行的为半双工读操作;当.rx_buff为null,.tx_buff不为null时,执行的为半双工写操作;当.rx_buff和.tx_buff都不为null时,哪怕dma mode被打开了,执行的也是全双工读写操作。对应的波形示意图如下:


    图7-2 waveform diagram

    图中MOSI对应的为.tx_buff中的数据,MISO对应的为.rx_buff中的数据。

    7.3.2. spidev_test使用方法

    1. 将开启配置后的内核镜像更新至开发板上。

    2. 进入内核目录:tools/spi/ 执行 make,此时会在该目录下生成测试程序:spidev_test

    3. 将该测试程序拷贝至开发板上,拷贝到开发板上的方式有很多,可以是编译到文件系统中、通过tftp发送至开发板、通过nfs挂载文件系统至开发板、通过usb复制至开发板等等。

    4. 在开发板下执行 spidev_test 即可对 spi master 进行操作,常用的操作有:

      参数 描述
      -D 用于选择操作的设备节点,如:/dev/spidev0.0,默认为:/dev/spidev1.1
      -s 用于选择spi时钟速率,单位为Hz
      -H 用于和 -O 搭配选择spi的通讯时序
      -O 用于和 -H 搭配选择spi的通讯时序
      -C 用于指定CS为高有效
      -L 用于指定字节序为LSB
      -3 用于指定通讯模式为三线模式
      -v 用于指定打印出发送&接收的数据内容
      -i 用于指定发送的文件(二进制文件)
      -o 用于指定接收的数据保存成文件的路径(二进制文件)
      -p 用于指定发送的数据,如:“1234\xde\xad”

      当前驱动所支持的功能上表所列,具体的使用方法可以参考help命令:spidev_test help。参数可以自由组合,请根据需要调整。

    7.3.3. 驱动支持的模式

    当前驱动中支持的模式如下表:

    模式 描述
    SPI_CPHA 用于和SPI_CPOL配合组成4种通讯时序
    SPI_CPOL 用于和SPI_CPHA配合组成4种通讯时序
    SPI_CS_HIGH 用于指定CS高有效
    SPI_LSB_FIRST 用于指定通讯序列为LSB(默认为MSB)
    SPI_3WIRE 用于指定使用三线模式

    7.3.4. 内核态读写SPI

    内核态读写MSPI可以参考/drivers/spi/spidev.c的实现,封装接口构建并初始化结构体spi_transfer和spi_message,然后调用内核接口spidev_sync():

     spidev_sync_read(struct spidev_data *spidev, size_t len)
     {
         struct spi_transfer t = {
                 .rx_buf     = spidev->rx_buffer,
                 .len        = len,
                 .speed_hz   = spidev->speed_hz,
             };
         struct spi_message  m;
    
         spi_message_init(&m);
         spi_message_add_tail(&t, &m);
         return spidev_sync(spidev, &m);
     }
    
     spidev_sync_write(struct spidev_data *spidev, size_t len)
     {
         struct spi_transfer t = {
                 .tx_buf     = spidev->tx_buffer,
                 .len        = len,
                 .speed_hz   = spidev->speed_hz,
             };
         struct spi_message  m;
    
         spi_message_init(&m);
         spi_message_add_tail(&t, &m);
         return spidev_sync(spidev, &m);
     }