RTOS_SPI使用参考


REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 08/22/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、104M、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. RTOS用法介绍

    MSPI通讯的时候,需要保证基本的如下步骤:

    • 硬件设备连接;
    • CONFIG支持MSPI驱动;
    • SYSDESC文件配置需求属性;
    • PADMUX设定;
    • API调用,执行通讯。

    5.1 DRIVER PATH

    sc/driver/sysdriver/mspi/drv/src/drv_mspi.c

    sc/driver/sysdriver/mspi/hal/ifado/src/hal_mspi.c

    sc/driver/sysdriver/mspi/hal/ifado/inc/hal_mspi.h

    sc/driver/sysdriver/mspi/hal/ifado/inc/hal_mspireg.h

    sc/driver/sysdriver/mspi/hal/ifado/inc/hal_mspi_cfg.h

    sc/driver/sysdriver/mspi/os/mspi_os.h

    sc/driver/sysdriver/mspi/drv/pub/drv_mspi.h

    sc/driver/sysdriver/mspi/drv/src/drv_mspi_test.c

    5.2 CONFIG配置

    #make menuconfig
    
    BSP Driver Options --->
        <*> Support MSPI driver
    

    5.3 SYSDESC配置

    chipname.sys文件位于sc/driver/sysdriver/sysdesc/hal/chipname/pub

    <mspi0>
            [reg_u32_u16] 0x1F222000 0x200;
            [interrupts_u8] INT_IRQ_MSPI_0;
            [camclk_u16] CAMCLK_mspi0;
            [dma_u8] 1;
            [cs_num_u8] 2;
            [cs_ext_u32] PAD_UNKNOWN;
            [4to3_mode_u8] 0;
            [clk_out_mode_u32] 0;
            [status_u8] 1;
    
    <mspi1>
            [reg_u32_u16] 0x1F222200 0x200;
            [interrupts_u8] INT_IRQ_MSPI_1;
            [camclk_u16] CAMCLK_mspi1;
            [dma_u8] 1;
            [cs_num_u8] 2;
            [cs_ext_u32] PAD_UNKNOWN;
            [4to3_mode_u8] 0;
            [clk_out_mode_u32] 0;
            [status_u8] 1;
    

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

    属性 描述 备注
    reg_u32_u16 用于指定SPI寄存器bank的IO地址 不需要修改
    interrupts_u8 用于指定使用的硬件中断号 不需要更改
    dma_u8 用于指定是否使能DMA模式 可根据需要修改
    clk-out-mode_u32 用于指定是否开启clk-out-mode 可根据需要修改
    cs_num_u8 用于指定Engine自带的cs pad的数量 和cs-ext_u32配合使用
    cs_ext_u32 用于指定除Engine自带的cs外要使用的pad index 可根据需要修改
    4to3_mode_u8 用于指定是否开启4Wires作为3Wires使用 可根据需要修改
    pad_mux_u16 用于选择mspi的padmux 可根据需要修改

    5.3.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属性。

    5.3.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节点下没有子节点,那么当我们开启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的时候就会失败,此时 /dev目录下的spidev0.0就没有了。

    5.3.3. cs-ext

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

        <PAD_MSPI_CZ        PINMUX_FOR_SPI0_MODE_1        MDRV_PUSE_SPI0_DO>,
        <PAD_MSPI_DI        PINMUX_FOR_SPI0_MODE_1        MDRV_PUSE_SPI0_DI>,
        <PAD_MSPI_CK        PINMUX_FOR_SPI0_MODE_1        MDRV_PUSE_SPI0_CK>,
        <PAD_MSPI_DO        PINMUX_FOR_SPI0_MODE_1        MDRV_PUSE_SPI0_CZ>,
        <PAD_I2C0_SCL       PINMUX_FOR_GPIO_MODE          MDRV_PUSE_SPI0_CZ2>,
    

    5.3.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_u32] 3750000; //使能clk-out-mode,输出3.75M频率波形
    

    5.3.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,如下图:


    图5-1 4to3 mode

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

        [4to3_mode_u8] 1; //使能4to3-mode
    

    5.4 PADMUX设定

    对于mspi的padmux,共有三种配置方法,三种情况只会使用一种,当前默认使用sysDesc配置方法。三种如下:

    方法一:CONFIG_PADMUX_SUPPORT=Y

    该方法通过使能PADMUX驱动,在chipname-padmux.c文件配置引脚复用功能,该文件位于sc/driver/sysdriver/padmux/hal/chipname/src,只需要在对应的schematic属性添加如下内容中设定:

    在文件的结构体数组变量schematic当中,依照格式添加对应的pad id、mode、puse

             typedef struct
              {
                 U32         u32PadId;
                 U32         u32Mode;
                 U32         u32Puse;
              }
    
             pad_info_t schematic[] =
             {
                 {PAD_MSPI_CZ,    PINMUX_FOR_SPI0_MODE_1,    MDRV_PUSE_SPI0_CZ},
                 {PAD_MSPI_CK,    PINMUX_FOR_SPI0_MODE_1,    MDRV_PUSE_SPI0_CK},
                 {PAD_MSPI_DI,    PINMUX_FOR_SPI0_MODE_1,    MDRV_PUSE_SPI0_DI},
                 {PAD_MSPI_DO,    PINMUX_FOR_SPI0_MODE_1,    MDRV_PUSE_SPI0_DO},
             }
    

    第一列为引脚索引号,可以在sc/drivers/sysdriver/gpio/hal/chipname/pub/gpio.h中查询;

    第二列为模式定义,可以在sc/drivers/sysdriver/gpio/hal/chipname/pub/padmux.h中查询;

    第三列为引脚及搭配模式的索引名称,可以在sc/drivers/sysdriver/padmux/drv/pub/drv_puse.h中查询;

    方法二:CONFIG_PADMUX_SUPPORT=N && CONFIG_GPIO_SUPPORT=Y

    该方法通过SYSDESC在chipname.sys文件配置引脚复用功能,该文件位于sc/driver/sysdriver/sysdesc/hal/chipname/pub,可在对应的MSPI节点属性padmux_u8配置padmux.h定义的模式:

        <mspi0>
         [reg_u32_u16] 0x1F222000 0x200;
         [interrupts_u8] INT_IRQ_MSPI_0;
         [camclk_u16] CAMCLK_mspi0;
         [dma_u8] 1;
         [cs_num_u8] 2;
         [cs_ext_u32] PAD_UNKNOWN;
         [pad_mux_u16] PINMUX_FOR_SPI0_MODE_1;
         [4to3_mode_u8] 0;
         [clk_out_mode_u32] 0;
    

    方法三:CONFIG_PADMUX_SUPPORT=N && CONFIG_GPIO_SUPPORT=N

    该方法通过SYSDESC在chipname.sys文件配置引脚复用功能,该文件位于sc/driver/sysdriver/sysdesc/hal/chipname/pub,可在对应的MSPI节点属性padmux_u8配置引脚模式:

        <mspi0>
         [reg_u32_u16] 0x1F222000 0x200;
         [interrupts_u8] INT_IRQ_MSPI_0;
         [camclk_u16] CAMCLK_mspi0;
         [dma_u8] 1;
         [cs_num_u8] 2;
         [cs_ext_u32] PAD_UNKNOWN;
         [pad_mux_u16] 1;
         [4to3_mode_u8] 0;
         [clk_out_mode_u32] 0;
    

    MSPI0硬件组padmux罗列

    MSPI Bus Register addr Padmod Pad Pin Name
    0 bank 103CH offset 68H bit[2:0] 1 PAD_MSPI_CZ MSPI0_CZ
    PAD_MSPI_CK MSPI0_CK
    PAD_MSPI_DI MSPI0_DI
    PAD_MSPI_DO MSPI0_DO
    2 PAD_GPIO4 MSPI0_CZ
    PAD_GPIO5 MSPI0_CK
    PAD_GPIO6 MSPI0_DI
    PAD_GPIO7 MSPI0_DO
    3 PAD_FUART_RX MSPI0_CZ
    PAD_FUART_TX MSPI0_CK
    PAD_FUART_CTS MSPI0_DI
    PAD_FUART_RTS MSPI0_DO
    4 PAD_SD1_IO0 MSPI0_CZ
    PAD_SD1_IO1 MSPI0_CK
    PAD_SD1_IO2 MSPI0_DI
    PAD_SD1_IO3 MSPI0_DO
    5 PAD_PWM1 MSPI0_CZ
    PAD_SPI0_CK MSPI0_CK
    PAD_SPI0_DI MSPI0_DI
    PAD_SPI0_DO MSPI0_DO
    bank 103CH offset 68H bit[5:4] mspi0 cz1 mode 1 PAD_PWM0 MSPI0_CZ1
    mspi0 cz1 mode 2 PAD_GPIO14 MSPI0_CZ1

    5.5 API说明

    头文件位于*sc/driver/sysdriver/mspi/drv/pub/drv_mspi.h*

        struct mspi_setup
        {
            u8  bus_num;                        /* mspi bus number */
            u32 max_speed_hz;                   /* transfer speed */
            u8  chip_select;
            u32 mode;                           /* define below */
        #define SPI_CPHA        0x01            /* clock phase */
        #define SPI_CPOL        0x02            /* clock polarity */
        #define SPI_MODE_0      (0|0)           /* (original MicroWire) */
        #define SPI_MODE_1      (0|SPI_CPHA)
        #define SPI_MODE_2      (SPI_CPOL|0)
        #define SPI_MODE_3      (SPI_CPOL|SPI_CPHA)
        #define SPI_CS_HIGH     0x04            /* chipselect active high? */
        #define SPI_LSB_FIRST   0x08            /* per-word bits-on-wire */
        #define SPI_3WIRE       0x10            /* SI/SO signals shared */
        #define SPI_LOOP        0x20            /* loopback mode */
        #define SPI_NO_CS       0x40            /* 1 dev/bus, no chipselect */
        #define SPI_READY       0x80            /* slave pulls low to pause */
        #define SPI_TX_DUAL     0x100           /* transmit with 2 wires */
        #define SPI_TX_QUAD     0x200           /* transmit with 4 wires */
        #define SPI_RX_DUAL     0x400           /* receive with 2 wires */
        #define SPI_RX_QUAD     0x800           /* receive with 4 wires */
        #define SPI_CS_WORD     0x1000          /* toggle cs after each word */
        #define SPI_TX_OCTAL    0x2000          /* transmit with 8 wires */
        #define SPI_RX_OCTAL    0x4000          /* receive with 8 wires */
        #define SPI_3WIRE_HIZ   0x8000          /* high impedance turnaround */
        }
    

    5.5.1 drv_mspi_setup

    • 功能

      设置属性包括模式、频率、片选等

    • 语法

      int drv_mspi_setup(struct mspi_setup *setup)
      
    • 参数

      参数名称 描述
      setup struct mspi_setup 描述通讯的参数配置,参数包含要设定的bus number、cs片选号、通讯频率、mode等
    • 返回值

      结果 描述
      成功 返回0
      失败 返回非0值

    5.5.2 drv_mspi_transfer

    • 功能

      数据传输

    • 语法

      int drv_mspi_transfer(struct mspi_setup *setup, struct mspi_tfr *tfr, u32 length)
      
    • 参数

      参数名称 描述
      setup mspi参数配置,参数包含要设定的bus number、cs片选号、通讯频率、mode等
      tfr struct mspi_tfr 用于描述通讯的信息,包括通讯数据,位宽等
      length mspi_tfr的数量
    • 返回值

      结果 描述
      成功 返回0
      失败 返回非0值

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

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

    5.6 DEMO

    测试demo文件位于:sc/driver/sysdriver/mspi/drv/src/drv_mspi_test.c

    static int spi_ut_test(CLI_t * cli, char * p)
    {
        u32  i;
        s32  ret;
        u8   argc;
        u32  port;
        u32  cs_select;
        u32  speed;
        char *cmd;
        struct mspi_setup setup = {0};
        struct mspi_tfr transfers[2] = {0};
    
        argc = CliTokenCount(cli);
    
        if (argc != 4)
            return eCLI_PARSE_INPUT_ERROR;
    
        cmd = CliTokenPop(cli);
    
        if (CliTokenPopNum(cli, &port, 0) != eCLI_PARSE_OK)
        {
            return eCLI_PARSE_INPUT_ERROR;
        }
    
        if (CliTokenPopNum(cli, &cs_select, 0) != eCLI_PARSE_OK)
        {
            return eCLI_PARSE_INPUT_ERROR;
        }
    
        if (CliTokenPopNum(cli, &speed, 0) != eCLI_PARSE_OK)
        {
            return eCLI_PARSE_INPUT_ERROR;
        }
    
        setup.bus_num = (u8)port;
        setup.chip_select = (u8)cs_select;
        setup.max_speed_hz = speed;
        setup.mode &= ~(MSPI_SETUP_CPHA | MSPI_SETUP_CPOL);
        //mspi initial configure
        ret = drv_mspi_setup(&setup);
        if(ret)
        {
            cliPrintf("drv_mspi_setup fail : %d\n", ret);
            return eCLI_PARSE_UNKNOWN;
        }
        //read
        if (strcmp(cmd, "r") == 0)
        {
            wdata[0] = 0x03;
            wdata[1] = 0x00;
            wdata[2] = 0x00;
            wdata[3] = 0x00;
            transfers[0].tx_buf = wdata;
            transfers[0].rx_buf = NULL;
            transfers[0].len = 4;
            transfers[0].bits_per_word = 8;
            transfers[1].tx_buf = NULL;
            transfers[1].rx_buf = rdata;
            transfers[1].len = 256;
            transfers[1].bits_per_word = 8;
            ret = drv_mspi_transfer(&setup, transfers, 2);
            if (ret)
            {
                cliPrintf("drv_mspi_transfer fail : %d\n", ret);
                return eCLI_PARSE_UNKNOWN;
            }
        }
        //write
        else if (strcmp(cmd, "w") == 0)
        {
            wdata[0] = 0x06;
            transfers[0].tx_buf = wdata;
            transfers[0].rx_buf = NULL;
            transfers[0].len = 1;
            transfers[0].bits_per_word = 8;
            ret = drv_mspi_transfer(&setup, transfers, 1);
            if (ret)
            {
                cliPrintf("drv_mspi_transfer fail : %d\n", ret);
                return eCLI_PARSE_UNKNOWN;
            }
    
            wdata[0] = 0x02;
            wdata[1] = 0x00;
            wdata[2] = 0x00;
            wdata[3] = 0x00;
            for (i = 0; i < 256; i++)
            {
                wdata[i+4] = i;
            }
            transfers[0].tx_buf = wdata;
            transfers[0].rx_buf = NULL;
            transfers[0].len = 260;
            transfers[0].bits_per_word = 8;
            ret = drv_mspi_transfer(&setup, transfers, 1);
            if (ret)
            {
                cliPrintf("drv_mspi_transfer fail : %d\n", ret);
                return eCLI_PARSE_UNKNOWN;
            }
        }
        ...
        return eCLI_PARSE_OK;
    }
    

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


    图5-2 waveform diagram

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

    6. DEBUG方法

    当出现通讯异常时,可以参考如下方面进行问题调试,提供了几种较为常见的排查方向,另调试过程建议抓取波形方便分析。后续会持续补充。

    排查方向
    常见问题
    备注
    padmux 1. 无波形,触发不了 可查看寄存器值有没有下下去,padmux设置参考PADMUX章节,或padmux模块说明
    device node 子设备节点占用 参考cs-num这一小节
    时钟源 1. LA抓取数据有误; 当clock速率设置过高时,比如超过45m有可能会出现这种情况,此时可以调gpio驱动能力
    cs_ext 扩展脚功能用不了 请确认是否是旧架构,旧架构不支持扩展脚功能,详情参考cs_ext这一小节