The usage of SPI is much simpler than that of I2C. There is no start byte and end byte such as START and STOP, and there is no need for ACK confirmation between the master and slave devices.
This is because the communication mechanisms of SPI and I2C are different. The master and slave of SPI are determined, and the host selects different slaves for communication through the CS line. However, I2C does not have a CS line, and the master and slave can only be determined through the communication mechanism of I2C.
The use of SPI mainly includes the following two parts:
- initialization
- Read and Write Operations
initialization
SPI initialization includes:
- Clock: Enable pin and SPI clock
- Pins: Pin initialization and setting to alternate functions
- SPI type: set the communication mode, waveform timing frequency
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_SPI1);
gpio_af_set(GPIOB, GPIO_AF_6, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
spi_parameter_struct spi_init_struct;
spi_i2s_deinit(SPI1);
spi_struct_para_init(&spi_init_struct);
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init_struct.device_mode = SPI_MASTER;
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT;
spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;
spi_init_struct.nss = SPI_NSS_SOFT;
spi_init_struct.prescale = SPI_PSC_32;
spi_init_struct.endian = SPI_ENDIAN_MSB;
spi_init(SPI1, &spi_init_struct);
spi_enable(SPI1);
SPI read and write operations
Read and write functions include:
void spi_i2s_data_transmit(uint32_t spi_periph, uint16_t data);
uint16_t spi_i2s_data_receive(uint32_t spi_periph);
Note that when sending data, you need to check SPI_FLAG_TBE
whether the flag is set before each transmission. This flag indicates that the send buffer is empty and new data can be written to the send buffer. And the send completion needs to be checked and SPI_FLAG_TRANS
set. This flag indicates whether the communication is in progress.
SPI Application - Using SPI interface to operate OLED screen
The OLED driver function actually only needs to modify one function. void OLED_WR_Byte(uint8_t dat,uint8_t cmd)
This function is responsible for writing data or instructions to the OLED. The other parts can directly use the test code provided by the manufacturer. But be sure to note that after sending the data, you must check SPI_FLAG_TRANS
the setting before pulling up the CS line.
At first, after I changed the code, there was no output on the screen. However, using IO to simulate SPI, it could be displayed on the screen. This was because the SPI_FLAG_TRANS
set bit was not checked before pulling the CS line high, resulting in the interruption of data transmission before the data was sent.
void OLED_WR_Byte(uint8_t dat,uint8_t cmd)
{
if(cmd)
gpio_bit_set(OLED_DC_PORT,OLED_DC_PIN);
else
gpio_bit_reset(OLED_DC_PORT,OLED_DC_PIN);
gpio_bit_reset(OLED_CS_PORT,OLED_CS_PIN);
while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_TBE));
spi_i2s_data_transmit(SPI1,dat);
while(RESET != spi_i2s_flag_get(SPI1, SPI_FLAG_TRANS));
//注意在拉高CS线之前一定要等待TRANS标志位置位
gpio_bit_set(OLED_CS_PORT,OLED_CS_PIN);
gpio_bit_set(OLED_DC_PORT,OLED_DC_PIN);
}