63. SPI

63.1. 概述

  • 主要介绍先楫的SPI外设的主要驱动接口说明和调用方法。更多内容请参考 hpm_spi_drv.h 的API说明以及相关用户手册。

  • 通信方式:

    • 支持单线SPI,双线SPI,四线SPI

    • 支持主机模式和从机模式

  • 主机SCLK时钟特性:

    • 支持SPI时钟极性和相位的配置(CPOL和CPHA)

    • CS 与 SCLK 接口时序可配置

    • SPI时钟源与SCLK频率比可配置

  • 数据格式:

    • 支持MSB和LSB传输

    • 数据单位长度1~32bit

    • 主机模式下可配置命令阶段,地址阶段,dummy阶段,数据阶段。(每个阶段可使能或者禁能)

    • 从机模式下可配置纯数据模式和非纯数据模式,对于纯数据模式,从机在总线收发都是数据阶段;对于非纯数据模式,从机在总线收发都是命令(8bit)+dummy(8bit)+数据阶段.

    • 命令阶段长度固定为8bit

    • 地址阶段长度可配置8,16,24,32bit

    • 数据阶段数据长度可配置,单次传输长度可参考每个SOC的 hpm_soc_feature.hSPI_SOC_TRANSFER_COUNT_MAX 宏定义。

    • 主机模式下最多支持4路硬件CS片选。此项支持需要看SOC的 hpm_soc_ip_feature.h 是否定义 HPM_IP_FEATURE_SPI_CS_SELECT

  • 数据缓冲:

    • SPI的TX和RX有各自独立的硬件FIFO缓冲,TX最大 8 字深, 32 位宽的 FIFO 缓冲,RX最大 8 字深, 32 位宽的 FIFO 缓冲。

    • 具体深度可参考 hpm_soc_feature.hSPI_SOC_FIFO_DEPTH 宏定义或者使用 hpm_spi_drv.hspi_get_rx_fifo_sizespi_get_tx_fifo_size API接口获取

  • 中断与DMA:

    • 从机命令中断,从机模式下收到属命令时产生中断

    • SPI 输出完成时产生中断

    • TXFIFO 有效数据小于等于设置阈值时产生中断(支持DMA触发)

    • RXFIFO 有效数据大于等于设置阈值时产生中断(支持DMA触发)

    • TXFIFO 欠载时产生中断

    • RXFIFO 溢出时产生中断

63.2. SPI相关初始化

  • 需要确保SPI的时钟源已经开启,并且初始化了相关SPI外设引脚。

63.2.1. SPI SCLK时钟配置

  • 仅支持主机模式,从机模式不需要配置。

  • 使用 spi_master_timing_init API接口设置主机SCLK时钟频率。

  • 在设置主机SCLK时钟频率之前,建议使用 spi_master_get_default_timing_config API接口获取默认的SCLK时钟配置。

  • 相关结构体介绍:

    • spi_timing_config_t

      • 目前用于设置主机SCLK时钟频率的结构体。

      typedef struct {
          uint32_t clk_src_freq_in_hz; /*!< SPI时钟源频率 */
          uint32_t sclk_freq_in_hz;    /*!< SPI SCLK时钟频率 */
          uint8_t cs2sclk;             /*!< CS 有效到 SCLK 边缘最短时间。 SCLK_周期 *(CS2SCLK+1)/2 */
          uint8_t csht;                /*< CS 有效到 SCLK 有效时间。 SCLK_周期 *(CSHT+1)/2 */
      } spi_master_timing_config_t
      
      typedef struct {
          spi_master_timing_config_t master_config; /*!< SPI主机模式配置 */
      } spi_timing_config_t;
      
      • clk_src_freq_in_hz :SPI时钟源频率,单位Hz。SPI的时钟源频率可以通过 hpm_clock_drv.hclock_get_frequency API获取, SPI时钟源默认80Mhz。

      • sclk_freq_in_hz :SPI SCLK时钟频率,单位Hz。用户根据实际情况设置。设置的条件如下:

        • 时钟源频率 sclk_freq_in_hz 必须能被目标SCLK频率 clk_src_freq_in_hz 整除。

        • 分频系数必须为偶数,即 时钟源频率 clk_src_freq_in_hz / 目标SCLK频率 sclk_freq_in_hz 必须为偶数。并且分频系数不可超过510。

        • 目标SCLK时钟频率大于等于SPI时钟源时,目标SCLK频率强制等于SPI时钟源。

        • 成功设置:

          • 时钟源频率80Mhz,目标SCLK频率10Mhz,分频系数为8,即 80Mhz / 10Mhz = 8。

          • 时钟源频率60Mhz,目标SCLK频率16Mhz,分频系数为6,即 60Mhz / 10Mhz = 6。

        • 失败设置:

          • 时钟源80MHz,目标频率33MHz → 不满足整除条件

          • 时钟源80MHz,目标频率16MHz → 分频系数5(不满足分频系数为偶数)

          • 时钟源80MHz,目标频率1KHz → 分频系数800(超过510上限)

      • 如果 spi_master_timing_init API 无法满足设置的目标SCLK频率 sclk_freq_in_hz,可以调整SPI时钟源频率来适配以上的设置条件。

      • cs2sclk :CS 有效到 SCLK 边缘最短时间。 SCLK_周期 * (CS2SCLK+1) / 2。

        • 例如 cs2sclk 设置为1,SCLK_周期为10ns,即 CS 有效到 SCLK 边缘最短时间为10ns。

      • csht :CS 高电平的最短时间。 SCLK_周期 * (CSHT + 1) / 2。

        • 例如 csht 设置为1,SCLK_周期为10ns,即 CS 高电平保持最短时间为10ns。

  • 相关API接口:

    • spi_master_get_default_timing_config

      • 获取SPI主机模式默认的SCLK时钟配置。

        void spi_master_get_default_timing_config(spi_master_timing_config_t *config);
        
        • 参数说明:

        参数名

        类型

        描述

        config

        spi_master_timing_config_t*

        指向SPI主机模式SCLK时钟配置结构体的指针,包含SCLK时钟频率和相关配置信息

        • 返回值:

    • spi_master_timing_init

      • 设置SPI主机模式的SCLK时钟频率。

        hpm_stat_t spi_master_timing_init(SPI_Type *ptr, spi_master_timing_config_t *config);
        
        • 参数说明:

        参数名

        类型

        描述

        ptr

        SPI_Type*

        指向SPI控制器基地址的指针,用于指定要配置的SPI控制器。

        config

        spi_master_timing_config_t*

        指向SPI主机模式SCLK时钟配置结构体的指针,包含SCLK时钟频率和相关配置信息

        • 返回值:

          • hpm_stat_t:

            • status_success:设置成功。

            • status_invalid_argument:参数无效。

  • 举例:

    • 可参考 spi示例 的主机部分的例程。

      #include "hpm_clock_drv.h"
      #include "hpm_spi_drv.h"
      
      void spi_master_timing_init_example(void)
      {
              spi_master_timing_config_t config;
              uint32_t clock_freq_in_hz;
              /* 初始化SPI控制器引脚 ,不做举例 */
      
              /* 使能SPI时钟源 */
              clock_add_to_group(clock_spi1, 0); /* 默认80Mhz */
      
              /* 获取SPI时钟源频率 */
              clock_freq_in_hz = clock_get_frequency(clock_spi1);
      
              /* 获取SPI主机模式默认的SCLK时钟配置 */
              spi_master_get_default_timing_config(&config);
      
              /* 设置SPI主机模式的SCLK时钟频率 */
              config.clk_src_freq_in_hz = clock_freq_in_hz;
              config.sclk_freq_in_hz = 10000000; /* 设置SCLK时钟频率为10MHz */
              config.cs2sclk = 1;                /* 设置CS2SCLK为1 */
              config.csht = 1;                   /* 设置CSHT为1 */
              if (spi_master_timing_init(HPM_SPI1, &config) != status_success) {
                  /* 设置失败 */
                  printf("spi_master_timing_init failed\n");
              }
      }
      

63.2.2. SPI 格式配置

  • 支持主机模式和从机模式。

  • 主要配置SPI数据格式,包括数据位宽,数据传输顺序,命令位宽,地址位宽,dummy位宽,数据位宽。

  • 在主机模式下,设置SPI格式配置之前,建议使用 spi_master_get_default_format_config API接口获取默认的SPI格式配置。

  • 在从机模式下,设置SPI格式配置之前,建议使用 spi_slave_get_default_format_config API接口获取默认的SPI格式配置。

  • 相关结构体介绍:

    • spi_master_format_config_t

      • 目前用于设置主机模式下的SPI格式配置的结构体。

         /*!< SPI格式配置结构体 */
        typedef struct {
            spi_master_format_config_t master_config;  /*!< SPI主机模式配置 */
            spi_common_format_config_t common_config;  /*!< SPI通用配置 */
        } spi_format_config_t;
        
        /*!< SPI主机模式格式配置结构体 */
        typedef struct {
            uint8_t addr_len_in_bytes;  /*!< 地址长度,单位字节 */
        } spi_master_format_config_t;
        
        /*!< SPI通用格式配置结构体 */
        typedef struct {
            uint8_t data_len_in_bits;  /*!< 数据长度,单位位 */
            bool data_merge;           /*!< 数据合并模式 */
            bool mosi_bidir;           /*!< MOSI双向模式 */
            bool lsb;                  /*!< LSB模式 */
            uint8_t mode;              /*!< SPI模式 */
            uint8_t cpol;              /*!< 时钟极性 */
            uint8_t cpha;              /*!< 时钟相位 */
        } spi_common_format_config_t;
        
      • spi_format_config_t 包括了 spi_master_format_config_tspi_common_format_config_t 两个结构体。

      • spi_master_format_config_t 用于设置主机模式下的SPI格式配置,包括地址长度。

        • addr_len_in_bytes :地址长度,单位字节。用于指定地址阶段的地址长度。如果地址阶段禁能,则不需要设置该参数。

      • spi_common_format_config_t 用于设置SPI通用格式配置,支持主从机模式,包括数据长度,数据合并模式,MOSI双向模式,LSB模式,SPI模式,时钟极性和时钟相位。

        • data_len_in_bits :单位数据长度,单位位。用于指定数据阶段的数据单位长度,支持1到32bit。

        • data_merge :数据合并模式。仅支持单位数据长度为8bit时有效,并且数据大小必须是4的整数倍。在接收和发送时将每次传输的8bit数据合并为一个32bit数据,提升接收和发送效率。

        • mosi_bidir :MOSI双向模式。如果设置为true,则MOSI双向模式,否则为单向模式。仅支持SPI单线模式。开启双向模式后,MOSI引脚是双向信号。

        • lsb :LSB模式。如果设置为true,则LSB模式,否则为MSB模式。

        • mode :设置SPI主机模式或者从机模式,使用 spi_master_mode 或者 spi_slave_mode 枚举类型。

        • cpol :时钟极性。用于指定SPI时钟极性,使用 spi_sclk_low_idle 或者 spi_sclk_high_idle 枚举类型。

        • cpha :时钟相位。用于指定SPI时钟相位,使用 spi_sclk_sampling_odd_clk_edges 或者 spi_sclk_sampling_even_clk_edges 枚举类型。

  • 相关API接口:

    • spi_master_get_default_format_config

      • 获取SPI主机模式默认的SPI格式配置。

        void spi_master_get_default_format_config(spi_format_config_t *config);
        
        • 参数说明:

          参数名

          类型

          描述

          config

          spi_format_config_t*

          指向SPI主机模式格式配置结构体的指针,包含SPI格式配置信息

        • 返回值:

    • spi_slave_get_default_format_config

      • 获取SPI从机模式默认的SPI格式配置。

        void spi_slave_get_default_format_config(spi_format_config_t *config);
        
        • 参数说明:

          参数名

          类型

          描述

          config

          spi_format_config_t*

          指向SPI从机模式格式配置结构体的指针,包含SPI格式配置信息

        • 返回值:

    • spi_format_init

      • 设置SPI的格式配置。

        void spi_format_init(SPI_Type *ptr, spi_format_config_t *config);
        
        • 参数说明:

          参数名

          类型

          描述

          ptr

          SPI_Type*

          指向SPI控制器基地址的指针,用于指定要配置的SPI控制器。

          config

          spi_format_config_t*

          指向SPI格式配置结构体的指针,包含SPI格式配置信息

        • 返回值:

  • 举例:

    • 可参考 spi示例 的例程。

    • 主机模式下,设置SPI格式配置:LSB模式,数据长度为8bit,时钟极性为低,时钟相位为奇数(CPOL = 0, CPHA = 0).

      #include "hpm_clock_drv.h"
      #include "hpm_spi_drv.h"
      void spi_format_init_example(void)
      {
          spi_format_config_t format_config;
          /* 初始化SPI控制器引脚,不做举例.. */
          /* 使能SPI时钟源,不做举例.. */
          /* 设置SPI主机模式的SCLK时钟频率,不做举例..*/
      
          /* 获取SPI主机模式默认的SPI格式配置 */
          spi_master_get_default_format_config(&format_config);
          format_config.common_config.data_len_in_bits = 8; /* 设置数据长度为8bit */
          format_config.common_config.lsb = true;           /* 设置LSB模式 */
          format_config.common_config.cpol = spi_sclk_low_idle; /* 设置时钟极性为低 */
          format_config.common_config.cpha = spi_sclk_sampling_odd_clk_edges; /* 设置时钟相位为奇数 */
          format_config.common_config.mode = spi_master_mode; /* 设置SPI主机模式 */
          /* 设置SPI格式配置 */
          spi_format_init(HPM_SPI1, &format_config);
      }
      
    • 主机模式下,设置SPI格式配置:MSB模式,地址阶段的地址长度为3字节(24bit地址),数据长度为16bit,时钟极性为高,时钟相位为偶数(CPOL = 1, CPHA = 1).

      #include "hpm_clock_drv.h"
      #include "hpm_spi_drv.h"
      void spi_format_init_example(void)
      {
          spi_format_config_t format_config;
          /* 初始化SPI控制器引脚,不做举例.. */
          /* 使能SPI时钟源,不做举例.. */
          /* 设置SPI主机模式的SCLK时钟频率,不做举例..*/
      
          /* 获取SPI主机模式默认的SPI格式配置 */
          spi_master_get_default_format_config(&format_config);
          format_config.common_config.data_len_in_bits = 16; /* 设置数据长度为16bit */
          format_config.common_config.lsb = false;           /* 设置MSB模式 */
          format_config.common_config.cpol = spi_sclk_high_idle; /* 设置时钟极性为高 */
          format_config.common_config.cpha = spi_sclk_sampling_even_clk_edges; /* 设置时钟相位为偶数 */
          format_config.common_config.mode = spi_master_mode; /* 设置SPI主机模式 */
          format_config.master_config.addr_len_in_bytes = 3; /* 设置地址阶段的地址长度为3字节 */
          /* 设置SPI格式配置 */
          spi_format_init(HPM_SPI1, &format_config);
      }
      
    • 从机模式下,设置SPI格式配置:MSB模式,数据长度为8bit,时钟极性为低,时钟相位为奇数(CPOL = 0, CPHA = 0).

      #include "hpm_clock_drv.h"
      #include "hpm_spi_drv.h"
      void spi_format_init_example(void)
      {
          spi_format_config_t format_config;
          /* 初始化SPI控制器引脚,不做举例.. */
          /* 使能SPI时钟源,不做举例.. */
          /* 设置SPI主机模式的SCLK时钟频率,不做举例..*/
      
          /* 获取SPI从机模式默认的SPI格式配置 */
          spi_slave_get_default_format_config(&format_config);
          format_config.common_config.data_len_in_bits = 8; /* 设置数据长度为8bit */
          format_config.common_config.lsb = false;           /* 设置MSB模式 */
          format_config.common_config.cpol = spi_sclk_low_idle; /* 设置时钟极性为低 */
          format_config.common_config.cpha = spi_sclk_sampling_odd_clk_edges; /* 设置时钟相位为奇数 */
          format_config.common_config.mode = spi_slave_mode; /* 设置SPI从机模式 */
          /* 设置SPI格式配置 */
          spi_format_init(HPM_SPI1, &format_config);
      }
      

63.3. SPI数据传输

  • 相关结构体介绍:

    /*!< SPI传输配置结构体 */
    typedef struct {
        bool cmd_enable;         /* !< 命令阶段使能 */
        bool addr_enable;        /*!< 地址阶段使能 */
        uint8_t addr_phase_fmt;  /*!< 地址阶段格式 */
        bool token_enable;       /*!< 令牌阶段使能 */
        uint8_t token_value;    /*!< 令牌值 */
    } spi_master_control_config_t;
    
    /*!< SPI从机模式控制配置结构体 */
    typedef struct {
        bool slave_data_only;    /*!< 从机纯数据模式 */
    } spi_slave_control_config_t;
    
    /*!< SPI传输配置结构体 */
    typedef struct {
        bool tx_dma_enable;     /*!< 发送DMA使能 */
        bool rx_dma_enable;     /*!< 接收DMA使能 */
        uint8_t trans_mode;     /*!< 传输模式 */
        uint8_t data_phase_fmt; /*!< 数据阶段格式 */
        uint8_t dummy_cnt;      /*!< dummy计数 */
    #if defined(HPM_IP_FEATURE_SPI_CS_SELECT) && (HPM_IP_FEATURE_SPI_CS_SELECT == 1)  /* 当前SOC支持硬件CS */
        uint8_t cs_index;       /*!< CS索引 */
    #endif
    } spi_common_control_config_t;
    
    /*!< SPI控制配置结构体 */
    typedef struct {
        spi_master_control_config_t master_config;  /*!< SPI主机模式控制配置 */
        spi_slave_control_config_t  slave_config;   /*!< SPI从机模式控制配置 */
        spi_common_control_config_t common_config;  /*!< SPI通用控制配置 */
    } spi_control_config_t;
    
  • 功能说明: - 从 结构体介绍 中可以概括以下功能: - 支持主机模式和从机模式。 - 支持DMA传输和轮询传输。

    • 主机模式下,SPI数据传输顺序是命令阶段 + 地址阶段 + 令牌阶段 + dummy阶段 + 数据阶段。各个阶段可使能或者禁能。

      • 命令阶段:

        • 命令阶段使能:cmd_enable 设置为true,命令阶段使能。

        • 命令阶段禁能:cmd_enable 设置为false,命令阶段禁能。

        • 命令阶段长度固定为8bit,命令值可通过 spi_write_command API接口设置。

        • 无论是否使能和禁能命令阶段,开启传输都必须设置命令值,可通过 `spi_write_command` API接口设置

      • 地址阶段:

        • 地址阶段使能:addr_enable 设置为true,地址阶段使能。

        • 地址阶段禁能:addr_enable 设置为false,地址阶段禁能。

        • 地址阶段的地址长度可配置8,16,24,32bit。

        • 地址阶段的地址长度每次传输都固定不变,可通过设置 spi_format_init 中的 addr_len_in_bytes 成员进行设置。

        • 地址阶段的地址长度值可通过 spi_write_address API接口设置。

        • 地址阶段的地址传输支持SPI,DSPI,QSPI三种格式,可 addr_phase_fmt 成员进行设置。 - addr_phase_fmt

          • spi_address_phase_format_single_io_mode :SPI地址格式。

          • spi_address_phase_format_dualquad_io_mode :DSPI或者QSPI地址格式。与 data_phase_fmt 成员一致的设置。比如 data_phase_fmt 设置为 spi_quad_io_mode QSPI格式,那么地址阶段的地址格式也为QSPI格式 。

      • 令牌阶段:

        • 令牌阶段使能:可通过 spi_master_enable_token_transfer API设置。

        • 令牌阶段禁能:可通过 spi_master_disable_token_transfer API设置。

        • 令牌阶段的令牌值可通过 spi_master_set_token_value API接口设置,只支持0x00和0x69两个令牌值。

      • dummy阶段:

        • dummy_cnt :设置dummy数,单位是 spi_format_init 中的 data_len_in_bits 。比如 data_len_in_bits 设置为8bit, dummy_cnt 设置为2,那么dummy阶段的长度为16bit。

        • 只在 trans_mode 设置为 spi_trans_write_dummy_readspi_trans_read_dummy_writespi_trans_dummy_writespi_trans_dummy_read 时有效。

      • 数据阶段:

        • data_phase_fmt :设置数据阶段的格式,支持十种数据传输格式。可在 spi_trans_mode_t 枚举类型中查看。

          • spi_trans_write_read_together:同时读写传输顺序,仅支持SPI模式。比如在SPI模式下,SPI控制器在发送数据时,也会同时接收数据。

          • spi_trans_write_only:仅发送数据。

          • spi_trans_read_only:仅接收数据。

          • spi_trans_write_read:先发送数据再接收数据。

          • spi_trans_read_write:先接收数据再发送数据。

          • spi_trans_write_dummy_read:先发送数据再发送dummy再接收数据。

          • spi_trans_read_dummy_write:先接收数据再接收dummy再发送数据。

          • spi_trans_no_data: 无数据传输。仅传输命令和地址。需要开启命令和地址阶段。

          • spi_trans_dummy_write:先发送dummy再发送数据

          • spi_trans_dummy_read:先发送dummy再接收数据

    • 从机模式下,SPI数据传输分为纯数据模式和非纯数据模式。

      • 纯数据模式:

        • 从机纯数据模式:slave_data_only 设置为true,从机纯数据模式。

        • 从机纯数据模式下,从机在总线收发都是数据阶段。仅支持 spi_trans_write_read_together 传输模式。

      • 非纯数据模式:

        • 从机非纯数据模式:slave_data_only 设置为false,从机非纯数据模式。

        • 从机非纯数据模式下,从机在总线收发都是命令(8bit)+dummy(8bit)+数据阶段。

  • 相关API接口:

    • 轮询传输数据API。

      hpm_stat_t spi_transfer(SPI_Type *ptr,
                  spi_control_config_t *config,
                  uint8_t *cmd, uint32_t *addr,
                  uint8_t *wbuff, uint32_t wcount, uint8_t *rbuff, uint32_t rcount);
      
      • 参数说明:

      参数名

      类型

      描述

      ptr

      SPI_Type*

      指向SPI控制器基地址的指针,用于指定要配置的SPI控制器。

      config

      spi_control_config_t*

      指向SPI控制配置结构体的指针,包含SPI控制配置信息

      cmd

      uint8_t*

      指向命令值的指针,用于指定命令值。

      addr

      uint32_t*

      指向地址值的指针,用于指定地址值。

      wbuff

      uint8_t*

      指向发送缓冲区的指针,用于指定发送缓冲区。

      wcount

      uint32_t

      发送数据的字节数。

      rbuff

      uint8_t*

      指向接收缓冲区的指针,用于指定接收缓冲区。

      rcount

      uint32_t

      接收数据的字节数。

      • 返回值:

        • status_success:传输成功。

        • status_invalid_argument:参数无效。

        • status_timeout:传输超时。

        • status_fail:传输错误。

    • 提示:

      • cmd_enable 设置为false, 则 cmd 可设置为NULL。

      • addr_enable 设置为false, 则 addr 可设置为NULL。

      • data_phase_fmt 设置为 spi_trans_no_data 时, wcountrcount 可设置为0。

    举例:

    • 主机模式下,单线SPI,开启命令阶段,命令值为0x01,地址阶段使能(SPI格式),地址值为0x02,传输模式为 spi_trans_write_read_together,数据收发长度为5字节。

      #include "hpm_clock_drv.h"
      #include "hpm_spi_drv.h"
      void spi_transfer_example(void)
      {
          spi_control_config_t control_config;
          uint8_t cmd = 0x01;
          uint32_t addr = 0x02;
          uint8_t wbuff[5] = {0x01, 0x02, 0x03, 0x04, 0x05};
          uint8_t rbuff[5] = {0};
          /* 初始化SPI控制器引脚,不做举例.. */
          /* 使能SPI时钟源,不做举例.. */
          /* 设置SPI主机模式的SCLK时钟频率,不做举例..*/
          /* 设置SPI主机模式的SPI格式配置,不做举例..*/
          spi_master_get_default_format_config(&control_config);
          control_config.common_config.trans_mode = spi_trans_write_read_together; /* 设置传输模式为同时读写传输顺序 */
          control_config.common_config.data_phase_fmt = spi_single_io_mode; /* 设置数据阶段的格式为同时读写传输顺序 */
          control_config.master_config.cmd_enable = true; /* 开启命令阶段 */
          control_config.master_config.addr_enable = true; /* 开启地址阶段 */
          control_config.master_config.addr_phase_fmt = spi_address_phase_format_single_io_mode; /* 设置地址阶段的格式为SPI格式 */
          if (spi_transfer(HPM_SPI1, &control_config, &cmd, &addr, wbuff, 5, rbuff, 5)!= status_success) {
              /* 传输失败 */
              printf("spi_transfer failed\n");
          }
      }
      
    • 主机模式下,四线QSPI,禁能命令阶段以及地址阶段,传输模式为 spi_trans_write_dummy_read,数据发送长度为5字节,数据接收长度为10字节,dummy数为2。

    void spi_transfer_example(void)
    {
        spi_control_config_t control_config;
        uint8_t wbuff[5] = {0x01, 0x02, 0x03, 0x04, 0x05};
        uint8_t rbuff[10] = {0};
        /* 初始化SPI控制器引脚,不做举例.. */
        /* 使能SPI时钟源,不做举例.. */
        /* 设置SPI主机模式的SCLK时钟频率,不做举例..*/
        /* 设置SPI主机模式的SPI格式配置,不做举例..*/
        spi_master_get_default_format_config(&control_config);
        control_config.common_config.trans_mode = spi_trans_write_dummy_read; /* 设置传输模式为先发送dummy再接收数据 */
        control_config.common_config.data_phase_fmt = spi_quad_io_mode; /* 设置数据阶段的格式为四线传输顺序 */
        control_config.master_config.cmd_enable = false; /* 禁能命令阶段 */
        control_config.master_config.addr_enable = false; /* 禁能地址阶段 */
        control_config.common_config.dummy_cnt = spi_dummy_count_2; /* 设置dummy数为2 */
        if (spi_transfer(HPM_SPI1, &control_config, NULL, NULL, wbuff, 5, rbuff, 10)!= status_success) {
            /* 传输失败 */
            printf("spi_transfer failed\n");
        }
    }
    
    • 从机模式下,四线QSPI,从机纯数据模式,传输模式为 spi_trans_write_read_together,数据收发长度为5字节。

    void spi_transfer_example(void)
    {
        spi_control_config_t control_config;
        uint8_t wbuff[5] = {0x01, 0x02, 0x03, 0x04, 0x05};
        uint8_t rbuff[5] = {0};
        /* 初始化SPI控制器引脚,不做举例.. */
        /* 使能SPI时钟源,不做举例.. */
        /* 设置SPI从机模式的SPI格式配置,不做举例..*/
        spi_slave_get_default_format_config(&control_config);
        control_config.common_config.trans_mode = spi_trans_write_read_together; /* 设置传输模式为同时读写传输顺序 */
        control_config.common_config.data_phase_fmt = spi_quad_io_mode; /* 设置数据阶段的格式为同时读写传输顺序 */
        control_config.slave_config.slave_data_only = true; /* 设置从机纯数据模式 */
        if (spi_transfer(HPM_SPI1, &control_config, NULL, NULL, wbuff, 5, rbuff, 5)!= status_success) {
            /* 传输失败 */
            printf("spi_transfer failed\n");
        }
    }
    
    • DMA传输初始化设置API

      • 用于设置SPI使用DMA传输的初始化设置,包括使能发送或者接收DMA,设置SPI控制器的传输长度等。

      • 如果每次传输的长度不变,则只需要初始化一次即可。主机模式下,下次传输可使用`spi_write_command` API接口设置。

      • TXFIFO 有效数据小于等于设置阈值时,会触发DMA传输。阈值设置可以通过 spi_set_tx_fifo_threshold API接口设置。

      • RXFIFO 有效数据大于等于设置阈值时,会触发DMA传输。阈值设置可以通过 spi_set_rx_fifo_threshold API接口设置。

      • 使用DMA收发API参考 SPI组件 spi_component

      hpm_stat_t spi_setup_dma_transfer(SPI_Type *ptr, spi_control_config_t *config,
                  uint8_t *cmd, uint32_t *addr, uint32_t wcount, uint32_t rcount);
      
      • 参数说明:

        参数名

        类型

        描述

        ptr

        SPI_Type*

        指向SPI控制器基地址的指针,用于指定要配置的SPI控制器。

        config

        spi_control_config_t*

        指向SPI控制配置结构体的指针,包含SPI控制配置信息

        cmd

        uint8_t*

        指向命令值的指针,用于指定命令值。

        addr

        uint32_t*

        指向地址值的指针,用于指定地址值。

        wcount

        uint32_t

        发送数据的字节数。

        rcount

        uint32_t

        接收数据的字节数。

      • 返回值:

        • status_success:DMA传输初始化设置成功。

        • status_invalid_argument:参数无效。

        • status_fail:DMA传输初始化设置失败。

      举例:

      • 主机模式下,双线DSPI,开启命令阶段,命令值为0x01,地址阶段使能(DSPI格式),地址值为0x02,传输模式为 spi_trans_write_read,数据收发长度为100字节。

        #include "hpm_clock_drv.h"
        #include "hpm_spi_drv.h"
        void spi_setup_dma_transfer_example(void)
        {
            spi_control_config_t control_config;
            uint8_t cmd = 0x01;
            uint32_t addr = 0x02;
            uint8_t wbuff[100] = {0};
            uint8_t rbuff[100] = {0};
            /* 初始化SPI控制器引脚,不做举例.. */
            /* 使能SPI时钟源,不做举例.. */
            /* 设置SPI主机模式的SCLK时钟频率,不做举例..*/
            /* 设置SPI主机模式的SPI格式配置,不做举例..*/
            spi_master_get_default_format_config(&control_config);
            control_config.common_config.trans_mode = spi_trans_write_read; /* 设置传输模式为先发送数据再接收数据 */
            control_config.common_config.data_phase_fmt = spi_dual_io_mode; /* 设置数据阶段的格式为双线传输顺序 */
            control_config.master_config.cmd_enable = true; /* 开启命令阶段 */
            control_config.master_config.addr_enable = true; /* 开启地址阶段 */
            control_config.master_config.addr_phase_fmt = spi_address_phase_format_dualquad_io_mode; /* 设置地址阶段的格式为DSPI格式 */
            if (spi_setup_dma_transfer(HPM_SPI1, &control_config, &cmd, &addr, 100, 100)!= status_success) {
                /* DMA传输初始化设置失败 */
                printf("spi_setup_dma_transfer failed\n");
                return;
            }
            /* 配置DMA的通道,DMA源地址和设备地址等等,启动DMA传输,不做举例,参考 `spi示例`的dma相关 */
        }
        
      • 主机模式下,四线QSPI,禁能命令阶段和地址阶段,传输模式为 spi_trans_write_only,数据发送长度为100字节。

        void spi_setup_dma_transfer_example(void)
        {
            spi_control_config_t control_config;
            uint8_t wbuff[100] = {0};
            /* 初始化SPI控制器引脚,不做举例.. */
            /* 使能SPI时钟源,不做举例.. */
            /* 设置SPI主机模式的SCLK时钟频率,不做举例..*/
            /* 设置SPI主机模式的SPI格式配置,不做举例..*/
            spi_master_get_default_format_config(&control_config);
            control_config.common_config.trans_mode = spi_trans_write_only; /* 设置传输模式为先发送数据 */
            control_config.common_config.data_phase_fmt = spi_quad_io_mode; /* 设置数据阶段的格式为四线传输顺序 */
            control_config.master_config.cmd_enable = false; /* 禁能命令阶段 */
            control_config.master_config.addr_enable = false; /* 禁能地址阶段 */
            if (spi_setup_dma_transfer(HPM_SPI1, &control_config, NULL, NULL, 100, 0)!= status_success) {
                /* DMA传输初始化设置失败 */
                printf("spi_setup_dma_transfer failed\n");
                return;
            }
            /* 配置DMA的通道,DMA源地址和设备地址等等,启动DMA传输,不做举例,参考 `spi示例`的dma相关 */
        }
        
      • 从机模式下,四线QSPI,从机纯数据模式,传输模式为 spi_trans_write_read_together,数据收发长度为100字节。

      void spi_setup_dma_transfer_example(void)
      {
          spi_control_config_t control_config;
          uint8_t wbuff[100] = {0};
          uint8_t rbuff[100] = {0};
          /* 初始化SPI控制器引脚,不做举例.. */
          /* 使能SPI时钟源,不做举例.. */
          /* 设置SPI从机模式的SPI格式配置,不做举例..*/
          spi_slave_get_default_format_config(&control_config);
          control_config.common_config.trans_mode = spi_trans_write_read_together; /* 设置传输模式为同时读写传输顺序 */
          control_config.common_config.data_phase_fmt = spi_quad_io_mode; /* 设置数据阶段的格式为四线传输顺序 */
          control_config.slave_config.slave_data_only = true; /* 设置从机纯数据模式 */
          if (spi_setup_dma_transfer(HPM_SPI1, &control_config, NULL, NULL, 100, 100)!= status_success) {
              /* DMA传输初始化设置失败 */
              printf("spi_setup_dma_transfer failed\n");
              return;
          }
          /* 配置DMA的通道,DMA源地址和设备地址等等,启动DMA传输,不做举例,参考 `spi示例`的dma相关 */
      }
      

63.4. 中断

  • 需要打开plic控制器对应的SPI中断,使用 intc_m_enable_irq API接口打开SPI中断。

  • 需要在plic控制器中配置SPI中断的优先级,使用 intc_m_enable_irq_with_priority API接口配置SPI中断的优先级。

  • 每次对应的中断事件发生,需要在中断处理函数中调用 spi_clear_interrupt_status API接口清除中断标志。

  • 每次对应的中断事件发生,可以在中断处理函数中调用 spi_get_interrupt_status API接口获取中断标志。

  • SPI外设支持以下中断,可以从 spi_interrupt_t 枚举类型中查看。

    typedef enum {
        spi_rx_fifo_overflow_int  = SPI_INTREN_RXFIFOORINTEN_MASK,   /* 接收FIFO溢出中断 */
        spi_tx_fifo_underflow_int = SPI_INTREN_TXFIFOURINTEN_MASK,   /* 发送FIFO下溢中断 */
        spi_rx_fifo_threshold_int = SPI_INTREN_RXFIFOINTEN_MASK,     /* 接收FIFO阈值中断 */
        spi_tx_fifo_threshold_int = SPI_INTREN_TXFIFOINTEN_MASK,     /* 发送FIFO阈值中断 */
        spi_end_int               = SPI_INTREN_ENDINTEN_MASK,        /* 传输结束中断 */
        spi_slave_cmd_int         = SPI_INTREN_SLVCMDEN_MASK,        /* 从机命令中断 */
    #if defined(HPM_IP_FEATURE_SPI_CS_EDGE_DETECT_FOR_SLAVE) && (HPM_IP_FEATURE_SPI_CS_EDGE_DETECT_FOR_SLAVE == 1)  /* 如果当前SOC支持硬件CS边沿检测 */
        spi_slave_cs_edge_falling_int = SPI_INTREN_CS_NEGEN_MASK,    /* 从机CS下降沿中断 */
        spi_slave_cs_edge_rising_int = SPI_INTREN_CS_POSEN_MASK,     /* 从机CS上升沿中断 */
    #endif
    } spi_interrupt_t;
    
    • 其中,spi_rx_fifo_overflow_intspi_tx_fifo_underflow_intspi_slave_cmd_intspi_slave_cs_edge_falling_intspi_slave_cs_edge_rising_int 仅支持从机模式。

    • spi_rx_fifo_threshold_int 当spi_set_rx_fifo_threshold设置的阈值大于等于接收FIFO有效数据时,会触发中断。比如SPI的单位宽度为8bit,spi_set_rx_fifo_threshold设置的阈值为5字节,当接收FIFO有效数据大于等于5字节,会触发中断。此时可以在中断处理函数中读取接收FIFO的数据。

    • spi_tx_fifo_threshold_int 当spi_set_tx_fifo_threshold设置的阈值小于等于发送FIFO剩余空间时,会触发中断。比如SPI的单位宽度为8bit,spi_set_tx_fifo_threshold设置的阈值为5字节,当发送FIFO剩余空间小于等于5字节,会触发中断。此时可以在中断处理函数中写入发送FIFO的数据。

  • 相关API接口:

    • 使能SPI中断

      void spi_enable_interrupt(SPI_Type *ptr, uint32_t mask);
      
      • 参数说明:

        参数名

        类型

        描述

        ptr

        SPI_Type*

        指向SPI控制器基地址的指针,用于指定要配置的SPI控制器。

        mask

        uint32_t

        中断掩码,用于指定要使能的中断。对应的中断掩码可以从 spi_interrupt_t 枚举类型中查看。

      • 返回值:

    • 禁用SPI中断

      void spi_disable_interrupt(SPI_Type *ptr, uint32_t mask);
      
      • 参数说明:

      参数名

      类型

      描述

      ptr

      SPI_Type*

      指向SPI控制器基地址的指针,用于指定要配置的SPI控制器。

      mask

      uint32_t

      中断掩码,用于指定要禁用的中断。对应的中断掩码可以从 spi_interrupt_t 枚举类型中查看。

      • 返回值:

63.5. SPI示例