已过天命之年,自Protues仿真学习AVR也有大半年时间了,一直对STM32望而却步,不敢涉足,怀着Protues8.0能仿真STM32的期待,结果失望,无奈经不住STM32高性能和低价格的诱惑,在克服对STM32超长C代码和复杂的寄存器操作恐惧后,还是踏进了STM32之门,一个月来,初学的困惑加之零基础的C语言知识,正是应了那句古语:“少壮不努力,老大图伤悲”。
首次发贴,诚惶诚恐,不当之处,还请各位单片机的先驱们斧正。本着开源精神和为后来初学者释疑的意图,把这半个月来对彩屏液晶汉字显示的理解,所做的一点努力与各位爱好者共享。
一、汉字字库的制作
汉字的取模采用软件ts3“点阵字库生成器3.8”模式用“横向取模方式1”,软件网上可以找到。用该软件可以制作任何字体和不大于32×32字号的汉字库。注意:做好的字库要用ULTRAEDIT或WINHEX等编辑软件,将字库从后面开始所有0X00的字节直到不含0x00的那些行删掉,以减少字库大小。
ASCII码取模,用PCtoLCD2002.exe软件制作,取模方式“顺向(高位在前)、逐行式”按16×16、24×24等方式取模,保存C文件,删除文件内的注释部份,只保存纯粹的16进制文件如:0x00,0x00,注意删除最后一个逗号,网上下载C文件转BIN软件,将ASCII的C文件转成字库文件.bin后缀文件。
二、图标字库的制作
在选择字体制作的时候,偶然选择了WINDOWS自带的WEBDINGS等4个字体,打开看到是一些图标字库,于是产生了制作图标字库用于液晶屏显示的想法,字库可不可以编辑修改,制作自己要求的图标呢?网上搜索果然有这样软件,下载字库编辑 FontCreator如下图:该软件非常容易上手,几分钟就可以熟练使用,十分方便。
1、我选择的是修改WEBDINGS字库,先备份WINDOWS\FONTS\WEBDINGS.TTF文件、然后删除该文件,用备份的文件复制一个取名为:WEB.TTF(因Webdings.ttf文件是WINDOWS系统管理的文件,无论你取什么名,最后复制粘贴安装的时候都会默认为Webdings.ttf名字)。
2、图标准备:可以选择任何后缀的图像文件,ICO、JPG、png等文件,用Photoshop等软件做去色处理,转换成黑白图像,转存为jpg后缀的文件,或者用矢量图制作软件CorelDraw、图标制作软件 IconWorkshop,无论什么图像只要是黑白的即可。用FontCreator软件开始每个字的修改,调整尺寸不超边界即可,FontCreator的任何设置不要修改,保持默认,你只要做插入图像、调整尺寸的排版工作。
3、特别说明:ASCII码的后128个图标,因为键盘无法输入(包括ALT+小键盘数字组合),不必修改,可以把你喜欢的复制到94个ASCII码的区域里。“””和“\”对应的图标可以不用修改,因为C编译器认为在输出字符串的时候为非法字符。
三、字库更新
说实话,刚转入STM32才接触到无处不在的结构体C语句,目前还不会使用,只好按照自己的想法,采用笨办法来更新字库,好在字库更新只做1、2次而已。用ULTRAEDIT打开字库文件,依次追加其他字库文件,需记住追加字库的名称和起始地址,如:宋体16×16的地址是0002DD90H------0006DBCFH那么把起始地址0002DD90H转换到10进制为:187792,后追加的字库如果不占整行,用00补齐。4.3寸的液晶屏的FLASH是W25Q16换成8M字节的W25Q64,SD卡的FATFS文件系统打不开大于2M字节的文件,因此制作了5个字库文件,每个大约1-1.7M左右,用开发板带的更新字库函数,分5次刷入FLASH中,注意:第二次以后刷入的时候,要修改文件名,屏蔽擦除整片FLASH的语句,修改写入地址即可。
四、汉字显示的实现
既然是单片机控制彩屏,那么单一的16×16的汉字显示,在界面设计上就显得力不从心了。尤其的4.3寸以上的彩屏,蝇头小字显得比例失调,因此就需要大字体来做显示,那末,如何实现大字体显示呢?且看板商提供的显示一个16×16汉字的部份程序:
pusMsk = (uint16_t *)pucMsk;
for(i=0; i<16; i++) /* 保存当前汉字点阵式字模 */
{ mod = *pusMsk++; /* 取得当前字模,半字对齐访问*/
mod = ((mod & 0xff00) >> 8) | ((mod & 0x00ff) << 8);/* 字模交换高低字节(为了显示 需要 */
}
y0 = y;
for(i=0; i<16; i++) /* 16行 */
{ /* 直接显示 */ //准备写GRAM
for(j=0; j<16; j++) /* 16列 */
{ if((mod << j)& 0x8000) /* 显示字模 */
{ LCD_SetPoint(x+j,y0,0x0); // 字符颜色
} else {
LCD_SetPoint(x+j,y0,0xffff); // 字符颜色 /* 用读方式跳过写空白点的像素*/
}
}
y0++;
}
从程序上看,意图很明显,就是要取一个汉字的16行,16列去显示,可这并不是遵循取模原理去显示的,为了兼顾显示问题,只好在一个大循环中做高低字节的交换,一个无为的字节交换。开始移植24×24汉字的时候,就被这无为的字节交换逼近死胡同里了。于是,回头看看汉字的取模原理,原来取模是分横向和竖向,分高位在前和地位在前的,所以回显也必须遵循这个原则。以横向取模高位在前为例,是按行取,每次取1个字节,所以就存在这样的关系:
16×16 每行2个字节,24×24 每行3个字节,32×32 每行4个字节 ,40×40 每行5个字节,依次类推。每个字所占容量是:SIZE×SIZE/BYTE
明白这个关系,剩下的就是如何读取这些点到数组,如何从这个数组中取数,送到什么位置显示的问题了,解决了1个字号的显示,其他任何字号的汉字显示迎刃而解。
实现显示1个汉字的程序如下:
u16 LCD_Show_Word(u16 x,u16 y,u8 FONT,u8 SIZE,u8 MODE,u16 CharColor,u16 bkColor,u8 *Str)
{
u8 k,i,j,qh,wh,EN_BUF,HZ_BUF;
u8 NEXT=2,H;//,QH
u32 offset, HZ_OFFSET,EN_OFFSET;
char buffer[200]; /*汉字缓存,取最大的40×40字体,备忘2013-5-22数组的长度不能用变量*/
H = SIZE/BYT; //汉字行占字节个数 备忘2013-5-22使用预编译#if等语句时,变量必须是个确定值
switch (SIZE) //字体大小判断
{
case 16: EN_BUF=EN_16BUF, HZ_BUF=HZ_16BUF, NEXT--, EN_OFFSET = EN_ADD_ST16 ;
switch (FONT)
{
case 0: HZ_OFFSET = HZ_ADD_ST16 ; break;//汉字16×16地址
case 1: HZ_OFFSET = HZ_ADD_KT16 ; break;
case 2: HZ_OFFSET = HZ_ADD_HT16 ; break;
case 3: HZ_OFFSET = HZ_ADD_YH16 ; break;
case 4: HZ_OFFSET = HZ_ADD_XK16 ; break;
case 5: HZ_OFFSET = HZ_ADD_CY16 ; break;
case 6: HZ_OFFSET = WEB_ADD_16; break;
case 7: HZ_OFFSET = WEB_DINGS_16; break;
case 8: HZ_OFFSET = WING_DINGS_16; break;
case 9: HZ_OFFSET = WING_DINGS2_16; break;
default: break;
}
break;
case 24: EN_BUF=EN_24BUF, HZ_BUF=HZ_24BUF, EN_OFFSET = EN_ADD_ST24;
switch (FONT)
{
case 0: HZ_OFFSET = HZ_ADD_ST24 ; break;
case 1: HZ_OFFSET = HZ_ADD_KT24 ; break;
case 2: HZ_OFFSET = HZ_ADD_HT24 ; break;
case 3: HZ_OFFSET = HZ_ADD_YH24 ; break;
case 4: HZ_OFFSET = HZ_ADD_XK24 ; break;
case 5: HZ_OFFSET = HZ_ADD_CY24 ; break;
case 6: HZ_OFFSET = WEB_ADD_24 ; break;
case 7: HZ_OFFSET = WEB_DINGS_24 ; break;
case 8: HZ_OFFSET = WING_DINGS_24; break;
case 9: HZ_OFFSET = WING_DINGS2_24; break;
default: break;
}
break;
case 32: EN_BUF = EN_32BUF, HZ_BUF=HZ_32BUF, EN_OFFSET = EN_ADD_ST32;
switch (FONT)
{
case 0: HZ_OFFSET = HZ_ADD_ST32 ; break;
case 1: HZ_OFFSET = HZ_ADD_KT32 ; break;
case 2: HZ_OFFSET = HZ_ADD_HT32 ; break;
case 3: HZ_OFFSET = HZ_ADD_YH32 ; break;
case 4: HZ_OFFSET = HZ_ADD_XK32 ; break;
case 5: HZ_OFFSET = HZ_ADD_CY32 ; break;
case 6: HZ_OFFSET = WEB_ADD_32 ; break;
case 7: HZ_OFFSET = WEB_DINGS_32 ; break;
case 8: HZ_OFFSET = WING_DINGS_32; break;
case 9: HZ_OFFSET = WING_DINGS2_32; break;
default: break;
}
break;
case 40: EN_BUF = EN_40BUF,HZ_BUF=HZ_40BUF, EN_OFFSET = EN_ADD_ST40;
switch (FONT)
{
case 0: HZ_OFFSET = HZ_ADD_ST40 ; break; //宋体40×40字库未安装不能使用
case 1: HZ_OFFSET = HZ_ADD_KT40 ; break; //楷体40×40字库未安装不能使用
case 2: HZ_OFFSET = HZ_ADD_HT40 ; break; //黑体40×40字库未安装不能使用
case 3: HZ_OFFSET = HZ_ADD_YH40 ; break; //雅黑40×40字库未安装不能使用
case 4: HZ_OFFSET = HZ_ADD_XK40 ; break; //行楷40×40字库未安装不能使用
case 5: HZ_OFFSET = HZ_ADD_CY40 ; break; //彩云40×40字库未安装不能使用
case 6: HZ_OFFSET = WEB_ADD_40 ; break;
case 7: HZ_OFFSET = WEB_DINGS_40; break;
case 8: HZ_OFFSET = WING_DINGS_40; break;
case 9: HZ_OFFSET = WING_DINGS2_40; break;
default: break;
}
break;
default: break;
}
while(*Str)
{
if((*Str<0x80)&&(FONT<CODE)) //判断ASCII码,图标按汉字显示
{
qh=*Str-0X20;;/*取ASCII码(2013-5-29原是offset=qh * EN_BUF,字库地址-1,改成下面,字库地址就不用-1了*/
offset=qh * EN_BUF-1 ;/*计算该ASCII码字库中偏移量*/
SPI_Flash_Read((u8*)buffer,offset+EN_OFFSET,EN_BUF); //从Ascii码偏移地址,读SPI_FLASH到缓存
if (MODE==0) //打印字符颜色2013-6-3增加模式开关
{
for(i=0; i<SIZE; i++)
for(j=0; j<NEXT; j++)
for(k=0;k<BYT;k++)
if((buffer[i*NEXT+j] << k) & 0x80)
LCD_SetPoint(x+k+j*BYT,y+i,CharColor);
}else { //打印字符与背景颜色
for(i=0; i<SIZE; i++)
for(j=0; j<NEXT; j++)
for(k=0;k<BYT;k++)
if((buffer[i*NEXT+j] << k) & 0x80)
{
LCD_SetPoint(x+k+j*BYT,y+i,CharColor);
}else {
LCD_SetPoint(x+k+j*BYT,y+i,bkColor);
}
}
} else { //是汉字
if(FONT<CODE)//是中文字体
{
qh=*(Str)- TH -1 ;/*汉字区位码qh=*(Str)- QH*/
wh=*(Str+1)- WH -1; // wh=*(Str+1)- WH 2013-5-29 原下载的24、32字库偏移问题,并不支持中文符号
offset=((qh * 94+ wh ) * HZ_BUF) - 1 ;/*计算该汉字在字库中偏移量原offset=((qh-1)* 94+(wh-1))* HZ_BUF;*/
/*2013-5-29统一地址在宏定义调整*/
}else { //是图标字体按汉字显示
qh=*Str-0X20;
offset=qh * HZ_BUF - 1;/**/
}
SPI_Flash_Read((u8*)buffer,offset+HZ_OFFSET,HZ_BUF);
if( MODE==0) //只打印字符颜色2013-6-3增加模式开关,方便图标字库套打选择用
{
for(i=0; i<SIZE; i++)
for(j=0; j<H; j++)
for(k=0;k<BYT;k++)
if((buffer[i*H+j] << k) & 0x80)
LCD_SetPoint(x+k+j*BYT,y+i,CharColor);// 汉字显示
}else{ //打印字符和背景颜色
for(i=0; i<SIZE; i++)
for(j=0; j<H; j++)
for(k=0;k<BYT;k++)
if((buffer[i*H+j] << k) & 0x80) {
LCD_SetPoint(x+k+j*BYT,y+i,CharColor);// 汉字显示
}else{
LCD_SetPoint(x+k+j*BYT,y+i,bkColor);//背景颜色
}
}
}break;
}
return 0;
}
五、初试打印界面
手机拍摄,效果不好。
六、程序及附件
|