In-Depth Guide to STM32 SPI Master-Slave Communication
SPI, as one of the commonly used communication interfaces for microcontrollers, is frequently utilized for high-speed data exchange with various peripherals. This article will take you deep into the STM32 SPI master-slave communication, mastering this practical skill.
1. Basic Concepts of SPI
SPI (Serial Peripheral Interface) is a synchronous serial communication protocol, typically consisting of four lines:
- • SCLK: Clock line, generated by the master
- • MOSI: Master Output/Slave Input
- • MISO: Master Input/Slave Output
- • CS/NSS: Chip Select line, used to select the slave
Imagine SPI as a bidirectional conveyor belt, where SCLK is the power of the conveyor, MOSI and MISO are the goods moving in two directions, and CS is the switch button.
Note: SPI is a full-duplex communication, meaning data can be transmitted in both directions simultaneously.
2. STM32 SPI Hardware Circuit
Taking STM32F103 as an example, a typical connection for SPI1 is as follows:
STM32 (Master) Slave Device
PA5(SCK) -----------------> SCK
PA6(MISO) <----------------- MISO
PA7(MOSI) -----------------> MOSI
PA4(NSS) -----------------> CS
Note: In practical applications, NSS is often simulated by a regular GPIO for more flexible control of multiple slaves.
3. SPI Master Initialization Code
void SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
// Enable clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);
// Configure GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// Configure SPI
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_256;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
// Enable SPI
SPI_Cmd(SPI1, ENABLE);
}
This piece of code is like configuring the working parameters for SPI1. Imagine you are setting up a sewing machine, adjusting stitch length, speed, etc.; here we are also setting parameters such as data width and clock polarity for SPI.
4. SPI Data Transmission and Reception
uint8_t SPI1_ReadWriteByte(uint8_t TxData)
{
uint8_t retry = 0;
// Wait for the transmit buffer to be empty
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)
{
if(++retry > 200) return 0;
}
// Send data
SPI_I2S_SendData(SPI1, TxData);
retry = 0;
// Wait for the receive buffer to be non-empty
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
{
if(++retry > 200) return 0;
}
// Return the received data
return SPI_I2S_ReceiveData(SPI1);
}
This function is like playing ping pong; you hit the ball (send data), then wait for the other side to return a ball (receive data). If the other side takes too long to respond, we stop waiting to avoid the program hanging.

5. Practical Application Case: Reading SPI Flash
Suppose we want to read the contents of an SPI Flash chip:
void ReadFlashID(void)
{
uint8_t Temp = 0;
SPI_CS_Low(); // Pull low chip select, select Flash chip
SPI1_ReadWriteByte(0x9F); // Send read ID command
Temp = SPI1_ReadWriteByte(0xFF); // Read manufacturer ID
printf("Manufacturer ID: 0x%02X\n", Temp);
Temp = SPI1_ReadWriteByte(0xFF); // Read memory type
printf("Memory Type: 0x%02X\n", Temp);
Temp = SPI1_ReadWriteByte(0xFF); // Read capacity
printf("Capacity: 0x%02X\n", Temp);
SPI_CS_High(); // Pull high chip select, release Flash chip
}
This process is like asking the Flash chip, “Who are you, where are you from, and where are you going?” We first send a specific command (0x9F), and then the Flash chip obediently tells us its identity information.
6. Common Problems and Solutions
- 1. Communication Failure
- • Check if the hardware connections are correct
- 2. Data Errors
- • Ensure that the SPI parameters (such as CPOL, CPHA) of the master and slave match
- • Use an oscilloscope to observe if the SCLK and MOSI signals are normal
- 3. Multiple Slaves Confusion
- • Ensure that the data length settings are correct (8-bit/16-bit)
- • Consider adding delays to give the slave enough response time
- • Consider using GPIO to simulate NSS for more flexible control of multiple slaves
Note: When debugging SPI communication, do not forget to check the power and ground connections. Many times, seemingly complex problems arise from the most basic connection issues.
Practical Suggestions
- 1. Start with simple SPI devices, such as EEPROM or Flash chips.
- 2. Use a logic analyzer to observe SPI signals and understand the communication process.
- 3. Try different SPI modes (combinations of CPOL and CPHA) to understand their differences.
- 4. Implement an SPI-based multi-slave system, such as a temperature sensor + OLED display.
- 5. Explore the SPI DMA functionality of STM32 to improve data transfer efficiency.
Through the above learning and practice, I believe you have gained a deep understanding of STM32 SPI communication. Remember, what you learn from books is always superficial; true understanding comes from practice. The more hands-on experience you have, the better you can master this skill.