最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255
本章教程为大家讲解LTDC应用中最基本的汉字显示和2D图形显示功能实现。
目录
第41章 STM32F429的LTDC应用之LCD汉字显示和2D图形显示
41.1 初学者重要提示
41.2 LCD相关的基础知识
41.2.1 显示屏相关知识
41.2.2 电阻触摸和电容触摸相关知识
41.3 LCD硬件设计
41.4 LCD驱动设计
41.4.1 第1步,LTDC显存使用SDRAM
41.4.2 第2步,LTDC涉及到的引脚配置
41.4.3 第3步,LTDC时钟和时序配置
41.4.4 第4步,如何验证LTDC的时序配置是否正确
41.4.5 第5步,LTDC图层配置
41.4.6 第6步,LCD背光实现
41.5 LCD板级支持包(bsp_ltdc_429.c和 bsp_tft_lcd.c)
41.5.1 函数LCD_InitHard
41.5.2 函数LCD_ClrScr
41.5.3 函数LCD_SetBackLight
41.5.4 函数LCD_DispStr
41.5.5 函数LCD_PutPixel
41.5.6 函数LCD_DrawLine
41.5.7 函数LCD_DrawRect
41.5.8 函数LCD_DrawCircle
41.5.9 函数LCD_Fill_Rect
41.6 LCD驱动移植和使用
41.7 实验例程设计框架
41.8 实验例程说明(MDK)
41.9 实验例程说明(IAR)
41.10 总结
显示屏的结构有必要给大家普及下,这里我们通过如下三种类型的显示屏进行说明,基本已经涵盖我们常用的方式了。
首先RA8875是一个显示屏控制器,自带显存,它的作用就是让不支持RGB接口的MCU也可以使用RGB接口的大屏。这起到了一个桥接的作用,可以将RGB接口屏转换成8080总线接口、SPI接口或者I2C接口方式。这种情况下,甚至低速的51单片机都可以外接大屏了。另外像SSD1963也是同样的作用。
这种类型是把显示控制器和显示屏都集成好了,支持8080总线接口,有些还支持SPI或者I2C接口,而且显存也都集成了,不过主要是驱动一些小屏。像ili9341,ili9326,SPFD5420等也是一样的。此外还要注意,部分这种类型显示屏也是支持RGB接口的,像ST官方的STM32F429探索板外接的ili9431就是用的RGB接口。
这个是我们本章节要讲解的,STM32F429是自带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分辨率:
有了TFT裸屏后还要配套电阻触摸板或者电容触摸板才可以获取触摸信息。触摸板是贴到TFT屏上面的,然后再通过电阻触摸芯片就可以获取电阻触摸板的信息,通过电容触摸芯片采集电容触摸板的信息。教程配套开发板的显示屏使用了三种触摸IC,电阻触摸IC是STMPE811,电容触摸IC是GT811和FT5X06。其中,电阻触摸和电容触摸两者的区别是初学者务必要知道的:
下面是四线电阻触摸板的效果:
下面是电容触摸板的效果:
了解了这些知识,基本已经够我们本章节使用了,更多电阻触摸和电容触摸的相关知识可以看这个文档,讲解比较全面:http://www.armbbs.cn/forum.php?mod=viewthread&tid=14898 。
下面是RGB888硬件接口的原理图,STM32-V6开发板制作了三个硬件接口。
了解了原理图后,再来看下实际的接口效果:
通过上面的原理图,我们要了解以下几个问题:
下面将程序设计中的相关问题逐一为大家做个说明。
设计LTDC驱动前,要先保证显存可以正常使用,V6开发板用的外部SDRAM作为显存。所以一定要保证SDRAM大批量读写数据时是正常的,SDRAM的测试可以自己专门做一个工程测试下。对于SDRAM的驱动实现,可以学习本教程第39章。不管你使用的是镁光的,海力士的,三星的,ISSI的或者华邦的,实现方法基本都是一样的。
V6开发板使用镁光的32位带宽、16MB的SDRAM,如果想最大限度的发挥STM32F429驱动SDRAM的性能,强烈建议使用32位带宽的SDRAM,或者两个16位SDRAM组成32位带宽的SDRAM也是可以的。那SDRAM主要起到什么作用呢?作用有二:
STM32F429的LTDC外接RGB接口屏是没有显存的,所以需要SDRAM用作显存。如果用户选择STM32F429 LTDC的颜色格式是32位色ARGB8888,那么所需要显存大小(单位字节)是:显示屏宽 * 显示屏高 * (32/8), 其中32/8是表示这种颜色格式的一个像素点需要4个字节来表示。又比如配置颜色格式是16位色的RGB565,那么需要的显存大小是:显示屏宽 * 显示屏高 * (16/8),其中16/8是表示这种颜色格式的一个像素点需要2个字节来表示。其它的颜色格式,依此类推。
如果想要实现炫酷效果,GUI是极其消耗动态内存的,所以用户可以将SDRAM除了用于显存以外的所有内存全部用作GUI动态内存。
如果SDRAM的驱动测试已经没有问题了,就可以将其添加到工程里面了,V6使用的SDRAM驱动文件是bsp_fmc_sdram.c。图层1占用2MB,图层2占用2MB,最后12MB可做其它使用。也许会有初学者会问,每个图层分配2MB是不是有些多了?实际上不多的,因为我们要让不同的颜色格式都通用,这里分配2MB的话,教程实例使用很方便。大家实际项目中的使用可以配置成实际大小。具体的配置如下,详情见bsp_fmc_sdram.h文件:
#define EXT_SDRAM_ADDR ((uint32_t)0xC0000000) #define EXT_SDRAM_SIZE (16 * 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层 */ /* 剩下的12M字节,提供给应用程序使用 */ #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)
本章第3小节用到了哪些引脚,这些引脚全部要做初始化,初始化时别忘了初始化引脚对应的时钟:
static void LCDF4_ConfigLTDC(void) { /* 配置LCD相关的GPIO */ { /* GPIOs Configuration */ /* +------------------------+-----------------------+----------------------------+ + LCD pins assignment + +------------------------+-----------------------+----------------------------+ | LCD429_TFT R0 <-> PI.15 | LCD429_TFT G0 <-> PJ.07 | LCD429_TFT B0 <-> PJ.12 | | LCD429_TFT R1 <-> PJ.00 | LCD429_TFT G1 <-> PJ.08 | LCD429_TFT B1 <-> PJ.13 | | LCD429_TFT R2 <-> PJ.01 | LCD429_TFT G2 <-> PJ.09 | LCD429_TFT B2 <-> PJ.14 | | LCD429_TFT R3 <-> PJ.02 | LCD429_TFT G3 <-> PJ.10 | LCD429_TFT B3 <-> PJ.15 | | LCD429_TFT R4 <-> PJ.03 | LCD429_TFT G4 <-> PJ.11 | LCD429_TFT B4 <-> PK.03 | | LCD429_TFT R5 <-> PJ.04 | LCD429_TFT G5 <-> PK.00 | LCD429_TFT B5 <-> PK.04 | | LCD429_TFT R6 <-> PJ.05 | LCD429_TFT G6 <-> PK.01 | LCD429_TFT B6 <-> PK.05 | | LCD429_TFT R7 <-> PJ.06 | LCD429_TFT G7 <-> PK.02 | LCD429_TFT B7 <-> PK.06 | ------------------------------------------------------------------------------- | LCD429_TFT HSYNC <-> PI.12 | LCDTFT VSYNC <-> PI.13 | | LCD429_TFT CLK <-> PI.14 | LCD429_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); } /* 其它省略未写 */ }
LTDC时序配置主要分三步就可以完成:
下面将这三点分别做个说明:
这里以V6开发板7寸RGB屏使用的source driver ic OTA7001为例进行说明(手册下载地址:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=18528)。
这几项配置要看OTA7001手册上面的时序图,对于DE模式,行同步和场同步的极性配置为高或者为低均可。因为我们这里使用的就是DE模式,所以主要配置DE的极性。这里要特别注意一个小问题,看时序图是DE高电平时数据有效,但是配置的时候要设置为低电平才可以。
下面的是V6开发板配套的7寸裸屏使用的source driver ic OTA7001的时序图:
实际配置STM32F429的工程时,将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:
将波形放缩后:
在OTA7001手册上面给出了支持的时钟范围:
下面配置LTDC输出30MHz
PLLSAI_VCO Input = HSE_VALUE / PLL_M = 8M / 8 = 1MHz PLLSAI_VCO Output = PLLSAI_VCO Input * PLLSAI_N = 1 * 420 = 420MHz PLLLCDCLK = PLLSAI_VCO Output / PLLSAI_R = 420 / 7 = 60MHz LTDC 时钟 = PLLLCDCLK / RCC_PLLSAIDivR = 60 / 2 = 30MHz
这里再额外补充点知识,LCD_CLK=30MHz时
刷新率 = 30MHz /((Width + HSYNC_W + HBP + HFP)*(Height + VSYNC_W + VBP + VFP))
= 30000000/((800 + 96 + 10 + 10)*(480 + 2 + 10 + 10))
= 30000000/(916*502)
= 65Hz
时序参数的配置也比较容易,其实就是先看STM3F429参考手册上面的公式说明,说是公式,其实就是简单的加减法。然后将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
STM32F429参考手册上面的公式如下:
********************************************************************************************************* * 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);
至此,时序配置工作就完成了。
这里特别注意,当前程序中实际使用的参数与本小节的参数略有区别。由于这些参数都有较大的容错范围,所以很多参数都可以正常使用。
下面说一个最重要的问题,配置好时序了,怎么检查自己的配置是否成功了?用户仅需在函数LCDF4_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;
如果背景层可以正常显示红色,说明引脚和时序配置都是没有问题的。如果不成功要从以下几个方面着手检查:
LTDC的图层配置就比较好理解了,下面是完整的驱动代码:
1. /* 2. ****************************************************************************************************** 3. * 函 数 名: LCDF4_ConfigLTDC 4. * 功能说明: 配置LTDC 5. * 形 参: 无 6. * 返 回 值: 无 7. ****************************************************************************************************** 8. */ 9. static void LCDF4_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 = {0}; 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. case LCD_50_480X272: /* 5.0寸 480 * 272 */ 35. Width = 480; 36. Height = 272; 37. 38. HSYNC_W = 40; 39. HBP = 2; 40. HFP = 2; 41. VSYNC_W = 9; 42. VBP = 2; 43. VFP = 2; 44. 45. /* LCD 时钟配置 */ 46. /* 47. PLLSAI_VCO Input = HSE_VALUE / PLL_M = 8M / 8 = 1MHz 48. PLLSAI_VCO Output = PLLSAI_VCO Input * PLLSAI_N = 1 * 420 = 420MHz 49. PLLLCDCLK = PLLSAI_VCO Output / PLLSAI_R = 420 / 7 = 60MHz 50. LTDC 时钟 = PLLLCDCLK / RCC_PLLSAIDivR = 60 / 4 = 15MHz 51. */ 52. /* 53. 刷新率 = 15MHz /((Width + HSYNC_W + HBP + HFP)*(Height + VSYNC_W + VBP + VFP)) 54. = 15000000/((480 + 40 + 2 + 2)*(272 + 9 + 2 + 2)) 55. = 15000000/(524*285) 56. = 100Hz 57. 58. */ 59. PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC; 60. PeriphClkInitStruct.PLLSAI.PLLSAIN = 420; 61. PeriphClkInitStruct.PLLSAI.PLLSAIR = 7; 62. PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_4; 63. if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) 64. { 65. Error_Handler(__FILE__, __LINE__); 66. } 67. break; 68. 69. case LCD_50_800X480: /* 5.0寸 800 * 480 */ 70. case LCD_70_800X480: /* 7.0寸 800 * 480 */ 71. Width = 800; 72. Height = 480; 73. 74. HSYNC_W = 96; 75. HBP = 10; 76. HFP = 10; 77. VSYNC_W = 2; 78. VBP = 10; 79. VFP = 10; 80. 81. /* LCD 时钟配置 */ 82. /* 83. PLLSAI_VCO Input = HSE_VALUE / PLL_M = 8M / 8 = 1MHz 84. PLLSAI_VCO Output = PLLSAI_VCO Input * PLLSAI_N = 1 * 420 = 420MHz 85. PLLLCDCLK = PLLSAI_VCO Output / PLLSAI_R = 420 / 7 = 60MHz 86. LTDC 时钟 = PLLLCDCLK / RCC_PLLSAIDivR = 60 / 2 = 30MHz 87. */ 88. /* 89. 刷新率 = 30MHz /((Width + HSYNC_W + HBP + HFP)*(Height + VSYNC_W + VBP + VFP)) 90. = 30000000/((800 + 96 + 10 + 10)*(480 + 2 + 10 + 10)) 91. = 30000000/(916*502) 92. = 65Hz 93. 94. 24位或者32位色选择LTDC输出15MHz,16位或者8位30MHz 95. */ 96. PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC; 97. PeriphClkInitStruct.PLLSAI.PLLSAIN = 420; 98. PeriphClkInitStruct.PLLSAI.PLLSAIR = 7; 99. PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_2; 100. if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) 101. { 102. Error_Handler(__FILE__, __LINE__); 103. } 104. break; 105. 106. case LCD_70_1024X600: /* 7.0寸 1024 * 600,未使用 */ 107. Width = 1024; 108. Height = 600; 109. 110. HSYNC_W = 2; 111. HBP = 157; 112. HFP = 160; 113. VSYNC_W = 2; 114. VBP = 20; 115. VFP = 12; 116. 117. break; 118. 119. default: /* 未使用 */ 120. Width = 800; 121. Height = 480; 122. 123. HSYNC_W = 80; 124. HBP = 10; 125. HFP = 10; 126. VSYNC_W = 10; 127. VBP = 10; 128. VFP = 10; 129. 130. break; 131. } 132. 133. g_LcdHeight = Height; 134. g_LcdWidth = Width; 135. 136. /* 配置信号极性 */ 137. hltdc_F.Init.HSPolarity = LTDC_HSPOLARITY_AL; /* HSYNC 低电平有效 */ 138. hltdc_F.Init.VSPolarity = LTDC_VSPOLARITY_AL; /* VSYNC 低电平有效 */ 139. hltdc_F.Init.DEPolarity = LTDC_DEPOLARITY_AL; /* DE 低电平有效 */ 140. hltdc_F.Init.PCPolarity = LTDC_PCPOLARITY_IPC; 141. 142. /* 时序配置 */ 143. hltdc_F.Init.HorizontalSync = (HSYNC_W - 1); 144. hltdc_F.Init.VerticalSync = (VSYNC_W - 1); 145. hltdc_F.Init.AccumulatedHBP = (HSYNC_W + HBP - 1); 146. hltdc_F.Init.AccumulatedVBP = (VSYNC_W + VBP - 1); 147. hltdc_F.Init.AccumulatedActiveH = (Height + VSYNC_W + VBP - 1); 148. hltdc_F.Init.AccumulatedActiveW = (Width + HSYNC_W + HBP - 1); 149. hltdc_F.Init.TotalHeigh = (Height + VSYNC_W + VBP + VFP - 1); 150. hltdc_F.Init.TotalWidth = (Width + HSYNC_W + HBP + HFP - 1); 151. 152. /* 配置背景层颜色 */ 153. hltdc_F.Init.Backcolor.Blue = 0; 154. hltdc_F.Init.Backcolor.Green = 0; 155. hltdc_F.Init.Backcolor.Red = 0; 156. 157. hltdc_F.Instance = LTDC; 158. 159. /* 开始配置图层 ------------------------------------------------------*/ 160. /* 窗口显示区设置 */ 161. pLayerCfg.WindowX0 = 0; 162. pLayerCfg.WindowX1 = Width; 163. pLayerCfg.WindowY0 = 0; 164. pLayerCfg.WindowY1 = Height; 165. 166. /* 配置颜色格式 */ 167. pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565; 168. 169. /* 显存地址 */ 170. pLayerCfg.FBStartAdress = LCDF4_FRAME_BUFFER; 171. 172. /* Alpha常数 (255 表示完全不透明) */ 173. pLayerCfg.Alpha = 255; 174. 175. /* 无背景色 */ 176. pLayerCfg.Alpha0 = 0; /* 完全透明 */ 177. pLayerCfg.Backcolor.Blue = 0; 178. pLayerCfg.Backcolor.Green = 0; 179. pLayerCfg.Backcolor.Red = 0; 180. 181. /* 配置图层混合因数 */ 182. pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA; 183. pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA; 184. 185. /* 配置行列大小 */ 186. pLayerCfg.ImageWidth = Width; 187. pLayerCfg.ImageHeight = Height; 188. 189. /* 配置LTDC */ 190. if (HAL_LTDC_Init(&hltdc_F) != HAL_OK) 191. { 192. /* 初始化错误 */ 193. Error_Handler(__FILE__, __LINE__); 194. } 195. 196. /* 配置图层1 */ 197. if (HAL_LTDC_ConfigLayer(&hltdc_F, &pLayerCfg, LTDC_LAYER_1) != HAL_OK) 198. { 199. /* 初始化错误 */ 200. Error_Handler(__FILE__, __LINE__); 201. } 202. 203. #if 0 204. /* 配置图层2 */ 205. if (HAL_LTDC_ConfigLayer(&hltdc_F, &pLayerCfg, LTDC_LAYER_2) != HAL_OK) 206. { 207. /* 初始化错误 */ 208. Error_Handler(__FILE__, __LINE__); 209. } 210. #endif 211. } 212. 213. #if 1 214. HAL_NVIC_SetPriority(LTDC_IRQn, 0xE, 0); 215. HAL_NVIC_EnableIRQ(LTDC_IRQn); 216. #endif 217. }
下面将几个关键的地方做个阐释:
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); }
函数的注释已经比较详细。另外,背光是基于第24章的API:bsp_SetTIMOutPWM实现,关于这个函数可以看第24章节。
bsp_ltdc_429.c是429的LTDC驱动文件,涉及到的函数比较多。而bsp_tft_lcd.c文件是在LTDC的API基础上面封装出更通用的函数,不仅仅LTDC,像RA8875,ili9488等也可通过此文件进行封装。
本章节主要给几个常用的基本API做个介绍:
函数原型:
/* ********************************************************************************************************* * 函 数 名: LCD_InitHard * 功能说明: 初始化LCD * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ void LCD_InitHard(void) { LCD_HardReset(); LCDH7_InitHard(); LCD_SetDirection(0); LCD_ClrScr(CL_BLACK); /* 清屏,显示全黑 */ // LCD_SetBackLight(BRIGHT_DEFAULT); }
函数描述:
此函数用于初始化LCD,配置了STM32F429的LTDC控制器,设置横向显示,默认清屏为黑色。
注意事项:
使用举例:
作为初始化函数,直接在bsp.c文件的bsp_Init函数里面调用即可。
函数原型:
void LCD_ClrScr(uint16_t _usColor)
函数描述:
此函数用于清屏操作。
函数参数:
/* 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)。
函数原型:
/* ********************************************************************************************************* * 函 数 名: 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背光设置。
函数参数:
使用举例:
比如设置LCD最亮LCD_SetBackLight(255)。
函数原型:
/* ********************************************************************************************************* * 函 数 名: 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指定位置显示字符串,中英文均支持。由于这个函数涉及到的知识点比较多,下章节会专门为大家讲解。
函数参数:
/* 字体属性结构, 用于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);
函数原型:
/* ********************************************************************************************************* * 函 数 名: LCD_PutPixel * 功能说明: 画1个像素 * 形 参: * _usX,_usY : 像素坐标 * _usColor : 像素颜色 * 返 回 值: 无 ********************************************************************************************************* */ void LCD_PutPixel(uint16_t _usX, uint16_t _usY, uint16_t _usColor) { LCDH7_PutPixel(_usX, _usY, _usColor); }
函数描述:
此函数用于在指定位置显示一个像素点。
函数参数:
使用举例:
比如在坐标(0, 0)显示红色,那就是LCD_PutPixel(0, 0, CL_RED)。
函数原型:
/* ********************************************************************************************************* * 函 数 名: 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 。
函数参数:
使用举例:
比如在坐标(0, 0)到坐标(100, 100)显示一条红色直线,那就是LCD_DrawLine(0, 0, 100, 100, CL_RED)。
函数原型:
/* ********************************************************************************************************* * 函 数 名: 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); }
函数描述:
此函数用于绘制矩形框。
函数参数:
使用举例:
比如在坐标(0, 0)绘制一个长度为100,高度为50的红色矩形框,那么就是LCD_DrawRect(0, 0, 100, 50, CL_RED)。
函数原型:
/* ********************************************************************************************************* * 函 数 名: 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); }
函数描述:
此函数显示一个圆圈。
函数参数:
使用举例:
比如在坐标(200, 200)绘制一个半径为50的红色圆圈,那么就是LCD_DrawCircle (200, 200, 50, CL_RED)。
函数原型:
/* ********************************************************************************************************* * 函 数 名: 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); }
函数描述:
此函数用绘制一个填充的矩形。
函数参数:
使用举例:
比如在坐标(0, 0)绘制一个长度为100,高度为50的红色填充矩形,那么就是LCD_Fill_Rect (0, 0, 100, 50, CL_RED)。
由于我们开发板要做不同显示屏的自适应,所以关联了好多个文件,所有关于TFT,触摸,触摸校准参数保存和字体的文件都要添加进来。这里有必要先为大家做个介绍才好移植。
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_fmc_sdram.c---用于TFT的显存。
bsp_tft_429.c --- STM32F429的LTDC的驱动文件。
bsp_tft_lcd.c --- TFT驱动和相关API函数汇总文件,比如RA8875显示屏,ili9488显示屏,
STM32f429所带TFT控制器驱动显示屏都可以有一个单独的文件,然后将这些显示屏相同功能的函数汇总成一个函数。这个文件就起到这个作用。
bsp_ts_touch.c --- 触摸芯片自适应驱动,根据用户使用的触摸IC选择不同的驱动。另外,电阻屏的触摸扫描,触摸校准和触摸滤波也是在这个文件里面实现。
bsp_gt811.c --- 电容触摸芯片GT811的驱动以及触摸扫描。
bsp_gt911.c --- 电容触摸芯片GT911的驱动以及触摸扫描。
bsp_ft5x06.c --- 电容触摸芯片FT5X06的驱动以及触摸扫描。
bsp_tim_pwm.c --- 定时器驱动,显示屏的背光要用到PWM。
bsp_ts_stmpe811.c --- 电阻触摸芯片 STMPE811 的驱动。
bsp_i2c_gpio.c --- I2C接口驱动,EEPROM,GT811,GT911,STMPE811和FT5X06都要用到,因为他们的接口都是I2C方式。
bsp_eeprom_24xx.c --- EEPROM驱动,用于存储电阻屏触摸校准参数。
stm32f4xx_ll_fmc.c 和stm32f4xx_hal_sdram.c ---SDRAM的驱动文件。
stm32f4xx_hal_tim.c --- 显示屏的背光是用PWM驱动的,需要用到这个定时器库文件。
stm32f4xx_hal_ltdc.c --- LTDC相关的API函数需要用到这个库文件。
stm32f4xx_hal_dma2d.c --- DMA2D相关的API函数需要用到这个库文件。
对于本章节的驱动,不推荐单独移植了,建议直接使用本章节配套例子的基础上做修改。
通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:
第1阶段,上电启动阶段:
第2阶段,进入main函数:
配套例子:
V6-020_LCD的汉字显示和2D图形显示(小字库)
实验目的:
实验内容:
上电后串口打印的信息:
波特率 115200,数据位 8,奇偶校验位无,停止位 1
程序设计:
系统栈大小分配:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/* ********************************************************************************************************* * 函 数 名: bsp_Init * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void bsp_Init(void) { /* STM32F429 HAL 库初始化,此时系统用的还是F429自带的16MHz,HSI时钟: - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。 - 设置NVIV优先级分组为4。 */ HAL_Init(); /* 配置系统时钟到168MHz - 切换使用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 */ }
主功能:
主程序实现如下操作:
/* ********************************************************************************************************* * 函 数 名: 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); } } }
配套例子:
V7-024_LCD的汉字显示和2D图形显示(小字库)
实验目的:
实验内容:
上电后串口打印的信息:
波特率 115200,数据位 8,奇偶校验位无,停止位 1
程序设计:
系统栈大小分配:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/* ********************************************************************************************************* * 函 数 名: bsp_Init * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void bsp_Init(void) { /* STM32F429 HAL 库初始化,此时系统用的还是F429自带的16MHz,HSI时钟: - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。 - 设置NVIV优先级分组为4。 */ HAL_Init(); /* 配置系统时钟到168MHz - 切换使用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 */ }
主功能:
主程序实现如下操作:
/* ********************************************************************************************************* * 函 数 名: 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); } } }
本章节涉及到的知识点比较多,需要大家花点时间去掌握,直至可以独立驱动一个显示屏。