【STM32H7教程】第51章 STM32H7的LTDC應用之LCD漢字顯示和2D圖形顯示

  • 2020 年 2 月 20 日
  • 筆記

完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第51章       STM32H7的LTDC應用之LCD漢字顯示和2D圖形顯示

本章教程為大家講解LTDC應用中最基本的漢字顯示和2D圖形顯示功能實現。

51.1 初學者重要提示

51.2 LCD相關的基礎支援

51.3 LCD硬體設計

51.4 LCD驅動設計

51.5 LCD板級支援包(bsp_ltdc_h7.c和bsp_tft_lcd.c)

51.6 LCD的驅動移植和使用

51.7 實驗常式設計框架

51.8 實驗常式說明(MDK)

51.9 實驗常式說明(IAR)

51.10 總結

51.1 初學者重要提示

  1.   學習本章節前,務必優先學習第50章,需要對LTDC的基礎知識和HAL庫的幾個常用API有個認識。
  2. 本章的第4小節LCD驅動設計非常重要。
  3.   如果自己觀察的話,LCD上電會有一個瞬間高亮的問題,在此貼進行了描述:http://www.armbbs.cn/forum.php?mod=viewthread&tid=82619 。這個解決方案已經應用到本章配套的例子上。
  4.   本章節用到的漢字方案會在下章專門為大家講解,下面是小字型檔的製作方法:http://www.armbbs.cn/forum.php?mod=viewthread&tid=202 。
  5.   測評STM32H7的LTDC+DMA2D性能,100Hz以上無壓力,刷800*480圖片和色塊僅需2.6ms一張:http://www.armbbs.cn/forum.php?mod=viewthread&tid=91489 。
  6.   調試狀態或者剛下載LCD的程式到H7裡面,螢幕會抖動,這個是正常現象,之前F429就有這個問題,詳情看此貼:http://www.armbbs.cn/forum.php?mod=viewthread&tid=16892 。

51.2 LCD相關的基礎知識

51.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解析度:

51.2.2        電阻觸摸和電容觸摸相關知識

有了TFT裸屏後還要配套電阻觸摸板或者電容觸摸板才可以獲取觸摸資訊。觸摸板是貼到TFT屏上面的,然後再通過電阻觸摸晶片就可以獲取電阻觸摸板的資訊,通過電容觸摸晶片採集電容觸摸板的資訊。教程配套開發板的顯示器使用了三種觸摸IC,電阻觸摸IC是STMPE811,電容觸摸IC是GT811和FT5X06。其中,電阻觸摸和電容觸摸兩者的區別是初學者務必要知道的:

  •   電阻觸摸晶片STMPE811其實就是ADC,返回的是ADC數值,而電容觸摸晶片GT811,GT911和FT5X06返回的是顯示器實際的坐標值。
  •   使用電阻觸摸晶片STMPE811需要做觸摸校準,而使用電容觸摸晶片GT811,GT911和FT5X06是自動校準的,無需手動校準。

下面是四線電阻觸摸板的效果:

下面是電容觸摸板的效果:

了解了這些知識,基本已經夠我們本章節使用了,更多電阻觸摸和電容觸摸的相關知識可以看這個文檔,講解比較全面:http://bbs.armfly.com/read.php?tid=14898 。

51.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]引腳即可,沒有用到的引腳可以繼續用作其它功能。

51.4 LCD驅動設計

下面將程式設計中的相關問題逐一為大家做個說明。

51.4.1 第1步,LTDC顯示記憶體使用SDRAM

設計LTDC驅動前,要先保證顯示記憶體可以正常使用,V7開發板用的外部SDRAM作為顯示記憶體。所以一定要保證SDRAM大批量讀寫數據時是正常的,SDRAM的測試可以自己專門做一個工程測試下。對於SDRAM的驅動實現,可以學習本教程第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刷新。

51.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);      }     /* 其它省略未寫 */    }

51.4.3 第3步,LTDC時鐘和時序配置

LTDC時序配置主要分三步就可以完成:

  1. 行同步,場同步和DE的極性配置。
  2. CLK時鐘配置。
  3. 時序參數配置。

下面將這三點分別做個說明:

  • 行同步,場同步和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);

至此,時序配置工作就完成了。

這裡特別注意,當前程式中實際使用的參數與本小節的參數略有區別。由於這些參數都有較大的容錯範圍,所以很多參數都可以正常使用。

51.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的速度等級。
  •   如果顯示了,但是顯示的位置不正確,可以重新調整時序參數即可。

51.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_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中斷的優先順序並使能。

51.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);  }

函數的注釋已經比較詳細。另外,背光是基於第34章的API:bsp_SetTIMOutPWM實現,關於這個函數可以看第34章節。

51.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

51.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控制器,設置橫向顯示,默認清屏為黑色。

注意事項:

  1. 調用此函數前,務必優先調用函數TOUCH_InitHard識別不同解析度面板。
  2. 通過函數LCD_SetDirection可以設置橫向,豎向,橫向180度,豎向180度顯示。在後面章節再為大家介紹具體的實現方法。

使用舉例:

作為初始化函數,直接在bsp.c文件的bsp_Init函數裡面調用即可。

51.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)。

51.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)。

51.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);

51.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)。

51.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)。

51.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)。

51.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)。

51.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)。

51.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_touch.c --- 觸摸晶片自適應驅動,根據用戶使用的觸摸IC選擇不同的驅動。另外,電阻屏的觸摸掃描,觸摸校準和觸摸濾波也是在這個文件裡面實現。

bsp_gt811.c --- 電容觸摸晶片GT811的驅動以及觸摸掃描。

bsp_gt911.c --- 電容觸摸晶片GT911的驅動以及觸摸掃描。

bsp_ft5x06.c --- 電容觸摸晶片FT5X06的驅動以及觸摸掃描。

TOUCH_STMPE811.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函數需要用到這個庫文件。

對於本章節的驅動,不推薦單獨移植了,建議直接使用本章節配套例子的基礎上做修改。

51.7 實驗常式設計框架

通過程式設計框架,讓大家先對配套常式有一個全面的認識,然後再理解細節,本次實驗常式的設計框架如下:

  第1階段,上電啟動階段:

  • 這部分在第14章進行了詳細說明。

  第2階段,進入main函數:

  •  第1步,硬體初始化,主要是MPU,Cache,HAL庫,系統時鐘,滴答定時器,LED和串口。
  •  第2步,LCD應用程式設計部分,顯示漢字和2D圖形。

51.8 實驗常式說明(MDK)

配套例子:

V7-024_LCD的漢字顯示和2D圖形顯示(小字型檔)

實驗目的:

  1. 學習LCD的小字型檔實現和2D圖形顯示。

實驗內容:

  1. 小字型檔通過此軟體生成:http://www.armbbs.cn/forum.php?mod=viewthread&tid=202 。
  2. LCD介面上顯示了漢字和2D圖形。
  3. 啟動1個200ms的自動重裝定時器,讓LED2每200ms翻轉一次。
  4. 同時在LCD介面上實現一個簡單計數,每200ms加1,計數到255後繼續從0開始。

上電後串口列印的資訊:

波特率 115200,數據位 8,奇偶校驗位無,停止位 1

程式設計:

系統棧大小分配:

RAM空間用的DTCM:

硬體外設初始化

硬體外設的初始化是在 bsp.c 文件實現:

/*  *********************************************************************************************************  *    函 數 名: bsp_Init  *    功能說明: 初始化所有的硬體設備。該函數配置CPU暫存器和外設的暫存器並初始化一些全局變數。只需要調用一次  *    形    參:無  *    返 回 值: 無  *********************************************************************************************************  */  void bsp_Init(void)  {      /* 配置MPU */      MPU_Config();        /* 使能L1 Cache */      CPU_CACHE_Enable();        /*         STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鐘:         - 調用函數HAL_InitTick,初始化滴答時鐘中斷1ms。         - 設置NVIV優先順序分組為4。       */      HAL_Init();        /*         配置系統時鐘到400MHz         - 切換使用HSE。         - 此函數會更新全局變數SystemCoreClock,並重新配置HAL_InitTick。      */      SystemClock_Config();        /*         Event Recorder:         - 可用於程式碼執行時間測量,MDK5.25及其以上版本才支援,IAR不支援。         - 默認不開啟,如果要使能此選項,務必看V7開發板用戶手冊第8章      */  #if Enable_EventRecorder == 1      /* 初始化EventRecorder並開啟 */      EventRecorderInitialize(EventRecordAll, 1U);      EventRecorderStart();  #endif        bsp_InitKey();        /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */      bsp_InitTimer();      /* 初始化滴答定時器 */      bsp_InitUart();    /* 初始化串口 */      bsp_InitExtIO();    /* 初始化FMC匯流排74HC574擴展IO. 必須在 bsp_InitLed()前執行 */      bsp_InitLed();        /* 初始化LED */        bsp_InitI2C();     /* 初始化I2C匯流排 */      TOUCH_InitHard();   /* 初始化觸摸晶片,LCD面板型號的檢查也在此函數,所以要在函數LCD_InitHard前調用 */      LCD_InitHard();     /* 初始化LCD */  }

MPU配置和Cache配置:

數據Cache和指令Cache都開啟。配置了AXI SRAM區(本例子未用到AXI SRAM),FMC的擴展IO區和SDRAM。由於SDRAM要用於LCD的顯示記憶體,方便起見,直接將其配置為WT模式。

/*  *********************************************************************************************************  *    函 數 名: MPU_Config  *    功能說明: 配置MPU  *    形    參: 無  *    返 回 值: 無  *********************************************************************************************************  */  static void MPU_Config( void )  {      MPU_Region_InitTypeDef MPU_InitStruct;        /* 禁止 MPU */      HAL_MPU_Disable();        /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */      MPU_InitStruct.Enable           = MPU_REGION_ENABLE;      MPU_InitStruct.BaseAddress      = 0x24000000;      MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;      MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;      MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;      MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;      MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;      MPU_InitStruct.Number           = MPU_REGION_NUMBER0;      MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;      MPU_InitStruct.SubRegionDisable = 0x00;      MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;        HAL_MPU_ConfigRegion(&MPU_InitStruct);          /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */      MPU_InitStruct.Enable           = MPU_REGION_ENABLE;      MPU_InitStruct.BaseAddress      = 0x60000000;      MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;      MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;      MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;      MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;      MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;      MPU_InitStruct.Number           = MPU_REGION_NUMBER1;      MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;      MPU_InitStruct.SubRegionDisable = 0x00;      MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;        HAL_MPU_ConfigRegion(&MPU_InitStruct);        /* 配置SDRAM的MPU屬性為Write through, read allocate,no write allocate */      MPU_InitStruct.Enable           = MPU_REGION_ENABLE;      MPU_InitStruct.BaseAddress      = 0xC0000000;      MPU_InitStruct.Size             = MPU_REGION_SIZE_32MB;      MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;      MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;      MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;      MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;      MPU_InitStruct.Number           = MPU_REGION_NUMBER2;      MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;      MPU_InitStruct.SubRegionDisable = 0x00;      MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;        HAL_MPU_ConfigRegion(&MPU_InitStruct);        /*使能 MPU */      HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);  }    /*  *********************************************************************************************************  *    函 數 名: CPU_CACHE_Enable  *    功能說明: 使能L1 Cache  *    形    參: 無  *    返 回 值: 無  *********************************************************************************************************  */  static void CPU_CACHE_Enable(void)  {      /* 使能 I-Cache */      SCB_EnableICache();        /* 使能 D-Cache */      SCB_EnableDCache();  }

主功能:

主程式實現如下操作:

  •   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);          }      }  }

51.9 實驗常式說明(IAR)

配套例子:

V7-024_LCD的漢字顯示和2D圖形顯示(小字型檔)

實驗目的:

  1. 學習LCD的小字型檔實現和2D圖形顯示。

實驗內容:

  1. 小字型檔通過此軟體生成:http://www.armbbs.cn/forum.php?mod=viewthread&tid=202 。
  2. LCD介面上顯示了漢字和2D圖形。
  3. 啟動1個200ms的自動重裝定時器,讓LED2每200ms翻轉一次。
  4. 同時在LCD介面上實現一個簡單計數,每200ms加1,計數到255後繼續從0開始。

上電後串口列印的資訊:

波特率 115200,數據位 8,奇偶校驗位無,停止位 1

程式設計:

系統棧大小分配:

RAM空間用的DTCM:

硬體外設初始化

硬體外設的初始化是在 bsp.c 文件實現:

/*  *********************************************************************************************************  *    函 數 名: bsp_Init  *    功能說明: 初始化所有的硬體設備。該函數配置CPU暫存器和外設的暫存器並初始化一些全局變數。只需要調用一次  *    形    參:無  *    返 回 值: 無  *********************************************************************************************************  */  void bsp_Init(void)  {      /* 配置MPU */      MPU_Config();        /* 使能L1 Cache */      CPU_CACHE_Enable();        /*         STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鐘:         - 調用函數HAL_InitTick,初始化滴答時鐘中斷1ms。         - 設置NVIV優先順序分組為4。       */      HAL_Init();        /*         配置系統時鐘到400MHz         - 切換使用HSE。         - 此函數會更新全局變數SystemCoreClock,並重新配置HAL_InitTick。      */      SystemClock_Config();        /*         Event Recorder:         - 可用於程式碼執行時間測量,MDK5.25及其以上版本才支援,IAR不支援。         - 默認不開啟,如果要使能此選項,務必看V7開發板用戶手冊第8章      */  #if Enable_EventRecorder == 1      /* 初始化EventRecorder並開啟 */      EventRecorderInitialize(EventRecordAll, 1U);      EventRecorderStart();  #endif        bsp_InitKey();        /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */      bsp_InitTimer();      /* 初始化滴答定時器 */      bsp_InitUart();    /* 初始化串口 */      bsp_InitExtIO();    /* 初始化FMC匯流排74HC574擴展IO. 必須在 bsp_InitLed()前執行 */      bsp_InitLed();        /* 初始化LED */        bsp_InitI2C();     /* 初始化I2C匯流排 */      TOUCH_InitHard();   /* 初始化觸摸晶片,LCD面板型號的檢查也在此函數,所以要在函數LCD_InitHard前調用 */      LCD_InitHard();     /* 初始化LCD */  }

MPU配置和Cache配置:

數據Cache和指令Cache都開啟。配置了AXI SRAM區(本例子未用到AXI SRAM),FMC的擴展IO區和SDRAM。由於SDRAM要用於LCD的顯示記憶體,方便起見,直接將其配置為WT模式。

/*  *********************************************************************************************************  *    函 數 名: MPU_Config  *    功能說明: 配置MPU  *    形    參: 無  *    返 回 值: 無  *********************************************************************************************************  */  static void MPU_Config( void )  {      MPU_Region_InitTypeDef MPU_InitStruct;        /* 禁止 MPU */      HAL_MPU_Disable();        /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */      MPU_InitStruct.Enable           = MPU_REGION_ENABLE;      MPU_InitStruct.BaseAddress      = 0x24000000;      MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;      MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;      MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;      MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;      MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;      MPU_InitStruct.Number           = MPU_REGION_NUMBER0;      MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;      MPU_InitStruct.SubRegionDisable = 0x00;      MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;        HAL_MPU_ConfigRegion(&MPU_InitStruct);          /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */      MPU_InitStruct.Enable           = MPU_REGION_ENABLE;      MPU_InitStruct.BaseAddress      = 0x60000000;      MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;      MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;      MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;      MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;      MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;      MPU_InitStruct.Number           = MPU_REGION_NUMBER1;      MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;      MPU_InitStruct.SubRegionDisable = 0x00;      MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;        HAL_MPU_ConfigRegion(&MPU_InitStruct);        /* 配置SDRAM的MPU屬性為Write through, read allocate,no write allocate */      MPU_InitStruct.Enable           = MPU_REGION_ENABLE;      MPU_InitStruct.BaseAddress      = 0xC0000000;      MPU_InitStruct.Size             = MPU_REGION_SIZE_32MB;      MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;      MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;      MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;      MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;      MPU_InitStruct.Number           = MPU_REGION_NUMBER2;      MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;      MPU_InitStruct.SubRegionDisable = 0x00;      MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;        HAL_MPU_ConfigRegion(&MPU_InitStruct);        /*使能 MPU */      HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);  }    /*  *********************************************************************************************************  *    函 數 名: CPU_CACHE_Enable  *    功能說明: 使能L1 Cache  *    形    參: 無  *    返 回 值: 無  *********************************************************************************************************  */  static void CPU_CACHE_Enable(void)  {      /* 使能 I-Cache */      SCB_EnableICache();        /* 使能 D-Cache */      SCB_EnableDCache();  }

主功能:

主程式實現如下操作:

  •  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);          }      }  }

51.10   總結

本章節涉及到的知識點比較多,需要大家花點時間去掌握,直至可以獨立驅動一個顯示器。

Tags: