前言:上一篇文章讲述在LCD中FSMC的使用,这篇文章主要是介绍lcd及其相关代码,没有看过前篇文章的请浏览一下,不然知识无法衔接。
目录:
前言:由于我经常使用CubeMX生成代码还有野火给的测试代码在移植有点问题,所以我对野火给的测试代码进行了修改,修改值后的代码没有野火给的测试代码封装好,大家可以参考一下我的修改。
一.3.2 寸 ILI9341 液晶屏
液晶面板的控制信号
1.RGB:RGB 信号线各有 8 根,红、绿、蓝颜色分量 ,RGB565 表示红绿蓝的数据线数分别为 5、6、5 根,一共为 16 个数据位可表示 2 16 种颜色,本篇文章里面是使用的RGB565。
2. 同步时钟信号 CLK:液晶屏与外部使用同步通讯方式,以 CLK 信号作为同步时钟,在同步时钟的驱动下,每个时钟传输一个像素点数据。
3.水平同步信号 HSYNC:一行像素数据的传输结束,HSYNC 会发生电平跳变。
4.垂直同步信号 VSYNC:液晶屏一帧像素数据的传输结束,每传输完成一帧像素数据时,VSYNC 会发生电平跳变。
5. 数据使能信号 DE,DE 信号线为高电平时,RGB 信号线表示的数据有效。
LCD引脚图:
LCD屏幕图:
ILI9341简介及控制器内部框图:
1.本液晶屏内部包含有一个液晶控制芯片 ILI9341,该芯片最主核心部分是位于中间的 GRAM(Graphics RAM),GRAM 中每个存储单元都对应着液晶面板的一个像素点。
2.在上面图中黄色部分为 ILI9341 的主要控制信号线和配置引脚,不同状态设置可以使芯片工作在不同的模式,可配置使用 SPI 接口、8080 接口还是 RGB接口与 MCU 进行通讯。在这里我们主要使用FSMC模拟8080控制 ILI9341与MCU 进行通讯。(学习技巧:其他没有使用到的引脚可以不用学习,这样能更容易的学习一种芯片,再别的芯片学习中也通用)
液晶屏的信号线及 8080 时序:
1.这些信号线ILI9341信号线即 FSMC模拟8080 通讯接口,带 X 的表示低电平有效。(FSMC模拟8080在上文章有讲解)
思维导图:
二.代码讲解
前言:由于我经常使用CubeMX生成代码还有野火给的测试代码在移植有点问题,所以我对野火给的测试代码进行了修改,修改值后的代码没有野火给的测试代码封装好,大家可以参考一下我的修改。
1.引脚初始化及FSMC的相关配置都在我上一篇文章里,这里将不再展示。
2.FSMC 访问数据及访问命令的地址设置:
1.使用 FSMC 访问数据及访问命令,利宏,再使用指针的形式访问其地址,直接操作ILI9341的地址内存。
...c
/***************************************************************************************
//不同地址的计算方法
2^26 =0X0400 0000 = 64MB,每个 BANK 有4*64MB = 256MB
64MB:FSMC_Bank1_NORSRAM1:0X6000 0000 ~ 0X63FF FFFF
64MB:FSMC_Bank1_NORSRAM2:0X6400 0000 ~ 0X67FF FFFF
64MB:FSMC_Bank1_NORSRAM3:0X6800 0000 ~ 0X6BFF FFFF
64MB:FSMC_Bank1_NORSRAM4:0X6C00 0000 ~ 0X6FFF FFFF选择BANK1-BORSRAM4 连接 TFT,地址范围为0X6C00 0000 ~ 0X6FFF FFFF
FSMC_A23 接LCD的DC(寄存器/数据选择)脚
寄存器基地址 = 0X6C00 0000
RAM基地址 = 0X6D00 0000 = 0X6C00 0000+2^23*2 = 0X6C00 0000 + 0X100 0000 = 0X6D00 0000
当选择不同的地址线时,地址要重新计算
****************************************************************************************//******************************* ILI9341 显示屏的 FSMC 参数定义 ***************************/
//FSMC_Bank1_NORSRAM用于LCD命令操作的地址
#define FSMC_Addr_ILI9341_CMD ( ( uint32_t ) 0x60000000 )//FSMC_Bank1_NORSRAM用于LCD数据操作的地址
#define FSMC_Addr_ILI9341_DATA ( ( uint32_t ) 0x60020000 )
3.基础配置:
1.向液晶屏发送命令,发送数据,读数据,简单延时的操作
```c
/******
*函数功能 :向ILI9341写入数据或命令
*函数名 :ILI9341_Write_CD();
*参数 :CmdData:要发送的命令或数据
CD:1时CmdData是数据
0时CmdData是命令
*返回值 :无
******/void ILI9341_Write_CD(uint16_t CmdData,uint8_t CD ) { if(CD==1) { //发数据 *(uint16_t*) (FSMC_Addr_ILI9341_DATA)=CmdData; } else { //发命令 *(uint16_t*) (FSMC_Addr_ILI9341_CMD)=CmdData; } }
/******
*函数功能 :从ILI9341读取数据
*函数名 :ILI9341_Read_Data();
*参数 :无
*返回值 :读取到的数据
******/void ILI9341_Write_CD(uint16_t CmdData,uint8_t CD ) { if(CD==1) { //发数据 *(uint16_t*) (FSMC_Addr_ILI9341_DATA)=CmdData; } else { //发命令 *(uint16_t*) (FSMC_Addr_ILI9341_CMD)=CmdData; } }
4.初始化相关寄存器:
4.利用上面的发送命令及数据操作,可以向液晶屏写入一些初始化配置(这些初始化ILI9341寄存器的代码,生产ILI9341厂家会提供,不要太纠结里面的内容,直接用就行了)
/******
*函数功能 :初始化ILI9341寄存器
*函数名 :ILI9341_Delay();
*参数 :无
*返回值 :无
******/static void ILI9341_REG_Config ( void ) { /* Power control B (CFh) */ DEBUG_DELAY (); ILI9341_Write_CD ( 0xCF,0 ); ILI9341_Write_CD ( 0x00,1 ); ILI9341_Write_CD ( 0x81,1 ); ILI9341_Write_CD ( 0x30,1 ); /* Power on sequence control (EDh) */ DEBUG_DELAY (); ILI9341_Write_CD ( 0xED ,0); ILI9341_Write_CD ( 0x64 ,1); ILI9341_Write_CD ( 0x03 ,1); ILI9341_Write_CD ( 0x12 ,1); ILI9341_Write_CD ( 0x81 ,1); /* Driver timing control A (E8h) */ DEBUG_DELAY (); ILI9341_Write_CD ( 0xE8,0); ILI9341_Write_CD ( 0x85,1); ILI9341_Write_CD ( 0x10,1 ); ILI9341_Write_CD ( 0x78 ,1); /* Power control A (CBh) */ DEBUG_DELAY (); ILI9341_Write_CD ( 0xCB,0 ); ILI9341_Write_CD ( 0x39,1 ); ILI9341_Write_CD ( 0x2C ,1); ILI9341_Write_CD ( 0x00,1 ); ILI9341_Write_CD ( 0x34 ,1); ILI9341_Write_CD ( 0x02,1 ); /* Pump ratio control (F7h) */ DEBUG_DELAY (); ILI9341_Write_CD ( 0xF7,0 ); ILI9341_Write_CD ( 0x20,1 ); /* Driver timing control B */ DEBUG_DELAY (); ILI9341_Write_CD ( 0xEA,0 ); ILI9341_Write_CD ( 0x00,1 ); ILI9341_Write_CD ( 0x00 ,1); /* Frame Rate Control (In Normal Mode/Full Colors) (B1h) */ DEBUG_DELAY (); ILI9341_Write_CD ( 0xB1,0 ); ILI9341_Write_CD ( 0x00,1 ); ILI9341_Write_CD ( 0x1B ,1); /* Display Function Control (B6h) */ DEBUG_DELAY (); ILI9341_Write_CD ( 0xB6,0 ); ILI9341_Write_CD ( 0x0A,1 ); ILI9341_Write_CD ( 0xA2,1 ); /* Power Control 1 (C0h) */ DEBUG_DELAY (); ILI9341_Write_CD ( 0xC0,0 ); ILI9341_Write_CD ( 0x35,1 ); /* Power Control 2 (C1h) */ DEBUG_DELAY (); ILI9341_Write_CD ( 0xC1,0 ); ILI9341_Write_CD ( 0x11,1 ); /* VCOM Control 1 (C5h) */ ILI9341_Write_CD ( 0xC5,0 ); ILI9341_Write_CD ( 0x45,1 ); ILI9341_Write_CD ( 0x45,1 ); /* VCOM Control 2 (C7h) */ ILI9341_Write_CD ( 0xC7,0 ); ILI9341_Write_CD ( 0xA2,1 ); /* Enable 3G (F2h) */ ILI9341_Write_CD ( 0xF2,0 ); ILI9341_Write_CD ( 0x00,1 ); /* Gamma Set (26h) */ ILI9341_Write_CD ( 0x26,0 ); ILI9341_Write_CD ( 0x01,1 ); DEBUG_DELAY (); /* Positive Gamma Correction */ ILI9341_Write_CD ( 0xE0,0 ); //Set Gamma ILI9341_Write_CD ( 0x0F,1 ); ILI9341_Write_CD ( 0x26,1 ); ILI9341_Write_CD ( 0x24,1 ); ILI9341_Write_CD ( 0x0B,1 ); ILI9341_Write_CD ( 0x0E,1 ); ILI9341_Write_CD ( 0x09,1 ); ILI9341_Write_CD ( 0x54,1 ); ILI9341_Write_CD ( 0xA8,1 ); ILI9341_Write_CD ( 0x46,1 ); ILI9341_Write_CD ( 0x0C,1 ); ILI9341_Write_CD ( 0x17,1 ); ILI9341_Write_CD ( 0x09,1 ); ILI9341_Write_CD ( 0x0F,1 ); ILI9341_Write_CD ( 0x07,1 ); ILI9341_Write_CD ( 0x00,1 ); /* Negative Gamma Correction (E1h) */ ILI9341_Write_CD ( 0XE1,0 ); //Set Gamma ILI9341_Write_CD ( 0x00,1 ); ILI9341_Write_CD ( 0x19,1 ); ILI9341_Write_CD ( 0x1B,1 ); ILI9341_Write_CD ( 0x04,1 ); ILI9341_Write_CD ( 0x10,1 ); ILI9341_Write_CD ( 0x07,1 ); ILI9341_Write_CD ( 0x2A,1 ); ILI9341_Write_CD ( 0x47,1 ); ILI9341_Write_CD ( 0x39,1 ); ILI9341_Write_CD ( 0x03,1 ); ILI9341_Write_CD ( 0x06,1 ); ILI9341_Write_CD ( 0x06,1 ); ILI9341_Write_CD ( 0x30,1 ); ILI9341_Write_CD ( 0x38,1 ); ILI9341_Write_CD ( 0x0F,1 ); /* memory access control set */ DEBUG_DELAY (); ILI9341_Write_CD ( 0x36,0); ILI9341_Write_CD ( 0xC8,1 ); /*竖屏 左上角到 (起点)到右下角 (终点)扫描方式*/ DEBUG_DELAY (); /* column address control set */ ILI9341_Write_CD ( CMD_SetCoordinateX,0 ); ILI9341_Write_CD ( 0x00,1 ); ILI9341_Write_CD ( 0x00,1 ); ILI9341_Write_CD ( 0x00,1 ); ILI9341_Write_CD ( 0xEF,1 ); /* page address control set */ DEBUG_DELAY (); ILI9341_Write_CD ( CMD_SetCoordinateY,0 ); ILI9341_Write_CD ( 0x00,1 ); ILI9341_Write_CD ( 0x00,1 ); ILI9341_Write_CD ( 0x01,1 ); ILI9341_Write_CD ( 0x3F,1 ); /* Pixel Format Set (3Ah) */ DEBUG_DELAY (); ILI9341_Write_CD ( 0x3a,0 ); ILI9341_Write_CD ( 0x55,1 ); /* Sleep Out (11h) */ ILI9341_Write_CD ( 0x11,0 ); ILI9341_Delay ( 0xAFFf<<2 ); DEBUG_DELAY (); /* Display ON (29h) */ ILI9341_Write_CD ( 0x29,0); }
5.ILI9341初始化:
1.ILI9341初始化及其相关函数:
/******
*函数功能 :ILI9341初始化函数,如果要用到lcd,一定要调用这个函数
*函数名 :ILI9341_Init();
*参数 :无
*返回值 :无
******/void ILI9341_Init ( void ) { MX_FSMC_Init(); ILI9341_BackLed_Control ( 0 ); //点亮LCD背光灯 ILI9341_Rst (); //ILI9341G 软件复位 ILI9341_REG_Config (); //初始化ILI9341寄存器 //设置默认扫描方向,其中 6 模式为大部分液晶例程的默认显示方向 ILI9341_GramScan(LCD_SCAN_MODE); }
/******
*函数功能 :ILI9341G背光LED控制
*函数名 :ILI9341_BackLed_Control();
*参数 :HL:1熄灭背光
0点亮背光
*返回值 :无
******/void ILI9341_BackLed_Control ( uint8_t HL ) { if ( HL==1) //背光灭 { HAL_GPIO_WritePin( GPIOD, GPIO_PIN_12,GPIO_PIN_SET); } else //背光亮 { HAL_GPIO_WritePin( GPIOD, GPIO_PIN_12,GPIO_PIN_RESET); } }
/******
*函数功能 :ILI9341G 软件复位
*函数名 :ILI9341_BackLed_Control();
*参数 :HL:1熄灭背光
0点亮背光
*返回值 :无
******/void ILI9341_Rst( void ) { HAL_GPIO_WritePin( GPIOE, GPIO_PIN_1,GPIO_PIN_RESET); //低电平复位 ILI9341_Delay ( 0xAFF ); HAL_GPIO_WritePin( GPIOE, GPIO_PIN_1,GPIO_PIN_SET); ILI9341_Delay ( 0xAFF ); }
/**
* @brief 设置ILI9341的GRAM的扫描方向
* @param ucOption :选择GRAM的扫描方向
* @arg 0-7 :参数可选值为0-7这八个方向
*
* !!!其中0、3、5、6 模式适合从左至右显示文字,
* 不推荐使用其它模式显示文字 其它模式显示文字会有镜像效果
*
* 其中0、2、4、6 模式的X方向像素为240,Y方向像素为320
* 其中1、3、5、7 模式下X方向像素为320,Y方向像素为240
*
* 其中 6 模式为大部分液晶例程的默认显示方向
* 其中 3 模式为摄像头例程使用的方向
* 其中 0 模式为BMP图片显示例程使用的方向
*
* @retval 无
* @note 坐标图例:A表示向上,V表示向下,<表示向左,>表示向右
X表示X轴,Y表示Y轴------------------------------------------------------------
模式0: . 模式1: . 模式2: . 模式3:
A . A . A . A
| . | . | . |
Y . X . Y . X
0 . 1 . 2 . 3
<--- X0 o . <----Y1 o . o 2X---> . o 3Y--->
------------------------------------------------------------
模式4: . 模式5: . 模式6: . 模式7:
<--- X4 o . <--- Y5 o . o 6X---> . o 7Y--->
4 . 5 . 6 . 7
Y . X . Y . X
| . | . | . |
V . V . V . V
---------------------------------------------------------
LCD屏示例
|-----------------|
| 野火Logo |
| |
| |
| |
| |
| |
| |
| |
| |
|-----------------|
屏幕正面(宽240,高320)*******************************************************/
void ILI9341_GramScan ( uint8_t ucOption ) { //参数检查,只可输入0-7 if(ucOption >7 ) return; //根据模式更新LCD_SCAN_MODE的值,主要用于触摸屏选择计算参数 LCD_SCAN_MODE = ucOption; //根据模式更新XY方向的像素宽度 if(ucOption%2 == 0) { //0 2 4 6模式下X方向像素宽度为240,Y方向为320 LCD_X_LENGTH = ILI9341_LESS_PIXEL; LCD_Y_LENGTH = ILI9341_MORE_PIXEL; } else { //1 3 5 7模式下X方向像素宽度为320,Y方向为240 LCD_X_LENGTH = ILI9341_MORE_PIXEL; LCD_Y_LENGTH = ILI9341_LESS_PIXEL; } //0x36命令参数的高3位可用于设置GRAM扫描方向 ILI9341_Write_CD ( 0x36,0 ); ILI9341_Write_CD ( 0x08 |(ucOption<<5),1);//根据ucOption的值设置LCD参数,共0-7种模式 ILI9341_Write_CD ( CMD_SetCoordinateX ,0); ILI9341_Write_CD ( 0x00,1 ); /* x 起始坐标高8位 */ ILI9341_Write_CD ( 0x00,1 ); /* x 起始坐标低8位 */ ILI9341_Write_CD ( ((LCD_X_LENGTH-1)>>8)&0xFF,1 ); /* x 结束坐标高8位 */ ILI9341_Write_CD ( (LCD_X_LENGTH-1)&0xFF,1 ); /* x 结束坐标低8位 */ ILI9341_Write_CD ( CMD_SetCoordinateY,0 ); ILI9341_Write_CD ( 0x00,1 ); /* y 起始坐标高8位 */ ILI9341_Write_CD ( 0x00,1 ); /* y 起始坐标低8位 */ ILI9341_Write_CD ( ((LCD_Y_LENGTH-1)>>8)&0xFF,1 ); /* y 结束坐标高8位 */ ILI9341_Write_CD ( (LCD_Y_LENGTH-1)&0xFF ,1); /* y 结束坐标低8位 */ /* write gram start */ ILI9341_Write_CD ( CMD_SetPixel,0 ); }
6.开辟一个窗口:
/******
*函数功能 :在ILI9341显示器上开辟一个窗口
*函数名 :ILI9341_OpenWindow();
*参数 :usX 在特定扫描方向下窗口的起点X坐标
usY 在特定扫描方向下窗口的起点Y坐标
usWidth 窗口的宽度
usHeight 窗口的高度
*返回值 :无
******/void ILI9341_OpenWindow ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight ) { ILI9341_Write_CD ( CMD_SetCoordinateX,0 ); /* 设置X坐标 */ ILI9341_Write_CD ( usX >> 8 ,1 ); /* 先高8位,然后低8位 */ ILI9341_Write_CD ( usX & 0xff,1 ); /* 设置起始点和结束点*/ ILI9341_Write_CD ( ( usX + usWidth-1 ) >> 8,1); ILI9341_Write_CD ( ( usX + usWidth - 1 ) & 0xff,1); ILI9341_Write_CD ( CMD_SetCoordinateY,0 ); /* 设置Y坐标*/ ILI9341_Write_CD ( usY >> 8,1 ); ILI9341_Write_CD ( usY & 0xff,1 ); ILI9341_Write_CD ( ( usY + usHeight - 1 ) >> 8,1 ); ILI9341_Write_CD ( ( usY + usHeight - 1) & 0xff,1 ); }
7. 设定光标坐标:
/******
*函数功能 :设定ILI9341的光标坐标
*函数名 :ILI9341_SetCursor();
*参数 :usX 在特定扫描方向下窗口的起点X坐标
usY 在特定扫描方向下窗口的起点Y坐标
*返回值 :无
******/static void ILI9341_SetCursor ( uint16_t usX, uint16_t usY ) { ILI9341_OpenWindow ( usX, usY, 1, 1 ); //在ILI9341显示器上开辟一个窗口 }
8. 以某一颜色填充像素点:
/******
*函数功能 :在ILI9341显示器上以某一颜色填充像素点
*函数名 :ILI9341_FillColor();
*参数 :ulAmout_Point 要填充颜色的像素点的总数目
usY 在特定扫描方向下窗口的起点Y坐标
*返回值 :无
******/static __inline void ILI9341_FillColor ( uint32_t ulAmout_Point, uint16_t usColor ) { uint32_t i = 0; /* memory write */ ILI9341_Write_CD ( CMD_SetPixel,0); for ( i = 0; i < ulAmout_Point; i ++ ) ILI9341_Write_CD ( usColor ,1); }
9. 对某一点填充:
/******
*函数功能 : 对ILI9341显示器的某一点以某种颜色进行填充
*函数名 :ILI9341_SetPointPixel();
*参数 :usX :在特定扫描方向下窗口的起点X坐标
usY :在特定扫描方向下该点的Y坐标*返回值 :无
******/void ILI9341_SetPointPixel ( uint16_t usX, uint16_t usY ) { if ( ( usX < LCD_X_LENGTH ) && ( usY < LCD_Y_LENGTH ) ) { ILI9341_SetCursor ( usX, usY ); ILI9341_FillColor ( 1, CurrentTextColor ); } }
/******
*函数功能 : 设置LCD的前景(字体)及背景颜色,RGB565
*函数名 :ILI9341_Delay();
*参数 :TextColor: 指定前景(字体)颜色
BackColor: 指定背景颜色
*返回值 :无;
******/void LCD_SetColors(uint16_t TextColor, uint16_t BackColor) { CurrentTextColor = TextColor; CurrentBackColor = BackColor; }
10. 某一窗口以某种颜色进行清屏:
/******
*函数功能 :对ILI9341显示器的某一窗口以某种颜色进行清屏
*函数名 :ILI9341_FillColor();
*参数 :usX :在特定扫描方向下窗口的起点X坐标
usY :在特定扫描方向下窗口的起点Y坐标
usWidth :窗口的宽度
usHeight :窗口的高度
*返回值 :无
******/void ILI9341_Clear ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight ) { ILI9341_OpenWindow ( usX, usY, usWidth, usHeight ); ILI9341_FillColor ( usWidth * usHeight, CurrentBackColor ); }
11. 读取GRAN 的一个像素数据:
/******
*函数功能 :读取ILI9341 GRAN 的一个像素数据
*函数名 :IILI9341_Read_PixelData();
*参数 :无
*返回值 :像素数据
******/static uint16_t ILI9341_Read_PixelData ( void ) { uint16_t us_RG=0, usB=0 ; ILI9341_Write_CD ( 0x2E00 ,0); /* 读数据 */ //前读取三次结果去掉 us_RG = ILI9341_Read_Data (); /*FIRST READ OUT DUMMY DATA*/ us_RG = ILI9341_Read_Data (); /*FIRST READ OUT DUMMY DATA*/ us_RG = ILI9341_Read_Data (); /*FIRST READ OUT DUMMY DATA*/ //获取红色通道与绿色通道的值 us_RG = ILI9341_Read_Data (); /*READ OUT RED AND GREEN DATA */ usB = ILI9341_Read_Data (); /*READ OUT BLUE DATA*/ return (us_RG&0xF800)| ((us_RG<<3)&0x7E0) | (usB>>11) ; }
12. 获取 某一个坐标点的像素数据:
/******
*函数功能 :获取 ILI9341 显示器上某一个坐标点的像素数据
*函数名 :ILI9341_GetPointPixel();
*参数 :usX :在特定扫描方向下该点的X坐标
usY :在特定扫描方向下该点的Y坐标
*返回值 :像素数据
******/uint16_t ILI9341_GetPointPixel ( uint16_t usX, uint16_t usY ) { uint16_t usPixelData; ILI9341_SetCursor ( usX, usY ); //设定光标坐标 usPixelData = ILI9341_Read_PixelData (); //读取一个像素数据 return usPixelData; }
13 .算法画线段:
/******
*函数功能 :在 ILI9341 显示器上使用 Bresenham 算法画线段
*函数名 :ILI9341_DrawLine();
*参数 :usX1 :在特定扫描方向下线段的一个端点X坐标
usY1 :在特定扫描方向下线段的一个端点Y坐标
usX2 :在特定扫描方向下线段的另一个端点X坐标
usY2 :在特定扫描方向下线段的另一个端点Y坐标*返回值 :无
******/
void ILI9341_DrawLine ( uint16_t usX1, uint16_t usY1, uint16_t usX2, uint16_t usY2 ) { uint16_t us; uint16_t usX_Current, usY_Current; int32_t lError_X = 0, lError_Y = 0, lDeTa_X , lDeTa_Y, lDistance; int32_t lIncrease_X, lIncrease_Y; lDeTa_X = usX2 - usX1; //计算坐标增量 lDeTa_Y = usY2 - usY1; usX_Current = usX1; //初始坐标 usY_Current = usY1; if ( lDeTa_X > 0 ) lIncrease_X = 1; //设置单步方向 else if ( lDeTa_X == 0 ) lIncrease_X = 0; //垂直线 else { lIncrease_X = -1; lDeTa_X = - lDeTa_X; } if ( lDeTa_Y > 0 ) lIncrease_Y = 1; else if ( lDeTa_Y == 0 ) lIncrease_Y = 0;//水平线 else { lIncrease_Y = -1; lDeTa_Y = - lDeTa_Y; } if ( lDeTa_X > lDeTa_Y ) lDistance = lDeTa_X; //选取基本增量坐标轴 else lDistance = lDeTa_Y; for ( us = 0; us <= lDistance + 1; us ++ )//画线输出 { ILI9341_SetPointPixel ( usX_Current, usY_Current );//画点 lError_X += lDeTa_X ; lError_Y += lDeTa_Y ; if ( lError_X > lDistance ) { lError_X -= lDistance; usX_Current += lIncrease_X; } if ( lError_Y > lDistance ) { lError_Y -= lDistance; usY_Current += lIncrease_Y; } } }
14. 画一个矩形:
/******
*函数功能 :在 ILI9341 显示器上画一个矩形
*函数名 :ILI9341_GetPointPixel();
*参数 :usX_Start :在特定扫描方向下矩形的起始点X坐标
usY_Start :在特定扫描方向下矩形的起始点Y坐标
usWidth:矩形的宽度(单位:像素)
usHeight:矩形的高度(单位:像素)
ucFilled :选择是否填充该矩形 * 0 :空心矩形
1 :实心矩形 *返回值 :无
******/void ILI9341_DrawRectangle ( uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight, uint8_t ucFilled ) { if ( ucFilled ) { ILI9341_OpenWindow ( usX_Start, usY_Start, usWidth, usHeight ); ILI9341_FillColor ( usWidth * usHeight ,CurrentTextColor); } else { ILI9341_DrawLine ( usX_Start, usY_Start, usX_Start + usWidth - 1, usY_Start ); ILI9341_DrawLine ( usX_Start, usY_Start + usHeight - 1, usX_Start + usWidth - 1, usY_Start + usHeight - 1 ); ILI9341_DrawLine ( usX_Start, usY_Start, usX_Start, usY_Start + usHeight - 1 ); ILI9341_DrawLine ( usX_Start + usWidth - 1, usY_Start, usX_Start + usWidth - 1, usY_Start + usHeight - 1 ); } }
15.算法画圆:
/******
*函数功能 :在 ILI9341 显示器上使用 Bresenham 算法画圆
*函数名 :ILI9341_DrawCircle();
*参数 : usX_Center :在特定扫描方向下圆心的X坐标
* usY_Center :在特定扫描方向下圆心的Y坐标
usRadius:圆的半径(单位:像素)
* ucFilled :选择是否填充该圆 0 :空心圆
* 1 :实心圆
*
*返回值 :无
******/void ILI9341_DrawCircle ( uint16_t usX_Center, uint16_t usY_Center, uint16_t usRadius, uint8_t ucFilled ) { int16_t sCurrentX, sCurrentY; int16_t sError; sCurrentX = 0; sCurrentY = usRadius; sError = 3 - ( usRadius << 1 ); //判断下个点位置的标志 while ( sCurrentX <= sCurrentY ) { int16_t sCountY; if ( ucFilled ) for ( sCountY = sCurrentX; sCountY <= sCurrentY; sCountY ++ ) { ILI9341_SetPointPixel ( usX_Center + sCurrentX, usY_Center + sCountY ); //1,研究对象 ILI9341_SetPointPixel ( usX_Center - sCurrentX, usY_Center + sCountY ); //2 ILI9341_SetPointPixel ( usX_Center - sCountY, usY_Center + sCurrentX ); //3 ILI9341_SetPointPixel ( usX_Center - sCountY, usY_Center - sCurrentX ); //4 ILI9341_SetPointPixel ( usX_Center - sCurrentX, usY_Center - sCountY ); //5 ILI9341_SetPointPixel ( usX_Center + sCurrentX, usY_Center - sCountY ); //6 ILI9341_SetPointPixel ( usX_Center + sCountY, usY_Center - sCurrentX ); //7 ILI9341_SetPointPixel ( usX_Center + sCountY, usY_Center + sCurrentX ); //0 } else { ILI9341_SetPointPixel ( usX_Center + sCurrentX, usY_Center + sCurrentY ); //1,研究对象 ILI9341_SetPointPixel ( usX_Center - sCurrentX, usY_Center + sCurrentY ); //2 ILI9341_SetPointPixel ( usX_Center - sCurrentY, usY_Center + sCurrentX ); //3 ILI9341_SetPointPixel ( usX_Center - sCurrentY, usY_Center - sCurrentX ); //4 ILI9341_SetPointPixel ( usX_Center - sCurrentX, usY_Center - sCurrentY ); //5 ILI9341_SetPointPixel ( usX_Center + sCurrentX, usY_Center - sCurrentY ); //6 ILI9341_SetPointPixel ( usX_Center + sCurrentY, usY_Center - sCurrentX ); //7 ILI9341_SetPointPixel ( usX_Center + sCurrentY, usY_Center + sCurrentX ); //0 } sCurrentX ++; if ( sError < 0 ) sError += 4 * sCurrentX + 6; else { sError += 10 + 4 * ( sCurrentX - sCurrentY ); sCurrentY --; } } }
实验及其现象一
1.画线实验代码:
while (1) { LCD_SetColors(BLACK,WHITE); ILI9341_DrawLine(10,10,100,200); LCD_SetColors(WHITE,BLACK); ILI9341_DrawLine(100,0,100,200); LCD_SetColors(BLUE,RED); ILI9341_DrawLine(240,0,100,200); LCD_SetColors(RED,BLUE); ILI9341_DrawLine(0,200,100,200); }
2.画矩形实验代码:
while (1) { LCD_SetColors(RED,WHITE); ILI9341_DrawRectangle(10,10,50,50,1);//内部填充 LCD_SetColors(BLUE,WHITE); ILI9341_DrawRectangle(100,100,50,50,0);//内部不填充 }
2.画圆实验代码:
while (1) { LCD_SetColors(RED,WHITE); ILI9341_DrawCircle(100,150,50,0); LCD_SetColors(BLUE ,WHITE); ILI9341_DrawCircle(100,150,10,1); LCD_SetColors(GREEN ,WHITE); ILI9341_DrawCircle(60,105,10,1); LCD_SetColors(GREEN ,WHITE); ILI9341_DrawCircle(130,100,10,1); }
实验现象
16.设置,获取英文字体类型:
/******
*函数功能 :设置英文字体类型
*函数名 :LCD_SetFont();
*参数 :fonts: 指定要选择的字体参数为以下值之一
Font24x32;
Font16x24;
Font8x16;
*返回值 :无
******/void LCD_SetFont(sFONT *fonts) { LCD_Currentfonts = fonts; }
/******
*函数功能 :获取当前字体类型
*函数名 :LCD_SetFont();
*参数 :无
*返回值 :无
******/sFONT *LCD_GetFont(void) { return LCD_Currentfonts; }
17.显示一个英文字符:
/******
*函数功能 :在 ILI9341 显示器上显示一个英文字符
*函数名 :ILI9341_ShowChar_EN();
*参数 : usX :在特定扫描方向下字符的起始X坐标
* usY :在特定扫描方向下该点的起始Y坐标
cChar :要显示的英文字符
*
*返回值 :无
******/void ILI9341_ShowChar_EN ( uint16_t usX, uint16_t usY, const char cChar ) { uint8_t byteCount, bitCount,fontLength; uint16_t ucRelativePositon; uint8_t *Pfont; //对ascii码表偏移(字模表不包含ASCII表的前32个非图形符号) ucRelativePositon = cChar - ' '; //每个字模的字节数 fontLength = (LCD_Currentfonts->Width*LCD_Currentfonts->Height)/8; //字模首地址 /*ascii码表偏移值乘以每个字模的字节数,求出字模的偏移位置*/ Pfont = (uint8_t *)&LCD_Currentfonts->table[ucRelativePositon * fontLength]; //设置显示窗口 ILI9341_OpenWindow ( usX, usY, LCD_Currentfonts->Width, LCD_Currentfonts->Height); ILI9341_Write_CD ( CMD_SetPixel,0 ); //按字节读取字模数据 //由于前面直接设置了显示窗口,显示数据会自动换行 for ( byteCount = 0; byteCount < fontLength; byteCount++ ) { //一位一位处理要显示的颜色 for ( bitCount = 0; bitCount < 8; bitCount++ ) { if ( Pfont[byteCount] & (0x80>>bitCount) ) ILI9341_Write_CD ( CurrentBackColor,1 ); else ILI9341_Write_CD ( CurrentTextColor,1 ); } } }
18. 显示英文字符串:
/******
*函数功能 :在 ILI9341 显示器上显示英文字符串
*函数名 : IlI9341_ShowString_EN();
*参数 : usX :在特定扫描方向下字符的起始X坐标
* usY :在特定扫描方向下字符的起始Y坐标
* pStr :要显示的英文字符串的首地址
*返回值 :无
******/void IlI9341_ShowString_EN(uint16_t usX,uint16_t usY,char* Pstr) { while(*Pstr!='\0') { if((usX-ILI9341_DispWindow_X_Star+LCD_Currentfonts ->Width)>ILI9341_LESS_PIXEL)//X边界判断 { usX=ILI9341_DispWindow_X_Star; usY+=LCD_Currentfonts->Height;//向下移一行 } if((usY-ILI9341_DispWindow_Y_Star+LCD_Currentfonts ->Width)>ILI9341_MORE_PIXEL) //Y边界判断 { usY=ILI9341_DispWindow_Y_Star;//到底从头开始 usX=ILI9341_DispWindow_X_Star; } ILI9341_ShowChar_EN(usX,usY,*Pstr); //显示 Pstr++; usX += LCD_Currentfonts->Width; //移位 } }
19. 清除某行文字
/******
*函数功能 :清除某行文字
*函数名 :LCD_SetFont();
*参数 :Line: 指定要删除的行
*返回值 :无
******/void LCD_ClearLine(uint16_t Line) { ILI9341_Clear(0,Line,LCD_X_LENGTH,((sFONT *)LCD_GetFont())->Height); /* 清屏,显示全黑 */ }
实验及其现象二
1.显示字符,字符串不同字体类型的:
while (1) { LCD_SetColors(BLACK,WHITE); //颜色设置 LCD_SetFont(&Font24x32); //字体大小设置 ILI9341_ShowChar_EN (0,0,'A'); LCD_SetFont(&Font16x24); ILI9341_ShowChar_EN (32,0,'A'); LCD_SetFont(&Font8x16); ILI9341_ShowChar_EN (56,0,'A'); LCD_SetColors(GREEN,BLACK); LCD_SetFont(&Font24x32); IlI9341_ShowString_EN(0,35,"Hello"); LCD_SetFont(&Font16x24); IlI9341_ShowString_EN(0,70,"Hello"); LCD_SetFont(&Font8x16); IlI9341_ShowString_EN(0,100,"Hello"); }
实验现象:
20. 显示一个中文汉字:
//在函数我做了不同类型字体的判别,更容易得到自己想要输出的字体类型
/******
*函数功能 : 在 ILI9341 显示器上显示一个中文汉字
*函数名 : ILI9341_ShowChina_one();
*参数 : usX :在特定扫描方向下字符的起始X坐标
* usY :在特定扫描方向下字符的起始Y坐标
data:要显示的中文的首地址
* ChSize:要显示的汉字大小可为:32,24,16
*返回值 :无
******/void ILI9341_ShowChina_one(uint16_t usX,uint16_t usY,char* data,uint8_t ChSize) { uint8_t rowCount,bitCount,CHFlag=0; uint32_t usTemp; //中间量 uint16_t Shu_Zi=0; //汉字的数量 uint16_t Buffe32[128]={0}; //字体大小为32,提取字模缓存 uint16_t Buffe24[72]={0}; //字体大小为24,提取字模缓存 uint16_t Buffe16[32]={0}; //字体大小为16,提取字模缓存 if(ChSize==32) { Shu_Zi=sizeof(ChinaFONT32x32)/sizeof(LCD_China_GB32); //得出有多少个汉字 LCD_ChFonts=&CH_Font32X32; CHFlag=1; //显示32位大小汉字 }else if(ChSize==24) { Shu_Zi=sizeof(ChinaFONT24x24)/sizeof(LCD_China24_GB32); //得出有多少个汉字 LCD_ChFonts=&CH_Font24X24; CHFlag=2; //显示24位大小汉字 }else if(ChSize==16) { Shu_Zi=sizeof(ChinaFONT16x16)/sizeof(LCD_China16_GB32); //得出有多少个汉字 LCD_ChFonts=&CH_Font16X16; CHFlag=3; //显示16位大小汉字 } ILI9341_OpenWindow(usX,usY,LCD_ChFonts->Width,LCD_ChFonts->Height); //开辟窗口 ILI9341_Write_CD(CMD_SetPixel,0); switch(CHFlag){ case 0:break; case 1: { for(int zh=0;zh<Shu_Zi;zh++) //索引判断,并提取字模 { if((ChinaFONT32x32[zh].Index[0]==* data)&&(ChinaFONT32x32[zh].Index[1]==* (data+1))) { for(int i=0;i<128;i++) { Buffe32[i]=ChinaFONT32x32[zh].MSK32[i]; } } } //拼接32位数据 for(rowCount=0;rowCount<32;rowCount++) { usTemp=Buffe32[rowCount*4]; usTemp=(usTemp<<8); usTemp|=Buffe32[rowCount*4+1]; usTemp=(usTemp<<8); usTemp|=Buffe32[rowCount*4+2]; usTemp=(usTemp<<8); usTemp|=Buffe32[rowCount*4+3]; for(bitCount=0;bitCount<32;bitCount++)//填充 { if(usTemp&(0x80000000>>bitCount)) ILI9341_Write_CD(CurrentTextColor,1);//填充的颜色 else ILI9341_Write_CD(CurrentBackColor,1);//背景颜色 } } } break; case 2: { for(int zh=0;zh<Shu_Zi;zh++) //索引判断,并提取字模 { if((ChinaFONT24x24[zh].Index[0]==* data)&&(ChinaFONT24x24[zh].Index[1]==* (data+1))) { for(int i=0;i<72;i++) { Buffe24[i]=ChinaFONT24x24[zh].MSK24[i]; } } } //拼接24位数据 for(rowCount=0;rowCount<24;rowCount++) { usTemp=Buffe24[rowCount*3]; usTemp=(usTemp<<8); usTemp|=Buffe24[rowCount*3+1]; usTemp=(usTemp<<8); usTemp|=Buffe24[rowCount*3+2]; for(bitCount=0;bitCount<24;bitCount++)//填充 { if(usTemp&(0x800000>>bitCount)) ILI9341_Write_CD(CurrentTextColor,1);//填充的颜色 else ILI9341_Write_CD(CurrentBackColor,1);//背景颜色 } } } break; case 3: { for(int zh=0;zh<Shu_Zi;zh++) //索引判断,并提取字模 { if((ChinaFONT16x16[zh].Index[0]==* data)&&(ChinaFONT16x16[zh].Index[1]==* (data+1))) { for(int i=0;i<32;i++) { Buffe16[i]=ChinaFONT16x16[zh].MSK16[i]; } } } //拼接24位数据 for(rowCount=0;rowCount<16;rowCount++) { usTemp=Buffe16[rowCount*2]; usTemp=(usTemp<<8); usTemp|=Buffe16[rowCount*2+1]; for(bitCount=0;bitCount<16;bitCount++)//填充 { if(usTemp&(0x8000>>bitCount)) ILI9341_Write_CD(CurrentTextColor,1);//填充的颜色 else ILI9341_Write_CD(CurrentBackColor,1);//背景颜色 } } } break; } }
21. 显示器上显示一行或一列中文汉字:
/******
*函数功能 : 在 ILI9341 显示器上显示一行或一列中文汉字
*函数名 : ILI9341_ShowChina_one();
*参数 : usX :在特定扫描方向下字符的起始X坐标
* usY :在特定扫描方向下字符的起始Y坐标
data:要显示的中文的首地址
* ChSize:要显示的汉字大小可为:32,24,16
model: 0按行显示
1按列显示
*返回值 :无
******/void ILI9341_ShowChina_String(uint16_t usX,uint16_t usY,char* data,uint8_t ChSize,uint8_t model) { //显示汉字大小 if(ChSize==32) { LCD_ChFonts=&CH_Font32X32; }else if(ChSize==24) { LCD_ChFonts=&CH_Font24X24; }else if(ChSize==16) { LCD_ChFonts=&CH_Font16X16; } if(model==0) //横着显示 { while(*data!='\0') { if((usY-ILI9341_DispWindow_Y_Star+LCD_ChFonts->Height)>ILI9341_MORE_PIXEL)//Y边界判断 { usX=ILI9341_DispWindow_X_Star; usY=ILI9341_DispWindow_Y_Star; } if((usX-ILI9341_DispWindow_X_Star+LCD_ChFonts->Width)>ILI9341_LESS_PIXEL)//X边界判断 { usX+=ILI9341_DispWindow_X_Star; usY=ILI9341_DispWindow_Y_Star; } ILI9341_ShowChina_one(usX,usY,data,ChSize); data++; usX+= LCD_ChFonts->Width; } }else { while(*data!='\0') //竖着显示 { if((usY-ILI9341_DispWindow_Y_Star+LCD_ChFonts->Height)>ILI9341_MORE_PIXEL)//Y边界判断 { usX+=LCD_ChFonts->Width; usY=ILI9341_DispWindow_Y_Star; } if((usX-ILI9341_DispWindow_X_Star+LCD_ChFonts->Width)>ILI9341_LESS_PIXEL)//X边界判断 { usX=ILI9341_DispWindow_X_Star; usY=ILI9341_DispWindow_Y_Star; } ILI9341_ShowChina_one(usX,usY,data,ChSize); data++; usY += LCD_ChFonts->Height; } } }
实验及其现象三:
1.显示不同大小的汉字及行列形式代码:
while (1) { ILI9341_ShowChina_one(0,0,"春",32); //不同大小字体 ILI9341_ShowChina_one(35,0,"夏",24); ILI9341_ShowChina_one(70,0,"秋",16); ILI9341_ShowChina_String(0,0,"春夏秋冬",32,1); //列显示 ILI9341_ShowChina_String(0,192,"冬秋春夏",32,0); //行显示 }
实验现象:
21.显示照片:
/******
*函数功能 : 在 ILI9341 显示照片
*函数名 : ILI9341_ShowChina_one();
*参数 : usX_Start :在特定扫描方向下矩形的起始点X坐标
* usY_Start :在特定扫描方向下矩形的起始点Y坐标
usWidth: 图片的宽度(单位:像素)
* usHeight:图片的高度(单位:像素)
*返回值 :无
******/void ILI9341_ShowPicture( uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight) { ILI9341_OpenWindow(usX_Start,usY_Start,usWidth,usHeight); //开辟窗口 ILI9341_Write_CD(CMD_SetPixel,0); uint64_t Count=0; for(Count=0;Count<(usWidth*usHeight*2);Count+=2) { ILI9341_Write_CD((gImage_123[Count]<<8)|(gImage_123[Count+1]),1); //写入十六位数据 } }
实验及其现象四:
1.显示一张彩图代码:
while (1) { ILI9341_ShowPicture(0,0,240,120); //显示图片 }
实验现象:
2.关于图片如何取模网上有大量教程,大家可以去自行搜索.
代码分享:上面驱动代码存在:驱动模块: 平常练习中对一些,代码模块化处理https://gitee.com/xuji-group_0/driver-module.git
完整程序存在:杂类: 日常练手小项目https://gitee.com/xuji-group_0/sundry.git 文件名LCD_ILI9341.