??作者:一只大喵咪1201
??專欄:《STM32學(xué)習(xí)》
??格言:你只管努力,剩下的交給時(shí)間!
??LCD簡介
- 顯示器屬于計(jì)算機(jī)的 I/O 設(shè)備,即輸入輸出設(shè)備。它是一種將特定電子信息輸出到屏幕上再反射到人眼的顯示工具。常見的有 CRT 顯示器、液晶顯示器、 LED 點(diǎn)陣顯示器及 OLED 顯示器。本喵這里使用的是CLD顯示器,也就是液晶顯示器。
- 液晶顯示器,簡稱 LCD(Liquid Crystal Display),相對于上一代 CRT 顯示器 (陰極射線管顯示器),LCD 顯示器具有功耗低、體積小、承載的信息量大及不傷眼的優(yōu)點(diǎn),因而它成為了現(xiàn)在的主流電子顯示設(shè)備,其中包括電視、電腦顯示器、手機(jī)屏幕及各種嵌入式設(shè)備的顯示器。液晶電視與 CRT 電視的外觀對比,很明顯液晶電視更薄,“時(shí)尚”是液晶電視給人的第一印象,而 CRT 電視則感覺很“笨重”。
- 液晶是一種介于固體和液體之間的特殊物質(zhì),它是一種有機(jī)化合物,常態(tài)下呈液態(tài),但是它的分子排列卻和固體晶體一樣非常規(guī)則,因此取名液晶。如果給液晶施加電場,會改變它的分子排列,從而改變光線的傳播方向,配合偏振光片,它就具有控制光線透過率的作用,再配合彩色濾光片,改變加給液晶電壓大小,就能改變某一顏色透光量的多少,下圖中液晶屏的結(jié)構(gòu)就是綠色顯示結(jié)構(gòu)。利用這種原理,做出可控紅、綠、藍(lán)光輸出強(qiáng)度的顯示結(jié)構(gòu),把三種顯示結(jié)構(gòu)組成一個(gè)顯示單位,通過控制紅綠藍(lán)的強(qiáng)度,可以使該單位混合輸出不同的色彩,這樣的一個(gè)顯示單位被稱為像素。
- 注意液晶本身是不發(fā)光的,所以需要有一個(gè)背光燈提供光源,光線經(jīng)過一系列處理過程才到輸出,所以輸出的光線強(qiáng)度是要比光源的強(qiáng)度低很多的,比較浪費(fèi)能源 (當(dāng)然,比 CRT 顯示器還是節(jié)能多了)。而且這些處理過程會導(dǎo)致顯示方向比較窄,也就是它的視角較小,從側(cè)面看屏幕會看不清它的顯示內(nèi)容。另外,輸出的色彩變換時(shí),液晶分子轉(zhuǎn)動也需要消耗一定的時(shí)間,導(dǎo)致屏
幕的響應(yīng)速度低。
顯示器的基本參數(shù)
不管是哪一種顯示器,都有一定的參數(shù)用于描述它們的特性,各個(gè)參數(shù)介紹如下:
參數(shù) | 說明 |
---|---|
像素 | 像素是組成圖像的最基本單元要素,顯示器的像素指它成像最小的點(diǎn),即前面講解液晶原理中提到的一個(gè)顯示單元。 |
分辨率 | 一些嵌入式設(shè)備的顯示器常常以“行像素值 x 列像素值”表示屏幕的分辨率。如分辨率 800x480 表示該顯示器的每一行有 800 個(gè)像素點(diǎn),每一列有 480 個(gè)像素點(diǎn),也可理解為有 800 列, 480 行。 |
色彩深度 | 色彩深度指顯示器的每個(gè)像素點(diǎn)能表示多少種顏色,一般用“位” (bit) 來表示。如單色屏的每個(gè)像素點(diǎn)能表示亮或滅兩種狀態(tài) (即實(shí)際上能顯示 2 種顏色),用 1 個(gè)數(shù)據(jù)位就可以表示像素點(diǎn)的所有狀態(tài),所以它的色彩深度為 1bit,其它常見的顯示屏色深為16bit、 24bit。 |
顯示器尺寸 | 顯示器的大小一般以英寸表示,如 5 英寸、 21 英寸、 24 英寸等,這個(gè)長度是指屏幕對角線的長度,通過顯示器的對角線長度及長寬比可確定顯示器的實(shí)際長寬尺寸。 |
點(diǎn)距 | 點(diǎn)距指兩個(gè)相鄰像素點(diǎn)之間的距離,它會影響畫質(zhì)的細(xì)膩度及觀看距離,相同尺寸的屏幕,若分辨率越高,則點(diǎn)距越小,畫質(zhì)越細(xì)膩。如現(xiàn)在有些手機(jī)的屏幕分辨率比電腦顯示器的還大,這是手機(jī)屏幕點(diǎn)距小的原因; LED 點(diǎn)陣顯示屏的點(diǎn)距一般都比較大,所以適合遠(yuǎn)距離觀看。 |
??TFTLCD驅(qū)動原理
本喵使用的是一個(gè)4.3存的TFTLCD液晶屏。它的分辨率是480*800,通過16位并口驅(qū)動,也就是我們所說的8080并口,是一款電容觸摸屏。
這是它的實(shí)物圖,具體的構(gòu)成本喵就不作講解了,有興趣的小伙伴可以查閱相關(guān)資料自行了解。
液晶顯示原理:
以本喵使用的分辨率為480*800的液晶屏為例,下是它的示意圖
紅色框線是顯示屏的邊界線,里面的黑色框是一個(gè)一個(gè)的像素點(diǎn),這樣的像素點(diǎn)一共有800*480個(gè)。
它的顯示過程就是將屏幕中的像素點(diǎn)用特定的顏色填充,比如我們在屏中顯示一個(gè)字母F
將像素點(diǎn)從左到右從上到下按順序填充,其中填充顏色為紅色的所有點(diǎn)顯示出來的就是一個(gè)紅色的F,在液晶屏中,因?yàn)橄袼攸c(diǎn)間的點(diǎn)距很小,所以我們看到的F是是連著的,中間沒有間隙。
在上面的簡介中介紹過顏色深度這一個(gè)參數(shù),這里我們使用的是RGB565格式的數(shù)據(jù):
- RGB 信號線的數(shù)量分別是5根,6根,5根,分別用于表示液晶屏一個(gè)像素點(diǎn)的紅、綠、藍(lán)顏色分量。使用紅綠藍(lán)顏色分量來表示顏色是一種通用的做法。
打開 Windows 系統(tǒng)自帶的畫板調(diào)色工具,可看到顏色的紅綠藍(lán)分量值,如圖中R(紅色)分量,G(綠色)分量,B(藍(lán)色)分量,分別是50,25,30。
常見的顏色表示會在“RGB”后面附帶各個(gè)顏色分量值的數(shù)據(jù)位數(shù),如 RGB565 表示紅綠藍(lán)的數(shù)據(jù)線數(shù)分別為 5、6、 5 根,一共為 16 個(gè)數(shù)據(jù)位,可表示 216 種顏色。
顯存:
- 液晶屏中的每個(gè)像素點(diǎn)都是數(shù)據(jù),在實(shí)際應(yīng)用中需要把每個(gè)像素點(diǎn)的數(shù)據(jù)緩存起來,再傳輸給液晶屏,一般會使用 SRAM 或 SDRAM 性質(zhì)的存儲器,而這些專門用于存儲顯示數(shù)據(jù)的存儲器,則被稱為顯存。
- 顯存一般至少要能存儲液晶屏的一幀(一頁,也就是480*800個(gè)像素點(diǎn))顯示數(shù)據(jù),如分辨率為 800x480 的液晶屏,使用 RGB565 格式顯示,一幀顯示數(shù)據(jù)大小為: 2x800x480=768000 字節(jié)。
- 一般來說,外置的液晶控制器會自帶顯存,而像 STM32F429 等集成液晶控制器的芯片可使用內(nèi)部 SRAM 或外擴(kuò) SDRAM 用于顯存空間。
??ILI9341 液晶控制器簡介
本喵使用的芯片是STM32F1系列的,它的內(nèi)部SRAM是比較小的,所以需要使用擴(kuò)展SRAM來緩存像素點(diǎn)的數(shù)據(jù),本喵使用的LCD液晶有一個(gè)單獨(dú)的控制器,這個(gè)控制器就是ILI9431芯片,它里面有一個(gè)GRAM的存儲器。下面來看它的詳細(xì)介紹。
-
該芯片最主核心部分是位于中間的 GRAM(Graphics RAM),它就是顯存。 GRAM 中每個(gè)存儲單元都對應(yīng)著液晶面板的一個(gè)像素點(diǎn)。它右側(cè)的各種模塊共同把 GRAM 存儲單元的數(shù)據(jù)轉(zhuǎn)化成液晶面板的控制信號,使像素點(diǎn)呈現(xiàn)特定的顏色,而像素點(diǎn)組合起來則成為一幅完整的圖像。
-
框圖的左上角為 ILI9341 的主要控制信號線和配置引腳,根據(jù)其不同狀態(tài)設(shè)置可以使芯片工作在不同的模式,如每個(gè)像素點(diǎn)的位數(shù)是 6、 16 還是 18 位;可配置使用 SPI 接口、 8080 接口還是 RGB接口與 MCU 進(jìn)行通訊。
-
MCU 通過 SPI、 8080 接口或 RGB 接口與 ILI9341 進(jìn)行通訊,從而訪問它的控制寄存器 (CR)、地址計(jì)數(shù)器 (AC)、及 GRAM。
-
在 GRAM 的左側(cè)還有一個(gè) LED 控制器 (LED Controller)。 LCD 為非發(fā)光性的顯示裝置,它需要借助背光源才能達(dá)到顯示功能, LED 控制器就是用來控制液晶屏中的 LED 背光源。
所以說,我們只需要控制這個(gè)LIL9341控制芯片就可以了,這個(gè)芯片會自動的區(qū)控制LCD液晶屏幕,顯示相應(yīng)的數(shù)據(jù)。
??液晶屏的信號線及 8080 時(shí)序
ILI9341 控制器根據(jù)自身的 IM[3:0] 信號線電平?jīng)Q定它與 MCU 的通訊方式,它本身支持 SPI 及8080 通訊方式,本示例中液晶屏的 ILI9341 控制器在出廠前就已經(jīng)按固定配置好 (內(nèi)部已連接硬件電路),它被配置為通過 8080 接口通訊,使用 16 根數(shù)據(jù)線的 RGB565 格式。
這是LCD液晶的硬件圖和引腳。
這是LCD液晶屏PCB板引出的排針。
- 這些信號線即 8080 通訊接口,帶 X 的表示低電平有效, STM32 通過該接口與 ILI9341 芯片進(jìn)行通訊,實(shí)現(xiàn)對液晶屏的控制。通訊的內(nèi)容主要包括命令和顯存數(shù)據(jù),顯存數(shù)據(jù)即各個(gè)像素點(diǎn)的RGB565 內(nèi)容;
- 命令是指對 ILI9341 的控制指令, MCU 可通過 8080 接口發(fā)送命令編碼控制 ILI9341的工作方式,例如復(fù)位指令、設(shè)置光標(biāo)指令、睡眠模式指令等等,具體的指令在《ILI9341數(shù)據(jù)手冊》中有詳細(xì)說明。
我們來看下8080接口的時(shí)序圖:
- 由圖可知,寫命令時(shí)序由片選信號 CSX 拉低開始
- 數(shù)據(jù)/命令選擇信號線 D/CX 也置低電平表示寫入的是命令地址 (可理解為命令編碼,如軟件復(fù)位命令: 0x01)
- 寫信號 WRX 為低,讀信號 RDX 為高表示數(shù)據(jù)傳輸方向?yàn)閷懭?,同時(shí),在數(shù)據(jù)線 D[17:0](或 D[15:0]) 輸出命令地址,在第二個(gè)傳輸階段傳送的是命令的參數(shù),所以 D/CX 要置高電平,表示寫入的是命令數(shù)據(jù),命令數(shù)據(jù)是某些指令帶有的參數(shù),如復(fù)位指令編碼為 0x01,它后面可以帶一個(gè)參數(shù),該參數(shù)表示多少秒后復(fù)位 (實(shí)際的復(fù)位命令不含參數(shù),此處只是為了講解指令編碼與參數(shù)的區(qū)別)。
- 當(dāng)需要把像素?cái)?shù)據(jù)寫入 GRAM 時(shí),過程很類似SRAM擴(kuò)展,把片選信號 CSX 拉低后,再把數(shù)據(jù)/命令選擇信號線 D/CX 置為高電平,這時(shí)由 D[17:0] 傳輸?shù)臄?shù)據(jù)則會被 ILI9341 保存至它的 GRAM 中。
上面舉例中的是8080接口的寫時(shí)序,讀時(shí)序和寫幾乎是一樣的,只是方向相反。
??使用 STM32 的 FSMC 模擬 8080 接口時(shí)序
ILI9341 的 8080 通訊接口時(shí)序可以由 STM32 使用普通 I/O 接口進(jìn)行模擬,但這樣效率太低,STM32 提供了一種特別的控制方法——使用 FSMC 接口實(shí)現(xiàn) 8080 時(shí)序。
FSMC的原理和使用本喵在文章【STM32】FSMC——擴(kuò)展外部SRAM中詳細(xì)的介紹,這里本喵就不再介紹,只介紹它是如何使用的,因?yàn)镮LI9341中GRAM也是一種擴(kuò)展的SRAM。
控制 LCD 時(shí),是使用 FSMC 的 NORPSRAM 模式的,且與前面使用 FSMC 控制 SRAM 的稍有不同,控制 SRAM 時(shí)使用的是模式 A,而控制 LCD 時(shí)使用的是與 NOR FLASH 一樣的模式 B,所以我們重點(diǎn)分析框圖中 NOR FLASH 控制信號線部分。
這是FSMC模式B寫時(shí)序與8080信號寫時(shí)序的對比。
對比 FSMC NOR/PSRAM 中的模式 B 時(shí)序與 ILI9341液晶控制器芯片使用的 8080 時(shí)序可發(fā)現(xiàn),這兩個(gè)時(shí)序是十分相似的 (除了 FSMC 的地址線 A 和8080 的 D/CX 線,可以說是完全一樣)。
- 對于 FSMC 和 8080 接口,前四種信號線都是完全一樣的,僅僅是 FSMC 的地址信號線 A[25:0] 與8080 的數(shù)據(jù)/命令選擇線 D/CX 有區(qū)別。
- 而對于 D/CX 線,它為高電平的時(shí)候表示數(shù)值,為低電平的時(shí)候表示命令,如果能使用 FSMC 的 A 地址線根據(jù)不同的情況產(chǎn)生對應(yīng)的電平,那么就完全可以使用 FSMC 來產(chǎn)生 8080 接口需要的時(shí)序了。
- 為了模擬出 8080 時(shí)序,我們可以把 FSMC 的 A0 地址線 (也可以使用其它 A1/A2 等地址線) 與ILI9341 芯片 8080 接口的 D/CX 信號線連接,那么當(dāng) A0 為高電平時(shí) (即 D/CX 為高電平),數(shù)據(jù)線 D[15:0] 的信號會被 ILI9341 理解為數(shù)值,若 A0 為低電平時(shí) (即 D/CX 為低電平),傳輸?shù)男盘杽t會被理解為命令。
- 由于 FSMC 會自動產(chǎn)生地址信號,當(dāng)使用 FSMC 向 0x6xxx xxx1、 0x6xxx xxx3、 0x6xxx xxx5…這些奇數(shù)地址寫入數(shù)據(jù)時(shí),地址最低位的值均為 1,所以它會控制地址線 A0(D/CX) 輸出高電平,那么這時(shí)通過數(shù)據(jù)線傳輸?shù)男盘枙焕斫鉃閿?shù)值;
- 若向 0x6xxx xxx0 、 0x6xxx xxx2、 0x6xxx xxx4…這些偶數(shù)地址寫入數(shù)據(jù)時(shí),地址最低位的值均為 0,所以它會控制地址線 A0(D/CX) 輸出低電平,
因此這時(shí)通過數(shù)據(jù)線傳輸?shù)男盘枙焕斫鉃槊?,見下表使?FSMC 輸出地址示例。
- 有了這個(gè)基礎(chǔ),只要配置好 FSMC 外設(shè),然后在代碼中利用指針變量,向不同的地址單元寫入數(shù)據(jù),就能夠由 FSMC 模擬出的 8080 接口向 ILI9341 寫入控制命令或 GRAM 的數(shù)據(jù)了。
在配置結(jié)構(gòu)體時(shí),大部分與擴(kuò)展SRAM的配置是一樣的,只是有一些不同。
- FSMC_AccessMode :
本成員設(shè)置存儲器訪問模式,不同的模式下 FSMC 訪問存儲器地址時(shí)引腳輸出的時(shí)序不一樣,可選 FSMC_AccessMode_A/B/C/D 模式??刂飘惒?NORFLASH 時(shí)使用 B模式。
- FSMC_MemoryType :
本成員用于設(shè)置要控制的存儲器類型,它支持控制的存儲器類型為 SRAM、 PSRAM以及 NOR FLASH(FSMC_MemoryType_SRAM/PSRAM/NOR)。我們這里選用的是NOR FLASH。
??硬件連接
在了解了LCD液晶屏與MCU的工作原理后就是來運(yùn)用了。先看硬件連接圖
這是TFTLCD的硬件圖。
本喵在文章【STM32】FSMC——擴(kuò)展外部SRAM詳細(xì)的列出了FSMC的控制引腳,地址引腳以及數(shù)據(jù)引腳對應(yīng)的IO口,這里本喵就不再演示了。
圖中圈出來的部分就是連接 TFTLCD 模塊的接口,液晶模塊直接插上去即可。
在硬件上, TFTLCD 模塊與戰(zhàn)艦 STM32F103 的 IO 口對應(yīng)關(guān)系如下:
LCD引腳 | IO口 |
---|---|
LCD_BL(背光控制) | 對應(yīng) PB0 |
LCD_CS(片選信號) | 對應(yīng) PG12 即 FSMC_NE4; |
LCD _RS(數(shù)據(jù)/命令控制) | 對應(yīng) PG0 即 FSMC_A10 |
LCD _WR(寫控制) | 對應(yīng) PD5 即 FSMC_NWE |
LCD _RD(讀控制) | 對應(yīng) PD4 即 FSMC_NOE |
LCD _D[15:0] | 則直接連接在 FSMC_D15~FSMC_D0 |
這些線的連接,戰(zhàn)艦 STM32 開發(fā)板的內(nèi)部已經(jīng)連接好了,我們只需要將 TFTLCD 模塊插上去就好了。
??代碼實(shí)現(xiàn)
1. 初始化通訊使用的目標(biāo)引腳及端口時(shí)鐘
void GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//將用到的IO口的時(shí)鐘全部使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PB0 推挽輸出 背光
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//PORTD復(fù)用推挽輸出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_14|GPIO_Pin_15; //PORTD復(fù)用推挽輸出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復(fù)用推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
//PORTE復(fù)用推挽輸出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; //PORTE復(fù)用推挽輸出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復(fù)用推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
//PORTG12復(fù)用推挽輸出 P0RTG0-->RS
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_12; //PORTD復(fù)用推挽輸出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復(fù)用推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOG, &GPIO_InitStructure);
}
這里是將所有用到的IO口配置好。
2. 配置 FSMC 為異步 NOR FLASH 模式以仿真 8080 時(shí)序
- 紅色框是與擴(kuò)展SRAM的不同點(diǎn)。
- 藍(lán)色框是設(shè)置的ADDSET和DATAST的值。
ADDSET和DATAST的值這里采用的是經(jīng)驗(yàn)值,分別是0x01和0x04,當(dāng)然也可以根據(jù)時(shí)序圖來計(jì)算
但是計(jì)算很繁瑣,我們可以使用就行了,所以采用經(jīng)驗(yàn)值。
3. 建立機(jī)制使用 FSMC 向液晶屏發(fā)送命令及數(shù)據(jù)
首先是需要確定訪問的地址,計(jì)算地址的過程如下:
- 本實(shí)驗(yàn)使用的是 FSMC_NE4 作為 8080_CS 片選信號,所以首先可以確認(rèn)地址范圍,當(dāng)訪問0X6C00 0000 ~ 0X6FFF FFFF 地址時(shí), FSMC 均會對外產(chǎn)生片選有效的訪問時(shí)序;
- 本工程中使用 FSMC_A10 地址線作為命令/數(shù)據(jù)選擇線 RS 信號,所以在以上地址范圍內(nèi),再選擇出使得 FSMC_A10輸出高電平的地址,即可控制表示數(shù)據(jù),選擇出使得 FSMC_A10輸出低電平的地址,即可控制表示命令。
- 要使 FSMC_A10 地址線為高電平,實(shí)質(zhì)是輸出地址信號的第 10 位為 1 即可,使用 0X6C000000~0X6FFF FFFF 內(nèi)的任意地址,作如下運(yùn)算:
設(shè)置地址的第 10 位為 1: 0X6C00 0000 |= (1?10) = 0x6C00 0200 - 要使 FSMC_A23 地址線為低電平,實(shí)質(zhì)是輸出地址信號的第 10 位為 0 即可,使用 0X6C000000~0X6FFF FFFF 內(nèi)的任意地址,作如下運(yùn)算:
設(shè)置地址的第 10 位為 0: 0X6C00 0000 &= ~ (1?10) = 0x6C00 0000
- 但是,以上方法計(jì)算的地址還不完全正確,根據(jù)《STM32 參考手冊》對 FSMC 訪問 NOR FLASH的說明,見下圖 , STM32 內(nèi)部訪問地址時(shí)使用的是內(nèi)部 HADDR 總線,它是需要轉(zhuǎn)換到外部存儲器的內(nèi)部 AHB 地址線,它是字節(jié)地址 (8 位),而存儲器訪問不都是按字節(jié)訪問,因此接到存儲器的地址線依存儲器的數(shù)據(jù)寬度有所不同。
- 在本實(shí)驗(yàn)中使用的是 16 位的數(shù)據(jù)訪問方式,所以 HADDR 與 FSMC_A 的地址線連接關(guān)系會左移一位,如 HADDR1 與 FSMC_A0 對應(yīng)、 HADDR2 與 FSMC_A1 對應(yīng)。因此,當(dāng) FSMC_A0 地址線為 1 時(shí),實(shí)際上內(nèi)部地址的第 1 位為 1, FSMC_A1 地址線為 1 時(shí),實(shí)際上內(nèi)部地址的第 2 位為1,這樣做是為了達(dá)到地址線A0每增加一個(gè)數(shù),實(shí)際上跳過的是倆個(gè)字節(jié),方便LCD訪問數(shù)據(jù)。
- 同樣地,當(dāng)希望 FSMC_A10 地址輸出高電平或低電平時(shí),需要重新調(diào)整計(jì)算公式:
- 要使 FSMC_A10 地址線為高電平,實(shí)質(zhì)是訪問內(nèi)部 HADDR 地址的第 (10+1) 位為 1 即可,使用 0X6C00 0000~0X6FFF FFFF 內(nèi)的任意地址,作如下運(yùn)算:
使 FSMC_A10 地址線為高電平: 0X6C00 0000 |= (1?(10+1)) = 0x6C00 0400 - 要使 FSMC_A10 地址線為低電平,實(shí)質(zhì)是訪問內(nèi)部 HADDR 地址的第 (10+1) 位為 0 即可,
使用 0X6C00 0000~0X6FFF FFFF 內(nèi)的任意地址,作如下運(yùn)算:
使 FSMC_A10 地址線為低電平: 0X6C00 0000 &= ~ (1?(10+1)) = 0x6C00 0000
根據(jù)最終的計(jì)算結(jié)果,總結(jié)如下:
- 當(dāng) STM32 訪問內(nèi)部的 0x6000 0400 地址時(shí), FSMC 自動輸出時(shí)序,且使得與液晶屏的數(shù)據(jù)/命令選擇線 RS(即 D/CX) 相連的 FSMC_A10 輸出高電平,使得液晶屏?xí)褌鬏斶^程理解為數(shù)據(jù)傳輸;
- 類似地,當(dāng) STM32 訪問內(nèi)部的 0X6C00 0000 地址時(shí), FSMC自動輸出時(shí)序,且使得與液晶屏的數(shù)據(jù)/命令選擇線 RS(即 D/CX) 相連的 FSMC_A23 輸出低電平,使得液晶屏?xí)褌鬏斶^程理解為命令傳輸。
我們將計(jì)算出的地址使用宏標(biāo)記出來。
- 這里必須使用volatile關(guān)鍵字修飾地址,否則就會在編譯的時(shí)候被優(yōu)化,導(dǎo)致實(shí)驗(yàn)達(dá)不到效果。
- 這里將 0x6C00 0400中小于A10的地址都設(shè)為1,因?yàn)闆]有影響,但是將A0的地址設(shè)為0,所以結(jié)果就是 0x6C00 07FE
- 將這個(gè)地址強(qiáng)轉(zhuǎn)為定義的結(jié)構(gòu)體類型的指針,此時(shí)結(jié)構(gòu)體中成員變量vu16 LCD_REG的地址就是 0x6C00 07FE,而vu16 LCD_RAM的地址就是 0x6C00 8000,此時(shí)A10是0
這樣一來,我們使用結(jié)構(gòu)體就可以做到對LCD傳送命令或者數(shù)據(jù)。
接下來需要將背光點(diǎn)亮
LCD_LED PBout(0) = 1;
可以使用ILI9341控制器中的D3h指令,用該指令獲取LCD的ID,再通過串口打印出來。
3. 發(fā)送控制命令初始化液晶屏
這里就是將我們對GPIO的配置,F(xiàn)SMC的配置以及背光燈的點(diǎn)亮放在一個(gè)初始化函數(shù)中,這里本喵就不演示了。
4. 編寫液晶屏的繪制像素點(diǎn)函數(shù)
為了操作方便,我們將向LCD寫命令,寫數(shù)據(jù),讀數(shù)據(jù)這幾個(gè)操作封裝為函數(shù)。
//寫寄存器函數(shù)
//regval:寄存器值
void LCD_WR_REG(u16 regval)
{
LCD->LCD_REG=regval;
}
//寫LCD數(shù)據(jù)
//data:要寫入的值
void LCD_WR_DATA(u16 data)
{
LCD->LCD_RAM=data;
}
//讀LCD數(shù)據(jù)
//返回值:讀到的值
u16 LCD_RD_DATA(void)
{
vu16 ram;
ram = LCD->LCD_RAM;
return ram;
}
因?yàn)樵谑褂肔CD屏幕的時(shí)候,我們需要給ILI9341不同的控制指令,但是每次查表又不方便,所以我們將常用到的指令放在一個(gè)結(jié)構(gòu)體中。
這些命令值我們在另一給函數(shù)中挨個(gè)賦給這個(gè)結(jié)構(gòu)體
通過查看手冊中的相關(guān)指令給結(jié)構(gòu)體變量賦值。
還要在屏幕上開一個(gè)窗口,用來顯示我們要顯示的內(nèi)容。
此時(shí)前期的準(zhǔn)備工作就做完了,接下來就可以畫點(diǎn)了。
這是畫點(diǎn)函數(shù),我們可以多次調(diào)用改函數(shù)畫各種各樣的圖形,文字。
看代碼:
int main()
{
u8 j;
u32 i;
delay_init();
LCD_Init();
LED_Init();
POINT_COLOR = RED;
LCD_Clear(WHITE);
for(j=75;j<100;j++)
{
for(i=100;i<400;i++)
{
LCD_DrawPoint(i,j);
}
}
while(1)
{
LED0 = !LED0;
delay_ms(500);
}
}
下面是效果。
這是我們利用畫點(diǎn)函數(shù)通過循環(huán)畫出的一個(gè)矩形。
5. 利用描點(diǎn)函數(shù)制作各種不同的液晶顯示應(yīng)用
本喵使用的是正點(diǎn)原子家的開發(fā)板,原子家提供了很多的應(yīng)用函數(shù),我們在用到的時(shí)候直接調(diào)用就可以。
這是一個(gè)圓圈,一個(gè)矩形,還有倆條直線。
??字符顯示
??英文字符顯示
由于計(jì)算機(jī)只能識別 0 和 1,文字也只能以 0 和 1 的形式在計(jì)算機(jī)里存儲,所以我們需要對文字進(jìn)行編碼才能讓計(jì)算機(jī)處理,編碼的過程就是規(guī)定特定的 01 數(shù)字符串來表示特定的文字,最簡單的字符編碼例子是 ASCII 碼。
如上圖中的ASCII碼表中的部分字符,大寫字母A的ASCII碼值的16進(jìn)制形式是0X41,空格的ASCII碼值的16進(jìn)制形式是0X20。
現(xiàn)在已經(jīng)知道了,將0X41規(guī)定為字符A,那么這個(gè)A是怎么輸出到屏幕的呢?
根據(jù)上面LCD的顯示原理我們知道,LCD顯示的過程就是將特定的像素點(diǎn)用特定的顏色填充的過程
- 上圖中,高度有60個(gè)像素點(diǎn),寬度有30個(gè)像素點(diǎn)
- 將這1800個(gè)像素點(diǎn)中的部分像素點(diǎn)用藍(lán)色的填充,就能夠得出字符A在屏幕中的樣子。
這個(gè)填充過程是怎樣的呢?
就像這樣,從下到上,從左到右,挨個(gè)點(diǎn)的填充。
- 每一個(gè)像素點(diǎn)對應(yīng)著一個(gè)比特位,當(dāng)該位為1時(shí),填充該點(diǎn),當(dāng)該位為0不填充,按照這樣的方式就可以顯示出我們想要的字符。
繼續(xù)看我們的字符A
這樣表示每一個(gè)像素點(diǎn)是1還是0的比特位,按照16進(jìn)制形式表示出來多個(gè)數(shù)據(jù)組成的數(shù)組,叫做字模。
按照上面動圖中的填充順序,我們可以根據(jù)字符A的樣子,倒推出每個(gè)像素點(diǎn)是0還是1,然后通過上面講解的畫點(diǎn)函數(shù)將這些點(diǎn)用顏色填充,但是如果是每個(gè)字符都這樣靠我們眼睛去看它對應(yīng)的點(diǎn)是1還是0的話就太費(fèi)勁了,所以我們需要借助一些工具。
使用這樣一個(gè)軟件,將上面紅色框中字節(jié)的寬度和高度都設(shè)置成60,因?yàn)檫@個(gè)尺寸是漢字的尺寸,對應(yīng)的因?yàn)樽址褪菍捨?0,高為60,其中30和60都是比特位的個(gè)數(shù)。
再點(diǎn)擊有下腳紅色框中的生成字模,就能得到我們上面分析過程中的每一個(gè)像素點(diǎn)是1還是0,用16進(jìn)制表示出來。
下面的紅色數(shù)據(jù)是我們上面說到的字符A的字模。
將字模直接用在程序中,按照動圖中的掃描順序?qū)?yīng)的字模中的數(shù)據(jù),將比特位的值為1的像素點(diǎn)填充,為0的不填充,得到的就是我們要的字符A。
這樣一個(gè)A我們就在屏幕上顯示出來了。
??英文字符串的顯示
單個(gè)英文字符已經(jīng)成功顯示出來了,那么一個(gè)英文字符串是如何在LCD上顯示出來的呢?
顯示原理和單個(gè)的一樣,只將單個(gè)字符的顯示放在一個(gè)循環(huán)里,這樣就可以顯示多個(gè)字符,組合在一起就成了字符串。
既然要顯示字符串,那么就不能僅有一個(gè)字母的字模了,需要將ASCII碼表中的所有字符的字模都生成出來,這被叫做字庫。
我們可以將每個(gè)字符都用上面制作單個(gè)字模的軟件制作出來,然后將這些生成的16進(jìn)制數(shù)據(jù)放在一個(gè)數(shù)組中,但是這樣會非常繁瑣,我們可以用一個(gè)軟件,直接生成英文的字庫。
這樣生成了所有ASCII碼表中字符組成的字庫了。
是一個(gè)數(shù)據(jù)量非常大的數(shù)組。
將其放在一個(gè)單獨(dú)的頭文件中,并且添加到工程中。
來看一下代碼如何調(diào)用這個(gè)字庫。
- 首先字符串中的每個(gè)字符必須在ASCII碼表的空格和‘~’之間,符號條件才進(jìn)行循環(huán)顯示。
- 當(dāng)一行顯示滿以后,要將x左邊重置到開始處,但是y坐標(biāo)要換到下一行。
- 在沒有超出顯示區(qū)域高度后,調(diào)用顯示單個(gè)字符的函數(shù)
- 這樣循環(huán)到\0后便結(jié)束顯示。
- 通過要顯示字符的大小,利用公式計(jì)算出單個(gè)字模所占的字節(jié)個(gè)數(shù)。
- 調(diào)用對應(yīng)size(字體大小)的字庫,這里制作了三種大小的字庫,1212,1616,和24*24
- 藍(lán)色框中通過循環(huán)判斷字庫中單個(gè)字模中每一個(gè)字節(jié)的每一位的情況,如果為1,則填充像素。
我們看效果:
此時(shí)我們想要的字符串就打印出來了。
注意:
ASCII碼值和是怎么和字庫聯(lián)系上的呢?文章來源:http://www.zghlxwxcb.cn/news/detail-780081.html
- 首先,要打印的字符與字符空格作差,得到的值是該字符的偏移量
- 將該偏移量與字符編碼對應(yīng)的字模大小相乘,得到的就是該字符的字模偏移量
- 在字庫中通過字模的偏移量找到該字符對應(yīng)字模的起始數(shù)據(jù),挨個(gè)掃描顯示出來
??總結(jié)
LCD液晶顯示關(guān)鍵有倆個(gè)點(diǎn),一個(gè)就是使用FSMC模擬8080時(shí)序來控制LIL9341芯片,進(jìn)而控制LCD液晶屏,另一個(gè)就是顯示原理,理解了顯示屏上是如何顯示內(nèi)容的,并且按照原理寫出相應(yīng)的代碼就可以實(shí)現(xiàn),不過在我們大部分的使用情況中,都可以使用已經(jīng)寫好的顯示函數(shù)來操作屏幕,除非需要特定的字體,這就需要我們按照原理來制作相應(yīng)的字庫。文章來源地址http://www.zghlxwxcb.cn/news/detail-780081.html
到了這里,關(guān)于【STM32】LCD液晶顯示的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!