5.16 SPI 使用说明
本章说明 N32G033 标准外设库中 SPI 驱动的文件组成、22 个公开函数,以及 FullDuplex_SoftNSS、Simplex_Interrupt、SPI_CRC、SPI_DMA、SPI_FLASH 5 个示例。时钟极性相位、NSS 管理与 CRC 请配合用户手册的“SPI”章节阅读;芯片已知问题以勘误手册为准。
5.16.1 相关文件
| 相对路径 | 作用 |
|---|---|
firmware/n32g033_std_periph_driver/inc/n32g033_spi.h |
定义 SPI_InitType 结构体与数据方向、主从模式、数据长度、时钟极性相位、NSS、波特率预分频、CRC 等宏,并按复位与初始化、中断与 DMA、NSS 与数据长度、CRC 与状态等区段声明 22 个公开函数 |
firmware/n32g033_std_periph_driver/src/n32g033_spi.c |
实现上述 22 个函数:SPI 复位与初始化、数据收发、NSS 与数据长度配置、CRC 计算与传输、状态标志与中断处理 |
projects/n32g033_EVAL/examples/SPI/ |
SPI 示例根目录,含 FullDuplex_SoftNSS、Simplex_Interrupt、SPI_CRC、SPI_DMA、SPI_FLASH 5 个示例 |
projects/n32g033_EVAL/examples/SPI/SPI_FLASH/ |
该示例的工程目录组成:MDK-ARM/、EWARM/、inc/、src/、readme.txt,inc/ 与 src/ 下另有 spi_flash.h、spi_flash.c |
projects/n32g033_EVAL/bsp/ |
示例共用的板级支持文件:delay.h 的 SysTick 延时与 log.h 的串口日志接口 |
5.16.2 API 参考
以下 22 个公开函数按 n32g033_spi.h 的声明顺序分为 6 组;每个条目给出功能摘要与 n32g033_spi.c 中该函数正上方的注释原文。
复位与初始化
SPI_DeInit、SPI_Init、SPI_InitStruct、SPI_Enable
中断、DMA 与数据收发
SPI_EnableInt、SPI_EnableDma、SPI_TransmitData、SPI_ReceiveData
NSS 与数据长度配置
SPI_SetNssLevel、SPI_SSOutputEnable、SPI_ConfigDataLen
CRC 与双向模式
SPI_TransmitCrcNext、SPI_EnableCalculateCrc、SPI_GetCRCDat、SPI_GetCRCPoly、SPI_ConfigBidirectionalMode
状态与标志
SPI_GetStatus、SPI_ClrCRCErrFlag、SPI_GetIntStatus、SPI_ClrITPendingBit
主机时钟延时与 CRC 失效控制
SPI_Configdelaytime、SPI_NSSFailCRCEnable
void SPI_DeInit(SPI_Module* SPIx)复位 SPI 寄存器。
name SPI_DeInit.
fun Reset the SPI registers.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
return none
void SPI_Init(SPI_Module* SPIx, SPI_InitType* SPI_InitStruct)根据 SPI_InitStruct 初始化 SPI。
name SPI_Init.
fun Initializes the SPI according to SPI_InitStruct.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param SPI_InitStruct (The input parameters must be the following values):
- 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
- 0x01 ~ 0xffff
return none
void SPI_InitStruct(SPI_InitType* SPI_StructInit)用默认值填充 SPI_InitStruct 的各个成员。
name SPI_InitStruct.
fun Fills each SPI_InitStruct member with its default value.
param SPI_InitStruct (The input parameters must be the following values):
- DataDirection
- SpiMode
- DataLen
- CLKPOL
- CLKPHA
- NSS
- BaudRatePres
- FirstBit
return none
void SPI_Enable(SPI_Module* SPIx, FunctionalState Cmd)使能或关闭指定的 SPI 外设。
name SPI_Enable.
fun Enables or disables the specified SPI peripheral.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param Cmd (The input parameters must be the following values):
- ENABLE
- DISABLE
return none
void SPI_EnableInt(SPI_Module* SPIx, uint32_t SPI_IT, FunctionalState Cmd)使能或关闭 SPI 中断。
name SPI_EnableInt.
fun Enable/disable SPI interrupts.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param SPI_IT (The input parameters must be the following values):
- SPI_INT_TE
- SPI_INT_RNE
- SPI_INT_ERR
param Cmd (The input parameters must be the following values):
- ENABLE
- DISABLE
return none
void SPI_EnableDma(SPI_Module* SPIx, uint32_t SPI_DMAReq, FunctionalState Cmd)使能 SPI 的 DMA 传输。
name SPI_EnableDma.
fun Enable SPI DMA transfer.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param SPI_DMAReq (The input parameters must be the following values):
- SPI_DMA_TX
- SPI_DMA_RX
param Cmd (The input parameters must be the following values):
- ENABLE
- DISABLE
return none
void SPI_TransmitData(SPI_Module* SPIx, uint16_t Data)通过 SPI 外设发送一个数据。
name SPI_TransmitData.
fun Transmits a Data through the SPI peripheral.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param Data: data to be transmitted
return none
uint16_t SPI_ReceiveData(SPI_Module* SPIx)从 SPI_DAT 寄存器读取 SPI 数据。
name SPI_ReceiveData.
fun Get SPI data from SPI_DAT register.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
return The data in the SPI_DAT register
void SPI_SetNssLevel(SPI_Module* SPIx, uint32_t SPI_NSSInternalSoft)用软件配置所选 SPI 的 NSS 引脚。
name SPI_SetNssLevel.
fun Configures internally by software the NSS pin for the selected SPI.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param SPI_NSSInternalSoft (The input parameters must be the following values):
- SPI_NSS_HIGH Set NSS pin internally
- SPI_NSS_LOW Reset NSS pin internally
return none
void SPI_SSOutputEnable(SPI_Module* SPIx, FunctionalState Cmd)用软件配置所选 SPI 的 NSS 引脚。
name SPI_SSOutputEnable.
fun Configures internally by software the NSS pin for the selected SPI.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param Cmd (The input parameters must be the following values):
- ENABLE
- DISABLE
return none
void SPI_ConfigDataLen(SPI_Module* SPIx, uint32_t DataLen)配置所选 SPI 的数据长度。
name SPI_ConfigDataLen.
fun Configures the data size for the selected SPI.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param DataLen (The input parameters must be the following values):
- SPI_DATA_SIZE_16BITS
- SPI_DATA_SIZE_8BITS
return none
void SPI_TransmitCrcNext(SPI_Module* SPIx)发送 SPIx 的 CRC 值。
name SPI_TransmitCrcNext.
fun Transmit the SPIx CRC value.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
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 (The input parameters must be the following values):
- SPI1
- SPI2
param Cmd (The input parameters must be the following values):
- ENABLE
- DISABLE
return none
uint32_t SPI_GetCRCDat(SPI_Module* SPIx, uint8_t SPI_CRC)从 SPI_CRCTDAT/SPI_CRCRDAT 寄存器读取 SPI CRC 数据。
name SPI_GetCRCDat.
fun Get SPI CRC data from SPI_CRCTDAT/SPI_CRCRDAT register.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param SPI_CRC (The input parameters must be the following values):
- SPI_CRC_TX
- SPI_CRC_RX
return Tx/Rx CRC register value.
uint32_t SPI_GetCRCPoly(SPI_Module* SPIx)从 SPI_CRC_POLY 寄存器读取 CRC 多项式。
name SPI_GetCRCPoly.
fun Get CRC Polynomial from SPI_CRC_POLY register.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
return The CRC Polynomial register value.
void SPI_ConfigBidirectionalMode(SPI_Module* SPIx, uint32_t DataDirection)在双向模式下选择指定 SPI 的数据传输方向。
name SPI_ConfigBidirectionalMode.
fun Selects the data transfer direction in bi-directional mode for the specified SPI.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param DataDirection (The input parameters must be the following values):
- SPI_BIDIRECTION_RX
- SPI_BIDIRECTION_TX
return none
FlagStatus SPI_GetStatus(SPI_Module* SPIx, uint32_t SPI_FLAG)获取 SPI 标志状态。
name SPI_GetStatus.
fun Get SPI flag status.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param SPI_FLAG (The input parameters must be the following values):
- SPI_TE_FLAG
- SPI_RNE_FLAG
- SPI_CRCERR_FLAG
- SPI_MODERR_FLAG
- SPI_OVER_FLAG
- SPI_BUSY_FLAG
return SET or RESET
void SPI_ClrCRCErrFlag(SPI_Module* SPIx, uint32_t SPI_FLAG)清除 SPI 标志状态。
name SPI_ClrCRCErrFlag.
fun Clear SPI flag status.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param SPI_FLAG (The input parameters must be the following values):
- SPI_CRCERR_FLAG
return none
note
- OVER (OverRun error) flag is cleared by software sequence: a read
operation to SPI_DAT register (SPI_ReceiveData()) followed by a read
operation to SPI_STS register (SPI_GetStatus()).
- UNDER (UnderRun error) flag is cleared by a read operation to
SPI_STS register (SPI_GetStatus()).
- MODERR (Mode Fault) flag is cleared by software sequence: a read/write
operation to SPI_STS register (SPI_GetStatus()) followed by a
write operation to SPI_CTRL1 register (SPI_Enable() to enable the SPI).
INTStatus SPI_GetIntStatus(SPI_Module* SPIx, uint32_t SPI_IT)获取 SPI 中断标志状态。
name SPI_GetIntStatus.
fun Get SPI interrupt flag status.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param SPI_IT (The input parameters must be the following values):
- SPI_INT_FLAG_RNE
- SPI_INT_FLAG_TE
- SPI_INT_FLAG_CRCERR
- SPI_INT_FLAG_MODERR
- SPI_INT_FLAG_OVER
- SPI_INT_FLAG_ERR
return SET or RESET
void SPI_ClrITPendingBit(SPI_Module* SPIx, uint32_t SPI_FLAG)清除 SPI 标志状态。
name SPI_ClrITPendingBit.
fun Clear SPI flag status.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param SPI_FLAG (The input parameters must be the following values):
- SPI_INT_FLAG_CRCERR
return none
note
- OVER (OverRun error) flag is cleared by software sequence: a read
operation to SPI_DAT register (SPI_ReceiveData()) followed by a read
operation to SPI_STS register (SPI_GetStatus()).
- UNDER (UnderRun error) flag is cleared by a read operation to
SPI_STS register (SPI_GetStatus()).
- MODERR (Mode Fault) flag is cleared by software sequence: a read/write
operation to SPI_STS register (SPI_GetStatus()) followed by a
write operation to SPI_CTRL1 register (SPI_Enable() to enable the SPI).
void SPI_Configdelaytime(SPI_Module* SPIx,uint32_t DelayTime)配置 SPI 主机时钟延时时间。
name SPI_Configdelaytime.
fun SPI Host Clock Delay Time Configuration.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param DelayTime (The input parameters must be the following values):
- 0x00 ~ 0x0f delay DelayTime/2 PCLK2
return The CRC Polynomial register value.
void SPI_NSSFailCRCEnable(SPI_Module* SPIx,FunctionalState Cmd)NSS 失效时 CRC 计算停止或继续。
name SPI_NSSFailCRCEnable.
fun NSS failure, CRC calculation stops or continues.
param SPIx (The input parameters must be the following values):
- SPI1
- SPI2
param Cmd (The input parameters must be the following values):
- ENABLE
- DISABLE
return none
5.16.3 Demo 示例说明
本节说明 SPI 的 5 个示例:FullDuplex_SoftNSS 用软件 NSS 做全双工互相收发并交换主从,Simplex_Interrupt 用单线模式与收发中断完成数据搬运,SPI_CRC 用 16 位帧校验收发数据,SPI_DMA 用 DMA 搬运主发从收的数据,SPI_FLASH 读写并擦除串行 FLASH。
5.16.3.1 FullDuplex_SoftNSS 软件 NSS 全双工收发
工程目录: projects/n32g033_EVAL/examples/SPI/FullDuplex_SoftNSS/
使能 GPIO 与 SPI 时钟→配置 SPI 引脚→把两路 SPI 分别设为主与从→全双工互相收发并比对→交换主从后再次收发并比对
| 项目 | 源码解读 |
|---|---|
| 功能 | 用两个 SPI 实例一主一从做全双工互发,再交换主从角色重复一次,各自比对收发数据。 |
| 源码组成 | src/main.c 含双实例初始化、收发流程与比对函数;src/n32g033_it.c 与 inc/n32g033_it.h 提供中断入口;inc/main.h 定义两个实例的引脚与时钟宏;readme.txt 给出配置与测试步骤;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:主从两路 SCK、MISO、MOSI 均复用推挽; 模式:8 位数据、全双工、软件 NSS、从机第二位边沿采样; 位序:低位在前; 预分频:8 分频; 时钟极性:空闲低电平。 |
| 程序与数据路径 | main → RCC_Configuration 开时钟 → GPIO_Configuration 配引脚 → SPI_Init 配一主一从 → 主循环全双工互换数据 → Buffercmp 比对 → 交换主从后再来一轮。 |
| 主要 API | SPI_InitStruct、SPI_Init、SPI_Enable、SPI_TransmitData、SPI_ReceiveData、SPI_GetStatus |
| 验证方法 | 编译下载后复位运行,串口分别打印两轮的主从测试结果; 两轮都通过说明全双工与角色互换都正常。 |
| 使用提示 | 两路 SPI 的 SCK、MISO、MOSI 需交叉相连,主设备片选由软件控制; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1,硬件基于评估板 N32G033K8Q7-1_STB V1.0; 系统时钟 64 MHz。 |
核心代码解读与移植
示例先用一套参数配好一主一从,再通过软件 NSS 与轮询收发完成全双工互换。
数据路径: SPI_InitStruct 填公共参数 → SPI_Init 先后配主从 → SPI_Enable 使能两路 → 主循环经发送与接收寄存器逐字节互换 → Buffercmp 比对 → 交换主从后再一轮
1. 同一套参数先配主机再配从机,只改主从模式一项。
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_LOW;
SPI_InitStructure.CLKPHA = SPI_CLKPHA_SECOND_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(SPIy, &SPI_InitStructure);
/* SPIz configuration */
SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
SPI_Init(SPIz, &SPI_InitStructure);
两路共用参数便于保持时钟极性相位与位序一致,改参数时两侧同步。
2. 全双工收发:等发送空后写入,等接收非空后读回。
while (TxIdx < BufferSize)
{
/* Wait for SPIy Tx buffer empty */
while (SPI_GetStatus(SPIy, SPI_TE_FLAG) == RESET)
;
/* Send SPIz data */
SPI_TransmitData(SPIz, SPIz_Buffer_Tx[TxIdx]);
/* Send SPIy data */
SPI_TransmitData(SPIy, SPIy_Buffer_Tx[TxIdx++]);
/* Wait for SPIz data reception */
while (SPI_GetStatus(SPIz, SPI_RNE_FLAG) == RESET)
;
/* Read SPIz received data */
SPIz_Buffer_Rx[RxIdx] = SPI_ReceiveData(SPIz);
/* Wait for SPIy data reception */
while (SPI_GetStatus(SPIy, SPI_RNE_FLAG) == RESET)
;
/* Read SPIy received data */
SPIy_Buffer_Rx[RxIdx++] = SPI_ReceiveData(SPIy);
}
收发时序要对齐,先把两侧数据写入再读回,避免读写顺序错乱。
3. 交换主从角色后重新配置,再跑一遍收发。
/* SPIy Re-configuration */
SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
SPI_Init(SPIy, &SPI_InitStructure);
/* SPIz Re-configuration */
SPI_InitStructure.SpiMode = SPI_MODE_MASTER;
SPI_Init(SPIz, &SPI_InitStructure);
/* Reset TxIdx, RxIdx indexes and receive tables values */
TxIdx = 0;
RxIdx = 0;
for (k = 0; k < BufferSize; k++)
SPIz_Buffer_Rx[k] = 0;
for (k = 0; k < BufferSize; k++)
SPIy_Buffer_Rx[k] = 0;
改模式前先关闭对应 SPI,重新配置后再使能,避免通信中改配置。
4. 时钟与引脚:开两路 SPI 时钟并配置引脚复用。
void RCC_Configuration(void)
{
/* Enable peripheral clocks --------------------------------------------------*/
/* GPIO clock enable */
RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_GPIO, ENABLE);
/* SPI Periph clock enable */
RCC_EnableAPBPeriphClk(SPIy_CLK | SPIz_CLK, ENABLE);
}
两路 SPI 挂在各自总线上,移植到其他引脚时同步修改时钟与复用宏。
对应源码: src/main.c、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_spi.h
5.16.3.2 Simplex_Interrupt 单线中断收发
工程目录: projects/n32g033_EVAL/examples/SPI/Simplex_Interrupt/
使能 GPIO 与 SPI 时钟→配置引脚与中断优先级→主机配单线发送、从机配单线接收→使能收发中断并启动→主循环等待接收完成并比对
| 项目 | 源码解读 |
|---|---|
| 功能 | 主机用单线发送、从机用单线接收,主机侧发送中断补数据,从机侧接收中断取数据。 |
| 源码组成 | src/main.c 含双实例初始化、NVIC 配置与比对函数;src/n32g033_it.c 与 inc/n32g033_it.h 提供两路中断服务函数;inc/main.h 定义两个实例的引脚、中断号线与缓冲区;readme.txt 给出配置与测试步骤;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:主机 SCK 接 PB3、MOSI 接 PB5,从机 SCK 接 PB0、MISO 接 PA12; 模式:8 位数据、单线、软件 NSS、从机第二位边沿采样; 传输中断使能; 时钟极性:空闲低电平。 |
| 程序与数据路径 | main → 使能两路的一线收发中断 → 从机接收中断逐字节取数、主机发送中断逐字节补数 → 主循环等待接收计数达到长度 → Buffercmp 比对。 |
| 主要 API | SPI_InitStruct、SPI_Init、SPI_Enable、SPI_EnableInt、SPI_TransmitData、SPI_ReceiveData、SPI_GetIntStatus、SPI_GetStatus |
| 验证方法 | 编译下载后复位运行,主机侧数据发完后从机侧接收计数达到长度即完成; 串口打印比对一致即为通过。 |
| 使用提示 | 单线模式下数据线在主机端为 MOSI、在从机端为 MISO,两线需直接连接; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1,硬件基于评估板 N32G033K8Q7-1_STB V1.0; 系统时钟 64 MHz。 |
核心代码解读与移植
示例把数据搬运交给收发中断,主循环只等到接收计数达到长度为止。
数据路径: 使能两路收发中断 → 主机发送寄存器空时经中断补数 → 从机接收寄存器非空时经中断取数 → 接收计数达到长度 → 主循环 Buffercmp 比对
1. 两个实例分别按单线发送与单线接收配置,并各自使能收发中断。
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_LOW;
SPI_InitStructure.CLKPHA = SPI_CLKPHA_SECOND_EDGE;
SPI_InitStructure.NSS = SPI_NSS_SOFT;
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);
/* Enable SPI_MASTER TXE interrupt */
SPI_EnableInt(SPI_MASTER, SPI_INT_TE, ENABLE);
/* Enable SPI_SLAVE RXNE interrupt */
SPI_EnableInt(SPI_SLAVE, SPI_INT_RNE, ENABLE);
/* Enable SPI_SLAVE */
SPI_Enable(SPI_SLAVE, ENABLE);
/* Enable SPI_MASTER */
SPI_Enable(SPI_MASTER, ENABLE);
单线方向由数据方向参数决定,主机与从机的参数要互为反向。
2. 中断优先级:主机与从机两路分开配置。
/* Configure and enable SPI_MASTER interrupt */
NVIC_InitStructure.NVIC_IRQChannel = SPI_MASTER_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Configure and enable SPI_SLAVE interrupt */
NVIC_InitStructure.NVIC_IRQChannel = SPI_SLAVE_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
从机接收中断要及时响应,优先级不宜低于主机发送中断。
3. 主机发送中断:发送寄存器空时写入下一个字节,发完即关闭该中断。
void SPI1_IRQHandler(void)
{
if (SPI_GetIntStatus(SPI_MASTER, SPI_INT_FLAG_TE) != RESET)
{
/* Send SPI_MASTER data */
SPI_TransmitData(SPI_MASTER, MASTER_Buffer_Tx[TxIdx++]);
/* Disable SPI_MASTER TE interrupt */
if (TxIdx == BufferSize)
{
SPI_EnableInt(SPI_MASTER, SPI_INT_TE, DISABLE);
}
}
}
发送完成要主动关闭发送中断,避免空转重复进入。
4. 从机接收中断:收到一个字节即存入缓冲区并递增计数。
void SPI2_IRQHandler(void)
{
if(SPI_GetIntStatus(SPI_SLAVE, SPI_INT_FLAG_RNE) != RESET)
{
/* Store SPI_SLAVE received data */
SLAVE_Buffer_Rx[RxIdx++] = SPI_ReceiveData(SPI_SLAVE);
}
}
接收计数会作为完成判据,长度要与发送侧严格一致。
对应源码: src/main.c、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_spi.h
5.16.3.3 SPI_CRC CRC 校验收发
工程目录: projects/n32g033_EVAL/examples/SPI/SPI_CRC/
使能 GPIO 与 SPI 时钟→配置两路 SPI 为 16 位帧→使能两路 CRC 计算→传输数据并在末字节触发 CRC→读取 CRC 并检查错误标志
| 项目 | 源码解读 |
|---|---|
| 功能 | 用 16 位数据帧在主从之间传输,逐字节发送后在末字节触发 CRC,再核对接收 CRC 与错误标志。 |
| 源码组成 | src/main.c 含双实例初始化、收发流程、CRC 计算与比对;src/n32g033_it.c 与 inc/n32g033_it.h 提供中断入口;inc/main.h 定义两个实例的引脚、时钟宏与缓冲区;readme.txt 给出配置与测试步骤;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:SPI1 的 SCK、MISO、MOSI 与 SPI2 对应引脚交叉相连; 模式:16 位数据、全双工、软件 NSS; 位序:高位在前; 预分频:16 分频; CRC:两路均使能,多项式为 7。 |
| 程序与数据路径 | main → 两路 SPI_Init 配 16 位帧 → SPI_EnableCalculateCrc 使能 CRC → 主循环逐字发送与接收 → 末字调 SPI_TransmitCrcNext → SPI_GetCRCDat 读取 CRC 并检查错误标志。 |
| 主要 API | SPI_InitStruct、SPI_Init、SPI_Enable、SPI_EnableCalculateCrc、SPI_TransmitCrcNext、SPI_TransmitData、SPI_ReceiveData、SPI_GetStatus、SPI_GetCRCDat |
| 验证方法 | 编译下载后复位运行,串口打印收发比对与 CRC 校验结果; CRC 收发值一致且无错误标志即为通过。 |
| 使用提示 | 16 位帧要求两路数据长度一致,CRC 多项式与使能时机要按手册顺序设置; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1,硬件基于评估板 N32G033K8Q7-1_STB V1.0; 系统时钟 64 MHz。 |
核心代码解读与移植
示例在 16 位帧下传输数据,并在末字触发 CRC,再核对双方 CRC 与错误标志。
数据路径: SPI_Init 配 16 位帧 → SPI_EnableCalculateCrc 使能 CRC → 主循环逐字发送与接收 → 末字 SPI_TransmitCrcNext 触发 CRC → SPI_GetCRCDat 读取本端 CRC → 与对端比较并查错误标志
1. 把数据长度改成 16 位并让两路都使能 CRC 计算。
SPI_InitStruct(&SPI_InitStructure);
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_LOW;
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(SPI1, &SPI_InitStructure);
/* SPI2 configuration */
SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
SPI_Init(SPI2, &SPI_InitStructure);
/* Enable SPI1 CRC calculation */
SPI_EnableCalculateCrc(SPI1, ENABLE);
/* Enable SPI2 CRC calculation */
SPI_EnableCalculateCrc(SPI2, ENABLE);
/* Enable SPI1 */
SPI_Enable(SPI1, ENABLE);
/* Enable SPI2 */
SPI_Enable(SPI2, ENABLE);
数据长度要在通信前设定,两路必须一致,改动后同步核对多项式。
2. 末字先发数据,再触发本端 CRC 发送。
/* Wait for SPI1 Tx buffer empty */
while (SPI_GetStatus(SPI1, SPI_TE_FLAG) == RESET)
;
/* Wait for SPI2 Tx buffer empty */
while (SPI_GetStatus(SPI2, SPI_TE_FLAG) == RESET)
;
/* Send last SPI2_Buffer_Tx data */
SPI_TransmitData(SPI2, SPI2_Buffer_Tx[TxIdx]);
/* Enable SPI2 CRC transmission */
SPI_TransmitCrcNext(SPI2);
/* Send last SPI1_Buffer_Tx data */
SPI_TransmitData(SPI1, SPI1_Buffer_Tx[TxIdx]);
/* Enable SPI1 CRC transmission */
SPI_TransmitCrcNext(SPI1);
/* Wait for SPI1 last data reception */
while (SPI_GetStatus(SPI1, SPI_RNE_FLAG) == RESET)
;
/* Read SPI1 last received data */
SPI1_Buffer_Rx[RxIdx] = SPI_ReceiveData(SPI1);
/* Read SPI2 last received data */
SPI2_Buffer_Rx[RxIdx] = SPI_ReceiveData(SPI2);
CRC 触发要紧跟在最后一个数据之后,顺序错误会导致 CRC 校验失败。
3. CRC 错误标志:任一方向置位即判为失败。
/* Test on the SPI1 CRC Error flag */
if ((SPI_GetStatus(SPI1, SPI_CRCERR_FLAG)) == SET)
{
TransferStatus2 = FAILED;
}
/* Test on the SPI2 CRC Error flag */
if ((SPI_GetStatus(SPI2, SPI_CRCERR_FLAG)) == SET)
{
TransferStatus1 = FAILED;
}
读标志要在收发完成后进行,早读会读到中间状态。
4. 读取数据 CRC 与本端 CRC 值并互相比对。
/* Read SPI1 received CRC value */
CRC1Value = SPI_ReceiveData(SPI1);
/* Read SPI2 received CRC value */
CRC2Value = SPI_ReceiveData(SPI2);
/* Get SPI1&SPI2 CRC value */
SPI1_CRCTX = SPI_GetCRCDat(SPI1,SPI_CRC_TX);
SPI1_CRCRX = SPI_GetCRCDat(SPI1,SPI_CRC_RX);
SPI2_CRCTX = SPI_GetCRCDat(SPI2,SPI_CRC_TX);
SPI2_CRCRX = SPI_GetCRCDat(SPI2,SPI_CRC_RX);
if((CRC1Value == SPI1_CRCRX) && (SPI1_CRCRX == SPI2_CRCTX))
{
log_info("SPI1 CRC is OK! \r\n");
}
else
{
log_info("SPI1 CRC is Fail! \r\n");
}
if((CRC2Value == SPI2_CRCRX) && (SPI2_CRCRX == SPI1_CRCTX))
{
log_info("SPI2 CRC is OK! \r\n");
}
else
{
log_info("SPI2 CRC is Fail! \r\n");
}
本端发送 CRC 应等于对端接收 CRC,改多项式或长度时该等式同步变化。
对应源码: src/main.c、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_spi.h
5.16.3.4 SPI_DMA DMA 收发
工程目录: projects/n32g033_EVAL/examples/SPI/SPI_DMA/
使能 GPIO 与 SPI 时钟→配置一主一从两路 SPI→配置主机发送 DMA 通道→配置从机接收 DMA 通道→使能通道并等待传输完成
| 项目 | 源码解读 |
|---|---|
| 功能 | 主机的发送与从机的接收各交由一个 DMA 通道搬运,主循环只等待两个通道完成。 |
| 源码组成 | src/main.c 含双实例初始化、两个 DMA 通道配置与比对函数;src/n32g033_it.c 与 inc/n32g033_it.h 提供中断入口;inc/main.h 定义两个实例的引脚、DMA 通道与缓冲区;readme.txt 给出配置与测试步骤;工程文件在 MDK-ARM/ 与 EWARM/。 |
| 关键配置 | 引脚:两路 SPI 的 SCK、MISO、MOSI 交叉相连; 模式:8 位数据、全双工、软件 NSS; 位序:高位在前; 预分频:8 分频; DMA:主机走发送通道、从机走接收通道。 |
| 程序与数据路径 | main → SPI_Init 配一主一从 → DMA_Init 分别配主机发送与从机接收通道并做请求重映射 → SPI_EnableDma 使能两路 DMA 请求 → 主循环等两个通道完成 → Buffercmp 比对。 |
| 主要 API | SPI_InitStruct、SPI_Init、SPI_Enable、SPI_EnableDma、SPI_TransmitData、SPI_ReceiveData、SPI_GetStatus |
| 验证方法 | 编译下载后复位运行,两个 DMA 通道均完成搬运后比对收发数据; 串口打印一致即为通过。 |
| 使用提示 | DMA 通道与 SPI 的请求映射要对应各自的收发方向,改通道时同步修改重映射与请求宏; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1,硬件基于评估板 N32G033K8Q7-1_STB V1.0; 系统时钟 64 MHz。 |
核心代码解读与移植
示例把主机发送与从机接收都交给 DMA,主循环只等待两个通道搬运完成。
数据路径: SPI_Init 配一主一从 → 主机发送 DMA 通道指向数据寄存器 → 从机接收 DMA 通道指向数据寄存器 → SPI_EnableDma 使能请求 → 启动通道 → 两个完成标志置位后 Buffercmp 比对
1. 先配好一主一从两路 SPI,参数共用一套。
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_LOW;
SPI_InitStructure.CLKPHA = SPI_CLKPHA_SECOND_EDGE;
SPI_InitStructure.NSS = SPI_NSS_SOFT;
SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_8;
SPI_InitStructure.FirstBit = SPI_FB_MSB;
SPI_InitStructure.CRCPoly = CRC_POLYNOMIAL;
SPI_Init(SPI_MASTER, &SPI_InitStructure);
/* SPI_SLAVE configuration */
SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
SPI_Init(SPI_SLAVE, &SPI_InitStructure);
两路参数保持一致,只有主从模式不同,便于 DMA 直接对接。
2. 主机发送通道:外设地址指向数据寄存器,传输方向为外设目标。
DMA_DeInit(SPI_MASTER_TX_DMA_CHANNEL);
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.PeriphAddr = (uint32_t)&SPI1->DAT;
DMA_InitStructure.MemAddr = (uint32_t)SPI_Master_Tx_Buffer;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_DST;
DMA_InitStructure.BufSize = BUFFER_SIZE;
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE;
DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE;
DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.CircularMode = DMA_MODE_NORMAL;
DMA_InitStructure.Priority = DMA_PRIORITY_LOW;
DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE;
DMA_Init(SPI_MASTER_TX_DMA_CHANNEL, &DMA_InitStructure);
DMA_RequestRemap(DMA_REMAP_SPI1_TX,DMA,SPI_MASTER_TX_DMA_CHANNEL,ENABLE);
通道与 SPI 请求要做重映射,改通道后同步修改重映射参数。
3. 从机接收通道:外设地址指向数据寄存器,传输方向为外设源。
DMA_DeInit(SPI_SLAVE_RX_DMA_CHANNEL);
DMA_InitStructure.PeriphAddr = (uint32_t)&SPI2->DAT;
DMA_InitStructure.MemAddr = (uint32_t)SPI_Slave_Rx_Buffer;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC;
DMA_InitStructure.Priority = DMA_PRIORITY_VERY_HIGH;
DMA_Init(SPI_SLAVE_RX_DMA_CHANNEL, &DMA_InitStructure);
DMA_RequestRemap(DMA_REMAP_SPI2_RX,DMA,SPI_SLAVE_RX_DMA_CHANNEL,ENABLE);
收发方向要与各自的 DMA 请求匹配,方向写反会导致数据不入缓冲。
4. 使能两路 DMA 请求与通道,再等待两个完成标志。
/* Enable SPI_MASTER DMA Tx request */
SPI_EnableDma(SPI_MASTER, SPI_DMA_TX, ENABLE);
/* Enable SPI_SLAVE DMA Rx request */
SPI_EnableDma(SPI_SLAVE, SPI_DMA_RX, ENABLE);
/* Enable SPI_SLAVE */
SPI_Enable(SPI_SLAVE, ENABLE);
/* Enable SPI_MASTER */
SPI_Enable(SPI_MASTER, ENABLE);
/* Enable DMA channels */
DMA_EnableChannel(SPI_SLAVE_RX_DMA_CHANNEL, ENABLE);
DMA_EnableChannel(SPI_MASTER_TX_DMA_CHANNEL, ENABLE);
/* Transfer complete */
while (!DMA_GetFlagStatus(SPI_SLAVE_RX_DMA_FLAG, DMA))
;
while (!DMA_GetFlagStatus(SPI_MASTER_TX_DMA_FLAG, DMA))
;
先使能请求与通道再启动传输,两个标志都要等到才算完成。
对应源码: src/main.c、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_spi.h
5.16.3.5 SPI_FLASH 读写串行 FLASH
工程目录: projects/n32g033_EVAL/examples/SPI/SPI_FLASH/
使能 GPIO 与 SPI 时钟→初始化 SPI 与片选引脚→读取 FLASH 器件 ID→擦除扇区后写入数据→读回并比对,再擦除后检查全为擦除态
| 项目 | 源码解读 |
|---|---|
| 功能 | 用 SPI 驱动片外串行 FLASH:读器件 ID、擦除扇区、写入数据、读回比对,再擦除后检查是否全为擦除态。 |
| 源码组成 | src/main.c 组织初始化、读写擦除与比对流程;src/spi_flash.c 实现 FLASH 的初始化、命令收发、页写、读回与等待;inc/spi_flash.h 定义命令、器件 ID 与引脚宏;src/n32g033_it.c 提供中断入口;readme.txt 给出配置与测试步骤。 |
| 关键配置 | 引脚:SPI 的 SCK、MISO、MOSI 复用推挽,片选为推挽输出; 模式:8 位数据、全双工、软件 NSS、从机第二位边沿采样; 位序:高位在前; 预分频:4 分频; 时钟极性:空闲高电平。 |
| 程序与数据路径 | main → sFLASH_Init 初始化 SPI 与片选 → sFLASH_ReadID 校验器件 → sFLASH_EraseSector 擦除 → sFLASH_WriteBuffer 页写 → sFLASH_ReadBuffer 读回 → Buffercmp 比对。 |
| 主要 API | SPI_InitStruct、SPI_Init、SPI_Enable、SPI_TransmitData、SPI_ReceiveData、SPI_GetStatus |
| 验证方法 | 编译下载后复位运行,串口分别打印读写与擦除的校验结果; 读写一致且擦除后全为 0xFF 即为通过。 |
| 使用提示 | FLASH 命令与器件 ID 要与实际器件匹配,片选时序需按手册保证; 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1,硬件基于评估板 N32G033K8Q7-1_STB V1.0; 系统时钟 64 MHz。 |
核心代码解读与移植
示例用 SPI 逐字节驱动串行 FLASH,把命令、地址与数据拼装成一次传输。
数据路径: sFLASH_Init 配 SPI 与片选 → sFLASH_SendByte 收发一字节 → sFLASH_ReadID 发读 ID 命令取三字节 → sFLASH_WriteBuffer 拆页写命令、地址与数据 → sFLASH_ReadBuffer 读回 → Buffercmp 比对
1. 初始化 SPI 与片选引脚,并把片选置为未选中。
void sFLASH_Init(void)
{
SPI_InitType SPI_InitStructure;
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_4;
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);
}
片选空闲保持高电平,移植时把命令与引脚宏换成目标器件即可。
2. 单字节收发:等发送空后写入,再等接收非空后读回,构成一次全双工交换。
uint8_t sFLASH_SendByte(uint8_t byte)
{
/* Loop while DAT register in not emplty */
while (SPI_GetStatus(sFLASH_SPI, SPI_TE_FLAG) == RESET)
;
/* Send byte through the SPI1 peripheral */
SPI_TransmitData(sFLASH_SPI, byte);
/* Wait to receive a byte */
while (SPI_GetStatus(sFLASH_SPI, SPI_RNE_FLAG) == RESET)
;
/* Return the byte read from the SPI bus */
return SPI_ReceiveData(sFLASH_SPI);
}
FLASH 命令都基于这一字节交换拼接,改动底层会波及全部命令。
3. 读器件 ID:发读 ID 命令后连收三个字节并拼成 24 位值。
uint32_t sFLASH_ReadID(void)
{
uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
/* Select the FLASH: Chip Select low */
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;
}
器件 ID 与期望值不匹配时后续读写会被跳过,移植时先核对 ID 宏。
4. 主流程:读 ID、擦除、写入、读回比对,再擦除后检查擦除态。
sFLASH_Init();
/* Get SPI Flash ID */
FlashID = sFLASH_ReadID();
/* Check the SPI Flash ID */
if (FlashID == sFLASH_ID)
{
/* 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 */
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);
/* TransferStatus1 = PASSED, if the transmitted and received data by SPI1
are the same */
/* TransferStatus1 = FAILED, if the transmitted and received data by SPI1
are different */
/* Perform an erase in the Flash followed by a read of the written data */
/* Erase SPI FLASH Sector to write on */
sFLASH_EraseSector(FLASH_SectorToErase);
/* Read data from SPI FLASH memory */
sFLASH_ReadBuffer(Rx_Buffer, FLASH_ReadAddress, BufferSize);
擦除后再写入,写入后等内部写完成,顺序颠倒会读回错误数据。
对应源码: src/main.c、src/spi_flash.c、inc/spi_flash.h、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_spi.h
5.16.4 注意与排障
| 场景 | 检查与建议 | 来源 |
|---|---|---|
| NSS 管理 | 用户手册 N32G033 的 SPI 章写明 NSS 有软件与硬件两种管理模式,主设备 NSS 应保持高电平、从设备 NSS 应保持低电平; 多主或需外部片选时按手册选择内部软件置位或外部引脚控制。 |
用户手册 |
| 主模式失效 | 用户手册 N32G033 的 SPI 章写明主模式下 NSS 被外部拉低会触发模式失效,硬件会清除 SPIEN 与 MSEL 并置 MODERR; 需软件重新初始化后再通信。 |
用户手册 |
| 通信中修改配置 | 用户手册 N32G033 的 SPI 章写明 LSBFF、MSEL、CLKPHA、CLKPOL、BR 等位不得在通信过程中修改;改配置前先关闭 SPI。 |
用户手册 |
| 关闭 SPI 的时机 | 用户手册 N32G033 的 SPI 章写明关闭 SPI 前要等待发送为空、总线不忙且接收非空等标志全部满足,避免丢数据。 | 用户手册 |
| CRC 计算使能时机 | 用户手册 N32G033 的 SPI 章写明 DATFF 与 CRCEN 只在 SPIEN 为 0 时可写,使能顺序为先清 SPIEN 与 CRCEN 再置 CRCEN 与 SPIEN;下一 CRC 值需在写完最后一个数据后立即触发。 |
用户手册 |
| 从机 CRC | 用户手册 N32G033 的 SPI 章写明从模式使能 CRC 时,即使 NSS 为高电平,收到时钟也会参与 CRC 计算。 | 用户手册 |
勘误手册 V1.0.0(2025.9.25)针对 A 版芯片,未列出 SPI 相关条目。