/********************************** (C) COPYRIGHT ******************************* * File Name : ch32v30x_spi.c * Author : WCH * Version : V1.0.0 * Date : 2021/06/06 * Description : This file provides all the SPI firmware functions. *********************************************************************************/ #include "ch32v30x_spi.h" #include "ch32v30x_rcc.h" /* SPI SPE mask */ #define CTLR1_SPE_Set ((uint16_t)0x0040) #define CTLR1_SPE_Reset ((uint16_t)0xFFBF) /* I2S I2SE mask */ #define I2SCFGR_I2SE_Set ((uint16_t)0x0400) #define I2SCFGR_I2SE_Reset ((uint16_t)0xFBFF) /* SPI CRCNext mask */ #define CTLR1_CRCNext_Set ((uint16_t)0x1000) /* SPI CRCEN mask */ #define CTLR1_CRCEN_Set ((uint16_t)0x2000) #define CTLR1_CRCEN_Reset ((uint16_t)0xDFFF) /* SPI SSOE mask */ #define CTLR2_SSOE_Set ((uint16_t)0x0004) #define CTLR2_SSOE_Reset ((uint16_t)0xFFFB) /* SPI registers Masks */ #define CTLR1_CLEAR_Mask ((uint16_t)0x3040) #define I2SCFGR_CLEAR_Mask ((uint16_t)0xF040) /* SPI or I2S mode selection masks */ #define SPI_Mode_Select ((uint16_t)0xF7FF) #define I2S_Mode_Select ((uint16_t)0x0800) /* I2S clock source selection masks */ #define I2S2_CLOCK_SRC ((uint32_t)(0x00020000)) #define I2S3_CLOCK_SRC ((uint32_t)(0x00040000)) #define I2S_MUL_MASK ((uint32_t)(0x0000F000)) #define I2S_DIV_MASK ((uint32_t)(0x000000F0)) /******************************************************************************* * Function Name : SPI_I2S_DeInit * Description : Deinitializes the SPIx peripheral registers to their default * reset values (Affects also the I2Ss). * Input : SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. * Return : None *******************************************************************************/ void SPI_I2S_DeInit(SPI_TypeDef* SPIx) { if (SPIx == SPI1) { RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, ENABLE); RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, DISABLE); } else if (SPIx == SPI2) { RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2, ENABLE); RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2, DISABLE); } else { if (SPIx == SPI3) { RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI3, ENABLE); RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI3, DISABLE); } } } /******************************************************************************* * Function Name : SPI_Init * Description : Initializes the SPIx peripheral according to the specified * parameters in the SPI_InitStruct. * Input : SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. * SPI_InitStruct: pointer to a SPI_InitTypeDef structure that * contains the configuration information for the specified SPI peripheral. * Return : None *******************************************************************************/ void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct) { uint16_t tmpreg = 0; tmpreg = SPIx->CTLR1; tmpreg &= CTLR1_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->CTLR1 = tmpreg; SPIx->I2SCFGR &= SPI_Mode_Select; SPIx->CRCR = SPI_InitStruct->SPI_CRCPolynomial; } /******************************************************************************* * Function Name : I2S_Init * Description : Initializes the SPIx peripheral according to the specified * parameters in the I2S_InitStruct. * Input : SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. * (configured in I2S mode). * I2S_InitStruct: pointer to an I2S_InitTypeDef structure that * contains the configuration information for the specified SPI peripheral * configured in I2S mode. * Return : None *******************************************************************************/ void I2S_Init(SPI_TypeDef* SPIx, I2S_InitTypeDef* I2S_InitStruct) { uint16_t tmpreg = 0, i2sdiv = 2, i2sodd = 0, packetlength = 1; uint32_t tmp = 0; RCC_ClocksTypeDef RCC_Clocks; uint32_t sourceclock = 0; SPIx->I2SCFGR &= I2SCFGR_CLEAR_Mask; SPIx->I2SPR = 0x0002; tmpreg = SPIx->I2SCFGR; if(I2S_InitStruct->I2S_AudioFreq == I2S_AudioFreq_Default) { i2sodd = (uint16_t)0; i2sdiv = (uint16_t)2; } else { if(I2S_InitStruct->I2S_DataFormat == I2S_DataFormat_16b) { packetlength = 1; } else { packetlength = 2; } if(((uint32_t)SPIx) == SPI2_BASE) { tmp = I2S2_CLOCK_SRC; } else { tmp = I2S3_CLOCK_SRC; } RCC_GetClocksFreq(&RCC_Clocks); sourceclock = RCC_Clocks.SYSCLK_Frequency; if(I2S_InitStruct->I2S_MCLKOutput == I2S_MCLKOutput_Enable) { tmp = (uint16_t)(((((sourceclock / 256) * 10) / I2S_InitStruct->I2S_AudioFreq)) + 5); } else { tmp = (uint16_t)(((((sourceclock / (32 * packetlength)) *10 ) / I2S_InitStruct->I2S_AudioFreq)) + 5); } tmp = tmp / 10; i2sodd = (uint16_t)(tmp & (uint16_t)0x0001); i2sdiv = (uint16_t)((tmp - i2sodd) / 2); i2sodd = (uint16_t) (i2sodd << 8); } if ((i2sdiv < 2) || (i2sdiv > 0xFF)) { i2sdiv = 2; i2sodd = 0; } SPIx->I2SPR = (uint16_t)(i2sdiv | (uint16_t)(i2sodd | (uint16_t)I2S_InitStruct->I2S_MCLKOutput)); tmpreg |= (uint16_t)(I2S_Mode_Select | (uint16_t)(I2S_InitStruct->I2S_Mode | \ (uint16_t)(I2S_InitStruct->I2S_Standard | (uint16_t)(I2S_InitStruct->I2S_DataFormat | \ (uint16_t)I2S_InitStruct->I2S_CPOL)))); SPIx->I2SCFGR = tmpreg; } /******************************************************************************* * Function Name : SPI_StructInit * Description : Fills each SPI_InitStruct member with its default value. * Input : SPI_InitStruct : pointer to a SPI_InitTypeDef structure which * will be initialized. * Return : None *******************************************************************************/ void SPI_StructInit(SPI_InitTypeDef* SPI_InitStruct) { SPI_InitStruct->SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStruct->SPI_Mode = SPI_Mode_Slave; SPI_InitStruct->SPI_DataSize = SPI_DataSize_8b; SPI_InitStruct->SPI_CPOL = SPI_CPOL_Low; SPI_InitStruct->SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStruct->SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; SPI_InitStruct->SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStruct->SPI_CRCPolynomial = 7; } /******************************************************************************* * Function Name : I2S_StructInit * Description : Fills each I2S_InitStruct member with its default value. * Input : I2S_InitStruct : pointer to a I2S_InitTypeDef structure which * will be initialized. * Return : None *******************************************************************************/ void I2S_StructInit(I2S_InitTypeDef* I2S_InitStruct) { I2S_InitStruct->I2S_Mode = I2S_Mode_SlaveTx; I2S_InitStruct->I2S_Standard = I2S_Standard_Phillips; I2S_InitStruct->I2S_DataFormat = I2S_DataFormat_16b; I2S_InitStruct->I2S_MCLKOutput = I2S_MCLKOutput_Disable; I2S_InitStruct->I2S_AudioFreq = I2S_AudioFreq_Default; I2S_InitStruct->I2S_CPOL = I2S_CPOL_Low; } /******************************************************************************* * Function Name : SPI_Cmd * Description : Enables or disables the specified SPI peripheral. * Input : SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. * NewState: ENABLE or DISABLE. * Return : None *******************************************************************************/ void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState) { if (NewState != DISABLE) { SPIx->CTLR1 |= CTLR1_SPE_Set; } else { SPIx->CTLR1 &= CTLR1_SPE_Reset; } } /******************************************************************************* * Function Name : I2S_Cmd * Description : Enables or disables the specified SPI peripheral (in I2S mode). * Input : SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. * NewState: ENABLE or DISABLE. * Return : None *******************************************************************************/ void I2S_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState) { if (NewState != DISABLE) { SPIx->I2SCFGR |= I2SCFGR_I2SE_Set; } else { SPIx->I2SCFGR &= I2SCFGR_I2SE_Reset; } } /******************************************************************************* * Function Name : SPI_I2S_ITConfig * Description : Enables or disables the specified SPI/I2S interrupts. * Input : SPIx: where x can be * - 1, 2 or 3 in SPI mode. * - 2 or 3 in I2S mode. * SPI_I2S_IT: specifies the SPI/I2S interrupt source to be * enabled or disabled. * SPI_I2S_IT_TXE: Tx buffer empty interrupt mask. * SPI_I2S_IT_RXNE: Rx buffer not empty interrupt mask. * SPI_I2S_IT_ERR: Error interrupt mask. * NewState: ENABLE or DISABLE. * Return : None *******************************************************************************/ void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState) { uint16_t itpos = 0, itmask = 0 ; itpos = SPI_I2S_IT >> 4; itmask = (uint16_t)1 << (uint16_t)itpos; if (NewState != DISABLE) { SPIx->CTLR2 |= itmask; } else { SPIx->CTLR2 &= (uint16_t)~itmask; } } /******************************************************************************* * Function Name : SPI_I2S_DMACmd * Description : Enables or disables the SPIx/I2Sx DMA interface. * Input : SPIx: where x can be * - 1, 2 or 3 in SPI mode. * - 2 or 3 in I2S mode. * SPI_I2S_DMAReq: specifies the SPI/I2S DMA transfer request to * be enabled or disabled. * SPI_I2S_DMAReq_Tx: Tx buffer DMA transfer request. * SPI_I2S_DMAReq_Rx: Rx buffer DMA transfer request. * NewState: ENABLE or DISABLE. * Return : None *******************************************************************************/ void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState) { if (NewState != DISABLE) { SPIx->CTLR2 |= SPI_I2S_DMAReq; } else { SPIx->CTLR2 &= (uint16_t)~SPI_I2S_DMAReq; } } /******************************************************************************* * Function Name : SPI_I2S_SendData * Description : Transmits a Data through the SPIx/I2Sx peripheral. * Input : SPIx: where x can be * - 1, 2 or 3 in SPI mode. * - 2 or 3 in I2S mode. * Data : Data to be transmitted. * Return : None *******************************************************************************/ void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data) { SPIx->DATAR = Data; } /******************************************************************************* * Function Name : SPI_I2S_ReceiveData * Description : Returns the most recent received data by the SPIx/I2Sx peripheral. * Input : SPIx: where x can be * - 1, 2 or 3 in SPI mode. * - 2 or 3 in I2S mode. * Data : Data to be transmitted. * Return : SPIx->DATAR: The value of the received data. *******************************************************************************/ uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) { return SPIx->DATAR; } /******************************************************************************* * Function Name : SPI_NSSInternalSoftwareConfig * Description : Configures internally by software the NSS pin for the selected SPI. * Input : SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. * SPI_NSSInternalSoft: * SPI_NSSInternalSoft_Set: Set NSS pin internally. * SPI_NSSInternalSoft_Reset: Reset NSS pin internally. * Return : None *******************************************************************************/ void SPI_NSSInternalSoftwareConfig(SPI_TypeDef* SPIx, uint16_t SPI_NSSInternalSoft) { if (SPI_NSSInternalSoft != SPI_NSSInternalSoft_Reset) { SPIx->CTLR1 |= SPI_NSSInternalSoft_Set; } else { SPIx->CTLR1 &= SPI_NSSInternalSoft_Reset; } } /******************************************************************************* * Function Name : SPI_SSOutputCmd * Description : Enables or disables the SS output for the selected SPI. * Input : SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. * NewState: new state of the SPIx SS output. * Return : None *******************************************************************************/ void SPI_SSOutputCmd(SPI_TypeDef* SPIx, FunctionalState NewState) { if (NewState != DISABLE) { SPIx->CTLR2 |= CTLR2_SSOE_Set; } else { SPIx->CTLR2 &= CTLR2_SSOE_Reset; } } /******************************************************************************* * Function Name : SPI_DataSizeConfig * Description : Configures the data size for the selected SPI. * Input : SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. * SPI_DataSize: specifies the SPI data size. * SPI_DataSize_16b: Set data frame format to 16bit. * SPI_DataSize_8b: Set data frame format to 8bit. * Return : None *******************************************************************************/ void SPI_DataSizeConfig(SPI_TypeDef* SPIx, uint16_t SPI_DataSize) { SPIx->CTLR1 &= (uint16_t)~SPI_DataSize_16b; SPIx->CTLR1 |= SPI_DataSize; } /******************************************************************************* * Function Name : SPI_TransmitCRC * Description : Transmit the SPIx CRC value. * Input : SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. * Return : None *******************************************************************************/ void SPI_TransmitCRC(SPI_TypeDef* SPIx) { SPIx->CTLR1 |= CTLR1_CRCNext_Set; } /******************************************************************************* * Function Name : SPI_CalculateCRC * Description : Enables or disables the CRC value calculation of the transferred bytes. * Input : SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. * NewState: new state of the SPIx CRC value calculation. * Return : None *******************************************************************************/ void SPI_CalculateCRC(SPI_TypeDef* SPIx, FunctionalState NewState) { if (NewState != DISABLE) { SPIx->CTLR1 |= CTLR1_CRCEN_Set; } else { SPIx->CTLR1 &= CTLR1_CRCEN_Reset; } } /******************************************************************************* * Function Name : SPI_GetCRC * Description : Returns the transmit or the receive CRC register value for the specified SPI. * Input : SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. * SPI_CRC: specifies the CRC register to be read. * SPI_CRC_Tx: Selects Tx CRC register. * SPI_CRC_Rx: Selects Rx CRC register. * Return : crcreg: The selected CRC register value. *******************************************************************************/ uint16_t SPI_GetCRC(SPI_TypeDef* SPIx, uint8_t SPI_CRC) { uint16_t crcreg = 0; if (SPI_CRC != SPI_CRC_Rx) { crcreg = SPIx->TCRCR; } else { crcreg = SPIx->RCRCR; } return crcreg; } /******************************************************************************* * Function Name : SPI_GetCRCPolynomial * Description : Returns the CRC Polynomial register value for the specified SPI. * Input : SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. * Return : SPIx->CRCR: The CRC Polynomial register value. *******************************************************************************/ uint16_t SPI_GetCRCPolynomial(SPI_TypeDef* SPIx) { return SPIx->CRCR; } /******************************************************************************* * Function Name : SPI_BiDirectionalLineConfig * Description : Selects the data transfer direction in bi-directional mode * for the specified SPI. * Input : SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. * SPI_Direction: specifies the data transfer direction in * bi-directional mode. * SPI_Direction_Tx: Selects Tx transmission direction. * SPI_Direction_Rx: Selects Rx receive direction. * Return : None *******************************************************************************/ void SPI_BiDirectionalLineConfig(SPI_TypeDef* SPIx, uint16_t SPI_Direction) { if (SPI_Direction == SPI_Direction_Tx) { SPIx->CTLR1 |= SPI_Direction_Tx; } else { SPIx->CTLR1 &= SPI_Direction_Rx; } } /******************************************************************************* * Function Name : SPI_I2S_GetFlagStatus * Description : Checks whether the specified SPI/I2S flag is set or not. * Input : SPIx: where x can be * - 1, 2 or 3 in SPI mode. * - 2 or 3 in I2S mode. * SPI_I2S_FLAG: specifies the SPI/I2S flag to check. * SPI_I2S_FLAG_TXE: Transmit buffer empty flag. * SPI_I2S_FLAG_RXNE: Receive buffer not empty flag. * SPI_I2S_FLAG_BSY: Busy flag. * SPI_I2S_FLAG_OVR: Overrun flag. * SPI_FLAG_MODF: Mode Fault flag. * SPI_FLAG_CRCERR: CRC Error flag. * I2S_FLAG_UDR: Underrun Error flag. * I2S_FLAG_CHSIDE: Channel Side flag. * Return : FlagStatus: SET or RESET. *******************************************************************************/ FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG) { FlagStatus bitstatus = RESET; if ((SPIx->STATR & SPI_I2S_FLAG) != (uint16_t)RESET) { bitstatus = SET; } else { bitstatus = RESET; } return bitstatus; } /******************************************************************************* * Function Name : SPI_I2S_ClearFlag * Description : Clears the SPIx CRC Error (CRCERR) flag. * Input : SPIx: where x can be * - 1, 2 or 3 in SPI mode. * - 2 or 3 in I2S mode. * SPI_I2S_FLAG: specifies the SPI flag to clear. * SPI_FLAG_CRCERR: CRC Error flag. * Return : FlagStatus: SET or RESET. *******************************************************************************/ void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG) { SPIx->STATR = (uint16_t)~SPI_I2S_FLAG; } /******************************************************************************* * Function Name : SPI_I2S_GetITStatus * Description : Checks whether the specified SPI/I2S interrupt has occurred or not. * Input : SPIx: where x can be * - 1, 2 or 3 in SPI mode. * - 2 or 3 in I2S mode. * SPI_I2S_IT: specifies the SPI/I2S interrupt source to check.. * SPI_I2S_IT_TXE: Transmit buffer empty interrupt. * SPI_I2S_IT_RXNE: Receive buffer not empty interrupt. * SPI_I2S_IT_OVR: Overrun interrupt. * SPI_IT_MODF: Mode Fault interrupt. * SPI_IT_CRCERR: CRC Error interrupt. * I2S_IT_UDR: Underrun Error interrupt. * Return : FlagStatus: SET or RESET. *******************************************************************************/ ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT) { ITStatus bitstatus = RESET; uint16_t itpos = 0, itmask = 0, enablestatus = 0; itpos = 0x01 << (SPI_I2S_IT & 0x0F); itmask = SPI_I2S_IT >> 4; itmask = 0x01 << itmask; enablestatus = (SPIx->CTLR2 & itmask) ; if (((SPIx->STATR & itpos) != (uint16_t)RESET) && enablestatus) { bitstatus = SET; } else { bitstatus = RESET; } return bitstatus; } /******************************************************************************* * Function Name : SPI_I2S_ClearITPendingBit * Description : Clears the SPIx CRC Error (CRCERR) interrupt pending bit. * Input : SPIx: where x can be * - 1, 2 or 3 in SPI mode. * SPI_I2S_IT: specifies the SPI interrupt pending bit to clear. * SPI_IT_CRCERR: CRC Error interrupt. * Return : FlagStatus: SET or RESET. *******************************************************************************/ void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT) { uint16_t itpos = 0; itpos = 0x01 << (SPI_I2S_IT & 0x0F); SPIx->STATR = (uint16_t)~itpos; }