STM32学习之ILI9341控制显示屏输出(三)

对于LCD显示屏的控制,简单的讲就是通过向芯片ILI9341发送指定的命令和参数,就可以直接控制。FSMC连接好外部存储器并连接好后,可以直接通过访问地址进行读写数据。使用FSMC外接存储器时,起存储单元是映射到STM32内部存储空间的,在程序中,定义一个指向这些地址的指针,然后就可以通过指针直接修改该存储单元的内容,FSMC外设会自动完成数据访问过程,读写命令之类的操作不需要程序控制。如本实验中:

#define CMD_BASE ((u32)(0x6C000000 | 0x00001FFE))
#define DATA_BASE ((u32)(0x6C000000 | 0x00002000))

#define LCD_CMD (*(u16*)CMD_BASE)
#define LCD_DATA (*(u16*)DATA_BASE)


/*如果需要发送指令,直接像下面这样发送指令:

LCD_CMD = 0xCF   //用于控制芯片供电
 

发送数据可以像下面这样发送:
LCD_DATA = 0xFF;
*/

本实验中会用到如下几个命令(ILI9341专用命令):

0xD3:9341读取ID的命令;

0x2A:设置X坐标;

0x2B:设置Y坐标;

0x2C:存储器写命令;

步骤:

在屏幕上写英语字母的一般步骤为:读取设备ID(确认设备)、初始化、定显示区域、设置更新屏幕方向、设置背景颜色、定鼠标位置、绘制单个像素点、根据库绘制字母。

 

1、读取设备ID:

u16 ILI9341_Read_id(void)
{
 	 u16 id;
	
	 LCD_CMD=0xD3;	//9341读ID命令   
	 id=LCD_DATA;	 
	 id=LCD_DATA; 	//0x00
	 id=LCD_DATA;   //0x93								   
	 id<<=8;
	 id|=LCD_DATA;  //0x41 
	
	 return id;
}

2、初始化:

//初始化lcd
void LCD_Init(void)
{ 	
	 LCD_FSMC_Config();  //配置好FSMC就可以驱动液晶屏

   lcd_id=ILI9341_Read_id(); //先读看看所接屏幕是不是9341驱动
		
	 if(lcd_id!=0x9341)		//如果不是9341,读看看是不是1963驱动	 
		{
			LCD_CMD=(0xA1);   //1963读ID命令  
			lcd_id=LCD_DATA;
			lcd_id=LCD_DATA;	//0x57
			lcd_id<<=8;	 
			lcd_id|=LCD_DATA;	//0x61	
			if(lcd_id==0x5761)
				 lcd_id=0x1963;  //SSD1963实际读出的ID是0x5761,为了直观,这边设置为1963
		}

  if(lcd_id==0X9341) //此驱动,设置写时序为最快
	{	 							    
		FSMC_Bank1E->BWTR[6]&=~(0XF<<0); //地址建立时间清零 	 
		FSMC_Bank1E->BWTR[6]&=~(0XF<<8); //数据保存时间清零
		FSMC_Bank1E->BWTR[6]|=3<<0;		   //地址建立时间为3个HCLK =18ns  	 
		FSMC_Bank1E->BWTR[6]|=2<<8;    	 //数据保存时间为6ns*3个HCLK=18ns
	}

	if(lcd_id==0X9341)	//9341初始化
	{	 
		LCD_CMD=0xCF;//电源控制命令  
		LCD_DATA=0x00;
		LCD_DATA=0xC1; 
		LCD_DATA=0X30; 
		LCD_CMD=0xED;//系列功率控制  
		LCD_DATA=0x64; 
		LCD_DATA=0x03; 
		LCD_DATA=0X12; 
		LCD_DATA=0X81; 
		LCD_CMD=0xE8;//驱动器时序控制  
		LCD_DATA=0x85; 
		LCD_DATA=0x10; 
		LCD_DATA=0x7A; 
		LCD_CMD=0xCB;  
		LCD_DATA=0x39; 
		LCD_DATA=0x2C; 
		LCD_DATA=0x00; 
		LCD_DATA=0x34; 
		LCD_DATA=0x02; 
		LCD_CMD=0xF7;  
		LCD_DATA=0x20; 
		LCD_CMD=0xEA;  
		LCD_DATA=0x00; 
		LCD_DATA=0x00; 
		LCD_CMD=0xC0;    
		LCD_DATA=0x1B;    
		LCD_CMD=0xC1;    
		LCD_DATA=0x01;    
		LCD_CMD=0xC5;     
		LCD_DATA=0x30; 	  
		LCD_DATA=0x30; 	  
		LCD_CMD=0xC7;      
		LCD_DATA=0XB7; 
		LCD_CMD=0x36;      
		LCD_DATA=0x48; 
		LCD_CMD=0x3A;   
		LCD_DATA=0x55; 
		LCD_CMD=0xB1;   
		LCD_DATA=0x00;   
		LCD_DATA=0x1A; 
		LCD_CMD=0xB6;     
		LCD_DATA=0x0A; 
		LCD_DATA=0xA2; 
		LCD_CMD=0xF2;      
		LCD_DATA=0x00; 
		LCD_CMD=0x26;      
		LCD_DATA=0x01; 
		LCD_CMD=0xE0;      
		LCD_DATA=0x0F; 
		LCD_DATA=0x2A; 
		LCD_DATA=0x28; 
		LCD_DATA=0x08; 
		LCD_DATA=0x0E; 
		LCD_DATA=0x08; 
		LCD_DATA=0x54; 
		LCD_DATA=0XA9; 
		LCD_DATA=0x43; 
		LCD_DATA=0x0A; 
		LCD_DATA=0x0F; 
		LCD_DATA=0x00; 
		LCD_DATA=0x00; 
		LCD_DATA=0x00; 
		LCD_DATA=0x00; 		 
		LCD_CMD=0XE1;     
		LCD_DATA=0x00; 
		LCD_DATA=0x15; 
		LCD_DATA=0x17; 
		LCD_DATA=0x07; 
		LCD_DATA=0x11; 
		LCD_DATA=0x06; 
		LCD_DATA=0x2B; 
		LCD_DATA=0x56; 
		LCD_DATA=0x3C; 
		LCD_DATA=0x05; 
		LCD_DATA=0x10; 
		LCD_DATA=0x0F; 
		LCD_DATA=0x3F; 
		LCD_DATA=0x3F; 
		LCD_DATA=0x0F; 
		LCD_CMD=0x2B; 
		LCD_DATA=0x00;
		LCD_DATA=0x00;
		LCD_DATA=0x01;
		LCD_DATA=0x3f;
		LCD_CMD=0x2A; 
		LCD_DATA=0x00;
		LCD_DATA=0x00;
		LCD_DATA=0x00;
		LCD_DATA=0xef;	 
		LCD_CMD=0x11;  
		delay_ms(120);
		LCD_CMD=0x29; 
		
		LCD_BACK=1;	  //点亮背光
	}
	else if(lcd_id==0X1963)
	{
		LCD_CMD=0xE2;		
		LCD_DATA=0x1D;		
		LCD_DATA=0x02;		
		LCD_DATA=0x04;		
		delay_us(100);
		
		LCD_CMD=0xE0;		
		LCD_DATA=0x01;		
		delay_ms(10);
		
		LCD_CMD=0xE0;		
		LCD_DATA=0x03;		
		delay_ms(12); 
		
		LCD_CMD=0x01;		//软复位
		delay_ms(10);

		LCD_CMD=0xE6;		
		LCD_DATA=0x2F;
		LCD_DATA=0xFF;
		LCD_DATA=0xFF;

		LCD_CMD=0xB0;		  
		LCD_DATA=0x20;		 
		LCD_DATA=0x00;		
	
		LCD_DATA=(800-1)>>8; //LCD水平像素设置
		LCD_DATA=800-1;		 
		LCD_DATA=(480-1)>>8; //LCD垂直像素设置
		LCD_DATA=480-1;		 
		LCD_DATA=0x00;		   //RGB序列 
		
		LCD_CMD=0xB4;	
		LCD_DATA=(800+46+210-1)>>8;
		LCD_DATA=800+46+210-1;
		LCD_DATA=46>>8;
		LCD_DATA=46;
		LCD_DATA=0;
		LCD_DATA=0x00;
		LCD_DATA=0x00;
		LCD_DATA=0x00;
		
		LCD_CMD=0xB6;		 
		LCD_DATA=(480+23+22-1)>>8;
		LCD_DATA=480+23+22-1;
		LCD_DATA=23>>8;
		LCD_DATA=23;
		LCD_DATA=21;
		LCD_DATA=0x00;
		LCD_DATA=0x00;
		
		LCD_CMD=0xF0;	 
		LCD_DATA=0x03;	//SSD1963与MCU的接口为16bit  

		LCD_CMD=0x29;	//开启显示
		LCD_CMD=0xD0;	//设置自动白平衡DBC
		LCD_DATA=0x00;	
	
		LCD_CMD=0xBE;	  //PWM输出
		LCD_DATA=0x05;	//PWM频率
		LCD_DATA=0xFE;	//PWM占空比
		LCD_DATA=0x01;	
		LCD_DATA=0x00;	
		LCD_DATA=0x00;	
		LCD_DATA=0x00;	
		
		LCD_CMD=0xB8;	 
		LCD_DATA=0x03;	 
		LCD_DATA=0x01;	 
		LCD_CMD=0xBA;
		LCD_DATA=0X01;	//控制LCD方向

		Ssd1963_Set_BackLight(255);//背光设置
	}
	Set_Display_Mode(0);		 //初始化为竖屏		
	LCD_Clear(WHITE);        //清屏白色
} 

定显示区域:

void LCD_Open_Window(u16 X0,u16 Y0,u16 width,u16 height)
{   
	width=X0+width-1;    //算出右下角坐标
	height=Y0+height-1;
  
	if(dir_flag==0&&lcd_id==0X1963)  //1963竖屏处理
		{
			X0=lcd_width-width-X0; 
			height=Y0+height-1; 
			LCD_CMD=setxcmd; 
			LCD_DATA=X0>>8; 
			LCD_DATA=X0&0XFF;	 
			LCD_DATA=(X0+width-1)>>8; 
			LCD_DATA=(X0+width-1)&0XFF;  
			LCD_CMD=setycmd; 
			LCD_DATA=Y0>>8; 
			LCD_DATA=Y0&0XFF; 
			LCD_DATA=height>>8; 
			LCD_DATA=height&0XFF; 		
		}
	else
		{
			LCD_CMD=setxcmd; 
			LCD_DATA=X0>>8; 
			LCD_DATA=X0&0XFF;	 
			LCD_DATA=width>>8; 
			LCD_DATA=width&0XFF;  
			LCD_CMD=setycmd; 
			LCD_DATA=Y0>>8; 
			LCD_DATA=Y0&0XFF; 
			LCD_DATA=height>>8; 
			LCD_DATA=height&0XFF; 
		}
} 

设置更新屏幕方向:

void Set_Scan_Direction(u8 direction)
{
	u16 skhda=0;
	u16 diomf=0;
	    //9341横屏和1963竖屏时需要转化下
	if( (dir_flag==1&&lcd_id==0X9341)||(dir_flag==0&&lcd_id==0X1963)) 
	  {			   
			switch(direction)//方向转换
				{
					case 0:direction=6;break;
					case 1:direction=7;break;
					case 2:direction=4;break;
					case 3:direction=5;break;
					case 4:direction=1;break;
					case 5:direction=0;break;
					case 6:direction=3;break;
					case 7:direction=2;break;	     
				}
    }	
		
		switch(direction)
		{
			case L2R_U2D:   //从左到右,从上到下
				skhda|=(0<<7)|(0<<6)|(0<<5); 
				break;
			case L2R_D2U:   //从左到右,从下到上
				skhda|=(1<<7)|(0<<6)|(0<<5); 
				break;
			case R2L_U2D:   //从右到左,从上到下
				skhda|=(0<<7)|(1<<6)|(0<<5); 
				break;
			case R2L_D2U:   //从右到左,从下到上
				skhda|=(1<<7)|(1<<6)|(0<<5); 
				break;	 
			case U2D_L2R:   //从上到下,从左到右
				skhda|=(0<<7)|(0<<6)|(1<<5); 
				break;
			case U2D_R2L:   //从上到下,从右到左
				skhda|=(0<<7)|(1<<6)|(1<<5); 
				break;
			case D2U_L2R:   //从下到上,从左到右
				skhda|=(1<<7)|(0<<6)|(1<<5); 
				break;
			case D2U_R2L:   //从下到上,从右到左
				skhda|=(1<<7)|(1<<6)|(1<<5); 
				break;	 
		}
	  diomf=0X36;
	  if(lcd_id==0X9341)		
			 skhda|=0X08;		
	  LCD_WriteReg(diomf,skhda);		
	  LCD_Open_Window(0,0,lcd_width,lcd_height);  //设置完扫描方向后,开显示区域为全屏窗口
}

设置背景颜色:

//初始化LCD的画笔颜色和背景色	   
u16 BRUSH_COLOR=BLACK;	//画笔颜色
u16 BACK_COLOR=WHITE;   //背景色 

定鼠标位置

void LCD_SetCursor(u16 Xaddr, u16 Yaddr)
{	 
	if(lcd_id==0X9341)
		{		    
			LCD_CMD=setxcmd; 
			LCD_DATA=(Xaddr>>8); 
			LCD_DATA=(Xaddr&0XFF);	 
			LCD_CMD=setycmd; 
			LCD_DATA=(Yaddr>>8); 
			LCD_DATA=(Yaddr&0XFF);
		}
	else if(lcd_id==0X1963)
		{  			 		
			if(dir_flag==0) 
			{
				Xaddr=lcd_width-1-Xaddr;
				LCD_CMD=setxcmd; 
				LCD_DATA=0;
				LCD_DATA=0; 		
				LCD_DATA=Xaddr>>8;
				LCD_DATA=Xaddr&0XFF;		 	 
			}else
			{
				LCD_CMD=setxcmd; 
				LCD_DATA=Xaddr>>8;
				LCD_DATA=Xaddr&0XFF; 		
				LCD_DATA=(lcd_width-1)>>8;
				LCD_DATA=(lcd_width-1)&0XFF;		 	 			
			}	
			LCD_CMD=setycmd; 
			LCD_DATA=Yaddr>>8;
			LCD_DATA=Yaddr&0XFF; 		
			LCD_DATA=(lcd_height-1)>>8;
			LCD_DATA=(lcd_height-1)&0XFF; 			 		
			
		}
} 

绘制单个像素点

void LCD_DrawPoint(u16 x,u16 y)
{
    LCD_SetCursor(x,y);            //设置光标位置 
    LCD_WriteGRAM();            //开始写入GRAM
    LCD_DATA=BRUSH_COLOR; 
}

 

根据库绘制字母

void LCD_DisplayChar(u16 x,u16 y,u8 word,u8 size)
{  							  
  u8  bytenum,bytedata, a,b;
	
	u16 xmid=x;   //存储初始X值(位置)			     
	 
	if(size==12) bytenum=12;        //从字库数组中可知道每种字体单个字符所占的字节数
	else if(size==16) bytenum=16;
	else if(size==24) bytenum=48;
	else return;    //其他字体退出
	                
	word=word-' ';  //字库数组是按ASCII表排列
  //cfont.h中字库是从空格开始的 空格就是第一个元素 其他字符的ASCII码减去空格后就得到在数组中的偏移值(位置) 
	    for(b=0;b<bytenum;b++)
	    {   
					if(size==12)bytedata=char_1206[word][b]; 	 	  //调用1206字体
					else if(size==16)bytedata=char_1608[word][b];	//调用1608字体
					else if(size==24)bytedata=char_2412[word][b];	//调用2412字体

					for(a=0;a<8;a++)   
					{	   
						if(bytedata&0x01) LCD_Color_DrawPoint(x,y,BRUSH_COLOR); //由于子模是低位在前 所以先从低位判断  为1时显示画笔颜色      
						else LCD_Color_DrawPoint(x,y,BACK_COLOR);               //0时显示背景颜色		
						bytedata>>=1;    //低位判断完 依次往高位判断
						x++;	           //显示完一位 往下一位显示 
						if((x-xmid)==size/2)  //x方向超出字体大小 如:16字体 实际是 08*16的点阵  故这边 size/2
						{
							x=xmid;  //从初始X位置在写下一行
							y++;     //上一行写完 从下一行再写
							break;   //跳出for(a=0;a<8;a++)循环
						}
		      }   	 
	    }       	 	  
}   

以上代码是参照网上的,经过测试是可以直接使用的:我自己写的代码如下链接中

  • 12
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
STM32F4芯片上使用ILI9341显示汉字需要涉及到汉字的编码问题和字库的存储问题。以下是一个简单的代码示例,可以输出“张翼鹏”这个汉字。 1. 字库存储 将汉字的字库存储到外部FLASH或SD卡中,然后通过读取字库数据进行汉字的显示。这里我们假设字库数据已经存储在外部FLASH中,首先需要将字库数据读取到内存中。 ```c #define FONT_SIZE 24 // 汉字字库大小 #define FONT_ADDR 0x08040000 // 外部FLASH中存储字库的地址 uint8_t font_buf[FONT_SIZE * 2]; // 存储读取到的字库数据 void read_font(uint16_t index) { uint32_t addr = FONT_ADDR + index * FONT_SIZE * 2; for (int i = 0; i < FONT_SIZE * 2; i += 2) { uint16_t data = *(volatile uint16_t *)addr; font_buf[i] = data & 0xFF; // 取低8位 font_buf[i + 1] = data >> 8; // 取高8位 addr += 2; } } ``` 2. 汉字编码 汉字编码采用Unicode编码,每个汉字用两个字节表示。在Unicode编码中,“张翼鹏”这个汉字分别对应的编码为0x5f20、0x7ffc和0x9e1f。需要注意的是,在STM32F4芯片上使用的是小端模式,因此需要将高位字节放在前面,低位字节放在后面。 ```c uint16_t utf8_to_unicode(uint8_t *utf8_str) { uint16_t unicode = 0; if ((utf8_str[0] & 0x80) == 0x00) { // 单字节编码 unicode = utf8_str[0]; } else if ((utf8_str[0] & 0xE0) == 0xC0) { // 双字节编码 unicode = ((utf8_str[0] & 0x1F) << 6) | (utf8_str[1] & 0x3F); } else if ((utf8_str[0] & 0xF0) == 0xE0) { // 字节编码 unicode = ((utf8_str[0] & 0x0F) << 12) | ((utf8_str[1] & 0x3F) << 6) | (utf8_str[2] & 0x3F); } return unicode; } uint16_t get_unicode(uint8_t *str) { uint16_t unicode = 0; if (str[0] == 0xE5 && str[1] == 0xBC && str[2] == 0xA0) { // 张 unicode = 0x5f20; } else if (str[0] == 0xE7 && str[1] == 0xBF && str[2] == 0xBC) { // 翼 unicode = 0x7ffc; } else if (str[0] == 0xE9 && str[1] == 0xB9 && str[2] == 0x9F { // 鹏 unicode = 0x9e1f; } return unicode; } ``` 3. 汉字绘制 使用ILI9341显示屏输出汉字需要将字库数据写入到ILI9341的GRAM中,然后设置GRAM中每个像素的颜色值,最后通过SPI接口将GRAM中的数据发送给ILI9341。以下是一个简单的代码示例,可以输出“张翼鹏”这个汉字。 ```c #define LCD_WIDTH 240 // 显示屏宽度 #define LCD_HEIGHT 320 // 显示屏高度 #define LCD_CS_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_4) #define LCD_CS_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_4) #define LCD_DC_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_3) #define LCD_DC_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_3) void lcd_write_cmd(uint8_t cmd) { LCD_CS_LOW(); LCD_DC_LOW(); SPI_I2S_SendData(SPI1, cmd); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); LCD_CS_HIGH(); } void lcd_write_data(uint8_t data) { LCD_CS_LOW(); LCD_DC_HIGH(); SPI_I2S_SendData(SPI1, data); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); LCD_CS_HIGH(); } void lcd_set_pos(uint16_t x, uint16_t y) { lcd_write_cmd(0x2A); lcd_write_data(x >> 8); lcd_write_data(x & 0xFF); lcd_write_data((x + FONT_SIZE - 1) >> 8); lcd_write_data((x + FONT_SIZE - 1) & 0xFF); lcd_write_cmd(0x2B); lcd_write_data(y >> 8); lcd_write_data(y & 0xFF); lcd_write_data((y + FONT_SIZE - 1) >> 8); lcd_write_data((y + FONT_SIZE - 1) & 0xFF); lcd_write_cmd(0x2C); } void lcd_draw_char(uint16_t x, uint16_t y, uint8_t *str) { uint16_t unicode = utf8_to_unicode(str); uint16_t index = (unicode - 0x4E00) * FONT_SIZE; // 计算字库数据的偏移量 read_font(index); lcd_set_pos(x, y); for (int i = 0; i < FONT_SIZE; i++) { uint8_t data = font_buf[i]; for (int j = 0; j < 8; j++) { uint16_t color = (data & 0x80) ? 0xFFFF : 0x0000; // 根据字库数据设置颜色值 lcd_write_data(color >> 8); lcd_write_data(color & 0xFF); data <<= 1; } } } void lcd_draw_string(uint16_t x, uint16_t y, uint8_t *str) { while (*str) { uint16_t unicode = get_unicode(str); if (unicode) { lcd_draw_char(x, y, str); x += FONT_SIZE; } str += 3; } } int main(void) { // 初始化SPI和GPIO // ... lcd_write_cmd(0x36); lcd_write_data(0x48); // RGB旋转180度 lcd_write_cmd(0x3A); lcd_write_data(0x05); // 16位色深 lcd_write_cmd(0x29); // 开始显示 lcd_draw_string(0, 0, "张翼鹏"); while (1); } ``` 上述代码仅供参考,具体实现还需要根据具体情况进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值