fqchd.cn-亚洲AV无码专区在线观看下载,下面一进一出好爽视频,人妻被按摩师玩弄到潮喷,人妻丰满熟妇av无码区乱

歡迎來到廣東TFT屏幕廠家官方網站!
contact us

聯系我們

廣東TFT屏幕廠家 > 新聞資訊 > ALIENTEK 阿波羅 STM32F767 開發板資料連載第十八章 TFTLCD實驗

ALIENTEK 阿波羅 STM32F767 開發板資料連載第十八章 TFTLCD實驗

編輯 :

廣東TFT屏幕

時間 : 2021-12-22 15:27 瀏覽量 : 50

1)試驗服務平臺:alientek 阿波羅 STM32F767 單片機開發板2)節選自《STM32F7 開發設計指引(HAL 庫版)》關心官微號微信公眾號,獲得大量材料:正點原子



第十八章 TFTLCD(MCU 屏)試驗

在第 16 章大家詳細介紹了 OLED 模塊以及顯示,可是該模塊只有顯示純色/兩色,不可以顯示彩

色,并且規格也較小。此章大家將詳細介紹 ALIENTEK 的 TFT LCD 模塊(MCU 屏),該模塊選用

TFTLCD 控制面板,可以顯示 16 位色的真彩照片。在這章中,大家將應用阿波羅 STM32F767 開發設計

板底版上的 TFTLCD 插口(僅適用 MCU 屏,此章僅詳細介紹 MCU 屏的應用),來照亮 TFTLCD,

并完成 ASCII 標識符和彩色的顯示等作用,并在串口通信打印出 LCD 控制板 ID,與此同時在 LCD 上邊顯示。

此章分成如下所示一些一部分:

18.1 TFTLCD&FMC 介紹

18.2 硬件開發

18.3 軟件開發

18.4 在線下載認證

18.5 STM32CubeMX 配備 FMC(SRAM)

18.1 TFTLCD&FMC 介紹

此章大家將根據 STM32F767的 FMC 插口來操縱TFTLCD 的顯示,因此這節分成2個一部分,

各自詳細介紹 TFTLCD 和 FMC。

18.1.1 TFTLCD 介紹

TFT-LCD 即塑料薄膜晶體三極管液晶顯示器。其英語全稱之為:Thin Film Transistor-Liquid Crystal

Display。TFT-LCD 與微波感應器 TN-LCD、STN-LCD 的簡易引流矩陣不一樣,它在液晶顯示屏的每一個象

素上面設定有一個塑料薄膜晶體三極管(TFT),可合理地擺脫非選通時的串擾,使顯示液晶顯示屏的靜態數據特

性與掃描線數不相干,因而進一步提高了圖象品質。TFT-LCD 也被稱為真彩液晶顯示器。

上一章詳細介紹了 OLED 模塊,此章,大家給各位詳細介紹 ALIENTEK TFTLCD 模塊(MCU 插口),

該模塊有以下特性:

1,2.8’/3.5’/4.3’/7’等 4 種尺寸的顯示屏可選。

2,320×240 的屏幕分辨率(3.5’屏幕分辨率為:320*480,4.3’和 7’屏幕分辨率為:800*480)。

3,16 位真彩顯示。

4,內置觸摸顯示屏,可以拿來做為操縱鍵入。

此章,大家以 2.8 寸(別的 3.5 寸/4.3 寸等 LCD 方式相近,請參照 2.8 的就可以)的 ALIENTEK

TFTLCD 模塊為例子詳細介紹,該模塊適用 65K 色顯示,顯示屏幕分辨率為 320×240,插口為 16 位的 80

并口,內置觸摸顯示屏。

該模塊的外型圖如下圖 18.1.1.1 所顯示

圖 18.1.1.1 ALIENTEK 2.8 寸 TFTLCD 外型圖


模塊電路原理圖如下圖 18.1.1.2 所顯示:

圖 18.1.1.2 ALIENTEK 2.8 寸 TFTLCD 模塊電路原理圖


TFTLCD 模塊選用 2*17 的 2.54 公排針與外界聯接,接口定義如下圖 18.1.1.3 所顯示:

圖 18.1.1.3 ALIENTEK 2.8 寸 TFTLCD 模塊插口圖


從圖 18.1.1.3 可以看得出,ALIENTEK TFTLCD 模塊選用 16 位的并方法與外界聯接,往往

不選用 8 位的方法,是由于顯示屏的信息量較為大,特別是在在顯示照片的情況下,假如用 8 位手機充電線,

便會比 16 位方法慢一倍以上,大家自然期待速率越是快就越好,因此大家挑選 16 位的插口。圖

18.1.1.3 還列舉了觸摸顯示屏集成ic的插口,有關觸摸顯示屏此章大家很少詳細介紹,后邊的章節目錄會出現詳盡的介

紹。該模塊的 80 并口有以下一些電源線:

CS:TFTLCD 片選數據信號。

WR:向 TFTLCD 載入數據信息。

RD:從 TFTLCD 接收數據。

D[15:0]:16 位雙重手機充電線。

RST:硬校準 TFTLCD。

RS:指令/數據信息標示(0,讀寫能力指令;1,讀寫能力數據信息)。

80 并口在上一節大家早已有完整的講解了,這兒大家就不會再詳細介紹,必須表明的是,TFTLCD

模塊的 RST 電源線是立即收到 STM32F767 的校準腳底,并不由自主APP操縱,那樣可以省出來一

個 IO 口。此外大家還要一個led背光控線來操縱 TFTLCD 的led背光。因此,大家一共必須的 IO

口數額為 21 個。這兒還要留意,大家標明的 DB1~DB8,DB10~DB17,是相比于 LCD 操縱

IC 標明的,事實上大伙兒可以把她們就相當于 D0~D15,那樣解釋起來就相對簡單一點。

ALIENTEK給予 2.8/3.5/4.3/7 寸等 4種不一樣規格和分辯率的TFTLCD 模塊,其推動集成ic為:

ILI9341/NT35310/NT35510/SSD1963 等(實際的型號規格,大伙兒可以根據在線下載此章試驗編碼,根據串

口或是 LCD 顯示查詢),這兒大家僅以 ILI9341 控制板為例子開展詳細介紹,別的的操縱基本上都相近,

大家也不詳盡論述了。

ILI9341 液晶控制板內置獨顯存儲,其獨顯存儲總尺寸為 172800(240*320*18/8),即 18 位方式(26

萬色)下的顯總量。在 16 位方式下,ILI9341 選用 RGB565 文件格式儲存色調數據信息,這時 ILI9341

的 18 位手機充電線與 MCU 的 16 位手機充電線及其 LCD GRAM 的對應關系如下圖 18.1.1.4 所顯示:

圖 18.1.1.4 16 位數據信息與獨顯存儲對應關系圖

從圖內可以看得出,ILI9341 在 16 位方式下邊,手機充電線有效的是:D17~D13 和 D11~D1,D0

和 D12 沒有使用,事實上在大家 LCD 模塊里邊,ILI9341 的 D0 和 D12 根本就沒有引過來,這

樣,ILI9341 的 D17~D13 和 D11~D1 相匹配 MCU 的 D15~D0。

那樣 MCU 的 16 位數據信息,最少 5 位意味著深藍色,正中間 6 位為翠綠色,最大 5 位為鮮紅色。標值越

大,表明該色調越重。此外,需注意 ILI9341 全部的命令全是 8 位的(高 8 位失效),且主要參數

除開讀寫能力 GRAM 的那時候是 16 位,別的實際操作主要參數,全是 8 位的。

下面,大家介紹一下 ILI9341 的一些關鍵指令,由于 ILI9341 的指令許多,大家這兒就

不所有詳細介紹了,有感興趣的各位可以尋找 ILI9341 的 datasheet 看一下。里邊對這種指令有完整的介

紹。大家將詳細介紹:0XD3,0X36,0X2A,0X2B,0X2C,0X2E 等 6 條命令。

最先看來命令:0XD3,這個是讀 ID4 命令,用以載入 LCD 控制板的 ID,該命令如表 18.1.1.1

所顯示:

表 18.1.1.1 0XD3 命令敘述


從以上可以看得出,0XD3 命令后邊跟了 4 個主要參數,最終 2 個主要參數,讀出是 0X93 和 0X41,

恰好是大家控制板 ILI9341 的小數一部分,進而,根據該命令,就可以辨別常用的 LCD 控制器是什

么型號規格,那樣,大家的編碼,就可以依據控制板的型號規格去實行相匹配推動 IC 的復位編碼,進而

兼容不一樣推動 IC 的屏,促使一個編碼適用幾款 LCD。

下面看命令:0X36,這也是儲存瀏覽程序控制,可以操縱 ILI9341 儲存器的讀寫能力方位,簡

單的說,便是在持續寫 GRAM 的情況下,可以操縱 GRAM 表針的提高方位,進而操縱顯示方法

(讀 GRAM 也是一樣)。該命令如表 18.1.1.2 所顯示:

表 18.1.1.2 0X36 命令敘述


從以上可以看得出,0X36 命令后邊,緊隨一個主要參數,這兒大家關鍵關心:MY、MX、MV

這三個位,根據這三個位的設定,我們可以操縱全部 ILI9341 的所有掃描儀方位,如表 18.1.1.3

所顯示:



表 18.1.1.3 MY、MX、MV 設定與 LCD 掃描儀方位關系表


那樣,我們在運用 ILI9341 顯示內容的情況下,就會有非常大靈敏性了,例如顯示 BMP 照片,

BMP 編解碼數據信息,就是以照片的左下方逐漸,漸漸地顯示到右上方,假如設定 LCD 掃描儀方位為從

左到右,從下向上,那麼大家只必須設定一次座標,隨后就不斷的往 LCD 填充顏色數據信息就可以,

那樣可以進一步提高顯示速率。

下面看命令:0X2A,這也是列詳細地址設定命令,在從左往右,自上而下的掃描儀方法(默認設置)

下邊,該命令用以設定橫坐標軸(x 座標),該命令如表 18.1.1.4 所顯示:

表 18.1.1.4 0X2A 命令敘述

在默認設置掃描儀方法時,該命令用以設定 x 座標,該命令含有 4 個主要參數,事實上是 2 個平面坐標:

SC 和 EC,即列詳細地址的起始值和完畢值,SC 務必不大于 EC,且 0≤SC/EC≤239。一般在設

置 x 座標的情況下,大家只必須帶 2 個主要參數就可以,也就是設定 SC 就可以,由于假如 EC 沒有轉變,

大家只必須設定一次就可以(在復位 ILI9341 的過程中設定),進而提高速度。

與 0X2A 命令相近,命令:0X2B,是頁詳細地址設定命令,在從左往右,自上而下的掃描儀方法

(默認設置)下邊,該命令用以設定縱軸(y 座標)。該命令如表 18.1.1.5 所顯示:

表 18.1.1.5 0X2B 命令敘述


在默認設置掃描儀方法時,該命令用以設定 y 座標,該命令含有 4 個主要參數,事實上是 2 個平面坐標:

SP 和 EP,即頁詳細地址的起始值和完畢值,SP 務必不大于 EP,且 0≤SP/EP≤319。一般在設定

y 座標的情況下,大家只必須帶 2 個主要參數就可以,也就是設定 SP 就可以,由于假如 EP 沒有轉變,我

們只必須設定一次就可以(在復位 ILI9341 的過程中設定),進而提高速度。

下面看命令:0X2C,該命令是寫 GRAM 命令,在推送該命令以后,大家便可以往 LCD

的 GRAM 里邊載入色調數據信息了,該命令適用持續寫,命令敘述如表 18.1.1.6 所顯示:


表 18.1.1.6 0X2C 命令敘述



從以上得知,在接到命令 0X2C 以后,數據信息合理位寬變成 16 位,我們可以持續載入 LCD

GRAM 值,而 GRAM 的詳細地址將依據 MY/MX/MV 設定的掃描儀方位開展自增。比如:假定設定

的是從左往右,自上而下的掃描儀方法,那麼設定好起止座標(根據 SC,SP 設定)后,每載入

一個顏色值,GRAM 詳細地址可能全自動自增 1(SC  ),假如遇到 EC,則返回 SC,與此同時 SP  ,一

直到座標:EC,EP 完畢,期間不用再度設定的座標,進而進一步提高載入速率。

最終,一起來看看命令:0X2E,該命令是讀 GRAM 命令,用以載入 ILI9341 的獨顯存儲(GRAM),

該命令在 ILI9341 的數據信息指南上邊的敘述是不正確的,真正的導出狀況如表 18.1.1.7 所顯示:


表 18.1.1.7 0X2E 命令敘述


該命令用以載入 GRAM,如表 18.1.1.7 所顯示,ILI9341 在得到該命令后,第一次導出的是

dummy 數據信息,也就是失效的數據信息,第二次逐漸,載入到的才算是合理的 GRAM 數據信息(從座標:

SC,SP 逐漸),導出規律性為:每一個色調份量占 8 個位數,一次導出 2 個色調份量。例如:第一次

導出是 R1G1,接著的規律性為:B1R2?G2B2?R3G3?B3R4?G4B4?R5G5... 依此類推。假如

大家只必須載入一個點的顏色值,那麼只必須接受到主要參數 3 就可以,假如要持續載入(運用 GRAM

詳細地址自增,方式跟上面一樣),那麼就依照以上規律性去接受色調數據信息。

以上,便是實際操作 ILI9341 常見的好多個命令,根據這好多個命令,大家便可以不錯的操縱 ILI9341

顯示大家所要顯示的內容了。

一般 TFTLCD 模塊的應用步驟如下圖 18.1.1.5:

圖 18.1.1.5 TFTLCD 應用步驟


一切 LCD,應用步驟都能夠簡易的用以上流程表表明。在其中硬校準和復位編碼序列,只必須

實行一次就可以。而畫點步驟便是:設定座標?寫 GRAM 命令?載入色調數據信息,隨后在 LCD 上

面,大家就可以見到相應的點顯示大家載入的色調了。讀點步驟為:設定座標?讀 GRAM 命令

?載入色調數據信息,那樣就可以獲得到對應的點的色調數據信息了。

以上僅僅非常簡單的實際操作,也是最常見的實際操作,擁有這種實際操作,一般就可以正常的應用 TFTLCD

了。下面人們將該模塊用于來顯示標識符和數據,根據以上詳細介紹,我們可以得到 TFTLCD 顯示

必須的有關設定流程如下所示:

1)設定 STM32F767 與 TFTLCD 模塊相連接的 IO。

這一步,先將我們與 TFTLCD 模塊相連的 IO 口進行初始化,以便驅動 LCD。這里我們用

到的是 FMC,FMC 將在 18.1.2 節向大家詳細介紹。

2)初始化 TFTLCD 模塊。

即圖 18.1.1.5 的初始化序列,這里我們沒有硬復位 LCD,因為阿波羅 STM32F767 開發板

的 LCD 接口,將 TFTLCD 的 RST 同 STM32F767 的 RESET 連接在一起了,只要按下開發板的

RESET 鍵,就會對 LCD 進行硬復位。初始化序列,就是向 LCD 控制器寫入一系列的設置值(比

如伽馬校準),這些初始化序列一般 LCD 供應商會提供給客戶,我們直接使用這些序列即可,

不需要深入研究。在初始化之后,LCD 才可以正常使用。

3)通過函數將字符和數字顯示到 TFTLCD 模塊上。

這一步則通過圖 18.1.1.5 左側的流程,即:設置坐標?寫 GRAM 指令?寫 GRAM 來實現,

但是這個步驟,只是一個點的處理,我們要顯示字符/數字,就必須要多次使用這個步驟,從而

達到顯示字符/數字的目的,所以需要設計一個函數來實現數字/字符的顯示,之后調用該函數,

就可以實現數字/字符的顯示了。

STM32F767xx 系列芯片都帶有 FMC 接口,即可變存儲存儲控制器,能夠與同步或異步存

儲器、SDRAM 存儲器和 NAND FLASH 等連接,STM32F767 的 FMC 接口支持包括 SRAM、

SDRAM、NAND FLASH、NOR FLASH 和 PSRAM 等存儲器。FMC 的框圖如圖 18.1.2.1 所示:

圖 18.1.2.1 FMC 框圖


從上圖我們可以看出,STM32F767 的 FMC 將外部設備分為 3 類:NOR/PSRAM 設備、NAND

設備和 SDRAM 設備。他們共用地址數據總線等信號,他們具有不同的 CS 以區分不同的設備,

比如本章我們用到的 TFTLCD 就是用的 FMC_NE1 做片選,其實就是將 TFTLCD 當成 SRAM

來控制。

這里我們介紹下為什么可以把 TFTLCD 當成 SRAM 設備用:首先我們了解下外部 SRAM

的連接,外部 SRAM 的控制一般有:地址線(如 A0~A18)、數據線(如 D0~D15)、寫信號(WE)、

讀信號(OE)、片選信號(CS),如果 SRAM 支持字節控制,那么還有 UB/LB 信號。而 TFTLCD

的信號我們在 18.1.1 節有介紹,包括:RS、D0~D15、WR、RD、CS、RST 和 BL 等,其中真

正在操作 LCD 的時候需要用到的就只有:RS、D0~D15、WR、RD 和 CS。其操作時序和 SRAM

的控制完全類似,唯一不同就是 TFTLCD 有 RS 信號,但是沒有地址信號。

TFTLCD 通過 RS 信號來決定傳送的數據是數據還是命令,本質上可以理解為一個地址信

號,比如我們把 RS 接在 A0 上面,那么當 FMC 控制器寫地址 0 的時候,會使得 A0 變為 0,

對 TFTLCD 來說,就是寫命令。而 FMC 寫地址 1 的時候,A0 將會變為 1,對 TFTLCD 來說,

就是寫數據了。這樣,就把數據和命令區分開了,他們其實就是對應 SRAM 操作的兩個連續地

址。當然 RS 也可以接在其他地址線上,阿波羅 STM32F767 開發板是把 RS 連接在 A18 上面的。

STM32F767 的 FMC 支持 8/16/32 位數據寬度,我們這里用到的 LCD 是 16 位寬度的,所

以在設置的時候,選擇 16 位寬就 OK 了。我們再來看看 FMC 的外部設備地址映像,STM32F767

的 FMC 將外部存儲器劃分為 6 個固定大小為 256M 字節的存儲區域,如圖 18.1.2.2 所示:

圖 18.1.2.2 FMC 存儲塊地址映像


從上圖可以看出,FMC 總共管理 1.5GB 空間,擁有 6 個存儲塊(Bank),本章,我們用到

的是塊 1,所以在本章我們僅討論塊 1 的相關配置,其他塊的配置,請參考《STM32F7 中文參

考手冊》第 13 章(286 頁)的相關介紹。

STM32F767 的 FMC 存儲塊 1(Bank1)被分為 4 個區,每個區管理  ** M 字節空間,每個

區都有獨立的寄存器對所連接的存儲器進行配置。Bank1 的 256M 字節空間由 28 根地址線

(HADDR[27:0])尋址。

這里 HADDR 是內部AHB地址總線,其中HADDR[25:0]來自外部存儲器地址 FMC_A[25:0],

而 HADDR[26:27]對 4 個區進行尋址。如表 18.1.2.1 所示:

表 18.1.2.1 Bank1 存儲區選擇表

HADDR[25:0]位包含外部存儲器的地址,由于 HADDR 為字節地址,而存儲器按字尋址,

所以,根據存儲器數據寬度的不同,實際上向存儲器發送的地址也有所不同,如表 18.1.2.2 所

示:

表 18.1.2.2 NOR/PSRAM 外部存儲器地址


因此,FMC 內部 HADDR 與存儲器尋址地址的實際對應關系就是:

當接的是 32 位寬度存儲器的時候:HADDR[25:2]? FMC_A [23:0]。

當接的是 16 位寬度存儲器的時候:HADDR[25:1]? FMC_A [24:0]。

當接的是 8 位寬度存儲器的時候:HADDR[25:0]? FMC_A [25:0]。

不論外部接 8 位/16 位/32 位寬設備,FMC_A[0]永遠接在外部設備地址 A[0]。 這里,

TFTLCD 使用的是 16 位數據寬度,所以 HADDR[0]并沒有用到,只有 HADDR[25:1]是有效的,

對應關系變為:HADDR[25:1]? FMC_A[24:0],相當于右移了一位,這里請大家特別留意。另

外,HADDR[27:26]的設置,是不需要我們干預的,比如:當你選擇使用 Bank1 的第一個區,

即使用 FMC_NE1 來連接外部設備的時候,即對應了 HADDR[27:26]=00,我們要做的就是配置

對應第 1 區的寄存器組,來適應外部設備即可。STM32F767 的 FMC 各 Bank 配置寄存器如表

18.1.2.3 所示:

表 18.1.2.3 FMC 各 Bank 配置寄存器表


對于 NOR FLASH 控制器,主要是通過 FMC_BCRx、FMC_BTRx 和 FMC_BWTRx 寄存器

設置(其中 x=1~4,對應 4 個區)。通過這 3 個寄存器,可以設置 FMC 訪問外部存儲器的時序

參數,拓寬了可選用的外部存儲器的速度范圍。FMC 的 NOR FLASH 控制器支持同步和異步突

發兩種訪問方式。選用同步突發訪問方式時,FMC 將 HCLK(系統時鐘)分頻后,發送給外部存

儲器作為同步時鐘信號 FMC_CLK。此時需要的設置的時間參數有 2 個:

1,HCLK 與 FMC_CLK 的分頻系數(CLKDIV),可以為 2~16 分頻;

2,同步突發訪問中獲得第 1 個數據所需要的等待延遲(DATLAT)。

對于異步突發訪問方式,FMC 主要設置 3 個時間參數:地址建立時間(ADDSET)、數據建

立時間(DATAST)和地址保持時間(ADDHLD)。FMC 綜合了 SRAM、PSRAM 和 NOR Flash 產品

的信號特點,定義了 4 種不同的異步時序模型。選用不同的時序模型時,需要設置不同的時序

參數,如表 18.1.2.4 所列:

表 18.1.2.4 NOR FLASH/PSRAM 控制器支持的時序模型


在實際擴展時,根據選用存儲器的特征確定時序模型,從而確定各時間參數與存儲器讀/

寫周期參數指標之間的計算關系;利用該計算關系和存儲芯片數據手冊中給定的參數指標,可

計算出 FMC 所需要的各時間參數,從而對時間參數寄存器進行合理的配置。

本章,我們使用異步模式 A(ModeA)方式來控制 TFTLCD,模式 A 的讀操作時序如圖

18.1.2.3 所示:

圖 18.1.2.3 模式 A 讀操作時序圖


模式 A 支持獨立的讀寫時序控制,這個對我們驅動 TFTLCD 來說非常有用,因為 TFTLCD

在讀的時候,一般比較慢,而在寫的時候可以比較快,如果讀寫用一樣的時序,那么只能以讀

的時序為基準,從而導致寫的速度變慢,或者在讀數據的時候,重新配置 FMC 的延時,在讀

操作完成的時候,再配置回寫的時序,這樣雖然也不會降低寫的速度,但是頻繁配置,比較麻

煩。而如果有獨立的讀寫時序控制,那么我們只要初始化的時候配置好,之后就不用再配置,

既可以滿足速度要求,又不需要頻繁改配置。

模式 A 的寫操作時序如圖 18.1.2.4 所示:


圖 18.1.2.4 模式 A 寫操作時序

圖 18.1.2.3 和圖 18.1.2.4 中的 ADDSET 與 DATAST,是通過不同的寄存器設置的,接下來

我們講解一下 Bank1 的幾個控制寄存器

首先,我們介紹 SRAM/NOR 閃存片選控制寄存器:FMC_BCRx(x=1~4),該寄存器各位

描述如圖 18.1.2.5 所示:

圖 18.1.2.5 FMC_BCRx 寄存器各位描述


該寄存器我們在本章用到的設置有:EXTMOD、WREN、MWID、MTYP 和 MBKEN 這幾

個設置,我們將逐個介紹。

EXTMOD:擴展模式使能位,也就是是否允許讀寫不同的時序,很明顯,我們本章需要讀

寫不同的時序,故該位需要設置為 1。

WREN:寫使能位。我們需要向 TFTLCD 寫數據,故該位必須設置為 1。

MWID[1:0]:存儲器數據總線寬度。00,表示 8 位數據模式;01 表示 16 位數據模式;10

表示 32 位數據模式;11 保留。我們的 TFTLCD 是 16 位數據線,所以設置 WMID[1:0]=01。

MTYP[1:0]:存儲器類型。00 表示 SRAM;01 表示 PSRAM;10 表示 NOR FLASH/OneNAND

FLASH;11 保留。前面提到,我們把 TFTLCD 當成 SRAM 用,所以需要設置 MTYP[1:0]=00。

MBKEN:存儲塊使能位。這個容易理解,我們需要用到該存儲塊控制 TFTLCD,當然要

使能這個存儲塊了。

接下來,我們看看 SRAM/NOR 閃存片選時序寄存器:FMC_BTRx(x=1~4),該寄存器各

位描述如圖 18.1.2.6 所示:

圖 18.1.2.6 FMC_BTRx 寄存器各位描述


這個寄存器包含了每個存儲器塊的控制信息,可以用于 SRAM 和 NOR 閃存存儲器等。如

果 FMC_BCRx 寄存器中設置了 EXTMOD 位,則有兩個時序寄存器分別對應讀(本寄存器)和寫

操作(FMC_BWTRx 寄存器)。因為我們要求讀寫分開時序控制,所以 EXTMOD 是使能了的,

也就是本寄存器是讀操作時序寄存器,控制讀操作的相關時序。本章我們要用到的設置有:

ACCMOD、DATAST 和 ADDSET 這三個設置。

ACCMOD[1:0]:訪問模式。00 表示訪問模式 A;01 表示訪問模式 B;10 表示訪問模式 C;

11 表示訪問模式 D,本章我們用到模式 A,故設置為 00。

DATAST[7:0]:數據保持時間。0 為保留設置,其他設置則代表保持時間為: DATAST 個

HCLK 時鐘周期,最大為 255 個 HCLK 周期。對 ILI9341 來說,其實就是 RD 低電平持續時間,

一般為 355ns。而一個 HCLK 時鐘周期為 4.6ns 左右(1/216Mhz),為了兼容其他屏,我們這里

設置 DATAST 為 80,也就是 80 個 HCLK 周期,時間大約是 368ns。

ADDSET[3:0]:地址建立時間。其建立時間為:ADDSET 個 HCLK 周期,最大為 15 個 HCLK

周期。對 ILI9341 來說,這里相當于 RD 高電平持續時間,為 90ns,我們設置 ADDSET 為最大

15,即 15*4.6=69ns(略超)。

最后,我們再來看看 SRAM/NOR 閃寫時序寄存器:FMC_BWTRx(x=1~4),該寄存器各

位描述如圖 18.1.2.7 所示:

圖 18.1.2.7 FMC_BWTRx 寄存器各位描述


該寄存器在本章用作寫操作時序控制寄存器,需要用到的設置同樣是:ACCMOD、DATAST

和 ADDSET 這三個設置。這三個設置的方法同 FMC_BTRx 一模一樣,只是這里對應的是寫操

作的時序,ACCMOD 設置同 FMC_BTRx 一模一樣,同樣是選擇模式 A,另外 DATAST 和

ADDSET 則對應低電平和高電平持續時間,對 ILI9341 來說,這兩個時間只需要 15ns 就夠了,

比讀操作快得多。所以我們這里設置 DATAST 為 4,即 4 個 HCLK 周期,時間約為 18.4ns。然

后 ADDSET 設置為 4,即 4 個 HCLK 周期,時間為 18.4ns。

至此,我們對 STM32F767 的 FMC 介紹就差不多了,關于 FMC 的詳細介紹,請大家參考

《STM32F7 中文參考手冊》第 13 章。通過以上兩個小節的了解,我們可以開始寫 LCD 的驅動

代碼了。不過,這里還要給大家做下科普,在 MDK 的寄存器定義里面,并沒有定義 FMC_BCRx、

FMC_BTRx、FMC_BWTRx 等這個單獨的寄存器,而是將他們進行了一些組合。

FMC_BCRx 和 FMC_BTRx,組合成 BTCR[8]寄存器組,他們的對應關系如下:

BTCR[0]對應 FMC_BCR1,BTCR[1]對應 FMC_BTR1

BTCR[2]對應 FMC_BCR2,BTCR[3]對應 FMC_BTR2

BTCR[4]對應 FMC_BCR3,BTCR[5]對應 FMC_BTR3

BTCR[6]對應 FMC_BCR4,BTCR[7]對應 FMC_BTR4

FMC_BWTRx 則組合成 BWTR[7],他們的對應關系如下:

BWTR[0]對應 FMC_BWTR1,BWTR[2]對應 FMC_BWTR2,

BWTR[4]對應 FMC_BWTR3,BWTR[6]對應 FMC_BWTR4,

BWTR[1]、BWTR[3]和 BWTR[5]保留,沒有用到。

通過上面的講解,通過對 FSC 相關的寄存器的描述,大家對 FMC 的原理有了一個初步的

認識,如果還不熟悉的朋友,請一定要搜索網絡資料理解 FMC 的原理。只有理解了原理,使

用庫函數才可以得心應手。那么在庫函數中是怎么實現 FMC 的配置的呢?FMC_BCRx,

FMC_BTRx 寄存器在庫函數是通過什么函數來配置的呢?下面我們來講解一下使用 FMC 接口

驅動 LCD(SRAM)相關的庫函數操作過程。與 SRAM 和 FMC 相關的庫函數定義和聲明在源

文件 stm32f7xx_hal_fmc.c/stm32f7xx_hal_sram.c 以及頭文件

stm32f7xx_hal_fmc.h/stm32f7xx_hal_sram.h 中。

1) 使能 FMC 和 GPIO 時鐘,初始化 IO 口配置,設置映射關系

這個步驟在前面實驗已多次講解。這里我們主要列出 FMC 時鐘使能方法:

__HAL_RCC_FMC_CLK_ENABLE ();

//使能 FMC 時鐘

對于 IO 配置,調用函數 HAL_GPIO_Init 配置即可,具體 請參考實驗源碼。

2) 初始化 FMC 接口讀寫時序參數,初始化 LCD(SRAM)控制接口

根據前面的講解,我們把 LCD 當 SRAM 使用,連接在 FMC 接口之上,所以我們要初始化

FMC 讀寫時序參數以及 LCD 數據接口,也就是初始化三個寄存器 FMC_BCRx,FMC_BTRx

和 FMC_BWTRx。HAL 庫提供了 SRAM 初始化函數 HAL_SRAM_Init,該函數聲明如下:

HAL_StatusTypeDef HAL_SRAM_Init(SRAM_HandleTypeDef *hsram,

FMC_NORSRAM_TimingTypeDef *Timing,

FMC_NORSRAM_TimingTypeDef *ExtTiming);

該函數有三個入口參數,首先我們來看看第一個入口參數 hsram,它是

SRAM_HandleTypeDef 結構體指針類型,該參數用來初始化當 FMC 接口當 SRAM 使用時的控

制接口參數。結構體 SRAM_HandleTypeDef 定義如下:

typedef struct

{

FMC_NORSRAM_TypeDef

*Instance;

FMC_NORSRAM_EXTENDED_TypeDef

*Extended;

FMC_NORSRAM_InitTypeDef

Init;

HAL_LockTypeDef

Lock;

__IO HAL_SRAM_StateTypeDef

State;

DMA_HandleTypeDef

*hd ** ;

}SRAM_HandleTypeDef;

成員變量 Instance 和成員變量 Extended 實際上是用來在指定的時序模型下,寄存器基地址

和擴展模式寄存器基地址。這個怎么理解呢,本實驗我們使用異步模式 A(ModeA)方式來控

制 TFTLCD,使用的存儲塊是 Bank1,所以寄存器基地址 Instance 我們直接寫 FMC_Bank1 即可,

當然,HAL 庫定義好了宏定義 FMC_NORSRAM_DEVICE,也就是如果是 SRAM 設備,直接

填寫這個宏定義標識符即可。因為我們要配置的讀寫時序是不一樣的,也就是我們前面講解的

FMC_BCRx 寄存器的 EXTMOD 位我們會配置為 1 允許讀寫不同的時序,所以我們這里還要指

定寫操作時序寄存器地址,也就是通過參數 Extended 來指定的,這里我們設置為 FMC_Bank1E

即可,同樣 MDK 定義好了宏定義標識符 FMC_NORSRAM_EXTENDED_DEVICE,所以這里

我們填寫這個宏定義標識符也是一樣的。對于寫時序參數配置,是在函數 HAL_SRAM_Init 的

第三個參數 ExtTiming 來配置的,這個我們后面會講解。

成員變量 Init 是 FMC_NORSRAM_InitTypeDef 結構體指針類型,改變量才是真正用來設置

SRAM 控制接口參數的。我們接下來看看這個結構體定義:

typedef struct

{

uint32_t NSBank;

//存儲區塊號

uint32_t DataAddressMux;

//地址/數據復用使能

uint32_t MemoryType;

//存儲器類型

uint32_t MemoryDataWidth; //存儲器數據寬度

uint32_t BurstAccessMode;

uint32_t WaitSignalPolarity;

uint32_t WaitSignalActive;

uint32_t WriteOperation;

//存儲器寫使能

uint32_t WaitSignal;

uint32_t ExtendedMode;

//是否使能擴展模式

uint32_t AsynchronousWait;

uint32_t WriteBurst;

uint32_t ContinuousClock;

//啟用/禁止 FMC 時鐘輸出到外部存儲設備

uint32_t WriteFifo;

uint32_t PageSize;

}FMC_NORSRAM_InitTypeDef;

NSBank 用來指定使用到的存儲塊區號,前面講過,我們是使用的存儲塊區號 1,所以選擇

值為 FMC_NORSRAM_BANK1。DataAddressMux 用來設置是否使能地址/數據復用,該變量僅對

NOR/PSRAM 有 效 , 所 以 這 里 我 們 選 擇 不 使 能 地 址 / 數據復用值

FMC_DATA_ADDRESS_MUX_DISABLE 即可。MemoryType 用來設置存儲器類型,這里我們

把 LCD 當 SRAM 使用,所以設置為 FMC_MEMORY_TYPE_SRAM 即可。MemoryDataWidth

用來設置存儲器數據總線寬度,可選 8 位還是 16 位,這里我們選擇 16 位數據寬度

FMC_NORSRAM_MEM_BUS_WIDTH_16。WriteOperation 用來設置存儲器寫使能,也就是是

否允許寫入。毫無疑問我們會進行存儲器寫操作,所以這里設置為

FMC_WRITE_OPERATION_ENABLE。ExtendedMode 用來設置是否使能擴展模式,也就是是

否允許讀寫使用不同時序,前面講解過本實驗讀寫采用不同時序,所以設置值為使能值

FMC_EXTENDED_MODE_ENABLE。ContinuousClock 用來設置啟用/禁止 FMC 時鐘輸出到外

部存儲設備 ,這里 僅 當 使 用 FMC_BCR1 寄 存 器 的 時 候 需 要 啟 用 , 啟 用 值 為

FMC_CONTINUOUS_CLOCK_SYNC_ASYNC 。 其 他 參 數 WriteBurst , BurstAccessMode ,

WaitSignalPolarity,WaitSignalActive,WaitSignal,AsynchronousWait 等是用在突發訪問和異步

時序情況下,這里我們不做過多講解。

成員變量 Lock 和 State 是 HAL 庫處理狀態標識變量。這里就不做過多講解。

成員變量 hd **  在使用 DMA 時候才使用,這里就先不講解了。

函數 HAL_SRAM_Init 的第一個入口參數就給大家講解到這里。

接下來看看后面 2 個參數 Timing 和 ExtTiming,它們都是 FMC_NORSRAM_TimingTypeDef

結構體指針類型,分別用來設置 FMC 接口讀和寫時序,主要涉及地址建立保持時間,數據建

立時間等等配置,對于我們的實驗中,讀寫時序不一樣,讀寫速度要求不一樣,所以對于參數

Timing 和 ExtTiming 設置了不同的值。

FMC_NORSRAM_TimingTypeDef 結構體定義如下:

typedef struct

{

uint32_t AddressSetupTime;

//地址建立時間

uint32_t AddressHoldTime;

//地址保持時間

uint32_t DataSetupTime;

//數據簡歷時間

uint32_t BusTurnAroundDuration; //總線周轉階段的持續時間

uint32_t CLKDivision;

//CLK 時鐘輸出信號的周期

uint32_t DataLatency;

//同步突發 NOR FLASH 的數據延遲

uint32_t AccessMode;

//異步模式配置

}FMC_NORSRAM_TimingTypeDef;

成員變量 AddressSetupTime 用來設置地址建立時間。AddressHoldTime 用來設置地址保持

時間。DataSetupTime 用來設置數據建立時間。BusTurnAroundDuration 用來配置總線周轉階段

的持續時間。CLKDivision 用來配置 CLK 時鐘輸出信號的周期,以 HCLK 周期數表示。

DataLatency 用來設置同步突發 NOR FLASH 的數據延遲。AccessMode 用來設置異步模式,取

值范圍為 FMC_ACCESS_MODE_A,FMC_ACCESS_MODE_B, FMC_ACCESS_MODE_C 和

FMC_ACCESS_MODE_D,這里我們用是異步模式 A,所以取值為 FMC_ACCESS_MODE_A。

HAL_SRAM_Init 函數各個入口參數含義和配置就給大家講解到這里。

和其他外設一樣,HAL 庫也提供了 SRAM 的初始化 MSP 回調函數,函數聲明如下:

void HAL_SRAM_MspInit(SRAM_HandleTypeDef *hsram) ;

關于 MSP 函數的使用方法相信大家已經非常熟悉。該函數內部一般用來使能時鐘以及初

始化 IO 口這些與 MCU 相關的步驟。

前面我們講解過,FMC 接口支持多種存儲器,包括 SDRAM,NOR,NAND 和 PC CARD

等。HAL 庫為每種支持的存儲器類型都定義了一個獨立的 HAL 庫文件,并且在文件中定義了

獨立的初始化函數。這里以 SDRAM 為例,HAL 提供庫支持文件 stm32f7xx_hal_sdram.c 和頭文

件 stm32f7xx_hal_sdram.h,同時還提供了獨立的初始化函數 HAL_SDRAM_Init,這里我們就列

出幾種存儲器的初始化函數:

HAL_SDRAM_Init();//SDRAM 初始化函數,省略入口參數

HAL_NOR_Init();//NOR 初始化函數,省略入口參數

HAL_NAND_Init();//NAND 初始化函數,省略入口參數

3)存儲區使能

實際上,當我們調用了存儲器初始化函數之后,相應的使用到的存儲區就已經被使能。

SRAM 存儲區使能方法為:

__FMC_NORSRAM_ENABLE(FMC_Bank1,FMC_NORSRAM_BANK1);

18.2 硬件設計

本實驗用到的硬件資源有:

1) 指示燈 DS0

2) TFTLCD 模塊

TFTLCD 模塊的電路見圖 18.1.1.2,這里我們介紹 TFTLCD 模塊與 ALIETEK 阿波羅

STM32F767 開發板的連接,阿波羅 STM32F767 開發板底板的 LCD 接口和 ALIENTEK TFTLCD

模塊直接可以對插,連接關系如圖 18.2.1 所示:

圖 18.2.1 TFTLCD 與開發板連接示意圖


圖 18.2.1 中圈出來的部分就是連接 TFTLCD 模塊的接口,液晶模塊直接插上去即可。

在硬件上,TFTLCD 模塊與阿波羅 STM32F767 開發板的 IO 口對應關系如下:

LCD_BL(背光控制)對應 PB5;

LCD_CS 對應 PD7 即 FMC_NE1;

LCD _RS 對應 PD13 即 FMC_A18;

LCD _WR 對應 PD5 即 FMC_NWE;

LCD _RD 對應 PD4 即 FMC_NOE;

LCD _D[15:0]則直接連接在 FMC_D15~FMC_D0;

這些線的連接,阿波羅 STM32F767 開發板的內部已經連接好了,我們只需要將 TFTLCD

模塊插上去就好了。實物連接(4.3 寸 TFTLCD 模塊)如圖 18.2.2 所示:

圖 18.2.2 TFTLCD 與開發板連接實物圖


18.3 軟件設計

打開我們光盤的實驗 13 TFTLCD(MCU 屏)工程可以看到我們添加了兩個文件 lcd.c 和頭

文 件 lcd.h 。 同 時 , FMC 和 SRAM 相 關 的 庫 函 數 和 聲 明 定 義 在 源 文 件

stm32f7xx_hal_fmc.c/stm32f7xx_hal_sdram.c 和頭文件 stm32f7xx_hal_fmc.h

/stm32f7xx_hal_sram.h 中。

在 lcd.c 里面要輸入的代碼比較多,我們這里就不貼出來了,只針對幾個重要的函數進行講

解。完整版的代碼見光盤?4,程序源碼?標準例程-寄存器版本?實驗 13 TFTLCD(MCU 屏)

實驗 的 lcd.c 文件。

本實驗,我們用到 FMC 驅動 LCD,通過前面的介紹,我們知道 TFTLCD 的 RS 接在 FMC

的 A18 上面,CS 接在 FMC_NE1 上,并且是 16 位數據總線。即我們使用的是 FMC 存儲器 1

的第 1 區,我們定義如下 LCD 操作結構體(在 lcd.h 里面定義):

//LCD 地址結構體

typedef struct

{

vu16 LCD_REG;

vu16 LCD_RAM;

} LCD_TypeDef;

//使用 NOR/SRAM 的 Bank1.sector1,地址位 HADDR[27,26]=00 A18 作為數據命令區分線

//注意設置時 STM32 內部會右移一位對其!

#define LCD_BASE ((u32)(0x | 0x0007FFFE))

#define LCD ((LCD_TypeDef *) LCD_BASE)

其中 LCD_BASE,必須根據我們外部電路的連接來確定,我們使用 Bank1.sector1 就是從

地址 0X 開始,而 0x0007FFFE,則是 A18 的偏移量,這里很多朋友不理解這個偏移量

的概念,簡單說明下:以 A18 為例,0x0007FFFE 轉換成二進制就是:0111 1111 1111 1111 1110,

而 16 位數據時,地址右移一位對齊,那么實際對應到地址引腳的時候,就是:A18:A0=011 1111

1111 1111 1111,此時 A18 是 0,但是如果 16 位地址再加 1(注意:對應到 8 位地址是加 2,即

0x0007FFFE +0X02),那么:A18:A0=100 0000 0000 0000 0000,時 A18 就是 1 了,即實現了對

RS 的 0 和 1 的控制。

我們將這個地址強制轉換為 LCD_TypeDef 結構體地址,那么可以得到 LCD->LCD_REG 的

地址就是 0X6007,FFFE,對應 A18 的狀態為 0(即 RS=0),而 LCD->LCD_RAM 的地址就是

0X6008,0000(結構體地址自增),對應 A18 的狀態為 1(即 RS=1)。

所以,有了這個定義,當我們要往 LCD 寫命令/數據的時候,可以這樣寫:

LCD->LCD_REG=CMD; //寫命令

LCD->LCD_RAM=DATA; //寫數據

而讀的時候反過來操作就可以了,如下所示:

CMD= LCD->LCD_REG; //讀 LCD 寄存器

DATA = LCD->LCD_RAM; //讀 LCD 數據

這其中,CS、WR、RD 和 IO 口方向都是由 FMC 硬件自動控制,不需要我們手動設置了。

接下來,我們先介紹一下 lcd.h 里面的另一個重要結構體:

//LCD 重要參數集

typedef struct

{

u16 width;

//LCD 寬度

u16 height;

//LCD 高度

u16 id;

//LCD ID

u8 dir;

//橫屏還是豎屏控制:0,豎屏;1,橫屏。

u16 wramcmd;

//開始寫 gram 指令

u16 setxcmd;

//設置 x 坐標指令

u16 setycmd;

//設置 y 坐標指令

}_lcd_dev;

//LCD 參數

extern _lcd_dev lcddev; //管理 LCD 重要參數

該結構體用于保存一些 LCD 重要參數信息,比如 LCD 的長寬、LCD ID(驅動 IC 型號)、

LCD 橫豎屏狀態等,這個結構體雖然占用了十幾個字節的內存,但是卻可以讓我們的驅動函數

支持不同尺寸的 LCD,同時可以實現 LCD 橫豎屏切換等重要功能,所以還是利大于弊的。有

了以上了解,下面我們開始介紹 lcd.c 里面的一些重要函數。

先看 7 個簡單,但是很重要的函數:

//寫寄存器函數

//regval:寄存器值

void LCD_WR_REG(vu16 regval)

{

regval=regval;

//使用-O2 優化的時候,必須插入的延時

LCD->LCD_REG=regval;//寫入要寫的寄存器序號

}

//寫 LCD 數據

//data:要寫入的值

void LCD_WR_DATA(vu16 data)

{

data=data;

//使用-O2 優化的時候,必須插入的延時

LCD->LCD_RAM=data;

}

//讀 LCD 數據

//返回值:讀到的值

u16 LCD_RD_DATA(void)

{

vu16 ram;

//防止被優化

ram=LCD->LCD_RAM;

return ram;

}

//寫寄存器

//LCD_Reg:寄存器地址

//LCD_RegValue:要寫入的數據

void LCD_WriteReg(u16 LCD_Reg, u16 LCD_RegValue)

{

LCD->LCD_REG = LCD_Reg;

//寫入要寫的寄存器序號

LCD->LCD_RAM = LCD_RegValue; //寫入數據

}

//讀寄存器

//LCD_Reg:寄存器地址

//返回值:讀到的數據

u16 LCD_ReadReg(u16 LCD_Reg)

{

LCD_WR_REG(LCD_Reg);

//寫入要讀的寄存器序號

delay_us(5);

return LCD_RD_DATA();

//返回讀到的值

}

//開始寫 GRAM

void LCD_WriteRAM_Prepare(void)

{

LCD->LCD_REG=lcddev.wramcmd;

}

//LCD 寫 GRAM

//RGB_Code:顏色值

void LCD_WriteRAM(u16 RGB_Code)

{

LCD->LCD_RAM = RGB_Code;//寫十六位 GRAM

}

因為 FMC 自動控制了 WR/RD/CS 等這些信號,所以這 7 個函數實現起來都非常簡單,我

們就不多說,注意,上面有幾個函數,我們添加了一些對 MDK –O2 優化的支持,去掉的話,

在-O2 優化的時候會出問題。這些函數實現功能見函數前面的備注,通過這幾個簡單函數的組

合,我們就可以對 LCD 進行各種操作了。

第七個要介紹的函數是坐標設置函數,該函數代碼如下:

//設置光標位置

//Xpos:橫坐標

//Ypos:縱坐標

void LCD_SetCursor(u16 Xpos, u16 Ypos)

{

if(lcddev.id==0X9341||lcddev.id==0X5310)

{

LCD_WR_REG(lcddev.setxcmd);

LCD_WR_DATA(Xpos>>8);LCD_WR_DATA(Xpos&0XFF);

LCD_WR_REG(lcddev.setycmd);

LCD_WR_DATA(Ypos>>8);LCD_WR_DATA(Ypos&0XFF);

}else if(lcddev.id==0X1963)

{

if(lcddev.dir==0)//x 坐標需要變換

{

Xpos=lcddev.width-1-Xpos;

LCD_WR_REG(lcddev.setxcmd);

LCD_WR_DATA(0);LCD_WR_DATA(0);

LCD_WR_DATA(Xpos>>8);LCD_WR_DATA(Xpos&0XFF);

}else

{

LCD_WR_REG(lcddev.setxcmd);

LCD_WR_DATA(Xpos>>8);LCD_WR_DATA(Xpos&0XFF);

LCD_WR_DATA((lcddev.width-1)>>8);

LCD_WR_DATA((lcddev.width-1)&0XFF);

}

LCD_WR_REG(lcddev.setycmd);

LCD_WR_DATA(Ypos>>8);LCD_WR_DATA(Ypos&0XFF);

LCD_WR_DATA((lcddev.height-1)>>8);LCD_WR_DATA((lcddev.height-1)&0XFF);

}else if(lcddev.id==0X5510)

{

LCD_WR_REG(lcddev.setxcmd);LCD_WR_DATA(Xpos>>8);

LCD_WR_REG(lcddev.setxcmd+1);LCD_WR_DATA(Xpos&0XFF);

LCD_WR_REG(lcddev.setycmd);LCD_WR_DATA(Ypos>>8);

LCD_WR_REG(lcddev.setycmd+1);LCD_WR_DATA(Ypos&0XFF);

}

}

該函數實現將 LCD 的當前操作點設置到指定坐標(x,y)。因為 9341/5310/1963/5510 等的設

置有些不太一樣,所以進行了區別對待。

接下來我們介紹第八個函數:畫點函數。該函數實現代碼如下:

//畫點

//x,y:坐標

//POINT_COLOR:此點的顏色

void LCD_DrawPoint(u16 x,u16 y)

{

LCD_SetCursor(x,y);

//設置光標位置

LCD_WriteRAM_Prepare(); //開始寫入 GRAM

LCD->LCD_RAM=POINT_COLOR;

}

該函數實現比較簡單,就是先設置坐標,然后往坐標寫顏色。其中 POINT_COLOR 是我們

定義的一個全局變量,用于存放畫筆顏色,順帶介紹一下另外一個全局變量:BACK_COLOR,

該變量代表 LCD 的背景色。LCD_DrawPoint 函數雖然簡單,但是至關重要,其他幾乎所有上

層函數,都是通過調用這個函數實現的。

有了畫點,當然還需要有讀點的函數,第九個介紹的函數就是讀點函數,用于讀取 LCD

的 GRAM,這里說明一下,為什么 OLED 模塊沒做讀 GRAM 的函數,而這里做了。因為 OLED

模塊是單色的,所需要全部 GRAM 也就 1K 個字節,而 TFTLCD 模塊為彩色的,點數也比 OLED

模塊多很多,以 16 位色計算,一款 320×240 的液晶,需要 320×240×2 個字節來存儲顏色值,

也就是也需要 150K 字節,這對任何一款單片機來說,都不是一個小數目了。而且我們在圖形

疊加的時候,可以先讀回原來的值,然后寫入新的值,在完成疊加后,我們又恢復原來的值。

這樣在做一些簡單菜單的時候,是很有用的。這里我們讀取 TFTLCD 模塊數據的函數為

LCD_ReadPoint,該函數直接返回讀到的 GRAM 值。該函數使用之前要先設置讀取的 GRAM

地址,通過 LCD_SetCursor 函數來實現。LCD_ReadPoint 的代碼如下:

//讀取個某點的顏色值

//x,y:坐標

//返回值:此點的顏色

u16 LCD_ReadPoint(u16 x,u16 y)

{

u16 r=0,g=0,b=0;

if(x>=lcddev.width||y>=lcddev.height)return 0; //超過了范圍,直接返回

LCD_SetCursor(x,y);

if(lcddev.id==0X9341||lcddev.id==0X5310||lcddev.id==0X1963)

LCD_WR_REG(0X2E);//9341/3510/1963 發送讀 GRAM 指令

else if(lcddev.id==0X5510)LCD_WR_REG(0X2E00);//5510 發送讀 GRAM 指令

r=LCD_RD_DATA();

//dummy Read

if(lcddev.id==0X1963)return r;

//1963 直接讀就可以

opt_delay(2);

r=LCD_RD_DATA();

//實際坐標顏色

//9341/NT35310/NT35510 要分 2 次讀出

opt_delay(2);

b=LCD_RD_DATA();

g=r&0XFF; //對于 9341/5310/5510,第一次讀取的是 RG 的值,R 在前,G 在后,各占 8 位

g<<=8;

return (((r>>11)<<11)|((g>>10)<<5)|(b>>11));

//需要公式轉換一下

}

在 LCD_ReadPoint 函數中,因為我們的代碼不止支持一種 LCD 驅動器,所以,我們根據

不同的 LCD 驅動器((lcddev.id)型號,執行不同的操作,以實現對各個驅動器兼容,提高函數

的通用性。

第十個要介紹的是字符顯示函數 LCD_ShowChar,該函數同前面 OLED 模塊的字符顯示函

數差不多,但是這里的字符顯示函數多了 1 個功能,就是可以以疊加方式顯示,或者以非疊加

方式顯示。疊加方式顯示多用于在顯示的圖片上再顯示字符。非疊加方式一般用于普通的顯示。

該函數實現代碼如下:

//在指定位置顯示一個字符

//x,y:起始坐標

//num:要顯示的字符:" "--->"~"

//size:字體大小 12/16/24/32

//mode:疊加方式(1)還是非疊加方式(0)

void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode)

{

u8 temp,t1,t;

u16 y0=y;

u8 csize=(size/8+((size%8)?1:0))*(size/2);//得到字體一個字符對應點陣集所占的字節數

num=num-' ';//ASCII 字庫是從空格開始取模,所以-' '就是對應字符的字庫

for(t=0;t<csize;t++)

{

if(size==12)temp=asc2_1206[num][t];

//調用 1206 字體

else if(size==16)temp=asc2_1608[num][t]; //調用 1608 字體

else if(size==24)temp=asc2_2412[num][t]; //調用 2412 字體

else if(size==32)temp=asc2_3216[num][t]; //調用 3216 字體

else return;

//沒有的字庫

for(t1=0;t1<8;t1++)

{

if(temp&0x80)LCD_Fast_DrawPoint(x,y,POINT_COLOR);

else if(mode==0)LCD_Fast_DrawPoint(x,y,BACK_COLOR);

temp<<=1;

y++;

if(y>=lcddev.height)return;

//超區域了

if((y-y0)==size)

{

y=y0;

x++;

if(x>=lcddev.width)return; //超區域了

break;

}

}

}

}

在 LCD_ShowChar 函數里面,我們采用快速畫點函數 LCD_Fast_DrawPoint 來畫點顯示字

符,該函數同 LCD_DrawPoint 一樣,只是帶了顏色參數,且減少了函數調用的時間,詳見本例

程源碼。該代碼中我們用到了四個字符集點陣數據數組 asc2_3216、asc2_2412、asc2_1206 和

asc2_1608,這幾個字符集的點陣數據的提取方式,同十六章介紹的提取方法是一模一樣的。詳

細請參考第十六章。

最后,我們再介紹一下 TFTLCD 模塊的初始化函數 LCD_Init,該函數先配置 FMC 控制器,

然后讀取 LCD 控制器的型號,根據控制 IC 的型號執行不同的初始化代碼,其簡化代碼如下:

//初始化 lcd

//該初始化函數可以初始化各種型號的 LCD(詳見本.c 文件最前面的描述)

void LCD_Init(void)

{

GPIO_InitTypeDef GPIO_Initure;

FMC_NORSRAM_TimingTypeDef FMC_ReadWriteTim;

FMC_NORSRAM_TimingTypeDef FMC_WriteTim;

__HAL_RCC_GPIOB_CLK_ENABLE();

//開啟 GPIOB 時鐘

GPIO_Initure.Pin=GPIO_PIN_5;

//PB5,背光控制

GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽輸出

GPIO_Initure.Pull=GPIO_PULLUP;

//上拉

GPIO_Initure.Speed=GPIO_SPEED_HIGH;

//高速

HAL_GPIO_Init(GPIOB,&GPIO_Initure);

LCD_MPU_Config(); //使能 MPU 保護 LCD 區域

SRAM_Handler.Instance= FMC_NORSRAM_DEVICE;

//SRAM BANK1

SRAM_Handler.Extended= FMC_NORSRAM_EXTENDED_DEVICE;

SRAM_Handler.Init.NSBank=FMC_NORSRAM_BANK1;

//使用 NE1

SRAM_Handler.Init.DataAddressMux=FMC_DATA_ADDRESS_MUX_DISABLE;

//地址/數據線不復用

SRAM_Handler.Init.MemoryType=FMC_MEMORY_TYPE_SRAM; //SRAM

SRAM_Handler.Init.MemoryDataWidth=FMC_NORSRAM_MEM_BUS_WIDTH_16;

//16 位數據寬度

SRAM_Handler.Init.BurstAccessMode=FMC_BURST_ACCESS_MODE_DISABLE;

//是否使能突發訪問,僅對同步突發存儲器有效,此處未用到

SRAM_Handler.Init.WaitSignalPolarity=FMC_WAIT_SIGNAL_POLARITY_LOW;

//等待信號的極性,僅在突發模式訪問下有用

SRAM_Handler.Init.WaitSignalActive=FMC_WAIT_TIMING_BEFORE_WS;

//存儲器是在等待周期之前的一個時鐘周期還是等待周期期間使能 NWAIT

SRAM_Handler.Init.WriteOperation=FMC_WRITE_OPERATION_ENABLE;

//存儲器寫使能

SRAM_Handler.Init.WaitSignal=FMC_WAIT_SIGNAL_DISABLE;

//等待使能位,此處未用到

SRAM_Handler.Init.ExtendedMode=FMC_EXTENDED_MODE_ENABLE;

//讀寫使用不同的時序

SRAM_Handler.Init.AsynchronousWait=FMC_ASYNCHRONOUS_WAIT_DISABLE;

//是否使能同步傳輸模式下的等待信號,此處未用到

SRAM_Handler.Init.WriteBurst=FMC_WRITE_BURST_DISABLE;

//禁止突發寫

SRAM_Handler.Init.ContinuousClock=FMC_CONTINUOUS_CLOCK_SYNC_ASYNC;

//FMC 讀時序控制寄存器

FMC_ReadWriteTim.AddressSetupTime=0x011; //地址建立時間為 17 個 HCLK

FMC_ReadWriteTim.AddressHoldTime=0x00;

FMC_ReadWriteTim.DataSetupTime=0x55; //數據保存時間(DATAST)為 85 個 HCLK

FMC_ReadWriteTim.AccessMode=FMC_ACCESS_MODE_A; //模式 A

//FMC 寫時序控制寄存器

FMC_WriteTim.AddressSetupTime=0x15; //地址建立時間(ADDSET)為 21 個 HCLK

FMC_WriteTim.AddressHoldTime=0x00;

FMC_WriteTim.DataSetupTime=0x015; //數據保存時間(DATAST)為 21 個 HCLK

FMC_WriteTim.AccessMode=FMC_ACCESS_MODE_A; //模式 A

HAL_SRAM_Init(&SRAM_Handler,&FMC_ReadWriteTim,&FMC_WriteTim);

delay_ms(50); // delay 50 ms

//嘗試 9341 ID 的讀取

LCD_WR_REG(0XD3);

lcddev.id=LCD_RD_DATA(); //dummy read

lcddev.id=LCD_RD_DATA(); //讀到 0X00

lcddev.id=LCD_RD_DATA();

//讀取 93

lcddev.id<<=8;

lcddev.id|=LCD_RD_DATA();

//讀取 41

if(lcddev.id!=0X9341)

//非 9341,嘗試看看是不是 NT35310

{

LCD_WR_REG(0XD4);

lcddev.id=LCD_RD_DATA();//dummy read

lcddev.id=LCD_RD_DATA();//讀回 0X01

lcddev.id=LCD_RD_DATA();//讀回 0X53

lcddev.id<<=8;

lcddev.id|=LCD_RD_DATA();

//這里讀回 0X10

if(lcddev.id!=0X5310)

//也不是 NT35310,嘗試看看是不是 NT35510

{

LCD_WR_REG(0XDA00);

lcddev.id=LCD_RD_DATA();

//讀回 0X00

LCD_WR_REG(0XDB00);

lcddev.id=LCD_RD_DATA();

//讀回 0X80

lcddev.id<<=8;

LCD_WR_REG(0XDC00);

lcddev.id|=LCD_RD_DATA();

//讀回 0X00

if(lcddev.id==0x8000)lcddev.id=0x5510;

//NT35510 讀回的 ID 是 8000H,為方便區分,我們強制設置為 5510

if(lcddev.id!=0X5510)

//也不是 NT5510,嘗試看看是不是 SSD1963

{

LCD_WR_REG(0XA1);

lcddev.id=LCD_RD_DATA();

lcddev.id=LCD_RD_DATA();

//讀回 0X57

lcddev.id<<=8;

lcddev.id|=LCD_RD_DATA();

//讀回 0X61

if(lcddev.id==0X5761)lcddev.id=0X1963;

//SSD1963 讀回的 ID 是 5761H,為方便區分,我們強制設置為 1963

}

}

}

printf(" LCD ID:%x",lcddev.id); //打印 LCD ID

if(lcddev.id==0X9341)

//9341 初始化

{

……//9341 初始化代碼

}else if(lcddev.id==0xXXXX) //其他 LCD 初始化代碼

{

……//其他 LCD 驅動 IC,初始化代碼

}

//初始化完成以后,提速

if(lcddev.id==0X9341||lcddev.id==0X5310||lcddev.id==0X5510||lcddev.id==0X1963)

{

//重新配置寫時序控制寄存器的時序

FMC_Bank1E->BWTR[0]&=~(0XF<<0);

//地址建立時間(ADDSET)清零

FMC_Bank1E->BWTR[0]&=~(0XF<<8);

//數據保存時間清零

FMC_Bank1E->BWTR[0]|=5<<0; //地址建立時間(ADDSET)為 5 個 HCLK =21ns

FMC_Bank1E->BWTR[0]|=5<<8;//數據保存時間(DATAST) 為 21ns

}

LCD_Display_Dir(0);

//默認為豎屏顯示

LCD_LED(1);

//點亮背光

LCD_Clear(WHITE);

}

該函數先對 FMC 相關 IO 進行初始化,然后是 FMC 的初始化,這個我們在前面都有介紹,

最后根據讀到的 LCD ID,對不同的驅動器執行不同的初始化代碼,從上面的代碼可以看出,

這個初始化函數針對多款不同的驅動 IC 執行初始化操作,這樣提高了整個程序的通用性。大家

在以后的學習中應該多使用這樣的方式,以提高程序的通用性、兼容性。

這里還要提醒大家,在 LCD_Init 函數中有如下一行代碼:

LCD_MPU_Config(); //使能 MPU 保護 LCD 區域

這行代碼的作用是調用函數 LCD_MPU_Config 使能 MPU 保護 LCD 區域,而函數

LCD_MPU_Config 定義的內容實際上是我們上一章給大家講解的使能 MPU 保護 LCD 區域。這

里我們之所以直接在 LCD 程序中加入 MPU 保護,是因為方便大家在移植 LCD 相關代碼到自

己的工程中的時候不會因為沒有引入 MPU 相關配置而導致 LCD 無 ** 常工作。

特別注意:本函數使用了 printf 來打印 LCD ID,所以,如果你在主函數里面沒有初始化串

口,那么將導致程序死在 printf 里面!!如果不想用 printf,那么請注釋掉它。

SRAM 初始化 MSP 回調函數 HAL_SRAM_MspInit 內容比較簡單,主要是進行時鐘使能以

及 IO 口映射配置,這里就不做過多講解。

LCD 驅動相關的函數就給大家講解到這里。接下來,我們看看主函數代碼如下:

int  ** in(void)

{

u8 x=0;

u8 lcd_id[12];

Cache_Enable(); //打開 L1-Cache

HAL_Init();

//初始化 HAL 庫

Stm32_Clock_Init(432,25,2,9); //設置時鐘,216Mhz

delay_init(216); //延時初始化

uart_init(115200);

//串口初始化

LED_Init(); //初始化 LED

LCD_Init(); //初始化 LCD

POINT_COLOR=RED;

sprintf((char*)lcd_id,"LCD ID:%04X",lcddev.id);//將 LCD ID 打印到 lcd_id 數組。

while(1)

{

switch(x)

{

case 0:LCD_Clear(WHITE);break;

……//此處省略部分代碼

case 11:LCD_Clear(BROWN);break;

}

POINT_COLOR=RED;

LCD_ShowString(10,40,260,32,32,"Apollo STM32F4/F7");

LCD_ShowString(10,80,240,24,24,"TFTLCD TEST");

LCD_ShowString(10,110,240,16,16,"ATOM@ALIENTEK");

LCD_ShowString(10,130,240,16,16,lcd_id);

//顯示 LCD ID

LCD_ShowString(10,150,240,12,12,"2016/7/11");

x++;

if(x==12)x=0;

LED0_Toggle;

delay_ms(1000);

}

}

該部分代碼將顯示一些固定的字符,字體大小包括 32*16、24*12、16*8 和 12*6 等四種,

同時顯示 LCD 驅動 IC 的型號,然后不停的切換背景顏色,每 1s 切換一次。而 LED0 也會不停

的閃爍,指示程序已經在運行了。其中我們用到一個 sprintf 的函數,該函數用法同 printf,只

是 sprintf 把打印內容輸出到指定的內存區間上,sprintf 的詳細用法,請百度學習。

另外特別注意:uart_init 函數,不能去掉,因為在 LCD_Init 函數里面調用了 printf,所以

一旦你去掉這個初始化,就會死機了!實際上,只要你的代碼有用到 printf,就必須初始化串口,

否則都會死機,即停在 usart.c 里面的 fputc 函數,出不來。

在編譯通過之后,我們開始下載驗證代碼。

18.4 下載驗證

將程序下載到阿波羅 STM32 后,可以看到 DS0 不停的閃爍,提示程序已經在運行了。同

時可以看到 TFTLCD 模塊的顯示如圖 18.4.1 所示:

圖 18.4.1 TFTLCD 顯示效果圖


我們可以看到屏幕的背景是不停切換的,同時 DS0 不停的閃爍,證明我們的代碼被正確的

執行了,達到了我們預期的目的。

18.5 STM32CubeMX 配置 FMC(SRAM)

當大家了解了 FMC 的基本工作原理,那么使用 STM32CubeMX 配置 FMC 相關參數就會非

常簡單。如果大家對 FMC 沒有理解,請仔細看教程學習。這里我們們不再詳細講解每個配置

項的含義。使用 STM32CubeMX 配置 FMC 的一般步驟為:

① 進入 Pinout->FMC 配置欄,配置 FMC 基本參數。根據前面的講解,這里我們使用的是

BANK1 的第一個分區 NE1,同時吧 LCD 作為 SRAM 使用,19 位地址線,16 位數據線。

配置參數如下圖 18.5.1 所示:

圖 18.5.1 FMC 配置參數


② 點擊 Configuration->FMC 進入 FMC 配置界面,在 NOR/SRAM 1 選項卡之下配置相關參

數。這些參數的含義這里我們不累贅,在 18.1 小節講解 HAL_SRAM_Init 函數的時候都

有講解。配置方法如下圖 18.5.2 所示:

圖 18.5.2 FMC Configuration 配置界面 NOR/PSRAM1 選項卡


在該配置界面,點擊右邊的 GPIO Settigns 選項卡,還可以配置相關 IO 口的信息。

經過上面配置步驟,我們就可以生成相應的初始化代碼,大家生成后和本章實驗工

程對比學習。

熱門推薦:

cache
Processed in 0.010193 Second.