STM32F4驱动ILI9341的TFT-LCD触摸屏(HAL库)(一)

前言

   实验硬件:STM32F407ZGT6;2.4寸TFT-LCD模块(ILI9341驱动芯片、XTP2046触摸芯片)
   硬件实物图
  
在这里插入图片描述
   最终目标:使用STMF407ZGT6驱动2.4寸TFT-LCD触摸屏,并移植LVGL库
   本节目标:ILI9341显示芯片驱动实现
   软件:STM32CubeMX;Keil
  完整项目在文章末尾github链接中

一、TFT-LCD介绍

  目前最常用的触摸屏有两种:电阻式触摸屏和电容式触摸屏。

1、电阻屏
  电阻屏的主要部分是一块与显示器表面配合非常好的电阻薄膜屏,这是一种多层的复合薄膜,由一层玻璃或有机玻璃作为基层,表面涂有一层秀明的导电层,上面再盖有一层外表硬化处理、光滑防刮的塑料层,它的内表面也涂有一层透明导电层,在两层导电层之间有许多细小(小于千分之一英寸)的透明隔离点把它们隔开绝缘。当手指触摸屏幕时,平常绝缘的两层导电层在触摸点位置就有了一个接触,控制器侦测到这个接通后,其中一面导电层接通У轴方向的5Ⅴ均匀电压场,另一导电层将接触点的电压引至控制卡进行A/D转换,得到电压值后与5Ⅴ相比即可得触摸点的у轴坐标,同理得出Χ轴的坐标,这就是所有电阻技术触摸屏共同的最基本原理。

2、ILI9341介绍
  ILI9341控制器支持的最大分辨率为240×320,拥有一个172800字节大小的GRAM。同时支持8位、9位、16位、18位并口数据总线,还支持3线制和4线制SPI串口。由于并行控制需要大量的IO口,所以最常用的还是SPI串口控制。ILI9341还支持65K、262K RGB颜色显示,显示色彩很丰富,同时支持旋转显示和滚动显示以及视频播放,显示方式多样。
  ILI9341控制器使用16bit(RGB565)来控制一个像素点显示,因此可以每个像素点显示颜色多达65K种。像素点地址设置按照行列的顺序进行,遵增递减方向由扫描方式决定。

3、硬件接口
在这里插入图片描述

模块引脚引脚说明
VCC电源
GND
CSLCD片选信号
RESETLCD复位信号
DCLCD寄存器/数据选择信号
SDI(MOSI)LCD SPI总线写信号
SCKLCD SPI总线时钟线
LED背光控制(高电平亮)
SDO(MISO)LCD SPI总线读信号
T_CLK触摸 SPI总线时钟
T_CS触摸片选信号
T_DIN触摸 SPI总线读信号
T_DO触摸 SPI总线写信号
T_IRQ触摸中断信号

4、SPI通讯
  自己随便找下学一下吧

二、CubeMx设置

  根据自己所用芯片自行调整
1、配置外部高速晶振
在这里插入图片描述

2、配置SYS
在这里插入图片描述

3、SPI配置,用于驱动LCD屏
在这里插入图片描述

4、TIM1配置,提供us延时函数
在这里插入图片描述

5、GPIO配置,只实现显示只需配置PB12、PB13、PB14、PB15即可,其他为触摸驱动IO,可暂不配置
在这里插入图片描述
6、时钟树配置168MHZ
在这里插入图片描述

三、LCD显示驱动实现

  本文只展示部分关键代码,完整程序请至github自行获取。此外,由于本文的最终目标为移植LVGL库,ILI9341驱动只完成了基本功能,其他GUI功能可自行编写。

  • ili9341.h
#ifndef __ILI9341_H__
#define __ILI9341_H__

#include <stdint.h>
#include "main.h"             
#include "spi.h"              
#include "gpio.h"             
#include "stm32f4xx_hal.h"  
#include "delay.h"

//LCD重要参数集
typedef struct  
{										    
	uint16_t width;			//LCD 宽度
	uint16_t height;			//LCD 高度
	uint16_t id;				//LCD ID
	uint8_t  dir;			//横屏还是竖屏控制:0,竖屏;1,横屏。	
	uint16_t	 wramcmd;		//开始写gram指令
	uint16_t  setxcmd;		//设置x坐标指令
	uint16_t  setycmd;		//设置y坐标指令	 
}_lcd_dev; 	

//LCD参数
extern _lcd_dev lcddev;	//管理LCD重要参数

#define USE_HORIZONTAL  	 0//定义液晶屏顺时针旋转方向 	0-0度旋转,1-90度旋转,2-180度旋转,3-270度旋转

// LCD尺寸定义
#define LCD_WIDTH   240     // LCD宽度
#define LCD_HEIGHT  320     // LCD高度

// 定义GPIO端口和引脚
#define LCD_RST_GPIO_Port    GPIOB
#define LCD_RST_Pin         GPIO_PIN_12
#define LCD_DC_GPIO_Port     GPIOB    
#define LCD_DC_Pin          GPIO_PIN_14
#define LCD_CS_GPIO_Port     GPIOB
#define LCD_CS_Pin          GPIO_PIN_15
#define LCD_LED_GPIO_Port     GPIOB
#define LCD_LED_Pin          GPIO_PIN_13


// 控制信号宏定义
#define LCD_RST_Clr()   HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_RESET)
#define LCD_RST_Set()   HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET)
                                              
#define LCD_DC_Clr()    HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_RESET)
#define LCD_DC_Set()    HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET)
                                              
#define LCD_CS_Clr()    HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET)
#define LCD_CS_Set()    HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET)
                                              
#define LCD_LED_ON()    HAL_GPIO_WritePin(LCD_LED_GPIO_Port, LCD_LED_Pin, GPIO_PIN_SET)
#define LCD_LED_OFF()   HAL_GPIO_WritePin(LCD_LED_GPIO_Port, LCD_LED_Pin, GPIO_PIN_RESET)


//画笔颜色
#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 //灰色
//GUI颜色
#define DARKBLUE      	 0X01CF	//深蓝色
#define LIGHTBLUE      	 0X7D7C	//浅蓝色  
#define GRAYBLUE       	 0X5458 //灰蓝色
//以上三色为PANEL的颜色 
 
#define LIGHTGREEN     	0X841F //浅绿色
#define LIGHTGRAY     0XEF5B //浅灰色(PANNEL)
#define LGRAY 			 		0XC618 //浅灰色(PANNEL),窗体背景色
#define LGRAYBLUE      	0XA651 //浅灰蓝色(中间层颜色)
#define LBBLUE          0X2B12 //浅棕蓝色(选择条目的反色)

void LCD_Init(void);
void LCD_Clear(uint16_t color);
void LCD_BackLight_On(void);
void LCD_BackLight_Off(void);

void LCD_DrawPoint(uint16_t startX, uint16_t startY, uint16_t color);
void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
void LCD_DrawFilledCircle(uint16_t centerX, uint16_t centerY, uint16_t radius, uint16_t color);

#endif // __LCD_H__

  • ili9341.c
#include "ili9341.h"

_lcd_dev lcddev;

/**
 * @brief  发送命令到ST7789显示屏
 * @param  cmd: 要发送的命令字节
 * @note   发送命令时,DC引脚需要置低,CS引脚先置低后置高
 * @retval None
 */
void LCD_SendCmd(uint8_t cmd)
{
    LCD_DC_Clr();    // 命令模式
    LCD_CS_Clr();    // 片选使能
    
    HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY);
    
    LCD_CS_Set();    // 片选禁用
}

/**
 * @brief  发送数据到ST7789显示屏
 * @param  data: 要发送的数据字节
 * @note   发送数据时,DC引脚需要置高,CS引脚先置低后置高
 * @retval None
 */
void LCD_SendData(uint8_t data)
{
    LCD_DC_Set();    // 数据模式
    LCD_CS_Clr();    // 片选使能
    
    HAL_SPI_Transmit(&hspi1, &data, 1, HAL_MAX_DELAY);
    
    LCD_CS_Set();    // 片选禁用
}

/**
 * @brief  向LCD写入寄存器和数据
 * @param  reg: 寄存器地址
 * @param  data: 要写入的数据
 * @retval None
 */
void LCD_WriteReg(uint8_t reg, uint16_t data)
{
    LCD_SendCmd(reg);
    LCD_SendData(data);
}

/**
 * @brief  准备向LCD GRAM写入数据
 * @note   在写入像素数据之前需要调用此函数
 * @param  None
 * @retval None
 */
void LCD_WriteRAM_Prepare(void)
{
    LCD_SendCmd(lcddev.wramcmd);
}

/**
 * @brief  向LCD写入16位数据
 * @param  data: 要写入的16位数据
 * @retval None
 */
void LCD_WriteData_16Bit(uint16_t data)
{
    LCD_DC_Set();    // 数据模式
    LCD_CS_Clr();    // 片选使能
    
    // 先发送高8位,再发送低8位
    HAL_SPI_Transmit(&hspi1, (uint8_t*)&data + 1, 1, HAL_MAX_DELAY);
    HAL_SPI_Transmit(&hspi1, (uint8_t*)&data, 1, HAL_MAX_DELAY);
    
    LCD_CS_Set();    // 片选禁用
}

void LCD_RESET(void)
{
	LCD_RST_Clr();
	delay_ms(50);
	LCD_RST_Set();
	delay_ms(50);
}

void LCD_BackLight_On(void){
	HAL_GPIO_WritePin(LCD_LED_GPIO_Port, LCD_LED_Pin, GPIO_PIN_SET);
}

void LCD_BackLight_Off(void){
	HAL_GPIO_WritePin(LCD_LED_GPIO_Port, LCD_LED_Pin, GPIO_PIN_RESET);
}

/**
 * @brief  设置LCD显示窗口
 * @details 设置显示窗口后可以连续发送颜色数据,无需重复设置坐标
 * @param  startX: 窗口起点x轴坐标
 * @param  startY: 窗口起点y轴坐标
 * @param  width:  窗口宽度
 * @param  height: 窗口高度
 * @note   设置显示区域后会自动开启写入模式(RAMWR)
 * @retval None
 */
void LCD_SetWindow(uint16_t startX, uint16_t startY, uint16_t width, uint16_t height)
{
    uint16_t endX = startX + width - 1;
    uint16_t endY = startY + height - 1;
    
    // 设置列地址范围
    LCD_SendCmd(0x2A);        // Column Address Set
    LCD_SendData(startX >> 8);     // 起始列地址高8位
    LCD_SendData(startX & 0xFF);   // 起始列地址低8位
    LCD_SendData(endX >> 8);       // 结束列地址高8位
    LCD_SendData(endX & 0xFF);     // 结束列地址低8位

    // 设置行地址范围
    LCD_SendCmd(0x2B);        // Row Address Set
    LCD_SendData(startY >> 8);     // 起始行地址高8位
    LCD_SendData(startY & 0xFF);   // 起始行地址低8位
    LCD_SendData(endY >> 8);       // 结束行地址高8位
    LCD_SendData(endY & 0xFF);     // 结束行地址低8位
    
    // 开始写入显存
    LCD_SendCmd(0x2C);        // Memory Write
}

/**
 * @brief  设置LCD光标位置
 * @param  x: x坐标
 * @param  y: y坐标
 * @retval None
 */
void LCD_SetCursor(uint16_t x, uint16_t y)
{
    LCD_SetWindow(x, y, 1, 1);
}

/**
 * @brief  使用指定颜色填充整个屏幕
 * @param  color: 16位RGB565格式的颜色值
 * @note   使用连续写入模式快速填充
 * @retval None
 */
void LCD_Clear(uint16_t color)
{
    uint32_t total_pixels = lcddev.width * lcddev.height;
    uint8_t color_high = color >> 8;
    uint8_t color_low = color & 0xFF;
    
    // 设置全屏显示窗口
    LCD_SetWindow(0, 0, lcddev.width-1, lcddev.height-1);
    
    // 连续发送颜色数据
    LCD_DC_Set();    // 数据模式
    LCD_CS_Clr();    // 片选使能
    
    for(uint32_t i = 0; i < total_pixels; i++) {
        HAL_SPI_Transmit(&hspi1, &color_high, 1, HAL_MAX_DELAY);
        HAL_SPI_Transmit(&hspi1, &color_low, 1, HAL_MAX_DELAY);
    }
    
    LCD_CS_Set();    // 片选禁用
}

/**
 * @brief  设置LCD显示方向
 * @param  direction: 显示方向
 *         0: 0度    (默认竖屏)
 *         1: 90度   (顺时针转90度)
 *         2: 180度  (顺时针转180度)
 *         3: 270度  (顺时针转270度)
 * @note   设置方向的同时会更新LCD设备的宽度和高度参数
 * @retval None
 */
void LCD_SetDirection(uint8_t direction)
{
    // 设置基本的LCD命令
    lcddev.setxcmd = 0x2A;    // 列地址设置命令
    lcddev.setycmd = 0x2B;    // 行地址设置命令
    lcddev.wramcmd = 0x2C;    // 写GRAM命令
    
    switch(direction)
    {
        case 0:     // 0度
            lcddev.width = LCD_WIDTH;
            lcddev.height = LCD_HEIGHT;
            // BGR=1,MY=0,MX=0,MV=0: 正常显示,不翻转,不旋转
            LCD_SendCmd(0x36);
            LCD_SendData((1<<3)|(0<<6)|(0<<7));
            break;
            
        case 1:     // 90度
            lcddev.width = LCD_HEIGHT;
            lcddev.height = LCD_WIDTH;
            // BGR=1,MY=0,MX=1,MV=1: X镜像,行列交换
            LCD_SendCmd(0x36);
            LCD_SendData((1<<3)|(0<<7)|(1<<6)|(1<<5));
            break;
            
        case 2:     // 180度
            lcddev.width = LCD_WIDTH;
            lcddev.height = LCD_HEIGHT;
            // BGR=1,MY=1,MX=1,MV=0: X和Y都镜像
            LCD_SendCmd(0x36);
            LCD_SendData((1<<3)|(1<<6)|(1<<7));
            break;
            
        case 3:     // 270度
            lcddev.width = LCD_HEIGHT;
            lcddev.height = LCD_WIDTH;
            // BGR=1,MY=1,MX=0,MV=1: Y镜像,行列交换
            LCD_SendCmd(0x36);
            LCD_SendData((1<<3)|(1<<7)|(1<<5));
            break;
            
        default:
            break;
    }
}

void LCD_Init(void)
{
    // 复位LCD
    LCD_RESET();
    delay_ms(100);
 
    // 开始初始化LCD
    LCD_SendCmd(0xCF);
    LCD_SendData(0x00);
    LCD_SendData(0xD9);
    LCD_SendData(0X30);
    
    LCD_SendCmd(0xED);
    LCD_SendData(0x64);
    LCD_SendData(0x03);
    LCD_SendData(0X12);
    LCD_SendData(0X81);
    
    LCD_SendCmd(0xE8);
    LCD_SendData(0x85);
    LCD_SendData(0x10);
    LCD_SendData(0x7A);
    
    LCD_SendCmd(0xCB);
    LCD_SendData(0x39);
    LCD_SendData(0x2C);
    LCD_SendData(0x00);
    LCD_SendData(0x34);
    LCD_SendData(0x02);
    
    LCD_SendCmd(0xF7);
    LCD_SendData(0x20);
    
    LCD_SendCmd(0xEA);
    LCD_SendData(0x00);
    LCD_SendData(0x00);
    
    // Power Control
    LCD_SendCmd(0xC0);
    LCD_SendData(0x1B);
    
    LCD_SendCmd(0xC1);
    LCD_SendData(0x12);
    
    // VCM Control
    LCD_SendCmd(0xC5);
    LCD_SendData(0x08);
    LCD_SendData(0x26);
    
    LCD_SendCmd(0xC7);
    LCD_SendData(0XB7);
    
    // Memory Access Control
    LCD_SendCmd(0x36);
    LCD_SendData(0x08);
    
    LCD_SendCmd(0x3A);
    LCD_SendData(0x55);
    
    LCD_SendCmd(0xB1);
    LCD_SendData(0x00);
    LCD_SendData(0x1A);
    
    // Display Function Control
    LCD_SendCmd(0xB6);
    LCD_SendData(0x0A);
    LCD_SendData(0xA2);
    
    // 3Gamma Function
    LCD_SendCmd(0xF2);
    LCD_SendData(0x00);
    
    LCD_SendCmd(0x26);
    LCD_SendData(0x01);
    
    // Set Gamma
    LCD_SendCmd(0xE0);
    LCD_SendData(0x0F);
    LCD_SendData(0x1D);
    LCD_SendData(0x1A);
    LCD_SendData(0x0A);
    LCD_SendData(0x0D);
    LCD_SendData(0x07);
    LCD_SendData(0x49);
    LCD_SendData(0x66);
    LCD_SendData(0x3B);
    LCD_SendData(0x07);
    LCD_SendData(0x11);
    LCD_SendData(0x01);
    LCD_SendData(0x09);
    LCD_SendData(0x05);
    LCD_SendData(0x04);
    
    LCD_SendCmd(0XE1);
    LCD_SendData(0x00);
    LCD_SendData(0x18);
    LCD_SendData(0x1D);
    LCD_SendData(0x02);
    LCD_SendData(0x0F);
    LCD_SendData(0x04);
    LCD_SendData(0x36);
    LCD_SendData(0x13);
    LCD_SendData(0x4C);
    LCD_SendData(0x07);
    LCD_SendData(0x13);
    LCD_SendData(0x0F);
    LCD_SendData(0x2E);
    LCD_SendData(0x2F);
    LCD_SendData(0x05);
    
    // 设置显示区域
    LCD_SendCmd(0x2B);
    LCD_SendData(0x00);
    LCD_SendData(0x00);
    LCD_SendData(0x01);
    LCD_SendData(0x3F);
    
    LCD_SendCmd(0x2A);
    LCD_SendData(0x00);
    LCD_SendData(0x00);
    LCD_SendData(0x00);
    LCD_SendData(0xEF);
    
    // 退出睡眠模式
    LCD_SendCmd(0x11);
    HAL_Delay(120);
    
    // 开启显示
    LCD_SendCmd(0x29);
    
    // 设置显示方向
    LCD_SetDirection(USE_HORIZONTAL);
    
    // 开启背光并清屏
    LCD_BackLight_On();
    LCD_Clear(WHITE);
}


/*************************以下为GUI**********************************************************************************/


/**
 * @brief  在指定位置画点
 * @param  startX: 点的X坐标
 * @param  startY: 点的Y坐标
 * @param  color:  点的颜色(RGB565格式)
 * @note   使用SetWindow函数设置1x1的显示区域后写入颜色数据
 * @retval None
 */
void LCD_DrawPoint(uint16_t startX, uint16_t startY, uint16_t color)
{
	// 设置1x1的显示窗口
	LCD_SetWindow(startX, startY, 1, 1);
	LCD_WriteData_16Bit(color);
}


/*******************************************************************
 * @name       :画线
 * @function   :Draw a line between two points
 * @parameters :	x1:the bebinning x coordinate of the line
				y1:the bebinning y coordinate of the line
				x2:the ending x coordinate of the line
				y2:the ending y coordinate of the line
 * @retvalue   :None
********************************************************************/
void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)
{
	uint16_t t; 
	int xerr=0,yerr=0,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+1;t++ )//画线输出 
	{  
		LCD_DrawPoint(uRow,uCol, color);//画点 
		xerr+=delta_x ; 
		yerr+=delta_y ; 
		if(xerr>distance) 
		{ 
			xerr-=distance; 
			uRow+=incx; 
		} 
		if(yerr>distance) 
		{ 
			yerr-=distance; 
			uCol+=incy; 
		} 
	}  
} 

/**
 * @brief  在指定位置画实心圆
 * @param  centerX: 圆心的X坐标
 * @param  centerY: 圆心的Y坐标
 * @param  radius:  圆的半径
 * @param  color:   圆的颜色(RGB565格式)
 * @retval None
 */
void LCD_DrawFilledCircle(uint16_t centerX, uint16_t centerY, uint16_t radius, uint16_t color)
{
    int16_t x = 0;
    int16_t y = radius;
    int16_t d = 1 - radius;
    int16_t deltaE = 3;
    int16_t deltaSE = -2 * radius + 5;

    // 画初始的八个点
    LCD_DrawLine(centerX - radius, centerY, centerX + radius, centerY, color);

    while (y > x)
    {
        if (d < 0) // 选择东点
        {
            d += deltaE;
            deltaE += 2;
            deltaSE += 2;
        }
        else // 选择东南点
        {
            d += deltaSE;
            deltaE += 2;
            deltaSE += 4;
            y--;
        }
        x++;

        // 画对称的八个点
        LCD_DrawLine(centerX - x, centerY + y, centerX + x, centerY + y, color);
        LCD_DrawLine(centerX - x, centerY - y, centerX + x, centerY - y, color);
        LCD_DrawLine(centerX - y, centerY + x, centerX + y, centerY + x, color);
        LCD_DrawLine(centerX - y, centerY - x, centerX + y, centerY - x, color);
    }
}

github链接:https://github.com/Syy001023/ILI9341_lvgl

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值