背景:
最近在使用裸机的方式绘制LCD显式页面,主要参考中景园LCD的历程,在显式驱动移植成果后,设计了两个页面,页面切换中有肉眼可见的延迟,因此想着能不能提高字符显式的频率。
中景园代码分析:
这里使用的代码是基于中景园LCD修改的,逻辑大体一直,可能部分会有不同,此处重在分析代码的逻辑。
中景园历程中代码中显式字符方式使用依次打点的方式显式的,具体过程如下;
1、查找字符font,
2、设置lcd填充区域为font字体大小,如16字体的char实际填充为宽8高16
2、在对应的font字体中逐bit查找,
3、bit如果是1则写入颜色为字体色,如果bit为0,则写入颜色为背景色
4、直到一个char的font查找完成
代码分析
/*
* function:drawChar
* parameter: x:x 轴显式坐标,
* y:y 轴显式坐标
* ch:显式的字符
* charColor: 字符的颜色
* backgroundColor:背景的颜色
* size:字号:12,16,24,32
* return:void
*/
void TFT_ST7789::drawChar(uint16_t x, uint16_t y, const char ch,
uint16_t color, uint16_t bgColor, uint8_t size)
{
uint8_t temp, sizex, t, m = 0;// m用于标记一行中的颜色bit数
uint16_t i, TypefaceNum; //一个字符所占字节大小
uint16_t x0 = x;
sizex = size / 2;// sizex用于记录字符显式的宽度的bit数
TypefaceNum = (sizex / 8 + ((sizex % 8) ? 1 : 0)) * size;// 计算font中byte的格式
char chTemp = ch - ' '; //得到偏移后的值
startWrite(); // 使能lcd写功能
setAddrWindow(x, y, sizex, size); //设置光标位置,以及填充区域
for (i = 0; i < TypefaceNum; i++)
{
if (size == 12)
temp = ascii_1206[chTemp][i]; //调用6x12字体
else if (size == 16)
temp = ascii_1608[chTemp][i]; //调用8x16字体
else if (size == 24)
temp = ascii_2412[chTemp][i]; //调用12x24字体
else if (size == 32)
temp = ascii_wenquan_3216[chTemp][i]; //调用16x32字体
else
return;
for (t = 0; t < 8; t++) // 在font中的每个byte的每个bit依次判断,
// 如果bit为1,则写字体color,如果bit为0,ze 写入背景色gbColor
{
//非叠加模式
if (temp & (0x01 << t))
writeWord(color);
else
writeWord(bgColor);
m++; // 记录字体宽度,
if (m % sizex == 0) // 一行的宽度达到后,切换下一行
{
m = 0;
break;
}
}
}
endWrite(); // 失能写功能
}
优化后的代码分析
为了提高刷屏效率,改为先写内存,后使用dma刷新屏幕的方式
1、需要在写char时先在内存中声明一块内存,为了方便,声明为二维数组
2、根据字体大小,在二维数组中填充背景色
3、查找char的font,
4、如果font中bit为1,则在对应二维数组的位置写入字体色
5、直达遍历完char的font数组
6、将二维数组使用dma逐行写入lcd
void TFT_ST7789::drawChar(uint16_t x, uint16_t y, const char ch,
uint16_t color, uint16_t bgColor, uint8_t size)
{
uint16_t temp, sizex, t, m = 0, n = 0; // sizex为字符宽度,m记录行,n记录列
uint16_t i, TypefaceNum; //一个字符所占字节大小
uint16_t x0 = x;
sizex = size / 2;
TypefaceNum = (sizex / 8 + ((sizex % 8) ? 1 : 0)) * size;
char chTemp = ch - ' '; //得到偏移后的值
uint16_t localDisplayBuff[32][16] = {0}; // 先将底色绘制到内存中,最大字体为32*16
for (int i = 0; i < size; i++)
{
for (int j = 0; j < sizex; j++)
{
localDisplayBuff[i][j] = bgColor;
}
}
for (i = 0; i < TypefaceNum; i++)
{
if (size == 12)
{
temp = ascii_1206[chTemp][i]; //调用6x12字体
}
else if (size == 16)
{
temp = ascii_1608[chTemp][i]; //调用8x16字体
}
else if (size == 24)
{
temp = ascii_2412[chTemp][i]; //调用12x24字体
}
else if (size == 32)
{
temp = ascii_wenquan_3216[chTemp][i]; //调用16x32字体
}
else
{
return;
}
for (t = 0; t < 8; t++)
{
if (temp & (0x01 << t))
{
localDisplayBuff[n][m] = color;
}
m++; // 记录一行中的像素点位置
if (m % sizex == 0)
{
n++; // 一行记录完成,行号加一
m = 0; // 列号清零
break;
}
}
}
startWrite();
setAddrWindow(x, y, sizex, size); // 设置光标位置与填充区域
for (int i = 0; i < size; i++) // 逐行绘制到LCD
{
writePixels(&localDisplayBuff[i][0], sizex);
}
endWrite();
}
进一步优化
将二维数组修改为一位数组,使用DAM一次写入。
/*
* function:drawChar
* parameter: x:x 轴显式坐标,
* y:y 轴显式坐标
* ch:显式的字符
* charColor: 字符的颜色
* backgroundColor:背景的颜色
* size:字号:12,16,24,32
* return:void
*/
void TFT_ST7789::drawChar(uint16_t x, uint16_t y, const char ch,
uint16_t color, uint16_t bgColor, uint8_t size)
{
uint16_t temp, sizex, t, m = 0, n = 0; // sizex为字符宽度,m记录行,n记录列
uint16_t i, TypefaceNum; //一个字符所占字节大小
uint16_t x0 = x;
sizex = size / 2;
TypefaceNum = (sizex / 8 + ((sizex % 8) ? 1 : 0)) * size;
char chTemp = ch - ' '; //得到偏移后的值
uint16_t localDisplayBuff[32 * 16] = {0}; // 先将底色绘制到内存中,最大字体为32*16
for (int i = 0; i < size * sizex; i++)
{
localDisplayBuff[i] = bgColor;
}
for (i = 0; i < TypefaceNum; i++)
{
if (size == 12)
{
temp = ascii_1206[chTemp][i]; //调用6x12字体
}
else if (size == 16)
{
temp = ascii_1608[chTemp][i]; //调用8x16字体
}
else if (size == 24)
{
temp = ascii_2412[chTemp][i]; //调用12x24字体
}
else if (size == 32)
{
temp = ascii_wenquan_3216[chTemp][i]; //调用16x32字体
}
else
{
return;
}
for (t = 0; t < 8; t++)
{
if (temp & (0x01 << t))
{
localDisplayBuff[n * sizex + m] = color;
}
m++; // 记录一行中的像素点位置
if (m % sizex == 0)
{
n++; // 一行记录完成,行号加一
m = 0; // 列号清零
break;
}
}
}
startWrite();
setAddrWindow(x, y, sizex, size); // 设置光标位置与填充区域
writePixels(localDisplayBuff, size * sizex);
endWrite();
}
总结
使用先在内存中绘制,然后再使用DMA写入LCD的方式极大的提高的LCD显式字符的效率,缺点是会增加代码中stack的使用量,综合起来,使用stack换时间的代价是值得的。