完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第72章       STM32H7的SPI总线基础知识和HAL库API

本章节为大家讲解SPI(Serial peripheral interface)总线的基础知识和对应的HAL库API。

目录

第72章       STM32H7的SPI总线基础知识和HAL库API

72.1 初学者重要提示

72.2 SPI总线基础知识

72.2.1 SPI总线的硬件框图

72.2.2 SPI接口的区别和时钟源(SPI1到SPI6)

72.2.3 SPI总线全双工,单工和半双工通信

72.2.4 SPI总线星型拓扑

72.2.5 SPI总线通信格式

72.3 SPI总线的HAL库用法

72.3.1 SPI总线结构体SPI_TypeDef

72.3.2 SPI总线初始化结构体SPI_InitTypeDef

72.3.3 SPI总线句柄结构体SPI_HandleTypeDef

72.4 SPI总线源文件stm32h7xx_hal_spi.c

72.4.1 函数HAL_SPI_Init

72.4.2 函数HAL_SPI_DeInit

72.4.3 函数HAL_SPI_TransmitReceive

72.4.4 函数HAL_SPI_TransmitReceive_IT

72.4.5 函数HAL_SPI_TransmitReceive_DMA

72.5 总结


72.1 初学者重要提示

  1. STM32H7的SPI支持4到32bit数据传输,而STM32F1和F4系列仅支持8bit或者16bit。
  2. STM32H7的主频400MHz时,SPI1, 2, 3最高通信时钟是100MHz,而SPI4, 5, 6是50MHz。
  3. STM32H7的MISO和MOSI引脚功能可以互换,使用比较灵活。
  4. SPI总线的片选引脚SS在单一的主从器件配置下是可选的,一般情况下可以不使用。
  5. 搜集了几篇质量比较高的SPI总线介绍帖:http://www.armbbs.cn/forum.php?mod=viewthread&tid=96788。

72.2 SPI总线基础知识

72.2.1 SPI总线的硬件框图

认识一个外设,最好的方式就是看它的框图,方便我们快速的了解SPI的基本功能,然后再看手册了解细节。

通过这个框图,我们可以得到如下信息:

  • spi_wkup输出

低功耗唤醒信号。

  • spi_it输出

spi的中断请求信号。

  • spi_tx_dma
  • spi_rx_dma

spi的DMA发送和接收请求信号。

  • spi_pclk

为寄存器提供时钟。

  • spi_ker_ck

为spi内核时钟。

  • SCK(CK),Serial Clock

此引脚在主机模式下用于时钟输出,从机模式下用于时钟输入。

  • MISO(SDI),Master In / Slave Out data

此引脚在从机模式下用于发送数据,主机模式下接收数据。

  • MOSI(SDO), Master Out / Slave In data

此引脚在从机模式下用于数据接收,主机模式下发送数据。

  • SS(WS), Slave select pin

根据SPI和SS设置,此引脚可用于:

a. 选择三个从器件进行通信。

b. 同步数据帧。

c. 检测多个主器件之间是否存在冲突。

通过这个框图还要认识到一点,SPI有三个时钟域,分别是寄存器所在的ABP总线时钟域,内核时钟发生器时钟域以及内核时钟发生器分频后的串行时钟域。

72.2.2 SPI接口的区别和时钟源(SPI1到SPI6)

这个知识点在初学的时候容易忽视,所以我们这里整理下。

  • SPI1到SPI6的区别

    • SPI1,SPI2和SPI3支持4到32bit数据传输,SPI4,SPI5和SPI6是4到16bit数据传输。
    • SPI1,SPI2和SPI3的FIFO大小是16*8bit,而SPI4,SPI5和SPI6的FIFO大小是8*8bit。

  • SPI1到SPI6的所在的总线(对应SPI框图的SPI_CLK时钟域)

SPI1,SPI4和SPI5在APB2总线,SPI2,SPI3在APB1总线,SPI6在APB4总线。注意,SPI的最高时钟不是由这些总线决定的。

  • SPI1到SPI6的支持的最高时钟(对应SPI框图的SPI_KER_CK)

STM32H7主频在400MHz下,SPI1,SPI2和SPI3的最高时钟是200MHz,而SPI4,5,6是100MHz, 以SPI1为了,可以选择的时钟源如下:

这里特别注意一点,SPI工作时最少选择二分频,也就是说SPI1,2,3实际通信时钟是100MHz,而SPI4,5,6是50MHz。

72.2.3 SPI总线全双工,单工和半双工通信

片选信号SS在单一的主从器件配置下是可选的,一般情况下可以不使用。但需要同步数据流,或者用于TI模式时需要此信号。

  •   全双工通信

全双工就是主从器件之间同时互传数据,SPI总线的全双工模式接线方式如下:

关于这个接线图要认识到以下几点:

    • 注意接线方式,对于主器件来说MISO引脚就是输入端,从器件的MISO是输出端,即Master In / Slave Out data。MOSI也是同样道理。
    • 每个时钟信号SCK的作用了,主器件的MISO引脚接收1个bit数据,MOSI引脚输出1个bit数据。
    • 这种单一的主从接线模式下,SS引脚可以不使用。
  •   半双工通信

半双工就是同一个时刻只能为一个方向传输数据,SPI总线的半工模式接线方式如下:

关于这个接线图要认识到以下几点:

    • 更改通信方式时,要先禁止SPI。
    • 主器件的MISO和从器件的MISO不使用,可以继续用作标准GPIO。
    • 1KΩ的接线电阻很有必要,因为当主器件和从器件的通信方向不是同步变化时,容易出现其中一个输出低电平,另一个输出高电平,造成短路。
    • 这种单一的主从接线模式下,SS引脚可以不使用。
  •   单工模式

单工就是只有一种通信方向,即发送或者接收,SPI总线的全双工模式接线方式如下:

关于这个接线图要认识到以下几点:

  • 未用到的MOSI或者MISO可以用作标准GPIO。
  • 这种单一的主从接线模式下,SS引脚可以不使用。

72.2.4 SPI总线星型拓扑

SPI总线星型拓扑用到的地方比较多,V7开发板就是用的星型拓扑外接多种SPI器件:

关于这个接线图,有以下几点需要大家了解:

  • 主器件的SS引脚不使用,使用通用GPIO控制。为每个器件配一个SS引脚,方便单独片选控制。
  • 从器件的MISO引脚要配置为复用开漏输出(很多外部芯片在未片选时,数据引脚是呈现高阻态)。

72.2.5 SPI总线通信格式

SPI总线主要有四种通信格式,由CPOL时钟极性和CPHA时钟相位控制:

四种通信格式如下:

  • 当CPOL = 1, CPHA = 1时

SCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据。

  • 当CPOL = 0, CPHA = 1时

SCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据。

  • 当CPOL = 1, CPHA = 0时

SCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据。

  • 当CPOL = 1, CPHA = 0时

SCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据。

72.3 SPI总线的HAL库用法

72.3.1 SPI总线结构体SPI_TypeDef

SPI总线相关的寄存器是通过HAL库中的结构体SPI_TypeDef定义的,在stm32h743xx.h中可以找到这个类型定义:

typedef struct
{__IO uint32_t CR1;           /*!< SPI/I2S Control register 1,                      Address offset: 0x00 */__IO uint32_t CR2;           /*!< SPI Control register 2,                          Address offset: 0x04 */__IO uint32_t CFG1;          /*!< SPI Configuration register 1,                    Address offset: 0x08 */__IO uint32_t CFG2;          /*!< SPI Configuration register 2,                    Address offset: 0x0C */__IO uint32_t IER;           /*!< SPI/I2S Interrupt Enable register,               Address offset: 0x10 */__IO uint32_t SR;            /*!< SPI/I2S Status register,                         Address offset: 0x14 */__IO uint32_t IFCR;          /*!< SPI/I2S Interrupt/Status flags clear register,   Address offset: 0x18 */uint32_t      RESERVED0;     /*!< Reserved, 0x1C                                                        */__IO uint32_t TXDR;          /*!< SPI/I2S Transmit data register,                  Address offset: 0x20 */uint32_t      RESERVED1[3];  /*!< Reserved, 0x24-0x2C                                                   */__IO uint32_t RXDR;          /*!< SPI/I2S Receive data register,                   Address offset: 0x30 */uint32_t      RESERVED2[3];  /*!< Reserved, 0x34-0x3C                                                   */__IO uint32_t CRCPOLY;       /*!< SPI CRC Polynomial register,                     Address offset: 0x40 */__IO uint32_t TXCRC;         /*!< SPI Transmitter CRC register,                    Address offset: 0x44 */__IO uint32_t RXCRC;         /*!< SPI Receiver CRC register,                       Address offset: 0x48 */__IO uint32_t UDRDR;         /*!< SPI Underrun data register,                      Address offset: 0x4C */__IO uint32_t I2SCFGR;       /*!< I2S Configuration register,                      Address offset: 0x50 */} SPI_TypeDef;

这个结构体的成员名称和排列次序和CPU的寄存器是一 一对应的。

__IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏:

#define     __O     volatile             /*!< Defines 'write only' permissions */
#define     __IO    volatile             /*!< Defines 'read / write' permissions */

下面我们看下SPI的定义,在stm32h743xx.h文件。

#define PERIPH_BASE           (0x40000000UL)
#define D2_APB1PERIPH_BASE     PERIPH_BASE
#define D2_APB2PERIPH_BASE    (PERIPH_BASE + 0x00010000UL)
#define D3_APB1PERIPH_BASE    (PERIPH_BASE + 0x18000000UL)#define SPI2_BASE             (D2_APB1PERIPH_BASE + 0x3800UL)
#define SPI3_BASE             (D2_APB1PERIPH_BASE + 0x3C00UL)
#define SPI1_BASE             (D2_APB2PERIPH_BASE + 0x3000UL)
#define SPI4_BASE             (D2_APB2PERIPH_BASE + 0x3400UL)
#define SPI5_BASE             (D2_APB2PERIPH_BASE + 0x5000UL)
#define SPI6_BASE             (D3_APB1PERIPH_BASE + 0x1400UL)#define SPI1                ((SPI_TypeDef *) SPI1_BASE)
#define SPI2                ((SPI_TypeDef *) SPI2_BASE)
#define SPI3                ((SPI_TypeDef *) SPI3_BASE)
#define SPI4                ((SPI_TypeDef *) SPI4_BASE)
#define SPI5                ((SPI_TypeDef *) SPI5_BASE)
#define SPI6                ((SPI_TypeDef *) SPI6_BASE) <----- 展开这个宏,(FLASH_TypeDef *)0x58001400

我们访问SPI的CR1寄存器可以采用这种形式:SPI->CR1 = 0。

72.3.2 SPI总线初始化结构体SPI_InitTypeDef

下面是SPI总线的初始化结构体,用到的地方比较多:

typedef struct
{uint32_t Mode;                            uint32_t Direction;                      uint32_t DataSize;                          uint32_t CLKPolarity;                       uint32_t CLKPhase;                         uint32_t NSS;                             uint32_t BaudRatePrescaler;                uint32_t FirstBit;                         uint32_t TIMode;                          uint32_t CRCCalculation;                   uint32_t CRCPolynomial;                     uint32_t CRCLength;                        uint32_t NSSPMode;                         uint32_t NSSPolarity;                    uint32_t TxCRCInitializationPattern;       uint32_t RxCRCInitializationPattern;       uint32_t MasterSSIdleness;                 uint32_t MasterInterDataIdleness;           uint32_t MasterReceiverAutoSusp;          uint32_t MasterKeepIOState;               uint32_t IOSwap;
} SPI_InitTypeDef;

下面将结构体成员逐一做个说明:

  • Mode

用于设置工作在主机模式还是从机模式。

#define SPI_MODE_SLAVE              (0x00000000UL)
#define SPI_MODE_MASTER             SPI_CFG2_MASTER
  • Direction

用于设置SPI工作在全双工,单工,还是半双工模式。

#define SPI_DIRECTION_2LINES           (0x00000000UL)     /* 全双工 */
#define SPI_DIRECTION_2LINES_TXONLY     SPI_CFG2_COMM_0   /* 单工,仅发送 */
#define SPI_DIRECTION_2LINES_RXONLY     SPI_CFG2_COMM_1   /* 单工,仅接收 */
#define SPI_DIRECTION_1LINE             SPI_CFG2_COMM     /* 半双工 */
  • DataSize

用于设置SPI总线数据收发的位宽,支持4-32bit。

#define SPI_DATASIZE_4BIT                             (0x00000003UL)
#define SPI_DATASIZE_5BIT                             (0x00000004UL)
#define SPI_DATASIZE_6BIT                             (0x00000005UL)
#define SPI_DATASIZE_7BIT                             (0x00000006UL)
#define SPI_DATASIZE_8BIT                             (0x00000007UL)
#define SPI_DATASIZE_9BIT                             (0x00000008UL)
#define SPI_DATASIZE_10BIT                            (0x00000009UL)
#define SPI_DATASIZE_11BIT                            (0x0000000AUL)
#define SPI_DATASIZE_12BIT                            (0x0000000BUL)
#define SPI_DATASIZE_13BIT                            (0x0000000CUL)
#define SPI_DATASIZE_14BIT                            (0x0000000DUL)
#define SPI_DATASIZE_15BIT                            (0x0000000EUL)
#define SPI_DATASIZE_16BIT                            (0x0000000FUL)
#define SPI_DATASIZE_17BIT                            (0x00000010UL)
#define SPI_DATASIZE_18BIT                            (0x00000011UL)
#define SPI_DATASIZE_19BIT                            (0x00000012UL)
#define SPI_DATASIZE_20BIT                            (0x00000013UL)
#define SPI_DATASIZE_21BIT                            (0x00000014UL)
#define SPI_DATASIZE_22BIT                            (0x00000015UL)
#define SPI_DATASIZE_23BIT                            (0x00000016UL)
#define SPI_DATASIZE_24BIT                            (0x00000017UL)
#define SPI_DATASIZE_25BIT                            (0x00000018UL)
#define SPI_DATASIZE_26BIT                            (0x00000019UL)
#define SPI_DATASIZE_27BIT                            (0x0000001AUL)
#define SPI_DATASIZE_28BIT                            (0x0000001BUL)
#define SPI_DATASIZE_29BIT                            (0x0000001CUL)
#define SPI_DATASIZE_30BIT                            (0x0000001DUL)
#define SPI_DATASIZE_31BIT                            (0x0000001EUL)
#define SPI_DATASIZE_32BIT                            (0x0000001FUL)
  • CLKPolarity

用于设置空闲状态时,CLK是高电平还是低电平。

#define SPI_POLARITY_LOW       (0x00000000UL)
#define SPI_POLARITY_HIGH      SPI_CFG2_CPOL
  • NSS

用于设置NSS信号由硬件NSS引脚管理或者软件SSI位管理。

#define SPI_NSS_SOFT                                  SPI_CFG2_SSM
#define SPI_NSS_HARD_INPUT                            (0x00000000UL)
#define SPI_NSS_HARD_OUTPUT                           SPI_CFG2_SSOE
  • BaudRatePrescaler

用于设置SPI时钟分频,仅SPI工作在主控模式下起作用,对SPI从机模式不起作用。

#define SPI_BAUDRATEPRESCALER_2                       (0x00000000UL)
#define SPI_BAUDRATEPRESCALER_4                       (0x10000000UL)
#define SPI_BAUDRATEPRESCALER_8                       (0x20000000UL)
#define SPI_BAUDRATEPRESCALER_16                      (0x30000000UL)
#define SPI_BAUDRATEPRESCALER_32                      (0x40000000UL)
#define SPI_BAUDRATEPRESCALER_64                      (0x50000000UL)
#define SPI_BAUDRATEPRESCALER_128                     (0x60000000UL)
#define SPI_BAUDRATEPRESCALER_256                     (0x70000000UL)
  • FirstBit

用于设置数据传输从最高bit开始还是从最低bit开始。

#define SPI_FIRSTBIT_MSB                              (0x00000000UL)
#define SPI_FIRSTBIT_LSB                              SPI_CFG2_LSBFRST
  • TIMode

用于设置是否使能SPI总线的TI模式。

#define SPI_TIMODE_DISABLE               (0x00000000UL)
#define SPI_TIMODE_ENABLE                SPI_CFG2_SP_0
  • CRCCalculation

用于设置是否使能CRC计算。

#define SPI_CRCCALCULATION_DISABLE                    (0x00000000UL)
#define SPI_CRCCALCULATION_ENABLE                     SPI_CFG1_CRCEN
  • CRCPolynomial

用于设置CRC计算使用的多项式,必须是奇数,范围0到65535。

  • CRCLength

用于设置CRC计算时的CRC长度。大小要与同属此结构体的DataSize一致。或是DataSize的整数倍。

#define SPI_CRC_LENGTH_DATASIZE                       (0x00000000UL)
#define SPI_CRC_LENGTH_4BIT                           (0x00030000UL)
#define SPI_CRC_LENGTH_5BIT                           (0x00040000UL)
#define SPI_CRC_LENGTH_6BIT                           (0x00050000UL)
#define SPI_CRC_LENGTH_7BIT                           (0x00060000UL)
#define SPI_CRC_LENGTH_8BIT                           (0x00070000UL)
#define SPI_CRC_LENGTH_9BIT                           (0x00080000UL)
#define SPI_CRC_LENGTH_10BIT                          (0x00090000UL)
#define SPI_CRC_LENGTH_11BIT                          (0x000A0000UL)
#define SPI_CRC_LENGTH_12BIT                          (0x000B0000UL)
#define SPI_CRC_LENGTH_13BIT                          (0x000C0000UL)
#define SPI_CRC_LENGTH_14BIT                          (0x000D0000UL)
#define SPI_CRC_LENGTH_15BIT                          (0x000E0000UL)
#define SPI_CRC_LENGTH_16BIT                          (0x000F0000UL)
#define SPI_CRC_LENGTH_17BIT                          (0x00100000UL)
#define SPI_CRC_LENGTH_18BIT                          (0x00110000UL)
#define SPI_CRC_LENGTH_19BIT                          (0x00120000UL)
#define SPI_CRC_LENGTH_20BIT                          (0x00130000UL)
#define SPI_CRC_LENGTH_21BIT                          (0x00140000UL)
#define SPI_CRC_LENGTH_22BIT                          (0x00150000UL)
#define SPI_CRC_LENGTH_23BIT                          (0x00160000UL)
#define SPI_CRC_LENGTH_24BIT                          (0x00170000UL)
#define SPI_CRC_LENGTH_25BIT                          (0x00180000UL)
#define SPI_CRC_LENGTH_26BIT                          (0x00190000UL)
#define SPI_CRC_LENGTH_27BIT                          (0x001A0000UL)
#define SPI_CRC_LENGTH_28BIT                          (0x001B0000UL)
#define SPI_CRC_LENGTH_29BIT                          (0x001C0000UL)
#define SPI_CRC_LENGTH_30BIT                          (0x001D0000UL)
#define SPI_CRC_LENGTH_31BIT                          (0x001E0000UL)
#define SPI_CRC_LENGTH_32BIT                          (0x001F0000UL)
  • NSSPMode

用于设置是否使能NSSP信号,可以通过SPIx_CR2寄存器的SSOM位使能。注意,只有配置为摩托罗拉SPI主控模式时设置此成员才有用。

#define SPI_NSS_PULSE_DISABLE                         (0x00000000UL)
#define SPI_NSS_PULSE_ENABLE                          SPI_CFG2_SSOM
  • NSSPolarity

用于设置NSS引脚上的高电平或者低电平作为激活电平。

#define SPI_NSS_POLARITY_LOW                          (0x00000000UL)
#define SPI_NSS_POLARITY_HIGH                          SPI_CFG2_SSIOP
  • FifoThreshold

用于设置SPI的FIFO阀值。

#define SPI_FIFO_THRESHOLD_01DATA                     (0x00000000UL)
#define SPI_FIFO_THRESHOLD_02DATA                     (0x00000020UL)
#define SPI_FIFO_THRESHOLD_03DATA                     (0x00000040UL)
#define SPI_FIFO_THRESHOLD_04DATA                     (0x00000060UL)
#define SPI_FIFO_THRESHOLD_05DATA                     (0x00000080UL)
#define SPI_FIFO_THRESHOLD_06DATA                     (0x000000A0UL)
#define SPI_FIFO_THRESHOLD_07DATA                     (0x000000C0UL)
#define SPI_FIFO_THRESHOLD_08DATA                     (0x000000E0UL)
#define SPI_FIFO_THRESHOLD_09DATA                     (0x00000100UL)
#define SPI_FIFO_THRESHOLD_10DATA                     (0x00000120UL)
#define SPI_FIFO_THRESHOLD_11DATA                     (0x00000140UL)
#define SPI_FIFO_THRESHOLD_12DATA                     (0x00000160UL)
#define SPI_FIFO_THRESHOLD_13DATA                     (0x00000180UL)
#define SPI_FIFO_THRESHOLD_14DATA                     (0x000001A0UL)
#define SPI_FIFO_THRESHOLD_15DATA                     (0x000001C0UL)
#define SPI_FIFO_THRESHOLD_16DATA                     (0x000001E0UL)
  • TxCRCInitializationPattern

发送CRC初始化模式。

#define SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN       (0x00000000UL)
#define SPI_CRC_INITIALIZATION_ALL_ONE_PATTERN        (0x00000001UL)
  • RxCRCInitializationPattern

接收CRC初始化模式

#define SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN       (0x00000000UL)
#define SPI_CRC_INITIALIZATION_ALL_ONE_PATTERN        (0x00000001UL)
  • MasterSSIdleness

在主模式下插入到SS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数。

#define SPI_MASTER_SS_IDLENESS_00CYCLE                (0x00000000UL)
#define SPI_MASTER_SS_IDLENESS_01CYCLE                (0x00000001UL)
#define SPI_MASTER_SS_IDLENESS_02CYCLE                (0x00000002UL)
#define SPI_MASTER_SS_IDLENESS_03CYCLE                (0x00000003UL)
#define SPI_MASTER_SS_IDLENESS_04CYCLE                (0x00000004UL)
#define SPI_MASTER_SS_IDLENESS_05CYCLE                (0x00000005UL)
#define SPI_MASTER_SS_IDLENESS_06CYCLE                (0x00000006UL)
#define SPI_MASTER_SS_IDLENESS_07CYCLE                (0x00000007UL)
#define SPI_MASTER_SS_IDLENESS_08CYCLE                (0x00000008UL)
#define SPI_MASTER_SS_IDLENESS_09CYCLE                (0x00000009UL)
#define SPI_MASTER_SS_IDLENESS_10CYCLE                (0x0000000AUL)
#define SPI_MASTER_SS_IDLENESS_11CYCLE                (0x0000000BUL)
#define SPI_MASTER_SS_IDLENESS_12CYCLE                (0x0000000CUL)
#define SPI_MASTER_SS_IDLENESS_13CYCLE                (0x0000000DUL)
#define SPI_MASTER_SS_IDLENESS_14CYCLE                (0x0000000EUL)
#define SPI_MASTER_SS_IDLENESS_15CYCLE                (0x0000000FUL)
  • MasterInterDataIdleness

主模式下在两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数。

#define SPI_MASTER_RX_AUTOSUSP_DISABLE                (0x00000000UL)
#define SPI_MASTER_RX_AUTOSUSP_ENABLE                 SPI_CR1_MASRX
  • MasterReceiverAutoSusp

用于控制主器件接收器模式下的连续 SPI 传输以及自动管理,以避免出现上溢情况。

#define SPI_MASTER_RX_AUTOSUSP_DISABLE                (0x00000000UL)
#define SPI_MASTER_RX_AUTOSUSP_ENABLE                 SPI_CR1_MASRX
  • MasterKeepIOState

禁止SPI后,SPI相关引脚保持当前状态,以防止出现毛刺。在从模式下,该位不应该使用。

#define SPI_MASTER_KEEP_IO_STATE_DISABLE              (0x00000000UL)
#define SPI_MASTER_KEEP_IO_STATE_ENABLE               SPI_CFG2_AFCNTR
  • IOSwap

用于交换MISO和MOSI引脚。

#define SPI_IO_SWAP_DISABLE                           (0x00000000UL)
#define SPI_IO_SWAP_ENABLE                            SPI_CFG2_IOSWP

72.3.3 SPI总线句柄结构体SPI_HandleTypeDef

下面是SPI总线的初始化结构体,用到的地方比较多:

typedef struct __SPI_HandleTypeDef
{SPI_TypeDef                *Instance;                 SPI_InitTypeDef            Init;                        uint8_t                    *pTxBuffPtr;                 uint16_t                   TxXferSize;                   __IO uint16_t              TxXferCount;                  uint8_t                    *pRxBuffPtr;                uint16_t                   RxXferSize;                  __IO uint16_t              RxXferCount;                 uint32_t                   CRCSize;                     void (*RxISR)(struct __SPI_HandleTypeDef *hspi);       void (*TxISR)(struct __SPI_HandleTypeDef *hspi);        DMA_HandleTypeDef          *hdmatx;                      DMA_HandleTypeDef          *hdmarx;                     HAL_LockTypeDef            Lock;                        __IO HAL_SPI_StateTypeDef  State;                        __IO uint32_t              ErrorCode;
#if defined(USE_SPI_RELOAD_TRANSFER)SPI_ReloadTypeDef          Reload;
#endif #if (USE_HAL_SPI_REGISTER_CALLBACKS == 1UL)void (* TxCpltCallback)(struct __SPI_HandleTypeDef *hspi);      void (* RxCpltCallback)(struct __SPI_HandleTypeDef *hspi);      void (* TxRxCpltCallback)(struct __SPI_HandleTypeDef *hspi);    void (* TxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi);  void (* RxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi);  void (* TxRxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi); void (* ErrorCallback)(struct __SPI_HandleTypeDef *hspi);       void (* AbortCpltCallback)(struct __SPI_HandleTypeDef *hspi);   void (* MspInitCallback)(struct __SPI_HandleTypeDef *hspi);    void (* MspDeInitCallback)(struct __SPI_HandleTypeDef *hspi);
#endif
} SPI_HandleTypeDef;

注意事项:

条件编译USE_HAL_SPI_REGISTER_CALLBACKS用来设置使用自定义回调还是使用默认回调,此定义一般放在stm32h7xx_hal_conf.h文件里面设置:

#define   USE_HAL_SPI_REGISTER_CALLBACKS   1

通过函数HAL_SPI_RegisterCallback注册回调,取消注册使用函数HAL_SPI_UnRegisterCallback。

这里重点介绍下面几个参数,其它参数主要是HAL库内部使用和自定义回调函数。

  • SPI_TypeDef   *Instance

这个参数是寄存器的例化,方便操作寄存器,比如使能SPI1。

SET_BIT(SPI1 ->CR1,  SPI_CR1_SPE)。

  • SPI_InitTypeDef  Init

这个参数是用户接触最多的,在本章节3.2小节已经进行了详细说明。

  • DMA_HandleTypeDef          *hdmatx
  • DMA_HandleTypeDef          *hdmarx

用于SPI句柄关联DMA句柄,方便操作调用。

72.4 SPI总线源文件stm32h7xx_hal_spi.c

此文件涉及到的函数较多,这里把几个常用的函数做个说明:

  • HAL_SPI_Init
  • HAL_SPI_DeInit
  • HAL_SPI_TransmitReceive
  • HAL_SPI_TransmitReceive_IT
  • HAL_SPI_TransmitReceive_DMA

72.4.1 函数HAL_SPI_Init

函数原型:

HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi)
{uint32_t crc_length = 0UL;uint32_t packet_length;/* 省略未写 *//* 如果数据位宽大于16bit,必须是SPI1,SPI2或者SPI3,而SPI4,SPI5和SPI6不支持大于16bit */if ((!IS_SPI_HIGHEND_INSTANCE(hspi->Instance)) && (hspi->Init.DataSize > SPI_DATASIZE_16BIT)){return HAL_ERROR;}/* SPI1,SPI2和SPI3的FIFO大小是16*8bit,而SPI4,SPI5和SPI6的FIFO大小是8*8bit这里是查看设置的缓冲大小是否超出了FIFO支持的大小。
*/packet_length = SPI_GetPacketSize(hspi);if (((!IS_SPI_HIGHEND_INSTANCE(hspi->Instance)) && (packet_length > SPI_LOWEND_FIFO_SIZE)) ||((IS_SPI_HIGHEND_INSTANCE(hspi->Instance)) && (packet_length > SPI_HIGHEND_FIFO_SIZE))){return HAL_ERROR;}#if (USE_SPI_CRC != 0UL)/* 省略未写 */
#endif if (hspi->State == HAL_SPI_STATE_RESET){/* 解锁 */hspi->Lock = HAL_UNLOCKED;/* 使用自定义回调 */
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1UL)/* 设置默认回调函数 */hspi->TxCpltCallback       = HAL_SPI_TxCpltCallback;       /* Legacy weak TxCpltCallback       */hspi->RxCpltCallback       = HAL_SPI_RxCpltCallback;       /* Legacy weak RxCpltCallback       */hspi->TxRxCpltCallback     = HAL_SPI_TxRxCpltCallback;     /* Legacy weak TxRxCpltCallback     */hspi->TxHalfCpltCallback   = HAL_SPI_TxHalfCpltCallback;   /* Legacy weak TxHalfCpltCallback   */hspi->RxHalfCpltCallback   = HAL_SPI_RxHalfCpltCallback;   /* Legacy weak RxHalfCpltCallback   */hspi->TxRxHalfCpltCallback = HAL_SPI_TxRxHalfCpltCallback; /* Legacy weak TxRxHalfCpltCallback */hspi->ErrorCallback        = HAL_SPI_ErrorCallback;        /* Legacy weak ErrorCallback        */hspi->AbortCpltCallback    = HAL_SPI_AbortCpltCallback;    /* Legacy weak AbortCpltCallback    */if (hspi->MspInitCallback == NULL){hspi->MspInitCallback = HAL_SPI_MspInit; }/* 初始化地址硬件: GPIO, CLOCK, NVIC... */hspi->MspInitCallback(hspi);
#else/* 初始化底层硬件: GPIO, CLOCK, NVIC... */HAL_SPI_MspInit(hspi);
#endif}hspi->State = HAL_SPI_STATE_BUSY;/* 禁止SPI外设 */__HAL_SPI_DISABLE(hspi);/*----------------------- SPIx CR1 & CR2 配置---------------------*/if ((hspi->Init.NSS == SPI_NSS_SOFT) && (hspi->Init.Mode == SPI_MODE_MASTER) && (hspi->Init.NSSPolarity ==SPI_NSS_POLARITY_LOW)){SET_BIT(hspi->Instance->CR1, SPI_CR1_SSI);}/* SPIx CFG1配置 */WRITE_REG(hspi->Instance->CFG1, (hspi->Init.BaudRatePrescaler | hspi->Init.CRCCalculation | crc_length |hspi->Init.FifoThreshold     | hspi->Init.DataSize));/* SPIx CFG2配置 */WRITE_REG(hspi->Instance->CFG2, (hspi->Init.NSSPMode     | hspi->Init.TIMode           | hspi->Init.NSSPolarity  |hspi->Init.NSS          | hspi->Init.CLKPolarity      | hspi->Init.CLKPhase     |hspi->Init.FirstBit     | hspi->Init.Mode             | hspi->Init.MasterInterDataIdleness |hspi->Init.Direction    | hspi->Init.MasterSSIdleness | hspi->Init.IOSwap));#if (USE_SPI_CRC != 0UL)/*---------------------------- SPIx CRC配置 ------------------*//* 配置SPI CRC */if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE){/* 初始化TX CRC初始值 */if (hspi->Init.TxCRCInitializationPattern == SPI_CRC_INITIALIZATION_ALL_ONE_PATTERN){SET_BIT(hspi->Instance->CR1, SPI_CR1_TCRCINI);}else{CLEAR_BIT(hspi->Instance->CR1, SPI_CR1_TCRCINI);}/* 初始化RXCRC初始值 */if (hspi->Init.RxCRCInitializationPattern == SPI_CRC_INITIALIZATION_ALL_ONE_PATTERN){SET_BIT(hspi->Instance->CR1, SPI_CR1_RCRCINI);}else{CLEAR_BIT(hspi->Instance->CR1, SPI_CR1_RCRCINI);}/* 使能 33/17 bit CRC计算 */if (((!IS_SPI_HIGHEND_INSTANCE(hspi->Instance)) && (crc_length == SPI_CRC_LENGTH_16BIT)) ||((IS_SPI_HIGHEND_INSTANCE(hspi->Instance))  && (crc_length == SPI_CRC_LENGTH_32BIT))){SET_BIT(hspi->Instance->CR1, SPI_CR1_CRC33_17);}else{CLEAR_BIT(hspi->Instance->CR1, SPI_CR1_CRC33_17);}/* 写CRC多项式到SPI寄存器 */WRITE_REG(hspi->Instance->CRCPOLY, hspi->Init.CRCPolynomial);}
#endif /* SPI从模式,下溢配置 */if (hspi->Init.Mode == SPI_MODE_SLAVE){/* 设置默认下溢配置 */
#if (USE_SPI_CRC != 0UL)if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_DISABLE)
#endif{MODIFY_REG(hspi->Instance->CFG1, SPI_CFG1_UDRDET, SPI_CFG1_UDRDET_0);}MODIFY_REG(hspi->Instance->CFG1, SPI_CFG1_UDRCFG, SPI_CFG1_UDRCFG_1);}#if defined(SPI_I2SCFGR_I2SMOD)CLEAR_BIT(hspi->Instance->I2SCFGR, SPI_I2SCFGR_I2SMOD);
#endif /* 确保AFCNTR bit由SPI主机模式管理 */if ((hspi->Init.Mode & SPI_MODE_MASTER) == SPI_MODE_MASTER){/* Alternate function GPIOs control */MODIFY_REG(hspi->Instance->CFG2, SPI_CFG2_AFCNTR, (hspi->Init.MasterKeepIOState));}hspi->ErrorCode = HAL_SPI_ERROR_NONE;hspi->State     = HAL_SPI_STATE_READY;return HAL_OK;
}

函数描述:

此函数用于初始化SPI。

函数参数:

  • 第1个参数是SPI_HandleTypeDef类型结构体指针变量,用于配置要初始化的参数。
  • 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

注意事项:

  1. 函数HAL_SPI_MspInit用于初始化SPI的底层时钟、引脚等功能。需要用户自己在此函数里面实现具体的功能。由于这个函数是弱定义的,允许用户在工程其它源文件里面重新实现此函数。当然,不限制一定要在此函数里面实现,也可以像早期的标准库那样,用户自己初始化即可,更灵活些。
  2. 如果形参hspi的结构体成员State没有做初始状态,这个地方就是个坑。特别是用户搞了一个局部变量SPI_HandleTypeDef SpiHandle。

对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个 HAL_SPI_STATE_RESET  = 0x00U。

解决办法有三

方法1:用户自己初始化SPI和涉及到的GPIO等。

方法2:定义SPI_HandleTypeDef SpiHandle为全局变量。

方法3:下面的方法

if(HAL_SPI_DeInit(&SpiHandle) != HAL_OK)
{Error_Handler();
}
if(HAL_SPI_Init(&SpiHandle) != HAL_OK)
{Error_Handler();
}

使用举例:

SPI_HandleTypeDef hspi = {0};/* 设置SPI参数 */
hspi.Instance               = SPIx;                   /* 例化SPI */
hspi.Init.BaudRatePrescaler = _BaudRatePrescaler;     /* 设置波特率 */
hspi.Init.Direction         = SPI_DIRECTION_2LINES;   /* 全双工 */
hspi.Init.CLKPhase          = _CLKPhase;              /* 配置时钟相位 */
hspi.Init.CLKPolarity       = _CLKPolarity;           /* 配置时钟极性 */
hspi.Init.DataSize          = SPI_DATASIZE_8BIT;      /* 设置数据宽度 */
hspi.Init.FirstBit          = SPI_FIRSTBIT_MSB;       /* 数据传输先传高位 */
hspi.Init.TIMode            = SPI_TIMODE_DISABLE;     /* 禁止TI模式  */
hspi.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;      /* 禁止CRC */
hspi.Init.CRCPolynomial     = 7;                               /* 禁止CRC后,此位无效 */
hspi.Init.CRCLength         = SPI_CRC_LENGTH_8BIT;             /* 禁止CRC后,此位无效 */
hspi.Init.NSS               = SPI_NSS_SOFT;                    /* 使用软件方式管理片选引脚 */
hspi.Init.FifoThreshold     = SPI_FIFO_THRESHOLD_01DATA;       /* 设置FIFO大小是一个数据项 */
hspi.Init.NSSPMode          = SPI_NSS_PULSE_DISABLE;           /* 禁止脉冲输出 */
hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI后,SPI相关引脚保持当前状态 */
hspi.Init.Mode                  = SPI_MODE_MASTER;            /* SPI工作在主控模式 */if (HAL_SPI_Init(&hspi) != HAL_OK)
{Error_Handler(__FILE__, __LINE__);
}

72.4.2 函数HAL_SPI_DeInit

函数原型:

HAL_StatusTypeDef HAL_SPI_DeInit(SPI_HandleTypeDef *hspi)
{
/* 检测SPI句柄是否有效 */if (hspi == NULL){return HAL_ERROR;}/* 检查SPI例化参数 */assert_param(IS_SPI_ALL_INSTANCE(hspi->Instance));hspi->State = HAL_SPI_STATE_BUSY;/* 禁止SPI外设时钟 */__HAL_SPI_DISABLE(hspi);#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1UL)if (hspi->MspDeInitCallback == NULL){hspi->MspDeInitCallback = HAL_SPI_MspDeInit; }/* 复位底层硬件: GPIO, CLOCK, NVIC... */hspi->MspDeInitCallback(hspi);
#else/* 复位底层硬件: GPIO, CLOCK, NVIC... */HAL_SPI_MspDeInit(hspi);
#endif /* 设置无错误,复位状态标记 */hspi->ErrorCode = HAL_SPI_ERROR_NONE;hspi->State = HAL_SPI_STATE_RESET;/* 解锁SPI */__HAL_UNLOCK(hspi);return HAL_OK;
}

函数描述:

用于复位SPI总线初始化。

函数参数:

  • 第1个参数是SPI_HandleTypeDef类型结构体指针变量。
  • 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中

72.4.3 函数HAL_SPI_TransmitReceive

函数原型:

HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout)
{/* 省略未写 *//* 大于16bit的数据收发 */if (hspi->Init.DataSize > SPI_DATASIZE_16BIT){/* 省略未写 */}/* 大于8bit,小于16bi的数据收发 */else if (hspi->Init.DataSize > SPI_DATASIZE_8BIT){/* 省略未写 */}/* 小于等于8bit的数据收发 */else{/* 省略未写 */}}

函数描述:

此函数主要用于SPI数据收发,全双工查询方式。

函数参数:

  • 第1个参数是SPI_HandleTypeDef类型结构体指针变量。
  • 第2个参数是发送数据缓冲地址。
  • 第3个参数是接收数据缓冲地址。
  • 第4个参数是传输的数据大小,单位字节个数。
  • 第5个参数是传输过程的溢出时间,单位ms。
  • 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

使用举例:

SPI_HandleTypeDef hspi = {0};if(HAL_SPI_TransmitReceive(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen, 1000000) != HAL_OK)
{Error_Handler(__FILE__, __LINE__);
}

72.4.4 函数HAL_SPI_TransmitReceive_IT

函数原型:

HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size)
{/* 省略未写 *//* 设置传输参数 */hspi->ErrorCode   = HAL_SPI_ERROR_NONE;hspi->pTxBuffPtr  = (uint8_t *)pTxData;hspi->TxXferSize  = Size;hspi->TxXferCount = Size;hspi->pRxBuffPtr  = (uint8_t *)pRxData;hspi->RxXferSize  = Size;hspi->RxXferCount = Size;/* 设置中断处理 */if (hspi->Init.DataSize > SPI_DATASIZE_16BIT){hspi->TxISR     = SPI_TxISR_32BIT;hspi->RxISR     = SPI_RxISR_32BIT;}else if (hspi->Init.DataSize > SPI_DATASIZE_8BIT){hspi->RxISR     = SPI_RxISR_16BIT;hspi->TxISR     = SPI_TxISR_16BIT;}else{hspi->RxISR     = SPI_RxISR_8BIT;hspi->TxISR     = SPI_TxISR_8BIT;}/* 设置当前传输数据大小 */MODIFY_REG(hspi->Instance->CR2, SPI_CR2_TSIZE, Size);/* 使能SPI外设 */__HAL_SPI_ENABLE(hspi);/* 使能各种中断标志 */__HAL_SPI_ENABLE_IT(hspi, (SPI_IT_EOT | SPI_IT_RXP | SPI_IT_TXP | SPI_IT_DXP | SPI_IT_UDR | SPI_IT_OVR |
SPI_IT_FRE | SPI_IT_MODF | SPI_IT_TSERF));if (hspi->Init.Mode == SPI_MODE_MASTER){/* 启动传输 */SET_BIT(hspi->Instance->CR1, SPI_CR1_CSTART);}/* 解锁 */__HAL_UNLOCK(hspi);return errorcode;
}

函数描述:

此函数主要用于SPI数据收发,全双工中断方式。

函数参数:

  • 第1个参数是SPI_HandleTypeDef类型结构体指针变量。
  • 第2个参数是发送数据缓冲地址。
  • 第3个参数是接收数据缓冲地址。
  • 第4个参数是传输的数据大小,单位字节个数。
  • 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

使用举例:

SPI_HandleTypeDef hspi = {0};if(HAL_SPI_TransmitReceive_IT(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)
{Error_Handler(__FILE__, __LINE__);
}

72.4.5 函数HAL_SPI_TransmitReceive_DMA

函数原型:

HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,uint16_t Size)
{/* 省略未写 *//* 注意DMA的位宽和对齐设置 */if (((hspi->Init.DataSize > SPI_DATASIZE_16BIT) && (hspi->hdmarx->Init.MemDataAlignment !=DMA_MDATAALIGN_WORD))  || ((hspi->Init.DataSize > SPI_DATASIZE_8BIT) && ((hspi->hdmarx->Init.MemDataAlignment != DMA_MDATAALIGN_HALFWORD) && (hspi->hdmarx->Init.MemDataAlignment != DMA_MDATAALIGN_WORD)))){}/* 调整DMA对齐和数据大小 */if (hspi->Init.DataSize <= SPI_DATASIZE_8BIT){/* 省略未写 */}else if (hspi->Init.DataSize <= SPI_DATASIZE_16BIT){/* 省略未写 */}else{/* 省略未写 */}/*  DMA接收配置 */if (HAL_OK != HAL_DMA_Start_IT(hspi->hdmarx, (uint32_t)&hspi->Instance->RXDR, (uint32_t)hspi->pRxBuffPtr,hspi->RxXferCount)){}/* DMA发送配置 */if (HAL_OK != HAL_DMA_Start_IT(hspi->hdmatx, (uint32_t)hspi->pTxBuffPtr, (uint32_t)&hspi->Instance->TXDR,hspi->TxXferCount)){}/* 省略未写 */
}

函数描述:

此函数主要用于SPI数据收发,全双工DMA方式。

函数参数:

  • 第1个参数是SPI_HandleTypeDef类型结构体指针变量。
  • 第2个参数是发送数据缓冲地址。
  • 第3个参数是接收数据缓冲地址。
  • 第4个参数是传输的数据大小,单位字节个数。
  • 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

使用举例:

SPI_HandleTypeDef hspi = {0};if(HAL_SPI_TransmitReceive_DMA(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)
{Error_Handler(__FILE__, __LINE__);
}

72.5 总结

本章节就为大家讲解这么多,要熟练掌握SPI总线的查询,中断和DMA方式的实现,因为基于SPI接口的外设芯片很多,熟练后,可以方便的驱动各种SPI接口芯片,以便选择合适的驱动方式。

【STM32H7教程】第72章 STM32H7的SPI总线基础知识和HAL库API相关推荐

  1. 【STM32H7教程】第87章 STM32H7的SDMMC总线基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第87章       STM32H7的SDMMC总线基础知识和 ...

  2. 【STM32H7教程】第78章 STM32H7的QSPI总线基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第78章       STM32H7的QSPI总线基础知识和H ...

  3. 【STM32H7教程】第91章 STM32H7的FDCAN总线基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第91章       STM32H7的FDCAN总线基础知识和 ...

  4. 【STM32H7教程】第70章 STM32H7的内部Flash基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第70章       STM32H7的内部Flash基础知识和 ...

  5. 【STM32H7教程】第29章 STM32H7的USART串口基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第29章       STM32H7的USART串口基础知识和 ...

  6. 【STM32H7教程】第65章 STM32H7的低功耗串口LPUART基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第65章       STM32H7的低功耗串口LPUART基 ...

  7. 【STM32H7教程】第55章 STM32H7的图形加速器DMA2D的基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第55章       STM32H7的图形加速器DMA2D的基 ...

  8. 【STM32H7教程】第63章 STM32H7的高分辨率定时器HRTIM基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第63章       STM32H7的高分辨率定时器HRTIM ...

  9. 【STM32F429开发板用户手册】第40章 STM32F429的LCD控制器LTDC基础知识和HAL库API

    最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255 第40章       STM32F429的LCD控制器LTDC基础 ...

最新文章

  1. 洛谷 3029 [USACO11NOV]牛的阵容Cow Lineup
  2. 程序员必备 Git 分支开发规范指南
  3. armv7 cortex a系列编程手册_STM32、Cortex-M3和ARMv8-M之间的关联
  4. VS2010 测试功能之旅:编码的UI测试(4)-通过编写测试代码的方式“.NET研究”建立UI测试(下)...
  5. 怎么判断一个字符串的最长回文子串是否在头尾_回文自动机入门
  6. android studio一个页面等待3秒跳转_Flutter 对 iOS、Android(双端开发者)的快速理解(二)
  7. # ; @REM !等符号在WINCE6.0下的意义和作用
  8. CMMI与Agile敏捷开发比较之二:需求管理篇(兼谈用敏捷实现和满足CMMI的ReqM过程域)...
  9. Linux内存管理学习资料
  10. Atitit 数据类型体系图 目录 1. 系统常用的数据类型 1 2. H5 form表单新数据类型 2 2.1. TML5 新的 Input 类型 2 2.2. HTML5 的新的表单元素: 2
  11. Atitit.可视化编程jbpm6 的环境and 使用总结...
  12. ADOBE CS3 序列号
  13. H5页面自定义标题、链接、描述、图片分享到微信朋友、朋友圈、QQ和QQ空间
  14. JavaScript swiper
  15. 解决height:100vh超出屏幕高度的问题
  16. 曾国藩座右铭:“物来顺应,未来不迎,当时不杂,既过不恋”!
  17. 惠普笔记本重装系统后没有声音如何解决
  18. 什么网站可以测试敏感词?
  19. 机器人瓦力船长机器人_机器人瓦力—关于守护和等待的小故事
  20. teamviewer存在linux版本_Ubuntu16.04 安装Teamviewer

热门文章

  1. ionic2实现社会化分享Social Sharing
  2. 测试 软通动力软件测试机试_软通动力软件测试笔试题.doc
  3. 华为携手思迈特软件成立BYOD移动办公联盟
  4. SecureCRT sftp上传文件报错:put: failed to upload xxx 拒绝访问
  5. 微信小程序 默认第一个选中变色
  6. 进程和线程的区别,进程间通信方式,怎么选择比较好
  7. 小程序与h5的相互跳转
  8. Linux显示中文—韩文—日文-Zhcon使用方法
  9. StratifiedShuffleSplit实现分层抽样交叉验证
  10. VK11\VK12\VK13 价格间隔拆分问题