完整教程下载地址: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 = ; /* 字符水平间距, 单位 = 像素 */ tFont.FontCode = FC_ST_16; /* 字体选择宋体16点阵,高16 x宽15) */
tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */
tFont.BackColor = CL_MASK; /* 文字背景颜色,透明 */
tFont.Space = ; /* 字符水平间距, 单位 = 像素 */ tFont.FontCode = FC_ST_24; /* 字体选择宋体24点阵,高24 x宽23) */
tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */
tFont.BackColor = CL_MASK; /* 文字背景颜色,透明 */
tFont.Space = ; /* 字符水平间距, 单位 = 像素 */
- 调用函数LCD_DispStr显示字符:
下面显示了12,16和24点阵字符。
LCD_DispStr(, , "故人西辞黄鹤楼,烟花三月下扬州。www.armfly.com", &tFont12);
LCD_DispStr(, , "孤帆远影碧空尽,唯见长江天际流。www.armfly.com", &tFont12);
LCD_DispStr(, , "故人西辞黄鹤楼,烟花三月下扬州。", &tFont16);
LCD_DispStr(, , "孤帆远影碧空尽,唯见长江天际流。", &tFont16);
LCD_DispStr(, , "故人西辞黄鹤楼烟花三月下扬州", &tFont24);
LCD_DispStr(, , "孤帆远影碧空尽唯见长江天际流", &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, , );
}
这个函数的注释已经比较详细,这里就不再赘述了。而这个函数是通过调用LCD_DispStrEx实现。
53.5.2 函数LCD_DispStrEx
此函数的源码如下:
. /*
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. */
. void LCD_DispStrEx(uint16_t _usX, uint16_t _usY, char *_ptr, FONT_T *_tFont, uint16_t _Width,
. uint8_t _Align)
. {
. uint32_t i;
. uint8_t code1;
. uint8_t code2;
. uint8_t buf[ * / ]; /* 最大支持32点阵汉字 */
. uint8_t width;
. uint16_t m;
. uint8_t font_width = ;
. uint8_t font_height = ;
. uint16_t x, y;
. uint16_t offset;
. uint16_t str_width; /* 字符串实际宽度 */
.
. switch (_tFont->FontCode)
. {
. case FC_ST_12: /* 12点阵 */
. font_height = ;
. font_width = ;
. break;
.
. case FC_ST_16:
. font_height = ;
. font_width = ;
. break;
.
. case FC_ST_24:
. font_height = ;
. font_width = ;
. break;
.
. case FC_ST_32:
. font_height = ;
. font_width = ;
. break;
. }
.
. str_width = LCD_GetStrWidth(_ptr, _tFont);/* 计算字符串实际宽度(RA8875内部ASCII点阵宽度为变长 */
. offset = ;
. if (_Width > str_width)
. {
. if (_Align == ALIGN_RIGHT) /* 右对齐 */
. {
. offset = _Width - str_width;
. }
. else if (_Align == ALIGN_CENTER) /* 居中 */
. {
. offset = (_Width - str_width) / ;
. }
. else /* 左对齐 ALIGN_LEFT */
. {
. ;
. }
. }
.
. /* 左侧填背景色, 中间对齐和右边对齐 */
. if (offset > )
. {
. LCD_Fill_Rect(_usX, _usY, LCD_GetFontHeight(_tFont), offset, _tFont->BackColor);
. _usX += offset;
. }
.
. /* 右侧填背景色 */
. if (_Width > str_width)
. {
. LCD_Fill_Rect(_usX + str_width, _usY, LCD_GetFontHeight(_tFont), _Width - str_width - offset,
. _tFont->BackColor);
. }
.
. /* 使用CPU内部字库. 点阵信息由CPU读取 */
. {
. /* 开始循环处理字符 */
. while (*_ptr != )
. {
. code1 = *_ptr; /* 读取字符串数据, 该数据可能是ascii代码,也可能汉字代码的高字节 */
. if (code1 < 0x80)
. {
. /* 将ascii字符点阵复制到buf */
. //memcpy(buf, &pAscDot[code1 * (font_bytes / 2)], (font_bytes / 2));
. _LCD_ReadAsciiDot(code1, _tFont->FontCode, buf); /* 读取ASCII字符点阵 */
. width = font_width / ;
. }
. else
. {
. code2 = *++_ptr;
. if (code2 == )
. {
. break;
. }
. /* 读1个汉字的点阵 */
. _LCD_ReadHZDot(code1, code2, _tFont->FontCode, buf);
. width = font_width;
. }
.
. y = _usY;
. /* 开始刷LCD */
. for (m = ; m < font_height; m++) /* 字符高度 */
. {
. x = _usX;
. for (i = ; i < width; i++) /* 字符宽度 */
. {
. if ((buf[m * (( * width) / font_width) + i / ] & (0x80 >> (i % ))) != 0x00)
. {
. LCD_PutPixel(x, y, _tFont->FrontColor); /* 设置像素颜色为文字色 */
. }
. else
. {
. if (_tFont->BackColor != CL_MASK) /* 透明色 */
. {
. LCD_PutPixel(x, y, _tFont->BackColor);/* 设置像素颜色为文字背景色 */
. }
. }
.
. x++;
. }
. y++;
. }
.
. if (_tFont->Space > )
. {
. /* 如果文字底色按_tFont->usBackColor,并且字间距大于点阵的宽度,那么需要在文字之间填
141. 充(暂时未实现) */
. }
. _usX += width + _tFont->Space; /* 列地址递增 */
. _ptr++; /* 指向下一个字符 */
. }
. }
. }
下面将代码中几个关键地方做个阐释:
- 第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编码值,读取对应的点阵数据出来。
. /*
2. ******************************************************************************************************
3. * 函 数 名: _LCD_ReadAsciiDot
4. * 功能说明: 读取1个ASCII字符的点阵数据
5. * 形 参:
6. * _code : ASCII字符的编码,1字节。1-128
7. * _fontcode :字体代码
8. * _pBuf : 存放读出的字符点阵数据
9. * 返 回 值: 文字宽度
10. ******************************************************************************************************
11. */
. static void _LCD_ReadAsciiDot(uint8_t _code, uint8_t _fontcode, uint8_t *_pBuf)
. {
. const uint8_t *pAscDot;
. uint8_t font_bytes = ;
.
. pAscDot = ;
. switch (_fontcode)
. {
. case FC_ST_12: /* 12点阵 */
. font_bytes = ;
. pAscDot = g_Ascii12;
. break;
.
. case FC_ST_24:
. case FC_ST_32:
. case FC_ST_16:
. /* 缺省是16点阵 */
. font_bytes = ;
. pAscDot = g_Ascii16;
. break;
.
. case FC_RA8875_16:
. case FC_RA8875_24:
. case FC_RA8875_32:
. return;
. }
.
. /* 将CPU内部Flash中的ascii字符点阵复制到buf */
. memcpy(_pBuf, &pAscDot[_code * (font_bytes / )], (font_bytes / ));
. }
下面将此函数涉及到的知识点为大家做个阐释:
- 第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编码值,读取对应的点阵数据出来。
. /*
2. ******************************************************************************************************
3. * 函 数 名: _LCD_ReadHZDot
4. * 功能说明: 读取1个汉字的点阵数据
5. * 形 参:
6. * _code1, _cod2 : 汉字内码. GB2312编码
7. * _fontcode :字体代码
8. * _pBuf : 存放读出的字符点阵数据
9. * 返 回 值: 无
10. ******************************************************************************************************
11. */
. static void _LCD_ReadHZDot(uint8_t _code1, uint8_t _code2, uint8_t _fontcode, uint8_t *_pBuf)
. {
. #ifdef USE_SMALL_FONT /* 使用CPU 内部Flash 小字库 */
. uint8_t *pDot;
. uint8_t font_bytes = ;
. uint32_t address;
. uint16_t m;
.
. pDot = ; /* 仅仅用于避免告警 */
. switch (_fontcode)
. {
. case FC_ST_12: /* 12点阵 */
. font_bytes = ;
. pDot = (uint8_t *)g_Hz12;
. break;
.
. case FC_ST_16:
. font_bytes = ;
. pDot = (uint8_t *)g_Hz16;
. break;
.
. case FC_ST_24:
. font_bytes = ;
. pDot = (uint8_t *)g_Hz24;
. break;
.
. case FC_ST_32:
. font_bytes = ;
. pDot = (uint8_t *)g_Hz32;
. break;
.
. case FC_RA8875_16:
. case FC_RA8875_24:
. case FC_RA8875_32:
. return;
. }
.
. m = ;
. while()
. {
. address = m * (font_bytes + );
. m++;
. if ((_code1 == pDot[address + ]) && (_code2 == pDot[address + ]))
. {
. address += ;
. memcpy(_pBuf, &pDot[address], font_bytes);
. break;
. }
. else if ((pDot[address + ] == 0xFF) && (pDot[address + ] == 0xFF))
. {
. /* 字库搜索完毕,未找到,则填充全FF */
. memset(_pBuf, 0xFF, font_bytes);
. break;
. }
. }
. #else /* 用全字库 */
. uint8_t *pDot = ;
. uint8_t font_bytes = ;
.
. switch (_fontcode)
. {
. case FC_ST_12: /* 12点阵 */
. font_bytes = ;
. pDot = (uint8_t *)HZK12_ADDR;
. break;
.
. case FC_ST_16:
. font_bytes = ;
. pDot = (uint8_t *)HZK16_ADDR;
. break;
.
. case FC_ST_24:
. font_bytes = ;
. pDot = (uint8_t *)HZK24_ADDR;
. break;
.
. case FC_ST_32:
. font_bytes = ;
. pDot = (uint8_t *)HZK32_ADDR;
. break;
.
. case FC_RA8875_16:
. case FC_RA8875_24:
. case FC_RA8875_32:
. return;
. }
.
. /* 此处需要根据字库文件存放位置进行修改 */
. if (_code1 >=0xA1 && _code1 <= 0xA9 && _code2 >=0xA1)
. {
. pDot += ((_code1 - 0xA1) * + (_code2 - 0xA1)) * font_bytes;
. }
. else if (_code1 >=0xB0 && _code1 <= 0xF7 && _code2 >=0xA1)
. {
. pDot += ((_code1 - 0xB0) * + (_code2 - 0xA1) + ) * font_bytes;
. }
. memcpy(_pBuf, pDot, font_bytes);
. #endif
. }
下面将此函数涉及到的知识点为大家做个阐释:
- 第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(); DispFirstPage(); /* 显示第1页 */ /* 界面整体显示完毕后,再打开背光,设置为缺省亮度 */
bsp_DelayMS();
ucBright = BRIGHT_DEFAULT;
LCD_SetBackLight(ucBright); bsp_StartAutoTimer(, ); /* 启动1个200ms的自动重装的定时器,软件定时器0 */ /* 进入主程序循环体 */
ucStatus = ;
fRefresh = ;
while ()
{
/* 判断软件定时器0是否超时 */
if(bsp_CheckTimer())
{
/* 每隔200ms 进来一次 */
bsp_LedToggle();
} if (fRefresh == )
{
fRefresh = ; switch (ucStatus)
{
case :
DispFirstPage(); /* 显示第1页 */
break; case :
DispAsciiDot(); /* 显示ASCII点阵 */
break; default:
/* 区码范围 :1 - 87 */
if (ucStatus <= )
{
DispHZK16(ucStatus); /* 显示一个区的94个汉字 */
}
break;
}
} ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
if (ucKeyCode != KEY_NONE)
{
/* 有键按下 */
switch (ucKeyCode)
{
case JOY_DOWN_L: /* 摇杆LEFT键按下 */
if (ucStatus > )
{
ucStatus--;
}
fRefresh = ; /* 请求刷新LCD */
break; case JOY_DOWN_R: /* 摇杆RIGHT键按下 */
if (ucStatus < DEMO_PAGE_COUNT - )
{
ucStatus++;
}
fRefresh = ; /* 请求刷新LCD */
break; case JOY_DOWN_OK: /* 摇杆OK键 */
ucStatus = ; /* 返回首页 */
fRefresh = ; /* 请求刷新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 = ;
}
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[]; LCD_ClrScr(CL_BLUE); /* 清屏,背景蓝色 */ /* 设置字体属性 */
tFont.FontCode = FC_ST_16; /* 字体选择宋体16点阵,高16x宽15) */
tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */
tFont.BackColor = CL_MASK; /* 文字背景颜色,透明 */
tFont.Space = ; /* 字符水平间距, 单位 = 像素 */ y = ;
usLineCap = ; /* 行间距 */
LCD_DispStr(, y, "安富莱STM32-V7开发板 www.armfly.com", &tFont);
y += usLineCap;
LCD_DispStr(, y, "汉字小字库和全字库测试实验", &tFont); y += * usLineCap;
LCD_DispStr(, y, "操作提示:", &tFont); y += usLineCap;
LCD_DispStr(, y, "摇杆上键 = 增加背光亮度", &tFont); y += usLineCap;
LCD_DispStr(, y, "摇杆下键 = 降低背光亮度", &tFont); y += usLineCap;
LCD_DispStr(, y, "摇杆左键 = 向前翻页", &tFont); y += usLineCap;
LCD_DispStr(, y, "摇杆右键 = 向后翻页", &tFont); y += usLineCap;
LCD_DispStr(, y, "摇杆OK键 = 返回首页", &tFont); y += * usLineCap; sprintf((char *)buf, "显示器分辨率 :%dx%d", g_LcdWidth, g_LcdHeight);
LCD_DispStr(, y, (char *)buf, &tFont); y += usLineCap;
LCD_DispStr(, 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[ + ];
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 = ; /* 字符水平间距, 单位 = 像素 */ LCD_DispStr(, , "16点阵ASCII码字库,代码1-127", &tFont); x = ;
y = ;
ascii = ;
for (i = ; i < ; i++)
{
for (k = ; k < ; k++)
{
buf[k] = ascii++;
}
buf[] = ;
if (buf[] == )
{
buf[] = ' ';
}
LCD_DispStr(x, y, buf, &tFont);
y += ;
}
}
下面是GB2312编码字符显示:
/*
*********************************************************************************************************
* 函 数 名: DispHZK16
* 功能说明: 显示16点阵汉字阵
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
static void DispHZK16(uint8_t _ucIndex)
{
uint8_t i,k;
uint16_t x,y;
char buf[ + ];
uint8_t code1,code2; /* 汉字内码 */
uint8_t usLineCap = ;
FONT_T tFont; /* 定义一个字体结构体变量,用于设置字体参数 */ printf(" Display HZK Area Code = %d\r\n", _ucIndex - ); if (_ucIndex == )
{
/* 第1次清屏,以后显示位置不变,可以不清屏,避免闪烁 */
LCD_ClrScr(CL_BLUE); /* 清屏,背景蓝色 */
} /* 设置字体属性 */
tFont.FontCode = FC_ST_16; /* 字体选择宋体16点阵,高16x宽15) */
tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */
tFont.BackColor = CL_BLUE; /* 文字背景颜色,蓝色 */
tFont.Space = ; /* 字符水平间距, 单位 = 像素 */ y = ;
LCD_DispStr(, y, "国标GB2312 16点阵汉字库(区码1-87,位码1-94)", &tFont); code1 = _ucIndex - ; /* 得到区码 */
code2 = ; /* 位码从1开始 */ y += usLineCap;
sprintf((char *)buf, (char *)"当前区码: %2d, 本页位码:1-94, 第10-15区无字符", code1);
LCD_DispStr(, y, buf, &tFont);
y += ( * usLineCap); /*
机内码高位 = 区码 + 0xA0
机内码低位 = 位码 + 0xA0 区码范围 :1 - 87
位码范围 : 1 - 94 每行显示20个汉字,一个区是94个汉字,需要5行显示,第5行显示14个汉字
*/ x = ;
code1 += 0xA0; /* 换算到内码高位 */
code2 = 0xA1; /* 内码低位起始 */
for (i = ; i < ; i++)
{
for (k = ; k < ; k++)
{
buf[ * k] = code1;
buf[ * k + ] = code2;
code2++;
if ((i == ) && (k == ))
{
k++;
break;
}
}
buf[ * k] = ;
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(); DispFirstPage(); /* 显示第1页 */ /* 界面整体显示完毕后,再打开背光,设置为缺省亮度 */
bsp_DelayMS();
ucBright = BRIGHT_DEFAULT;
LCD_SetBackLight(ucBright); bsp_StartAutoTimer(, ); /* 启动1个200ms的自动重装的定时器,软件定时器0 */ /* 进入主程序循环体 */
ucStatus = ;
fRefresh = ;
while ()
{
/* 判断软件定时器0是否超时 */
if(bsp_CheckTimer())
{
/* 每隔200ms 进来一次 */
bsp_LedToggle();
} if (fRefresh == )
{
fRefresh = ; switch (ucStatus)
{
case :
DispFirstPage(); /* 显示第1页 */
break; case :
DispAsciiDot(); /* 显示ASCII点阵 */
break; default:
/* 区码范围 :1 - 87 */
if (ucStatus <= )
{
DispHZK16(ucStatus); /* 显示一个区的94个汉字 */
}
break;
}
} ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
if (ucKeyCode != KEY_NONE)
{
/* 有键按下 */
switch (ucKeyCode)
{
case JOY_DOWN_L: /* 摇杆LEFT键按下 */
if (ucStatus > )
{
ucStatus--;
}
fRefresh = ; /* 请求刷新LCD */
break; case JOY_DOWN_R: /* 摇杆RIGHT键按下 */
if (ucStatus < DEMO_PAGE_COUNT - )
{
ucStatus++;
}
fRefresh = ; /* 请求刷新LCD */
break; case JOY_DOWN_OK: /* 摇杆OK键 */
ucStatus = ; /* 返回首页 */
fRefresh = ; /* 请求刷新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 = ;
}
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[]; LCD_ClrScr(CL_BLUE); /* 清屏,背景蓝色 */ /* 设置字体属性 */
tFont.FontCode = FC_ST_16; /* 字体选择宋体16点阵,高16x宽15) */
tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */
tFont.BackColor = CL_MASK; /* 文字背景颜色,透明 */
tFont.Space = ; /* 字符水平间距, 单位 = 像素 */ y = ;
usLineCap = ; /* 行间距 */
LCD_DispStr(, y, "安富莱STM32-V7开发板 www.armfly.com", &tFont);
y += usLineCap;
LCD_DispStr(, y, "汉字小字库和全字库测试实验", &tFont); y += * usLineCap;
LCD_DispStr(, y, "操作提示:", &tFont); y += usLineCap;
LCD_DispStr(, y, "摇杆上键 = 增加背光亮度", &tFont); y += usLineCap;
LCD_DispStr(, y, "摇杆下键 = 降低背光亮度", &tFont); y += usLineCap;
LCD_DispStr(, y, "摇杆左键 = 向前翻页", &tFont); y += usLineCap;
LCD_DispStr(, y, "摇杆右键 = 向后翻页", &tFont); y += usLineCap;
LCD_DispStr(, y, "摇杆OK键 = 返回首页", &tFont); y += * usLineCap; sprintf((char *)buf, "显示器分辨率 :%dx%d", g_LcdWidth, g_LcdHeight);
LCD_DispStr(, y, (char *)buf, &tFont); y += usLineCap;
LCD_DispStr(, 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[ + ];
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 = ; /* 字符水平间距, 单位 = 像素 */ LCD_DispStr(, , "16点阵ASCII码字库,代码1-127", &tFont); x = ;
y = ;
ascii = ;
for (i = ; i < ; i++)
{
for (k = ; k < ; k++)
{
buf[k] = ascii++;
}
buf[] = ;
if (buf[] == )
{
buf[] = ' ';
}
LCD_DispStr(x, y, buf, &tFont);
y += ;
}
}
下面是GB2312编码字符显示:
/*
*********************************************************************************************************
* 函 数 名: DispHZK16
* 功能说明: 显示16点阵汉字阵
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
static void DispHZK16(uint8_t _ucIndex)
{
uint8_t i,k;
uint16_t x,y;
char buf[ + ];
uint8_t code1,code2; /* 汉字内码 */
uint8_t usLineCap = ;
FONT_T tFont; /* 定义一个字体结构体变量,用于设置字体参数 */ printf(" Display HZK Area Code = %d\r\n", _ucIndex - ); if (_ucIndex == )
{
/* 第1次清屏,以后显示位置不变,可以不清屏,避免闪烁 */
LCD_ClrScr(CL_BLUE); /* 清屏,背景蓝色 */
} /* 设置字体属性 */
tFont.FontCode = FC_ST_16; /* 字体选择宋体16点阵,高16x宽15) */
tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */
tFont.BackColor = CL_BLUE; /* 文字背景颜色,蓝色 */
tFont.Space = ; /* 字符水平间距, 单位 = 像素 */ y = ;
LCD_DispStr(, y, "国标GB2312 16点阵汉字库(区码1-87,位码1-94)", &tFont); code1 = _ucIndex - ; /* 得到区码 */
code2 = ; /* 位码从1开始 */ y += usLineCap;
sprintf((char *)buf, (char *)"当前区码: %2d, 本页位码:1-94, 第10-15区无字符", code1);
LCD_DispStr(, y, buf, &tFont);
y += ( * usLineCap); /*
机内码高位 = 区码 + 0xA0
机内码低位 = 位码 + 0xA0 区码范围 :1 - 87
位码范围 : 1 - 94 每行显示20个汉字,一个区是94个汉字,需要5行显示,第5行显示14个汉字
*/ x = ;
code1 += 0xA0; /* 换算到内码高位 */
code2 = 0xA1; /* 内码低位起始 */
for (i = ; i < ; i++)
{
for (k = ; k < ; k++)
{
buf[ * k] = code1;
buf[ * k + ] = code2;
code2++;
if ((i == ) && (k == ))
{
k++;
break;
}
}
buf[ * k] = ;
LCD_DispStr(x, y, buf, &tFont);
y += usLineCap;
}
}
53.10 总结
本章节涉及到的知识点比较多,需要大家花点时间去掌握,直至可以独立驱动一个显示屏。