5.26 SPI 使用说明
本章说明 N32H49x 标准外设库中 SPI 的驱动文件、32 个非 I2S_ 前缀公开函数,以及 CRC、CRC_FIFO、FullDuplex_SoftNSS、Simplex_Interrupt、SPI_DMA_T&R、SPI_FLASH、SPI_FLASH_DMA、SPI_Simplex_DMA_Recv 共 8 个示例。SPI 与 I2S 共用 n32h49x_spi.h、n32h49x_spi.c,以 I2S_ 为前缀的 11 个函数收录在 I2S 篇。片选管理、四线时序、CRC 与 FIFO 等细节请配合用户手册的「串行外设接口/内置音频总线(SPI/I2S)」章节阅读。
5.26.1 相关文件
| 相对路径 | 作用 |
|---|---|
firmware/n32h49x_std_periph_driver/inc/n32h49x_spi.h |
定义 SPI_InitType 结构体与数据方向、主从模式、数据长度、时钟极性/相位、NSS、波特率预分频、位序、CRC 多项式等宏,并声明 32 个非 I2S_ 前缀函数;同一头文件以 I2S_ 为前缀的 11 个函数归 I2S 篇 |
firmware/n32h49x_std_periph_driver/src/n32h49x_spi.c |
实现上述 32 个函数:初始化与使能、中断与 DMA 使能、数据收发、NSS 与数据长度配置、CRC 计算与校验、标志与中断状态查询、FIFO 与传输控制 |
projects/n32h49x_EVAL/examples/SPI/ |
SPI 示例根目录,含 CRC、CRC_FIFO、FullDuplex_SoftNSS、Simplex_Interrupt、SPI_DMA_T&R、SPI_FLASH、SPI_FLASH_DMA、SPI_Simplex_DMA_Recv 共 8 个示例 |
projects/n32h49x_EVAL/examples/SPI/CRC/ |
该示例的工程目录组成:MDK-ARM/、EWARM/、inc/、src/、readme.txt |
projects/n32h49x_EVAL/bsp/ |
部分示例引用的公共板级支持文件:串口日志打印(log.c/log.h)与 printf 重定向(print_remap.c) |
5.26.2 API 参考
以下 32 个公开函数按 n32h49x_spi.h 的声明顺序分为 5 组;每个条目给出功能摘要与 n32h49x_spi.c 中该函数正上方的注释原文。
SPI 初始化与开关
SPI_I2S_DeInit、SPI_Init、SPI_InitStruct、SPI_Enable
中断、DMA 与数据收发
SPI_I2S_EnableInt、SPI_I2S_EnableDma、SPI_I2S_TransmitData、SPI_I2S_ReceiveData
NSS、数据长度、CRC 与双向模式
SPI_SetNssLevel、SPI_SSOutputEnable、SPI_ConfigDataLen、SPI_TransmitCrcNext、SPI_EnableCalculateCrc、SPI_GetCRCDat、SPI_GetCRCPoly、SPI_ConfigBidirectionalMode
标志与中断状态
SPI_I2S_GetStatus、SPI_I2S_ClrCRCErrFlag、SPI_I2S_GetIntStatus、SPI_I2S_ClrITPendingBit
FIFO 与传输控制
SPI_I2S_FIFO_Cmd、SPI_I2S_ClearFIFOBit、SPI_RxFIFOSizeConfig、SPI_TxFIFOSizeConfig、SPI_RX_FIFO_CNT_GET、SPI_TX_FIFO_CNT_GET、SPI_TRANSNUM_SET、SPI_TRANSNUM_GET、SPI_DELAYTIME_SET、SPI_DELAYTIME_GET、SPI_RX_FIFO_SET、SPI_RX_FIFO_GET
void SPI_I2S_DeInit(const SPI_Module* SPIx)将 SPIx 外设寄存器复位为默认值(同时影响 I2S)。
name SPI_I2S_DeInit.
fun Deinitializes the SPIx peripheral registers to their default reset values (Affects also the I2Ss)
param SPIx :
- 1, 2, 3, 4, 5 or 6
return none
void SPI_Init(SPI_Module* SPIx,const SPI_InitType* SPI_InitStruct)根据 SPI_InitStruct 中指定的参数初始化 SPIx 外设。
name SPI_Init.
fun Initializes the SPIx peripheral according to the specified parameters in the SPI_InitStruct.
param SPIx :
- 1, 2, 3, 4, 5 or 6
param SPI_InitStruct :
- DataDirection
- SPI_DIR_DOUBLELINE_FULLDUPLEX
- SPI_DIR_DOUBLELINE_RONLY
- SPI_DIR_SINGLELINE_RX
- SPI_DIR_SINGLELINE_TX
- SpiMode
- SPI_MODE_MASTER
- SPI_MODE_SLAVE
- DataLen
- SPI_DATA_SIZE_16BITS
- SPI_DATA_SIZE_8BITS
- CLKPOL
- SPI_CLKPOL_LOW
- SPI_CLKPOL_HIGH
- CLKPHA
- SPI_CLKPHA_FIRST_EDGE
- SPI_CLKPHA_SECOND_EDGE
- NSS
- SPI_NSS_SOFT
- SPI_NSS_HARD
- BaudRatePres
- SPI_BR_PRESCALER_2
- SPI_BR_PRESCALER_4
- SPI_BR_PRESCALER_8
- SPI_BR_PRESCALER_16
- SPI_BR_PRESCALER_32
- SPI_BR_PRESCALER_64
- SPI_BR_PRESCALER_128
- SPI_BR_PRESCALER_256
- FirstBit
- SPI_FB_MSB
- SPI_FB_LSB
- CRCPoly default 0x0007, max 0xffff
return none
void SPI_InitStruct(SPI_InitType* SPI_InitStruct)为 SPI_InitStruct 的每个成员填入默认值。
name SPI_InitStruct.
fun Fills each SPI_InitStruct member with its default value.
param SPI_InitStruct :
- DataDirection
- SpiMode
- DataLen
- CLKPOL
- CLKPHA
- NSS
- BaudRatePres
- FirstBit
- CRCPoly
return none
void SPI_Enable(SPI_Module* SPIx, FunctionalState Cmd)使能或禁用指定的 SPI 外设。
name SPI_Enable.
fun Enables or disables the specified SPI peripheral.
param SPIx :
- 1, 2, 3, 4, 5 or 6
param Cmd :
- ENABLE
- DISABLE
return none
void SPI_I2S_EnableInt(SPI_Module* SPIx, uint8_t SPI_I2S_IT, FunctionalState Cmd)使能或禁用指定的 SPI/I2S 中断。
name SPI_I2S_EnableInt.
fun Enables or disables the specified SPI/I2S interrupts.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
param SPI_I2S_IT :
- SPI_I2S_INT_TE
- SPI_I2S_INT_RNE
- SPI_I2S_INT_ERR
- SPI_I2S_INT_RXONLYC
- SPI_I2S_INT_RXFIFOF
- SPI_I2S_INT_RXFIFOHF
- SPI_I2S_INT_TXFIFOHE
param Cmd :
- ENABLE
- DISABLE
return none
void SPI_I2S_EnableDma(SPI_Module* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState Cmd)使能或禁用 SPIx/I2Sx 的 DMA 接口。
name SPI_I2S_EnableDma.
fun Enables or disables the SPIx/I2Sx DMA interface.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
param SPI_I2S_DMAReq :
- SPI_I2S_DMA_TX
- SPI_I2S_DMA_RX
param Cmd :
- ENABLE
- DISABLE
return none
void SPI_I2S_TransmitData(SPI_Module* SPIx, uint16_t Data)通过 SPIx/I2Sx 外设发送一个数据。
name SPI_I2S_TransmitData.
fun Transmits a Data through the SPIx/I2Sx peripheral.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
param Data \* Data to be transmitted *\
return none
uint16_t SPI_I2S_ReceiveData(const SPI_Module* SPIx)返回 SPIx/I2Sx 外设最近接收到的数据。
name SPI_I2S_ReceiveData.
fun Returns the most recent received data by the SPIx/I2Sx peripheral.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
return The data in the SPI_DAT register.
void SPI_SetNssLevel(SPI_Module* SPIx, uint16_t SPI_NSSInternalSoft)通过软件在内部配置所选 SPI 的 NSS 引脚。
name SPI_SetNssLevel.
fun Configures internally by software the NSS pin for the selected SPI.
param SPIx :
- 1, 2, 3, 4, 5 or 6
param SPI_NSSInternalSoft :
- SPI_NSS_HIGH
- SPI_NSS_LOW
return none
void SPI_SSOutputEnable(SPI_Module* SPIx, FunctionalState Cmd)使能或禁用所选 SPI 的 SS 输出。
name SPI_SSOutputEnable.
fun Enables or disables the SS output for the selected SPI.
param SPIx :
- 1, 2, 3, 4, 5 or 6
param Cmd :
- ENABLE
- DISABLE
return none
void SPI_ConfigDataLen(SPI_Module* SPIx, uint16_t DataLen)配置所选 SPI 的数据长度。
name SPI_ConfigDataLen.
fun Configures the data size for the selected SPI.
param SPIx :
- 1, 2, 3, 4, 5 or 6
param DataLen :
- SPI_DATA_SIZE_16BITS
- SPI_DATA_SIZE_8BITS
return none
void SPI_TransmitCrcNext(SPI_Module* SPIx, FunctionalState Cmd)发送 SPIx 的 CRC 值。
name SPI_TransmitCrcNext.
fun Transmit the SPIx CRC value.
param SPIx :
- 1, 2, 3, 4, 5 or 6
param Cmd :
- ENABLE
- DISABLE
return none
void SPI_EnableCalculateCrc(SPI_Module* SPIx, FunctionalState Cmd)使能或禁用传输字节的 CRC 值计算。
name SPI_EnableCalculateCrc.
fun Enables or disables the CRC value calculation of the transferred bytes.
param SPIx :
- 1, 2, 3, 4, 5 or 6
param Cmd :
- ENABLE
- DISABLE
return none
uint16_t SPI_GetCRCDat(const SPI_Module* SPIx, uint8_t SPI_CRC)返回指定 SPI 的发送或接收 CRC 寄存器值。
name SPI_GetCRCDat.
fun Returns the transmit or the receive CRC register value for the specified SPI.
param SPIx :
- 1, 2, 3, 4, 5 or 6
param Cmd :
- SPI_CRC_TX
- SPI_CRC_RX
return Tx/Rx CRC register value.
uint16_t SPI_GetCRCPoly(const SPI_Module* SPIx)返回指定 SPI 的 CRC 多项式寄存器值。
name SPI_GetCRCPoly.
fun Returns the CRC Polynomial register value for the specified SPI.
param SPIx :
- 1, 2, 3, 4, 5 or 6
return The CRC Polynomial register value.
void SPI_ConfigBidirectionalMode(SPI_Module* SPIx, uint16_t DataDirection)选择指定 SPI 在双向模式下的数据传输方向。
name SPI_ConfigBidirectionalMode.
fun Selects the data transfer direction in bi-directional mode for the specified SPI.
param SPIx :
- 1, 2, 3, 4, 5 or 6
param DataDirection :
- SPI_BIDIRECTION_TX
- SPI_BIDIRECTION_RX
return none.
FlagStatus SPI_I2S_GetStatus(const SPI_Module* SPIx, uint16_t SPI_I2S_FLAG)检查指定的 SPI/I2S 标志是否置位。
name SPI_I2S_GetStatus.
fun Checks whether the specified SPI/I2S flag is set or not.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
param SPI_I2S_FLAG :
- SPI_I2S_BUSY_FLAG
- SPI_I2S_OVER_FLAG
- SPI_MODERR_FLAG
- SPI_CRCERR_FLAG
- I2S_UNDER_FLAG
- I2S_CHSIDE_FLAG
- SPI_I2S_TE_FLAG
- SPI_I2S_RNE_FLAG
- SPI_I2S_RXONLYC_FLAG
- SPI_I2S_RXFIFOF_FLAG
- SPI_I2S_TXFIFOE_FLAG
- SPI_I2S_RXFIFOHF_FLAG
- SPI_I2S_TXFIFOHE_FLAG
return The new state of SPI_I2S_FLAG (SET or RESET).
void SPI_I2S_ClrCRCErrFlag(SPI_Module* SPIx, uint16_t SPI_I2S_FLAG)清除 SPIx 的 CRC 错误(CRCERR)标志。
name SPI_I2S_ClrCRCErrFlag.
fun Clears the SPIx CRC Error (CRCERR) flag.
param SPIx :
- 1, 2, 3, 4, 5 or 6
param SPI_I2S_FLAG :
- SPI_CRCERR_FLAG
return none.
INTStatus SPI_I2S_GetIntStatus(const SPI_Module* SPIx, uint8_t SPI_I2S_IT)检查指定的 SPI/I2S 中断是否发生。
name SPI_I2S_GetIntStatus.
fun Checks whether the specified SPI/I2S interrupt has occurred or not.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
param SPI_I2S_IT :
- SPI_I2S_INT_TE
- SPI_I2S_INT_RNE
- SPI_I2S_INT_RXONLYC
- SPI_I2S_INT_RXFIFOF
- SPI_I2S_INT_RXFIFOHF
- SPI_I2S_INT_TXFIFOHE
- SPI_I2S_INT_CRCERR
- SPI_I2S_INT_MODERR
- SPI_I2S_INT_OVERERR
- I2S_I2S_INT_UNDER
return The new state of SPI_I2S_IT (SET or RESET).
void SPI_I2S_ClrITPendingBit(SPI_Module* SPIx, uint8_t SPI_I2S_IT)清除 SPIx 的 CRC 错误(CRCERR)中断挂起位。
name SPI_I2S_ClrITPendingBit.
fun Clears the SPIx CRC Error (CRCERR) interrupt pending bit.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
param SPI_I2S_IT :
- SPI_INT_CRCERR
return none.
void SPI_I2S_FIFO_Cmd(SPI_Module* SPIx, FunctionalState NewState)使能或禁用 FIFO(SPI/I2S 模式)。
name SPI_I2S_FIFO_Cmd.
fun Enables or disables the FIFO (in SPI/I2S mode).
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
param Cmd :
- ENABLE
- DISABLE
return none.
void SPI_I2S_ClearFIFOBit(SPI_Module* SPIx, uint16_t SPI_I2S_FIFO_Clear)清除 SPIx 的 FIFO 位。
name SPI_I2S_ClearFIFOBit.
fun Clears the SPIx FIFO bit.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
param SPI_I2S_FIFO_Clear :
- SPI_I2S_FIFO_CLR
return none.
void SPI_RxFIFOSizeConfig(SPI_Module* SPIx, uint16_t SPI_FIFOSize)配置所选 SPI 的接收 FIFO 大小。
name SPI_RxFIFOSizeConfig.
fun Configures the Rx FIFO size for the selected SPI.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
param SPI_FIFOSize :
- SPI_RX_FIF0NUM_7
- SPI_RX_FIF0NUM_6
- SPI_RX_FIF0NUM_5
- SPI_RX_FIF0NUM_4
- SPI_RX_FIF0NUM_3
- SPI_RX_FIF0NUM_2
- SPI_RX_FIF0NUM_1
- SPI_RX_FIF0NUM_0
return none.
void SPI_TxFIFOSizeConfig(SPI_Module* SPIx, uint16_t SPI_FIFOSize)配置所选 SPI 的发送 FIFO 大小。
name SPI_TxFIFOSizeConfig.
fun Configures the Tx FIFO size for the selected SPI.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
param SPI_FIFOSize :
- SPI_TX_FIF0NUM_7
- SPI_TX_FIF0NUM_6
- SPI_TX_FIF0NUM_5
- SPI_TX_FIF0NUM_4
- SPI_TX_FIF0NUM_3
- SPI_TX_FIF0NUM_2
- SPI_TX_FIF0NUM_1
- SPI_TX_FIF0NUM_0
return none.
uint16_t SPI_RX_FIFO_CNT_GET(const SPI_Module* SPIx)在 FIFO 模式下,当前有效 FIFO 的个数。
name SPI_RX_FIFO_CNT_GET.
fun In FIFO mode, the current number of valid FIFOs.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
return Current effective number of FIFOs.
uint16_t SPI_TX_FIFO_CNT_GET(const SPI_Module* SPIx)在 FIFO 模式下,当前有效 FIFO 的个数。
name SPI_TX_FIFO_CNT_GET.
fun In FIFO mode, the current number of valid FIFOs.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
return Current effective number of FIFOs.
void SPI_TRANSNUM_SET(SPI_Module* SPIx, uint16_t Data)FIFO 模式且启用 CRC 功能时,用户需提前写入待传输数据的个数。
name SPI_TRANSNUM_SET.
fun When in FIFO mode with CRC function,
the user needs to write the number of data to be transmitted in advance.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
param Data \* Data to be transmitted *\
return none.
uint16_t SPI_TRANSNUM_GET(const SPI_Module* SPIx)FIFO 模式且启用 CRC 功能时,用户提前写入的待传输数据个数。
name SPI_TRANSNUM_GET.
fun When in FIFO mode with CRC function,
the user write the number of data to be transmitted in advance.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
return The number of data transferred.
void SPI_DELAYTIME_SET(SPI_Module* SPIx, uint16_t Data)配置 SPI 主时钟延迟时间。
name SPI_DELAYTIME_SET.
fun SPI master clock delay time configuration.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
param Data \* Data to be transmitted *\
return none.
uint16_t SPI_DELAYTIME_GET(const SPI_Module* SPIx)获取 SPI 主时钟延迟时间。
name SPI_DELAYTIME_GET.
fun Get SPI master clock delay time.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
return The clock delay time.
void SPI_RX_FIFO_SET(SPI_Module* SPIx, uint16_t Data)在 FIFO 模式下,写入接收 FIFO 的数据。
name SPI_RX_FIFO_SET.
fun In FIFIO mode, write the data in the receiving FIFO.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
param Data \* Data to be transmitted *\
return none.
uint16_t SPI_RX_FIFO_GET(const SPI_Module* SPIx)在 FIFO 模式下,读取接收 FIFO 的数据。
name SPI_RX_FIFO_GET.
fun In FIFIO mode, read the data in the receiving FIFO.
param SPIx :
- 1, 2, 3, 4, 5 or 6 in SPI mode
- 2 or 3 in I2S mode
return Receive data in FIFO.
5.26.3 Demo 示例说明
本节说明 SPI 的 8 个示例:CRC 与 CRC_FIFO 在两块 SPI 之间做带 CRC 的全双工互传(后者经 FIFO),FullDuplex_SoftNSS 用软件 NSS 分两阶段交换主从,Simplex_Interrupt 用 TE、RNE 中断做单线收发,SPI_DMA_T&R 与 SPI_Simplex_DMA_Recv 用 DMA 做单线发送与接收,SPI_FLASH 与 SPI_FLASH_DMA 读写 W25Q128。SPI5 作主机使用 PF6~PF9,SPI2 作从机使用 PC2、PC7、PD1、PD2,两两之间需用跳线连接。
5.26.3.1 CRC 双机全双工 CRC 校验
工程目录: projects/n32h49x_EVAL/examples/SPI/CRC/
配置系统时钟与 GPIO 复用→初始化主机与从机→使能两端 CRC 计算与 SPI→主从同时全双工互发数据→末帧一并发送 CRC→比对数据与 CRC 并检查校验错误
| 项目 | 源码解读 |
|---|---|
| 功能 | SPI5 作主机、SPI2 作从机,以 16 位数据全双工互传 32 帧,收发结束后发送 CRC 值并比对,检查数据与 CRC 状态。 |
| 源码组成 | src/main.c:收发缓冲区、main 与 RCC_Configuration、GPIO_Configuration、Buffercmp;src/n32h49x_it.c、inc/n32h49x_it.h:中断服务函数;inc/main.h:外设、引脚与复用宏;readme.txt:功能、环境与接线说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | 数据方向为 SPI_DIR_DOUBLELINE_FULLDUPLEX,16 位数据,主机 SPI5、从机 SPI2;时钟极性/相位为 SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE,NSS 为 SPI_NSS_SOFT,预分频 128,位序 MSB,CRC 多项式为 7;SPI5 引脚 SCK:PF7、MISO:PF8、MOSI:PF9(AF11),SPI2 引脚 SCK:PC7、MISO:PC2、MOSI:PD2; 系统时钟为 HSI+PLL、240 MHz。 |
| 程序与数据路径 | main 初始化两端并调用 SPI_EnableCalculateCrc 打开 CRC 计算;循环内 SPI5 与 SPI2 同时收发,写 SPI_I2S_TransmitData、读 SPI_I2S_ReceiveData;发送最后一帧时以 SPI_TransmitCrcNext 触发 CRC 发送;最后用 Buffercmp 比对收发数据并检查 SPI_CRCERR_FLAG。 |
| 主要 API | SPI_InitStruct、SPI_Init、SPI_Enable、SPI_EnableCalculateCrc、SPI_TransmitCrcNext、SPI_I2S_TransmitData、SPI_I2S_ReceiveData、SPI_I2S_GetStatus |
| 验证方法 | 按 readme 用跳线连接 SPI5 与 SPI2 的 SCK、MISO、MOSI;TransferStatus1、TransferStatus2 均为 PASSED 且未置 CRCERR,表示数据与 CRC 校验正确。 |
| 使用提示 | 须用跳线在同一块板上对连 SPI5 与 SPI2; 从机使能 CRC 计算前要先等时钟稳定; 基于 N32H497ZGL7_STB 评估板; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例把 SPI5 配成主机、SPI2 配成从机,两端都打开 CRC 计算,在最后一帧发送前把 CRCNEXT 置位,让硬件在数据后附发 CRC 值。
数据路径: SPI_Master_Buffer_Tx → SPI_I2S_TransmitData → SPI5 SPI_DAT → PF9(MOSI)→ SPI2 → PC2(MISO)→ SPI2_Buffer_Rx → Buffercmp
1. 初始化主机与从机。 先填默认值,再按主机参数配置 SPI5;从机只改工作模式后复用同一结构体。
SPI_InitStruct(&SPI_InitStructure);
/* SPI_Master configuration ------------------------------------------------------*/
SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
SPI_InitStructure.SpiMode = SPI_MODE_MASTER;
SPI_InitStructure.DataLen = SPI_DATA_SIZE_16BITS;
SPI_InitStructure.CLKPOL = SPI_CLKPOL_HIGH;
SPI_InitStructure.CLKPHA = SPI_CLKPHA_FIRST_EDGE;
SPI_InitStructure.NSS = SPI_NSS_SOFT;
SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_128;
SPI_InitStructure.FirstBit = SPI_FB_MSB;
SPI_InitStructure.CRCPoly = 7;
SPI_Init(SPI_Master, &SPI_InitStructure);
/* SPI2 configuration ------------------------------------------------------*/
SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
SPI_Init(SPI2, &SPI_InitStructure);
2. 打开两端 CRC 计算并使能。 CRC 使能位置位即清零收发 CRC 寄存器。
/* Enable SPI_Master CRC calculation */
SPI_EnableCalculateCrc(SPI_Master, ENABLE);
/* Enable SPI2 CRC calculation */
SPI_EnableCalculateCrc(SPI2, ENABLE);
/* Enable SPI_Master */
SPI_Enable(SPI_Master, ENABLE);
/* Enable SPI2 */
SPI_Enable(SPI2, ENABLE);
3. 全双工互传并在末帧发送 CRC。 主从在同一循环里各写各发、各读各收。
/* Wait for SPI_Master Tx buffer empty */
while (SPI_I2S_GetStatus(SPI_Master, SPI_I2S_TE_FLAG) == RESET)
;
/* Send SPI2 data */
SPI_I2S_TransmitData(SPI2, SPI2_Buffer_Tx[TxIdx]);
/* Send SPI_Master data */
SPI_I2S_TransmitData(SPI_Master, SPI_Master_Buffer_Tx[TxIdx++]);
4. 检查 CRC 结果并读出接收 CRC 值。 任一端的 CRCERR 置位即判失败,随后读取两端接收到的 CRC 寄存器值。
/* Test on the SPI_Master CRC Error flag */
if ((SPI_I2S_GetStatus(SPI_Master, SPI_CRCERR_FLAG)) == SET)
{
TransferStatus2 = FAILED;
}
/* Test on the SPI2 CRC Error flag */
if ((SPI_I2S_GetStatus(SPI2, SPI_CRCERR_FLAG)) == SET)
{
TransferStatus1 = FAILED;
}
/* Read SPI_Master received CRC value */
CRC1Value = SPI_I2S_ReceiveData(SPI_Master);
/* Read SPI2 received CRC value */
CRC2Value = SPI_I2S_ReceiveData(SPI2);
对应源码: src/main.c、inc/main.h
5.26.3.2 CRC_FIFO 经 FIFO 的 CRC 校验收发
工程目录: projects/n32h49x_EVAL/examples/SPI/CRC_FIFO/
配置系统时钟与 GPIO 复用→初始化主机与从机→设置收发 FIFO 水位并使能 FIFO→使能两端 CRC 计算与 SPI→经 FIFO 全双工互发数据→末帧发送 CRC 并从接收 FIFO 取数比对
| 项目 | 源码解读 |
|---|---|
| 功能 | 在 CRC 全双工互传的基础上启用 FIFO,SPI5 与 SPI2 经收发 FIFO 互传 32 帧 16 位数据,末帧发送 CRC 并从接收 FIFO 读出比对。 |
| 源码组成 | src/main.c:收发缓冲区、main 与 RCC_Configuration、GPIO_Configuration、Buffercmp;src/n32h49x_it.c、inc/n32h49x_it.h:中断服务函数;inc/main.h:外设、引脚与复用宏;readme.txt:功能、环境与接线说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | 数据方向为 SPI_DIR_DOUBLELINE_FULLDUPLEX,16 位数据,主机 SPI5、从机 SPI2;两端收发 FIFO 水位均设为 SPI_RX_FIF0NUM_1/SPI_TX_FIF0NUM_1 并以 SPI_I2S_FIFO_Cmd 使能 FIFO;时钟极性/相位为 SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE,NSS 为 SPI_NSS_SOFT,预分频 128,位序 MSB,CRC 多项式为 7;系统时钟为 HSI+PLL、240 MHz。 |
| 程序与数据路径 | main 初始化两端后先设置收发 FIFO 水位再使能 FIFO,并调用 SPI_EnableCalculateCrc 打开 CRC;循环内主从同时收发,接收改用 SPI_RX_FIFO_GET 从接收 FIFO 取数;末帧以 SPI_TransmitCrcNext 触发 CRC 发送;最后用 Buffercmp 比对收发数据并检查 SPI_CRCERR_FLAG。 |
| 主要 API | SPI_InitStruct、SPI_Init、SPI_Enable、SPI_RxFIFOSizeConfig、SPI_TxFIFOSizeConfig、SPI_I2S_FIFO_Cmd、SPI_EnableCalculateCrc、SPI_TransmitCrcNext、SPI_RX_FIFO_GET |
| 验证方法 | 按 readme 用跳线连接 SPI5 与 SPI2 的 SCK、MISO、MOSI;TransferStatus1、TransferStatus2 均为 PASSED 且未置 CRCERR,表示数据与 CRC 校验正确。 |
| 使用提示 | 须用跳线在同一块板上对连 SPI5 与 SPI2; 使能 FIFO 后接收须读接收 FIFO,未使能时读数据寄存器; 基于 N32H497ZGL7_STB 评估板; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例在 CRC 互传的基础上打开 8 字节收发 FIFO,并把读取接收数据的方式从数据寄存器改为接收 FIFO。
数据路径: SPI_Master_Buffer_Tx → SPI_I2S_TransmitData → SPI5 TX_FIFO → PF9(MOSI)→ SPI2 RX_FIFO → SPI_RX_FIFO_GET → SPI2_Buffer_Rx → Buffercmp
1. 设置收发 FIFO 水位并使能 FIFO。 两端先配水位再打开 FIFO,水位决定半满/半空标志的触发点。
/* Set the FIFO half-full watermark */
SPI_RxFIFOSizeConfig(SPI_Master, SPI_RX_FIF0NUM_1);
SPI_TxFIFOSizeConfig(SPI_Master, SPI_TX_FIF0NUM_1);
/* Enable FIFO */
SPI_I2S_FIFO_Cmd(SPI_Master, ENABLE);
/* Set the FIFO half-full watermark */
SPI_RxFIFOSizeConfig(SPI2, SPI_RX_FIF0NUM_1);
SPI_TxFIFOSizeConfig(SPI2, SPI_TX_FIF0NUM_1);
/* Enable FIFO */
SPI_I2S_FIFO_Cmd(SPI2, ENABLE);
2. 使能两端 CRC 计算与 SPI。 CRC 寄存器在使能位一置位时被清零。
/* Enable SPI_Master CRC calculation */
SPI_EnableCalculateCrc(SPI_Master, ENABLE);
/* Enable SPI2 CRC calculation */
SPI_EnableCalculateCrc(SPI2, ENABLE);
/* Enable SPI_Master */
SPI_Enable(SPI_Master, ENABLE);
/* Enable SPI2 */
SPI_Enable(SPI2, ENABLE);
3. 经接收 FIFO 读取数据。 使能 FIFO 后接收数据不再读数据寄存器,而是从接收 FIFO 取出。
/* Wait for SPI2 data reception */
while (SPI_I2S_GetStatus(SPI2, SPI_I2S_RNE_FLAG) == RESET)
;
/* Read SPI2 received data */
SPI2_Buffer_Rx[RxIdx] = SPI_RX_FIFO_GET(SPI2);
/* Wait for SPI_Master data reception */
while (SPI_I2S_GetStatus(SPI_Master, SPI_I2S_RNE_FLAG) == RESET)
;
/* Read SPI_Master received data */
SPI_Master_Buffer_Rx[RxIdx++] = SPI_RX_FIFO_GET(SPI_Master);
4. 读出接收 CRC 值。 末帧 CRC 发送完成后,同样从接收 FIFO 读取两端收到的 CRC 值。
/* Read SPI_Master received CRC value */
CRC1Value = SPI_RX_FIFO_GET(SPI_Master);
/* Read SPI2 received CRC value */
CRC2Value = SPI_RX_FIFO_GET(SPI2);
对应源码: src/main.c、inc/main.h
5.26.3.3 FullDuplex_SoftNSS 软件 NSS 全双工主从互换
工程目录: projects/n32h49x_EVAL/examples/SPI/FullDuplex_SoftNSS/
第一阶段配置 SPI5 主机、SPI2 从机→软件 NSS 下全双工互传并比对→停止并复位两端→交换主从后重新配置复用→第二阶段 SPI2 主机、SPI5 从机再互传→复位计数与缓冲区并比对结果
| 项目 | 源码解读 |
|---|---|
| 功能 | SPI5 与 SPI2 用软件 NSS 全双工互传 32 字节,第一阶段 SPI5 主、SPI2 从,第二阶段交换为主从后再传一次,两阶段各自比对收发数据。 |
| 源码组成 | src/main.c:收发缓冲区、main 与 RCC_Configuration、GPIO_Configuration、Buffercmp;src/n32h49x_it.c、inc/n32h49x_it.h:中断服务函数;inc/main.h:外设、引脚与复用宏;readme.txt:功能、环境与接线说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | 数据方向为 SPI_DIR_DOUBLELINE_FULLDUPLEX,8 位数据,NSS 为 SPI_NSS_SOFT;时钟极性/相位为 SPI_CLKPOL_LOW/SPI_CLKPHA_FIRST_EDGE,预分频 8,位序 LSB,CRC 多项式为 7;两阶段仅交换 SpiMode,其余参数相同, GPIO_Configuration 按主从模式切换 SCK、MOSI 引脚为复用输出或输入;系统时钟为 HSI+PLL、240 MHz。 |
| 程序与数据路径 | 第一阶段 main 配置 SPI5 主、SPI2 从后全双工互传,用 Buffercmp 得到 TransferStatus1、TransferStatus2;SPI_I2S_DeInit 复位两端后交换主从,再次配置并重传;第二阶段前清零收发索引与接收缓冲区,比对得到 TransferStatus3、TransferStatus4。 |
| 主要 API | SPI_InitStruct、SPI_Init、SPI_Enable、SPI_I2S_DeInit、SPI_I2S_TransmitData、SPI_I2S_ReceiveData、SPI_I2S_GetStatus |
| 验证方法 | 按 readme 用跳线连接 SPI5 与 SPI2 的 SCK、MISO、MOSI; 四个 TransferStatus 全为 PASSED,表示两阶段主从互换收发均正确。 |
| 使用提示 | 须用跳线在同一块板上对连 SPI5 与 SPI2; 切换主从前调用 SPI_I2S_DeInit 复位寄存器,并重配 GPIO 复用方向;基于 N32H497ZGL7_STB 评估板; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例分两个阶段做全双工对传,阶段之间复位两端并交换主从,用来验证软件 NSS 下角色可以互换。
数据路径: SPI_Master_Buffer_Tx ⇄ SPI5 ⇄ PF7/PF8/PF9 ⇄ SPI2 ⇄ SPI2_Buffer_Rx,第二阶段主从互换后数据流向相反
1. 第一阶段配置主机与从机。 SPI5 配成主机,SPI2 只改工作模式后沿用同一参数。
SPI_InitStruct(&SPI_InitStructure);
/* SPI_Master Config -------------------------------------------------------------*/
SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
SPI_InitStructure.SpiMode = SPI_MODE_MASTER;
SPI_InitStructure.DataLen = SPI_DATA_SIZE_8BITS;
SPI_InitStructure.CLKPOL = SPI_CLKPOL_LOW;
SPI_InitStructure.CLKPHA = SPI_CLKPHA_FIRST_EDGE;
SPI_InitStructure.NSS = SPI_NSS_SOFT;
SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_8;
SPI_InitStructure.FirstBit = SPI_FB_LSB;
SPI_InitStructure.CRCPoly = 7;
SPI_Init(SPI_Master, &SPI_InitStructure);
/* SPI2 Config -------------------------------------------------------------*/
SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
SPI_Init(SPI2, &SPI_InitStructure);
2. 复位两端并交换主从。 两阶段之间先用 SPI_I2S_DeInit 把寄存器复位,再把 SPI5 改从、SPI2 改主。
SPI_I2S_DeInit(SPI_Master);
SPI_I2S_DeInit(SPI2);
SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
SPI_InitStructure.DataLen = SPI_DATA_SIZE_8BITS;
SPI_InitStructure.CLKPOL = SPI_CLKPOL_LOW;
SPI_InitStructure.CLKPHA = SPI_CLKPHA_FIRST_EDGE;
SPI_InitStructure.NSS = SPI_NSS_SOFT;
SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_8;
SPI_InitStructure.FirstBit = SPI_FB_LSB;
SPI_InitStructure.CRCPoly = 7;
SPI_Init(SPI_Master, &SPI_InitStructure);
/* SPI2 Re-configuration ---------------------------------------------------*/
SPI_InitStructure.SpiMode = SPI_MODE_MASTER;
SPI_Init(SPI2, &SPI_InitStructure);
3. 第二阶段收发。 主从互换后,收发顺序随之改变,但读写数据的写法不变。
/* Send SPI_Master data */
SPI_I2S_TransmitData(SPI_Master, SPI_Master_Buffer_Tx[TxIdx]);
/* Send SPI2 data */
SPI_I2S_TransmitData(SPI2, SPI2_Buffer_Tx[TxIdx++]);
/* Wait for SPI_Master data reception */
while (SPI_I2S_GetStatus(SPI_Master, SPI_I2S_RNE_FLAG) == RESET)
;
/* Read SPI_Master received data */
SPI_Master_Buffer_Rx[RxIdx] = SPI_I2S_ReceiveData(SPI_Master);
/* Wait for SPI2 data reception */
while (SPI_I2S_GetStatus(SPI2, SPI_I2S_RNE_FLAG) == RESET)
;
/* Read SPI2 received data */
SPI2_Buffer_Rx[RxIdx++] = SPI_I2S_ReceiveData(SPI2);
4. 比对第二阶段结果。 每个阶段都用 Buffercmp 比较接收缓冲区与对端发送缓冲区。
TransferStatus3 = Buffercmp(SPI2_Buffer_Rx, SPI_Master_Buffer_Tx, BufferSize);
TransferStatus4 = Buffercmp(SPI_Master_Buffer_Rx, SPI2_Buffer_Tx, BufferSize);
对应源码: src/main.c、inc/main.h
5.26.3.4 Simplex_Interrupt 单线中断收发
工程目录: projects/n32h49x_EVAL/examples/SPI/Simplex_Interrupt/
配置系统时钟、NVIC 与 GPIO 复用→初始化主机单线只发、从机单线只收→使能 NSS 输出与两端收发中断→使能 SPI 进入主循环等待→主机在发送中断里逐帧发出、从机在接收中断里逐帧接收→收满缓冲后比对并打印结果
| 项目 | 源码解读 |
|---|---|
| 功能 | SPI5 作主机单线只发、SPI2 作从机单线只收,主机以 TE 中断逐帧发送、从机以 RNE 中断逐帧接收 32 字节,收满后比对并打印结果。 |
| 源码组成 | src/main.c:发送缓冲区、接收缓冲区、main 与 RCC_Configuration、GPIO_Configuration、NVIC_Configuration、Buffercmp;src/n32h49x_it.c、inc/n32h49x_it.h:SPI5 与 SPI2 中断服务函数;inc/main.h:外设、引脚与复用宏;readme.txt:功能、环境与接线说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | 主机数据方向为 SPI_DIR_SINGLELINE_TX、从机为 SPI_DIR_SINGLELINE_RX,8 位数据,NSS 为 SPI_NSS_HARD;时钟极性/相位为 SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE,预分频 8,位序 MSB;主机使能 SPI_I2S_INT_TE、从机使能 SPI_I2S_INT_RNE,NVIC 中 SPI5 抢占 1、SPI2_I2S2 抢占 0;SPI5 引脚 SCK:PF7、MOSI:PF9,SPI2 引脚 SCK:PC7、MISO:PC2。 |
| 程序与数据路径 | main 配置 GPIO 与 NVIC 后初始化两端并使能中断与 SPI;SPI5_IRQHandler 在 TE 中断里发送 SPI_Master_Buffer_Tx[TxIdx++],发满后关 TE 中断;SPI2_I2S2_IRQHandler 在 RNE 中断里把 SPI_I2S_ReceiveData 存入 SPI2_Buffer_Rx[RxIdx++];主循环等到收满后 Buffercmp 比对并打印结果。 |
| 主要 API | SPI_InitStruct、SPI_Init、SPI_SSOutputEnable、SPI_I2S_EnableInt、SPI_Enable、SPI_I2S_GetIntStatus、SPI_I2S_TransmitData、SPI_I2S_ReceiveData |
| 验证方法 | 按 readme 用跳线连接 SPI5 与 SPI2 的 SCK 与单线数据线; 串口打印 Test PASS!! 表示 TransferStatus 为 PASSED,收发数据一致。 |
| 使用提示 | 单线数据线在主机端为 MOSI 引脚、在从机端为 MISO 引脚; 主循环用收满计数退出、不轮询中断标志; 基于 N32H497ZGL7_STB 评估板; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例用中断而不是查询方式收发:主机在发送缓冲空时进中断发下一字节,从机在接收缓冲非空时进中断收字节。
数据路径: SPI_Master_Buffer_Tx → SPI5_IRQHandler(TE 中断)→ SPI5 SPI_DAT → PF9(MOSI)→ 单线 → PC2(MISO)→ SPI2_I2S2_IRQHandler(RNE 中断)→ SPI2_Buffer_Rx
1. 配置单线发送与接收。 主机配成单线只发、从机配成单线只收,NSS 采用硬件模式。
SPI_InitStruct(&SPI_InitStructure);
/* SPI_Master configuration ------------------------------------------------*/
SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX;
SPI_InitStructure.SpiMode = SPI_MODE_MASTER;
SPI_InitStructure.DataLen = SPI_DATA_SIZE_8BITS;
SPI_InitStructure.CLKPOL = SPI_CLKPOL_HIGH;
SPI_InitStructure.CLKPHA = SPI_CLKPHA_FIRST_EDGE;
SPI_InitStructure.NSS = SPI_NSS_HARD;
SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_8;
SPI_InitStructure.FirstBit = SPI_FB_MSB;
SPI_InitStructure.CRCPoly = 7;
SPI_Init(SPI_Master, &SPI_InitStructure);
/* SPI2 configuration -------------------------------------------------*/
SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_RX;
SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
SPI_Init(SPI2, &SPI_InitStructure);
2. 打开两端中断并使能 SPI。 主机使能发送中断、从机使能接收中断,再使能两个外设。
/* Enable SPI_Master TXE interrupt */
SPI_I2S_EnableInt(SPI_Master, SPI_I2S_INT_TE, ENABLE);
/* Enable SPI2 RXNE interrupt */
SPI_I2S_EnableInt(SPI2, SPI_I2S_INT_RNE, ENABLE);
/* Enable SPI2 */
SPI_Enable(SPI2, ENABLE);
/* Enable SPI_Master */
SPI_Enable(SPI_Master, ENABLE);
3. 配置并打开 NVIC。 采用 1 位抢占、3 位子优先级,SPI5 与 SPI2 分设不同抢占级。
/* 1 bit for pre-emption priority, 3 bits for subpriority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* Configure and enable SPI_Master interrupt -------------------------------*/
NVIC_InitStructure.NVIC_IRQChannel = SPI5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Configure and enable SPI2 interrupt --------------------------------*/
NVIC_InitStructure.NVIC_IRQChannel = SPI2_I2S2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
4. 主机 TE 中断发送。 每次发送缓冲空时写入下一字节,发满后关闭发送中断。
if (SPI_I2S_GetIntStatus(SPI5, SPI_I2S_INT_TE) != RESET)
{
/* Send SPI5 data */
SPI_I2S_TransmitData(SPI5, SPI_Master_Buffer_Tx[TxIdx++]);
/* Disable SPI5 TXE interrupt */
if (TxIdx == BufferSize)
{
SPI_I2S_EnableInt(SPI5, SPI_I2S_INT_TE, DISABLE);
}
}
5. 从机 RNE 中断接收。 每次接收缓冲非空时读出一字节存入接收缓冲区。
/* Check the interrupt source */
if (SPI_I2S_GetIntStatus(SPI2, SPI_I2S_INT_RNE) == SET)
{
/* Store SPI2 received data */
SPI2_Buffer_Rx[RxIdx++] = SPI_I2S_ReceiveData(SPI2);
}
对应源码: src/main.c、src/n32h49x_it.c、inc/main.h
5.26.3.5 SPI_DMA_T&R DMA 单线发送与接收
工程目录: projects/n32h49x_EVAL/examples/SPI/SPI_DMA_T&R/
配置时钟、GPIO 与片选空闲电平→配置从机接收 DMA 通道→配置主机发送 DMA 通道→初始化主机单线只发、从机单线只收→打开收发 DMA 请求并使能 SPI 与 DMA 通道→等 DMA 完成并比对数据打印结果
| 项目 | 源码解读 |
|---|---|
| 功能 | SPI5 作主机单线只发、SPI2 作从机单线只收,发送由 DMA1_CH3 逐字节搬出、接收由 DMA1_CH4 逐字节搬入缓冲,完成后比对并打印结果。 |
| 源码组成 | src/main.c:发送缓冲区、接收缓冲区、main 与 RCC_Configuration、GPIO_Configuration、Buffercmp;src/n32h49x_it.c、inc/n32h49x_it.h:中断服务函数;inc/main.h:外设、引脚、复用、DMA 通道与完成标志宏;readme.txt:功能、环境与接线说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | 主机数据方向为 SPI_DIR_SINGLELINE_TX、从机为 SPI_DIR_SINGLELINE_RX,8 位数据,NSS 为 SPI_NSS_HARD;时钟极性/相位为 SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE,预分频 8,位序 MSB;DMA 为字节宽度、外设地址不递增、存储器地址递增、普通模式、最高优先级,发送用 DMA1_CH3、接收用 DMA1_CH4; 主机引脚 NSS:PF6、SCK:PF7、MOSI:PF9,从机引脚 NSS:PD1、SCK:PC7、MISO:PC2。 |
| 程序与数据路径 | main 先配置从机接收与主机发送两条 DMA 通道并重映射请求;初始化两端后调用 SPI_I2S_EnableDma 打开从机接收与主机发送请求;使能 SPI 与两条 DMA 通道后等两个完成标志; 最后用 Buffercmp 比对 SPI_SLAVE_Buffer_Rx 与发送缓冲区并打印结果。 |
| 主要 API | SPI_InitStruct、SPI_Init、SPI_SSOutputEnable、SPI_I2S_EnableDma、SPI_Enable、DMA_Init、DMA_RequestRemap、DMA_EnableChannel、DMA_GetFlagStatus |
| 验证方法 | 按 readme 用跳线连接 SPI5 与 SPI2 的 NSS、SCK 与单线数据线; 串口打印 Test PASS!! 表示 TransferStatus 为 PASSED,DMA 收发数据一致。 |
| 使用提示 | 单线数据线在主机端为 MOSI 引脚、在从机端为 MISO 引脚; DMA 请求要先于 SPI 使能打开; 基于 N32H497ZGL7_STB 评估板; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例把发送与接收都交给 DMA:主机端一条通道把发送缓冲区写进数据寄存器,从机端另一条通道把接收数据寄存器读进缓冲区。
数据路径: SPI_MASTER_Buffer_Tx → DMA1_CH3 → SPI_MASTER->DAT → SPI5 → PF9(MOSI)→ 单线 → PC2(MISO)→ SPI2 → SPI_SLAVE->DAT → DMA1_CH4 → SPI_SLAVE_Buffer_Rx → Buffercmp
1. 配置从机接收 DMA 通道。 方向为外设到存储器,从 SPI_SLAVE->DAT 读字节写入接收缓冲区。
DMA_DeInit(SPI_SLAVE_Rx_DMA_Channel);
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.PeriphAddr = (uint32_t)&SPI_SLAVE->DAT;
DMA_InitStructure.MemAddr = (uint32_t)SPI_SLAVE_Buffer_Rx;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC;
DMA_InitStructure.BufSize = BufferSize;
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
2. 配置主机发送 DMA 通道。 复用同一结构体,只把方向改成存储器到外设并换成发送侧地址。
/* SPI_MASTER_Tx_DMA_Channel configuration ---------------------------------------------*/
DMA_DeInit(SPI_MASTER_Tx_DMA_Channel);
DMA_InitStructure.PeriphAddr = (uint32_t)&SPI_MASTER->DAT;
DMA_InitStructure.MemAddr = (uint32_t)SPI_MASTER_Buffer_Tx;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_DST;
DMA_Init(SPI_MASTER_Tx_DMA_Channel, &DMA_InitStructure);
DMA_RequestRemap(SPI_MASTER_Tx_DMA_Remap, SPI_MASTER_Tx_DMA_Channel, ENABLE);
3. 初始化主机与从机。 主机配成单线只发、从机配成单线只收,随后打开主机的 NSS 输出。
/* SPI_MASTER configuration ------------------------------------------------------*/
SPI_InitStruct(&SPI_InitStructure);
SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX;
SPI_InitStructure.SpiMode = SPI_MODE_MASTER;
SPI_InitStructure.DataLen = SPI_DATA_SIZE_8BITS;
SPI_InitStructure.CLKPOL = SPI_CLKPOL_HIGH;
SPI_InitStructure.CLKPHA = SPI_CLKPHA_FIRST_EDGE;
SPI_InitStructure.NSS = SPI_NSS_HARD;
SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_8;
SPI_InitStructure.FirstBit = SPI_FB_MSB;
SPI_InitStructure.CRCPoly = 7;
SPI_Init(SPI_MASTER, &SPI_InitStructure);
/* SPI_SLAVE configuration ------------------------------------------------------*/
SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_RX;
SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
SPI_Init(SPI_SLAVE, &SPI_InitStructure);
4. 打开收发 DMA 请求。 从机使能接收请求、主机使能发送请求,请求要先于 SPI 使能打开。
/* Enable SPI_MASTER NSS output for master mode */
SPI_SSOutputEnable(SPI_MASTER, ENABLE);
/* Enable SPI_SLAVE Rx request */
SPI_I2S_EnableDma(SPI_SLAVE, SPI_I2S_DMA_RX, ENABLE);
/* Enable SPI_MASTER Tx request */
SPI_I2S_EnableDma(SPI_MASTER, SPI_I2S_DMA_TX, ENABLE);
5. 使能 SPI 与 DMA 通道。 先使能两个 SPI,再打开两条 DMA 通道触发搬运。
/* Enable SPI_SLAVE */
SPI_Enable(SPI_SLAVE, ENABLE);
/* Enable SPI_MASTER */
SPI_Enable(SPI_MASTER, ENABLE);
/* Enable DMA1 Channel3 */
DMA_EnableChannel(SPI_MASTER_Tx_DMA_Channel, ENABLE);
/* Enable DMA1 Channel4 */
DMA_EnableChannel(SPI_SLAVE_Rx_DMA_Channel, ENABLE);
对应源码: src/main.c、inc/main.h
5.26.3.6 SPI_FLASH 读写与擦除 W25Q128
工程目录: projects/n32h49x_EVAL/examples/SPI/SPI_FLASH/
初始化 SPI FLASH 驱动→读取并校验 W25Q128 的 ID→擦除扇区并写入发送缓冲区→读回数据并比对→再擦除扇区并读回校验为全 0xFF→串口打印读写与擦除结果
| 项目 | 源码解读 |
|---|---|
| 功能 | 通过 SPI3 驱动外挂 W25Q128,先读 ID 校验型号,再擦除指定扇区、写入一段字符串并读回比对,最后擦除后读回校验全为 0xFF。 |
| 源码组成 | src/main.c:发送/接收缓冲区、main 与 Buffercmp;src/spi_flash.c:驱动实现 sFLASH_Init、sFLASH_ReadID、sFLASH_EraseSector、sFLASH_WriteBuffer、sFLASH_ReadBuffer 与底层收发;inc/spi_flash.h:命令、容量与引脚等宏;readme.txt:功能、环境与接线说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | 驱动内 SPI 为 SPI_DIR_DOUBLELINE_FULLDUPLEX、主机、8 位数据,SPI_CLKPOL_HIGH/SPI_CLKPHA_SECOND_EDGE,NSS 为 SPI_NSS_SOFT,预分频 16,位序 MSB,CRC 多项式为 7;SPI3 引脚 NSS:PA15、SCK:PB3、MISO:PD11、MOSI:PD12; 串口 TX:PA9、115200、8 位数据、1 位停止; 系统时钟为 HSI+PLL、240 MHz。 |
| 程序与数据路径 | main 调 sFLASH_Init 初始化驱动,再 sFLASH_ReadID 读 ID 并与 sFLASH_W25Q128_ID 比较;擦除扇区后 sFLASH_WriteBuffer 写入 Tx_Buffer,sFLASH_ReadBuffer 读回 Rx_Buffer;Buffercmp 检查读写一致,再擦除并用循环检查读回数据均为 0xFF。 |
| 主要 API | SPI_InitStruct、SPI_Init、SPI_Enable、SPI_I2S_TransmitData、SPI_I2S_ReceiveData |
| 验证方法 | 按 readme 连接 J20 跳线帽; 串口打印 Read ID 为 W25Q128 的 ID、Flash write OK.、Sector erase OK.,表示读写与擦除均正确。 |
| 使用提示 | 需连接跳线帽 J20 并外挂 W25Q128; 写前须先擦除对应扇区; 基于 N32H497ZGL7_EVB 全功能板; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例用片内 SPI3 以软件片选访问外挂 W25Q128,驱动里封装了读 ID、扇区擦除、页写与读缓冲。
数据路径: Tx_Buffer → sFLASH_WriteBuffer → sFLASH_WritePage → W25Q128 存储单元 → sFLASH_ReadBuffer → Rx_Buffer → Buffercmp
1. 初始化驱动并读取 ID。 先初始化 SPI 与片选,再发 RDID 命令读回三字节组成 ID 并与目标型号比较。
/* Initialize the SPI FLASH driver */
sFLASH_Init();
/* Get SPI Flash ID */
FlashID = sFLASH_ReadID();
printf("Read ID=0x%06x\r\n", FlashID);
/* Check the SPI Flash ID */
if ((FlashID == sFLASH_ID) || (FlashID == sFLASH_W25Q128_ID_DTR))
{
2. 擦除、写入、读回并比对。 写前必须先擦除扇区,写完成后读回并比对。
/* Erase SPI FLASH Sector to write on */
sFLASH_EraseSector(FLASH_SectorToErase);
/* Write Tx_Buffer data to SPI FLASH memory */
sFLASH_WriteBuffer(Tx_Buffer, FLASH_WriteAddress, BufferSize);
/* Read data from SPI FLASH memory */
sFLASH_ReadBuffer(Rx_Buffer, FLASH_ReadAddress, BufferSize);
/* Check the correctness of written dada */
TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);
3. 驱动内的 SPI 初始化。 片选默认拉高,SPI3 配成主机全双工、软件 NSS。
sFLASH_LowLevel_Init();
/*!< Deselect the FLASH: Chip Select high */
sFLASH_CS_HIGH();
/*!< SPI configuration */
SPI_InitStruct(&SPI_InitStructure);
SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
SPI_InitStructure.SpiMode = SPI_MODE_MASTER;
SPI_InitStructure.DataLen = SPI_DATA_SIZE_8BITS;
SPI_InitStructure.CLKPOL = SPI_CLKPOL_HIGH;
SPI_InitStructure.CLKPHA = SPI_CLKPHA_SECOND_EDGE;
SPI_InitStructure.NSS = SPI_NSS_SOFT;
SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_16;
SPI_InitStructure.FirstBit = SPI_FB_MSB;
SPI_InitStructure.CRCPoly = 7;
SPI_Init(sFLASH_SPI, &SPI_InitStructure);
/*!< Enable the sFLASH_SPI */
SPI_Enable(sFLASH_SPI, ENABLE);
4. 读 ID 的实现。 拉低片选后发 0x9F,连续读回三字节并在退出前拉高片选。
sFLASH_CS_LOW();
/*!< Send "RDID " instruction */
sFLASH_SendByte(0x9F);
/*!< Read a byte from the FLASH */
Temp0 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
/*!< Read a byte from the FLASH */
Temp1 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
/*!< Read a byte from the FLASH */
Temp2 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
/*!< Deselect the FLASH: Chip Select high */
sFLASH_CS_HIGH();
Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
return Temp;
对应源码: src/main.c、src/spi_flash.c、inc/spi_flash.h
5.26.3.7 SPI_FLASH_DMA DMA 读写 W25Q128
工程目录: projects/n32h49x_EVAL/examples/SPI/SPI_FLASH_DMA/
初始化 SPI FLASH 驱动→读取并校验 W25Q128 的 ID→填入递增测试数据→擦除扇区并经 DMA 写入→经 DMA 读回并比对→再擦除扇区并读回校验为全 0xFF→串口打印结果
| 项目 | 源码解读 |
|---|---|
| 功能 | 通过 SPI3 加 DMA 访问外挂 W25Q128,读 ID 校验型号后擦除扇区、经 DMA 写入 300 字节递增数据并读回比对,再擦除后读回校验全为 0xFF。 |
| 源码组成 | src/main.c:测试数据与接收缓冲区、main 与 Buffercmp;src/spi_flash.c:驱动实现 sFLASH_DMA_Init、sFLASH_DMA_WriteBuffer、sFLASH_DMA_ReadBuffer 及底层收发;inc/spi_flash.h:命令、容量、引脚与 DMA 通道宏;readme.txt:功能、环境与接线说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | 批量读写走 DMA,读时把 SPI 切为只读模式免去持续发送 0xFF; DMA 为字节宽度、外设地址不递增、存储器地址递增、普通模式、最高优先级; SPI3 引脚 NSS:PA15、SCK:PB3、MISO:PD11、MOSI:PD12; 串口 TX:PA9、115200、8 位数据、1 位停止,系统时钟 HSI+PLL、240 MHz。 |
| 程序与数据路径 | main 先 sFLASH_Init 与 sFLASH_ReadID,并把 Tx_Buffer 填成递增序列;擦除后 sFLASH_DMA_WriteBuffer 经 DMA 写入,sFLASH_DMA_ReadBuffer 经 DMA 读回 Rx_Buffer;Buffercmp 检查读写一致,再擦除并用循环检查读回数据均为 0xFF。 |
| 主要 API | SPI_InitStruct、SPI_Init、SPI_Enable、SPI_I2S_DeInit、SPI_I2S_EnableDma、DMA_Init、DMA_RequestRemap、DMA_EnableChannel、DMA_GetFlagStatus |
| 验证方法 | 按 readme 连接 J20 跳线帽; 串口打印 spi dma write and read flash OK. 与 Sector erase OK.,表示 DMA 读写与擦除均正确。 |
| 使用提示 | 仅大批量读写时用 DMA,读时改为只读模式以提升性能; 需连接跳线帽 J20 并外挂 W25Q128; 基于 N32H497ZGL7_EVB 全功能板; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例让 DMA 承担 Flash 的批量读写;写前先擦除扇区,读时把 SPI 改成只读模式,避免为读数据而持续发送 0xFF。
数据路径: Tx_Buffer → sFLASH_DMA_WriteBuffer → sFLASH_DMA_PageWrite → DMA → W25Q128 → DMA → Rx_Buffer → Buffercmp
1. 填数据并调用 DMA 读写。 擦除扇区后用 DMA 写入,再用 DMA 读回并比对。
sFLASH_Init();
/* Get SPI Flash ID */
FlashID = sFLASH_ReadID();
printf("Read ID=0x%06x\r\n", FlashID);
for(i = 0; i < 512; i++)
{
Tx_Buffer[i] = i + 1;
}
/* Check the SPI Flash ID */
if ((FlashID == sFLASH_ID) || (FlashID == sFLASH_W25Q128_ID_DTR))
{
/* Perform a write in the Flash followed by a read of the written data */
/* Erase SPI FLASH Sector to write on */
sFLASH_EraseSector(FLASH_SectorToErase);
/* Write Tx_Buffer data to SPI FLASH memory */
//SPI_DMA_PageWrite(FLASH_WriteAddress);
sFLASH_DMA_WriteBuffer(Tx_Buffer, FLASH_WriteAddress, BufferSize);
/* Read data from SPI FLASH memory */
//SPI_DMA_BufferRead(FLASH_ReadAddress);
sFLASH_DMA_ReadBuffer(Rx_Buffer, FLASH_ReadAddress, BufferSize);
/* Check the correctness of written dada */
TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);
2. 初始化 DMA 通道。 按收发类型选择通道,外设地址固定指向 SPI 数据寄存器,存储器地址递增。
if(TransmitType == DMA_RECV)
{
/* SPI_FLASH_Rx_DMA_Channel configuration ---------------------------------------------*/
DMA_DeInit(sFLASH_SPI_Rx_DMA_Channel);
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.PeriphAddr = (uint32_t)&sFLASH_SPI->DAT;
DMA_InitStructure.MemAddr = (uint32_t)MemoryAddr;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC;
DMA_InitStructure.BufSize = BufferLen;
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
3. 经 DMA 读回并切只读模式。 发读命令后把 SPI 复位并改为只读,再开 DMA 接收请求并等完成标志。
/* Select the FLASH: Chip Select low */
sFLASH_CS_LOW();
/* Send "Read from Memory " instruction */
sFLASH_SendByte(sFLASH_CMD_READ);
/* Send ReadAddr high nibble address byte to read from */
sFLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
/* Send ReadAddr medium nibble address byte to read from */
sFLASH_SendByte((ReadAddr & 0xFF00) >> 8);
/* Send ReadAddr low nibble address byte to read from */
sFLASH_SendByte(ReadAddr & 0xFF);
/*!< Disable the sFLASH_SPI */
SPI_Enable(sFLASH_SPI, DISABLE);
/*!< DeInitializes the sFLASH_SPI */
SPI_I2S_DeInit(sFLASH_SPI);
/*!< Set the sFLASH_SPI ReadOnly*/
SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_RONLY;
SPI_Init(sFLASH_SPI, &SPI_InitStructure);
sFLASH_DMA_Init(DMA_RECV, (uint32_t)pBuffer, NumByteToRead);
/* Enable DMA1 Channel2 */
DMA_EnableChannel(sFLASH_SPI_Rx_DMA_Channel, ENABLE);
/* Enable sFLASH_SPI Rx request */
SPI_I2S_EnableDma(sFLASH_SPI, SPI_I2S_DMA_RX, ENABLE);
/*!< Enable the sFLASH_SPI */
SPI_Enable(sFLASH_SPI, ENABLE);
/* Wait for sFLASH_SPI_Rx_DMA_FLAG transfer complete */
while (!DMA_GetFlagStatus(sFLASH_SPI_Rx_DMA_FLAG, sFLASH_SPI_DMA));
/* Disnable sFLASH_SPI Rx request */
SPI_I2S_EnableDma(sFLASH_SPI, SPI_I2S_DMA_RX, DISABLE);
/* Disable DMA1 Channel2 */
DMA_EnableChannel(sFLASH_SPI_Rx_DMA_Channel, DISABLE);
/* Deselect the FLASH: Chip Select high */
sFLASH_CS_HIGH();
4. 写缓冲按页拆分。 写 300 字节超过一页,函数先把要写的字节拆成整页与不足一页两部分。
uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
Addr = WriteAddr % sFLASH_SPI_PAGESIZE;
count = sFLASH_SPI_PAGESIZE - Addr;
NumOfPage = NumByteToWrite / sFLASH_SPI_PAGESIZE;
NumOfSingle = NumByteToWrite % sFLASH_SPI_PAGESIZE;
对应源码: src/main.c、src/spi_flash.c、inc/spi_flash.h
5.26.3.8 SPI_Simplex_DMA_Recv DMA 单线接收
工程目录: projects/n32h49x_EVAL/examples/SPI/SPI_Simplex_DMA_Recv/
配置时钟、GPIO 与片选空闲电平→配置从机接收 DMA 通道→初始化主机单线只发、从机单线只收→打开从机接收 DMA 请求并使能 SPI 与 DMA 通道→主机逐帧写数据、从机经 DMA 接收→等 DMA 完成并比对数据打印结果
| 项目 | 源码解读 |
|---|---|
| 功能 | SPI5 作主机单线只发、SPI2 作从机单线只收,主机以查询方式逐字节发送、从机用 DMA1_CH4 把接收数据搬入缓冲,收满后比对并打印结果。 |
| 源码组成 | src/main.c:发送缓冲区、接收缓冲区、main 与 RCC_Configuration、GPIO_Configuration、Buffercmp;src/n32h49x_it.c、inc/n32h49x_it.h:中断服务函数;inc/main.h:外设、引脚、复用、DMA 通道与完成标志宏;readme.txt:功能、环境与接线说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | 主机数据方向为 SPI_DIR_SINGLELINE_TX、从机为 SPI_DIR_SINGLELINE_RX,8 位数据,NSS 为 SPI_NSS_HARD;时钟极性/相位为 SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE,预分频 32,位序 MSB;接收 DMA 为字节宽度、外设地址不递增、存储器地址递增、普通模式、最高优先级,使用 DMA1_CH4; 主机引脚 NSS:PF6、SCK:PF7、MOSI:PF9,从机引脚 NSS:PD1、SCK:PC7、MISO:PC2。 |
| 程序与数据路径 | main 先配置从机接收 DMA 通道并重映射请求;初始化两端后调用 SPI_I2S_EnableDma 打开从机接收请求并使能 SPI 与 DMA 通道;主机在循环里等 TE 后写 SPI_I2S_TransmitData,从机数据经 DMA 写入 SPI_SLAVE_Buffer_Rx;等接收完成标志后用 Buffercmp 比对并打印结果。 |
| 主要 API | SPI_InitStruct、SPI_Init、SPI_SSOutputEnable、SPI_I2S_EnableDma、SPI_Enable、SPI_I2S_GetStatus、SPI_I2S_TransmitData、DMA_Init、DMA_EnableChannel、DMA_GetFlagStatus |
| 验证方法 | 按 readme 用跳线连接 SPI5 与 SPI2 的 NSS、SCK 与单线数据线; 串口打印 Test PASS!! 表示 TransferStatus 为 PASSED,DMA 接收数据与发送一致。 |
| 使用提示 | 单线数据线在主机端为 MOSI 引脚、在从机端为 MISO 引脚; 发送用查询、接收用 DMA,二者共用一个 SPI 时钟; 基于 N32H497ZGL7_STB 评估板; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例只把接收交给 DMA:主机仍逐字节查询发送,从机的接收数据由 DMA 自动搬进缓冲区。
数据路径: SPI_MASTER_Buffer_Tx → SPI_I2S_TransmitData → SPI5 → PF9(MOSI)→ 单线 → PC2(MISO)→ SPI2 → SPI_SLAVE->DAT → DMA1_CH4 → SPI_SLAVE_Buffer_Rx → Buffercmp
1. 配置从机接收 DMA 通道。 方向为外设到存储器,从 SPI_SLAVE->DAT 读字节写入接收缓冲区。
DMA_DeInit(SPI_SLAVE_Rx_DMA_Channel);
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.PeriphAddr = (uint32_t)&SPI_SLAVE->DAT;
DMA_InitStructure.MemAddr = (uint32_t)SPI_SLAVE_Buffer_Rx;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC;
DMA_InitStructure.BufSize = BufferSize;
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
2. 初始化主机与从机并打开请求。 主机配成单线只发、从机配成单线只收,主机打开 NSS 输出、从机打开接收请求。
/* SPI_MASTER configuration ------------------------------------------------------*/
SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX;
SPI_InitStructure.SpiMode = SPI_MODE_MASTER;
SPI_InitStructure.DataLen = SPI_DATA_SIZE_8BITS;
SPI_InitStructure.CLKPOL = SPI_CLKPOL_HIGH;
SPI_InitStructure.CLKPHA = SPI_CLKPHA_FIRST_EDGE;
SPI_InitStructure.NSS = SPI_NSS_HARD;
SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_32;
SPI_InitStructure.FirstBit = SPI_FB_MSB;
SPI_InitStructure.CRCPoly = 7;
SPI_Init(SPI_MASTER, &SPI_InitStructure);
/* SPI_SLAVE configuration ------------------------------------------------------*/
SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_RX;
SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
SPI_Init(SPI_SLAVE, &SPI_InitStructure);
/* Enable SPI_MASTER NSS output for master mode */
SPI_SSOutputEnable(SPI_MASTER, ENABLE);
/* Enable SPI_SLAVE Rx request */
SPI_I2S_EnableDma(SPI_SLAVE, SPI_I2S_DMA_RX, ENABLE);
3. 使能 SPI 与接收 DMA 通道。 先使能两个 SPI,再打开接收 DMA 通道等待搬运。
/* Enable SPI_SLAVE */
SPI_Enable(SPI_SLAVE, ENABLE);
/* Enable SPI_MASTER */
SPI_Enable(SPI_MASTER, ENABLE);
/* Enable DMA1 Channel4 */
DMA_EnableChannel(SPI_SLAVE_Rx_DMA_Channel, ENABLE);
4. 主机逐帧发送并等 DMA 完成。 主机等发送缓冲空后写下一字节,发完等接收 DMA 完成标志。
/* Transfer procedure */
while (TxIdx < BufferSize)
{
/* Wait for SPI_MASTER Tx buffer empty */
while (SPI_I2S_GetStatus(SPI_MASTER, SPI_I2S_TE_FLAG) == RESET)
;
/* Send SPI_MASTER data */
SPI_I2S_TransmitData(SPI_MASTER, SPI_MASTER_Buffer_Tx[TxIdx++]);
}
/* Wait for DMA1 channel4 transfer complete */
while (!DMA_GetFlagStatus(SPI_SLAVE_Rx_DMA_FLAG, DMA1))
;
对应源码: src/main.c、inc/main.h
5.26.4 注意与排障
| 场景 | 检查与建议 | 来源 |
|---|---|---|
| 关闭 SPI 模块的等待条件 | 全双工模式关闭前依次等最后一个数据接收完成、TE 置 1、BUSY 清 0; 只发送模式写最后一个数据后先等 TE=1 再等 BUSY=0; 主机只接收模式在倒数第二个数据接收后延迟一个 SPI 时钟再关闭; 从机只接收模式可在任意时刻关闭,进入停机模式前须先等 BUSY=0。 |
用户手册 |
| 主从时钟参数必须一致 | 主设备与从设备的时钟极性 CLKPOL、时钟相位 CLKPHA 配置必须相同,否则数据采样沿错位。 | 用户手册 |
| 从机 CRC 使能的时机 | 从机模式下须在时钟稳定之后再使能 CRC 计算,否则 CRC 结果可能出错; 只要 CRCEN 置位且 SCK 上有输入时钟,无论 SPIEN 状态都会进行 CRC 计算。 |
用户手册 |
| 发送 CRC 的时机与开销 | CPU 模式(非 FIFO)须在发送最后一个数据前置位 CRCNEXT 以发送 CRC,DMA 模式由硬件自动发送; FIFO 只收模式按 TRANSNUM 自动发送; SPI 时钟较高时应减少 CPU 参与并在发送 CRC 期间避免函数调用。 |
用户手册 |
| 多从设备下 CRC 的清除 | 从设备切换(NSS 由高变低)时,应清除主从两端 CRC 数值以保持下次结果同步,步骤为关 SPI、清 CRCEN、置 CRCEN、再使能 SPI。 | 用户手册 |
| 软件计算 CRC 的参数 | 软件模拟硬件 CRC 时采用多项式经 CRCPOLY 配置、初始值 0x0000、结果异或 0x0000、输入与输出均不反转。 | 用户手册 |
| FIFO 模式下的数据读写 | 使能 FIFO 后从 SPI_RX_FIFO 读接收数据,未使能 FIFO 时读 SPI_DAT; 无论是否使能 FIFO,发送都写 SPI_DAT。 |
用户手册 |
| 溢出与 BUSY 标志的处理 | 只发送模式不读接收数据会使 OVER 置 1,软件应忽略该标志; 从机模式每个数据项之间 BUSY 会有一个 SPI 时钟周期为低,不要用 BUSY 判断每个数据项的收发。 |
用户手册 |