一、前言


TFT屏的驱动芯片常见的有ST7789、SSD1289、ILI9341等,本项目中使用的TFT屏由ILI9341驱动芯片控制。为了提升显示效果与系统效率,对原有只使用硬件SPI驱动程序(【STM32项目】智慧鱼缸 )进行了优化,并使用DMA(直接内存访问)和硬件SPI的控制方式提升数据传输效率并降低处理器负载。

在这里插入图片描述


二、系统介绍


1. 项目实现的功能


(1)使用DMA+硬件SPI控制方式实现全屏快速刷新。

(2)通过优化的数据传输方式处理图像、字符及文字内容显示输出。


2. 项目硬件组成


(1)主控芯片:该项目使用的是STM32F103RCT6。

(2)TFT显示屏:该显示屏是2.8寸彩屏,采用4线制SPI通信方式,驱动芯片是ILI9341,分辨率为320*240。

在这里插入图片描述

在这里插入图片描述


三、项目软件开发


1.初始化操作


(1)硬件SPI的初始化:配置SPI对应使用管脚;再根据ILI9341驱动芯片要求设置SPI参数,使用8bit数据传输格式,波特率预分频器数值(用于配置发送和接收SCK时钟)应考虑系统时钟频率并满足TFT控制器的时钟要求;最后使能SPI功能模块。

//* @name       :void SPI2_Init(void)	
//* @function   :硬件SPI2初始化
void SPI2_Init(void)	
{
    SPI_InitTypeDef  SPI_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
	 
    //配置SPI2管脚    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_14;    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_Init(GPIOB, &GPIO_InitStructure);  
	
    //SPI2配置选项
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2 ,ENABLE);
	   
    SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; //单线发送模式
    SPI_InitStructure.SPI_Mode      = SPI_Mode_Master;       
    SPI_InitStructure.SPI_DataSize  = SPI_DataSize_8b;        //8bit数据
    SPI_InitStructure.SPI_CPOL       = SPI_CPOL_Low;          //传输时序:CPOL=0;(时钟相位)
    SPI_InitStructure.SPI_CPHA       = SPI_CPHA_1Edge;        //  CPHA=0;(时钟极性)
    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 = 7;
    SPI_Init(SPI2, &SPI_InitStructure);

    //使能SPI2
    SPI_Cmd(SPI2, ENABLE);   
}

(2)DMA控制器配置:配置DMA参数以适配不同使用场景。

配置参数说明:
● 数据传输方向设为从内存到外设模式。
外设地址设为不变,对于外设SPI发送的地址寄存器具有固定地址不需要自增,每次数据传输都写入或读取该地址的值。
内存地址设为递增模式,由于传输的颜色数据会发生变化。若刷新整屏或某个区域的颜色不变,则内存地址可设为不变。
数据宽度配置为8bit,与SPI接口数据格式保持一致。

//* @name :       void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
//* @function:   DMA配置
//* @parameters : DMA_CHx: DMA通道CHx
//                cpar:    外设地址
//                cmar:    存储器地址
//                cndtr:   数据传输量 
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	//使能DMA传输
	
    DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值

    DMA1_MEM_LEN=cndtr;
    DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;     //DMA外设ADC基地址
    DMA_InitStructure.DMA_MemoryBaseAddr = cmar;        //DMA内存基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //数据传输方向,从内存读取发送到外设
    DMA_InitStructure.DMA_BufferSize = cndtr;                         //DMA通道的DMA缓存的大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
    DMA_InitStructure.DMA_MemoryInc    = DMA_MemoryInc_Enable;        //内存地址寄存器递增 	
    DMA_InitStructure.DMA_PeripheralDataSize  =   DMA_PeripheralDataSize_Byte; //数据宽度为8位
    DMA_InitStructure.DMA_MemoryDataSize    =    DMA_MemoryDataSize_Byte;      //数据宽度为8位      
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;          //工作在正常缓存模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;  //DMA通道 x拥有中优先级  
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;           //DMA通道x没有设置为内存到内存传输
    DMA_Init(DMA_CHx, &DMA_InitStructure);  	  	
} 

DMA配置通道可查看STM32主控芯片的DMA控制器;外设地址设为SPI对应的数据寄存器;内存地址指向自定义的数组,由于显示屏16bitRGB颜色显示,则该数组大小设为显示屏宽度的2倍;则数据传输量也应设为显示屏宽度的2倍

在这里插入图片描述

MYDMA_Config(DMA1_Channel5,(u32)&(SPI2->DR),(u32)SendBuff,2*320);     //DMA配置

(3)TFT屏的初始化:将显示屏引脚初始化;再对显示屏的驱动芯片ILI9341初始化,可根据实际应用需求参考ILI9341指令集调整相关参数。

// * @name       :void LCD_GPIOInit(void)
// * @function   :LCD屏幕引脚初始化
void LCD_GPIOInit(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;	      
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB ,ENABLE);	        //使能GPIOB时钟
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9| GPIO_Pin_10| GPIO_Pin_11| GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   	        //推挽输出
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    LCD_CS_SET;
}

// * @name       :void LCD_Init(void)
// * @function   :LCD屏幕初始化 

u8 SendBuff[2*320];  //横屏宽度320,用于缓存一行数据
	 
void LCD_Init(void)
{  
    SPI2_Init(); 	//硬件SPI2初始化
    MYDMA_Config(DMA1_Channel5,(u32)&(SPI2->DR),(u32)SendBuff,2*lcddev.width);  //DMA配置
	
    LCD_GPIOInit();	//LCD GPIO初始化	
									 
   //****************LCD复位*************//	
    LCD_RST_CLR;
    delay_ms(10);	
    LCD_RST_SET;
    delay_ms(130);

    //*************ILI9341初始化**********//	
    LCD_CS_CLR;       //CS低电平
    delay_ms(10);	
	
    LCD_WR_REG(0xCF);  
    LCD_WR_DATA(0x00); 
    LCD_WR_DATA(0xC9);  
    LCD_WR_DATA(0X30); 
	
    LCD_WR_REG(0xED);  
    LCD_WR_DATA(0x64); 
    LCD_WR_DATA(0x03); 
    LCD_WR_DATA(0X12); 
    LCD_WR_DATA(0X81); 
	
    LCD_WR_REG(0xE8);  
    LCD_WR_DATA(0x85); 
    LCD_WR_DATA(0x10); 
    LCD_WR_DATA(0x7A); 
	
    LCD_WR_REG(0xCB);  
    LCD_WR_DATA(0x39); 
    LCD_WR_DATA(0x2C); 
    LCD_WR_DATA(0x00); 
    LCD_WR_DATA(0x34); 
    LCD_WR_DATA(0x02); 
	
    LCD_WR_REG(0xF7);  
    LCD_WR_DATA(0x20); 
	
    LCD_WR_REG(0xEA);  
    LCD_WR_DATA(0x00); 
    LCD_WR_DATA(0x00); 
	
    LCD_WR_REG(0xC0);    //Power control 
    LCD_WR_DATA(0x1B);   //VRH[5:0] 
	
    LCD_WR_REG(0xC1);    //Power control 
    LCD_WR_DATA(0x00);   //SAP[2:0];BT[3:0] 01 
	
    LCD_WR_REG(0xC5);    //VCM control 
    LCD_WR_DATA(0x30); 	 
    LCD_WR_DATA(0x30); 	 
	
    LCD_WR_REG(0xC7);    //VCM control2 
    LCD_WR_DATA(0XB7); 
	
    LCD_WR_REG(0x36);    // Memory Access Control 
    LCD_WR_DATA(0x08); 
	
    LCD_WR_REG(0x3A);   
    LCD_WR_DATA(0x55); 
	
    LCD_WR_REG(0xB1);   
    LCD_WR_DATA(0x00);   
    LCD_WR_DATA(0x1A); 
	
    LCD_WR_REG(0xB6);    // Display Function Control 
    LCD_WR_DATA(0x0A); 
    LCD_WR_DATA(0xA2);
	
    LCD_WR_REG(0xF2);    // 3Gamma Function Disable 
    LCD_WR_DATA(0x00); 
	
    LCD_WR_REG(0x26);    //Gamma curve selected 
    LCD_WR_DATA(0x01); 
	
    LCD_WR_REG(0xE0);    //Set Gamma 
    LCD_WR_DATA(0x0F); 
    LCD_WR_DATA(0x2A); 
    LCD_WR_DATA(0x28); 
    LCD_WR_DATA(0x08); 
    LCD_WR_DATA(0x0E); 
    LCD_WR_DATA(0x08); 
    LCD_WR_DATA(0x54); 
    LCD_WR_DATA(0XA9); 
    LCD_WR_DATA(0x43); 
    LCD_WR_DATA(0x0A); 
    LCD_WR_DATA(0x0F); 
    LCD_WR_DATA(0x00); 
    LCD_WR_DATA(0x00); 
    LCD_WR_DATA(0x00); 
    LCD_WR_DATA(0x00); 	
	
    LCD_WR_REG(0XE1);    //Set Gamma 
    LCD_WR_DATA(0x00); 
    LCD_WR_DATA(0x15); 
    LCD_WR_DATA(0x17); 
    LCD_WR_DATA(0x07); 
    LCD_WR_DATA(0x11); 
    LCD_WR_DATA(0x06); 
    LCD_WR_DATA(0x2B); 
    LCD_WR_DATA(0x56); 
    LCD_WR_DATA(0x3C); 
    LCD_WR_DATA(0x05); 
    LCD_WR_DATA(0x10); 
    LCD_WR_DATA(0x0F); 
    LCD_WR_DATA(0x3F); 
    LCD_WR_DATA(0x3F); 
    LCD_WR_DATA(0x0F); 
	
    LCD_WR_REG(0x2B); 
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x01);
    LCD_WR_DATA(0x3f);
	
    LCD_WR_REG(0x2A); 
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0xef);
	
    LCD_WR_REG(0x11); //Exit Sleep
    delay_ms(120);
    LCD_WR_REG(0x29); //display on		
    LCD_direction(USE_HORIZONTAL);//设置LCD显示方向
		
    LCD_LED=1;//点亮背光	
	
    LCD_Clear(WHITE);//清全屏白色
	
}

提升数据传输效率,可在显示屏复位完成后将CS片选信号脚置为低电平使能状态,此后在连续的数据或命令写入操作中无需反复切换片选信号。


2.写命令和数据函数


(1)写一个字节数据:若无需处理返回值,接收方可以根据需要选择是否接收返回数据。

// * @name       : void SPI_WriteByte(SPI_TypeDef* SPIx,u8 Byte)
// * @function   : 使用硬件SPI写入一个字节的数据
// * @parameters : SPIx: SPI类型,x表示1,2,3
//                 Byte:待写入数据
void SPI_WriteByte(SPI_TypeDef* SPIx,u8 Byte)//u8 SPI_WriteByte(SPI_TypeDef* SPIx,u8 Byte)
{
    while((SPIx->SR&SPI_I2S_FLAG_TXE)==RESET);  //等待发送缓冲为空	  
    SPIx->DR=Byte;	 	                             //发送一个byte   
    while ((SPIx->SR&SPI_I2S_FLAG_BSY));               //等待通信停止
    //return SPIx->DR;          	     //返回收到的数据		
} 

(2)写命令值:写入命令值前,需先将寄存器/数据选择信号脚设置为低电平以选择写命令指令,然后执行8bit命令值的写入操作。

// * @name        : void LCD_WR_REG(u8 data)
// * @function    : 在LCD屏幕上写入8位命令
// * @parameters  : data:要写入的命令值
void LCD_WR_REG(u8 data)
{    
    //LCD_CS_CLR;
    LCD_RS_CLR;	//GPIOB->BRR = 1<<10;//直接寄存器操作,可减少执行时间
    SPI_WriteByte(SPI2,data);
    //LCD_CS_SET;
}

(3)写数据值:写入数据值前,需先将寄存器/数据选择信号脚设置为高电平以选择写数据指令,然后执行8bit数据值的写入操作。

// * @name       : void LCD_WR_DATA(u8 data)
// * @function   : 将8位数据写入LCD屏幕
// * @parameters : data:要写入的数据值
void LCD_WR_DATA(u8 data)
{
    //LCD_CS_CLR;
    LCD_RS_SET;	//GPIOB->BSRR = 1<<10;//直接寄存器操作,可减少执行时间
    SPI_WriteByte(SPI2,data);	
    //LCD_CS_SET;
}

3.设置显示窗口函数


在向外设写入显存数据之前,必须先配置显示窗口的x轴和y轴坐标地址,以确定数据写入的具体显示位置。

// * @name       : void LCD_SetWindows(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd)
// * @function   : 设置LCD显示窗口
// * @parameters : xStar : 显示窗口的x起始坐标
//	               yStar : 显示窗口的y起始坐标
//	               xEnd : 显示窗口的x末端坐标
//	               yEnd : 显示窗口的y末端坐标
void LCD_SetWindows(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd)
{
    LCD_WR_REG(0x2A);	 
    Lcd_WriteData_16Bit(xStar);
    Lcd_WriteData_16Bit(xEnd);
	
    LCD_WR_REG(0x2B);	
    Lcd_WriteData_16Bit(yStar);
    Lcd_WriteData_16Bit(yEnd);

    LCD_WR_REG(0x2C);            //开始写入GRAM			
} 

4. DMA传输函数


DMA相关函数位于stm32f10x_dma.c库文件中,本项目采用SPI通信传输方式。需先通过SPI_I2S_DMACmd函数启用SPI2的DMA接口,然后使能DMA通道进行数据传输,最后在传输完成后清除通道传输标志位以完成一次完整的DMA传输操作。

u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度 

// * @name       : void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
// * @function   : 开启一次DMA传输
// * @parameters :DMA_CHx : DMA通道CHx
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{ 
    DMA_Cmd(DMA_CHx, DISABLE );                    //关闭 TX DMA1 所指示的通道      
    DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);  //DMA通道的DMA缓存的大小
    DMA_Cmd(DMA_CHx, ENABLE);                      //使能 TX DMA1 所指示的通道 
}	  

// * @name         : void DMA_Start(void)
// * @function     : 启动DMA传输	
void DMA_Start(void)
{
    //LCD_CS_CLR;     //关闭片选    
    LCD_RS_SET;       //写数据
    SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);    //启用SPI2 DMA通道。
    MYDMA_Enable(DMA1_Channel5);
	
    while(DMA_GetFlagStatus(DMA1_FLAG_TC5)==RESET);  //等待传输完成
    DMA_ClearFlag(DMA1_FLAG_TC5);                    //清除通道5传输完成标志
}

5. 填充函数


通过复用单行颜色数据实现批量填充,只需将一行数据(16bit颜色值)写入传送数组,然后启动传输并发送所需行数即可完成填充操作。由于传输数据方式是8bit发送,初始化配置的是先发送高位,因此当16bit颜色值按高位在前、低位在后排列时,需要将数值的高位放在前8位数值、低位放在后8位数值中进行发送。

// * @name       : void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color)
// * @function   : 填充规定的区域
// * @parameters : sx : 规定区域的x起始坐标
//                 sy : 规定区域的y起始坐标
//	               ex : 规定区域的x末端坐标
//	               ey : 规定区域的y末端坐标
//	               color : 填充的颜色数值
void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color)
{  
    u16 i,j;			

    DMA1_MEM_LEN=2*(ex-sx+1);        //一行数据长度
    LCD_SetWindows(sx,sy,ex,ey);     //设置显示窗口

    for(i=0;i<(ex-sx+1);i++)
    {				
    	SendBuff[2*i] = color>>8;   //数据高位在前
    	SendBuff[2*i+1] = color;				
    }
		
    for(j=0;j<(ey-sy+1);j++)	
    	DMA_Start();	//启动传输		
}

当显示区域设置为全屏尺寸时,可实现全屏填充颜色效果。


6. 图像显示函数


原理与填充函数相同,区别在于每行数据(16bit颜色值)不相同,只能一行一行传输完成填充预设区域。图像数组使用的时Image2Lcd取模软件生成,取模选项设置为水平扫描、不好含图像头数据、自左向右扫描、自顶向底扫描和低位在前。

// * @name        : void Gui_Drawbmp(u16 x,u16 y,u16 w,u16 h,u16 bc,const unsigned char *p) 
// * @function    : 显示16位图像
// * @parameters  : x : 图像x起始坐标
//                  y : 图像y起始坐标
//                  w : 图像显示的宽度
//                  h : 图像显示的高度
//                  bc : 图像背景的颜色数值
//                  p : 图像数组的起始地址
void Gui_Drawbmp(u16 x,u16 y,u16 w,u16 h,u16 bc,const unsigned char *p) 
{
    u16 i,j; 
    u32 k=0;
    unsigned char picH,picL; 
    DMA1_MEM_LEN=2*w;
    LCD_SetWindows(x,y,x+w-1,y+h-1);  //窗口设置
	
    for(j=0;j<h;j++)
    {
     	for(i=0;i<w;i++)
     	{
     	    picH=*(p+k+1);	  
     	    picL=*(p+k);	
			
     	    if((picL==0xff)&&(picH==0xff))  //0xffff -> 白色
     	    {
				SendBuff[2*i]     = bc>>8;  //数据高位在前
				SendBuff[2*i+1] = bc;				
     	    }			
     	    else
     	    {				
				SendBuff[2*i]     = picH;    //数据高位在前
				SendBuff[2*i+1] = picL;				
     	    }
     	    k+=2;			
     	}
     	DMA_Start();	//启动传输	
    }
}

7. 英文和数字字符显示函数


使用PCtoLCD2002取模软件生成所需字符的点阵数据并填入预定义数组中,通过获取字符数组的总个数来判断数据传输是否完成,最终完成字符数据的传输和显示填充。取模选项设置为阴码格式、逐行取模和顺向走向。

// * @name       : void LCD_ShowChar(u16 x,u16 y,u16 fc, u16 bc, u8 num,u8 size,u8 mode)
// * @function   : 显示英文和数字字符(自定义字符,非标准ASCII码)
// * @parameters : x:字符显示位置的x坐标
//                 y:字符显示位置的y坐标
//                 fc:字符的颜色数值
//                 bc:字符背景的颜色数值
//                 num:字符的第num位
//                 size:字符的尺寸
//                 mode:0非叠加(用指定的背景色填充),1叠加(保持原背景不变)
void LCD_ShowChar(u16 x,u16 y,u16 fc, u16 bc, u8 num,u8 size,u8 mode)
{  
    u8 temp,t1,t,i=0;
    u16 x0=x;
    u8 csize=(size/8+((size%8)?1:0)+1)*(size/2);   //得到字体一个字符对应点阵集所占的字节数
	
    //设置窗口		   
    DMA1_MEM_LEN=size;    						 //字体尺寸横向宽度为纵向宽度的一半
    LCD_SetWindows(x,y,x+size/2-1,y+size-1);	 //设置单个文字显示窗口

    for(t=0;t<csize;t++)
    {
    	if(size==36)
	    temp=asc2_3618[num][t];	//调用3618字体
    	else if(size==20)
	    temp=asc2_2010[num][t];	//调用1608字体
    	else if(size==24)
	    temp=asc2_2412[num][t];	//调用2412字体
    	else return;		//没有的字库
			
    	for(t1=0;t1<8;t1++)
    	{
	    	if(!mode)
	    	{
				if(temp&0x80)
				{	
				    SendBuff[2*i] = fc>>8;
				    SendBuff[2*i+1] = fc;	
				}
				else 		
				{							
				    SendBuff[2*i] = bc>>8;
				    SendBuff[2*i+1] = bc;									
				}
						
				temp<<=1;
				i++;			
				
				if(i>=(size/2))
				{
				    i=0;				
				    DMA_Start();	//启动传输	 
				    break;				
				}	
	    	}
	    	else
	    	{
				if(temp&0x80) 	LCD_Fill(x,y,x,y,fc);					
				temp <<= 1;
				x++;
				if((x-x0)>=size/2)
				{
				    x=x0;
				    y++;
				    break;
				}
	    	}
    	} 			 
    } 	
}

8. 中文字符显示函数


原理与英文和数字字符显示相同,同样使用PCtoLCD2002取模软件生成所需字符的点阵数据,取模选项设置也相同。

// * @name       : void LCD_ShowChinese(u16 x, u16 y, u16 fc, u16 bc, u8 *str,u8 size,u8 mode)
// * @function   : 显示中文字符
// * @parameters : x:中文字符x起始坐标
//                 y:中文字符y起始坐标
//                 fc:中文字符的颜色数值
//                 bc:中文字符背景的颜色数值
//                 str:中文字符的起始地址 
//                 size:中文字符
//                 mode:0非叠加,1叠加 		   
void LCD_ShowChinese(u16 x, u16 y, u16 fc, u16 bc, u8 *str,u8 size,u8 mode)
{													  	  
    while(*str!=0)//数据未结束
    {         
    	if(x>(lcddev.width-size)||y>(lcddev.height-size)) 
	    return;  
   
    	if(size==24)
	    GUI_DrawFont24(x,y,fc,bc,str,mode);	
    	else if(size==20)
	    GUI_DrawFont20(x,y,fc,bc,str,mode);	
    	else
	    GUI_DrawFont12(x,y,fc,bc,str,mode);
			
    	str+=2; 
    	x+=size;//下一个汉字偏移	          						 
    }   
}

// * @name       : void GUI_DrawFont12(u16 x, u16 y, u16 fc, u16 bc, u8 *s,u8 mode)
// * @function   : 显示12x12中文字符
// * @parameters : x:汉字的x坐标
//                 y:汉字的y坐标
//                 fc:汉字的颜色数值
//                 bc:汉字的背景颜色数值
//                 s:汉字的起始地址
//                 mode:0非叠加,1叠加
void GUI_DrawFont12(u16 x, u16 y, u16 fc, u16 bc, u8 *s,u8 mode)
{
    u8 i,j;
    u16 k;
    u16 HZnum;
    u16 y0=y;

    HZnum=sizeof(tfont12)/sizeof(typFNT_GB12);	//自动统计汉字数目	
			
    for (k=0;k<HZnum;k++) 
    {
    	if ((tfont12[k].Index[0]==*(s))&&(tfont12[k].Index[1]==*(s+1))) //判断显示字体是否存在于tfont12数组中
    	{
    	    LCD_SetWindows(x,y,x+10-1,y+10-1);
    	    for(i=0;i<12*2;i++)
    	    {
    	    	for(j=0;j<8;j++)
    	    	{
    	    	    if(!mode) //非叠加方式
    	    	    {
    	    	    	if(tfont12[k].Msk[i]&(0x80>>j))	
    	    	    	    LCD_Fill(x,y,x,y,fc);	
    	    	    	else 
    	    	    	    LCD_Fill(x,y,x,y,bc);	
    	    	    }
    	    	    else
    	    	    {						
    	    	    	if(tfont12[k].Msk[i]&(0x80>>j))	
    	    	    	    LCD_Fill(x,y,x,y,fc);							
    	    	    }
    	    	    y++;
    	    	    if((y-y0)==12)  
    	    	    {
    	    	    	y=y0;
    	    	    	x++;
    	    	    	break;
    	    	    }

    	    	}				
    	    }						
    	}				  	
    	continue;  //查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
    }
} 


四、总结


使用DMA配合硬件SPI可以显著提升数据传输速度。若希望进一步提高性能,可尝试启用16位数据传输模式,但需确认所用外设是否支持该模式。例如,在本项目中采用的TFT显示屏由ILI9341芯片驱动。该芯片支持多种MCU接口类型,包括8080系列并行接口与3线/4线串行接口。查表可知,ILI9341并不支持16位4线串行接口,因此无法通过此方式进一步提升传输效率。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述




在这里插入图片描述

关注「YouziTech」公众号 ,获取更多内容
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐