LCD驱动方式分为软件SPI和硬件SPI(具体的显示等函数就不展示了)
起初SPI初始化函数和传输数据的函数是这样子的,但是屏幕就是不能显示东西。
/**@brief LCD SPI初始化
* @param 无
* @retval 无
*/
void LCD_SPI_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.GPIO_Pin = SCLK_PIN | SDA_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1);
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
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_2;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 10;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
}
/**@brief LCD写数据(8位)
* @param dat:要发送的数据
* @retval 无
*/
void LCD_WR_DATA8(u8 dat)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1, dat);
}
/**@brief LCD写数据(16位)
* @param dat:要发送的数据
* @retval 无
*/
void LCD_WR_DATA(u16 dat)
{
uint8_t temp[2];
temp[0] = (dat >> 8)&0xFF;
temp[1] = dat&0xFF;
LCD_WR_DATA8(temp[0]);
LCD_WR_DATA8(temp[1]);
}
/**@brief LCD写命令
* @param dat:要发送的命令
* @retval 无
*/
void LCD_WR_REG(u8 dat)
{
LCD_DC_Clr();//写命令
LCD_WR_DATA8(dat);
LCD_DC_Set();//写数据
}
然后我无奈试了试软件SPI驱动看看屏幕能不能显示东西出来;
软件SPI代码:
void LCD_GPIO_Init(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.GPIO_Pin = RES_PIN | CS_PIN | DC_PIN | SDA_PIN | SCLK_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, RES_PIN | CS_PIN | DC_PIN);
}
void LCD_WR_DATA8(u8 dat)
{
//软件SPI驱动
u8 i;
LCD_SCLK_Set(); // 初始时钟高电平(CPOL=1)
for (i = 0; i < 8; i++) {
LCD_SCLK_Clr(); // 第一个边沿(下降沿)
delay_us(1); // 短暂延时
if (dat & 0x80) {
LCD_MOSI_Set();
} else {
LCD_MOSI_Clr();
}
LCD_SCLK_Set(); // 第二个边沿(上升沿,数据采样)
delay_us(1); // 短暂延时
dat <<= 1;
}
}
出乎我的意料,软件SPI居然能让屏幕显示东西出来,但是毕竟是软件模拟,速率肯定比不上硬件SPI而且还很占用CPU资源。于是我按照同学的指示去查SPI寄存器的值,发现DR寄存器是有值变化的,但就是点不亮屏幕,百思不得其解。
然后在第二天,我开始怀疑SPI速率的问题,把SPI时钟频率降低又把GPIO口的速率降低,但是这样依旧没有效果。实在没啥能改的我就在函数SPI_I2S_SendData(SPI1, dat);的后面加了一个delay_us(1);神奇的是屏幕能显示东西了,而且速率不算太慢,但是毕竟是delay还是会占用cpu资源,我把这个问题丢给AI,它建议我将delay换成
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY));
得到的解答是 不等待 BSY
会导致 SPI 数据未完整发送 或 MOSI 信号不稳定,而 LCD 对时序极其敏感,因此必须等待 BSY=0
以确保可靠通信。那么应该可以理解为DR寄存器上的数据还没有传出去就又有新数据传进来了,导致数据没有稳定传到LCD,自然也就显示不了。
最后发现等待bsy标志位置位的方法要比delay得到的屏幕刷新快1倍左右。
硬件SPI代码:
void LCD_SPI_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.GPIO_Pin = SCLK_PIN | SDA_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1);
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
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_2;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 10;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
}
void LCD_WR_DATA8(u8 dat)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1, dat);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
}
还有硬件SPI+DMA的方法还在实现当中,这个方法速率更快而且CPU资源占用率很低。