本文講解如何配置LTDC驅(qū)動(dòng)LCD的參數(shù)配置,以及CubeMx參數(shù)配置說明
本文使用的是淘寶買的一塊帶電容觸摸的液晶顯示屏:5寸TFT液晶顯示屏高清800*480免驅(qū)40P通用RGBIPS全視角彩屏GT911
說實(shí)話,價(jià)格還是相對(duì)挺便宜的,值得入手,哈哈哈
這款屏幕采用的是RGB888格式
這里面也就是常用的引腳:
R0-R7、G0-G7、B0-B7
DCLK–時(shí)鐘線
HSYNC\VSYNC–同步線
DE–數(shù)據(jù)使能
DISP是顯示使能,控制屏幕的哈
背光是通過控制LED+\LED-的電流實(shí)現(xiàn)的
由此可以找到,LED需要流過20mA電流,下面是它的推薦電路
接著我就繪制了我的PCB,如下所示:我的是電容觸摸屏,所以電阻觸摸引腳位置懸空的
接下來,我們就來CubeMx配置LTDC驅(qū)動(dòng)它吧
CubeMx配置LTDC
硬件相關(guān)參數(shù)設(shè)置
注意:這里的HSYNC、VSYNC、DE的有效極性需要和實(shí)際相反
由此圖可以看出HSYNC、VSYNC、DE的有效極性都是高,clk的下降沿采樣
上面我的LTDC配置取得都是典型值
Pulse Width對(duì)應(yīng)的是HSW和VSW
引腳需要全部高速
然后就還有LTDC輸出給LCD的時(shí)鐘信號(hào),由上面的數(shù)據(jù)手冊(cè)給出的典型值配置25MHz,LTDC會(huì)由LTDC_PCLK引腳輸出給LCD
到此,LTDC硬件相關(guān)的參數(shù)配置完畢
LTDC圖像層配置
開啟全局中斷,并且優(yōu)先級(jí)可以設(shè)置低點(diǎn)
DMA2D在代碼里重新配置過得,可以按此設(shè)置
關(guān)于FMC的SDRAM存儲(chǔ)屬性設(shè)置可以參考如下:
調(diào)試
如何判定硬件問題:
在函數(shù)void MX_LTDC_Init(void)中的HAL_LTDC_Init()后如下處理:
如果LCD能顯示紅色說明硬件正常,否則有問題
lcd_base_backlight_set是開啟屏幕背光
完整工程下載:(舊版本,不建議使用,LCD顯示刷新沒處理后續(xù)說的cache更新)
鏈接:https://pan.baidu.com/s/1g_VezTfR_-fgqSpPFlvtqQ
提取碼:qqio
補(bǔ)充:
該屏幕GT911驅(qū)動(dòng):
需要注意的是INT引腳禁止上下拉,應(yīng)配置為浮空輸入
觸摸屏手指觸摸時(shí)會(huì)觸摸70ms左右,這段時(shí)間其實(shí)坐標(biāo)是同一點(diǎn),INT引腳持續(xù)輸出方波信號(hào)10ms間隔
所以應(yīng)采集判定坐標(biāo)避免重復(fù)點(diǎn)如下函數(shù):
/****************************************************
@function:觸摸芯片掃描
@param:(x,y)--點(diǎn)
@return:-1--無觸摸,0--觸摸
@note:只讀取一個(gè)點(diǎn),建議10ms執(zhí)行一次
注意:此函數(shù)未處理重復(fù)觸摸點(diǎn),尤其是在兩次點(diǎn)擊相同像素點(diǎn)的情況下,但是概率不大
****************************************************/
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;//移除重復(fù)點(diǎn)
xlast = *x;
ylast = *y;
return 0;
}
完整驅(qū)動(dòng)代碼:
GT911_driver.c
GT911_driver.h
i2c_driver.c//模擬IIC
i2c_driver.h
delay_driver.c//定時(shí)器微秒精確延時(shí)
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引腳會(huì)在觸摸時(shí)出現(xiàn)10ms的方波信號(hào),手指單次觸摸維持在70ms左右,
持續(xù)觸摸會(huì)一直到釋放,默認(rèn)高電平,觸摸低電平,引腳應(yīng)浮空嚴(yán)禁上下拉
**********************************************************************/
#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 /* 坐標(biāo)寄存器 */
#define GT911_CLEARBUF_REG 0x814E /* 清除坐標(biāo)寄存器 */
#define GT911_CONFIG_REG 0x8047 /* 配置參數(shù)寄存器 */
#define GT911_COMMAND_REG 0x8040 /* 實(shí)時(shí)命令 */
#define GT911_PRODUCT_ID_REG 0x8140 /* 芯片ID */
#define GT911_VENDOR_ID_REG 0x814A /* 當(dāng)前模組選項(xiàng)信息 */
#define GT911_CONFIG_VERSION_REG 0x8047 /* 配置文件版本號(hào) */
#define GT911_CONFIG_CHECKSUM_REG 0x80FF /* 配置文件校驗(yàn)碼 */
#define GT911_FIRMWARE_VERSION_REG 0x8144 /* 固件版本號(hào) */
typedef struct
{
//芯片參數(shù)
uint8_t i2cAddress;//i2c地址
uint16_t width;//屏幕寬度
uint16_t height;//屏幕高度
//用戶變量
uint8_t Enable;//使能狀態(tài),初始化成功則為1,失敗為0
}GT911_StructDef;
static GT911_StructDef gt911 = \
{
.Enable = 0,
};//觸摸屏結(jié)構(gòu)體變量
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個(gè)或連續(xù)的多個(gè)寄存器
@param: _usRegAddr : 寄存器地址
_pRegBuf : 寄存器數(shù)據(jù)緩沖區(qū)
_ucLen : 數(shù)據(jù)長度
@return:void
@note:
****************************************************/
static void GT911_WriteReg(uint16_t _usRegAddr, uint8_t *_pRegBuf, uint8_t _ucLen)
{
uint8_t i;
i2c_Start(); /* 總線開始信號(hào) */
i2c_SendByte(gt911.i2cAddress); /* 發(fā)送設(shè)備地址+寫信號(hào) */
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]); /* 寄存器數(shù)據(jù) */
i2c_WaitAck();
}
i2c_Stop(); /* 總線停止信號(hào) */
}
/****************************************************
@function:讀1個(gè)或連續(xù)的多個(gè)寄存器
@param: _usRegAddr : 寄存器地址
_pRegBuf : 寄存器數(shù)據(jù)緩沖區(qū)
_ucLen : 數(shù)據(jù)長度
@return:void
@note:
****************************************************/
static void GT911_ReadReg(uint16_t _usRegAddr, uint8_t *_pRegBuf, uint8_t _ucLen)
{
uint8_t i;
{
i2c_Start(); /* 總線開始信號(hào) */
i2c_SendByte(gt911.i2cAddress); /* 發(fā)送設(shè)備地址+寫信號(hào) */
i2c_WaitAck();
i2c_SendByte(_usRegAddr >> 8); /* 地址高8位 */
i2c_WaitAck();
i2c_SendByte(_usRegAddr); /* 地址低8位 */
i2c_WaitAck();
i2c_Start();
i2c_SendByte(gt911.i2cAddress + 0x01); /* 發(fā)送設(shè)備地址+讀信號(hào) */
i2c_WaitAck();
}
for (i = 0; i < 30; i++);
for (i = 0; i < _ucLen - 1; i++)
{
_pRegBuf[i] = i2c_ReadByte(); /* 讀寄存器數(shù)據(jù) */
i2c_Ack();
}
/* 最后一個(gè)數(shù)據(jù) */
_pRegBuf[i] = i2c_ReadByte(); /* 讀寄存器數(shù)據(jù) */
i2c_NAck();
i2c_Stop(); /* 總線停止信號(hào) */
}
/****************************************************
@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時(shí)鐘 */
TP_INT_GPIO_CLK_ENABLE();
TP_RST_GPIO_CLK_ENABLE();
/* 第2步:配置所有的按鍵GPIO為浮動(dòng)輸入模式 */
gpio_init.Mode = GPIO_MODE_INPUT; /* 設(shè)置輸入 */
gpio_init.Pull = GPIO_NOPULL; /* 浮空:嚴(yán)禁上下拉 */
gpio_init.Speed = GPIO_SPEED_FREQ_LOW; /* GPIO速度等級(jí) */
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速度等級(jí) */
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:設(shè)置GT911芯片屏幕的寬度
@param:void
@return:void
@note:
****************************************************/
uint16_t GT911_ScreenWidthGet(void)
{
return gt911.width;
}
/****************************************************
@function:設(shè)置GT911芯片屏幕的高度
@param:void
@return:void
@note:
****************************************************/
uint16_t GT911_ScreenHeigthGet(void)
{
return gt911.height;
}
/****************************************************
@function:觸摸芯片識(shí)別
@param:void
@return:-1--識(shí)別失敗,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復(fù)位就緒,才能探測GT811芯片 ID
printf("開始識(shí)別觸摸屏型號(hào)...\r\n");
address = 0x28;
for(i=0;i < 5;i++)//GT811電容觸摸板和GT911的I2C地址相同,一般就 0x28 、 0xBA 兩種,通過讀取觸摸IC的芯片ID來識(shí)別
{
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("未識(shí)別出顯示模塊\r\n");
return -1;
}
/****************************************************
@function:配置觸摸芯片初始化
@param:void
@return:-1--識(shí)別失敗,0--成功
@note:調(diào)用此函數(shù)初始化觸摸芯片需初始化IIC引腳i2c_Init()
****************************************************/
int GT911_Init(void)
{
gt911.Enable = 0;
i2c_Init();//主函數(shù)中已經(jīng)初始化
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:讀取觸摸點(diǎn)
@param:x,y--點(diǎn)
@return:-1--參數(shù)錯(cuò)誤,0--成功
@note:讀取GT911觸摸數(shù)據(jù);讀取全部8個(gè)點(diǎn)的數(shù)據(jù),需要 720us左右
****************************************************/
static void GT911_TouchPointRead(uint16_t *x,uint16_t *y)
{
uint8_t clear_flag = 0;
uint8_t buf[48];//一個(gè)點(diǎn)8個(gè)字節(jié),5點(diǎn)*8=40
GT911_ReadReg(GT911_READ_XY_REG, buf, 8);
GT911_WriteReg(GT911_READ_XY_REG, &clear_flag, 1);//讀完坐標(biāo)后必須寫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)--點(diǎn)
@return:-1--無觸摸,0--觸摸
@note:只讀取一個(gè)點(diǎn),建議10ms執(zhí)行一次
注意:此函數(shù)未處理重復(fù)觸摸點(diǎn),尤其是在兩次點(diǎn)擊相同像素點(diǎn)的情況下,但是概率不大
****************************************************/
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;//移除重復(fù)點(diǎn)
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時(shí)鐘線的GPIO */
#define I2C_SDA_GPIO GPIOB /* 連接到SDA數(shù)據(jù)線的GPIO */
#define I2C_SCL_PIN GPIO_PIN_6 /* 連接到SCL時(shí)鐘線的GPIO */
#define I2C_SDA_PIN GPIO_PIN_7 /* 連接到SDA數(shù)據(jù)線的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口線狀態(tài) */
#define I2C_SCL_READ() ((I2C_SCL_GPIO->IDR & I2C_SCL_PIN) != 0) /* 讀SCL口線狀態(tài) */
static void i2c_Delay(void);
/****************************************************
@function:配置I2C總線的GPIO,采用模擬IO的方式實(shí)現(xiàn)
@param:void
@return:void
@note:
****************************************************/
void i2c_Init(void)
{
GPIO_InitTypeDef gpio_init;
/* 第1步:打開GPIO時(shí)鐘 */
ALL_I2C_GPIO_CLK_ENABLE();
gpio_init.Mode = GPIO_MODE_OUTPUT_OD; /* 設(shè)置開漏輸出 */
gpio_init.Pull = GPIO_NOPULL; /* 上下拉電阻不使能 */
gpio_init.Speed = GPIO_SPEED_FREQ_LOW; // GPIO_SPEED_FREQ_HIGH; /* GPIO速度等級(jí) */
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);
/* 給一個(gè)停止信號(hào), 復(fù)位I2C總線上的所有設(shè)備到待機(jī)模式 */
i2c_Stop();
}
/****************************************************
@function:I2C總線位延遲,最快400KHz
@param:void
@return:void
@note:
****************************************************/
static void i2c_Delay(void)
{
delay_us(2);
}
/****************************************************
@function:發(fā)起I2C總線啟動(dòng)信號(hào)
@param:void
@return:void
@note:
****************************************************/
void i2c_Start(void)
{
/* 當(dāng)SCL高電平時(shí),SDA出現(xiàn)一個(gè)下跳沿表示I2C總線啟動(dòng)信號(hào) */
I2C_SDA_1();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_0();
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}
/****************************************************
@function:發(fā)起I2C總線停止信號(hào)
@param:void
@return:void
@note:
****************************************************/
void i2c_Stop(void)
{
/* 當(dāng)SCL高電平時(shí),SDA出現(xiàn)一個(gè)上跳沿表示I2C總線停止信號(hào) */
I2C_SDA_0();
i2c_Delay();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_1();
i2c_Delay();
}
/****************************************************
@function:向I2C總線設(shè)備發(fā)送8bit數(shù)據(jù)
@param:_ucByte : 等待發(fā)送的字節(jié)
@return:void
@note:
****************************************************/
void i2c_SendByte(uint8_t _ucByte)
{
uint8_t i;
/* 先發(fā)送字節(jié)的高位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 針對(duì)GT811電容觸摸,添加一行,相當(dāng)于延遲幾十ns */
if (i == 7)
{
I2C_SDA_1(); // 釋放總線
}
_ucByte <<= 1; /* 左移一個(gè)bit */
}
}
/****************************************************
@function:從I2C總線設(shè)備讀取8bit數(shù)據(jù)
@param:void
@return:讀到的數(shù)據(jù)
@note:
****************************************************/
uint8_t i2c_ReadByte(void)
{
uint8_t i;
uint8_t value;
/* 讀到第1個(gè)bit為數(shù)據(jù)的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:產(chǎn)生一個(gè)時(shí)鐘,并讀取器件的ACK應(yīng)答信號(hào)
@param:void
@return:返回0表示正確應(yīng)答,1表示無器件響應(yīng)
@note:
****************************************************/
uint8_t i2c_WaitAck(void)
{
uint8_t re;
I2C_SDA_1(); /* CPU釋放SDA總線 */
i2c_Delay();
I2C_SCL_1(); /* CPU驅(qū)動(dòng)SCL = 1, 此時(shí)器件會(huì)返回ACK應(yīng)答 */
i2c_Delay();
if (I2C_SDA_READ()) /* CPU讀取SDA口線狀態(tài) */
{
re = 1;
}
else
{
re = 0;
}
I2C_SCL_0();
i2c_Delay();
return re;
}
/****************************************************
@function:產(chǎn)生一個(gè)ACK信號(hào)
@param:void
@return:void
@note:
****************************************************/
void i2c_Ack(void)
{
I2C_SDA_0(); /* CPU驅(qū)動(dòng)SDA = 0 */
i2c_Delay();
I2C_SCL_1(); /* CPU產(chǎn)生1個(gè)時(shí)鐘 */
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
I2C_SDA_1(); /* CPU釋放SDA總線 */
i2c_Delay();
}
/****************************************************
@function:產(chǎn)生1個(gè)NACK信號(hào)
@param:void
@return:void
@note:
****************************************************/
void i2c_NAck(void)
{
I2C_SDA_1(); /* CPU驅(qū)動(dòng)SDA = 1 */
i2c_Delay();
I2C_SCL_1(); /* CPU產(chǎn)生1個(gè)時(shí)鐘 */
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}
/****************************************************
@function:檢測I2C總線設(shè)備,CPU向發(fā)送設(shè)備地址,然后讀取設(shè)備應(yīng)答來判斷該設(shè)備是否存在
@param:_Address:設(shè)備的I2C總線地址
@return:返回值 0 表示正確, 返回1表示未探測到
@note:
****************************************************/
uint8_t i2c_CheckDevice(uint8_t _Address)
{
uint8_t ucAck;
if (I2C_SDA_READ() && I2C_SCL_READ())
{
i2c_Start(); /* 發(fā)送啟動(dòng)信號(hào) */
/* 發(fā)送設(shè)備地址+讀寫控制bit(0 = w, 1 = r) bit7 先傳 */
i2c_SendByte(_Address | I2C_WR);
ucAck = i2c_WaitAck(); /* 檢測設(shè)備的ACK應(yīng)答 */
i2c_Stop(); /* 發(fā)送停止信號(hào) */
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:微秒級(jí)精確延時(shí)
*author:殘夢
*versions:V1.0
*date:2023.10.17
*note:基礎(chǔ)時(shí)基是0.1us
1、修改dDelayTIM和dDelayTIM_Handle
配置定時(shí)器參數(shù)參考delay_Init()
**********************************************************************/
#include "delay_driver.h"
#include "tim.h"
#define dDelayTIM TIM24
#define dDelayTIM_Handle htim24
#define dTIM_Bit (32) //定時(shí)器位數(shù);32位定時(shí)器時(shí)0xFFFFFFFF,16位定時(shí)器0xFFFF
#define dTIM_Period_MAX ((uint32_t )((dTIM_Bit == 16)?0xFFFF:0xFFFFFFFF))
//TIM_HandleTypeDef htim24;//CubeMx配置了,就不重復(fù)定義
/******************************
@function:初始化延時(shí)
@param:void
@return:void
@remark:CubeMx配置了,就不重復(fù)配置
******************************/
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延時(shí)
@param:us--待延時(shí)的時(shí)間
@return:void
@remark:
******************************/
void delay_us(uint32_t us)
{
if(!us){return;}
us = (us > (dTIM_Period_MAX))?dTIM_Period_MAX:us;
//us *= 1;//基礎(chǔ)是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驅(qū)動(dòng)說明:
在使用DMA2D刷新SDRAM數(shù)據(jù)時(shí)應(yīng)根據(jù)自己內(nèi)存區(qū)屬性是否添加CACHE更新
SCB_CleanInvalidateDCache();//注意,此處需要讓Cache內(nèi)容更新
如H7開啟MPU和Cache后使用0x24000000做數(shù)顯,配置為透寫或回寫,不添加此句更新cache就會(huì)出現(xiàn)隨機(jī)花屏
填充函數(shù)和復(fù)制顏色塊函數(shù)文章來源:http://www.zghlxwxcb.cn/news/detail-752811.html
/****************************************************
@function:通過DMA2D對(duì)于指定區(qū)域進(jìn)行顏色填充(固定顏色)
@param:
LayerIndex 圖層
pDst 顏色數(shù)據(jù)目的地址
xSize 要復(fù)制區(qū)域的X軸大小,即每行像素?cái)?shù)
ySize 要復(fù)制區(qū)域的Y軸大小,即行數(shù)
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;
/* 設(shè)置填充的顏色目的地址 */
DMA2D->OMAR = (uint32_t)pDst;
/* 目的行偏移地址 */
DMA2D->OOR = OffLine;
/* 設(shè)置顏色格式 */
DMA2D->OPFCCR = PixelFormat;
/* 設(shè)置填充大小 */
DMA2D->NLR = (uint32_t)(xSize << 16) | (uint16_t)ySize;
SCB_CleanInvalidateDCache();//注意,此處需要讓Cache內(nèi)容更新
DMA2D->CR |= DMA2D_CR_START;
/* 等待DMA2D傳輸完成 */
while (DMA2D->CR & DMA2D_CR_START)
{
}
}
/**********************************************************************************************************
* 函 數(shù) 名: _DMA2D_Copy
* 功能說明: 通過DMA2D從前景層復(fù)制指定區(qū)域的顏色數(shù)據(jù)到目標(biāo)區(qū)域
* 形 參: pSrc 顏色數(shù)據(jù)源地址
* pDst 顏色數(shù)據(jù)目的地址
* xSize 目的區(qū)域的X軸大小,即每行像素?cái)?shù)
* ySize 目的區(qū)域的Y軸大小,即行數(shù)
* OffLineSrc 前景層圖像的行偏移
* OffLineDst 輸出的行偏移
* PixelFormat 目標(biāo)區(qū)顏色格式
* 返 回 值: 無
**********************************************************************************************************/
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采用存儲(chǔ)器到存儲(chǔ)器模式, 這種模式是前景層作為DMA2D輸入 */
DMA2D->CR = 0x00000000UL | (1 << 9);
DMA2D->FGMAR = (uint32_t)pSrc;
DMA2D->OMAR = (uint32_t)pDst;
DMA2D->FGOR = OffLineSrc;
DMA2D->OOR = OffLineDst;
/* 前景層和輸出區(qū)域都采用的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內(nèi)容更新
/* 啟動(dòng)傳輸 */
DMA2D->CR |= DMA2D_CR_START;
/* 等待DMA2D傳輸完成 */
while (DMA2D->CR & DMA2D_CR_START) {}
}
/****************************************************
@function:通過DMA2D對(duì)于指定區(qū)域進(jìn)行顏色復(fù)制(圖像復(fù)制)
@param:
LayerIndex 圖層
pDst 顏色數(shù)據(jù)目的地址
xSize 要復(fù)制區(qū)域的X軸大小,即每行像素?cái)?shù)
ySize 要復(fù)制區(qū)域的Y軸大小,即行數(shù)
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, /* 目標(biāo)區(qū)行偏移 */
LTDC_PIXEL_FORMAT_RGB565); /* 目標(biāo)區(qū)顏色格式 */
}
完整工程:(最新)
鏈接:https://pan.baidu.com/s/1I64wD4Ft7PBI0cIogGp45A
提取碼:qpxx文章來源地址http://www.zghlxwxcb.cn/news/detail-752811.html
到了這里,關(guān)于21.1 stm32使用LTDC驅(qū)動(dòng)LCD--配置說明的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!