STM32H7教程第53章 STM32H7的LTDC应用之汉字小字库和全字库制作
Posted armfly
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32H7教程第53章 STM32H7的LTDC应用之汉字小字库和全字库制作相关的知识,希望对你有一定的参考价值。
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980
第53章 STM32H7的LTDC应用之汉字小字库和全字库制作
本章教程为大家讲解汉字小字库和全字库的制作方式,实际项目中用到的地方比较多。
53.1 初学者重要提示
53.2 使用MakeDot小软件生成C文件格式小字库方法
53.3 使用MakeDot小软件生成C文件格式全字库方法
53.4 C文件格式汉字使用方法
53.5 汉字显示方法解析
53.6 LCD驱动移植和使用
53.7 实验例程设计框架
53.8 实验例程说明(MDK)
53.9 实验例程说明(IAR)
5.10 总结
53.1 初学者重要提示
- 学习本章节前,务必优先学习第52章,需要对点阵字体字符编码有个认识。
- LTDC驱动设计和相关问题在第51章有详细说明。
- 本章节为大家讲解的小字库和全字库方法,简单易用,是直接以C文件格式存储到内部Flash的。支持12点阵,16点阵,24点阵和32点阵的ASCII以及GB2312编码汉字显示。
53.2 使用MakeDot小软件生成C文件格式小字库方法
生成方法比较简单,这里做个介绍:
53.2.1 第1步,准备好显示的字符
比如要显示如下字符,采用16点阵格式:
安富莱电子,www.armfly.com
故人西辞黄鹤楼,烟花三月下扬州。
孤帆远影碧空尽,唯见长江天际流。
53.2.2 第2步,复制要显示的字符到MakeDot小软件
选择16点阵,并将要显示的字符复制到输入窗口:
点击生成数组按钮后的效果如下:
53.2.3 第3步,复制生成的数组到工程中
在输出窗口鼠标右击,选择“全选”,然后再次鼠标右击选择复制。
这样就可以粘贴到工程的hz.c文件里面:
将点阵数据放在相应的文件里面时要注意加上两个0XFF。hz.c文件的内容如下:
/* FLASH中内嵌小字库,只包括本程序用到的汉字点阵 每行点阵数据,头2字节是汉子的内码,后面是16点阵汉子的字模数据。 */ #ifdef USE_SMALL_FONT unsigned char const g_Hz16[] = { 0xA1,0xA3, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 。 // 0x00,0x00,0x00,0x00,0x18,0x00,0x24,0x00,0x24,0x00,0x18,0x00,0x00,0x00,0x00,0x00, 0xA3,0xAC, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// , // 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x30,0x00,0x10,0x00,0x20,0x00,0x00,0x00, 0xB0,0xB2, 0x02,0x00,0x01,0x00,0x3F,0xFC,0x20,0x04,0x42,0x08,0x02,0x00,0x02,0x00,0xFF,0xFE,// 安 // 0x04,0x20,0x08,0x20,0x18,0x40,0x06,0x40,0x01,0x80,0x02,0x60,0x0C,0x10,0x70,0x08, /* 中间部分省略未写 */ 0xD7,0xD3, 0x00,0x00,0x7F,0xF8,0x00,0x10,0x00,0x20,0x00,0x40,0x01,0x80,0x01,0x00,0xFF,0xFE,// 子 // 0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x05,0x00,0x02,0x00, /* 最后一行必须用0xFF,0xFF结束,这是字库数组结束标志 */ 0xFF,0xFF }; #else unsigned char const g_Hz16[] = {0xFF, 0xFF}; #endif
添加完毕点阵数据后,在font.h文件里面使能使用小字库:
#define USE_SMALL_FONT /* 定义此行表示使用小字库, 这个宏只在bsp_tft+lcd.c中使用 */
至此就完成了小字库的汉字添加,用户就可以在使用16点阵时显示第1步中转换的字符了。
53.3 使用MakeDot小软件生成C文件格式全字库方法
生成方法比较简单,这里做个介绍:
53.3.1 第1步,准备好GB2312字符集
GB2312字符集已经在MakeDot小软件里面存好,点击汉字编码按钮可以看到:
53.3.2 第2步,复制GB2312全部字符到MakeDot小软件
复制MakeDot小软件中GB2312所有字符到“输入窗口区”(在GB2312字符显示区,鼠标右击选择全选,之后就可以复制了),
点击生成数组按钮后的效果如下:
53.3.3 第3步,复制生成的数组到工程中
在输出窗口鼠标右击,选择“全选”,然后再次鼠标右击选择复制。
这样就可以粘贴到工程的hz.c文件里面:
将点阵数据放在相应的文件里面时要注意加上两个0XFF。hz.c文件的内容如下:
/* FLASH中内嵌小字库,只包括本程序用到的汉字点阵 每行点阵数据,头2字节是汉子的内码,后面是16点阵汉子的字模数据。 */ #ifdef USE_SMALL_FONT unsigned char const g_Hz16[] = { 0xA1,0xA1, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xA1,0xA2, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 、 // 0x00,0x00,0x00,0x00,0x20,0x00,0x18,0x00,0x0C,0x00,0x04,0x00,0x00,0x00,0x00,0x00, 0xA1,0xA3, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 。 // 0x00,0x00,0x00,0x00,0x18,0x00,0x24,0x00,0x24,0x00,0x18,0x00,0x00,0x00,0x00,0x00, /* 中间部分省略未写 */ 0xF7,0xFB, 0x20,0x0E,0xCE,0xF0,0x82,0x22,0xEE,0x92,0x82,0x44,0x82,0x20,0xFE,0x44,0x00,0xF8,// 鼷 // 0x92,0x10,0x92,0x24,0xDA,0xFE,0x92,0x10,0xDA,0xFE,0x92,0x28,0x93,0x44,0xD9,0x82, 0xF7,0xFC, 0x10,0x20,0x3E,0x20,0x22,0x20,0x3E,0x20,0x22,0xF8,0x3E,0x28,0x00,0x28,0x7F,0x28,// 鼽 // 0x49,0x28,0x7F,0x28,0x49,0x28,0x7F,0x2A,0x00,0x2A,0xFF,0xCA,0x22,0x46,0x42,0x80, 0xF7,0xFD, 0x10,0x00,0x3E,0x00,0x22,0x7C,0x3E,0x10,0x22,0x10,0x3E,0x10,0x00,0x10,0x7F,0x10,// 鼾 // 0x49,0xFE,0x7F,0x10,0x49,0x10,0x7F,0x10,0x00,0x10,0xFF,0x90,0x22,0x10,0x42,0x10, 0xF7,0xFE, 0x10,0x10,0x3E,0x10,0x22,0xFE,0x3E,0x38,0x22,0x54,0x3E,0x92,0x00,0x00,0x7F,0x7C,// 齄 // 0x49,0x44,0x7F,0x7C,0x49,0x44,0x7F,0x7C,0x00,0x44,0xFF,0x80,0x22,0xFE,0x42,0x00, /* 最后一行必须用0xFF,0xFF结束,这是字库数组结束标志 */ 0xFF,0xFF }; #else unsigned char const g_Hz16[] = {0xFF, 0xFF}; #endif
添加完毕点阵数据后,在font.h文件里面使能宏定义:
#define USE_SMALL_FONT /*这个宏只在bsp_tft+lcd.c中使用 */
至此就完成了全字库的汉字添加,用户就可以使用16点阵的汉字了。
53.4 C文件格式汉字使用方法
汉字的显示方法比较简单。
- 定义一个FONT_T类型变量:
FONT_T tFont12; /* 定义一个12点阵字体结构体变量,用于设置字体参数 */ FONT_T tFont16; /* 定义一个16点阵字体结构体变量,用于设置字体参数 */ FONT_T tFont24; /* 定义一个24点阵字体结构体变量,用于设置字体参数 */
FONT_T的原始定义如下:
typedef struct { FONT_CODE_E FontCode; /* 字体代码 FONT_CODE_E */ uint16_t FrontColor; /* 字体颜色 */ uint16_t BackColor; /* 文字背景颜色,透明 */ uint16_t Space; /* 文字间距,单位 = 像素 */ }FONT_T;
- 初始化变量tFont:
设置12,16和24点阵。
/* 设置字体属性 */ tFont.FontCode = FC_ST_12; /* 字体选择宋体12点阵,高12 x宽11) */ tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */ tFont.BackColor = CL_MASK; /* 文字背景颜色,透明 */ tFont.Space = 0; /* 字符水平间距, 单位 = 像素 */ tFont.FontCode = FC_ST_16; /* 字体选择宋体16点阵,高16 x宽15) */ tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */ tFont.BackColor = CL_MASK; /* 文字背景颜色,透明 */ tFont.Space = 0; /* 字符水平间距, 单位 = 像素 */ tFont.FontCode = FC_ST_24; /* 字体选择宋体24点阵,高24 x宽23) */ tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */ tFont.BackColor = CL_MASK; /* 文字背景颜色,透明 */ tFont.Space = 0; /* 字符水平间距, 单位 = 像素 */
- 调用函数LCD_DispStr显示字符:
下面显示了12,16和24点阵字符。
LCD_DispStr(5, 3, "故人西辞黄鹤楼,烟花三月下扬州。www.armfly.com", &tFont12); LCD_DispStr(5, 20, "孤帆远影碧空尽,唯见长江天际流。www.armfly.com", &tFont12); LCD_DispStr(5, 38, "故人西辞黄鹤楼,烟花三月下扬州。", &tFont16); LCD_DispStr(5, 68, "孤帆远影碧空尽,唯见长江天际流。", &tFont16); LCD_DispStr(5, 98, "故人西辞黄鹤楼烟花三月下扬州", &tFont24); LCD_DispStr(5, 128, "孤帆远影碧空尽唯见长江天际流", &tFont24);
53.5 汉字显示方法解析
下面将汉字的显示流程做个说明,几个函数的调用关系如下:
LCD_DispStr ----> LCD_DispStrEx ----->_LCD_ReadAsciiDot
_LCD_ReadHZDot
53.5.1 函数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_DispStrEx实现。
53.5.2 函数LCD_DispStrEx
此函数的源码如下:
1. /* 2. ****************************************************************************************************** 3. * 函 数 名: LCD_DispStrEx 4. * 功能说明: 在LCD指定坐标(左上角)显示一个字符串。 增强型函数。支持左中右对齐,支持定长清屏。 5. * 形 参: 6. * _usX : X坐标 7. * _usY : Y坐标 8. * _ptr : 字符串指针 9. * _tFont : 字体结构体,包含颜色、背景色(支持透明)、字体代码、文字间距等参数。可以指定RA8875字库 10. * 显示汉字。 11. * _Width : 字符串显示区域的宽度. 0 表示不处理留白区域,此时_Align无效 12. * _Align :字符串在显示区域的对齐方式, 13. * ALIGN_LEFT = 0, 14. * ALIGN_CENTER = 1, 15. * ALIGN_RIGHT = 2 16. * 返 回 值: 无 17. ****************************************************************************************************** 18. */ 19. void LCD_DispStrEx(uint16_t _usX, uint16_t _usY, char *_ptr, FONT_T *_tFont, uint16_t _Width, 20. uint8_t _Align) 21. { 22. uint32_t i; 23. uint8_t code1; 24. uint8_t code2; 25. uint8_t buf[32 * 32 / 8]; /* 最大支持32点阵汉字 */ 26. uint8_t width; 27. uint16_t m; 28. uint8_t font_width = 0; 29. uint8_t font_height = 0; 30. uint16_t x, y; 31. uint16_t offset; 32. uint16_t str_width; /* 字符串实际宽度 */ 33. 34. switch (_tFont->FontCode) 35. { 36. case FC_ST_12: /* 12点阵 */ 37. font_height = 12; 38. font_width = 12; 39. break; 40. 41. case FC_ST_16: 42. font_height = 16; 43. font_width = 16; 44. break; 45. 46. case FC_ST_24: 47. font_height = 24; 48. font_width = 24; 49. break; 50. 51. case FC_ST_32: 52. font_height = 32; 53. font_width = 32; 54. break; 55. } 56. 57. str_width = LCD_GetStrWidth(_ptr, _tFont);/* 计算字符串实际宽度(RA8875内部ASCII点阵宽度为变长 */ 58. offset = 0; 59. if (_Width > str_width) 60. { 61. if (_Align == ALIGN_RIGHT) /* 右对齐 */ 62. { 63. offset = _Width - str_width; 64. } 65. else if (_Align == ALIGN_CENTER) /* 居中 */ 66. { 67. offset = (_Width - str_width) / 2; 68. } 69. else /* 左对齐 ALIGN_LEFT */ 70. { 71. ; 72. } 73. } 74. 75. /* 左侧填背景色, 中间对齐和右边对齐 */ 76. if (offset > 0) 77. { 78. LCD_Fill_Rect(_usX, _usY, LCD_GetFontHeight(_tFont), offset, _tFont->BackColor); 79. _usX += offset; 80. } 81. 82. /* 右侧填背景色 */ 83. if (_Width > str_width) 84. { 85. LCD_Fill_Rect(_usX + str_width, _usY, LCD_GetFontHeight(_tFont), _Width - str_width - offset, 86. _tFont->BackColor); 87. } 88. 89. /* 使用CPU内部字库. 点阵信息由CPU读取 */ 90. { 91. /* 开始循环处理字符 */ 92. while (*_ptr != 0) 93. { 94. code1 = *_ptr; /* 读取字符串数据, 该数据可能是ascii代码,也可能汉字代码的高字节 */ 95. if (code1 < 0x80) 96. { 97. /* 将ascii字符点阵复制到buf */ 98. //memcpy(buf, &pAscDot[code1 * (font_bytes / 2)], (font_bytes / 2)); 99. _LCD_ReadAsciiDot(code1, _tFont->FontCode, buf); /* 读取ASCII字符点阵 */ 100. width = font_width / 2; 101. } 102. else 103. { 104. code2 = *++_ptr; 105. if (code2 == 0) 106. { 107. break; 108. } 109. /* 读1个汉字的点阵 */ 110. _LCD_ReadHZDot(code1, code2, _tFont->FontCode, buf); 111. width = font_width; 112. } 113. 114. y = _usY; 115. /* 开始刷LCD */ 116. for (m = 0; m < font_height; m++) /* 字符高度 */ 117. { 118. x = _usX; 119. for (i = 0; i < width; i++) /* 字符宽度 */ 120. { 121. if ((buf[m * ((2 * width) / font_width) + i / 8] & (0x80 >> (i % 8 ))) != 0x00) 122. { 123. LCD_PutPixel(x, y, _tFont->FrontColor); /* 设置像素颜色为文字色 */ 124. } 125. else 126. { 127. if (_tFont->BackColor != CL_MASK) /* 透明色 */ 128. { 129. LCD_PutPixel(x, y, _tFont->BackColor);/* 设置像素颜色为文字背景色 */ 130. } 131. } 132. 133. x++; 134. } 135. y++; 136. } 137. 138. if (_tFont->Space > 0) 139. { 140. /* 如果文字底色按_tFont->usBackColor,并且字间距大于点阵的宽度,那么需要在文字之间填 141. 充(暂时未实现) */ 142. } 143. _usX += width + _tFont->Space; /* 列地址递增 */ 144. _ptr++; /* 指向下一个字符 */ 145. } 146. } 147. }
下面将代码中几个关键地方做个阐释:
- 第34-55行,根据使用的的12,16,24和32点阵字体,设置字体的高度变量font_height和宽度变量font_width。
- 第57-73行,通过函数LCD_GetStrWidth计算字符串的长度,然后根据设置的字符总宽度和实际宽度做比较,来实现左对齐,右对齐和居中显示。由于函数LCD_DispStr调用LCD_DispStrEx时,将形参_Width设置为0,所以这部分代码功能未用到。
- 第76-87行,用于填充显示字符以外区域的背景色,只有设置的字符总宽度大于实际宽度时才会用到,填充的就是实际宽度以外的区域。
- 第90-146行,显示所有字符。
-
- 第95行,如果编码值小于0x80,表示ASCII字符。
- 第99行,根据编码值读取ASCII值对应的点阵数据到数组buf里面。
- 第100行,显示ASCII字符仅需要一半宽度即可,比如显示12*12点阵字符,显示成ASCII仅需6*12即可。
- 第102行,如果编码值大于等于0x80,汉字编码在这个范围。
- 第104行,因为GB编码需要两个字节表示,所以这里再读取一个字节。
- 第110行,根据汉字编码值对应的点阵数据到数组buf里面。
- 第116-136行,采用从左到右,从上到下的方式刷新字符。这里特别注意点阵数据位置的获取:buf[m * ((2 * width) / font_width) + i / 8] & (0x80 >> (i % 8 )
对于这个公式,大家通过代数法,代入两次数值就好理解了。
53.5.3 函数_LCD_ReadAsciiDot
此函数的作用是根据ASCII编码值,读取对应的点阵数据出来。
1. /* 2. ****************************************************************************************************** 3. * 函 数 名: _LCD_ReadAsciiDot 4. * 功能说明: 读取1个ASCII字符的点阵数据 5. * 形 参: 6. * _code : ASCII字符的编码,1字节。1-128 7. * _fontcode :字体代码 8. * _pBuf : 存放读出的字符点阵数据 9. * 返 回 值: 文字宽度 10. ****************************************************************************************************** 11. */ 12. static void _LCD_ReadAsciiDot(uint8_t _code, uint8_t _fontcode, uint8_t *_pBuf) 13. { 14. const uint8_t *pAscDot; 15. uint8_t font_bytes = 0; 16. 17. pAscDot = 0; 18. switch (_fontcode) 19. { 20. case FC_ST_12: /* 12点阵 */ 21. font_bytes = 24; 22. pAscDot = g_Ascii12; 23. break; 24. 25. case FC_ST_24: 26. case FC_ST_32: 27. case FC_ST_16: 28. /* 缺省是16点阵 */ 29. font_bytes = 32; 30. pAscDot = g_Ascii16; 31. break; 32. 33. case FC_RA8875_16: 34. case FC_RA8875_24: 35. case FC_RA8875_32: 36. return; 37. } 38. 39. /* 将CPU内部Flash中的ascii字符点阵复制到buf */ 40. memcpy(_pBuf, &pAscDot[_code * (font_bytes / 2)], (font_bytes / 2)); 41. }
下面将此函数涉及到的知识点为大家做个阐释:
- 第20-23行,显示12点阵ASCII,每个字符需要24个字节,存储在数组g_Ascii12里面。
- 第25-31行,显示16,24和32点阵ASCII,这里采用同一大小字符进行显示。每个字符需要32个字节,存储在数组g_Ascii16里面。
- 第33-35行,这个是RA8875的字库处理,V7开发板用不到。
- 第40行,将ASCII点阵数据复制到缓冲_pBuf里面。
53.5.4 函数_LCD_ReadHZDot
此函数的作用是根据ASCII编码值,读取对应的点阵数据出来。
1. /* 2. ****************************************************************************************************** 3. * 函 数 名: _LCD_ReadHZDot 4. * 功能说明: 读取1个汉字的点阵数据 5. * 形 参: 6. * _code1, _cod2 : 汉字内码. GB2312编码 7. * _fontcode :字体代码 8. * _pBuf : 存放读出的字符点阵数据 9. * 返 回 值: 无 10. ****************************************************************************************************** 11. */ 12. static void _LCD_ReadHZDot(uint8_t _code1, uint8_t _code2, uint8_t _fontcode, uint8_t *_pBuf) 13. { 14. #ifdef USE_SMALL_FONT /* 使用CPU 内部Flash 小字库 */ 15. uint8_t *pDot; 16. uint8_t font_bytes = 0; 17. uint32_t address; 18. uint16_t m; 19. 20. pDot = 0; /* 仅仅用于避免告警 */ 21. switch (_fontcode) 22. { 23. case FC_ST_12: /* 12点阵 */ 24. font_bytes = 24; 25. pDot = (uint8_t *)g_Hz12; 26. break; 27. 28. case FC_ST_16: 29. font_bytes = 32; 30. pDot = (uint8_t *)g_Hz16; 31. break; 32. 33. case FC_ST_24: 34. font_bytes = 72; 35. pDot = (uint8_t *)g_Hz24; 36. break; 37. 38. case FC_ST_32: 39. font_bytes = 128; 40. pDot = (uint8_t *)g_Hz32; 41. break; 42. 43. case FC_RA8875_16: 44. case FC_RA8875_24: 45. case FC_RA8875_32: 46. return; 47. } 48. 49. m = 0; 50. while(1) 51. { 52. address = m * (font_bytes + 2); 53. m++; 54. if ((_code1 == pDot[address + 0]) && (_code2 == pDot[address + 1])) 55. { 56. address += 2; 57. memcpy(_pBuf, &pDot[address], font_bytes); 58. break; 59. } 60. else if ((pDot[address + 0] == 0xFF) && (pDot[address + 1] == 0xFF)) 61. { 62. /* 字库搜索完毕,未找到,则填充全FF */ 63. memset(_pBuf, 0xFF, font_bytes); 64. break; 65. } 66. } 67. #else /* 用全字库 */ 68. uint8_t *pDot = 0; 69. uint8_t font_bytes = 0; 70. 71. switch (_fontcode) 72. { 73. case FC_ST_12: /* 12点阵 */ 74. font_bytes = 24; 75. pDot = (uint8_t *)HZK12_ADDR; 76. break; 77. 78. case FC_ST_16: 79. font_bytes = 32; 80. pDot = (uint8_t *)HZK16_ADDR; 81. break; 82. 83. case FC_ST_24: 84. font_bytes = 72; 85. pDot = (uint8_t *)HZK24_ADDR; 86. break; 87. 88. case FC_ST_32: 89. font_bytes = 128; 90. pDot = (uint8_t *)HZK32_ADDR; 91. break; 92. 93. case FC_RA8875_16: 94. case FC_RA8875_24: 95. case FC_RA8875_32: 96. return; 97. } 98. 99. /* 此处需要根据字库文件存放位置进行修改 */ 100. if (_code1 >=0xA1 && _code1 <= 0xA9 && _code2 >=0xA1) 101. { 102. pDot += ((_code1 - 0xA1) * 94 + (_code2 - 0xA1)) * font_bytes; 103. } 104. else if (_code1 >=0xB0 && _code1 <= 0xF7 && _code2 >=0xA1) 105. { 106. pDot += ((_code1 - 0xB0) * 94 + (_code2 - 0xA1) + 846) * font_bytes; 107. } 108. memcpy(_pBuf, pDot, font_bytes); 109. #endif 110. }
下面将此函数涉及到的知识点为大家做个阐释:
- 第15-66行,小字库显示,这个方案既可以显示小字库,也可以显示全字库。
- 第23-41行,获取12点阵,16点阵,24点阵和32点阵汉字显示需要的字节数以及存储点阵数据的缓冲地址。
- 第49-66行,这里是通过比较汉字的编码值找到点阵数据位置,如果遇到两个0xFF,表示检索到数组末尾了也没有找到汉字点阵数组。找到数据后,将其复制到缓冲_pBuf里面。
- 第68-108行,本章暂时用不到这种方案,后面章节用到这种方案了再为大家做说明。
53.6 LCD驱动移植和使用
与第51章51.7小节相同,这里就不再赘述了。
53.7 实验例程设计框架
通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:
第1阶段,上电启动阶段:
- 这部分在第14章进行了详细说明。
第2阶段,进入main函数:
- 第1步,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器,LED,串口,LCD,SDRAM等。
- 第2步,LCD应用程序设计部分,显示汉字。通过按键实现三种界面的处理,其中GB2312有几十个界面。
53.8 实验例程说明(MDK)
配套例子:
V7-033_LCD的汉字小字库和全字库制作实验
实验目的:
- 学习LCD的汉字小字库和全字库制作实验。
实验内容:
- 小字库和全字库通过此软件生成:http://www.armbbs.cn/forum.php?mod=viewthread&tid=202 。
- LCD界面上展示ASCII字符和GB2312编码汉字。
- 启动1个200ms的自动重装定时器,让LED2每200ms翻转一次。
实验操作:
- 摇杆上键,增加LCD背景光亮度。
- 摇杆下键,降低LCD背景光亮度。
- 摇杆左键,显示上一页汉字。
- 摇杆右键,显示下一页汉字。
- 摇杆OK键,返回首页。
LCD的界面显示效果如下:
部分截图:
上电后串口打印的信息:
波特率 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界面上展示ASCII字符和GB2312编码汉字
- 启动1个200ms的自动重装定时器,让LED2每200ms翻转一次。
- 通过按键来实现翻页功能,方便查看所有GB2312编码汉字。
/* ********************************************************************************************************* * 函 数 名: main * 功能说明: c程序入口 * 形 参: 无 * 返 回 值: 错误代码(无需处理) ********************************************************************************************************* */ int main(void) { uint16_t ucBright; /* 背光亮度(0-255) */ uint8_t ucKeyCode; /* 按键代码 */ uint8_t ucStatus; /* 主程序状态字 */ uint8_t fRefresh; /* 刷屏请求标志,1表示需要刷新 */ bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程名称和版本等信息 */ PrintfHelp(); /* 打印操作提示 */ /* 延迟200ms再点亮背光,避免瞬间高亮 */ bsp_DelayMS(200); DispFirstPage(); /* 显示第1页 */ /* 界面整体显示完毕后,再打开背光,设置为缺省亮度 */ bsp_DelayMS(100); ucBright = BRIGHT_DEFAULT; LCD_SetBackLight(ucBright); bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器,软件定时器0 */ /* 进入主程序循环体 */ ucStatus = 0; fRefresh = 0; while (1) { /* 判断软件定时器0是否超时 */ if(bsp_CheckTimer(0)) { /* 每隔200ms 进来一次 */ bsp_LedToggle(2); } if (fRefresh == 1) { fRefresh = 0; switch (ucStatus) { case 0: DispFirstPage(); /* 显示第1页 */ break; case 1: DispAsciiDot(); /* 显示ASCII点阵 */ break; default: /* 区码范围 :1 - 87 */ if (ucStatus <= 89) { DispHZK16(ucStatus); /* 显示一个区的94个汉字 */ } break; } } ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */ if (ucKeyCode != KEY_NONE) { /* 有键按下 */ switch (ucKeyCode) { case JOY_DOWN_L: /* 摇杆LEFT键按下 */ if (ucStatus > 0) { ucStatus--; } fRefresh = 1; /* 请求刷新LCD */ break; case JOY_DOWN_R: /* 摇杆RIGHT键按下 */ if (ucStatus < DEMO_PAGE_COUNT - 1) { ucStatus++; } fRefresh = 1; /* 请求刷新LCD */ break; case JOY_DOWN_OK: /* 摇杆OK键 */ ucStatus = 0; /* 返回首页 */ fRefresh = 1; /* 请求刷新LCD */ break; case JOY_DOWN_U: /* 摇杆UP键按下 */ ucBright += BRIGHT_STEP; if (ucBright > BRIGHT_MAX) { ucBright = BRIGHT_MAX; } LCD_SetBackLight(ucBright); printf("当前背景光亮度 : %d ", ucBright); break; case JOY_DOWN_D: /* 摇杆DOWN键按下 */ if (ucBright < BRIGHT_STEP) { ucBright = 0; } else { ucBright -= BRIGHT_STEP; } LCD_SetBackLight(ucBright); printf("当前背景光亮度 : %d ", ucBright); break; default: break; } } } }
下面的代码用于LCD首页显示:
/* ********************************************************************************************************* * 函 数 名: DispFirstPage * 功能说明: 显示操作提示 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void DispFirstPage(void) { FONT_T tFont; /* 定义一个字体结构体变量,用于设置字体参数 */ uint16_t y; /* Y坐标 */ uint16_t usLineCap; /* 行高 */ uint8_t buf[50]; LCD_ClrScr(CL_BLUE); /* 清屏,背景蓝色 */ /* 设置字体属性 */ tFont.FontCode = FC_ST_16; /* 字体选择宋体16点阵,高16x宽15) */ tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */ tFont.BackColor = CL_MASK; /* 文字背景颜色,透明 */ tFont.Space = 0; /* 字符水平间距, 单位 = 像素 */ y = 0; usLineCap = 18; /* 行间距 */ LCD_DispStr(5, y, "安富莱STM32-V7开发板 www.armfly.com", &tFont); y += usLineCap; LCD_DispStr(5, y, "汉字小字库和全字库测试实验", &tFont); y += 2 * usLineCap; LCD_DispStr(30, y, "操作提示:", &tFont); y += usLineCap; LCD_DispStr(50, y, "摇杆上键 = 增加背光亮度", &tFont); y += usLineCap; LCD_DispStr(50, y, "摇杆下键 = 降低背光亮度", &tFont); y += usLineCap; LCD_DispStr(50, y, "摇杆左键 = 向前翻页", &tFont); y += usLineCap; LCD_DispStr(50, y, "摇杆右键 = 向后翻页", &tFont); y += usLineCap; LCD_DispStr(50, y, "摇杆OK键 = 返回首页", &tFont); y += 2 * usLineCap; sprintf((char *)buf, "显示器分辨率 :%dx%d", g_LcdWidth, g_LcdHeight); LCD_DispStr(5, y, (char *)buf, &tFont); y += usLineCap; LCD_DispStr(5, y, "每行可以显示25个汉字,或50个字符", &tFont); }
下面是ASCII字符显示:
/* ********************************************************************************************************* * 函 数 名: DispAsciiDot * 功能说明: 显示ASCII点阵 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void DispAsciiDot(void) { uint8_t i,k; FONT_T tFont; /* 定义一个字体结构体变量,用于设置字体参数 */ uint16_t x; /* X坐标 */ uint16_t y; /* Y坐标 */ char buf[32 + 1]; uint8_t ascii; LCD_ClrScr(CL_BLUE); /* 清屏,背景蓝色 */ /* 设置字体属性 */ tFont.FontCode = FC_ST_16; /* 字体选择宋体16点阵,高16x宽15) */ tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */ tFont.BackColor = CL_MASK; /* 文字背景颜色,透明 */ tFont.Space = 2; /* 字符水平间距, 单位 = 像素 */ LCD_DispStr(50, 0, "16点阵ASCII码字库,代码1-127", &tFont); x = 50; y = 40; ascii = 0; for (i = 0; i < 4; i++) { for (k = 0; k < 32; k++) { buf[k] = ascii++; } buf[32] = 0; if (buf[0] == 0) { buf[0] = ‘ ‘; } LCD_DispStr(x, y, buf, &tFont); y += 20; } }
下面是GB2312编码字符显示:
/* ********************************************************************************************************* * 函 数 名: DispHZK16 * 功能说明: 显示16点阵汉字阵 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void DispHZK16(uint8_t _ucIndex) { uint8_t i,k; uint16_t x,y; char buf[50 + 1]; uint8_t code1,code2; /* 汉字内码 */ uint8_t usLineCap = 18; FONT_T tFont; /* 定义一个字体结构体变量,用于设置字体参数 */ printf(" Display HZK Area Code = %d ", _ucIndex - 1); if (_ucIndex == 2) { /* 第1次清屏,以后显示位置不变,可以不清屏,避免闪烁 */ LCD_ClrScr(CL_BLUE); /* 清屏,背景蓝色 */ } /* 设置字体属性 */ tFont.FontCode = FC_ST_16; /* 字体选择宋体16点阵,高16x宽15) */ tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */ tFont.BackColor = CL_BLUE; /* 文字背景颜色,蓝色 */ tFont.Space = 0; /* 字符水平间距, 单位 = 像素 */ y = 0; LCD_DispStr(20, y, "国标GB2312 16点阵汉字库(区码1-87,位码1-94)", &tFont); code1 = _ucIndex - 1; /* 得到区码 */ code2 = 1; /* 位码从1开始 */ y += usLineCap; sprintf((char *)buf, (char *)"当前区码: %2d, 本页位码:1-94, 第10-15区无字符", code1); LCD_DispStr(20, y, buf, &tFont); y += (2 * usLineCap); /* 机内码高位 = 区码 + 0xA0 机内码低位 = 位码 + 0xA0 区码范围 :1 - 87 位码范围 : 1 - 94 每行显示20个汉字,一个区是94个汉字,需要5行显示,第5行显示14个汉字 */ x = 40; code1 += 0xA0; /* 换算到内码高位 */ code2 = 0xA1; /* 内码低位起始 */ for (i = 0; i < 5; i++) { for (k = 0; k < 20; k++) { buf[2 * k] = code1; buf[2 * k + 1] = code2; code2++; if ((i == 4) && (k == 13)) { k++; break; } } buf[2 * k] = 0; LCD_DispStr(x, y, buf, &tFont); y += usLineCap; } }
53.9 实验例程说明(IAR)
配套例子:
V7-033_LCD的汉字小字库和全字库制作实验
实验目的:
- 学习LCD的汉字小字库和全字库制作实验。
实验内容:
- 小字库和全字库通过此软件生成:http://www.armbbs.cn/forum.php?mod=viewthread&tid=202 。
- LCD界面上展示ASCII字符和GB2312编码汉字。
- 启动1个200ms的自动重装定时器,让LED2每200ms翻转一次。
实验操作:
- 摇杆上键,增加LCD背景光亮度。
- 摇杆下键,降低LCD背景光亮度。
- 摇杆左键,显示上一页汉字。
- 摇杆右键,显示下一页汉字。
- 摇杆OK键,返回首页。
LCD的界面显示效果如下:
部分截图:
上电后串口打印的信息:
波特率 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界面上展示ASCII字符和GB2312编码汉字
- 启动1个200ms的自动重装定时器,让LED2每200ms翻转一次。
- 通过按键来实现翻页功能,方便查看所有GB2312编码汉字。
/* ********************************************************************************************************* * 函 数 名: main * 功能说明: c程序入口 * 形 参: 无 * 返 回 值: 错误代码(无需处理) ********************************************************************************************************* */ int main(void) { uint16_t ucBright; /* 背光亮度(0-255) */ uint8_t ucKeyCode; /* 按键代码 */ uint8_t ucStatus; /* 主程序状态字 */ uint8_t fRefresh; /* 刷屏请求标志,1表示需要刷新 */ bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程名称和版本等信息 */ PrintfHelp(); /* 打印操作提示 */ /* 延迟200ms再点亮背光,避免瞬间高亮 */ bsp_DelayMS(200); DispFirstPage(); /* 显示第1页 */ /* 界面整体显示完毕后,再打开背光,设置为缺省亮度 */ bsp_DelayMS(100); ucBright = BRIGHT_DEFAULT; LCD_SetBackLight(ucBright); bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器,软件定时器0 */ /* 进入主程序循环体 */ ucStatus = 0; fRefresh = 0; while (1) { /* 判断软件定时器0是否超时 */ if(bsp_CheckTimer(0)) { /* 每隔200ms 进来一次 */ bsp_LedToggle(2); } if (fRefresh == 1) { fRefresh = 0; switch (ucStatus) { case 0: DispFirstPage(); /* 显示第1页 */ break; case 1: DispAsciiDot(); /* 显示ASCII点阵 */ break; default: /* 区码范围 :1 - 87 */ if (ucStatus <= 89) { DispHZK16(ucStatus); /* 显示一个区的94个汉字 */ } break; } } ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */ if (ucKeyCode != KEY_NONE) { /* 有键按下 */ switch (ucKeyCode) { case JOY_DOWN_L: /* 摇杆LEFT键按下 */ if (ucStatus > 0) { ucStatus--; } fRefresh = 1; /* 请求刷新LCD */ break; case JOY_DOWN_R: /* 摇杆RIGHT键按下 */ if (ucStatus < DEMO_PAGE_COUNT - 1) { ucStatus++; } fRefresh = 1; /* 请求刷新LCD */ break; case JOY_DOWN_OK: /* 摇杆OK键 */ ucStatus = 0; /* 返回首页 */ fRefresh = 1; /* 请求刷新LCD */ break; case JOY_DOWN_U: /* 摇杆UP键按下 */ ucBright += BRIGHT_STEP; if (ucBright > BRIGHT_MAX) { ucBright = BRIGHT_MAX; } LCD_SetBackLight(ucBright); printf("当前背景光亮度 : %d ", ucBright); break; case JOY_DOWN_D: /* 摇杆DOWN键按下 */ if (ucBright < BRIGHT_STEP) { ucBright = 0; } else { ucBright -= BRIGHT_STEP; } LCD_SetBackLight(ucBright); printf("当前背景光亮度 : %d ", ucBright); break; default: break; } } } }
下面的代码用于LCD首页显示:
/* ********************************************************************************************************* * 函 数 名: DispFirstPage * 功能说明: 显示操作提示 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void DispFirstPage(void) { FONT_T tFont; /* 定义一个字体结构体变量,用于设置字体参数 */ uint16_t y; /* Y坐标 */ uint16_t usLineCap; /* 行高 */ uint8_t buf[50]; LCD_ClrScr(CL_BLUE); /* 清屏,背景蓝色 */ /* 设置字体属性 */ tFont.FontCode = FC_ST_16; /* 字体选择宋体16点阵,高16x宽15) */ tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */ tFont.BackColor = CL_MASK; /* 文字背景颜色,透明 */ tFont.Space = 0; /* 字符水平间距, 单位 = 像素 */ y = 0; usLineCap = 18; /* 行间距 */ LCD_DispStr(5, y, "安富莱STM32-V7开发板 www.armfly.com", &tFont); y += usLineCap; LCD_DispStr(5, y, "汉字小字库和全字库测试实验", &tFont); y += 2 * usLineCap; LCD_DispStr(30, y, "操作提示:", &tFont); y += usLineCap; LCD_DispStr(50, y, "摇杆上键 = 增加背光亮度", &tFont); y += usLineCap; LCD_DispStr(50, y, "摇杆下键 = 降低背光亮度", &tFont); y += usLineCap; LCD_DispStr(50, y, "摇杆左键 = 向前翻页", &tFont); y += usLineCap; LCD_DispStr(50, y, "摇杆右键 = 向后翻页", &tFont); y += usLineCap; LCD_DispStr(50, y, "摇杆OK键 = 返回首页", &tFont); y += 2 * usLineCap; sprintf((char *)buf, "显示器分辨率 :%dx%d", g_LcdWidth, g_LcdHeight); LCD_DispStr(5, y, (char *)buf, &tFont); y += usLineCap; LCD_DispStr(5, y, "每行可以显示25个汉字,或50个字符", &tFont); }
下面是ASCII字符显示:
/* ********************************************************************************************************* * 函 数 名: DispAsciiDot * 功能说明: 显示ASCII点阵 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void DispAsciiDot(void) { uint8_t i,k; FONT_T tFont; /* 定义一个字体结构体变量,用于设置字体参数 */ uint16_t x; /* X坐标 */ uint16_t y; /* Y坐标 */ char buf[32 + 1]; uint8_t ascii; LCD_ClrScr(CL_BLUE); /* 清屏,背景蓝色 */ /* 设置字体属性 */ tFont.FontCode = FC_ST_16; /* 字体选择宋体16点阵,高16x宽15) */ tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */ tFont.BackColor = CL_MASK; /* 文字背景颜色,透明 */ tFont.Space = 2; /* 字符水平间距, 单位 = 像素 */ LCD_DispStr(50, 0, "16点阵ASCII码字库,代码1-127", &tFont); x = 50; y = 40; ascii = 0; for (i = 0; i < 4; i++) { for (k = 0; k < 32; k++) { buf[k] = ascii++; } buf[32] = 0; if (buf[0] == 0) { buf[0] = ‘ ‘; } LCD_DispStr(x, y, buf, &tFont); y += 20; } }
下面是GB2312编码字符显示:
/* ********************************************************************************************************* * 函 数 名: DispHZK16 * 功能说明: 显示16点阵汉字阵 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void DispHZK16(uint8_t _ucIndex) { uint8_t i,k; uint16_t x,y; char buf[50 + 1]; uint8_t code1,code2; /* 汉字内码 */ uint8_t usLineCap = 18; FONT_T tFont; /* 定义一个字体结构体变量,用于设置字体参数 */ printf(" Display HZK Area Code = %d ", _ucIndex - 1); if (_ucIndex == 2) { /* 第1次清屏,以后显示位置不变,可以不清屏,避免闪烁 */ LCD_ClrScr(CL_BLUE); /* 清屏,背景蓝色 */ } /* 设置字体属性 */ tFont.FontCode = FC_ST_16; /* 字体选择宋体16点阵,高16x宽15) */ tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */ tFont.BackColor = CL_BLUE; /* 文字背景颜色,蓝色 */ tFont.Space = 0; /* 字符水平间距, 单位 = 像素 */ y = 0; LCD_DispStr(20, y, "国标GB2312 16点阵汉字库(区码1-87,位码1-94)", &tFont); code1 = _ucIndex - 1; /* 得到区码 */ code2 = 1; /* 位码从1开始 */ y += usLineCap; sprintf((char *)buf, (char *)"当前区码: %2d, 本页位码:1-94, 第10-15区无字符", code1); LCD_DispStr(20, y, buf, &tFont); y += (2 * usLineCap); /* 机内码高位 = 区码 + 0xA0 机内码低位 = 位码 + 0xA0 区码范围 :1 - 87 位码范围 : 1 - 94 每行显示20个汉字,一个区是94个汉字,需要5行显示,第5行显示14个汉字 */ x = 40; code1 += 0xA0; /* 换算到内码高位 */ code2 = 0xA1; /* 内码低位起始 */ for (i = 0; i < 5; i++) { for (k = 0; k < 20; k++) { buf[2 * k] = code1; buf[2 * k + 1] = code2; code2++; if ((i == 4) && (k == 13)) { k++; break; } } buf[2 * k] = 0; LCD_DispStr(x, y, buf, &tFont); y += usLineCap; } }
53.10 总结
本章节涉及到的知识点比较多,需要大家花点时间去掌握,直至可以独立驱动一个显示屏。
以上是关于STM32H7教程第53章 STM32H7的LTDC应用之汉字小字库和全字库制作的主要内容,如果未能解决你的问题,请参考以下文章
STM32H7教程第39章 STM32H7的DMAMUX基础知识(重要)
STM32H7教程第49章 STM32H7的FMC总线应用之SDRAM
STM32H7的DSP教程第33章 STM32H7不限制点数FFT实现
STM32H7教程第89章 STM32H7的CAN FD总线基础之前世今生