本文讲解如何配置LTDC驱动LCD的参数配置,以及CubeMx参数配置说明
本文使用的是淘宝买的一块带电容触摸的液晶显示屏:5寸TFT液晶显示屏高清800*480免驱40P通用RGBIPS全视角彩屏GT911
说实话,价格还是相对挺便宜的,值得入手,哈哈哈
这款屏幕采用的是RGB888格式
这里面也就是常用的引脚:
R0-R7、G0-G7、B0-B7
DCLK–时钟线
HSYNC\VSYNC–同步线
DE–数据使能
DISP是显示使能,控制屏幕的哈
背光是通过控制LED+\LED-的电流实现的
由此可以找到,LED需要流过20mA电流,下面是它的推荐电路
接着我就绘制了我的PCB,如下所示:我的是电容触摸屏,所以电阻触摸引脚位置悬空的
接下来,我们就来CubeMx配置LTDC驱动它吧
CubeMx配置LTDC
硬件相关参数设置
注意:这里的HSYNC、VSYNC、DE的有效极性需要和实际相反
由此图可以看出HSYNC、VSYNC、DE的有效极性都是高,clk的下降沿采样
上面我的LTDC配置取得都是典型值
Pulse Width对应的是HSW和VSW
引脚需要全部高速
然后就还有LTDC输出给LCD的时钟信号,由上面的数据手册给出的典型值配置25MHz,LTDC会由LTDC_PCLK引脚输出给LCD
到此,LTDC硬件相关的参数配置完毕
LTDC图像层配置
开启全局中断,并且优先级可以设置低点
DMA2D在代码里重新配置过得,可以按此设置
关于FMC的SDRAM存储属性设置可以参考如下:
调试
如何判定硬件问题:
在函数void MX_LTDC_Init(void)中的HAL_LTDC_Init()后如下处理:
如果LCD能显示红色说明硬件正常,否则有问题
lcd_base_backlight_set是开启屏幕背光
完整工程下载:(旧版本,不建议使用,LCD显示刷新没处理后续说的cache更新)
链接:https://pan.baidu.com/s/1g_VezTfR_-fgqSpPFlvtqQ
提取码:qqio
补充:
该屏幕GT911驱动:
需要注意的是INT引脚禁止上下拉,应配置为浮空输入
触摸屏手指触摸时会触摸70ms左右,这段时间其实坐标是同一点,INT引脚持续输出方波信号10ms间隔
所以应采集判定坐标避免重复点如下函数:
/****************************************************
@function:触摸芯片扫描
@param:(x,y)--点
@return:-1--无触摸,0--触摸
@note:只读取一个点,建议10ms执行一次
注意:此函数未处理重复触摸点,尤其是在两次点击相同像素点的情况下,但是概率不大
****************************************************/
int GT911_Scan(uint16_t *x,uint16_t *y)
{
static uint16_t xlast = 0,ylast = 0;
if(!gt911.Enable)return -1;
if(!GT911_PenInt())return -1;//没有触摸
GT911_TouchPointRead(x,y);
if((*x == xlast) && (*y == ylast))return -1;//移除重复点
xlast = *x;
ylast = *y;
return 0;
}
完整驱动代码:
GT911_driver.c
GT911_driver.h
i2c_driver.c//模拟IIC
i2c_driver.h
delay_driver.c//定时器微秒精确延时
delay_driver.h
GT911_driver.h
#ifndef _GT911_driver_H_
#define _GT911_driver_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "stdint.h"
uint16_t GT911_ScreenWidthGet(void);
uint16_t GT911_ScreenHeigthGet(void);
int GT911_Init(void);
int GT911_Scan(uint16_t *x,uint16_t *y);
#ifdef __cplusplus
}
#endif
#endif
GT911_driver.c
/**********************************************************************
*file:自己编写的GT911触摸检测
*author:残梦
*versions:V1.0
*date:2023.11.8
*note:
注意GT911的INT引脚会在触摸时出现10ms的方波信号,手指单次触摸维持在70ms左右,
持续触摸会一直到释放,默认高电平,触摸低电平,引脚应浮空严禁上下拉
**********************************************************************/
#include "GT911_driver.h"
#include "i2c_driver.h"
#include "delay_driver.h"
#include "gpio.h"
#include "stdio.h"
/* 定义触笔中断INT的GPIO端口 */
#define TP_INT_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE()
#define TP_INT_GPIO_PORT GPIOD
#define TP_INT_PIN GPIO_PIN_4
#define TP_RST_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE()
#define TP_RST_GPIO_PORT GPIOD
#define TP_RST_PIN GPIO_PIN_5
//#define GT911_I2C_ADDR1 0xBA
#define GT911_READ_XY_REG 0x814E /* 坐标寄存器 */
#define GT911_CLEARBUF_REG 0x814E /* 清除坐标寄存器 */
#define GT911_CONFIG_REG 0x8047 /* 配置参数寄存器 */
#define GT911_COMMAND_REG 0x8040 /* 实时命令 */
#define GT911_PRODUCT_ID_REG 0x8140 /* 芯片ID */
#define GT911_VENDOR_ID_REG 0x814A /* 当前模组选项信息 */
#define GT911_CONFIG_VERSION_REG 0x8047 /* 配置文件版本号 */
#define GT911_CONFIG_CHECKSUM_REG 0x80FF /* 配置文件校验码 */
#define GT911_FIRMWARE_VERSION_REG 0x8144 /* 固件版本号 */
typedef struct
{
//芯片参数
uint8_t i2cAddress;//i2c地址
uint16_t width;//屏幕宽度
uint16_t height;//屏幕高度
//用户变量
uint8_t Enable;//使能状态,初始化成功则为1,失败为0
}GT911_StructDef;
static GT911_StructDef gt911 = \
{
.Enable = 0,
};//触摸屏结构体变量
static void GT911_WriteReg(uint16_t _usRegAddr, uint8_t *_pRegBuf, uint8_t _ucLen);
static void GT911_ReadReg(uint16_t _usRegAddr, uint8_t *_pRegBuf, uint8_t _ucLen);
static void GT911_ReadMaxXY(uint16_t *_X, uint16_t *_Y);
static void GT911_InitPin(void);
static uint32_t GT911_ReadID(void);
static int GT911_DetectID(void);
static uint8_t GT911_PenInt(void);
static void GT911_TouchPointRead(uint16_t *x,uint16_t *y);
/****************************************************
@function:写1个或连续的多个寄存器
@param: _usRegAddr : 寄存器地址
_pRegBuf : 寄存器数据缓冲区
_ucLen : 数据长度
@return:void
@note:
****************************************************/
static void GT911_WriteReg(uint16_t _usRegAddr, uint8_t *_pRegBuf, uint8_t _ucLen)
{
uint8_t i;
i2c_Start(); /* 总线开始信号 */
i2c_SendByte(gt911.i2cAddress); /* 发送设备地址+写信号 */
i2c_WaitAck();
i2c_SendByte(_usRegAddr >> 8); /* 地址高8位 */
i2c_WaitAck();
i2c_SendByte(_usRegAddr); /* 地址低8位 */
i2c_WaitAck();
for (i = 0; i < _ucLen; i++)
{
i2c_SendByte(_pRegBuf[i]); /* 寄存器数据 */
i2c_WaitAck();
}
i2c_Stop(); /* 总线停止信号 */
}
/****************************************************
@function:读1个或连续的多个寄存器
@param: _usRegAddr : 寄存器地址
_pRegBuf : 寄存器数据缓冲区
_ucLen : 数据长度
@return:void
@note:
****************************************************/
static void GT911_ReadReg(uint16_t _usRegAddr, uint8_t *_pRegBuf, uint8_t _ucLen)
{
uint8_t i;
{
i2c_Start(); /* 总线开始信号 */
i2c_SendByte(gt911.i2cAddress); /* 发送设备地址+写信号 */
i2c_WaitAck();
i2c_SendByte(_usRegAddr >> 8); /* 地址高8位 */
i2c_WaitAck();
i2c_SendByte(_usRegAddr); /* 地址低8位 */
i2c_WaitAck();
i2c_Start();
i2c_SendByte(gt911.i2cAddress + 0x01); /* 发送设备地址+读信号 */
i2c_WaitAck();
}
for (i = 0; i < 30; i++);
for (i = 0; i < _ucLen - 1; i++)
{
_pRegBuf[i] = i2c_ReadByte(); /* 读寄存器数据 */
i2c_Ack();
}
/* 最后一个数据 */
_pRegBuf[i] = i2c_ReadByte(); /* 读寄存器数据 */
i2c_NAck();
i2c_Stop(); /* 总线停止信号 */
}
/****************************************************
@function:获得GT911触摸板的分辨率,X、Y最大值+1.
@param: *_X : 水平分辨率
*_Y : 垂直分辨率
@return:void
@note:
****************************************************/
static void GT911_ReadMaxXY(uint16_t *_X, uint16_t *_Y)
{
uint8_t buf[4];
GT911_ReadReg(0x8048, buf, 4);
*_X = buf[0] + buf[1] * 256;
*_Y = buf[2] + buf[3] * 256;
}
/****************************************************
@function:配置触摸芯片初始化引脚
@param:void
@return:void
@note:
****************************************************/
static void GT911_InitPin(void)
{
GPIO_InitTypeDef gpio_init;
/* 第1步:打开GPIO时钟 */
TP_INT_GPIO_CLK_ENABLE();
TP_RST_GPIO_CLK_ENABLE();
/* 第2步:配置所有的按键GPIO为浮动输入模式 */
gpio_init.Mode = GPIO_MODE_INPUT; /* 设置输入 */
gpio_init.Pull = GPIO_NOPULL; /* 浮空:严禁上下拉 */
gpio_init.Speed = GPIO_SPEED_FREQ_LOW; /* GPIO速度等级 */
gpio_init.Pin = TP_INT_PIN;
HAL_GPIO_Init(TP_INT_GPIO_PORT, &gpio_init);
gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init.Pull = GPIO_NOPULL;
gpio_init.Speed = GPIO_SPEED_FREQ_LOW; /* GPIO速度等级 */
gpio_init.Pin = TP_RST_PIN;
HAL_GPIO_Init(TP_RST_GPIO_PORT, &gpio_init);
HAL_GPIO_WritePin(TP_RST_GPIO_PORT,TP_RST_PIN,GPIO_PIN_SET);//使能触摸芯片
}
/****************************************************
@function:获得GT911的芯片ID
@param:void
@return:void
@note:16位版本
****************************************************/
static uint32_t GT911_ReadID(void)
{
uint8_t buf[4];
GT911_ReadReg(GT911_PRODUCT_ID_REG, buf, 4);
return ((uint32_t)buf[3] << 24) + ((uint32_t)buf[2] << 16) + ((uint32_t)buf[1] <<8) + buf[0];
}
/****************************************************
@function:设置GT911芯片屏幕的宽度
@param:void
@return:void
@note:
****************************************************/
uint16_t GT911_ScreenWidthGet(void)
{
return gt911.width;
}
/****************************************************
@function:设置GT911芯片屏幕的高度
@param:void
@return:void
@note:
****************************************************/
uint16_t GT911_ScreenHeigthGet(void)
{
return gt911.height;
}
/****************************************************
@function:触摸芯片识别
@param:void
@return:-1--识别失败,0--成功
@note:
****************************************************/
static int GT911_DetectID(void)
{
uint8_t i = 0,address = 0;
uint32_t id = 0;
uint16_t MaxX = 0, MaxY = 0;
HAL_Delay(50);//50ms,等待GT811复位就绪,才能探测GT811芯片 ID
printf("开始识别触摸屏型号...\r\n");
address = 0x28;
for(i=0;i < 5;i++)//GT811电容触摸板和GT911的I2C地址相同,一般就 0x28 、 0xBA 两种,通过读取触摸IC的芯片ID来识别
{
address = (address == 0x28)?0xBA:0x28;
if(i2c_CheckDevice(address) == 0)
{
delay_us(500);
gt911.i2cAddress = address;
id = GT911_ReadID();
if(id == 0x00313139)
{
GT911_ReadMaxXY(&MaxX, &MaxY);//读取屏幕宽度、高度
gt911.width = MaxX;
gt911.height = MaxY;
if (MaxX == 480 && MaxY == 272){printf("检测到4.3寸电容触摸屏GT911(0x28) 480x272\r\n");}
else{printf("检测到7.0寸电容触摸屏GT911(0x28) 800x480\r\n");}
return 0;
}
else
{
printf("检测到7.0寸电容触摸屏GT811(0x28) 800x480\r\n");
return -1;
}
}
HAL_Delay(10);
}
printf("未识别出显示模块\r\n");
return -1;
}
/****************************************************
@function:配置触摸芯片初始化
@param:void
@return:-1--识别失败,0--成功
@note:调用此函数初始化触摸芯片需初始化IIC引脚i2c_Init()
****************************************************/
int GT911_Init(void)
{
gt911.Enable = 0;
i2c_Init();//主函数中已经初始化
GT911_InitPin();
if(GT911_DetectID() < 0)return -1;
gt911.Enable = 1;
return 0;
}
/****************************************************
@function:判断触摸按下
@param:void
@return:0表示无触笔按下,1表示有触笔按下
@note:
****************************************************/
static uint8_t GT911_PenInt(void)
{
//if(HAL_GPIO_ReadPin(TP_INT_GPIO_PORT,TP_INT_PIN) == GPIO_PIN_RESET)return 1;
if ((TP_INT_GPIO_PORT->IDR & TP_INT_PIN) == 0)return 1;
return 0;
}
/****************************************************
@function:读取触摸点
@param:x,y--点
@return:-1--参数错误,0--成功
@note:读取GT911触摸数据;读取全部8个点的数据,需要 720us左右
****************************************************/
static void GT911_TouchPointRead(uint16_t *x,uint16_t *y)
{
uint8_t clear_flag = 0;
uint8_t buf[48];//一个点8个字节,5点*8=40
GT911_ReadReg(GT911_READ_XY_REG, buf, 8);
GT911_WriteReg(GT911_READ_XY_REG, &clear_flag, 1);//读完坐标后必须写0清除
//TouchpointFlag = buf[0];
//Touchkeystate = buf[1];
*x = ((uint16_t)buf[3] << 8) + buf[2];
*y = ((uint16_t)buf[5] << 8) + buf[4];
//p = ((uint16_t)buf[7] << 8) + buf[6];
}
/****************************************************
@function:触摸芯片扫描
@param:(x,y)--点
@return:-1--无触摸,0--触摸
@note:只读取一个点,建议10ms执行一次
注意:此函数未处理重复触摸点,尤其是在两次点击相同像素点的情况下,但是概率不大
****************************************************/
int GT911_Scan(uint16_t *x,uint16_t *y)
{
static uint16_t xlast = 0,ylast = 0;
if(!gt911.Enable)return -1;
if(!GT911_PenInt())return -1;//没有触摸
GT911_TouchPointRead(x,y);
if((*x == xlast) && (*y == ylast))return -1;//移除重复点
xlast = *x;
ylast = *y;
return 0;
}
i2c_driver.h
#ifndef _i2c_driver_H_
#define _i2c_driver_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "stdint.h"
#define I2C_WR 0 /* 写控制bit */
#define I2C_RD 1 /* 读控制bit */
void i2c_Init(void);
void i2c_Start(void);
void i2c_Stop(void);
void i2c_SendByte(uint8_t _ucByte);
uint8_t i2c_ReadByte(void);
uint8_t i2c_WaitAck(void);
void i2c_Ack(void);
void i2c_NAck(void);
uint8_t i2c_CheckDevice(uint8_t _Address);
#ifdef __cplusplus
}
#endif
#endif
i2c_driver.c
/**********************************************************************
*file:GPIO引脚模拟IIC文件
*author:残梦
*versions:V1.1
*date:2023.07.30
*note:
**********************************************************************/
#include "i2c_driver.h"
#include "delay_driver.h"
#include "gpio.h"
//宏定义
/* 定义I2C总线连接的GPIO端口, 用户只需要修改下面4行代码即可任意改变SCL和SDA的引脚 */
#define I2C_SCL_GPIO GPIOB /* 连接到SCL时钟线的GPIO */
#define I2C_SDA_GPIO GPIOB /* 连接到SDA数据线的GPIO */
#define I2C_SCL_PIN GPIO_PIN_6 /* 连接到SCL时钟线的GPIO */
#define I2C_SDA_PIN GPIO_PIN_7 /* 连接到SDA数据线的GPIO */
#define ALL_I2C_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
/* 定义读写SCL和SDA的宏 */
#define I2C_SCL_1() I2C_SCL_GPIO->BSRR = I2C_SCL_PIN /* SCL = 1 */
#define I2C_SCL_0() I2C_SCL_GPIO->BSRR = ((uint32_t)I2C_SCL_PIN << 16U)/* SCL = 0 */
#define I2C_SDA_1() I2C_SDA_GPIO->BSRR = I2C_SDA_PIN /* SDA = 1 */
#define I2C_SDA_0() I2C_SDA_GPIO->BSRR = ((uint32_t)I2C_SDA_PIN << 16U)/* SDA = 0 */
#define I2C_SDA_READ() ((I2C_SDA_GPIO->IDR & I2C_SDA_PIN) != 0) /* 读SDA口线状态 */
#define I2C_SCL_READ() ((I2C_SCL_GPIO->IDR & I2C_SCL_PIN) != 0) /* 读SCL口线状态 */
static void i2c_Delay(void);
/****************************************************
@function:配置I2C总线的GPIO,采用模拟IO的方式实现
@param:void
@return:void
@note:
****************************************************/
void i2c_Init(void)
{
GPIO_InitTypeDef gpio_init;
/* 第1步:打开GPIO时钟 */
ALL_I2C_GPIO_CLK_ENABLE();
gpio_init.Mode = GPIO_MODE_OUTPUT_OD; /* 设置开漏输出 */
gpio_init.Pull = GPIO_NOPULL; /* 上下拉电阻不使能 */
gpio_init.Speed = GPIO_SPEED_FREQ_LOW; // GPIO_SPEED_FREQ_HIGH; /* GPIO速度等级 */
gpio_init.Pin = I2C_SCL_PIN;
HAL_GPIO_Init(I2C_SCL_GPIO, &gpio_init);
gpio_init.Pin = I2C_SDA_PIN;
HAL_GPIO_Init(I2C_SDA_GPIO, &gpio_init);
/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
i2c_Stop();
}
/****************************************************
@function:I2C总线位延迟,最快400KHz
@param:void
@return:void
@note:
****************************************************/
static void i2c_Delay(void)
{
delay_us(2);
}
/****************************************************
@function:发起I2C总线启动信号
@param:void
@return:void
@note:
****************************************************/
void i2c_Start(void)
{
/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
I2C_SDA_1();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_0();
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}
/****************************************************
@function:发起I2C总线停止信号
@param:void
@return:void
@note:
****************************************************/
void i2c_Stop(void)
{
/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
I2C_SDA_0();
i2c_Delay();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_1();
i2c_Delay();
}
/****************************************************
@function:向I2C总线设备发送8bit数据
@param:_ucByte : 等待发送的字节
@return:void
@note:
****************************************************/
void i2c_SendByte(uint8_t _ucByte)
{
uint8_t i;
/* 先发送字节的高位bit7 */
for (i = 0; i < 8; i++)
{
if (_ucByte & 0x80)
{
I2C_SDA_1();
}
else
{
I2C_SDA_0();
}
i2c_Delay();
I2C_SCL_1();
i2c_Delay();
I2C_SCL_0();
I2C_SCL_0(); /* 2019-03-14 针对GT811电容触摸,添加一行,相当于延迟几十ns */
if (i == 7)
{
I2C_SDA_1(); // 释放总线
}
_ucByte <<= 1; /* 左移一个bit */
}
}
/****************************************************
@function:从I2C总线设备读取8bit数据
@param:void
@return:读到的数据
@note:
****************************************************/
uint8_t i2c_ReadByte(void)
{
uint8_t i;
uint8_t value;
/* 读到第1个bit为数据的bit7 */
value = 0;
for (i = 0; i < 8; i++)
{
value <<= 1;
I2C_SCL_1();
i2c_Delay();
if (I2C_SDA_READ())
{
value++;
}
I2C_SCL_0();
i2c_Delay();
}
return value;
}
/****************************************************
@function:产生一个时钟,并读取器件的ACK应答信号
@param:void
@return:返回0表示正确应答,1表示无器件响应
@note:
****************************************************/
uint8_t i2c_WaitAck(void)
{
uint8_t re;
I2C_SDA_1(); /* CPU释放SDA总线 */
i2c_Delay();
I2C_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
i2c_Delay();
if (I2C_SDA_READ()) /* CPU读取SDA口线状态 */
{
re = 1;
}
else
{
re = 0;
}
I2C_SCL_0();
i2c_Delay();
return re;
}
/****************************************************
@function:产生一个ACK信号
@param:void
@return:void
@note:
****************************************************/
void i2c_Ack(void)
{
I2C_SDA_0(); /* CPU驱动SDA = 0 */
i2c_Delay();
I2C_SCL_1(); /* CPU产生1个时钟 */
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
I2C_SDA_1(); /* CPU释放SDA总线 */
i2c_Delay();
}
/****************************************************
@function:产生1个NACK信号
@param:void
@return:void
@note:
****************************************************/
void i2c_NAck(void)
{
I2C_SDA_1(); /* CPU驱动SDA = 1 */
i2c_Delay();
I2C_SCL_1(); /* CPU产生1个时钟 */
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}
/****************************************************
@function:检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
@param:_Address:设备的I2C总线地址
@return:返回值 0 表示正确, 返回1表示未探测到
@note:
****************************************************/
uint8_t i2c_CheckDevice(uint8_t _Address)
{
uint8_t ucAck;
if (I2C_SDA_READ() && I2C_SCL_READ())
{
i2c_Start(); /* 发送启动信号 */
/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
i2c_SendByte(_Address | I2C_WR);
ucAck = i2c_WaitAck(); /* 检测设备的ACK应答 */
i2c_Stop(); /* 发送停止信号 */
return ucAck;
}
return 1; /* I2C总线异常 */
}
delay_driver.h
#ifndef _delay_driver_H_
#define _delay_driver_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "stdlib.h"
void delay_Init(void);
void delay_us(unsigned int us);
#ifdef __cplusplus
}
#endif
#endif
delay_driver.h
/**********************************************************************
*file:微秒级精确延时
*author:残梦
*versions:V1.0
*date:2023.10.17
*note:基础时基是0.1us
1、修改dDelayTIM和dDelayTIM_Handle
配置定时器参数参考delay_Init()
**********************************************************************/
#include "delay_driver.h"
#include "tim.h"
#define dDelayTIM TIM24
#define dDelayTIM_Handle htim24
#define dTIM_Bit (32) //定时器位数;32位定时器时0xFFFFFFFF,16位定时器0xFFFF
#define dTIM_Period_MAX ((uint32_t )((dTIM_Bit == 16)?0xFFFF:0xFFFFFFFF))
//TIM_HandleTypeDef htim24;//CubeMx配置了,就不重复定义
/******************************
@function:初始化延时
@param:void
@return:void
@remark:CubeMx配置了,就不重复配置
******************************/
void delay_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
dDelayTIM_Handle.Instance = dDelayTIM;
dDelayTIM_Handle.Init.Prescaler = 275-1;
dDelayTIM_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
dDelayTIM_Handle.Init.Period = dTIM_Period_MAX;
dDelayTIM_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
dDelayTIM_Handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&dDelayTIM_Handle) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&dDelayTIM_Handle, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&dDelayTIM_Handle, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
/******************************
@function:us延时
@param:us--待延时的时间
@return:void
@remark:
******************************/
void delay_us(uint32_t us)
{
if(!us){return;}
us = (us > (dTIM_Period_MAX))?dTIM_Period_MAX:us;
//us *= 1;//基础是1us
dDelayTIM_Handle.Instance->CNT = 0;
HAL_TIM_Base_Start(&dDelayTIM_Handle);
while(dDelayTIM_Handle.Instance->CNT < us);
HAL_TIM_Base_Stop(&dDelayTIM_Handle);
}
该屏幕LCD驱动说明:
在使用DMA2D刷新SDRAM数据时应根据自己内存区属性是否添加CACHE更新
SCB_CleanInvalidateDCache();//注意,此处需要让Cache内容更新
如H7开启MPU和Cache后使用0x24000000做数显,配置为透写或回写,不添加此句更新cache就会出现随机花屏
填充函数和复制颜色块函数文章来源:https://www.toymoban.com/news/detail-752811.html
/****************************************************
@function:通过DMA2D对于指定区域进行颜色填充(固定颜色)
@param:
LayerIndex 图层
pDst 颜色数据目的地址
xSize 要复制区域的X轴大小,即每行像素数
ySize 要复制区域的Y轴大小,即行数
OffLine 前景层图像的行偏移
ColorIndex 要填充的颜色值
@return:void
@note:
****************************************************/
static void lcd_base_DMA2D_FillBuffer(uint32_t LayerIndex, void * pDst, uint32_t xSize, uint32_t ySize, uint32_t OffLine, uint32_t ColorIndex)
{
uint32_t PixelFormat;
PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
/* 颜色填充 */
DMA2D->CR = 0x00030000UL | (1 << 9);
DMA2D->OCOLR = ColorIndex;
/* 设置填充的颜色目的地址 */
DMA2D->OMAR = (uint32_t)pDst;
/* 目的行偏移地址 */
DMA2D->OOR = OffLine;
/* 设置颜色格式 */
DMA2D->OPFCCR = PixelFormat;
/* 设置填充大小 */
DMA2D->NLR = (uint32_t)(xSize << 16) | (uint16_t)ySize;
SCB_CleanInvalidateDCache();//注意,此处需要让Cache内容更新
DMA2D->CR |= DMA2D_CR_START;
/* 等待DMA2D传输完成 */
while (DMA2D->CR & DMA2D_CR_START)
{
}
}
/**********************************************************************************************************
* 函 数 名: _DMA2D_Copy
* 功能说明: 通过DMA2D从前景层复制指定区域的颜色数据到目标区域
* 形 参: pSrc 颜色数据源地址
* pDst 颜色数据目的地址
* xSize 目的区域的X轴大小,即每行像素数
* ySize 目的区域的Y轴大小,即行数
* OffLineSrc 前景层图像的行偏移
* OffLineDst 输出的行偏移
* PixelFormat 目标区颜色格式
* 返 回 值: 无
**********************************************************************************************************/
static void lcd_base_DMA2D_Copy(void * pSrc, void * pDst, uint32_t xSize, uint32_t ySize, uint32_t OffLineSrc, uint32_t OffLineDst, uint32_t PixelFormat)
{
/* DMA2D采用存储器到存储器模式, 这种模式是前景层作为DMA2D输入 */
DMA2D->CR = 0x00000000UL | (1 << 9);
DMA2D->FGMAR = (uint32_t)pSrc;
DMA2D->OMAR = (uint32_t)pDst;
DMA2D->FGOR = OffLineSrc;
DMA2D->OOR = OffLineDst;
/* 前景层和输出区域都采用的RGB565颜色格式 */
DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_RGB565;
DMA2D->OPFCCR = LTDC_PIXEL_FORMAT_RGB565;
DMA2D->NLR = (uint32_t)(xSize << 16) | (uint16_t)ySize;
SCB_CleanInvalidateDCache();//注意,此处需要让Cache内容更新
/* 启动传输 */
DMA2D->CR |= DMA2D_CR_START;
/* 等待DMA2D传输完成 */
while (DMA2D->CR & DMA2D_CR_START) {}
}
/****************************************************
@function:通过DMA2D对于指定区域进行颜色复制(图像复制)
@param:
LayerIndex 图层
pDst 颜色数据目的地址
xSize 要复制区域的X轴大小,即每行像素数
ySize 要复制区域的Y轴大小,即行数
OffLine 前景层图像的行偏移
ColorIndex 要填充的颜色值
@return:void
@note:只支持横屏
****************************************************/
void lcd_base_DMA2D_CopyBuffer(uint16_t x,uint16_t y,uint32_t xSize, uint32_t ySize,void * color)
{
if((color == NULL) || (xSize == 0) || (ySize == 0))return;
lcd_base_DMA2D_Copy((uint32_t *)color, /* 位图地址 */
(uint32_t *)(s_CurrentFrameBuffer + g_LcdWidth*y*2 + x*2), /* 显示起始地址(328, 20) */
xSize, /* 位图长 */
ySize, /* 位图高 */
0, /* 位图行偏移 */
g_LcdWidth-xSize, /* 目标区行偏移 */
LTDC_PIXEL_FORMAT_RGB565); /* 目标区颜色格式 */
}
完整工程:(最新)
链接:https://pan.baidu.com/s/1I64wD4Ft7PBI0cIogGp45A
提取码:qpxx文章来源地址https://www.toymoban.com/news/detail-752811.html
到了这里,关于21.1 stm32使用LTDC驱动LCD--配置说明的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!