【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 初學者重要提示

  1.   學習本章節前,務必優先學習第52章,需要對點陣字體字符編碼有個認識。
  2.   LTDC驅動設計和相關問題在第51章有詳細說明。
  3.   本章節為大家講解的小字庫和全字庫方法,簡單易用,是直接以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.    }

 

下面將代碼中幾個關鍵地方做個闡釋:

  1.   第34-55行,根據使用的的12,16,24和32點陣字體,設置字體的高度變量font_height和寬度變量font_width。
  2.   第57-73行,通過函數LCD_GetStrWidth計算字符串的長度,然后根據設置的字符總寬度和實際寬度做比較,來實現左對齊,右對齊和居中顯示。由於函數LCD_DispStr調用LCD_DispStrEx時,將形參_Width設置為0,所以這部分代碼功能未用到。
  3.   第76-87行,用於填充顯示字符以外區域的背景色,只有設置的字符總寬度大於實際寬度時才會用到,填充的就是實際寬度以外的區域。
  4.   第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的漢字小字庫和全字庫制作實驗

實驗目的:

  1. 學習LCD的漢字小字庫和全字庫制作實驗。

實驗內容:

  1. 小字庫和全字庫通過此軟件生成:http://www.armbbs.cn/forum.php?mod=viewthread&tid=202
  2. LCD界面上展示ASCII字符和GB2312編碼漢字。
  3. 啟動1個200ms的自動重裝定時器,讓LED2每200ms翻轉一次。

實驗操作:

  1. 搖桿上鍵,增加LCD背景光亮度。
  2. 搖桿下鍵,降低LCD背景光亮度。
  3. 搖桿左鍵,顯示上一頁漢字。
  4. 搖桿右鍵,顯示下一頁漢字。
  5. 搖桿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的漢字小字庫和全字庫制作實驗

實驗目的:

  1. 學習LCD的漢字小字庫和全字庫制作實驗。

實驗內容:

  1. 小字庫和全字庫通過此軟件生成:http://www.armbbs.cn/forum.php?mod=viewthread&tid=202
  2. LCD界面上展示ASCII字符和GB2312編碼漢字。
  3. 啟動1個200ms的自動重裝定時器,讓LED2每200ms翻轉一次。

實驗操作:

  1. 搖桿上鍵,增加LCD背景光亮度。
  2. 搖桿下鍵,降低LCD背景光亮度。
  3. 搖桿左鍵,顯示上一頁漢字。
  4. 搖桿右鍵,顯示下一頁漢字。
  5. 搖桿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   總結

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM