一、LCD屏幕的介绍
TFTLCD的全称是Thin Film Transistor Liquid Crystal Display,即薄膜晶体管液晶显示器。它是一种使用薄膜晶体管作为开关器件来控制液晶像素的显示技术。TFTLCD屏幕的工作原理基于液晶分子的电光效应,通过薄膜晶体管控制液晶分子的扭曲与排列,从而实现像素的开关与亮度的调节。每个像素都由红色(R)、绿色(G)和蓝色(B)子像素组成,每个子像素都有自己的TFT。这些TFT就像开关一样,控制向每个子像素发送多少电压,当背光和滤色器一起工作时,TFT屏幕可以显示各种颜色和亮度级别。
二、LCD屏幕的特点及原理
1、LCD的特点
位置原则: 行和列来确定具体起始位置
刷屏原则: 逐行刷屏
数据原则: 一个像素点由十六位二进制控制,十六位数据就是一个像素点的颜色数据
取模软件的数据表示是否要打点,用来判断的;一位表示一个点;
如果要打点传入颜色数据
显示规则:
通过确定起始横坐标和纵坐标,结束横坐标和纵坐标来确定一个区域!
区域里有多少个像素点,就传多少次颜色!
2、LCD的显示原理
打开背光灯后,通过偏光片的折射,点亮每一个像素点,使之屏幕显示设定好的颜色
屏幕点亮的条件:
- 颜色设定
- 打开背光灯
3、LCD的显示过程
LCD显示系统
LCD显示系统的构成分成三部分:MCU、LCD显示驱动芯片、LCD屏。
CPU将要显示的数据通过地址总线和数据总线发送给LCD显示驱动器,LCD显示驱动器经过一系列处理得到三基色数据,LCD显示驱动器将三基色数据送给TFT-LCD液晶屏显示。
LCD屏幕的相关参数
分辨率:屏幕上能显示的像素点的个数,对于显示器分辨率是指显示器所能显示点数的多少,包括水平分辨率和垂直分辨率。对于TFT-LCD显示器来说,像素的数目和分辨率在数值上是相等的,都等于屏幕上横向和纵向点个数的乘积。
帧:显示屏显示一副完整的画面即为一帧。
像素:是由图像和元素两个字母组成。是构成数字图像的最小单位。若把数字图像放大数倍,就会发现数字图像其实是由许多色彩相近的小方格所组成,这些小方格点就是“像素”。
颜色位深:表示RGB颜色的二进制位数。
例:
24色位深 RGB
R G B
8 8 8 255
16位色位深
R G B
5 6 5 16位 2个字节 要想给一个像素点传入颜色,实际就是给这个像素点传2个字节数据
发送原则:先高位后低位
三、LCD屏幕的使用过程
1、LCD驱动芯片
ST7789VM驱动芯片介绍
本款LCD的驱动芯片是ST7789VM
显存:显示数据RAM的240x320x18 = 1,382,400 bits,而这款LCD显示所需的内存是240*240*16
显存,即显示内存,是驱动芯片中用于存储图像数据的存储区域。它负责将接收到的图像数据(如RGB信号)暂存起来,并根据显示控制逻辑将这些数据发送到TFT-LCD显示屏上进行显示。显存的大小和性能直接影响屏幕的分辨率、颜色深度和刷新率等关键参数。
通信接口
根据原理图可知,选用的SPI通信 4线 --- 由于不需要返回给内核,所以MISO不需要接
引脚描述
SCK:时钟引脚,用于SPI通信的时钟信号。
MOSI:主输出从输入引脚,用于SPI通信的数据输入。
CS:片选线,用于内核与屏幕的通信,片选线拉低起到断开通信的作用
RST:复位引脚,用于芯片复位操作。不接在最小系统的复位电路上的话,就需要单独接在某个IO口上,以便驱动芯片的启动,所以需要写复位程序 100ms低电平
DCX:数据/命令选择引脚,用于区分传输的是数据还是命令。在4-wire模式下,DCX信号线的低电平表示传输的是命令,而高电平则表示传输的是数据
原理图
结合芯片引脚与原理图可得,各引脚连接如下:
LEDK---------------------------------PB1 背光灯引脚 //通用输出 高电平
RESET-------------------------------PB10 复位引脚 //通用输出
LCD_CS------------------------------PB12 片选引脚 //通用输出
LCD_D/C----------------------------PB14 数据命令选择线 //通用输出
LCD_CLK----------------------------PB13 时钟线 //通用输出 ----模拟SPI
LCD_SDA----------------------------PB15 数据线 //通用输出 - ------模拟SPI
程序设计
程序中所用到的宏定义:
#define LCD_W 240
#define LCD_H 240
/* 背景色 */
#define Backgroundcolor 1
#define NoBackgroundcolor 0
/* 颜色定义开始 */
#define WHITE 0xFFFF
#define BLACK 0x0000
#define BLUE 0x001F
#define BRED 0XF81F
#define GRED 0XFFE0
#define GBLUE 0X07FF
#define RED 0xF800
#define MAGENTA 0xF81F
#define GREEN 0x07E0
#define CYAN 0x7FFF
#define YELLOW 0xFFE0
#define BROWN 0XBC40 //棕色
#define BRRED 0XFC07 //棕红色
#define GRAY 0X8430 //灰色
#define DARKBLUE 0X01CF //深蓝色
#define LIGHTBLUE 0X7D7C //浅蓝色
#define GRAYBLUE 0X5458 //灰蓝色
#define LIGHTGREEN 0X841F //浅绿色
#define LGRAY 0XC618 //浅灰色(PANNEL),窗体背景色
#define LGRAYBLUE 0XA651 //浅灰蓝色(中间层颜色)
#define LBBLUE 0X2B12 //浅棕蓝色(选择条目的反色)
/* 颜色定义结束 */
#define LCD_SPI_SCL_H GPIO_SetBits(GPIOB, GPIO_Pin_13)
#define LCD_SPI_SCL_L GPIO_ResetBits(GPIOB, GPIO_Pin_13)
#define LCD_MOSI_H GPIO_SetBits(GPIOB, GPIO_Pin_15)
#define LCD_MOSI_L GPIO_ResetBits(GPIOB, GPIO_Pin_15)
#define LCD_RES_H GPIO_SetBits(GPIOB, GPIO_Pin_10)
#define LCD_RES_L GPIO_ResetBits(GPIOB, GPIO_Pin_10)
#define LCD_CS_H GPIO_SetBits(GPIOB,GPIO_Pin_12)
#define LCD_CS_L GPIO_ResetBits(GPIOB,GPIO_Pin_12)
#define LCD_DATA GPIO_SetBits(GPIOB,GPIO_Pin_14)
#define LCD_CMD GPIO_ResetBits(GPIOB,GPIO_Pin_14)
#define LCD_LED_ON GPIO_SetBits(GPIOB,GPIO_Pin_1)
#define LCD_LED_OFF GPIO_ResetBits(GPIOB,GPIO_Pin_1)
①SPI通信相关函数
SPI所用IO口的初始化函数
/**************************************************
*函数名 :LCD_spi_IO_init
*函数功能 :模拟SPI的管脚初始函数
*函数参数 :无
*函数返回值:无
*函数描述 :0,0模式---IO口模拟配置
LCD_CLK----------------------------PB13--推挽输出
LCD_SDA----------------------------PB15--推挽输出
****************************************************/
void LCD_spi_IO_init(void)
{
/*IO口控制器配置*/
//使能端口时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
//PB13、PB15
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;//输出模式
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStruct.GPIO_Pin = (GPIO_Pin_13) | (GPIO_Pin_15) ;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;//无上下拉
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
GPIO_Init(GPIOB, &GPIO_InitStruct);
//空闲
LCD_SPI_SCL_L;
}
SPI发送一字节函数
/**************************************************
*函数名 :LCD_spi_byte
*函数功能 :SPI传输一个字节函数
*函数参数 :u8 data
*函数返回值:无
*函数描述 :传一位收一位
****************************************************/
void LCD_spi_byte(u8 data)
{
u8 i;
for(i=0;i<8;i++)
{
LCD_SPI_SCL_L;//拉低时钟线,以便主机写入数据
if(data & 0x80) LCD_MOSI_H;
else LCD_MOSI_L;
LCD_SPI_SCL_H;//主机帮从机拉高时钟线,以便从机读取数据
data = data << 1;//下一位数据
}
}
②LCD屏幕相关函数
LCD屏幕所用IO口的初始化函数
{
/*IO控制器配置*/
/*初始状态*/
//片选拉高
//背光灯关闭
}
具体程序:
/**************************************************
*函数名 :LCD_IO_init
*函数功能 :LCD的管脚初始函数
*函数参数 :无
*函数返回值:无
*函数描述 : LEDK-----PB1 背光灯引脚 //通用输出
RESET----PB10 复位引脚 //通用输出
LCD_CS---PB12 片选引脚 //通用输出
LCD_D/C--PB14 数据命令选择线 //通用输出
****************************************************/
void LCD_IO_init(void)
{
//GPIO初始化
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
//推挽输出
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_10 | GPIO_Pin_12 | GPIO_Pin_14;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStruct);
//初始状态
LCD_LED_OFF; //背光关
LCD_CS_H; //片选拉高
}
对LCD复位函数
{
//拉低复位线
//延时100ms
//拉高复位线
}
具体程序:
/**************************************************
*函数名 :st7789vm_rest
*函数功能 :对LCD复位函数
*函数参数 :无
*函数返回值:无
*函数描述 :
****************************************************/
void st7789vm_rest(void)
{
LCD_RES_L;//拉低复位线
tim5_delay_ms(100);
LCD_RES_H;//拉高复位线
}
发送命令函数(u8)
{
//片选线拉低
//DC线拉低
//调用SPI发送一字节函数
//片选线拉高
}
具体程序:
/**************************************************
*函数名 :st7789vm_write_command
*函数功能 :对LCD写命令函数
*函数参数 :u8 cmd
*函数返回值:无
*函数描述 :DCX信号线的低电平表示传输的是命令,而高电平则表示传输的是数据
****************************************************/
void st7789vm_write_command(u8 cmd)
{
LCD_CS_L;//拉低片选线
LCD_CMD;//拉低DC线-指令
LCD_spi_byte(cmd);//发送指令
LCD_CS_H;//拉高片选线
}
发送命令数据函数(u8)
{
//片选线拉低
//DC线拉高
//调用SPI发送一字节函数
//片选线拉高
}
具体程序:
/**************************************************
*函数名 :st7789vm_write_command_parameter
*函数功能 :对LCD写命令参数值函数
*函数参数 :u8 cmd
*函数返回值:无
*函数描述 :DCX信号线的低电平表示传输的是命令,而高电平则表示传输的是数据
****************************************************/
void st7789vm_write_command_parameter(u8 cmd)
{
LCD_CS_L;//拉低片选线
LCD_DATA;//拉高DC线-数据
LCD_spi_byte(cmd);//发送指令
LCD_CS_H;//拉高片选线
}
发送数据函数(u16)
{
//片选线拉低
//DC线拉高
//发送高八位--调用SPI发送一字节函数
//发送低八位--调用SPI发送一字节函数
//片选线拉高
}
具体程序:
/**************************************************
*函数名 :st7789vm_write_data
*函数功能 :对LCD写数据函数
*函数参数 :u16 data
*函数返回值:无
*函数描述 :DCX信号线的低电平表示传输的是命令,而高电平则表示传输的是数据
坐标值/颜色值
****************************************************/
void st7789vm_write_data(u16 data)
{
LCD_CS_L;//拉低片选线
LCD_DATA;//拉高DC线-数据
LCD_spi_byte(data>>8);//发送高八位
LCD_spi_byte(data);//发送低八位
LCD_CS_H;//拉高片选线
}
发送原则:先发高位再发低位
接收原则:先接高位再接低位
清屏函数
{
/*确定区域*/
//确定X轴
//发送命令
//起始横坐标
//结束横坐标
//确定X轴
//发送命令
//起始纵坐标
//结束纵坐标
/*确定颜色*/
//发送命令
//循环发送颜色数据
}
具体程序:
/**************************************************
*函数名 :LCD_clear
*函数功能 :清屏
*函数参数 :u16 color
*函数返回值:无
*函数描述 :像素的数目等于屏幕上横向和纵向点个数的乘积。
****************************************************/
void LCD_clear(u16 color)
{
/*确定区域*/
//确定起始和结束的横坐标
st7789vm_write_command(0x2A);//发送命令
st7789vm_write_data(0);//起始横坐标
st7789vm_write_data(LCD_W-1);//结束横坐标
//确定起始和结束的纵坐标
st7789vm_write_command(0x2B);//发送命令
st7789vm_write_data(0);//起始纵坐标
st7789vm_write_data(LCD_H-1);//起始纵坐标
/*确定颜色*/
st7789vm_write_command(0x2C);//发送命令
for(u32 i=0;i<LCD_W*LCD_H;i++)//逐个刷
{
st7789vm_write_data(color);
}
/*或者
//逐行刷
for(u16 i=0;i<LCD_W;i++)
{
//每行的像素点
for(u16 j=0;j<LCD_H;j++)
{
st7789vm_write_data(color);
}
}*/
}
LCD屏幕初始化
{
//SPI所用IO初始化函数
//LCD所用IO初始化函数
//复位程序
/*移植屏幕IC驱动代码*/
//开背光灯
//清屏
}
什么是IC驱动?
IC驱动,即集成电路(Integrated Circuit,简称IC)驱动,是指用于控制和驱动集成电路工作的技术或组件。在电子领域中,驱动IC扮演着至关重要的角色,它们能够接收来自处理器或其他控制源的信号,并将这些信号转换为适合驱动外部设备(如显示器、电机、传感器等)的信号
具体程序:
/**************************************************
*函数名 :LCD_init
*函数功能 :对LCD初始函数
*函数参数 :无
*函数返回值:无
*函数描述 :
****************************************************/
void LCD_init(void)
{
//SPI所用IO初始化函数
LCD_spi_IO_init();
//LCD所用IO初始化函数
LCD_IO_init();
//复位程序
st7789vm_rest();
//IC驱动代码移植
/*设置显示方向*/ //0x36是方向控制命令,0x00是参数,表示默认方向(通常是横向)
st7789vm_write_command(0x36);
st7789vm_write_command_parameter(0x00);
/*设置颜色深度*/ //0x3A是颜色格式设置命令,0x05表示16位颜色(RGB565)。
st7789vm_write_command(0x3A);
st7789vm_write_command_parameter(0x05);
/*设置电源管理相关的参数*/
st7789vm_write_command(0xB2);
st7789vm_write_command_parameter(0x0C);
st7789vm_write_command_parameter(0x0C);
st7789vm_write_command_parameter(0x00);
st7789vm_write_command_parameter(0x33);
st7789vm_write_command_parameter(0x33);
st7789vm_write_command(0xB7);
st7789vm_write_command_parameter(0x35);
/*设置VCOM(垂直方向公共电极电压)的幅度*/
st7789vm_write_command(0xBB);
st7789vm_write_command_parameter(0x19);
/*设置显示对比度*/
st7789vm_write_command(0xC0);
st7789vm_write_command_parameter(0x2C);
/*设置显示模式相关的参数*/ //如RGB接口、扫描方向等。
st7789vm_write_command(0xC2);
st7789vm_write_command_parameter(0x01);
st7789vm_write_command(0xC3);
st7789vm_write_command_parameter(0x12);
st7789vm_write_command(0xC4);
st7789vm_write_command_parameter(0x20);
st7789vm_write_command(0xC6);
st7789vm_write_command_parameter(0x0F);
/*设置显示时钟、输出频率*/
st7789vm_write_command(0xD0);
st7789vm_write_command_parameter(0xA4);
st7789vm_write_command_parameter(0xA1);
/*设置伽马校正,以优化显示效果*/
st7789vm_write_command(0xE0);
st7789vm_write_command_parameter(0xD0);
st7789vm_write_command_parameter(0x04);
st7789vm_write_command_parameter(0x0D);
st7789vm_write_command_parameter(0x11);
st7789vm_write_command_parameter(0x13);
st7789vm_write_command_parameter(0x2B);
st7789vm_write_command_parameter(0x3F);
st7789vm_write_command_parameter(0x54);
st7789vm_write_command_parameter(0x4C);
st7789vm_write_command_parameter(0x18);
st7789vm_write_command_parameter(0x0D);
st7789vm_write_command_parameter(0x0B);
st7789vm_write_command_parameter(0x1F);
st7789vm_write_command_parameter(0x23);
st7789vm_write_command(0xE1);
st7789vm_write_command_parameter(0xD0);
st7789vm_write_command_parameter(0x04);
st7789vm_write_command_parameter(0x0C);
st7789vm_write_command_parameter(0x11);
st7789vm_write_command_parameter(0x13);
st7789vm_write_command_parameter(0x2C);
st7789vm_write_command_parameter(0x3F);
st7789vm_write_command_parameter(0x44);
st7789vm_write_command_parameter(0x51);
st7789vm_write_command_parameter(0x2F);
st7789vm_write_command_parameter(0x1F);
st7789vm_write_command_parameter(0x1F);
st7789vm_write_command_parameter(0x20);
st7789vm_write_command_parameter(0x23);
/*显示启动*/
st7789vm_write_command(0x21); //退出睡眠模式
st7789vm_write_command(0x11); //进入数据模式(准备接收显示数据)
st7789vm_write_command(0x29); // 开启显示
//开背光灯
LCD_LED_ON;
//清屏
LCD_clear(YELLOW);
}
打点函数
/**************************************************
*函数名 :LCD_point
*函数功能 :打点函数
*函数参数 :u16 x,u16 y,u16 color
*函数返回值:无
*函数描述 :对于打点操作来说,我们不需要定义一个区域(即起始和结束坐标之间的间隔),
而只是需要指定一个精确的位置。
****************************************************/
void LCD_point(u16 x,u16 y,u16 color)
{
/*确定区域*/
//确定x方向
st7789vm_write_command(0x2A); //横坐标命令
//起始横坐标
st7789vm_write_data(x);
//结束横坐标
st7789vm_write_data(x);
//确定Y方向
st7789vm_write_command(0x2B); //纵坐标命令
//起始纵坐标
st7789vm_write_data(y);
//结束纵坐标
st7789vm_write_data(y);
/*确定颜色*/
st7789vm_write_command(0x2C); //颜色命令
st7789vm_write_data(color);
}
2、LCD屏幕显示
①命令介绍
确定横坐标
0x2A:设置LCD屏的X轴坐标 (起始横坐标 结束横坐标)
确定横坐标
0x2B:设置LCD屏的Y轴坐标 (起始坐标 结束坐标)
确定颜色
0x2C:设置区域中某一个点的颜色
注意:通常以图像的左上角为原点,横轴(u轴或x轴)表示像素的列数,纵轴(v轴或y轴)表示像素的行数。在这个坐标系中,每个像素点的坐标都是唯一的,并且可以用整数表示
②功能函数
基于LCD屏幕的相关底层函数可以实现以下功能
显示空心圆和实心圆
方法1:利用勾股定理
Rx=R*cos(Rad)
Ry=R*sin(Rad)
Rad=Angle*3.14/180
核心:在LCD屏幕上绘制一个圆,确定好圆心跟半径,然后从0°遍历到359°
思路:
1、圆心绘制:定点圆心
2、循环绘制圆的边缘:
函数通过一个从0到359的循环来遍历圆的每一个角度(Angle)。对于每一个角度,它执行以下步骤:
①角度转弧度:由于cos和sin函数在C语言中通常接受弧度作为参数,而不是角度,因此需要将角度转换为弧度。这通过Rad = Angle * 3.14 / 180;实现,其中3.14是π的近似值。
②计算坐标:对于当前的弧度Rad,使用cos(Rad)和sin(Rad)计算出该弧度对应的x轴和y轴上的分量(Rx和Ry),然后乘以半径r得到圆上该点的实际坐标相对于圆心的偏移量。
③绘制点:最后,使用打点函数,将计算出的点绘制在LCD屏幕上。这里,Rx+x和Ry+y是将圆的偏移量转换为屏幕上的绝对坐标。
具体程序:
/********************************************************
*函数名 :LCD_DrawCircle
*函数功能 :LCD屏幕画圆函数
*函数参数 :
x:圆心的横坐标。
y:圆心的纵坐标。
r:圆的半径。
colour:用于绘制圆的颜色
*函数返回值:无
*函数描述 :
Rx=R*cos(Rad)
Ry=R*sin(Rad)
Rad=Angle*3.14/180
***********************************************************/
void LCD_DrawCircle(u16 x,u16 y,u16 r,u16 colour)
{
float Rx,Ry,Angle,Rad;
//画圆心
LCD_point(x,y,RED);
for(Angle=0;Angle<360;Angle++)
{
//角度转弧度
Rad = Angle * 3.14 / 180;
//坐标
Rx=r * cos(Rad);
Ry=r * sin(Rad);
//画点
LCD_point(Rx+x,Ry+y,RED);
}
}
/********************************************************
*函数名 :LCD_Ring_shi
*函数功能 :LCD屏幕画圆函数实心圆
*函数参数 :x:圆心的横坐标。
y:圆心的纵坐标。
r:圆的半径。
colour:用于绘制圆的颜色
*函数返回值:无
*函数描述 :
***********************************************************/
void LCD_SolidCircle(u16 x,u16 y,u16 r,u16 colour)
{
//画圆心
LCD_point(x,y,RED);
for(u8 i=0;i<=r;i++)
{
LCD_DrawCircle(100,100,i,RED);
}
}
方法2:利用圆的标准形式
(x - x0)² + (y - y0)² = r²
核心:LCD屏幕上绘制一个圆环,确定好圆心跟半径,然后遍历横坐标和纵坐标
两个圆之间的区域:
R²<(x - x0)² + (y - y0)²< r² (R小于r)
思路:
1、遍历坐标:
函数通过两个嵌套的for循环遍历所有可能的点(i, j),这些点的坐标范围是从(x-r, y-r)到(x+r, y+r)。这个范围确保了所有可能位于圆环内的点都被检查。
2、判断点是否在圆环内:
对于每个点(i, j),函数计算它到圆环中心(x, y)的距离的平方,即(i-x)*(i-x) + (j-y)*(j-y)。
然后,这个距离的平方被用来判断点是否在圆环的范围内。圆环的范围由两个条件定义:
外边界:(i-x)*(i-x) + (j-y)*(j-y) <= r*r,即点到中心的距离不超过外半径r。
内边界:(i-x)*(i-x) + (j-y)*(j-y) >= (r-2)*(r-2),即点到中心的距离大于r-2
如果一个点同时满足这两个条件,那么它就被认为是圆环的一部分。
3、绘制点:
对于每个在圆环内的点,函数调用LCD_point(i,j,colour)来在LCD屏幕上绘制这个点,并赋予它指定的颜色。
具体程序:
/***************************************************
*函数名 :LCD_Ring
*函数功能 :LCD屏幕画圆函数
*函数参数 :u16 x, u16 y:指定了圆环中心的坐标。
u16 r:指定了圆环的外半径。
u16 colour:指定了圆环的颜色。
*函数返回值:无
*函数描述 :
(x - x0)^2 + (y - y0)^2 == r*r
*****************************************************/
void LCD_Ring(u16 x,u16 y,u16 r,u16 colour)
{
u8 i,j;
//确认长度 遍历横坐标
for(i=x-r;i<x+r;i++)
{
//确定宽度 遍历纵坐标
for(j=y-r;j<y+r;j++)
{
if((i-x)*(i-x)+(j-y)*(j-y)>=(r-2)*(r-2) && (i-x)*(i-x)+(j-y)*(j-y)<=r*r)
{
LCD_point(i,j,colour); //将遍历到的点打出来
}
}
}
}
/********************************************************
*函数名 :LCD_Ring_shi
*函数功能 :LCD屏幕画圆函数实心圆
*函数参数 :u16 x,u16 y,u16 r,u16 colour
*函数返回值:无
*函数描述 :
(x - x0)^2 + (y - y0)^2 <= r*r
***********************************************************/
void LCD_Ring_shi(u16 x,u16 y,u16 r,u16 colour)
{
u16 i,j;
for(i=x-r;i<x+r;i++) //遍历横坐标
{
for(j=y-r;j<y+r;j++) //遍历纵坐标
{
if((i-x)*(i-x) + (j-y)*(j-y) <=r*r) //判断遍历的点是否符合条件
{
//打点函数
LCD_point(i,j,colour); //将遍历到的点打出来
}
}
}
}
显示可设定大小的字符
字模软件的使用
下面的字符或者汉字取模数据全是以一维数组的形式存储的
显示一个16*16的字符函数:
按ASC码顺序取模数据存储到一维数组中
用一个变量保存用所要显示的字符减去空格字符的偏移量
一行8位需要一个字节的数据([n*16+i]),用一个变量保存每一行的数据
取模数据仅仅是用来判断是否需要打点,真正起到传输坐标和颜色的是打点函数
是否需要背景色取决于字符是否在同一个坐标
关于16*16的字符模数据:文章顶部
具体程序:
/**************************************************
*函数名 :LCD_show_ch16
*函数功能 :LCD显示16*16字符数据函数
*函数参数 :u16 x,u16 y,u8 ch,u16 colour,u8 mode,u16 b_colour
*函数返回值:无
*函数描述 : ch --- 所要显示的字符
mode ---传入1 有背景色 在同一个位置显示 背景色参数随便传
mode ---传入0 无背景色 不在同一个位置显示 背景色参数根据需求设定
****************************************************/
void LCD_show_ch16(u16 x,u16 y,u8 ch,u16 colour,u8 mode,u16 b_colour)
{
u8 n,i,j;
u8 temp;
//用所要显示的字符减去空格字符
n = ch - ' ';
//遍历行
for(i=0;i<16;i++)
{
//拿一行的数据
temp = ASC16[n*16+i];
//遍历每行的每一个像素点
for(j=0;j<8;j++)
{
//下标偏移&判断打点
if(temp & (1 << (7 - j)))
{
//判断哪个像素点是否打点
LCD_point(x+j,y+i,colour);
}
else
{
//在同一个位置显示
if(mode == 1)
//不打点的地方为背景色
LCD_point(x+j,y+i,b_colour);
}
}
}
}
显示一个32*32的字符函数:
按ASC码顺序取模数据存储到一维数组中
用一个变量保存用所要显示的字符减去空格字符的偏移量
一行16位需要两个字节的数据[(n*64+2i]<<8,[n*64+2i+1]),用一个变量保存每一行的数据
取模数据仅仅是用来判断是否需要打点,真正起到传输坐标和颜色的是打点函数
是否需要背景色取决于字符是否在同一个坐标
关于32*32的字符模数据:文章顶部
具体程序 :
/******************************************************************************
*函数名 :LCD_show_ch32
*函数功能 :LCD屏幕显示32*32的字符
*函数参数 :u16 x,u16 y,u8 ch,u16 color,u8 mode,u16 b_color
*函数返回值:无
*函数描述 :mode 传入0 无背景色 背景色参数随便传
mode 传入1 有背景色 背景色参数根据需求设定
********************************************************************************/
void LCD_show_ch32(u16 x,u16 y,u8 ch,u16 colour,u8 mode,u16 b_colour)
{
u8 n,i,j;
u16 temp;
//用所要显示的字符减去空格字符
n = ch - ' ';
//遍历行
for(i=0;i<32;i++)
{
//拿一行的数据
temp = ASC32[n*64+2*i]<<8 | ASC32[n*64+2*i+1];
//遍历每行的每一个像素点
for(j=0;j<16;j++)
{
//下标偏移&判断打点
if(temp & (1 << (15 - j)))
{
LCD_point(x+j,y+i,colour);
}
else
{
//在同一个位置显示
if(mode == 1)
//不打点的地方为背景色
LCD_point(x+j,y+i,b_colour);
}
}
}
}
优化程序:
显示一个可选择大小的字符函数
按ASC码顺序分别取模16*16和32*32的字符数据存储到一维字模数组中
用一个变量保存用所要显示的字符减去空格字符的偏移量
遍历行-->取出每一行数据
遍历每行的每一个像素点-->根据所取出每一行数据,判断打点
具体程序:
/******************************************************************************
*函数名 :LCD_show_ch
*函数功能 :LCD屏幕显示可设定大小的字符
*函数参数 :u16 x,u16 y,u16 color,u8 ch,u8 size ,u8 mode,u16 b_color
*函数返回值:无
*函数描述 :
size 字号大小 16 32
mode 传入1 就带背景颜色
mode 传入0 就不带背景颜色
********************************************************************************/
void LCD_show_ch(u16 x,u16 y,u8 ch,u16 colour,u8 size ,u8 mode,u16 b_colour)
{
u8 n,i,j;
u16 temp;
//用所要显示的字符减去空格字符
n = ch - ' ';
//遍历行
for(i=0;i<size;i++)
{
if(size == 16)
{
//拿一行的数据
temp = ASC16[n*16+i];
}
else if(size ==32)
{
//拿一行的数据
temp = ASC32[n*64+2*i]<<8 | ASC32[n*64+2*i+1];
}
//遍历每行的每一个像素点
for(j=0;j<size/2;j++)
{
//下标偏移&判断打点
if(temp & (1 << (size / 2 - 1 - j)))
{
LCD_point(x+j,y+i,colour);
}
else
{
//在同一个位置显示
if(mode == 1)
//不打点的地方为背景色
LCD_point(x+j,y+i,b_colour);
}
}
}
}
显示可设定大小的汉字
思路:
把所要显示的汉字存储到一维字库数组中
把所要显示的汉字按顺序分别取模16*16和32*32的字库数据存储到一维字模数组中
通过传入想要打印的汉字在字库中查找,计算出所要显示的汉字与字库中的第一个汉字的偏移量
需要避免所要找的汉字不是字库里的,并且如果不是字库里的,则需要退出程序
如果是字库里的,则:
遍历行-->取出每一行数据
遍历每行的每一个像素点-->根据所取出每一行数据,判断打点
注意:一个汉字占两个字节,在数组中占两个元素--->汉字的编码:区码和位码
具体程序:
/**************************************************
*函数名 :lcd_show_chinese
*函数功能 :显示中文初始函数
*函数参数 :无
*函数返回值:无
*函数描述 :size 字号大小 16 32
mode 传入1 就带背景颜色
mode 传入0 就不带背景颜色
****************************************************/
void LCD_show_chinese(u16 x, u16 y, u8*hz, u16 colour,u8 size ,u8 mode,u16 b_colour)
{
u8 n = 0;
u8 i, j;
u32 temp;
/*计算要显示的汉字与第一个汉字的偏移量*/
while(table[2*n] != '\0')//避免所要找的汉字不是汉字库里的
{
if(*hz==table[2*n] && *(hz+1)==table[2*n+1])
{
break;
}
n++;
}
//n值就是汉字个数偏移量
if(table[2*n] == '\0')
{
return ;
}
//遍历行
for(i=0;i<size;i++)
{
if(size == 16)
{
//拿一行的数据
temp = hz16[n*16+2*i]<<8 | hz16[n*16+2*i+1];
}
else if(size == 32)
{
//拿一行的数据
temp = hz32[n*128+4*i]<<24| hz32[n*128+4*i+1]<<16 | hz32[n*128+4*i+2]<<8 | hz32[n*128+4*i+3];
}
//遍历每行的每一个像素点
for(j=0;j<size;j++)
{
//下标偏移&判断打点
if(temp & (1 << (size - 1 - j)))
{
LCD_point(x+j,y+i,colour);
}
else
{
//在同一个位置显示
if(mode == 1)
//不打点的地方为背景色
LCD_point(x+j,y+i,b_colour);
}
}
}
}
显示可选大小的汉字或字符(混合)
接收字符串,通过指针偏移循环查找所显示的字符或汉字
判断字符-->避免宽度不够(换行),显示字符,偏移到下一个元素(字符占一个字节),偏移到下个像素点,调用显示可设定大小的字符函数
否则是汉字-->避免宽度不够(换行),显示汉字,偏移到下一个元素(汉字占两个字节),偏移到下个像素点,调用显示可设定大小的汉字函数
具体程序:
/******************************************************************************
*函数名 :LCD_show
*函数功能 :LCD屏幕显示可选大小的汉字和字符混合
*函数参数 :u16 x,u16 y,u16 color,u8 *str,u8 size ,u8 mode,u16 b_color
*函数返回值:无
*函数描述 :
size 字号大小 16 32
mode 传入1 就带背景颜色
mode 传入0 就不带背景颜色
********************************************************************************/
void LCD_show(u16 x, u16 y, u8*str, u16 colour,u8 size ,u8 mode,u16 b_colour)
{
while(*str != '\0')
{
/*判断是字符*/
if(32<=*str && *str<=127)
{
//以防宽度不够
if(x > LCD_W - size)
{
x = 0;//换行
y+=size;
}
/*显示字符*/
LCD_show_ch( x, y,*str,colour, size , mode, b_colour);
str++;//偏移到下一个元素
x+=size/2;//偏移到下个像素点
}
/*汉字*/
else
{
//以防宽度不够
if(x > LCD_W - size)
{
x = 0;//换行
y+=size;
}
/*显示字符*/
LCD_show_chinese( x, y,str,colour, size , mode, b_colour);
str+=2;//偏移到下一个元素
x+=size;//偏移到下个像素点
}
}
}
显示任意大小的图片
取模软件的使用
图片显示原理,同在屏幕某一区域显示颜色。
注意:图片取模数组的前8个元素代表头数据
typedef struct _HEADCOLOR
{
unsigned char scan; //扫描方向
unsigned char gray; //灰度值
unsigned short w; //宽
unsigned short h; //高
unsigned char is565; // 5 6 5
unsigned char rgb; //RGB
}HEADCOLOR;
图片的 宽和高 通过结构体成员 w ,h 成员确定。
图片的颜色数据是从 数组的 8 号元素开始。
数组中的两个数据是一个像素点的颜色
思路:
用取模软件将图片的模数据取出,再定义图片头数据的结构体,最后声明
确定区域-->定义结构体指针指向图片内的空间,得到图片的宽和高
确定颜色-->定义数据类型为u16的指针指向图片内的空间并且使用指针跳过头数据(8位), 最后通过传输颜色数据,在区域内显示图片的颜色
具体程序:
typedef struct _HEADCOLOR
{
unsigned char scan;
unsigned char gray;
unsigned short w;
unsigned short h;
unsigned char is565;
unsigned char rgb;
}HEADCOLOR;
/**********************************************
*函数名 :LCD_show_pic
*函数功能 :LCD屏幕显示任意大小图片
*函数参数 :u16 x,u16 y,const u8 *pic
*函数返回值:无
*函数描述 :x,y为起始坐标
************************************************/
void LCD_show_pic(u16 x,u16 y,const u8 *pic)
{
HEADCOLOR* head;
u16 *p;
head = (HEADCOLOR*)pic;//指向图片地址
p = (u16*)(pic + sizeof(HEADCOLOR));//指向图片地址(跳过头数据)
/*确定区域*/
//确定x方向
st7789vm_write_command(0x2A); //横坐标命令
//起始横坐标
st7789vm_write_data(x);
st7789vm_write_data(x+head->w-1);
//确定Y方向
st7789vm_write_command(0x2B); //纵坐标命令
//起始纵坐标
st7789vm_write_data(y);
st7789vm_write_data(y+head->h-1);
/*确定颜色*/
st7789vm_write_command(0x2C); //颜色命令
for(u32 i=0;i<(head->w)*(head->h);i++)
{
st7789vm_write_data(*p);//除头数据以外
p++;//地址偏移 16位-2个字节
}
}
需求:一个按键控制切换图片,模拟电子相册
利用标志位锁定和标志位解锁的方式来切换,但是图片多的时候就显得代码很冗余
所以优化程序:
利用指针数组存放每张图片的地址,用按键控制变量来寻找图片的地址空间,以实现在LCD屏幕上实现切换图片
具体程序:
//自己添加的图片
const u8 *pic[]={gImage_dog_pic,gImage_girl1_pic,gImage_girl2_pic,gImage_us_pic};
int main(void)
{
u8 keynum;
key_init();
LCD_init();
printf("硬件初始化成功\r\n");
while(1)
{
keynum = key2_scan();
if(keynum == 2)
{
num++;
LCD_show_pic(0,0,pic[num-1]);
}
}
}