SPI的用法比I2C的用法简单很多,没有START和STOP之类的起始字节和结束字节,主从机之间也不用通过ACK进行确认。
这是因为SPI与I2C的通信机制不同。SPI的主从机是确定的,主机通过CS线选择不同的从机进行通信。而I2C没有CS线,主从机的确定只能通过I2C的通信机制。
SPI的使用主要包括以下两部分:
SPI的初始化包括:
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);
读写函数包括:
void spi_i2s_data_transmit(uint32_t spi_periph, uint16_t data);
uint16_t spi_i2s_data_receive(uint32_t spi_periph);
注意在发送数据的时候,每次发送之前要检测标志位SPI_FLAG_TBE
是否置位,这个标志位表示发送缓冲区空,可以向发送缓冲区写入新的数据了。而发送完成需要检测SPI_FLAG_TRANS
置位,这个标志位表示通信是否在进行中。
oled的驱动函数实际上只需要修改一个函数,void OLED_WR_Byte(uint8_t dat,uint8_t cmd)
。这个函数负责向OLED写入数据或指令。其它部分可以直接使用厂家给出的测试代码。但是一定要注意,在发送完数据之后,一定要检查SPI_FLAG_TRANS
置位之后在拉高CS线。
一开始我改了代码之后屏幕始终没有输出,但是用IO模拟SPI就可以在屏幕上显示,这个就是因为在拉高CS线之前没有检查SPI_FLAG_TRANS
置位,造成数据还没有发送完就中断了数据的发送。
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);
}