完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980
本章节为大家讲解SPI(Serial peripheral interface)总线的基础知识和对应的HAL库API。
72.1 初学者重要提示
72.2 SPI总线基础知识
72.3 SPI总线的HAL库用法
72.4 源文件stm32h7xx_hal_spi.c
72.5 总结
认识一个外设,最好的方式就是看它的框图,方便我们快速的了解SPI的基本功能,然后再看手册了解细节。
通过这个框图,我们可以得到如下信息:
低功耗唤醒信号。
spi的中断请求信号。
spi的DMA发送和接收请求信号。
为寄存器提供时钟。
为spi内核时钟。
此引脚在主机模式下用于时钟输出,从机模式下用于时钟输入。
此引脚在从机模式下用于发送数据,主机模式下接收数据。
此引脚在从机模式下用于数据接收,主机模式下发送数据。
根据SPI和SS设置,此引脚可用于:
a. 选择三个从器件进行通信。
b. 同步数据帧。
c. 检测多个主器件之间是否存在冲突。
通过这个框图还要认识到一点,SPI有三个时钟域,分别是寄存器所在的ABP总线时钟域,内核时钟发生器时钟域以及内核时钟发生器分频后的串行时钟域。
这个知识点在初学的时候容易忽视,所以我们这里整理下。
SPI1,SPI4和SPI5在APB2总线,SPI2,SPI3在APB1总线,SPI6在APB4总线。注意,SPI的最高时钟不是由这些总线决定的。
STM32H7主频在400MHz下,SPI1,SPI2和SPI3的最高时钟是200MHz,而SPI4,5,6是100MHz, 以SPI1为了,可以选择的时钟源如下:
这里特别注意一点,SPI工作时最少选择二分频,也就是说SPI1,2,3实际通信时钟是100MHz,而SPI4,5,6是50MHz。
片选信号SS在单一的主从器件配置下是可选的,一般情况下可以不使用。但需要同步数据流,或者用于TI模式时需要此信号。
全双工就是主从器件之间同时互传数据,SPI总线的全双工模式接线方式如下:
关于这个接线图要认识到以下几点:
半双工就是同一个时刻只能为一个方向传输数据,SPI总线的半工模式接线方式如下:
关于这个接线图要认识到以下几点:
单工就是只有一种通信方向,即发送或者接收,SPI总线的全双工模式接线方式如下:
关于这个接线图要认识到以下几点:
SPI总线星型拓扑用到的地方比较多,V7开发板就是用的星型拓扑外接多种SPI器件:
关于这个接线图,有以下几点需要大家了解:
SPI总线主要有四种通信格式,由CPOL时钟极性和CPHA时钟相位控制:
四种通信格式如下:
SCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据。
SCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据。
SCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据。
SCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据。
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。
下面是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;
下面将结构体成员逐一做个说明:
用于设置工作在主机模式还是从机模式。
#define SPI_MODE_SLAVE (0x00000000UL) #define SPI_MODE_MASTER SPI_CFG2_MASTER
用于设置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 /* 半双工 */
用于设置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)
用于设置空闲状态时,CLK是高电平还是低电平。
#define SPI_POLARITY_LOW (0x00000000UL) #define SPI_POLARITY_HIGH SPI_CFG2_CPOL
用于设置NSS信号由硬件NSS引脚管理或者软件SSI位管理。
#define SPI_NSS_SOFT SPI_CFG2_SSM #define SPI_NSS_HARD_INPUT (0x00000000UL) #define SPI_NSS_HARD_OUTPUT SPI_CFG2_SSOE
用于设置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)
用于设置数据传输从最高bit开始还是从最低bit开始。
#define SPI_FIRSTBIT_MSB (0x00000000UL) #define SPI_FIRSTBIT_LSB SPI_CFG2_LSBFRST
用于设置是否使能SPI总线的TI模式。
#define SPI_TIMODE_DISABLE (0x00000000UL) #define SPI_TIMODE_ENABLE SPI_CFG2_SP_0
用于设置是否使能CRC计算。
#define SPI_CRCCALCULATION_DISABLE (0x00000000UL) #define SPI_CRCCALCULATION_ENABLE SPI_CFG1_CRCEN
用于设置CRC计算使用的多项式,必须是奇数,范围0到65535。
用于设置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)
用于设置是否使能NSSP信号,可以通过SPIx_CR2寄存器的SSOM位使能。注意,只有配置为摩托罗拉SPI主控模式时设置此成员才有用。
#define SPI_NSS_PULSE_DISABLE (0x00000000UL) #define SPI_NSS_PULSE_ENABLE SPI_CFG2_SSOM
用于设置NSS引脚上的高电平或者低电平作为激活电平。
#define SPI_NSS_POLARITY_LOW (0x00000000UL) #define SPI_NSS_POLARITY_HIGH SPI_CFG2_SSIOP
用于设置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)
发送CRC初始化模式。
#define SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN (0x00000000UL) #define SPI_CRC_INITIALIZATION_ALL_ONE_PATTERN (0x00000001UL)
接收CRC初始化模式
#define SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN (0x00000000UL) #define SPI_CRC_INITIALIZATION_ALL_ONE_PATTERN (0x00000001UL)
在主模式下插入到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)
主模式下在两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数。
#define SPI_MASTER_RX_AUTOSUSP_DISABLE (0x00000000UL) #define SPI_MASTER_RX_AUTOSUSP_ENABLE SPI_CR1_MASRX
用于控制主器件接收器模式下的连续 SPI 传输以及自动管理,以避免出现上溢情况。
#define SPI_MASTER_RX_AUTOSUSP_DISABLE (0x00000000UL) #define SPI_MASTER_RX_AUTOSUSP_ENABLE SPI_CR1_MASRX
禁止SPI后,SPI相关引脚保持当前状态,以防止出现毛刺。在从模式下,该位不应该使用。
#define SPI_MASTER_KEEP_IO_STATE_DISABLE (0x00000000UL) #define SPI_MASTER_KEEP_IO_STATE_ENABLE SPI_CFG2_AFCNTR
用于交换MISO和MOSI引脚。
#define SPI_IO_SWAP_DISABLE (0x00000000UL) #define SPI_IO_SWAP_ENABLE SPI_CFG2_IOSWP
下面是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库内部使用和自定义回调函数。
这个参数是寄存器的例化,方便操作寄存器,比如使能SPI1。
SET_BIT(SPI1 ->CR1, SPI_CR1_SPE)。
这个参数是用户接触最多的,在本章节3.2小节已经进行了详细说明。
用于SPI句柄关联DMA句柄,方便操作调用。
此文件涉及到的函数较多,这里把几个常用的函数做个说明:
函数原型:
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。
函数参数:
注意事项:
对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般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__); }
函数原型:
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总线初始化。
函数参数:
函数原型:
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数据收发,全双工查询方式。
函数参数:
使用举例:
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__); }
函数原型:
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数据收发,全双工中断方式。
函数参数:
使用举例:
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__); }
函数原型:
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方式。
函数参数:
使用举例:
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__); }
本章节就为大家讲解这么多,要熟练掌握SPI总线的查询,中断和DMA方式的实现,因为基于SPI接口的外设芯片很多,熟练后,可以方便的驱动各种SPI接口芯片,以便选择合适的驱动方式。
【STM32H7教程】第72章 STM32H7的SPI总线基础知识和HAL库API
原文:https://www.cnblogs.com/armfly/p/12523671.html