一、TFT-LCD簡介
TFT-LCD(Thin Film Transistor-Liquid Crystal Display) 即薄膜晶體管液晶顯示器。TFT-LCD 與無源 TN-LCD、 STN-LCD 的簡單矩陣不同,它在液晶顯示屏的每一個象素上都設(shè)置有一個薄膜晶體管(TFT),可有效地克服非選通時的串?dāng)_,使顯示液晶屏的靜態(tài)特性與掃描線數(shù)無關(guān),因此大大提高了圖像質(zhì)量。 TFT-LCD 也被叫做真彩液晶顯示器。
雖然TFT-LCD被統(tǒng)稱為LCD,不過它是種主動式矩陣LCD,被應(yīng)用在電視、平面顯示器及投影機上。
1.1 電阻式觸摸屏檢測原理
電阻式的觸摸屏結(jié)構(gòu)如下圖示,它主要由表面硬涂層、兩個ITO層、間隔點以及玻璃底層構(gòu)成,這些結(jié)構(gòu)層都是透明的,整個觸摸屏覆蓋在液晶面板上,透過觸摸屏可看到液晶面板。表面涂層起到保護作用,玻璃底層起承載的作用,而兩個ITO層是觸摸屏的關(guān)鍵結(jié)構(gòu),它們是涂有銦錫金屬氧化物的導(dǎo)電層。兩個ITO層之間使用間隔點使兩層分開,當(dāng)觸摸屏表面受到壓力時,表面彎曲使得上層ITO與下層ITO接觸,在觸點處連通電路。

兩個ITO涂層的兩端分別引出X-、X+、Y-、Y+四個電極,這是電阻屏最常見的四線結(jié)構(gòu),通過這些電極,外部電路向這兩個涂層可以施加勻強電場或檢測電壓。

當(dāng)觸摸屏被按下時,兩個 ITO 層相互接觸,從觸點處把 ITO 層分為兩個電阻,且由于 ITO 層均勻?qū)щ?,兩個電阻的大小與觸點離兩電極的距離成比例關(guān)系,利用這個特性,可通過以下過程來檢測坐標(biāo),這也正是電阻觸摸屏名稱的由來。

- 計算 X 坐標(biāo)時,在 X+ 電極施加驅(qū)動電壓 Vref,X-極接地,所以 X+ 與 X-處形成了勻強電場,而觸點處的電壓通過 Y+ 電極采集得到,由于 ITO 層均勻?qū)щ姡|點電壓與 Vref 之比等于觸點 X 坐標(biāo)與屏寬度之比,從而:
- 計算 Y 坐標(biāo)時,在 Y+ 電極施加驅(qū)動電壓 Vref,Y-極接地,所以 Y+ 與 Y-處形成了勻強電場,而觸點處的電壓通過 X+ 電極采集得到,由于 ITO 層均勻?qū)щ?,觸點電壓與 Vref 之比等于觸點 Y 坐標(biāo)與屏高度之比,從而:
1.2 XPT2046電阻觸摸屏控制器簡介
為了方便檢測觸摸的坐標(biāo),一些芯片廠商制作了電阻屏專用的控制芯片,控制上述采集過程、采集電壓,外部微控制器直接與觸摸控制芯片通訊直接獲得觸點的電壓或坐標(biāo)。野火 3.2 寸電阻觸摸屏就是采用 XPT2046 芯片作為觸摸控制芯片,XPT2046 芯片控制 4 線電阻觸摸屏,采用 SPI 模式進(jìn)行通訊,內(nèi)含 12 位分辨率 125KHz 轉(zhuǎn)換速率逐步逼近型 A/D 轉(zhuǎn)換器。XPT2046引腳圖(TSSOP-16封裝)及引腳說明如下圖示:


二、FSMC簡介
FSMC(Flexible Static Memory Controller),譯為靈活的靜態(tài)存儲控制器。STM32F1 系列芯片使用 FSMC 外設(shè)來管理擴展的存儲器,它可以用于驅(qū)動包括 SRAM、NOR FLASH 以及 NAND FLSAH 類型的存儲器,不能驅(qū)動如 SDRAM 這種動態(tài)的存儲器而在 STM32F429 系列的控制器中,它具有 FMC 外設(shè),支持控制 SDRAM 存儲器。
由于 FSMC 外設(shè)可以用于控制擴展的外部存儲器,而 MCU 對液晶屏的操作實際上就是把顯示數(shù)據(jù)寫入到顯存中,與控制存儲器非常類似,且 8080 接口的通訊時序完全可以使用 FSMC 外設(shè)產(chǎn)生,因而非常適合使用 FSMC 控制液晶屏。


三、引腳確定

四、新建工程
1. 打開 STM32CubeMX 軟件,點擊“新建工程”

2. 選擇 MCU 和封裝

3. 配置時鐘
RCC 設(shè)置,選擇 HSE(外部高速時鐘) 為 Crystal/Ceramic Resonator(晶振/陶瓷諧振器)

選擇 Clock Configuration,配置系統(tǒng)時鐘 SYSCLK 為 72MHz
修改 HCLK 的值為 72 后,輸入回車,軟件會自動修改所有配置

4. 配置調(diào)試模式
非常重要的一步,否則會造成第一次燒錄程序后續(xù)無法識別調(diào)試器
SYS 設(shè)置,選擇 Debug 為 Serial Wire

五、FSMC
5.1 參數(shù)配置

在
Connectivity 中選擇 FSMC 設(shè)置,并在 NOR Flash/PSRAM/SRAM/ROM/LCD 1 中選擇 NE1 Chip Select 片選選擇原理圖中的片選引腳NE1【選擇Bank1的第一區(qū),是根據(jù)原理圖的映射管腳進(jìn)行選擇的,這里選擇不同區(qū)對應(yīng)的引腳是不同的】
FSMC_NE 是用于控制存儲器芯片的片選控制信號線,STM32 具有 FSMC_NE1/2/3/4 號引腳,不同的引腳對應(yīng) STM32 內(nèi)部不同的地址區(qū)域。例如,當(dāng) STM32 訪問 0x68000000-0x6BFFFFFF 地址空間時,F(xiàn)SMC_NE3 引腳會自動設(shè)置為低電平,由于它一般連接到外部存儲器的片選引腳且低電平有效,所以外部存儲器的片選被使能,而訪問 0x60000000-
0x63FFFFFF 地址時,F(xiàn)SMC_NE1 會輸出低電平。當(dāng)使用不同的 FSMC_NE 引腳連接外部存儲器時,STM32 訪問外部存儲的地址不一樣,從而達(dá)到控制多個外部存儲器芯片的目的。
-
Memory type(設(shè)置要控制的存儲器類型): 選擇
LCD InterfaceLCD接口 -
LCD Register Select(RS引腳): 選擇
A16,RS腳也就是命令/數(shù)據(jù)選擇位,同樣是根據(jù)原理圖得知這里應(yīng)該選擇A16
-
Data(設(shè)置要控制的存儲器的數(shù)據(jù)寬度): 選擇
16 bits很明顯從原理圖看出有16個數(shù)據(jù)引腳,這里選擇16bits就好
在 NOR/PSRAM 1 進(jìn)行具體參數(shù)配置。

NOR/PSRAM control:
-
Write operation(設(shè)置是否寫使能): 選擇
Enabled,禁止寫使能的話 FSMC 只能從存儲器中讀取數(shù)據(jù),不能寫入。 -
Extended mode(設(shè)置是否使用擴展模式): 選擇
Enabled,在非擴展模式下,對存儲器讀寫的時序都只使用 FSMC_BCR 寄存器中的配;在擴展模式下,對存儲器的讀寫時序可以分開配置,讀時序使用 FSMC_BCR 寄存器,寫時序使用 FSMC_BWTR 寄存器的配置。
5.1.1 FSMC讀時序配置
這里引入一個基本概念:
HCLK周期:
按STM32F103的默認(rèn)配置,HCLK的時鐘頻率為72MHz,即一個 THCLK 為 1/72us=0.0138us=13.8us。
NOR/PSRAM timing(FSMC讀時序配置):
-
Address setup time in HCLK clock cycles(地址建立時間): 填
0 -
Data setup time in HCLK clock cycles(數(shù)據(jù)建立時間): 填
26
根據(jù)ILI9341時序配置FSMC讀時序
NEx片選后,NOE要保持一段時間的高電平,這個時間就是ADDSET地址建立時間(通過寄存器FMC_BTRx可配置)。
之后NOE變?yōu)榈碗娖?,讀使能。低電平保持的時間由DATAST數(shù)據(jù)建立時間(通過寄存器FMC_BTRx可配置)決定。
tast:
tast 表示地址建立時間,最小為0ns
由時序圖可以知道,F(xiàn)SMC在ADDSET周期之后,進(jìn)入DATAST周期之后將會進(jìn)行數(shù)據(jù)采樣。
所以我們設(shè)置(ADDSET)HCLK的時間要大于等于tast地址建立時間。
(ADDSET)HCLK >= 0ns,(0)·13.8 = 0ns,所以ADDSET可以設(shè)置為0就可保證滿足最小tast地址建立時間。trdlfm:
trdlfm 表示讀取數(shù)據(jù)低電平的時間,最小為355ns
ILI9341時序圖沒有給出ILI9341操作數(shù)據(jù)線傳輸被讀取的數(shù)據(jù)時的相關(guān)信息,我們最好做到滿足其讀取數(shù)據(jù)低電平的最小時間。
當(dāng)然不做到也行,影響不大,只要在FSMC在DATAST的這個周期的數(shù)據(jù)采樣中獲取所有的要訪問的數(shù)據(jù)就行。
(DATAST)HCLK >355ns,(26) * 13.8 = 358.8ns>355ns,所以DATAST設(shè)置為26。
-
Bus turn around time in HCLK clock cycles(總線轉(zhuǎn)換周期): 填
0,僅適用于總線復(fù)用模式的NOR Flash操作,所以這里設(shè)0。 -
Access mode(存儲器訪問模式): 選
A,LCD控制器使用 Mode A ,該模式用來控制SRAM/PSRAM且OE會翻轉(zhuǎn)??刂飘惒?NOR FLASH 時使用 B 模式。
5.1.2 FSMC寫時序配置
NOR/PSRAM timing for write accesses(FSMC寫時序配置):
-
Extended address setup time(地址建立時間): 填
0 -
Extended data setup time(數(shù)據(jù)建立時間): 填
1
根據(jù)ILI9341時序配置FSMC寫時序
NEx片選后,NWE要保持一段時間的高電平,這個時間就是ADDSET地址建立時間(通過寄存器FMC_BTRx可配置)。
之后NWE變?yōu)榈碗娖剑瑢懯鼓?。低電平保持的時間由DATAST數(shù)據(jù)建立時間(通過寄存器FMC_BTRx可配置)決定。
tast:
tast 表示地址建立時間,最小為0ns
由時序圖可以知道,F(xiàn)SMC在ADDSET周期之后,進(jìn)入DATAST周期之后將會進(jìn)行數(shù)據(jù)采樣。
所以我們設(shè)置(ADDSET)HCLK的時間要大于等于tast地址建立時間。
(ADDSET)HCLK >= 0ns,(0)·13.8 = 0ns,所以ADDSET可以設(shè)置為0就可保證滿足最小tast地址建立時間。tdst、tdht:
tdst:數(shù)據(jù)設(shè)置時間最小是10ns,在這個周期內(nèi)WRX線處于低電平。
tdht:數(shù)據(jù)保持時間,與 twrh寫控制高電平的最小時間相同,是10ns,在這個周期內(nèi)WRX線處于高電平。
觀察時序圖,我們設(shè)置 tdst數(shù)據(jù)設(shè)置時間 為1HCLK(13.8>10)就能滿足數(shù)據(jù)設(shè)置最小時間的要求,我們不需要考慮tdht數(shù)據(jù)保持時間(看上面模式B時序圖,NWE變成高電平后,會持續(xù)1HCLK=13.8ns,默認(rèn)滿足tdht了)。
故我們只需考慮數(shù)據(jù)建立周期 DATAST 要大于10ns就行。
(DATAST)HCLK > 10ns,13.8>10 ,故DATAST 至少設(shè)置為1
-
Extended bus turn around time(總線轉(zhuǎn)換周期): 填
0 -
Extended access mode(存儲器訪問模式): 選
A
六、設(shè)置背光和復(fù)位引腳
在 System Core 中選擇 GPIO 設(shè)置。


在右邊圖中找到 LCD 背光和復(fù)位對應(yīng)引腳,選擇
GPIO_Output。
在
GPIO output level 中選擇 Low 輸出低電平點亮,可以添加自定義標(biāo)簽(這樣生成代碼也會根據(jù)標(biāo)簽設(shè)置引腳的宏定義)。
七、模擬SPI
由于野火指南者開發(fā)板上畫的是這幾個引腳,所以這里只能用模擬SPI。實際項目可使用硬件SPI接口。

在
System Core 中選擇 GPIO 設(shè)置。
配置以下 5 個引腳:
XPT2046_SPI_CSXPT2046_SPI_CLKXPT2046_SPI_MOSIXPT2046_SPI_MISO-
XPT2046_SPI_PENIRQ筆接觸中斷引腳
八、生成代碼
輸入項目名和項目路徑

選擇應(yīng)用的 IDE 開發(fā)環(huán)境 MDK-ARM V5

每個外設(shè)生成獨立的
’.c/.h’ 文件不勾:所有初始化代碼都生成在 main.c
勾選:初始化代碼生成在對應(yīng)的外設(shè)文件。 如 GPIO 初始化代碼生成在 gpio.c 中。

點擊 GENERATE CODE 生成代碼

九、修改代碼優(yōu)化級別
STM32CubeMX生成的代碼默認(rèn)優(yōu)化級別為Level 3,在此優(yōu)化級別下編譯無誤下載到開發(fā)板后,LCD屏不能正常運行;將優(yōu)化級別調(diào)整到Level 0編譯下載后,LCD屏能夠正常運行讀取到ID。

KEIL5中C/C++優(yōu)化等級介紹:
-O0:最少的優(yōu)化,可以最大程度上配合產(chǎn)生代碼調(diào)試信息,可以在任何代碼行打斷點,特別是死代碼處。
-O1:有限的優(yōu)化,去除無用的inline和無用的static函數(shù)、死代碼消除等,在影響到調(diào)試信息的地方均不進(jìn)行優(yōu)化。在適當(dāng)?shù)拇a體積和充分的調(diào)試之間平衡,代碼編寫階段最常用的優(yōu)化等級。
-O2:高度優(yōu)化,調(diào)試信息不友好,有可能會修改代碼和函數(shù)調(diào)用執(zhí)行流程,自動對函數(shù)進(jìn)行內(nèi)聯(lián)等。
-O3:最大程度優(yōu)化,產(chǎn)生極少量的調(diào)試信息。會進(jìn)行更多代碼優(yōu)化,例如循環(huán)展開,更激進(jìn)的函數(shù)內(nèi)聯(lián)等。
十、添加LCD驅(qū)動文件
鏈接:https://pan.baidu.com/s/1sNSlc5mRcfBRF6XgMkwXZA?pwd=kolq 提取碼:kolq
加入野火的LCD顯示驅(qū)動文件,屏蔽 ILI9341_Init() 中 GPIO 初始化 ILI9341_GPIO_Config() 和 FSMC配置 ILI9341_FSMC_Config(),屏蔽 XPT2046_Init() ,因為 STM32CubeMX 工程在 main.c 里已經(jīng)配置了。

十一、修改main.c
加入 ILI9341_Init() LCD屏驅(qū)動初始化后,進(jìn)行 Palette_Init() 繪制觸摸畫板界面,隨后在while循環(huán)里面檢測觸摸 XPT2046_TouchEvenHandler()。
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "bsp_ili9341_lcd.h"
#include "bsp_xpt2046_lcd.h"
#include "palette.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;
SRAM_HandleTypeDef hsram1;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_FSMC_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_FSMC_Init();
/* USER CODE BEGIN 2 */
ILI9341_Init();
//其中0、3、5、6 模式適合從左至右顯示文字,
//不推薦使用其它模式顯示文字 其它模式顯示文字會有鏡像效果
//其中 6 模式為大部分液晶例程的默認(rèn)顯示方向
ILI9341_GramScan(3);
//繪制觸摸畫板界面
Palette_Init(LCD_SCAN_MODE);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//觸摸檢測函數(shù),本函數(shù)至少10ms調(diào)用一次
XPT2046_TouchEvenHandler();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief USART1 Initialization Function
* @param None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel4_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
/* DMA1_Channel5_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOE, XPT2046_SPI_MOSI_Pin|LCD_RST_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOD, LCD_BL_Pin|XPT2046_SPI_CS_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(XPT2046_SPI_CLK_GPIO_Port, XPT2046_SPI_CLK_Pin, GPIO_PIN_SET);
/*Configure GPIO pins : XPT2046_SPI_MOSI_Pin XPT2046_SPI_CLK_Pin */
GPIO_InitStruct.Pin = XPT2046_SPI_MOSI_Pin|XPT2046_SPI_CLK_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/*Configure GPIO pins : XPT2046_SPI_MISO_Pin XPT2046_PENIRQ_Pin */
GPIO_InitStruct.Pin = XPT2046_SPI_MISO_Pin|XPT2046_PENIRQ_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/*Configure GPIO pin : LCD_BL_Pin */
GPIO_InitStruct.Pin = LCD_BL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LCD_BL_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : XPT2046_SPI_CS_Pin */
GPIO_InitStruct.Pin = XPT2046_SPI_CS_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(XPT2046_SPI_CS_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : LCD_RST_Pin */
GPIO_InitStruct.Pin = LCD_RST_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LCD_RST_GPIO_Port, &GPIO_InitStruct);
}
/* FSMC initialization function */
static void MX_FSMC_Init(void)
{
/* USER CODE BEGIN FSMC_Init 0 */
/* USER CODE END FSMC_Init 0 */
FSMC_NORSRAM_TimingTypeDef Timing = {0};
FSMC_NORSRAM_TimingTypeDef ExtTiming = {0};
/* USER CODE BEGIN FSMC_Init 1 */
/* USER CODE END FSMC_Init 1 */
/** Perform the SRAM1 memory initialization sequence
*/
hsram1.Instance = FSMC_NORSRAM_DEVICE;
hsram1.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
/* hsram1.Init */
hsram1.Init.NSBank = FSMC_NORSRAM_BANK1;
hsram1.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
hsram1.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
hsram1.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
hsram1.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
hsram1.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
hsram1.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
hsram1.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
hsram1.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
hsram1.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
hsram1.Init.ExtendedMode = FSMC_EXTENDED_MODE_ENABLE;
hsram1.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
hsram1.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
/* Timing */
Timing.AddressSetupTime = 0;
Timing.AddressHoldTime = 15;
Timing.DataSetupTime = 26;
Timing.BusTurnAroundDuration = 0;
Timing.CLKDivision = 16;
Timing.DataLatency = 17;
Timing.AccessMode = FSMC_ACCESS_MODE_A;
/* ExtTiming */
ExtTiming.AddressSetupTime = 0;
ExtTiming.AddressHoldTime = 15;
ExtTiming.DataSetupTime = 1;
ExtTiming.BusTurnAroundDuration = 0;
ExtTiming.CLKDivision = 16;
ExtTiming.DataLatency = 17;
ExtTiming.AccessMode = FSMC_ACCESS_MODE_A;
if (HAL_SRAM_Init(&hsram1, &Timing, &ExtTiming) != HAL_OK)
{
Error_Handler( );
}
/** Disconnect NADV
*/
__HAL_AFIO_FSMCNADV_DISCONNECTED();
/* USER CODE BEGIN FSMC_Init 2 */
/* USER CODE END FSMC_Init 2 */
}
/* USER CODE BEGIN 4 */
/**
* @brief 閲嶅畾鍚慶搴撳嚱鏁皃rintf鍒癠SARTx
* @retval None
*/
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
/**
* @brief 閲嶅畾鍚慶搴撳嚱鏁癵etchar,scanf鍒癠SARTx
* @retval None
*/
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
查看效果:

十二、工程代碼
鏈接:https://pan.baidu.com/s/1Q7Diy4oGQ_iEOrYom4UJdw?pwd=ugpp 提取碼:ugpp
十三、注意事項
用戶代碼要加在 USER CODE BEGIN N 和 USER CODE END N 之間,否則下次使用 STM32CubeMX 重新生成代碼后,會被刪除。

? 由 Leung 寫于 2022 年 1 月 22 日
? 參考:stm32學(xué)習(xí)筆記 -根據(jù)外接存儲器時序初始化FSMC結(jié)構(gòu)體
STM32CubeMX實戰(zhàn)教程(七)——TFT_LCD液晶顯示(附驅(qū)動代碼)
STM32CubeMX | 35-使用硬件FSMC驅(qū)動TFT-LCD屏幕(MCU屏,NT35510控制器)
STM32CubeMX系列|觸摸屏
















