无论什么屏幕,都是由一个个像素点组成的,所以显示英文字符’A’的原理如图:
显示一个字符需要确定字符的高度和宽度:
取模软件使用PCtoLCD2002完美版,附上下载链接。
取模软件中的设置如下:
这里我使用的屏幕分辨率240240,所以我们选择显示一个240120的字符’A’,在取模软件中设置大小:
修改文件名为font_A.h
,然后修改文件格式如下,便于后续使用:
const char CH_A[] =
{
//生成的字符数据
};
这个数组具有225行,每行16个字节的数据,每个字节对应该处的8个像素点是否显示,然后程序根据此数组判断是否要向像素点写入颜色。
简单的测试程序如下:
void LCD_ShowChar(uint16_t x, uint16_t y, char ch, uint16_t back_color, uint16_t font_color, uint8_t font_size)
{
int i = 0, j = 0;
uint8_t temp = 0;
LCD_Address_Set(x, y, x + font_size/2 - 1, y + font_size - 1);//(x,y,x+8-1,y+16-1)
for(i = 0;i < 225*16; i++)
{
temp = CH_A[i];
for(j = 0; j < 8;j++)
{
//判断temp的最高位是否为1
if(temp & 0x80)
{
//最高位为1,显示该点,设置颜色为字体颜色
LCD_Write_2Byte(font_color);
}
else
{
//最高位为0,不显示该点,设置该点为背景色
LCD_Write_2Byte(back_color);
}
temp <<= 1;
}
}
}
在mian.c中调用,如下:
int main(void)
{
HAL_Init();
SystemClock_Config();
LCD_Init();
LCD_ShowChar(60 ,0,'A',BLACK,YELLOW,240);
while (1);
}
当程序中只需要显示几个字符时,可以使用取模的方式,也非常节省内存,但是当我们程序中需要使用不确定的字符时或者大量字符时,逐个取模的方式显然不可行,需要使用一个已经将常用字符按常用大小曲目完成的字库文件,即font.h
,文件详细内容见我的Github。
首先将字符文件包含进来:
#include "font.h"
然后在之前编写的函数上进行改进:
/**
* @brief 显示一个ASCII码字符
* @param x,y 显示起始坐标
* @param ch 需要显示的字符
* @param size 字体大小(支持16/24/32号字体)
* @return none
* @note 需要font.h字库文件的支持
*/
void LCD_ShowChar(uint16_t x, uint16_t y, char ch, uint16_t back_color, uint16_t font_color, uint8_t font_size)
{
int i = 0, j = 0;
uint8_t temp = 0;
uint8_t size = 0;
uint8_t t = 0;
/* 检测显示是否会越界 */
if((x > (LCD_Width - font_size / 2)) || (y > (LCD_Height - font_size)))
return;
/* 根据字符大小设置显存操作区域 */
LCD_Address_Set(x, y, x + font_size/2 - 1, y + font_size - 1);
/* 计算字符在字库中的偏移值*/
ch = ch - ' ';
/* 显示16号/32号字体 */
if((font_size == 16) || (font_size == 32) )
{
/* 计算字体一个字符对应点阵集所占的字节数 */
size = (font_size / 8 + ((font_size % 8) ? 1 : 0)) * (font_size / 2);
for(i = 0; i < size; i++)
{
if(font_size == 16)
temp = asc2_1608[ch][i]; //调用1608字体
else if(font_size == 32)
temp = asc2_3216[ch][i]; //调用3216字体
else
return; //没有的字库
for(j = 0; j < 8; j++)
{
if(temp & 0x80)
LCD_Write_2Byte(font_color);
else
LCD_Write_2Byte(back_color);
temp <<= 1;
}
}
}
/* 显示12号字体 */
else if(font_size == 12)
{
/* 计算字体一个字符对应点阵集所占的字节数 */
size = (font_size / 8 + ((font_size % 8) ? 1 : 0)) * (font_size / 2);
for(i = 0; i < size; i++)
{
temp = asc2_1206[ch][i];
for(j = 0; j < 6; j++)
{
if(temp & 0x80)
LCD_Write_2Byte(font_color);
else
LCD_Write_2Byte(back_color);
temp <<= 1;
}
}
}
/* 显示24号字体 */
else if(font_size == 24)
{
/* 计算字体一个字符对应点阵集所占的字节数 */
size = (font_size * 16) / 8;
for(i = 0; i < size; i++)
{
temp = asc2_2412[ch][i];
if(i % 2 == 0)
t = 8;
else
t = 4;
for(j = 0; j < t; j++)
{
if(temp & 0x80)
LCD_Write_2Byte(font_color);
else
LCD_Write_2Byte(back_color);
temp <<= 1;
}
}
}
/* 其余字体 */
else
return;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
LCD_Init();
LCD_ShowChar(0,0,'A',BLACK,BLUE,12);
LCD_ShowChar(6,12,'B',BLACK,YELLOW,16);
LCD_ShowChar(14,28,'C',BLACK,GREEN,24);
LCD_ShowChar(26,52,'D',BLACK,PINK,32);
while (1);
}
/**
* @brief 显示一个ASCII码字符串
* @param x,y 显示起始坐标
* @param str 需要显示的字符串
* @param size 字体大小(支持16/24/32号字体)
* @return none
* @note 1. 需要font.h字库文件的支持
* 2. 超过指定width不显示超过的字符
*/
void LCD_ShowCharStr(uint16_t x, uint16_t y, uint8_t max_width, char* str, uint16_t back_color, uint16_t font_color, uint8_t font_size)
{
max_width += x;
while((*str <= '~') && (*str >= ' ')) //判断是否非法字符
{
if(x >= max_width)
{
//x方向越界,结束
break;
}
LCD_ShowChar(x,y,*str,back_color, font_color,font_size);
x += font_size / 2;
str++;
}
}
测试代码:
int main(void)
{
HAL_Init();
SystemClock_Config();
LCD_Init();
LCD_ShowChar(0,0,'A',BLACK,BLUE,12);
LCD_ShowChar(6,12,'B',BLACK,YELLOW,16);
LCD_ShowChar(14,28,'C',BLACK,GREEN,24);
LCD_ShowChar(26,52,'D',BLACK,PINK,32);
LCD_ShowCharStr(0,240-32,240,"Mculover666",BLACK,BLUE,32);
while (1);
}