stm32硬件SPI驱动3线SPI-LCD的方法
1.基本控制原理
三线SPI LCD, 顾名思义,最少只需要3个IO控制LCD显示,如果采用硬件控制上电时序和背光,最少只需要接SCK,CS,MOSI三个引脚即可控制LCD,并且不管接不接其他引脚,控制刷屏的只需要这三个引脚;
CS为片选引脚,CS拉低代表数据传输开始;CS拉高,代表数据传输结束;
SCK为SPI时钟线,一般LCD上标识SCL;
MOSI是master数据输出,即单片机发送数据到LCD,一般LCD上标识SDA;
3线SPI-LCD与4线的唯一区别是少了一个DC引脚,DC拉低代表传输的是指令;DC拉高代表传输的是数据,3线SPI驱动需要使用MOSI模拟DC的时序;
那么使用STM32单片机驱动,很容易使用GPIO模拟SPI时序实现3线SPI-LCD的驱动,但是由于GPIO的翻转速度,就算使用寄存器操作,屏幕刷新速度也非常慢;
2.如何使用硬件SPI驱动3线SPI-LCD
本文提供两种方法使用stm32单片机实现:
(1)单字节转换后SPI发送;
所谓单字节转换后发送,就是每次只发送一个有效字节,将一个8位的数据,转换成16位,再位或0x8000,然后通过SPI发送出去,具体可以拆分成两个8bit数据发送;
具体实现如下:
unsigned char spi_send_data_8bit(unsigned char Txdata)
{
while((SPI2->SR & SPI_I2S_FLAG_TXNE) == (uint16_t)RESET);
SPI2->DR=Txdata;
while((SPI2->SR & SPI_I2S_FLAG_RXNE) == (uint16_t)RESET);
return (unsigned char)(SPI2->DR);
}
void LCD_Send_8bit(unsigned char data)
{
cs_low;
spi_send_data_8bit((data>>1)|0x80);
spi_send_data_8bit(data<<7);
cs_high;
}
上述单字节发送在stm32F4平台,50MHz SPI下,320x240 分辨率显示能刷4fps;
320x240x2x4x8 大约有5Mbps,如果采用更小的分辨率刷10帧以上没有问题。
(2)多字节转换后发送
使用单字节发送存在一个问题,SPI 每次只能按字节发送,就算SPI速度再快,单字节发送出去也会影响性能,其次每次发送一个有效字节都存在cs的拉高拉低,极大的影响了效率。既然单字节转换后可以发送,多字节转换后当然也能发送。其原理就是每一个DC后面跟8个有效bit,使用MOSI模拟时序做数据转换;
先看看怎么一次发8个字节;
void LCD_Send_8byte(unsigned char *send_buf,unsigned char buflen)
{
if(send_buf==NULL||buflen!=8)
{
printf("param error");
return;
}
unsigned char *pbuf=send_buf;
nsigned char lcd_buf[9]={0};
lcd_buf[0]=0x80|(*pbuf>>1);
lcd_buf[1]=0x40|(*pbuf<<7)|(*(pbuf+1)>>2);
lcd_buf[2]=0x20|(*(pbuf+1)<<6)|(*(pbuf+2)>>3);
lcd_buf[3]=0x10|(*(pbuf+2)<<5)|(*(pbuf+3)>>4);
lcd_buf[4]=0x08|(*(pbuf+3)<<4)|(*(pbuf+4)>>5);
lcd_buf[5]=0x04|(*(pbuf+4)<<3)|(*(pbuf+5)>>6);
lcd_buf[6]=0x02|(*(pbuf+5)<<2)|(*(pbuf+6)>>7);
lcd_buf[7]=0x01|(*(pbuf+6)<<1);
lcd_buf[8]=*(pbuf+7);
int i=0;
cs_low;
for(i=0;i<9;i++)
{
spi_send_data_8bit(lcd_buf[i]);
}
cs_high;
}
//使用CCM快速内存分配数据作为LCD的缓冲,如果内存足够这个缓冲也可以大一点
static int nwidth=image_width8/9*16*2;//分配一个16行的缓冲
unsigned char* lcd_buf=mymalloc(SRAM_CCM,nwidth);
/*******************************
功能:lcd显示n行数据,发送RGB565
入参:图像数据指针,发送长度,LCD缓冲数据指针,长度
出参:无
*********************************/
void LCD_Send_Nwith(unsigned char *send_buf,unsigned int send_length,unsigned char *lcd_buf,unsigned int nwidth)
{
if((send_buf ==NULL)||((send_length%8)!=0))
{
printf(“param error”);
return;
}
unsigned char *pbuf=send_buf;
unsigned char *plcd_buf=lcd_buf;
or(i=0;i<nwidth/9;i++)
{
*plcd_buf=0x80|(*pbuf>>1);
*(plcd_buf+1)=0x40|(*pbuf<<7)|(*(pbuf+1)>>2);
*(plcd_buf+2)=0x20|(*(pbuf+1)<<6)|(*(pbuf+2)>>3);
*(plcd_buf+3)=0x10|(*(pbuf+2)<<5)|(*(pbuf+3)>>4);
*(plcd_buf+4)=0x08|(*(pbuf+3)<<4)|(*(pbuf+4)>>5);
*(plcd_buf+5)=0x04|(*(pbuf+4)<<3)|(*(pbuf+5)>>6);
*(plcd_buf+6)=0x02|(*(pbuf+5)<<2)|(*(pbuf+6)>>7);
*(plcd_buf+7)=0x01|(*(pbuf+6)<<1);
*(plcd_buf+8)=(*pbuf+7);
pbuf+=8;
plcd_buf+=9;
}
pbuf=NULL;
plcd_buf=lcd_buf;;
cs_low;
while(nwidth–)
{
spi_send_data_8bit(*plcd_buf++);
}
cs_high;
plcd_buf=NULL;
}
```
对应与发送8bit的有效数据,实际需要MOSI发送9bit数据,因为第一个bit代表了数据标识,那么发送任意字节长度的3线SPI数据,可以按照下面的方式:
void LCD_Send_bytes(unsigned char *send_buf,int length)
{
if((send_buf ==NULL)||((length%8)!=0))
{
printf("param error \n");
return;
}
int len1=length/8;
unsigned char *pbuf= send_buf;
cs_low;
while( len1--)
{
LCD_Send_8byte(pbuf,8);
pbuf+=8;
}
cs_high;
}
按照以上方式,3线SPI发送数据时可以按行进行批量发送,并且可以配置DMA进行搬运,CPU只需要处理数据转换即可;
上述单字节发送在stm32F4平台,50MHz SPI下,320x240 分辨率显示能刷到8fps;
320x240x2x8x8 大约有10Mbps,如果采用更小的分辨率刷20帧以上没有问题。
以上为全部3线SPI-LCD的硬件SPI驱动方法,下次在ESP32-CAM平台也试试,采用16bit模式会更快。