【第3版emWin教程】第4章 emWin上手之STM32H7 LTDC基礎知識
教程不斷更新中:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98429
第4章 emWin上手之STM32H7 LTDC基礎知識
本章教程為大家講解LTDC應用中最基本的漢字顯示和2D圖形顯示功能實現。
4.1 初學者重要提示
4.2 LCD相關的基礎支援
4.3 LCD硬體設計
4.4 LCD驅動設計
4.5 LCD板級支援包(bsp_ltdc_h7.c和bsp_tft_lcd.c)
4.6 LCD的驅動移植和使用
4.7 實驗例程設計框架
4.8 實驗例程說明
4.9 總結
4.1 初學者重要提示
- 本章是為emWin的LTDC移植部分做準備。
- 本章的第4小節LCD驅動設計非常重要。
- 如果自己觀察的話,LCD上電會有一個瞬間高亮的問題,在此貼進行了描述:http://www.armbbs.cn/forum.php?mod=viewthread&tid=82619 。這個解決方案已經應用到本章配套的例子上。
- 本章節用到的漢字方案會在下章專門為大家講解,下面是小字型檔的製作方法:http://www.armbbs.cn/forum.php?mod=viewthread&tid=202 。
- 測評STM32H7的LTDC+DMA2D效能,100Hz以上無壓力,刷800*480圖片和色塊僅需2.6ms一張:http://www.armbbs.cn/forum.php?mod=viewthread&tid=91489
- 除錯狀態或者剛下載LCD的程式到H7裡面,螢幕會抖動,這個是正常現象,之前F429就有這個問題,詳情看此貼:http://www.armbbs.cn/forum.php?mod=viewthread&tid=16892 。
- 遮蔽MDK AC6使用中文GBK編碼的警告方法,讓大家可以繼續使用GBK編碼漢字:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98670 。
4.2 LCD相關的基礎知識
4.2.1 顯示屏相關知識
顯示屏的結構有必要給大家普及下,這裡我們通過如下三種類型的顯示屏進行說明,基本已經涵蓋我們常用的方式了。
- RA8875 + RGB介面裸屏
首先RA8875是一個顯示屏控制器,自帶視訊記憶體,它的作用就是讓不支援RGB介面的MCU也可以使用RGB介面的大屏。這起到了一個橋接的作用,可以將RGB介面屏轉換成8080匯流排介面、SPI介面或者I2C介面方式。這種情況下,甚至低速的51微控制器都可以外接大屏了。另外像SSD1963也是同樣的作用。
- ili9488類顯示屏
這種型別是把顯示控制器和顯示屏都整合好了,支援8080匯流排介面,有些還支援SPI或者I2C介面,而且視訊記憶體也都集成了,不過主要是驅動一些小屏。像ili9341,ili9326,SPFD5420等也是一樣的。此外還要注意,部分這種型別顯示屏也是支援RGB介面的,像ST官方的STM32F429探索板外接的ili9431就是用的RGB介面。
- STM32H7 + SDRAM + RGB介面裸屏
這個是我們本章節要講解的,STM32H7是自帶LCD控制器的,再配合SDRAM作為顯示屏的視訊記憶體,整體作用跟RA8875是一樣的,可以直接外接RGB介面的屏了。
有了這些認識後,對於裸屏還有些知識點需要了解。首先,裸屏本身不是什麼控制晶片都沒有,其構成也是比較複雜的,有興趣瞭解的話,可以搜尋關鍵字“TFT結構”進行學習。其次,TFT裸屏中主要的兩個IC是Gate Driver IC和Source Driver IC,這兩個IC的引腳都超級多,基本都是幾百個引腳。最後,不管使用的哪種裸屏,一般都有規格書,會給出時序引數,這個在配置STM32H7的LTDC時要用到,如果規格書沒有直接給出時序引數,則會給出使用的Driver IC型號,使用者可以搜尋此Driver IC的手冊,在手冊中會給出。
為了讓大家有個感性認識,我們來看一看TFT裸屏的實際效果,下面是SPDF5420顯示屏,400*240解析度:
下面是TFT裸屏,480*272解析度:
下面是TFT裸屏,800*480解析度:
4.2.2 電阻觸控和電容觸控相關知識
有了TFT裸屏後還要配套電阻觸控板或者電容觸控板才可以獲取觸控資訊。觸控板是貼到TFT屏上面的,然後再通過電阻觸控晶片就可以獲取電阻觸控板的資訊,通過電容觸控晶片採集電容觸控板的資訊。教程配套開發板的顯示屏使用了三種觸控IC,電阻觸控IC是STMPE811,電容觸控IC是GT811和FT5X06。其中,電阻觸控和電容觸控兩者的區別是初學者務必要知道的:
- 電阻觸控晶片STMPE811其實就是ADC,返回的是ADC數值,而電容觸控晶片GT811,GT911和FT5X06返回的是顯示屏實際的座標值。
- 使用電阻觸控晶片STMPE811需要做觸控校準,而使用電容觸控晶片GT811,GT911和FT5X06是自動校準的,無需手動校準。
下面是四線電阻觸控板的效果:
下面是電容觸控板的效果:
瞭解了這些知識,基本已經夠我們本章節使用了,更多電阻觸控和電容觸控的相關知識可以看這個文件,講解比較全面:http://www.armbbs.cn/forum.php?mod=viewthread&tid=14898 。
4.3 LCD硬體設計
下面是RGB888硬體介面的原理圖,STM32-V7開發板製作了三個硬體介面。
- 加高2層的雙排母介面
- 2.54mm的排針介面
- FPC軟排線介面方式
瞭解了原理圖後,再來看下實際的介面效果:
通過上面的原理圖,我們要了解以下幾個問題:
- V7開發板採用的是RGB888硬體介面,也許大家會問ARGB8888這種顏色格式怎麼用於這種介面?Alpha通道是軟體程式設計的時候用的,用於設定透明度,透明度會反應到RGB顏色值上。
- STM32H7支援的8種顏色格式都可以在RGB888硬體介面上實現。
- 如果大家用的是16位色的RGB565顏色格式,那麼僅需用到LCD_R[7:3]、LCD_G[7:2] 和 LCD_B[7:3]引腳即可,沒有用到的引腳可以繼續用作其它功能。
4.4 LCD驅動設計
下面將程式設計中的相關問題逐一為大家做個說明。
4.4.1 第1步,LTDC視訊記憶體使用SDRAM
設計LTDC驅動前,要先保證視訊記憶體可以正常使用,V7開發板用的外部SDRAM作為視訊記憶體。所以一定要保證SDRAM大批量讀寫資料時是正常的,SDRAM的測試可以自己專門做一個工程測試下。對於SDRAM的驅動實現,可以學習V7開發板BSP驅動教程第49章。不管你使用的是鎂光的,海力士的,三星的,ISSI的或者華邦的,實現方法基本都是一樣的。
V7開發板使用ISSI的32位頻寬、32MB的SDRAM,如果想最大限度的發揮STM32H7驅動SDRAM的效能,強烈建議使用32位頻寬的SDRAM,或者兩個16位SDRAM組成32位頻寬的SDRAM也是可以的。那SDRAM主要起到什麼作用呢?作用有二:
- 用作顯示屏的視訊記憶體
STM32H7的LTDC外接RGB介面屏是沒有視訊記憶體的,所以需要SDRAM用作視訊記憶體。如果使用者選擇STM32H7 LTDC的顏色格式是32位色ARGB8888,那麼所需要視訊記憶體大小(單位位元組)是:顯示屏寬 * 顯示屏高 * (32/8), 其中32/8是表示這種顏色格式的一個畫素點需要4個位元組來表示。又比如配置顏色格式是16位色的RGB565,那麼需要的視訊記憶體大小是:顯示屏寬 * 顯示屏高 * (16/8),其中16/8是表示這種顏色格式的一個畫素點需要2個位元組來表示。其它的顏色格式,依此類推。
- 用作GUI動態記憶體
如果想要實現炫酷效果,GUI是極其消耗動態記憶體的,所以使用者可以將SDRAM除了用於視訊記憶體以外的所有記憶體全部用作GUI動態記憶體。
如果SDRAM的驅動測試已經沒有問題了,就可以將其新增到工程裡面了,V7使用的SDRAM驅動檔案是bsp_fmc_sdram.c。圖層1佔用2MB,圖層2佔用2MB,最後28MB可做其它使用。也許會有初學者會問,每個圖層分配2MB是不是有些多了?實際上不多的,因為我們要讓不同的顏色格式都通用,這裡分配2MB的話,教程例項使用很方便。大家實際專案中的使用可以配置成實際大小。具體的配置如下,詳情見bsp_fmc_sdram.h檔案:
#define EXT_SDRAM_ADDR ((uint32_t)0xC0000000) #define EXT_SDRAM_SIZE (32 * 1024 * 1024) /* LCD視訊記憶體,第1頁, 分配2M位元組 */ #define SDRAM_LCD_BUF1 EXT_SDRAM_ADDR /* LCD視訊記憶體,第2頁, 分配2M位元組 */ #define SDRAM_LCD_BUF2 (EXT_SDRAM_ADDR + SDRAM_LCD_SIZE) #define SDRAM_LCD_SIZE (2 * 1024 * 1024) /* 每層2M */ #define SDRAM_LCD_LAYER 2 /* 2層 */ /* 剩下的28M位元組,提供給應用程式使用 */ #define SDRAM_APP_BUF (EXT_SDRAM_ADDR + SDRAM_LCD_SIZE * SDRAM_LCD_LAYER) #define SDRAM_APP_SIZE (EXT_SDRAM_SIZE - SDRAM_LCD_SIZE * SDRAM_LCD_LAYER)
由於使用者要重新整理資料到SDRAM,而且LTDC也要從SDRAM讀取資料,這就屬於多匯流排訪問SDRAM,此時就要注意Cache配置。為了使用方便起見,直接將SDRAM配置為WT模式,這樣使用者重新整理的資料就可以立即寫入到SDRAM,從而不影響LTDC重新整理。
4.4.2 第2步,LTDC涉及到的引腳配置
本章第3小節用到了哪些引腳,這些引腳全部要做初始化,初始化時別忘了初始化引腳對應的時鐘:
static void LCDH7_ConfigLTDC(void) { /* 配置LCD相關的GPIO */ { /* GPIOs Configuration */ /* +------------------------+-----------------------+----------------------------+ + LCD pins assignment + +------------------------+-----------------------+----------------------------+ | LCDH7_TFT R0 <-> PI.15 | LCDH7_TFT G0 <-> PJ.07 | LCDH7_TFT B0 <-> PJ.12 | | LCDH7_TFT R1 <-> PJ.00 | LCDH7_TFT G1 <-> PJ.08 | LCDH7_TFT B1 <-> PJ.13 | | LCDH7_TFT R2 <-> PJ.01 | LCDH7_TFT G2 <-> PJ.09 | LCDH7_TFT B2 <-> PJ.14 | | LCDH7_TFT R3 <-> PJ.02 | LCDH7_TFT G3 <-> PJ.10 | LCDH7_TFT B3 <-> PJ.15 | | LCDH7_TFT R4 <-> PJ.03 | LCDH7_TFT G4 <-> PJ.11 | LCDH7_TFT B4 <-> PK.03 | | LCDH7_TFT R5 <-> PJ.04 | LCDH7_TFT G5 <-> PK.00 | LCDH7_TFT B5 <-> PK.04 | | LCDH7_TFT R6 <-> PJ.05 | LCDH7_TFT G6 <-> PK.01 | LCDH7_TFT B6 <-> PK.05 | | LCDH7_TFT R7 <-> PJ.06 | LCDH7_TFT G7 <-> PK.02 | LCDH7_TFT B7 <-> PK.06 | ------------------------------------------------------------------------------- | LCDH7_TFT HSYNC <-> PI.12 | LCDTFT VSYNC <-> PI.13 | | LCDH7_TFT CLK <-> PI.14 | LCDH7_TFT DE <-> PK.07 | ----------------------------------------------------- */ GPIO_InitTypeDef GPIO_Init_Structure; /*##-1- Enable peripherals and GPIO Clocks #################################*/ /* 使能LTDC時鐘 */ __HAL_RCC_LTDC_CLK_ENABLE(); /* 使能GPIO時鐘 */ __HAL_RCC_GPIOI_CLK_ENABLE(); __HAL_RCC_GPIOJ_CLK_ENABLE(); __HAL_RCC_GPIOK_CLK_ENABLE(); /* GPIOI 配置 */ GPIO_Init_Structure.Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15; GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP; GPIO_Init_Structure.Pull = GPIO_NOPULL; GPIO_Init_Structure.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC; HAL_GPIO_Init(GPIOI, &GPIO_Init_Structure); /* GPIOJ 配置 */ GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | \ GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | 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; GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP; GPIO_Init_Structure.Pull = GPIO_NOPULL; GPIO_Init_Structure.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC; HAL_GPIO_Init(GPIOJ, &GPIO_Init_Structure); /* GPIOK 配置 */ GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | \ GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP; GPIO_Init_Structure.Pull = GPIO_NOPULL; GPIO_Init_Structure.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC; HAL_GPIO_Init(GPIOK, &GPIO_Init_Structure); } /* 其它省略未寫 */ }
4.4.3 第3步,LTDC時鐘和時序配置
LTDC時序配置主要分三步就可以完成:
- 行同步,場同步和DE的極性配置。
- CLK時鐘配置。
- 時序引數配置。
下面將這三點分別做個說明:
- 行同步,場同步和DE的極性配置
這裡以V7開發板7寸RGB屏使用的source driver ic OTA7001為例進行說明(手冊下載地址:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=18528)。
這幾項配置要看OTA7001手冊上面的時序圖,對於DE模式,行同步和場同步的極性配置為高或者為低均可。因為我們這裡使用的就是DE模式,所以主要配置DE的極性。這裡要特別注意一個小問題,看時序圖是DE高電平時資料有效,但是配置的時候要設定為低電平才可以。
下面的是V7開發板配套的7寸裸屏使用的source driver ic OTA7001的時序圖:
實際配置STM32H7的工程時,將DE配置為低有效才是上面截圖的效果,這個問題的確是有些奇葩了。
大家使用的時候也特別注意。
/* 配置訊號極性 */ hltdc_F.Init.HSPolarity = LTDC_HSPOLARITY_AL; /* HSYNC 低電平有效 */ hltdc_F.Init.VSPolarity = LTDC_VSPOLARITY_AL; /* VSYNC 低電平有效 */ hltdc_F.Init.DEPolarity = LTDC_DEPOLARITY_AL; /* DE 低電平有效 */ hltdc_F.Init.PCPolarity = LTDC_PCPOLARITY_IPC; /* Pixel Clock 或者Dot Clock極性 */
ps:注意LTDC_PCPOLARITY_IPC和LTDC_PCPOLARITY_IIPC兩種極性,選擇不當會有一些問題。比如下面這個現象,底邊會有黑的。
下面是用示波器實際測量的波形效果,黃色的波形是DE訊號,另一個是行同步訊號Hsync:
將波形放縮後:
- LTDC的畫素時鐘輸出配置
在OTA7001手冊上面給出了支援的時鐘範圍:
由於USB和LTDC都是用的PLL3產生時鐘,為了方便起見,直接將產生LTDC時鐘的PLL3R設定為24MHz(PLL3Q輸出的48MHz時鐘供USB使用)。
首先,輸入時鐘 PLL3_VCO Input = HSE_VALUE/PLL3M = 25MHz/5 = 5MHz 輸出時鐘 PLL3_VCO Output = PLL3_VCO Input * PLL3N = 5MHz * 48 = 240MHz PLLLCDCLK = PLL3_VCO Output/PLL3R = 240 / 10 = 24MHz 最後,LTDC 時鐘LTDC clock frequency = PLLLCDCLK = 24MHz
這裡再額外補充點知識,LCD_CLK=24MHz時
重新整理率 = 24MHz /((Width + HSYNC_W + HBP + HFP)*(Height + VSYNC_W + VBP + VFP))
= 24000000/((800 + 96 + 10 + 10)*(480 + 2 + 10 + 10))
= 24000000/(916*502)
= 52Hz
根據需要可以加大,100Hz重新整理率完全沒問題,設定PeriphClkInitStruct.PLL3.PLL3N = 100即可,
此時的LTDC時鐘是50MHz
重新整理率 = 50MHz /((Width + HSYNC_W+ HBP+ HFP )*(Height + VSYNC_W +VBP +VFP ))
= 5000000/(916*502)
= 108.7Hz
- 時序引數配置
時序引數的配置也比較容易,其實就是先看STM32H7參考手冊上面的公式說明,說是公式,其實就是簡單的加減法。然後將OTA7001的引數代到這個公式就可以了。又因為手冊一般都是給出了引數的最小值,典型值和最大值,大家可以根據實際情況做簡單的調整即可。需要用到的引數:
uint16_t Width, Height, HSYNC_W, VSYNC_W, HBP, HFP, VBP, VFP;
Horizontal Synchronization (Hsync) 對應變數HSYNC_W
Horizontal Back Porch (HBP) 對應變數HBP
Active Width 對應變數Width
Horizontal Front Porch (HFP) 對應變數HFP
Vertical Synchronization (Vsync) 對應變數VSYNC_W
Vertical Back Porch (VBP) 對應變數VBP
Active Heigh 對應變數Heigh
Vertical Front Porch (VFP) 對應變數VFP
STM32H7參考手冊上面的公式如下:
********************************************************************************************************* * LCD_TFT 同步時序配置(整理自官方做的一個截圖,言簡意賅): * ---------------------------------------------------------------------------- * * Total Width * <---------------------------------------------------> * Hsync width HBP Active Width HFP * <---><--><--------------------------------------><--> * ____ ____|_______________________________________|____ * |___| | | | * | | | * __| | | | * /|\ /|\ | | | | * | VSYNC| | | | | * |Width\|/ |__ | | | * | /|\ | | | | * | VBP | | | | | * | \|/_____|_________|_______________________________________| | * | /|\ | | / / / / / / / / / / / / / / / / / / / | | * | | | |/ / / / / / / / / / / / / / / / / / / /| | * Total | | | |/ / / / / / / / / / / / / / / / / / / /| | * Heigh | | | |/ / / / / / / / / / / / / / / / / / / /| | * |Active| | |/ / / / / / / / / / / / / / / / / / / /| | * |Heigh | | |/ / / / / / Active Display Area / / / /| | * | | | |/ / / / / / / / / / / / / / / / / / / /| | * | | | |/ / / / / / / / / / / / / / / / / / / /| | * | | | |/ / / / / / / / / / / / / / / / / / / /| | * | | | |/ / / / / / / / / / / / / / / / / / / /| | * | | | |/ / / / / / / / / / / / / / / / / / / /| | * | \|/_____|_________|_______________________________________| | * | /|\ | | * | VFP | | | * \|/ \|/_____|______________________________________________________| * * * 每個LCD裝置都有自己的同步時序值: * Horizontal Synchronization (Hsync) * Horizontal Back Porch (HBP) * Active Width * Horizontal Front Porch (HFP) * * Vertical Synchronization (Vsync) * Vertical Back Porch (VBP) * Active Heigh * Vertical Front Porch (VFP) * * LCD_TFT 視窗水平和垂直的起始以及結束位置 : * ---------------------------------------------------------------- * * HorizontalStart = (Offset_X + Hsync + HBP); * HorizontalStop = (Offset_X + Hsync + HBP + Window_Width - 1); * VarticalStart = (Offset_Y + Vsync + VBP); * VerticalStop = (Offset_Y + Vsync + VBP + Window_Heigh - 1); * *********************************************************************************************************
OTA7001手冊中已經給出了我們需要的數值:
uint16_t Width, Height, HSYNC_W, HBP, HFP, VSYNC_W, VBP, VFP。
引數設定好了,直接代入公式並與行同步,場同步和DE一起初始化:
/* 時序配置 */ hltdc_F.Init.HorizontalSync = (HSYNC_W - 1); hltdc_F.Init.VerticalSync = (VSYNC_W - 1); hltdc_F.Init.AccumulatedHBP = (HSYNC_W + HBP - 1); hltdc_F.Init.AccumulatedVBP = (VSYNC_W + VBP - 1); hltdc_F.Init.AccumulatedActiveH = (Height + VSYNC_W + VBP - 1); hltdc_F.Init.AccumulatedActiveW = (Width + HSYNC_W + HBP - 1); hltdc_F.Init.TotalHeigh = (Height + VSYNC_W + VBP + VFP - 1); hltdc_F.Init.TotalWidth = (Width + HSYNC_W + HBP + HFP - 1);
至此,時序配置工作就完成了。
這裡特別注意,當前程式中實際使用的引數與本小節的引數略有區別。由於這些引數都有較大的容錯範圍,所以很多引數都可以正常使用。
4.4.4 第4步,如何驗證LTDC的時序配置是否正確
下面說一個最重要的問題,配置好時序了,怎麼檢查自己的配置是否成功了?使用者僅需在函式LCDH7_ConfigLTDC裡面的如下程式碼後面加上兩個函式:
/* 配置LTDC */ if (HAL_LTDC_Init(&hltdc_F) != HAL_OK) { /* 初始化錯誤 */ Error_Handler(__FILE__, __LINE__); } /* 下面是新增的 */ LCD_SetBackLight(BRIGHT_DEFAULT); while(1);
加上這兩行程式碼後,再將背景層設定為一個合適的顏色,建議設定成紅色,方便觀察:
/* 配置背景層顏色 */ hltdc_F.Init.Backcolor.Blue = 0; hltdc_F.Init.Backcolor.Green = 0; hltdc_F.Init.Backcolor.Red = 255;
如果背景層可以正常顯示紅色,說明引腳和時序配置都是沒有問題的。如果不成功要從以下幾個方面著手檢查:
- 首先要清楚一點,當前的配置是否成功與SDRAM沒有任何關係,因為背景層還用不到SDRAM,圖層1和圖層2才需要SDRAM做視訊記憶體使用。
- 從硬體著手檢查,保證STM32H7晶片焊接沒問題,TFT介面一定要牢固,防止接觸不良,特別是使用FPC軟排線的時候,測試階段,軟排線越短越好。有時候也可能是顯示屏有問題,最好可以備兩個顯示屏測試。
- 從軟體配置著手檢查,檢視LTDC涉及到的所有引腳是否配置,引腳時鐘是否使能。有時候無法顯示也有可能是板子硬體設計不規範導致干擾較大造成的,此時,可以降低LTDC所涉及到GPIO的速度等級。
- 如果顯示了,但是顯示的位置不正確,可以重新調整時序引數即可。
4.4.5 第5步,LTDC圖層配置
LTDC的圖層配置就比較好理解了,下面是完整的驅動程式碼:
1. /* 2. ****************************************************************************************************** 3. * 函 數 名: LCDH7_ConfigLTDC 4. * 功能說明: 配置LTDC 5. * 形 參: 無 6. * 返 回 值: 無 7. ****************************************************************************************************** 8. */ 9. static void LCDH7_ConfigLTDC(void) 10. { 11. /* GPIO初始化部分省略未寫 */ 12. 13. /*##-2- LTDC初始化 #############################################################*/ 14. { 15. LTDC_LayerCfgTypeDef pLayerCfg; 16. uint16_t Width, Height, HSYNC_W, HBP, HFP, VSYNC_W, VBP, VFP; 17. RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; 18. 19. /* 支援6種面板 */ 20. switch (g_LcdType) 21. { 22. case LCD_35_480X320: /* 3.5寸 480 * 320 */ 23. Width = 480; 24. Height = 272; 25. HSYNC_W = 10; 26. HBP = 20; 27. HFP = 20; 28. VSYNC_W = 20; 29. VBP = 20; 30. VFP = 20; 31. break; 32. 33. case LCD_43_480X272: /* 4.3寸 480 * 272 */ 34. Width = 480; 35. Height = 272; 36. 37. HSYNC_W = 40; 38. HBP = 2; 39. HFP = 2; 40. VSYNC_W = 9; 41. VBP = 2; 42. VFP = 2; 43. 44. /* LCD 時鐘配置 */ 45. /* PLL3_VCO Input = HSE_VALUE/PLL3M = 25MHz/5 = 5MHz */ 46. /* PLL3_VCO Output = PLL3_VCO Input * PLL3N = 5MHz * 48 = 240MHz */ 47. /* PLLLCDCLK = PLL3_VCO Output/PLL3R = 240 / 10 = 24MHz */ 48. /* LTDC clock frequency = PLLLCDCLK = 24MHz */ 49. /* 50. 重新整理率 = 24MHz /((Width + HSYNC_W + HBP + HFP)*(Height + VSYNC_W + VBP + VFP)) 51. = 24000000/((480 + 40 + 2 + 2)*(272 + 9 + 2 + 2)) 52. = 24000000/(524*285) 53. = 160Hz 54. 55. 當前這個配置方便使用者使用PLL3Q輸出的48MHz時鐘供USB使用。 56. */ 57. PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC; 58. PeriphClkInitStruct.PLL3.PLL3M = 5; 59. PeriphClkInitStruct.PLL3.PLL3N = 48; 60. PeriphClkInitStruct.PLL3.PLL3P = 2; 61. PeriphClkInitStruct.PLL3.PLL3Q = 5; 62. PeriphClkInitStruct.PLL3.PLL3R = 10; 63. HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); 64. break; 65. 66. case LCD_50_480X272: /* 5.0寸 480 * 272 */ 67. Width = 480; 68. Height = 272; 69. 70. HSYNC_W = 40; 71. HBP = 2; 72. HFP = 2; 73. VSYNC_W = 9; 74. VBP = 2; 75. VFP = 2; 76. break; 77. 78. case LCD_50_800X480: /* 5.0寸 800 * 480 */ 79. case LCD_70_800X480: /* 7.0寸 800 * 480 */ 80. Width = 800; 81. Height = 480; 82. 83. HSYNC_W = 96; 84. HBP = 10; 85. HFP = 10; 86. VSYNC_W = 2; 87. VBP = 10; 88. VFP = 10; 89. 90. /* LCD 時鐘配置 */ 91. /* PLL3_VCO Input = HSE_VALUE/PLL3M = 25MHz/5 = 5MHz */ 92. /* PLL3_VCO Output = PLL3_VCO Input * PLL3N = 5MHz * 48 = 240MHz */ 93. /* PLLLCDCLK = PLL3_VCO Output/PLL3R = 240 / 10 = 24MHz */ 94. /* LTDC clock frequency = PLLLCDCLK = 24MHz */ 95. /* 96. 重新整理率 = 24MHz /((Width + HSYNC_W + HBP + HFP)*(Height + VSYNC_W + VBP + VFP)) 97. = 24000000/((800 + 96 + 10 + 10)*(480 + 2 + 10 + 10)) 98. = 24000000/(916*502) 99. = 52Hz 100. 101. 根據需要可以加大,100Hz重新整理率完全沒問題,設定PeriphClkInitStruct.PLL3.PLL3N = 100即可 102. 此時的LTDC時鐘是50MHz 103. 重新整理率 = 50MHz /((Width + HSYNC_W + HBP + HFP )*(Height + VSYNC_W + VBP +VFP )) 104. = 5000000/(916*502) 105. = 108.7Hz 106. 107. 當前這個配置方便使用者使用PLL3Q輸出的48MHz時鐘供USB使用。 108. */ 109. PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC; 110. PeriphClkInitStruct.PLL3.PLL3M = 5; 111. PeriphClkInitStruct.PLL3.PLL3N = 48; 112. PeriphClkInitStruct.PLL3.PLL3P = 2; 113. PeriphClkInitStruct.PLL3.PLL3Q = 5; 114. PeriphClkInitStruct.PLL3.PLL3R = 10; 115. HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); 116. break; 117. 118. case LCD_70_1024X600: /* 7.0寸 1024 * 600 */ 119. /* 實測畫素時鐘 = 53.7M */ 120. Width = 1024; 121. Height = 600; 122. 123. HSYNC_W = 2; 124. HBP = 157; 125. HFP = 160; 126. VSYNC_W = 2; 127. VBP = 20; 128. VFP = 12; 129. 130. PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC; 131. PeriphClkInitStruct.PLL3.PLL3M = 5; 132. PeriphClkInitStruct.PLL3.PLL3N = 48; 133. PeriphClkInitStruct.PLL3.PLL3P = 2; 134. PeriphClkInitStruct.PLL3.PLL3Q = 5; 135. PeriphClkInitStruct.PLL3.PLL3R = 10; 136. HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); 137. break; 138. 139. default: 140. Width = 800; 141. Height = 480; 142. 143. HSYNC_W = 80; 144. HBP = 10; 145. HFP = 10; 146. VSYNC_W = 10; 147. VBP = 10; 148. VFP = 10; 149. 150. /* LCD 時鐘配置 */ 151. /* PLL3_VCO Input = HSE_VALUE/PLL3M = 25MHz/5 = 5MHz */ 152. /* PLL3_VCO Output = PLL3_VCO Input * PLL3N = 5MHz * 48 = 240MHz */ 153. /* PLLLCDCLK = PLL3_VCO Output/PLL3R = 240 / 10 = 24MHz */ 154. /* LTDC clock frequency = PLLLCDCLK = 24MHz */ 155. /* 156. 重新整理率 = 24MHz /((Width + HSYNC_W + HBP + HFP)*(Height + VSYNC_W + VBP + VFP)) 157. = 24000000/((800 + 96 + 10 + 10)*(480 + 2 + 10 + 10)) 158. = 24000000/(916*502) 159. = 52Hz 160. 161. 根據需要可以加大,100Hz重新整理率完全沒問題,設定PeriphClkInitStruct.PLL3.PLL3N = 100即可 162. 此時的LTDC時鐘是50MHz 163. 重新整理率 = 50MHz /((Width + HSYNC_W + HBP + HFP )*(Height + VSYNC_W + VBP +VFP )) 164. = 5000000/(916*502) 165. = 108.7Hz 166. 167. 當前這個配置方便使用者使用PLL3Q輸出的48MHz時鐘供USB使用。 168. */ 169. PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC; 170. PeriphClkInitStruct.PLL3.PLL3M = 5; 171. PeriphClkInitStruct.PLL3.PLL3N = 48; 172. PeriphClkInitStruct.PLL3.PLL3P = 2; 173. PeriphClkInitStruct.PLL3.PLL3Q = 5; 174. PeriphClkInitStruct.PLL3.PLL3R = 10; 175. HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); 176. break; 177. } 178. 179. g_LcdHeight = Height; 180. g_LcdWidth = Width; 181. 182. /* 配置訊號極性 */ 183. hltdc_F.Init.HSPolarity = LTDC_HSPOLARITY_AL; /* HSYNC 低電平有效 */ 184. hltdc_F.Init.VSPolarity = LTDC_VSPOLARITY_AL; /* VSYNC 低電平有效 */ 185. hltdc_F.Init.DEPolarity = LTDC_DEPOLARITY_AL; /* DE 低電平有效 */ 186. hltdc_F.Init.PCPolarity = LTDC_PCPOLARITY_IPC; 187. 188. /* 時序配置 */ 189. hltdc_F.Init.HorizontalSync = (HSYNC_W - 1); 190. hltdc_F.Init.VerticalSync = (VSYNC_W - 1); 191. hltdc_F.Init.AccumulatedHBP = (HSYNC_W + HBP - 1); 192. hltdc_F.Init.AccumulatedVBP = (VSYNC_W + VBP - 1); 193. hltdc_F.Init.AccumulatedActiveH = (Height + VSYNC_W + VBP - 1); 194. hltdc_F.Init.AccumulatedActiveW = (Width + HSYNC_W + HBP - 1); 195. hltdc_F.Init.TotalHeigh = (Height + VSYNC_W + VBP + VFP - 1); 196. hltdc_F.Init.TotalWidth = (Width + HSYNC_W + HBP + HFP - 1); 197. 198. /* 配置背景層顏色 */ 199. hltdc_F.Init.Backcolor.Blue = 0; 200. hltdc_F.Init.Backcolor.Green = 0; 201. hltdc_F.Init.Backcolor.Red = 0; 202. 203. hltdc_F.Instance = LTDC; 204. 205. /* 開始配置圖層 ------------------------------------------------------*/ 206. /* 視窗顯示區設定 */ 207. pLayerCfg.WindowX0 = 0; 208. pLayerCfg.WindowX1 = Width; 209. pLayerCfg.WindowY0 = 0; 210. pLayerCfg.WindowY1 = Height; 211. 212. /* 配置顏色格式 */ 213. pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565; 214. 215. /* 視訊記憶體地址 */ 216. pLayerCfg.FBStartAdress = LCDH7_FRAME_BUFFER; 217. 218. /* Alpha常數 (255 表示完全不透明) */ 219. pLayerCfg.Alpha = 255; 220. 221. /* 無背景色 */ 222. pLayerCfg.Alpha0 = 0; /* 完全透明 */ 223. pLayerCfg.Backcolor.Blue = 0; 224. pLayerCfg.Backcolor.Green = 0; 225. pLayerCfg.Backcolor.Red = 0; 226. 227. /* 配置圖層混合因數 */ 228. pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA; 229. pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA; 230. 231. /* 配置行列大小 */ 232. pLayerCfg.ImageWidth = Width; 233. pLayerCfg.ImageHeight = Height; 234. 235. /* 配置LTDC */ 236. if (HAL_LTDC_Init(&hltdc_F) != HAL_OK) 237. { 238. /* 初始化錯誤 */ 239. Error_Handler(__FILE__, __LINE__); 240. } 241. 242. /* 配置圖層1 */ 243. if (HAL_LTDC_ConfigLayer(&hltdc_F, &pLayerCfg, LTDC_LAYER_1) != HAL_OK) 244. { 245. /* 初始化錯誤 */ 246. Error_Handler(__FILE__, __LINE__); 247. } 248. 249. #if 0 250. /* 配置圖層2 */ 251. if (HAL_LTDC_ConfigLayer(&hltdc_F, &pLayerCfg, LTDC_LAYER_2) != HAL_OK) 252. { 253. /* 初始化錯誤 */ 254. Error_Handler(__FILE__, __LINE__); 255. } 256. #endif 257. } 258. 259. #if 1 260. HAL_NVIC_SetPriority(LTDC_IRQn, 0xE, 0); 261. HAL_NVIC_EnableIRQ(LTDC_IRQn); 262. #endif 263. }
下面將幾個關鍵的地方做個闡釋:
- 第20行,六種顯示面板的LTDC輸出時鐘和時序引數配置,六種面板的識別是在bsp_ts_touch.c檔案中實現的。大家自己配置時用不到這個,僅需提供一組時序引數和輸出時鐘即可,除非專案中需要切換不同顯示屏。
- 第78-116行,這裡是7寸面板的LTDC時鐘輸出配置和時序引數配置,配置方法在前面幾步已經介紹,這裡不再贅述,其它面板的設定方法是一樣的。
- 第179-180行,全域性變數g_LcdWidth和g_LcdHeight在檔案bsp_tft_lcd.c檔案定義,用來記錄顯示屏的解析度。
- 第183-186行,用來配置行同步,場同步,DE資料使能和LTDC畫素時鐘的極性。
- 第189-196行,配置時序。
- 第199-201行,配置背景層顏色。
- 第207-210行,用來設定圖層在LCD顯示區的起始位置和結束位置。
- 第213行,用來設定圖層的顏色格式,STM32H7支援8種顏色格式,這裡是設定為RGB565。
- 第216行,用來設定圖層的顯示首地址。
- 第219-229行,在第50章的2.5小節詳細講解了這行程式碼的作用。
- 第232-233行,用於設定從視訊記憶體中要讀取的行長和行數。
- 第236-240行,配置LTDC的基本引數。
- 第243-247行,配置圖層1。
- 第251-255行,配置圖層2,暫時未用到圖層2(通過條件編譯取消執行)。
- 第260-261行,配置LTDC中斷的優先順序並使能。
4.4.6 第6步,LCD背光實現
LCD的背光是PWM驅動方式,涉及到的程式碼如下:
/* ********************************************************************************************************* * 函 數 名: LCD_SetBackLight * 功能說明: 初始化控制LCD背景光的GPIO,配置為PWM模式。 * 當關閉背光時,將CPU IO設定為浮動輸入模式(推薦設定為推輓輸出,並驅動到低電平);將TIM3關閉 省電 * 形 參: _bright 亮度,0是滅,255是最亮 * 返 回 值: 無 ********************************************************************************************************* */ void LCD_SetBackLight(uint8_t _bright) { s_ucBright = _bright; /* 儲存背光值 */ LCD_SetPwmBackLight(s_ucBright); } /* ********************************************************************************************************* * 函 數 名: LCD_SetPwmBackLight * 功能說明: 初始化控制LCD背景光的GPIO,配置為PWM模式。 * 當關閉背光時,將CPU IO設定為浮動輸入模式(推薦設定為推輓輸出,並驅動到低電平);將TIM3關閉 省電 * 形 參: _bright 亮度,0是滅,255是最亮 * 返 回 值: 無 ********************************************************************************************************* */ static void LCD_SetPwmBackLight(uint8_t _bright) { /* 背光有CPU輸出PWM控制,PA0/TIM5_CH1/TIM2_CH1 */ //bsp_SetTIMOutPWM(GPIOA, GPIO_PIN_0, TIM5, 1, 100, (_bright * 10000) /255); //bsp_SetTIMOutPWM(GPIOA, GPIO_PIN_0, TIM5, 1, 20000, (_bright * 10000) /255); bsp_SetTIMOutPWM(GPIOA, GPIO_PIN_8, TIM1, 1, 20000, (_bright * 10000) /255); }
函式的註釋已經比較詳細。另外,背光是基於V7開發板BSP驅動教程第34章的API:bsp_SetTIMOutPWM實現,關於這個函式可以看第34章節。
4.5 LCD板級支援包(bsp_ltdc_h7.c和 bsp_tft_lcd.c)
bsp_ltdc_h7.c是H7的LTDC驅動檔案,涉及到的函式比較多。而bsp_tft_lcd.c檔案是在LTDC的API基礎上面封裝出更通用的函式,不僅僅LTDC,像RA8875,ili9488等也可通過此檔案進行封裝。
本章節主要給幾個常用的基本API做個介紹:
- LCD_InitHard
- LCD_ClrScr
- LCD_SetBackLight
- LCD_DispStr
- LCD_PutPixel
- LCD_DrawLine
- LCD_DrawRect
- LCD_DrawCircle
- LCD_Fill_Rect
4.5.1 函式LCD_InitHard
函式原型:
/* ********************************************************************************************************* * 函 數 名: LCD_InitHard * 功能說明: 初始化LCD * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void LCD_InitHard(void) { LCD_HardReset(); LCDH7_InitHard(); LCD_SetDirection(0); LCD_ClrScr(CL_BLACK); /* 清屏,顯示全黑 */ // LCD_SetBackLight(BRIGHT_DEFAULT); }
函式描述:
此函式用於初始化LCD,配置了STM32H7的LTDC控制器,設定橫向顯示,預設清屏為黑色。
注意事項:
- 呼叫此函式前,務必優先呼叫函式TOUCH_InitHard識別不同解析度面板。
- 通過函式LCD_SetDirection可以設定橫向,豎向,橫向180度,豎向180度顯示。在後面章節再為大家介紹具體的實現方法。
使用舉例:
作為初始化函式,直接在bsp.c檔案的bsp_Init函式裡面呼叫即可。
4.5.2 函式LCD_ClrScr
函式原型:
void LCD_ClrScr(uint16_t _usColor)
函式描述:
此函式用於清屏操作。
函式引數:
- 第1個引數是清屏的顏色值設定,在bsp_tft_lcd.h檔案中定義了幾個常用的顏色:
/* LCD 顏色程式碼,CL_是Color的簡寫 16Bit由高位至低位, RRRR RGGG GGGB BBBB 下面的RGB 巨集將24位的RGB值轉換為16位格式。 啟動windows的畫筆程式,點選編輯顏色,選擇自定義顏色,可以獲得的RGB值。 推薦使用迷你取色器軟體獲得你看到的介面顏色。 */ #define RGB(R,G,B) (((R >> 3) << 11) | ((G >> 2) << 5) | (B >> 3))/* 將8位R,G,B轉化為 16位RGB565格式 */ /* 解碼出 R=8bit G=8bit B=8bit */ #define RGB565_R(x) ((x >> 8) & 0xF8) #define RGB565_G(x) ((x >> 3) & 0xFC) #define RGB565_B(x) ((x << 3) & 0xF8) /* 解碼出 R=5bit G=6bit B=5bit */ #define RGB565_R2(x) ((x >> 11) & 0x1F) #define RGB565_G2(x) ((x >> 5) & 0x3F) #define RGB565_B2(x) ((x >> 0) & 0x1F) enum { CL_WHITE = RGB(255,255,255), /* 白色 */ CL_BLACK = RGB( 0, 0, 0), /* 黑色 */ CL_RED = RGB(255, 0, 0), /* 紅色 */ CL_GREEN = RGB( 0,255, 0), /* 綠色 */ CL_BLUE = RGB( 0, 0,255), /* 藍色 */ CL_YELLOW = RGB(255,255, 0), /* 黃色 */ CL_GREY = RGB( 98, 98, 98), /* 深灰色 */ CL_GREY1 = RGB( 150, 150, 150), /* 淺灰色 */ CL_GREY2 = RGB( 180, 180, 180), /* 淺灰色 */ CL_GREY3 = RGB( 200, 200, 200), /* 最淺灰色 */ CL_GREY4 = RGB( 230, 230, 230), /* 最淺灰色 */ CL_BUTTON_GREY = RGB( 220, 220, 220), /* WINDOWS 按鈕表面灰色 */ CL_MAGENTA = 0xF81F, /* 紅紫色,洋紅色 */ CL_CYAN = 0x7FFF, /* 藍綠色,青色 */ CL_BLUE1 = RGB( 0, 0, 240), /* 深藍色 */ CL_BLUE2 = RGB( 0, 0, 128), /* 深藍色 */ CL_BLUE3 = RGB( 68, 68, 255), /* 淺藍色1 */ CL_BLUE4 = RGB( 0, 64, 128), /* 淺藍色1 */ /* UI 介面 Windows控制元件常用色 */ CL_BTN_FACE = RGB(236, 233, 216), /* 按鈕表面顏色(灰) */ CL_BTN_FONT = CL_BLACK, /* 按鈕字型顏色(黑) */ CL_BOX_BORDER1 = RGB(172, 168,153), /* 分組框主線顏色 */ CL_BOX_BORDER2 = RGB(255, 255,255), /* 分組框陰影線顏色 */ CL_MASK = 0x9999 /* 顏色掩碼,用於文字背景透明 */ };
使用舉例:
比如將LCD清為紅色,即LCD_ClrSrc(CL_RED)。
4.5.3 函式LCD_SetBackLight
函式原型:
/* ********************************************************************************************************* * 函 數 名: LCD_SetBackLight * 功能說明: 初始化控制LCD背景光的GPIO,配置為PWM模式。 * 當關閉背光時,將CPU IO設定為浮動輸入模式(推薦設定為推輓輸出,並驅動到低電平);將TIM3關閉 省電 * 形 參: _bright 亮度,0是滅,255是最亮 * 返 回 值: 無 ********************************************************************************************************* */ void LCD_SetBackLight(uint8_t _bright) { s_ucBright = _bright; /* 儲存背光值 */ LCD_SetPwmBackLight(s_ucBright); }
函式描述:
此函式主要用於LCD背光設定。
函式引數:
- 第1個引數是背光引數,0表示滅,255表示最亮。
使用舉例:
比如設定LCD最亮LCD_SetBackLight(255)。
4.5.4 函式LCD_DispStr
函式原型:
/* ********************************************************************************************************* * 函 數 名: LCD_DispStr * 功能說明: 在LCD指定座標(左上角)顯示一個字串 * 形 參: * _usX : X座標 * _usY : Y座標 * _ptr : 字串指標 * _tFont : 字型結構體,包含顏色、背景色(支援透明)、字型程式碼、文字間距等引數 * 返 回 值: 無 ********************************************************************************************************* */ void LCD_DispStr(uint16_t _usX, uint16_t _usY, char *_ptr, FONT_T *_tFont) { LCD_DispStrEx(_usX, _usY, _ptr, _tFont, 0, 0); }
函式描述:
此函式用於在LCD指定位置顯示字串,中英文均支援。由於這個函式涉及到的知識點比較多,下章節會專門為大家講解。
函式引數:
- 第1個引數是x軸座標位置。
- 第2個引數是y軸座標位置。
- 第3個引數是要顯示的字串。
- 第4個引數是FONT_T型別結構體,定義如下:
/* 字型屬性結構, 用於LCD_DispStr() */ typedef struct { FONT_CODE_E FontCode; /* 字型程式碼 FONT_CODE_E */ uint16_t FrontColor; /* 字型顏色 */ uint16_t BackColor; /* 文字背景顏色,透明 */ uint16_t Space; /* 文字間距,單位 = 畫素 */ }FONT_T;
使用舉例:
比如顯示12點陣和16點陣字元。
FONT_T tFont12; /* 定義一個字型結構體變數,用於設定字型引數 */ FONT_T tFont16; /* 定義一個字型結構體變數,用於設定字型引數 */ /* 設定字型引數 */ { tFont12.FontCode = FC_ST_12; /* 字型程式碼 12點陣 */ tFont12.FrontColor = CL_WHITE; /* 字型顏色 */ tFont12.BackColor = CL_BLUE; /* 文字背景顏色 */ tFont12.Space = 0; /* 文字間距,單位 = 畫素 */ } /* 設定字型引數 */ { tFont16.FontCode = FC_ST_16; /* 字型程式碼 16點陣 */ tFont16.FrontColor = CL_WHITE; /* 字型顏色 */ tFont16.BackColor = CL_BLUE; /* 文字背景顏色 */ tFont16.Space = 0; /* 文字間距,單位 = 畫素 */ } LCD_ClrScr(CL_BLUE); LCD_DispStr(5, 3, "故人西辭黃鶴樓,煙花三月下揚州。", &tFont12); LCD_DispStr(5, 18, "孤帆遠影碧空盡,唯見長江天際流。", &tFont12); LCD_DispStr(5, 38, "故人西辭黃鶴樓,煙花三月下揚州。", &tFont16); LCD_DispStr(5, 58, "孤帆遠影碧空盡,唯見長江天際流。", &tFont16);
4.5.5 函式LCD_PutPixel
函式原型:
/* ********************************************************************************************************* * 函 數 名: LCD_PutPixel * 功能說明: 畫1個畫素 * 形 參: * _usX,_usY : 畫素座標 * _usColor : 畫素顏色 * 返 回 值: 無 ********************************************************************************************************* */ void LCD_PutPixel(uint16_t _usX, uint16_t _usY, uint16_t _usColor) { LCDH7_PutPixel(_usX, _usY, _usColor); }
函式描述:
此函式用於在指定位置顯示一個畫素點。
函式引數:
- 第1個引數是x軸座標位置。
- 第2個引數是y軸座標位置。
- 第3個引數是畫素點顏色。
使用舉例:
比如在座標(0, 0)顯示紅色,那就是LCD_PutPixel(0, 0, CL_RED)。
4.5.6 函式LCD_DrawLine
函式原型:
/* ********************************************************************************************************* * 函 數 名: LCD_DrawLine * 功能說明: 採用 Bresenham 演算法,在2點間畫一條直線。 * 形 參: * _usX1, _usY1 : 起始點座標 * _usX2, _usY2 : 終止點Y座標 * _usColor : 顏色 * 返 回 值: 無 ********************************************************************************************************* */ void LCD_DrawLine(uint16_t _usX1 , uint16_t _usY1 , uint16_t _usX2 , uint16_t _usY2 , uint16_t _usColor) { LCDH7_DrawLine(_usX1 , _usY1 , _usX2, _usY2 , _usColor); }
函式描述:
此函式用於任意兩點間的直線繪製,採用的Bresenham演算法,關於這個演算法的介紹在帖子:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=4048 。
函式引數:
- 第1個引數是起始座標x軸位置。
- 第2個引數是起始座標y軸位置。
- 第3個引數是結束座標x軸位置。
- 第4個引數是結束座標y軸位置。
- 第5個引數是直線顏色。
使用舉例:
比如在座標(0, 0)到座標(100, 100)顯示一條紅色直線,那就是LCD_DrawLine(0, 0, 100, 100, CL_RED)。
4.5.7 函式LCD_DrawRect
函式原型:
/* ********************************************************************************************************* * 函 數 名: LCD_DrawRect * 功能說明: 繪製水平放置的矩形。 * 形 參: * _usX,_usY: 矩形左上角的座標 * _usHeight : 矩形的高度 * _usWidth : 矩形的寬度 * 返 回 值: 無 ********************************************************************************************************* */ void LCD_DrawRect(uint16_t _usX, uint16_t _usY, uint16_t _usHeight, uint16_t _usWidth, uint16_t _usColor) { LCDH7_DrawRect(_usX, _usY, _usHeight, _usWidth, _usColor); }
函式描述:
此函式用於繪製矩形框。
函式引數:
- 第1個引數是左上角X軸位置。
- 第2個引數是左上角Y軸位置。
- 第3個引數是矩形框的長度。
- 第4個引數是矩形框的高度。
- 第5個引數是矩形框的顏色。
使用舉例:
比如在座標(0, 0)繪製一個長度為100,高度為50的紅色矩形框,那麼就是LCD_DrawRect(0, 0, 100, 50, CL_RED)。
4.5.8 函式LCD_DrawCircle
函式原型:
/* ********************************************************************************************************* * 函 數 名: LCD_DrawCircle * 功能說明: 繪製一個圓,筆寬為1個畫素 * 形 參: * _usX,_usY : 圓心的座標 * _usRadius : 圓的半徑 * 返 回 值: 無 ********************************************************************************************************* */ void LCD_DrawCircle(uint16_t _usX, uint16_t _usY, uint16_t _usRadius, uint16_t _usColor) { LCDH7_DrawCircle(_usX, _usY, _usRadius, _usColor); }
函式描述:
此函式顯示一個圓圈。
函式引數:
- 第1個引數是圓心座標x軸。
- 第2個引數是圓心座標y軸。
- 第3個引數是圓半徑。
- 第4個引數是圓顏色。
使用舉例:
比如在座標(200, 200)繪製一個半徑為50的紅色圓圈,那麼就是LCD_DrawCircle (200, 200, 50, CL_RED)。
4.5.9 函式LCD_Fill_Rect
函式原型:
/* ********************************************************************************************************* * 函 數 名: LCD_Fill_Rect * 功能說明: 用一個顏色值填充一個矩形。【emWin 中有同名函式 LCD_FillRect,因此加了下劃線區分】 * 形 參: * _usX,_usY: 矩形左上角的座標 * _usHeight : 矩形的高度 * _usWidth : 矩形的寬度 * 返 回 值: 無 ********************************************************************************************************* */ void LCD_Fill_Rect(uint16_t _usX, uint16_t _usY, uint16_t _usHeight, uint16_t _usWidth, uint16_t _usColor) { LCDH7_FillRect(_usX, _usY, _usHeight, _usWidth, _usColor); }
函式描述:
此函式用繪製一個填充的矩形。
函式引數:
- 第1個引數是左上角X軸位置。
- 第2個引數是左上角Y軸位置。
- 第3個引數是填充矩形的長度。
- 第4個引數是填充矩形的高度。
- 第5個引數是填充矩形的顏色。
使用舉例:
比如在座標(0, 0)繪製一個長度為100,高度為50的紅色填充矩形,那麼就是LCD_Fill_Rect (0, 0, 100, 50, CL_RED)。
4.6 LCD驅動移植和使用
由於我們開發板要做不同顯示屏的自適應,所以關聯了好多個檔案,所有關於TFT,觸控,觸控校準引數儲存和字型的檔案都要新增進來。這裡有必要先為大家做個介紹才好移植。
- Fonts分組中的檔案
asc12.c ---12點陣ASCII字元字型檔。
asc16.c---16點陣ASCII字元字型檔。
hz12.c --- 12點陣宋體小字型檔。
hz16.c --- 16點陣宋體小字型檔。
hz24.c --- 24點陣宋體小字型檔。
hz32.c --- 32點陣宋體小字型檔。
ra8875_asc_width.c -- RA8875 ASCII字型的寬度表。
- bsp分組中的檔案
bsp_fmc_sdram.c---用於TFT的視訊記憶體。
bsp_tft_h7.c --- STM32H7的LTDC的驅動檔案。
bsp_tft_lcd.c --- TFT驅動和相關API函式彙總檔案,比如RA8875顯示屏,ili9488顯示屏,STM32H7所帶TFT控制器驅動顯示屏都可以有一個單獨的檔案,然後將這些顯示屏相同功能的函式彙總成一個函式。這個檔案就起到這個作用。
bsp_ts_touch.c --- 觸控晶片自適應驅動,根據使用者使用的觸控IC選擇不同的驅動。另外,電阻屏的觸控掃描,觸控校準和觸控濾波也是在這個檔案裡面實現。
bsp_ts_gt811.c --- 電容觸控晶片GT811的驅動以及觸控掃描。
bsp_ts_gt911.c --- 電容觸控晶片GT911的驅動以及觸控掃描。
bsp_ts_ft5x06.c --- 電容觸控晶片FT5X06的驅動以及觸控掃描。
bsp_ts_smpe811.c --- 電阻觸控晶片STMPE811的驅動。
bsp_tim_pwm.c --- 定時器驅動,顯示屏的背光要用到PWM。
bsp_i2c_gpio.c --- I2C介面驅動,EEPROM,GT811,GT911,STMPE811和FT5X06都要用到,因為他們的介面都是I2C方式。
bsp_eeprom_24xx.c --- EEPROM驅動,用於儲存電阻屏觸控校準引數。
- HAL_Driver分組中的檔案
stm32h7xx_ll_fmc.c 和stm32h7xx_hal_sdram.c ---SDRAM的驅動檔案。
stm32h7xx_hal_tim.c --- 顯示屏的背光是用PWM驅動的,需要用到這個定時器庫檔案。
stm32h7xx_hal_ltdc.c --- LTDC相關的API函式需要用到這個庫檔案。
stm32h7xx_hal_dma2d.c --- DMA2D相關的API函式需要用到這個庫檔案。
對於本章節的驅動,不推薦單獨移植了,建議直接使用本章節配套例子的基礎上做修改。
4.7 實驗例程設計框架
通過程式設計框架,讓大家先對配套例程有一個全面的認識,然後再理解細節,本次實驗例程的設計框架如下:
第1階段,上電啟動階段:
- 這部分在V7板子BSP驅動教程的第14章進行了詳細說明。
第2階段,進入main函式:
- 第1步,硬體初始化,主要是MPU,Cache,HAL庫,系統時鐘,滴答定時器,LED和串列埠。
- 第2步,LCD應用程式設計部分,顯示漢字和2D圖形。
4.8 實驗例程說明
配套例子:
V7-501_LCD BASE
實驗目的:
- 學習LCD的小字型檔實現和2D圖形顯示。
實驗內容:
- 小字型檔通過此軟體生成:http://www.armbbs.cn/forum.php?mod=viewthread&tid=202 。
- LCD介面上顯示了漢字和2D圖形。
- 啟動1個200ms的自動重灌定時器,讓LED2每200ms翻轉一次。
- 同時在LCD介面上實現一個簡單計數,每200ms加1,計數到255後繼續從0開始
主功能:
主程式實現如下操作:
- LCD介面上顯示了漢字和2D圖形。
- 啟動1個200ms的自動重灌定時器,讓LED2每200ms翻轉一次。
- 同時在LCD介面上實現一個簡單計數,每200ms加1,計數到255後繼續從0開始。
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: c程式入口 * 形 參: 無 * 返 回 值: 錯誤程式碼(無需處理) ********************************************************************************************************* */ int main(void) { FONT_T tFont12; /* 定義一個字型結構體變數,用於設定字型引數 */ FONT_T tFont16; /* 定義一個字型結構體變數,用於設定字型引數 */ uint8_t buf[100], count = 0; /* 設定字型引數 */ { tFont12.FontCode = FC_ST_12; /* 字型程式碼 12點陣 */ tFont12.FrontColor = CL_WHITE; /* 字型顏色 */ tFont12.BackColor = CL_BLUE; /* 文字背景顏色 */ tFont12.Space = 0; /* 文字間距,單位 = 畫素 */ } /* 設定字型引數 */ { tFont16.FontCode = FC_ST_16; /* 字型程式碼 16點陣 */ tFont16.FrontColor = CL_WHITE; /* 字型顏色 */ tFont16.BackColor = CL_BLUE; /* 文字背景顏色 */ tFont16.Space = 0; /* 文字間距,單位 = 畫素 */ } bsp_Init(); /* 硬體初始化 */ PrintfLogo(); /* 列印例程名稱和版本等資訊 */ /* 延遲200ms再點亮背光,避免瞬間高亮 */ bsp_DelayMS(200); /* 清屏 */ LCD_ClrScr(CL_BLUE); /* 顯示漢字 */ LCD_DispStr(5, 3, "故人西辭黃鶴樓,煙花三月下揚州。", &tFont12); LCD_DispStr(5, 18, "孤帆遠影碧空盡,唯見長江天際流。", &tFont12); LCD_DispStr(5, 38, "故人西辭黃鶴樓,煙花三月下揚州。", &tFont16); LCD_DispStr(5, 58, "孤帆遠影碧空盡,唯見長江天際流。", &tFont16); /* 繪製2D圖形 */ LCD_DrawLine(5, 120, 100, 220, CL_RED); LCD_DrawRect(120, 120, 100, 100, CL_RED); LCD_DrawCircle(280, 170, 50, CL_RED); LCD_Fill_Rect (340, 120, 100, 100, CL_BUTTON_GREY); /* 介面整體顯示完畢後,再開啟背光,設定為預設亮度 */ bsp_DelayMS(100); LCD_SetBackLight(BRIGHT_DEFAULT); bsp_StartAutoTimer(0, 200); /* 啟動1個200ms的自動重灌的定時器,軟體定時器0 */ while (1) { /* 判斷軟體定時器0是否超時 */ if(bsp_CheckTimer(0)) { /* 每隔200ms 進來一次 */ bsp_LedToggle(2); sprintf((char *)buf, "count = %03d", count++); LCD_DispStr(5, 90, (char *)buf, &tFont16); } } }
4.9 總結
本章節涉及到的知識點比較多,需要大家花點時間去掌握,直至可以獨立驅動一個顯示屏。