完整教程下載地址: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\r\n", ucBright); break; case JOY_DOWN_D: /* 搖桿DOWN鍵按下 */ if (ucBright < BRIGHT_STEP) { ucBright = 0; } else { ucBright -= BRIGHT_STEP; } LCD_SetBackLight(ucBright); printf("當前背景光亮度 : %d\r\n", 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\r\n", _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\r\n", ucBright); break; case JOY_DOWN_D: /* 搖桿DOWN鍵按下 */ if (ucBright < BRIGHT_STEP) { ucBright = 0; } else { ucBright -= BRIGHT_STEP; } LCD_SetBackLight(ucBright); printf("當前背景光亮度 : %d\r\n", 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\r\n", _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 總結
本章節涉及到的知識點比較多,需要大家花點時間去掌握,直至可以獨立驅動一個顯示屏。