stm32【 1.3寸LCD屏幕(2)】

1.3寸LCD屏幕 显示图片

测试平台:STM32F103RFT6
库版本:官方标准库3.5.0版本

屏幕:中景园1.3寸24Pin接插式LCD屏幕
分辨率:240*240像素
驱动芯片:ST7789
驱动方式:4线SPI

stm32【 1.3寸LCD屏幕(1)】
上一篇文章介绍了LCD屏幕的基础配置以及实现全屏颜色显示的功能

本文会介绍一些基础的显示函数和显示图片的实现方式

注: 文中首次出现的代码块会标注[xxx.c]或[xxx.h],表明该代码是属于对应的文件,未标注的即为重复出现的

1、基础显示函数

画点和画线函数▼

[lcd.c]
/********************************************************************************
  * @brief  在指定位置画点
  * @param  (x,y)	坐标
  * @param  color	颜色
  * @retval none
  *******************************************************************************/
void LCD_Draw_Point(u16 x,u16 y,u16 color)
{
	LCD_SetRegion(x, y, x, y);
	LCD_WR_Data16(color);
}
/********************************************************************************
  * @brief  LCD画线函数,使用Bresenham算法
  * @param  (x1,y1)			起点
  * @param  (x1,y1)			终点
  * @param  PixcelColor		点颜色
  * @retval none
  *******************************************************************************/
void LCD_Draw_Line(u16 x1, u16 y1, u16 x2, u16 y2, u16 PixcelColor)
{
	u16 t; 
	int xerr = 1,yerr = 1;
	int delta_x, delta_y, distance; 
	int incx, incy, uRow, uCol; 

	delta_x = x2 - x1; //计算坐标增量
	delta_y = y2 - y1; 
	
	uRow = x1; 
	uCol = y1; 
	
	if(delta_x > 0)incx = 1;				//设置单步方向
	else if(delta_x == 0)incx = 0;			//垂直线
	else {incx = -1; delta_x = -delta_x;}
	
	if(delta_y > 0)incy = 1;
	else if(delta_y == 0)incy = 0;			//水平线
	else{incy = -1; delta_y = -delta_y;}
	
	if(delta_x > delta_y)distance = delta_x;//选取基本增量坐标轴
	else distance = delta_y; 

	for(t = 0; t <= distance; t++ ){		//画线输出

		LCD_Draw_Point(uRow,uCol,PixcelColor);//画点
		xerr += delta_x ; 
		yerr += delta_y ;
		
		if(xerr > (distance>>1)){			// 等于(distance/2)
			xerr -= distance;
			uRow += incx;
		}
		if(yerr > distance){ 
			yerr -= distance; 
			uCol += incy; 
		}
	}	
}

画线函数就不解释了,这个Bresenham算法网上有很多很详细的介绍

2、图片显示

要在屏幕上显示图片,有两个步骤
1、图片取模,生成数组或者bin文件
如果生成数组,需要创建一个 [xxx.h] 文件来存储该数组,直接烧录到单片机内部,需要时调用数组即可

如果生成bin文件,则需要直接写入外部存储空间,例如外部flash芯片或者SD卡,但是flash烧写不太方便,这里推介使用SD卡的,搭配FATFS文件操作系统的话,可以直接将bin文件通过电脑拷贝进SD卡,再读使用文件取文件,那么更换图片就很方便了。

第二种方法过于复杂,涉及到SDIO和FATFS文件系统,可以单独开两篇文章来介绍了,以后有时间再写,这里介绍第一种方式来显示图片

图片取模

驱动芯片ST7789第8.12章节的图表▼

可以看见,注意(DDRAM)这列图片,无论屏幕显示方向如何改变,数据的方向始终是【从左到右,从上到下】

在8.4.2小节里介绍到SPI模式下写入指令时提到:需要先发送高位▼

上一篇文章的【2.4章节LCD初始化】配置中,色彩模式的配置是5-6-5的RGB色彩

因此,无论使用哪款图片取模软件,取模设置都需要符合以下条件
(1)取模方向【从左到右,从上到下】
(3)数据结构【高位在前,MSB first】
(2)色彩格式【5-6-5的RGB比例】

▼这里使用【Image2lcd】这款软件,3.2版本(该版本可以批量转换图片,可以用来制作可播放的视频文件)

输出类型【C语言数组】

扫描模式【水平扫描】,上方有数据生成方向的预览图,正好符合需要的取模方向

输出灰度【16位真彩色】在上一篇文章中提到色彩配置是65K色彩

//颜色数据格式,见8.8和9.1.33章节
	LCD_WR_Cmd(0x3A);
	LCD_WR_Data8(0x05);

16位真彩色:2^16正好是65536

宽度和高度【xxx】这个就是根据需要显示的大小来填写了
不过输入(240,240)输出的图片格式并不是(240,240),而是按照宽高比例输出,在上图最底下的一栏文字,这里显示了原始图片的分辨率和输出图片的分辨率(240,230)

接下来的是一些勾选的选项:
包含图像头数据: 该选项会在颜色数据前面附加一些文件信息,关于这些信息的解读可以参考【帮助】-【用法说明】-【图像头数据结构】

根据该数据结构可以对图像信息进行解读,其中比较重要的是w和h,也就是图片的宽度和高度,在图片显示函数中会使用到
当然你也可以不包含数据头信息,只是这样显示图片的话需要手动输入图片宽高,不太方便。

字节内像素数据反序 这个没有使用过,也勾选不了,估计大概是跟计算机数据存储的大端模式和小端模式有关,在这里就不介绍了,感兴趣的可以自行了解

自右自左扫描

自底自顶扫描 这两个配合扫描模式可以改变数据扫描方向

高位在前(MSB First) 这个很重要,必须勾选,生成数据时高八位在前

在预览图的下方是色彩格式的选择,这里选【16位彩色】,颜色位数选【5-6-5】,符合第三个条件

这是我的最终配置▼

这里选择了16色真彩色
配置完成点击保存就可以生成一个C文件,这样就完成了图片的取模
打开生成的文件,可以看到有一个数组,这里截取开头部分说明下该数组应怎么使用,为后边的编程做铺垫

const unsigned char gImage_my_logo[110408] = { 0X10,0X10,0X00,0XF0,0X00,0XE6,0X01,0X1B,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,

首先可以知道的是该数组长度为【110408】,这是可以计算出来的
前面提到,实际输出的图像分辨率为(240,230)
那么240 x 230 = 5520
由于一个像素点有2byte数据,那么这张图片的数据量就是:55200 x 2 = 110400
在image2lcd软件的【帮助】中有16位真彩色信息头的结构体▼

4096/16位真彩色/18位真彩色/24位真彩色/32位真彩色”的图像数据头如下:

typedef struct _HEADCOLOR
{
   unsigned char scan;
   unsigned char gray;
   unsigned short w;
   unsigned short h;
   unsigned char is565;
   unsigned char rgb;
}HEADCOLOR; 

也就是信息头大小是8byte
那么加起来就是110400 + 8 = 110408

数组的[0]~[7]就是图片信息头▼

0X10,0X10,0X00,0XF0,0X00,0XE6,0X01,0X1B,

第3-6个是宽度和高度,都是2byte组成
宽度:0x00 F0 转换成十进制即240
高度:0x00 E6 转换成十进制即230
就是图片输出的分辨率(240,230),在编程的时候把这4byte数据解析出来就能知道图片的大小了

其他的具体含义不解释,有兴趣的可以自己去看看

显示函数

显示图片的话首先做个准备工作:
上一篇文章中设置显示区域函数,需要设置起始坐标和结束坐标,这里写一个新的,设置起始点和宽度高度,方便图片显示▼

[lcd.c]
/********************************************************************************
  * @brief  设置图片显示位置
  * @param  xy起点和区域大小(图片长宽)
  * @retval none
  *******************************************************************************/
void LCD_SetRegion_Image(u16 x, u16 y, u16 w, u16 h)
{
#if USE_HORIZONTAL == 1
	
	y = y + 80;		//Y轴中心点偏移80格

#elif USE_HORIZONTAL == 3

	x = x + 80;		//X轴中心点偏移80格
	
#endif
	
	LCD_WR_Cmd(0x2a);//列地址设置
	LCD_WR_Data16(x);
	LCD_WR_Data16(x + w - 1);
	LCD_WR_Cmd(0x2b);//行地址设置
	LCD_WR_Data16(y);
	LCD_WR_Data16(y + h - 1);
	LCD_WR_Cmd(0x2c);//储存器写
	
}

然后是根据上节提到的信息头来编写显示函数了

[lcd.c]
/*
	API使用参考
	图片数据存放在主控芯片的flash
	通过指针*p传递数据地址
	
	图片取模软件:"Image2LCD"
	注意图片取模参数,软件 “帮助” 中有图片信息头说明
	图片信息头包含:扫描的方向、灰度值、图片宽度和高度等
*/
/********************************************************************************
  * @brief	定点显示图片,注意图片取模的信息头
  * @param	(x,y)			图片起始点
            (Wide,Height)	图片分辨率(最大240x240)
            *pdata			图片取模数组 
                            取模方式:水平扫描 从左到右 高位在前
  * @retval none
  *******************************************************************************/
void Showimage_1(u32 x, u32 y, u8 *pdata)
{
	u8 *u_pdata;
	u32 i;
	
	u16 Wide;
	u16 Height;
	u32 data_cnt;
	
	u_pdata = pdata;
	
	//提取图片信息头
	Wide = (u16)u_pdata[2] << 8;		//高八位在前
	Wide += (u16)u_pdata[3];			//低八位在后
	
	Height = (u16)u_pdata[4] << 8;		//高八位在前
	Height += (u16)u_pdata[5];			//低八位在后
	
	u_pdata += 8;
	
	LCD_SetRegion_Image(x, y, Wide, Height);	//坐标设置
    
	data_cnt = Wide * Height*2;

	for(i = 0; i < data_cnt; i++){	
		LCD_WR_Data8(u_pdata[i]);  						
	}
}

没啥好解释的,获取宽度和高度信息后,设定显示区域,把指针【*u_pdata】往后移8位,然后开始刷数据就行了,注意数据量是点阵数的2倍

把生成C文件改成头文件,然后添加进工程中【#include “my_logo.h”】
在main函数里调用该函数即可显示图片

	Showimage_1(0,0,gImage_my_logo);

3、总结

这是实物展示▼

图片越大,烧写越久,而且要注意所选的单片机是否有足够的空间来存储该数组,这张图片足足需要110K,这也是开头提到的第二种方法里为啥不推荐使用外部flash来存储,烧写太麻烦了,耗时长,当然用来固化一些不再修改的Logo的话,就另当别论了

使用SD卡这样的存储介质的话,可以存储更多的图片,如果把图片一张张放映出来,能达40ms一张的播放速度,到就可以变成小电视了,以后有空再写吧

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值