正点原子优化代码系列1——字符串排版
- 刚刚开始学习嵌入式开发,现在使用的是 正点原子@ALIENTEK 的stm32的板子。
本系列主要记录博主在学习过程中,一些针对正点原子原生代码的优化和一些脑洞想法以及其实现。
在学习正点原子的lcd章节中,正点原子可以提供了一部分可以作为工具使用的原生函数,但是在些代码很多带有小瑕疵。
比如 输出字符串:
void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p)
//起始的x,y位置 单行长度 单行高度 字号 字符串指针
{
u8 x0=x;
width+=x;
//计算单行结束位置
height+=y;
//计算结束高度
//输出是从左到右,从上到下进行的,所以结束高度也是输出结束的标志
while((*p<='~')&&(*p>=' '))//判断非法字符
{
if(x>=width){x=x0;y+=size;}
//当输出光标超过单行结束位置时,光标打回同时换行
if(y>=height)break;
//当光标超过结束高度时,结束输出
LCD_ShowChar(x,y,*p,size,0);
//执行输出单个字符
x+=size/2;
//英文字母的字号佔标准字号的一般,所以这里只用增加一般的size
p++;
}
}
- 注释由博主添加
其中LCD_ShowChar也是原生代码中自带的函数,这个函数的主要功能显而易见,在指定的x,y位置输出size大小的一个字符。
接下来看看实际效果:
(图片截录来自正点原子的开发教程,lcd开发实验)
乍看之下除了背景色和字符串底色不同,其实并没有什么问题。(这个问题后面也会解决。)
但是来看一下展示的代码:
LCD_ShowString(30,40,200,24,24,"Mini STM32 ^_^");
LCD_ShowString(30,70,100,80,16,"TFTLCD TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,lcd_id);
LCD_ShowString(30,130,200,12,12,"2014/3/7");
所有的换行都是由代码实现,但是就之前那showstring的源码来看,这个函数是具备换行功能的。
那么实际的效果呢???
(图片截录来自正点原子的开发教程,触摸屏开发实验)
图中可以发现,cross、styluts、until等单词都出现“断词“的迹象。
- 那么我们就来思考一下怎么解决这一问题!
首先,要解决这一个问题我们要做的第一件事就是确保单词的连通性,也就说每次输出的都是一个单词,而非一个字母。那么思考一下文本中单词和单词之间的共性是什么?
答案是空格!
两个相邻的单词之间必然存在空格,也就是说空格可以成为我们保障读取单词的标志。
(ps.寻找共性也是NS规划执行中最重要的手段之一)
接下来实现代码:
void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p)
{
u8 x0=x;
u8 *char_end;
//在原生代码基础上增加了扫描单词结束的标志指针
width+=x;
height+=y;
char_end=p;
//定位到字符串
while((*p<='~')&&(*p>=' '))//判断非法字符
{
if(y>height)break;
while((*char_end<='~')&&(*char_end>' ')){char_end++;}
//开始读取单词,判断是否为非法字符,并当读到空格时结束。
if((char_end-p)*(size>>1)<(width-x)){
//第一次执行时,字符串指针p还在原始位置,char_end指针指向了字符串第一个单词后的空格
//判断剩余的空格是不是足够存放这个单词
//*(size>>1)说明:char_end-p是多少个单词,在屏幕上具体佔多少位还要*(size>>1)
//是的话
while(p<char_end){
//开始输出单词
LCD_ShowChar(x,y,*p,size,0);
p++;
x+=(size>>1);
}
char_end++;
//输出结束,char_end指针移向下一个单词的开头
}else{
//不是的话,换行
x=x0;y+=size;
}
}
}
OK!代码完成,但是实际效果会有问题!
- 由于没有照相,博主只能口述实际效果;解决了“断词”现象,但是每次换行的首位会出现一个空格。
重新过一遍代码我们发现源代码逻辑有这样的问题。
while(p<char_end){
//开始输出单词
LCD_ShowChar(x,y,*p,size,0);
p++;
x+=(size>>1);
}
char_end++;
//输出结束,char_end指针移向下一个单词的开头
这段代码在结束后,p指在空格,char_end指在新单词的首字。接下来char_end继续读取单词,对比,当发现单词不能填入本行后换行时,p依旧指在空格,char_end指在单词结尾,输出。
逻辑清楚了,改很简单。
else{
x=x0;y+=size;
p++;//换行时p后移,去除空格
}
好了,代码完成了。
这时候想想有没可能出现极端报错,比如第一个单词就超出设定的行宽,单词之间有不法字符,单词之间有多个空格。
好吧,经过测试,这些极端情况都没有错误。
那么最后有一个问题,由于单词无法填入每行的空余量时就会换行,这就导致了每一行其实都很难填满,除非凑巧字数刚刚好。这就导致了在屏幕上的展示效果极差。应该如何修正?
if((char_end-p)<(width-x))
原先在判断时,我们仅考虑能不能填满,现在需要给出一个修正量,也就说允许超过几个字母。
最后代码修正如下:
{
u8 x0=x;
u8 *char_end;
width+=x;
height+=y;
char_end=p;
while((*p<='~')&&(*p>=' ')){
if(y>height)break;
while((*char_end<='~')&&(*char_end>' ')){char_end++;}
if(((char_end-p)*(size>>1)-(size<<1))<(width-x)){
///size<<1 2倍的size即4个字母的长度
while(p<char_end){
LCD_ShowChar(x,y,*p,size,0);
p++;
x+=(size>>1);
}
char_end++;
}else{
x=x0;y+=size;
p++;
}
}
}
最后跑了一个莎翁的《as you like it》节选确认了效果。
- ps.小知识:莎士比亚一生创造了超过1700个英语词汇,使用的手段包括名词活用为动词,动词活用为形容词,把从不在一起用的两个词组合成一个词,添加前缀和后缀,以及创造全新的词汇。现在大量公司默认选用其作品作为英语展示的测试。