stm32硬件SPI驱动3线SPI-LCD的方法

本文介绍了如何使用STM32单片机驱动3线SPI-LCD,包括单字节和多字节转换后发送的方法,以及如何通过批量发送和DMA优化性能,以提升在50MHz SPI下至8fps的刷新速率,适用于不同分辨率的显示需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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模式会更快。















基于STM32的WT588D三线控制程序需要考虑到STM32硬件资源和软件开发环境。以下是一个简单的流程: 1. 硬件连接 - 将WT588D的三个引脚(P01、P02和P03)连接到STM32的三个IO引脚。 - 将WT588D的VCC和GND引脚连接到STM32的电源引脚。 2. 软件开发 - 初始化IO引脚,设置为输出模式。 - 配置STM32SPI接口,用于与WT588D模块的通信。 - 发送控制命令和音频数据到WT588D模块,控制其播放音频。 - 在程序中添加延时函数,控制音频的播放时间和间隔。 以下是一个简单的基于STM32的WT588D三线控制程序示例: ```c #include "stm32f10x.h" #define WT588D_CS_Pin GPIO_Pin_0 #define WT588D_CLK_Pin GPIO_Pin_1 #define WT588D_DAT_Pin GPIO_Pin_2 #define WT588D_CS_Port GPIOA #define WT588D_CLK_Port GPIOA #define WT588D_DAT_Port GPIOA void DelayUs(unsigned int i) { while(i--) { asm("nop"); } } void WT588D_SendCmd(unsigned char cmd) { unsigned char i; GPIO_ResetBits(WT588D_CS_Port, WT588D_CS_Pin); DelayUs(1); GPIO_ResetBits(WT588D_CLK_Port, WT588D_CLK_Pin); DelayUs(1); for(i=0; i<8; i++) { GPIO_WriteBit(WT588D_DAT_Port, WT588D_DAT_Pin, (cmd & 0x01) ? Bit_SET : Bit_RESET); cmd >>= 1; DelayUs(1); GPIO_SetBits(WT588D_CLK_Port, WT588D_CLK_Pin); DelayUs(1); GPIO_ResetBits(WT588D_CLK_Port, WT588D_CLK_Pin); DelayUs(1); } GPIO_SetBits(WT588D_CS_Port, WT588D_CS_Pin); } void WT588D_PlaySound(unsigned char sound) { WT588D_SendCmd(0x02); WT588D_SendCmd(sound); } int main(void) { unsigned char i; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = WT588D_CS_Pin | WT588D_CLK_Pin | WT588D_DAT_Pin; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); for(i=0; i<3; i++) { WT588D_PlaySound(i+1); DelayUs(5000); } while(1); } ``` 以上是一个简单的基于STM32的WT588D三线控制程序示例,可以根据具体需求进行修改和优化。需要注意的是,STM32硬件资源和软件开发环境相对较为复杂,需要具备一定的STM32开发经验。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值