[STM32] STM32 SPI peripheral use

related articles

1. "Introduction to SPI Communication Protocol"
2. "Use of STM32 SPI Peripherals"
3. "[IMU] Analysis and Use of BMI160 Driver"

1 Introduction

STM32's SPI peripherals can be used as communication masters and slaves, and support the highest SCK clock frequency of f pclk /2 (STM32F103The default model chip F PCLK1 is 36MHz , F PCLK2 is 72MHz ), fully supports the SPI protocol . 4 modes, the data frame length can be set . 8 bits or 16 bits of data may be set MSBin advance or LSBahead. It also supports two-wire full-duplex, two-wire one-way, and single-wire modes. Unidirectional mode wherein the wire may be used simultaneously MOSIand MISOthe transmission data line in one direction to speed up transmission double. The single-wire mode can reduce the hardware wiring, of course, the speed will be affected.

Note: The following introductions are based on the STM32F103 platform.

2. SPI functional block diagram

Usually SPI is connected to external devices through 4 pins:

  • MISO : Master input/slave output pin. This pin sends data in slave mode and receives data in master mode.
  • MOSI : Master device output/slave device input pin. This pin sends data in master mode and receives data in slave mode.
  • SCK : Serial port clock, as the output of the master device and the input of the slave device
  • NSS : Select from the device. This is an optional pin to select the master/slave device.

STM32F103 SPI pin summary:

Pin SPI1 SPI2
DAWDLE PA7 PB15
MISO PA6 PB14
SCK PA5 PB13
NSS PA4 PB12

Among them, SPI1 is a device on APB2 , with a maximum communication rate of 36Mbits/s ( 72M/2). SPI2 is a device on APB1 with a maximum communication rate of 18Mbits/s ( 36M/2).

3. Initialize the SPI interface

The main steps of initializing the SPI interface:

  1. Initialize GPIO to SPI mode ( Bsp_Spi_LowLevel_Init);
  2. SPI_DirectionSet to full duplex mode SPI_Direction_2Lines_FullDuplex;
  3. SPI_ModeSet to Master mode SPI_Mode_Master;
  4. SPI_DataSizeSet the data width to 8Bit;
  5. SPI_CPOLSPI_CPHABoth and are set to 1, working in mode 3; ( need to be set according to the mode supported by SPI Slave. )
  6. SPI_NSSSet to software control SPI_NSS_Soft, the software directly manipulates the IO level when communication is required.
  7. SPI_BaudRatePrescalerSet the communication rate divider parameter to divide by 16 SPI_BaudRatePrescaler_16. ( SPI_BaudRate = 72M / 16 = 4.5M , this parameter needs to be determined according to the maximum rate supported by the SPI Slave. )
  8. SPI_FirstBitSet to start transmission from the high position SPI_FirstBit_MSB;
  9. After initializing SPI and enabling;
void Bsp_Spi_Init(void)
{
    
    
	SPI_InitTypeDef  SPI_InitStructure;

	Bsp_Spi_LowLevel_Init();

	/*!< Deselect the FLASH: Chip Select high */
	BMI160_CS_HIGH();

	/*!< SPI configuration */
	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_High;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;

	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_Init(BMI160_SPI, &SPI_InitStructure);

	/*!< Enable the sFLASH_SPI  */
	SPI_Cmd(BMI160_SPI, ENABLE);
}

According to the actual test, the transmission rate is 4.5M , which is the same as the expected value of our SPI initialization.
Insert picture description here

4. SPI communication to read and write data

When STM32 uses SPI peripheral communication, it will write parameters to different data bits of the "status register SR" at different stages of communication . We understand the communication status by reading these register flags. The following figure is the "master mode" process, that is, the data receiving and sending process when STM32 is used as the host of SPI communication.
Insert picture description here
The main mode sending and receiving process and event description are as follows:

  1. Control the NSS signal line to generate the start signal (not shown in the figure);
  2. Write the data to be sent into the "data register DR" , and the data will be stored in the sending buffer;
  3. The communication starts, and the SCK clock starts to run. MOSI transmits the data in the sending buffer bit by bit; MISO stores the data in the receiving buffer bit by bit;
  4. When a frame of data is sent , the “TXE flag in the “status register SR” will be set to 1, indicating that the transmission buffer is empty after a frame is transmitted; similarly, when a frame of data is received, "RXNE flag" will be set to 1, indicating that the receive buffer is not empty after a frame has been transmitted;
  5. Wait until the "TXE flag bit" is 1, if you want to continue sending data, you can write data to the "data register DR" again ; when you wait until the "RXNE flag bit" is 1, you can read the "data register DR" " You can get the contents of the receiving buffer.
/**
* @brief  使用SPI读取一个字节的数据
* @param  读取数据的地址
* @retval 返回接收到的数据状态
*/
int8_t SPI_FLASH_ReadByte(uint8_t* pBuffer)
{
    
    
	SPITimeout = SPIT_FLAG_TIMEOUT;

	/* 等待接收缓冲区非空,RXNE事件 */
	while (SPI_I2S_GetFlagStatus(BMI160_SPI, SPI_I2S_FLAG_RXNE) == RESET)
	{
    
    
		if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);
	}

	/* 读取数据寄存器,获取接收缓冲区数据 */
	*pBuffer =SPI_I2S_ReceiveData(BMI160_SPI);

	return 0;
}

 /**
  * @brief  使用SPI发送一个字节的数据
  * @param  byte:要发送的数据
  * @retval 返回接发送的数据状态
  */
int8_t SPI_FLASH_SendByte(uint8_t byte)
{
    
    
  SPITimeout = SPIT_FLAG_TIMEOUT;

  /* 等待发送缓冲区为空,TXE事件 */
  while (SPI_I2S_GetFlagStatus(BMI160_SPI, SPI_I2S_FLAG_TXE) == RESET)
   {
    
    
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);
   }

  /* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
  SPI_I2S_SendData(BMI160_SPI, byte);

  return 0;
}

/**
  * @brief  Writes block of data to the Slave. 
  * @param  pBuffer: pointer to the buffer  containing the data to be written
  *         to the Slave.
  * @param  WriteAddr: Slave's internal address to write to.
  * @param  NumByteToWrite: number of bytes to write to the Slave.
  * @retval Communication result
  */
int8_t Bsp_Spi_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
    
    
	int8_t ret;
	uint8_t tmpBuf;
	
	/*!< Select the Slave: Chip Select low */
	BMI160_CS_LOW();

	ret = SPI_FLASH_SendByte(WriteAddr);
	ret = SPI_FLASH_ReadByte(&tmpBuf);

	/*!< while there is data to be written on the Slave*/
	while (NumByteToWrite--)
	{
    
    
	  /*!< Send the current byte */
	  ret = SPI_FLASH_SendByte(*pBuffer);
	  ret = SPI_FLASH_ReadByte(&tmpBuf);
	  /*!< Point on the next byte to be written */
	  pBuffer++;
	}
	
	/*!< Deselect the FLASH: Chip Select high */
	BMI160_CS_HIGH();

	return ret;
}

/**
  * @brief  Reads a block of data from the Slave.
  * @param  pBuffer: pointer to the buffer that receives the data read from the Slave.
  * @param  ReadAddr: Slave internal address to read from.
  * @param  NumByteToRead: number of bytes to read from the Slave.
  * @retval Communication result
  */
int8_t Bsp_Spi_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
    
    
	int8_t ret;
	uint8_t tmpBuf;

	/*!< Select the FLASH: Chip Select low */
	BMI160_CS_LOW();

	ret = SPI_FLASH_SendByte(ReadAddr);
	ret = SPI_FLASH_ReadByte(&tmpBuf);

	while (NumByteToRead--) /*!< while there is data to be read */
	{
    
    
		/*!< Read a byte from the FLASH */
		ret = SPI_FLASH_SendByte(Dummy_Byte);
		ret = SPI_FLASH_ReadByte(pBuffer);
		/*!< Point to the next location where the byte read will be saved */
		pBuffer++;
	}

	/*!< Deselect the FLASH: Chip Select high */
	BMI160_CS_HIGH();

	return ret;
}

Note: SPI works in full-duplex mode:

  • The data will be read while sending the data. The main purpose is to send the data. The read data is to clear the status bit and discard it.
  • The data will be sent while reading the data. The main purpose is to read the data. Sending the data is to clear the status bit and send invalid data.

Such operations are all to clear the status corresponding to the status register SR .

5. Verification results

Log printed by the device after transplanting SPI IMU BMI160:

starting up!!!
BMI160 Init is successful!!!
rslt = 0, chip_id = 0xd1
BMI160 accel & gyro config is successful!!!
[accel] X = 1.11g/s, Y = 0.26g/s, Z = 9.95g/s
[gyro] X = 0.30°/s, Y = -0.24°/s, Z = -0.37°/s
[accel] X = 1.01g/s, Y = 0.17g/s, Z = 10.06g/s

The following is the data captured when the logic analyzer communicates (read the chip ID data):
Insert picture description here

6. Data download address

The complete code download address for successful transplantation is as follows:
https://download.csdn.net/download/ZHONGCAI0901/13218801

Guess you like

Origin blog.csdn.net/ZHONGCAI0901/article/details/110467935