STM32:SPI

1 SPI协议

  SPI全称serial peripheral interface,串行外设接口;为串行通讯接口协议;

  spi接口通过NSS、SCK、MISO、MOSI四线同其他设备相连,每个slave都需要一条独立的NSS,SCK总是由master提供;

  spi接口作为常用的板间通信协议,常用在ADC,LCD,EEPROM和FLASH存储器上;

  spi接口时序要求低,传输速率快,没有应答位;通讯速率最高为APB的二分频;

  1.1 工作模式 

    mode[1:0] = mode[ CPOL : CPHA ],有四种模式;

    CPOL:clock polarity 时钟极性,决定SCK的起始电平是0还是1,CPOL=0表示时钟的起始电平为低;

    CPHA:clock phase   时钟相位,决定数据采样点是在SCK的第1个跳变沿还是第二个跳变沿,CPHA=0表示取样点为第一个跳变沿;

    

    目前遇到的SPI协议清一色是模式0;w25qxx只支持模式0和模式3的全双工从模式spi协议,数据采样点都是在上升沿;

    上升沿通常由D触发器生成,信号较为稳定;下降沿的触发器触发时会有一些组合逻辑的延时,不如上升沿稳定;

    有效数据应该在数据采样点之前放入寄存器中;意思是如果数据是在上升沿采样,那么数据在下降沿触发变化;

2 SPI寄存器

  

  

  

  为什么单工的1-line是bidirectional,而双工的2-line是unidirectional我想了一个小时也没想明白,我决定不想了;

  可能是因为bidirectional不应该理解成单工,而应该理解成半双工;而双工的2-line_unidirectional 是说没有分时复用都是单向的;

3 标准库函数

  3.1 结构体封装

typedef struct SPI_TypeDef
/***stm32f10x.h 地址映射***/
typedef struct
{
__IO uint16_t CR1;
uint16_t RESERVED0;
__IO uint16_t CR2;
uint16_t RESERVED1;
__IO uint16_t SR;
uint16_t RESERVED2;
__IO uint16_t DR;
uint16_t RESERVED3;
__IO uint16_t CRCPR;
uint16_t RESERVED4;
__IO uint16_t RXCRCR;
uint16_t RESERVED5;
__IO uint16_t TXCRCR;
uint16_t RESERVED6;
__IO uint16_t I2SCFGR;
uint16_t RESERVED7;
__IO uint16_t I2SPR;
uint16_t RESERVED8;
} SPI_TypeDef;
#define SPI2 ((SPI_TypeDef *) SPI2_BASE)
#define SPI2_BASE (APB1PERIPH_BASE + 0x3800)
#define APB1PERIPH_BASE PERIPH_BASE
#define PERIPH_BASE ((uint32_t)0x40000000)
typedef struct SPI_InitTypeDef
/***stm32f10x_spi.h***/
typedef struct
{
uint16_t SPI_Direction;
uint16_t SPI_Mode;
uint16_t SPI_DataSize;
uint16_t SPI_CPOL;
uint16_t SPI_CPHA;
uint16_t SPI_NSS;
uint16_t SPI_BaudRatePrescaler;
uint16_t SPI_FirstBit;
uint16_t SPI_CRCPolynomial;
}SPI_InitTypeDef;
#define SPI_Direction_2Lines_FullDuplex ((uint16_t)0x0000)
#define SPI_Direction_2Lines_RxOnly ((uint16_t)0x0400)
#define SPI_Direction_1Line_Rx ((uint16_t)0x8000)
#define SPI_Direction_1Line_Tx ((uint16_t)0xC000)
#define SPI_Mode_Master ((uint16_t)0x0104)
#define SPI_Mode_Slave ((uint16_t)0x0000)
#define SPI_DataSize_16b ((uint16_t)0x0800)
#define SPI_DataSize_8b ((uint16_t)0x0000)
#define SPI_CPOL_Low ((uint16_t)0x0000)
#define SPI_CPOL_High ((uint16_t)0x0002)
#define SPI_CPHA_1Edge ((uint16_t)0x0000)
#define SPI_CPHA_2Edge ((uint16_t)0x0001)
#define SPI_NSS_Soft ((uint16_t)0x0200)
#define SPI_NSS_Hard ((uint16_t)0x0000)
#define SPI_BaudRatePrescaler_2 ((uint16_t)0x0000)
#define SPI_BaudRatePrescaler_4 ((uint16_t)0x0008)
#define SPI_BaudRatePrescaler_8 ((uint16_t)0x0010)
#define SPI_BaudRatePrescaler_16 ((uint16_t)0x0018)
#define SPI_BaudRatePrescaler_32 ((uint16_t)0x0020)
#define SPI_BaudRatePrescaler_64 ((uint16_t)0x0028)
#define SPI_BaudRatePrescaler_128 ((uint16_t)0x0030)
#define SPI_BaudRatePrescaler_256 ((uint16_t)0x0038)
#define SPI_FirstBit_MSB ((uint16_t)0x0000)
#define SPI_FirstBit_LSB ((uint16_t)0x0080)
#define IS_SPI_CRC_POLYNOMIAL(POLYNOMIAL) ((POLYNOMIAL) >= 0x1)

  3.2 标准库函数

SPI_I2S_DeInit(SPI_TypeDef* SPIx)
void SPI_I2S_DeInit(SPI_TypeDef* SPIx)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
if (SPIx == SPI1)
{
/* Enable SPI1 reset state */
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, ENABLE);
/* Release SPI1 from reset state */
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, DISABLE);
}
else if (SPIx == SPI2)
{
/* Enable SPI2 reset state */
RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2, ENABLE);
/* Release SPI2 from reset state */
RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2, DISABLE);
}
else
{
if (SPIx == SPI3)
{
/* Enable SPI3 reset state */
RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI3, ENABLE);
/* Release SPI3 from reset state */
RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI3, DISABLE);
}
}
}
SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)
/***stm32f10x_spi.c***/
#define CR1_CLEAR_Mask ((uint16_t)0xE9F3) /*1110 1001 1111 0011b,清0[12],[10],[9],[3],[2]*/
#define SPI_Mode_Select ((uint16_t)0xF7FF)
#define I2S_Mode_Select ((uint16_t)0x0800)
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)
{
uint16_t tmpreg = 0;
/* check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
/* Check the SPI parameters */
assert_param(IS_SPI_DIRECTION_MODE(SPI_InitStruct->SPI_Direction));
assert_param(IS_SPI_MODE(SPI_InitStruct->SPI_Mode));
assert_param(IS_SPI_DATASIZE(SPI_InitStruct->SPI_DataSize));
assert_param(IS_SPI_CPOL(SPI_InitStruct->SPI_CPOL));
assert_param(IS_SPI_CPHA(SPI_InitStruct->SPI_CPHA));
assert_param(IS_SPI_NSS(SPI_InitStruct->SPI_NSS));
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_InitStruct->SPI_BaudRatePrescaler));
assert_param(IS_SPI_FIRST_BIT(SPI_InitStruct->SPI_FirstBit));
assert_param(IS_SPI_CRC_POLYNOMIAL(SPI_InitStruct->SPI_CRCPolynomial));
/*---------------------------- SPIx CR1 Configuration ------------------------*/
tmpreg = SPIx->CR1;
tmpreg &= CR1_CLEAR_Mask;
tmpreg |= (uint16_t)((uint32_t)SPI_InitStruct->SPI_Direction | SPI_InitStruct->SPI_Mode |
SPI_InitStruct->SPI_DataSize | SPI_InitStruct->SPI_CPOL |
SPI_InitStruct->SPI_CPHA | SPI_InitStruct->SPI_NSS |
SPI_InitStruct->SPI_BaudRatePrescaler | SPI_InitStruct->SPI_FirstBit);
SPIx->CR1 = tmpreg;
/* Activate the SPI mode (Reset I2SMOD bit in I2SCFGR register) */
SPIx->I2SCFGR &= SPI_Mode_Select;
/*---------------------------- SPIx CRCPOLY Configuration --------------------*/
/* Write to SPIx CRCPOLY */
SPIx->CRCPR = SPI_InitStruct->SPI_CRCPolynomial;
}
SPI_StructInit(SPI_InitTypeDef* SPI_InitStruct)
 /***spi初始化例程***/
void SPI_StructInit(SPI_InitTypeDef* SPI_InitStruct)
{
/*--------------- Reset SPI init structure parameters values -----------------*/
/* Initialize the SPI_Direction member */
SPI_InitStruct->SPI_Direction = SPI_Direction_2Lines_FullDuplex;
/* initialize the SPI_Mode member */
SPI_InitStruct->SPI_Mode = SPI_Mode_Slave;
/* initialize the SPI_DataSize member */
SPI_InitStruct->SPI_DataSize = SPI_DataSize_8b;
/* Initialize the SPI_CPOL member */
SPI_InitStruct->SPI_CPOL = SPI_CPOL_Low;
/* Initialize the SPI_CPHA member */
SPI_InitStruct->SPI_CPHA = SPI_CPHA_1Edge;
/* Initialize the SPI_NSS member */
SPI_InitStruct->SPI_NSS = SPI_NSS_Hard;
/* Initialize the SPI_BaudRatePrescaler member */
SPI_InitStruct->SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
/* Initialize the SPI_FirstBit member */
SPI_InitStruct->SPI_FirstBit = SPI_FirstBit_MSB;
/* Initialize the SPI_CRCPolynomial member */
SPI_InitStruct->SPI_CRCPolynomial = 7;
}
SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState)
 /* SPI SPE mask */
#define CR1_SPE_Set ((uint16_t)0x0040)
#define CR1_SPE_Reset ((uint16_t)0xFFBF)
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
/* Enable the selected SPI peripheral */
SPIx->CR1 |= CR1_SPE_Set;
}
else
{
/* Disable the selected SPI peripheral */
SPIx->CR1 &= CR1_SPE_Reset;
}
}
SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState)
/***stm32f10x_spi.h***/
#define SPI_I2S_IT_TXE ((uint8_t)0x71)
#define SPI_I2S_IT_RXNE ((uint8_t)0x60)
#define SPI_I2S_IT_ERR ((uint8_t)0x50)
#define IS_SPI_I2S_CONFIG_IT(IT) (((IT) == SPI_I2S_IT_TXE) || \
((IT) == SPI_I2S_IT_RXNE) || \
((IT) == SPI_I2S_IT_ERR))
/***stm32f10x_spi.c***/
void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState)
{
uint16_t itpos = 0, itmask = 0 ;
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
assert_param(IS_FUNCTIONAL_STATE(NewState));
assert_param(IS_SPI_I2S_CONFIG_IT(SPI_I2S_IT));
/* Get the SPI/I2S IT index */
itpos = SPI_I2S_IT >> 4;
/* Set the IT mask */
itmask = (uint16_t)1 << (uint16_t)itpos;
if (NewState != DISABLE)
{
/* Enable the selected SPI/I2S interrupt */
SPIx->CR2 |= itmask;
}
else
{
/* Disable the selected SPI/I2S interrupt */
SPIx->CR2 &= (uint16_t)~itmask;
}
}
SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState)
 /***stm32f10x_spi.h***/
#define SPI_I2S_DMAReq_Tx ((uint16_t)0x0002)
#define SPI_I2S_DMAReq_Rx ((uint16_t)0x0001)
#define IS_SPI_I2S_DMAREQ(DMAREQ) ((((DMAREQ) & (uint16_t)0xFFFC) == 0x00) && ((DMAREQ) != 0x00))
/***stm32f10x_spi.c***/
void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
assert_param(IS_FUNCTIONAL_STATE(NewState));
assert_param(IS_SPI_I2S_DMAREQ(SPI_I2S_DMAReq));
if (NewState != DISABLE)
{
/* Enable the selected SPI/I2S DMA requests */
SPIx->CR2 |= SPI_I2S_DMAReq;
}
else
{
/* Disable the selected SPI/I2S DMA requests */
SPIx->CR2 &= (uint16_t)~SPI_I2S_DMAReq;
}
}
SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)
 void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
/* Write in the DR register the data to be sent */
SPIx->DR = Data;
}
SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)
 uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
/* Return the data in the DR register */
return SPIx->DR;
}
SPI_NSSInternalSoftwareConfig(SPI_TypeDef* SPIx, uint16_t SPI_NSSInternalSoft)
 /***stm32f10x_spi.h***/
#define SPI_NSSInternalSoft_Set ((uint16_t)0x0100)
#define SPI_NSSInternalSoft_Reset ((uint16_t)0xFEFF)
#define IS_SPI_NSS_INTERNAL(INTERNAL) (((INTERNAL) == SPI_NSSInternalSoft_Set) || \
((INTERNAL) == SPI_NSSInternalSoft_Reset))
/***stm32f10x_spi.c***/
void SPI_NSSInternalSoftwareConfig(SPI_TypeDef* SPIx, uint16_t SPI_NSSInternalSoft)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
assert_param(IS_SPI_NSS_INTERNAL(SPI_NSSInternalSoft));
if (SPI_NSSInternalSoft != SPI_NSSInternalSoft_Reset)
{
/* Set NSS pin internally by software */
SPIx->CR1 |= SPI_NSSInternalSoft_Set;
}
else
{
/* Reset NSS pin internally by software */
SPIx->CR1 &= SPI_NSSInternalSoft_Reset;
}
}
SPI_SSOutputCmd(SPI_TypeDef* SPIx, FunctionalState NewState)
 /* SPI SSOE mask */
#define CR2_SSOE_Set ((uint16_t)0x0004)
#define CR2_SSOE_Reset ((uint16_t)0xFFFB)
void SPI_SSOutputCmd(SPI_TypeDef* SPIx, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
/* Enable the selected SPI SS output */
SPIx->CR2 |= CR2_SSOE_Set;
}
else
{
/* Disable the selected SPI SS output */
SPIx->CR2 &= CR2_SSOE_Reset;
}
}
SPI_DataSizeConfig(SPI_TypeDef* SPIx, uint16_t SPI_DataSize)
 /***stm32f10x_spi.h***/
#define SPI_DataSize_16b ((uint16_t)0x0800)
#define SPI_DataSize_8b ((uint16_t)0x0000)
#define IS_SPI_DATASIZE(DATASIZE) (((DATASIZE) == SPI_DataSize_16b) || \
((DATASIZE) == SPI_DataSize_8b))
/***stm32f10x_spi.c***/
void SPI_DataSizeConfig(SPI_TypeDef* SPIx, uint16_t SPI_DataSize)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
assert_param(IS_SPI_DATASIZE(SPI_DataSize));
/* Clear DFF bit */
SPIx->CR1 &= (uint16_t)~SPI_DataSize_16b;
/* Set new DFF bit value */
SPIx->CR1 |= SPI_DataSize;
}
SPI_TransmitCRC(SPI_TypeDef* SPIx)
 /* SPI CRCNext mask */
#define CR1_CRCNext_Set ((uint16_t)0x1000)
void SPI_TransmitCRC(SPI_TypeDef* SPIx)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
/* Enable the selected SPI CRC transmission */
SPIx->CR1 |= CR1_CRCNext_Set;
}
SPI_CalculateCRC(SPI_TypeDef* SPIx, FunctionalState NewState)
 /* SPI CRCEN mask 是否使能[CRCEN]位;*/
#define CR1_CRCEN_Set ((uint16_t)0x2000)
#define CR1_CRCEN_Reset ((uint16_t)0xDFFF)
void SPI_CalculateCRC(SPI_TypeDef* SPIx, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
/* Enable the selected SPI CRC calculation */
SPIx->CR1 |= CR1_CRCEN_Set;
}
else
{
/* Disable the selected SPI CRC calculation */
SPIx->CR1 &= CR1_CRCEN_Reset;
}
}
SPI_GetCRC(SPI_TypeDef* SPIx, uint8_t SPI_CRC)
 /***stm32f10x_spi.h***/
#define SPI_CRC_Tx ((uint8_t)0x00)
#define SPI_CRC_Rx ((uint8_t)0x01)
#define IS_SPI_CRC(CRC) (((CRC) == SPI_CRC_Tx) || ((CRC) == SPI_CRC_Rx))
/***stm32f10x_spi.c***/
uint16_t SPI_GetCRC(SPI_TypeDef* SPIx, uint8_t SPI_CRC)
{
uint16_t crcreg = 0;
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
assert_param(IS_SPI_CRC(SPI_CRC));
if (SPI_CRC != SPI_CRC_Rx)
{
/* Get the Tx CRC register */
crcreg = SPIx->TXCRCR;
}
else
{
/* Get the Rx CRC register */
crcreg = SPIx->RXCRCR;
}
/* Return the selected CRC register */
return crcreg;
}
SPI_GetCRCPolynomial(SPI_TypeDef* SPIx)
 uint16_t SPI_GetCRCPolynomial(SPI_TypeDef* SPIx)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
/* Return the CRC polynomial register */
return SPIx->CRCPR;
}
SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)
 /***stm32f10x_spi.h***/
#define SPI_FLAG_CRCERR ((uint16_t)0x0010)
#define IS_SPI_I2S_CLEAR_FLAG(FLAG) (((FLAG) == SPI_FLAG_CRCERR))
/***stm32f10x_spi.c 这个函数虽然叫clear flag,但是它只清除[CRCERR];小离谱***/
void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
assert_param(IS_SPI_I2S_CLEAR_FLAG(SPI_I2S_FLAG));
/* Clear the selected SPI CRC Error (CRCERR) flag */
SPIx->SR = (uint16_t)~SPI_I2S_FLAG;
}
SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT)
 /***stm32f10x_spi.h***/
#define SPI_IT_CRCERR ((uint8_t)0x54)
#define IS_SPI_I2S_CLEAR_IT(IT) (((IT) == SPI_IT_CRCERR))
/***stm32f10x_spi.c***/
void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT)
{
uint16_t itpos = 0;
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
assert_param(IS_SPI_I2S_CLEAR_IT(SPI_I2S_IT));
/* Get the SPI IT index */
itpos = 0x01 << (SPI_I2S_IT & 0x0F);
/* Clear the selected SPI CRC Error (CRCERR) interrupt pending bit */
SPIx->SR = (uint16_t)~itpos;
}
SPI_BiDirectionalLineConfig(SPI_TypeDef* SPIx, uint16_t SPI_Direction)
 /***stm32f10x_spi.h***/
#define SPI_Direction_Rx ((uint16_t)0xBFFF)
#define SPI_Direction_Tx ((uint16_t)0x4000)
#define IS_SPI_DIRECTION(DIRECTION) (((DIRECTION) == SPI_Direction_Rx) || \
((DIRECTION) == SPI_Direction_Tx))
/***stm32f10x_spi.c***/
void SPI_BiDirectionalLineConfig(SPI_TypeDef* SPIx, uint16_t SPI_Direction)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
assert_param(IS_SPI_DIRECTION(SPI_Direction));
if (SPI_Direction == SPI_Direction_Tx)
{
/* Set the Tx only mode */
SPIx->CR1 |= SPI_Direction_Tx;
}
else
{
/* Set the Rx only mode */
SPIx->CR1 &= SPI_Direction_Rx;
}
}
SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)
  /***stm32f10x_spi.h***/
#define SPI_I2S_FLAG_RXNE ((uint16_t)0x0001)
#define SPI_I2S_FLAG_TXE ((uint16_t)0x0002)
#define I2S_FLAG_CHSIDE ((uint16_t)0x0004)
#define I2S_FLAG_UDR ((uint16_t)0x0008)
#define SPI_FLAG_CRCERR ((uint16_t)0x0010)
#define SPI_FLAG_MODF ((uint16_t)0x0020)
#define SPI_I2S_FLAG_OVR ((uint16_t)0x0040)
#define SPI_I2S_FLAG_BSY ((uint16_t)0x0080)
#define IS_SPI_I2S_CLEAR_FLAG(FLAG) (((FLAG) == SPI_FLAG_CRCERR))
#define IS_SPI_I2S_GET_FLAG(FLAG) (((FLAG) == SPI_I2S_FLAG_BSY) || ((FLAG) == SPI_I2S_FLAG_OVR) || \
((FLAG) == SPI_FLAG_MODF) || ((FLAG) == SPI_FLAG_CRCERR) || \
((FLAG) == I2S_FLAG_UDR) || ((FLAG) == I2S_FLAG_CHSIDE) || \
((FLAG) == SPI_I2S_FLAG_TXE) || ((FLAG) == SPI_I2S_FLAG_RXNE))
 /***stm32f10x_spi.c***/
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)
{
FlagStatus bitstatus = RESET;
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
assert_param(IS_SPI_I2S_GET_FLAG(SPI_I2S_FLAG));
/* Check the status of the specified SPI/I2S flag */
if ((SPIx->SR & SPI_I2S_FLAG) != (uint16_t)RESET)
{
/* SPI_I2S_FLAG is set */
bitstatus = SET;
}
else
{
/* SPI_I2S_FLAG is reset */
bitstatus = RESET;
}
/* Return the SPI_I2S_FLAG status */
return bitstatus;
}
SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT)
 /***stm32f10x_spi.h***/
#define SPI_I2S_IT_TXE ((uint8_t)0x71)
#define SPI_I2S_IT_RXNE ((uint8_t)0x60)
#define SPI_I2S_IT_OVR ((uint8_t)0x56)
#define SPI_IT_MODF ((uint8_t)0x55)
#define SPI_IT_CRCERR ((uint8_t)0x54)
#define I2S_IT_UDR ((uint8_t)0x53)
#define IS_SPI_I2S_GET_IT(IT) (((IT) == SPI_I2S_IT_RXNE) || ((IT) == SPI_I2S_IT_TXE) || \
((IT) == I2S_IT_UDR) || ((IT) == SPI_IT_CRCERR) || \
((IT) == SPI_IT_MODF) || ((IT) == SPI_I2S_IT_OVR))
/***stm32f10x_spi.c***/
ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT)
{
ITStatus bitstatus = RESET;
uint16_t itpos = 0, itmask = 0, enablestatus = 0;
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
assert_param(IS_SPI_I2S_GET_IT(SPI_I2S_IT));
/* Get the SPI/I2S IT index */
itpos = 0x01 << (SPI_I2S_IT & 0x0F);
/* Get the SPI/I2S IT mask */
itmask = SPI_I2S_IT >> 4;
/* Set the IT mask */
itmask = 0x01 << itmask;
/* Get the SPI_I2S_IT enable bit status */
enablestatus = (SPIx->CR2 & itmask) ;
/* Check the status of the specified SPI/I2S interrupt */
if (((SPIx->SR & itpos) != (uint16_t)RESET) && enablestatus)
{
/* SPI_I2S_IT is set */
bitstatus = SET;
}
else
{
/* SPI_I2S_IT is reset */
bitstatus = RESET;
}
/* Return the SPI_I2S_IT status */
return bitstatus;
}

  3.3 使用函数

spi.c
 #include "spi.h"
#include "usart.h"
/***不知道为什么几个板子都喜欢在等待RXNE、TXE的时候加计数超时返回,等下去不就完了?
***DR原理都一样,串口收发都没加spi收发为什么就加呢?我决定不加,但是备个小注以防;***/
void SPI2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );//SPI2时钟使能
//PB12_NSS, PB13_SCK, PB15_MOSI, PB14_MISO;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI2, &SPI_InitStructure);
SPI_Cmd(SPI2, ENABLE);
}
u8 SPI2_sendRevByte(u8 sendbyte)
{
SPI_I2S_SendData(SPI2, sendbyte);
while ( !SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) )
;
return SPI_I2S_ReceiveData(SPI2);
}
u8 SPI2_sendBuff(u8 * buff,int size)
{
int i=0;
for(i=0;i<size;i++)
{
SPI_I2S_SendData(SPI2, buff[i]);
while ( !SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) )
;
}
return 0;
}
u8 SPI2_revBuff(u8 * buff,int size)
{
int i=0;
for(i=0;i<size;i++)
{
buff[i] = SPI_I2S_ReceiveData(SPI2);
while ( !SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) )
;
}
return 0;
}

   3.4 stm32h7系列的hal库函数

     【STM32H7教程】第72章 STM32H7的SPI总线基础知识和HAL库API - 硬汉嵌入式 - 博客园 (cnblogs.com)

  3.5 stm32h7_spi_demo

    stm32h7_demo: 计划将stm32h7的常用外设的代码,整理出一个测试工程来存储; 佛系更新; (gitee.com)

4 W25Q128FV

  w25qxx的spi已配置为全双工slave,8bit数据,仅支持模式0、模式3,MSB传输;

  w25qxx的spi传输速率可达104Mbps,qspi传输速率可达416Mbps;超10万次擦写操作,超20年存储时间;

  flash制作工艺为wordline并联,bitline串联,为了节省资源简化操作所以不同时充放电;(这话网友说的,我看着挺有道理就抄过来了;)

  flash写操作之前先把寄存器全部充电为1,写操作时只针对需要写0的数据通过放电写0;可以单独写bit0,不能单独写bit1;   

  4.1 结构框图

    

  4.2 w25qxx引脚

/CS 0:片选使能,芯片准备收发指令数据;1:属于高阻态,芯片待机状态;
CLK      上升沿读取DI,下降沿修改DO;
/HOLD  (IO3) 
/RESET  (IO3)
0:[CS]使能且[HOLD]使能,则DI、SCK、DO无效;初始化的时候直接拉高不使用该引脚;
0:w25qxx复位,也可以使用复位指令复位;
/WP      (IO2) 0:写保护使能,SR不能被修改;目的是为了防止掉电状态下的SR寄存器可能被噪音之类的修改;
DO        (IO1)      data output;
DI          (IO0)      data input;

    /HOLD、/RESET引脚是同一个引脚,它们的复用选择由状态寄存器决定;

    /HOLD、 /RESET、/WP都与QSPI的功能冲突,如果芯片配置成qspi模式,则引脚功能无效;

  4.3 状态寄存器

    w25qxx一共有3个状态寄存器;

    读SR可以知道各种指令操作之后寄存器的状态,通过状态寄存器判断指令操作是否完成;

    写SR可以配置芯片的各种功能,如写保护功能,QSPI模式,复位引脚复用等;

    指令集在数据手册page24 instruction章节开始,主要翻这个即可;看指令的发送和返回数据是否对应就可以了;

    不对的话再去查看状态寄存器什么意思就可以了,没有必要把状态寄存器写这么详细,除了 [BUSY] 的判断之外大概率其他不咋用得上;

    2023-03-23 w25qxx的代码写过一次之后,只要改改IO接口就可以反复套用了,所以这一小节基本等于一次性废话;

    

  (S0)BUSY: 当busy为1时,芯片正在执行指令,且不接收新的指令;当busy为0时,芯片不再执行指令,准备接收指令;

  (S1) WEL: 当wel为1时,芯片可以执行写指令;当wel为0时,不能执行写指令;上电后以及执行完各种指令后,wel会被置位为0;

  (S4-S2) BP[2:0]:当配置后,对应地址的擦写操作受到保护,需要先取消保护才能修改;BP[2:0]默认初始化为0,即所有地址均可执行擦写操作;

  (S5)      TB:同BP[2:0]一起决定block protected;默认初始化为0;//可以通过配置SRP[1:0]和WEL来配置TB bit;

  (S6)      SEC:当SEC为1时,BP[2:0]的写保护单位是sector;当SEC为0时,写保护单位是block;

  S[6:2]用来决定数据存储区的非易失性存储功能是否启用;如果启动,则特定block或sector不能直接擦写操作;具体见数据手册第七章末尾的表格;  

  (S8-S7) SRP[1:0]:决定/WP控制状态寄存器的功能是否有效;是针对状态寄存器操作的配置;如下图所示:

  (S9)       QE:当置1后,表示为QSPI模式,且/WP引脚和/HOLD引脚功能无效,将对应引脚配置为QSPI数据引脚;

      当QE为0时,/WP和/HOLD引脚功能有效;

      QEbit需要先配置为1,然后执行Enter QPI(38h)指令,才能进入QPI模式;

  (S13-S11) LB[3:1]:当置1后,对应的256bytes的security register永久性只读;是针对安全性寄存器的配置;

  (S14)    CMP:配合前面5bit一起配置非易失性保护;默认初始化为0;

  (S15)    SUS:当SUS为1时,表示执行完了擦写操作75h;当SUS为0时,表示执行完了擦写操作暂停指令7Ah;默认上电初始化为0;

  (S18)     WPS:当WPS为0时,决定存储区域的非易失性写保护由寄存器的5bit决定;

         当WPS为1时,表示存储区域的写保护通过发送指令来决定;具体见第7章末尾的表格;

  (S22-S21)DRV[1:0]:用来决定驱动输出数据的强度,默认为11b,25%;

  (S23)    HOLD/RESET:默认为0,表示引脚功能为/HOLD;当配置为1时,表示引脚功能为/RESET;

  4.4 指令操作

    详见数据手册page24_instruction,为w25qxx编程主要查看部分;

    芯片为SPI/DSPI/QSPI提供了45条基本指令。这些指令在CS拉低后便准备好接收了;数据传输高位在前先传输;

    芯片为QPI提供了32条基本指令;QSPI接口仅支持从SPI接口使用38h指令切换过去,然后使用FFh切换回QSPI;

    QSPI和QPI的主要差别在于QPI发送指令也是通过4线进行传输的,QSPI发送指令同SPI一样是通过1线进行传输的;

    确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败;

    SPI的解码缓存是256字节,所以每次执行写操作的时候,写入数据的大小不大于256字节;

    所以执行读写操作数据的时候都要先使能WEL,然后等BUSY位为0;

posted @   rls_v  阅读(2712)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示