本篇詳細(xì)的記錄了如何使用STM32CubeMX配置 STM32f407ZGT6 的硬件FSMC外設(shè)驅(qū)動TFT-LCD屏幕。
硬件準(zhǔn)備
開發(fā)板
首先需要準(zhǔn)備一個開發(fā)板,這里我準(zhǔn)備的是STM32F407ZGT6的開發(fā)板。TFT-LCD
開發(fā)板底板接正點(diǎn)原子4.3寸TFT-LCD。
2.1. 什么是FSMC
FSMC全稱 Flexible static memory controller,靈活的靜態(tài)內(nèi)存控制器,顧名思義,其主要作用是:負(fù)責(zé)向外部擴(kuò)展的存儲類設(shè)備提供控制信號。
FSMC內(nèi)存控制器支持的存儲設(shè)備有:
Nor Flash、SRAM、PSRAM
Nand Flash
類SRAM設(shè)備
2.2. FSMC外設(shè)的功能框圖
2.3. 外部設(shè)備的地址映射(重點(diǎn))
從FSMC的角度來看,外部的存儲設(shè)備被分為幾個固定大小的Bank,每個bank 256 MB。
整個FSMC外設(shè)映射地址的劃分如圖:
2.3.1. Bank1
Bank1的地址空間為:0x6000 0000 - 0x6FFF FFFF,支持外接Nor Flash、PSRAM、SRAM等設(shè)備,還可以外接DM9000等類存儲設(shè)備。
整個Bank1的地址空間被劃分為四個子bank,每個子bank的大小為64MB,剛好對應(yīng)FSMC外設(shè)的地址總線(FSMC_A[0:25])有26條(2^26=64MB)。
FSMC還有兩條內(nèi)部總線ADDR[27:26],用這兩路控制片選信號,如下表:
BANK1控制時序模型
接下來講述BANK1控制外部存儲器的時序模式,BANK1又稱為Nor Flash/SRAM/PSRAM控制器,后續(xù)暫且叫它SRAM控制器。
SRAM控制器支持兩種控制模式:
同步模式
異步模式
對于異步模式,F(xiàn)SMC主要設(shè)置三個時序參數(shù):
地址建立時間:ADDSET
數(shù)據(jù)建立時間:DATASET
地址保持時間:ADDHLD
根據(jù)SRAM、PSRAM、Nor Flash的綜合特點(diǎn),F(xiàn)MC定義了四種不同的異步時序模型,如下表:
本文中控制TFT-LCD使用的就是異步ModeA時序模型。
異步ModeA時序模型
模式A時序模型的優(yōu)勢在于:支持獨(dú)立的讀寫時序控制。這一點(diǎn)對于控制TFT-LCD來說,非常符合。因為TFT-LCD在讀的時候,一般比較慢,而在寫入的時候一般比較快。
模式A的讀操作時序如圖:
模式A的寫操作時序如圖:
圖中ADDSET和DATASET兩個時序的值,后續(xù)配置的時候會詳細(xì)講述。
2.3.2. Bank2、3/4
只能外接Nand Flash設(shè)備和PC Card設(shè)備:
選擇芯片型號
打開STM32CubeMX,打開MCU選擇器:
搜索并選中芯片STM32F407ZGT6:
配置時鐘源
如果選擇使用外部高速時鐘(HSE),則需要在System Core中配置RCC;
如果使用默認(rèn)內(nèi)部時鐘(HSI),這一步可以略過;
這里我都使用外部時鐘:
調(diào)試選項配置
默認(rèn)沒有配置下載引腳,燒錄之后下載器將無法再檢測到,這里我使用ST-Link,所以配置為SW選項:
配置串口
開發(fā)板板載了一個CH340換串口,連接到USART1,但是引腳不是默認(rèn)引腳,需要手動修改。
接下來開始配置USART1:
配置FSMC外設(shè)
本文所使用的開發(fā)板中,將TFT-LCD當(dāng)做SRAM來操作,連接在FSMC的BANK1的第4個區(qū)域。
知識點(diǎn):為什么TFT-LCD可以當(dāng)做SRAM來控制?
因為TFT-LCD和SRAM相比,同樣需要D0-D15數(shù)據(jù)線,WR、RD、CS控制線,唯一不同的就是TFT-LCD需要一條RS信號線(用于控制傳輸?shù)氖敲钸€是數(shù)據(jù)),而SRAM則需要一堆地址線,所以可以巧妙的使用任意一條地址線來當(dāng)做RS信號。
FSMC配置
開發(fā)板上 TFT-LCD 的原理圖如下:
通過原理圖可以看出:
LCD D0-D15:使用了16bit:FSMC D0 - FSMC D15;
LCD_RS:使用FSMC A6來控制向LCD寫入數(shù)據(jù)還是命令(0-命令,1-數(shù)據(jù));
LCD_BL:背光控制,對應(yīng)PB5;
LCD_CS:LCD片選信號,F(xiàn)MC_NE4,表示使用Bank1的Bank4子區(qū)域
LCD_WR :LCD寫使能,F(xiàn)SMC_NWE;
LCD_RD:LCD讀使能,F(xiàn)SMC_NOE;
根據(jù)這些信息,在STM32CubeMX中先配置SRAM4的基本設(shè)置:
此處如果選擇LCD接口類型和SRAM類型的區(qū)別在于:
LCD接口類型只會配置用到的那一個地址引腳,而SRAM類型則會配置所有的地址引腳。
SRAM基本參數(shù)配置
首先設(shè)置基本的參數(shù),允許讀與寫使用不同的模式:
SRAM時序參數(shù)配置
本文中使用的LCD控制器為NT35510控制器,找到其數(shù)據(jù)手冊,查看:
其中主要的時序參數(shù)配置方法如下。
讀時序配置
① HCLK
時序參數(shù)都是以HCLK的周期為單位的,在本文中HCLK=168Mhz,所以一個周期為5.95ns。
② 地址建立時間:Address setup time(ADDSET)
該時序的最大值的15個HCLK,從圖中可以看出,NT35110控制器要求讀的時候最小為10ns,,所以設(shè)為2即可,2x5.95=11.9ns。
③ 數(shù)據(jù)持續(xù)時間:Data setup time(DATASET)
讀時序比較慢,該時序的最大值為255個HCLK,從圖中可以看出,NT35510控制器要求的數(shù)據(jù)建立時間最小為15ns,但因為讀時序比較慢,所以設(shè)為4,4x5.95=23.8ns。
寫時序配置
① HCLK
時序參數(shù)都是以HCLK的周期為單位的,在本文中HCLK=168Mhz,所以一個周期為5.95ns。
② 地址建立時間:Address setup time(ADDSET)
該時序的最大值為15個HCLK,NT35110控制器要求寫的時候最小為0,,所以設(shè)為0即可。
③ 數(shù)據(jù)持續(xù)時間:Data setup time(DATASET)
寫時序比較快,該時序的最大值為255個HCLK,圖中可以看出,NT35510控制器要求的數(shù)據(jù)建立時間最小為15ns,但因為讀時序比較慢,所以設(shè)為3,3x5.95=17.85ns。
綜合上述計算,配置情況如下:
配置背光引腳
配置時鐘樹
STM32F407ZGT6的最高主頻到168M,使HCLK = 168Mhz即可:
生成工程設(shè)置
代碼生成設(shè)置
最后設(shè)置生成獨(dú)立的初始化文件:
生成代碼
點(diǎn)擊GENERATE CODE即可生成MDK-V5工程:
4. 編寫TFT-LCD驅(qū)動(測試是否可以正常讀寫ID)
特別提醒:STM32CubeMX生成的工程默認(rèn)開啟了-O3優(yōu)化,編寫的驅(qū)動太菜了,會出問題,所以遇到玄學(xué)Bug請改為-O0優(yōu)化!
封裝底層發(fā)送/讀取函數(shù)
LCD的底層無非就是兩個API:發(fā)送命令、發(fā)送數(shù)據(jù),(有的還需要從屏幕讀取數(shù)據(jù)),接下來封裝出這兩(三)個底層API。
之前查看原理圖的時候,表示命令或者數(shù)據(jù)的LCD_RS控制引腳接在FMC_A6上,也就是說地址數(shù)據(jù)的第6位,所以在頭文件lcd-fsmc.h中先定義:
/* 通過地址線控制RS引腳 */
#define LCD_CMD_ADDR 0x6c00007E
#define LCD_DAT_ADDR 0x6c000080
接著開始封裝兩個(三個)底層操作函數(shù):
① 發(fā)送命令函數(shù):
/**
* @brief 向LCD寫入命令
* @param cmd 待寫入命令
* @retval none
*/
static void lcd_write_cmd(__IO uint16_t cmd)
{
*(uint16_t *)(LCD_CMD_ADDR) = cmd;
}
② 發(fā)送數(shù)據(jù)函數(shù):
/**
* @brief 向LCD寫入數(shù)據(jù)
* @param data 待寫入數(shù)據(jù)
* @retval none
*/
static void lcd_write_data(__IO uint16_t data)
{
*(uint16_t *)(LCD_DAT_ADDR) = data;
}
③ 讀取數(shù)據(jù)函數(shù):
/**
* @brief 從LCD讀取數(shù)據(jù)
* @param none
* @retval 讀取到的數(shù)據(jù)
*/
static uint16_t lcd_read_data(void)
{
__IO uint16_t data;
data = *(uint16_t *)(LCD_DAT_ADDR);
return data;
}
基于這三個底層API,還可以封裝出讀寫LCD內(nèi)部寄存器的函數(shù):
/**
* @brief 寫LCD中的寄存器
* @param reg 寄存器序號
* @param data 要寫入寄存器的值
* @retval none
*/
static void lcd_write_reg(__IO uint16_t reg, __IO uint16_t data)
{
lcd_write_cmd(reg);
lcd_write_data(data);
}
LCD控制參數(shù)結(jié)構(gòu)體
為了方便驅(qū)動不同的IC,保存不同的控制參數(shù),在lcd_fmc.h中封裝如下數(shù)據(jù)類型:
/**
* @brief 保存LCD屏幕參數(shù)
* @param lcd_width LCD屏幕寬度
* @param lcd_height LCD屏幕高度
* @param lcd_id LCD 驅(qū)動IC ID
* @param lcd_direction LCD橫屏顯示還是豎屏顯示,0-豎屏,1-橫屏
* @param wram_cmd 開始寫gram指令
* @param set_x_cmd 設(shè)置x坐標(biāo)指令
* @param set_y_cmd 設(shè)置y坐標(biāo)指令
*/
typedef struct lcd_params_st {
uint16_t lcd_width;
uint16_t lcd_height;
uint16_t lcd_id;
uint8_t lcd_direction;
uint16_t wram_cmd;
uint16_t set_x_cmd;
uint16_t set_y_cmd;
} lcd_params_t;
然后在頭文件中聲明外部變量定義,方便其他程序訪問:
extern lcd_params_t lcd_params;
在lcd_fsmc.c中定義此變量為全局變量:
lcd_params_t lcd_params;
LCD驅(qū)動打印日志的處理
為了方便程序開發(fā),難免要打印一些日志,但是如果printf沒有被重定向,則會導(dǎo)致LCD驅(qū)動卡死。為了避免這個問題,我們使用宏開關(guān)的方式來控制是否打印。
在lcd_fsmc.h中定義此宏開關(guān):
/* 使能此驅(qū)動是否打印調(diào)試日志(需要printf支持) */
#define LCD_LOG_ENABLE 1
接著可以定義一個日志打印函數(shù):
#if LCD_LOG_ENABLE
#include #define LCD_LOG printf #else #define LCD_LOG(format,...) #endif 之后所以需要打印的地方使用LCD_LOG代替printf即可。 編寫LCD控制器ID讀取函數(shù) 通過主動讀取此控制器ID,可以自動檢測出是哪種類型的控制器,然后執(zhí)行不同的驅(qū)動代碼: static int lcd_read_id(void) { /* 嘗試執(zhí)行ILI9341控制器ID的讀取流程 */ lcd_write_cmd(0XD3); lcd_params.lcd_id = lcd_read_data(); lcd_params.lcd_id = lcd_read_data(); lcd_params.lcd_id = lcd_read_data(); lcd_params.lcd_id <<= 8; lcd_params.lcd_id |= lcd_read_data(); /* 如果正常讀到,則返回成功 */ if (lcd_params.lcd_id == 0x9341) { return 0; } /* 嘗試執(zhí)行NT35310控制器ID的讀取流程 */ lcd_write_cmd(0XD4); lcd_params.lcd_id = lcd_read_data(); lcd_params.lcd_id = lcd_read_data(); lcd_params.lcd_id = lcd_read_data(); lcd_params.lcd_id <<= 8; lcd_params.lcd_id |= lcd_read_data(); /* 如果正常讀到,則返回成功 */ if (lcd_params.lcd_id == 0x5310) { return 0; } /* 嘗試執(zhí)行NT35510控制器ID的讀取流程 */ lcd_write_cmd(0XDA00); lcd_params.lcd_id = lcd_read_data(); lcd_write_cmd(0XDB00); lcd_params.lcd_id = lcd_read_data(); lcd_params.lcd_id <<= 8; lcd_write_cmd(0XDC00); lcd_params.lcd_id |= lcd_read_data(); /* 如果正常讀到,則返回成功 */ if (lcd_params.lcd_id == 0x8000) { lcd_params.lcd_id = 0x5510; return 0; } /* 驅(qū)動IC不支持 */ lcd_params.lcd_id = 0; return -1; } 編寫LCD初始化函數(shù) LCD初始化需要發(fā)送大量的命令和數(shù)據(jù),本文限于篇幅,只給出讀LCD 控制IC的ID的部分,用來測試LCD是否能正常讀寫足矣。 void lcd_init(void) { /* 初始化FMC接口 */ //MX_FSMC_Init(); /* 開啟背光 */ HAL_GPIO_WritePin(LCD_BL_GPIO_Port, LCD_BL_Pin, GPIO_PIN_SET); HAL_Delay(50); /* 讀取LCD控制器IC */ if (lcd_read_id() == -1) { LCD_LOG("Not Support LCD IC!rn"); return; } else { LCD_LOG("LCD IC ID is:%#xrn", lcd_params.lcd_id); } return; } 在lcd_fsmc.h中聲明該函數(shù): void lcd_init(void); 測試是否可以正常操作LCD 在main.c中包含進(jìn)來頭文件: /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include #include "lcd_fsmc.h" /* USER CODE END Includes */ 然后在man函數(shù)中調(diào)用: /* USER CODE BEGIN 2 */ printf("4.3' TFT-LCD Test By Mculover666rn"); lcd_init(); /* USER CODE END 2 */ 編譯,下載,在串口助手中查看結(jié)果: 5. 編寫TFT-LCD驅(qū)動(初始化、刷屏測試) 可以正常讀取ID之后,接下來的工作是: 發(fā)送一堆一堆的命令,初始化屏幕; 設(shè)置坐標(biāo) 清屏 刷屏測試 ① LCD開顯示、關(guān)顯示、LCD設(shè)置掃描方向、 LCD設(shè)置顯示方向、LCD設(shè)置光標(biāo)位置這些函數(shù)代碼不多,需要的話請查看源碼。 ② 清屏函數(shù): static void lcd_write_ram_start(void) { lcd_write_cmd(lcd_params.wram_cmd); } static void lcd_write_ram(uint16_t rgb_color) { lcd_write_data(rgb_color); } void lcd_clear(uint16_t color) { uint32_t index = 0; uint32_t totalpoint = lcd_params.lcd_width; /* 計算得到總點(diǎn)數(shù) */ totalpoint *= lcd_params.lcd_height; /* 設(shè)置光標(biāo)位置 */ lcd_set_cursor(0x00,0x0000); /* 開始寫入GRAM */ lcd_write_ram_start(); /* 寫入數(shù)據(jù)到GRAM */ for (index = 0; index < totalpoint; index++) { lcd_write_ram(color); } } ③ 初始化函數(shù):代碼過長,請查看源碼。 這三類函數(shù)實現(xiàn)完之后,就可以編寫一個如下的刷屏測試函數(shù): void lcd_auto_clear(uint16_t period_ms) { lcd_clear(BLACK); HAL_Delay(period_ms); lcd_clear(BLUE); HAL_Delay(period_ms); lcd_clear(GREEN); HAL_Delay(period_ms); lcd_clear(GBLUE); HAL_Delay(period_ms); lcd_clear(CYAN); HAL_Delay(period_ms); lcd_clear(GRAY); HAL_Delay(period_ms); lcd_clear(BROWN); HAL_Delay(period_ms); lcd_clear(RED); HAL_Delay(period_ms); lcd_clear(BRED); HAL_Delay(period_ms); lcd_clear(BRRED); HAL_Delay(period_ms); lcd_clear(YELLOW); HAL_Delay(period_ms); lcd_clear(WHITE); HAL_Delay(period_ms); } 在main函數(shù)中調(diào)用此函數(shù),分別給予不同的刷新頻率,測試刷屏速度和效果。 6. 實現(xiàn)打點(diǎn)、畫線、填充函數(shù)(重點(diǎn)) 打點(diǎn)函數(shù) /** * @brief LCD打點(diǎn)函數(shù) * @param x_pos x方向坐標(biāo) * @param y_pos y方向坐標(biāo) * @retval none */ void lcd_draw_point(uint16_t x_pos, uint16_t y_pos, uint16_t color)
上一篇:STM32CubeMX | 33-使用GPIO讀取溫度傳感器數(shù)據(jù)(DS18B20)
下一篇:移植uc/OS-III最新版到小熊派開發(fā)板(STM32L431)
- 熱門資源推薦
- 熱門放大器推薦
設(shè)計資源 培訓(xùn) 開發(fā)板 精華推薦
- LTC3331 的典型應(yīng)用 - 具有能量收集電池充電器的納米功率降壓-升壓型 DC/DC
- LTC1728ES5-1.8 單電源監(jiān)視器與其他禁用的典型應(yīng)用
- 使用 Silicon Labs 的 Si3024-KT 的參考設(shè)計
- SY89542U EV,SY89542U 評估板,2.5V,3.2Gbps 雙路差分 2:1 多路復(fù)用器,帶內(nèi)部終端
- 使用 Analog Devices 的 LT1110CN8 的參考設(shè)計
- 使用 ON Semiconductor 的 CAT4240 的參考設(shè)計
- LT1934IDCB 3.3V 降壓轉(zhuǎn)換器的典型應(yīng)用電路
- 直流和步進(jìn)電機(jī)驅(qū)動IC —— TC78H610FNG
- 雙向 400V-12V DC/DC 轉(zhuǎn)換器參考設(shè)計
- LTC7812EUH 低 EMI、寬輸入范圍直通級聯(lián)升壓+降壓穩(wěn)壓器的典型應(yīng)用電路
- 阿里黑科技落地!夸克AI眼鏡全球首發(fā),高德、淘寶、支付寶都能用
- 化繁為簡, 適配復(fù)雜磁場環(huán)境,MT73xx 3D雙路輸出霍爾鎖存器賦能車規(guī)電機(jī)精準(zhǔn)控制
- 9.5億美元收購恩智浦MEMS傳感器業(yè)務(wù),意法半導(dǎo)體 在傳感器領(lǐng)域的地位再升級
- 高性能電動滑板車 BLDC 電機(jī)驅(qū)動器:技術(shù)解析與應(yīng)用展望
- 5G工業(yè)網(wǎng)關(guān)的“邊緣計算+AI推理”一體化設(shè)計,PLC協(xié)議解析與缺陷檢測的實時聯(lián)動
- AR眼鏡的“工業(yè)指令投射”系統(tǒng),SLAM的空間定位、PLC數(shù)據(jù)實時疊加顯示
- 多光譜氣體傳感器的抗交叉干擾設(shè)計
- 多模態(tài)融合感知的“語義-幾何”聯(lián)合建模
- 工業(yè)觸摸屏的“壓感-手勢”多模態(tài)交互設(shè)計
- 工業(yè)機(jī)器人高精度力控的“雙模融合”傳感器設(shè)計
- 上半年凈利同比或降48%-62%,精研科技暴跌超14%
- 南京疫情復(fù)燃 臺積電南京廠運(yùn)營沒受影響
- 納芯微推全新集成電流路徑霍爾傳感器:NSM201X系列
- 臺積電:今年全球晶圓制造產(chǎn)值將漲20%
- 三星印度、俄羅斯手機(jī)市占受到中國品牌強(qiáng)烈沖擊
- 換電池比換新車還貴,新能源汽車該如何發(fā)展?
- 低功耗Bluetooth?技術(shù)應(yīng)用于汽車門禁系統(tǒng)
- 紅獅控制為MILLBROOK科技園量身定制一套業(yè)內(nèi)領(lǐng)先的解決方案
- 升級至24度電起步的寶馬X5 PHEV電池
- 眾泰高端汽車夢碎君馬,敲響了怎樣的警鐘?