【项目1_电子书】第2.2课、字符的点阵显示

时间:2022-01-10 16:12:36

主 机:VMWare--Ubuntu-16.04.2-x64-100ask
开发板:Mini2440--256M NandFlash,   2M NorFlash,   64M SDRAM,   LCD-TD35;
    bootlorder:u-boot1.16,      Kernel:2.6.22.6;
编译器:arm-linux-gcc-4.3.2


 

1、汉字显示的函数
/*
* 功能: 画汉字;
* GB2312编码: 二字节str是一个GB2312编码,其高位字节表示区号,低位字节表示位号;
* GB2312编码表中每个区94个字节;
* HZK16是16*16字符集,每个字32字节,其每一行是2字节16位,例如: 0x0480 还是 0x04,0x80?
*/
void lcd_put_chinese(int x, int y, unsigned char *str)
{
unsigned int area = str[0] - 0xA1; //区码从0xA1开始;
unsigned int where = str[1] - 0xA1; //位码从0xA1开始;

/*
* 汉字、GB2312编码号与HZK16字符集的关系:
* 汉字*str在GB2312编码中的编号,被用来作为HZK16的索引,而HZK16中该汉字的16*16点阵,将
* 在LCD指定坐标点亮该汉字;
*/
unsigned char *dots = hzkmem + (area * 94 + where)*32; //hzkmem是内存中,HZK16文件在的地址,也即16x16字符阵列数据的开始地址;
//dots是内存中(HZK16文件所在),汉字*str的16x16点阵的开始地址;
//94是每个区94个字,32是HZK16字符集中每个字32字节;
unsigned char byte;

int i, j, b;
for (i = 0; i < 16; i++)
for (j = 0; j < 2; j++)
{
byte = dots[i*2 + j]; //提取出该汉字在HZK16字符集中的数据,一行一行取,一次只取一字节数据;
for (b = 7; b >=0; b--)
{
if (byte & (1<<b))
{
/* show */
lcd_put_pixel(x+j*8+7-b, y+i, 0xffffff); //白色, //在此处,汉字的16*16点阵数据转换成LCD的需要画点的像素坐标数据!
//慎重计算像素坐标数据!
}
else
{
/* hide */
lcd_put_pixel(x+j*8+7-b, y+i, 0); /* 黑 */
}
}
}
}
方法:以函数参数x, y作原点坐标(x, y)构建解决方法;
执行步骤:
① i=0, j=0, byte=dots[0], 则fb_put_pixel(x+7-b, y, 0xffffff); //b=7~0
② i=0, j=1, byte=dots[1], 则fb_put_pixel(x+8+7-b, y, 0xffffff); //b=7~0
③ i=1, j=0, byte=dots[2], 则fb_put_pixel(x+7-b, y+1, 0xffffff); //b=7~0
④ i=1, j=1, byte=dots[3], 则fb_put_pixel(x+8+7-b, y+1, 0xffffff); //b=7~0
...


|<---16位,2字节-->|+
○○○○○●○○●○○○○○○○ | -> 0x04,0x80
○○○○●●●○●○●○○○○○ | -> 0x0E,0xA0
○●●●●○○○●○○●○○○○ | -> 0x78,0x90
○○○○●○○○●○○●○○○○ | -> 0x08,0x90
○○○○●○○○●○○○○●○○ | -> 0x08,0x84
●●●●●●●●●●●●●●●○ | -> 0xFF,0xFE
○○○○●○○○●○○○○○○○ | -> 0x08,0x80
○○○○●○○○●○○●○○○○ |16位-> 0x08,0x90
○○○○●○●○●○○●○○○○ |2字节-> 0x0A,0x90
○○○○●●○○○●●○○○○○ | -> 0x0C,0x60
○○○●●○○○○●○○○○○○ | -> 0x18,0x40
○●●○●○○○●○●○○○○○ | -> 0x68,0xA0
○○○○●○○●○○●○○○○○ | -> 0x09,0x20
○○○○●○●○○○○●○●○○ | -> 0x0A,0x14
○○●○●○○○○○○●○●○○ | -> 0x28,0x14
○○○●○○○○○○○○●●○○ + -> 0x10,0x0C

所以,'我'在HZK16 16*16点阵字库的存放的序列为:
一行一行地保存,共16行,每行2个字节, 共32个字节
04 80 0E A0 78 90 08 90 08 84 FF FE 08 80 08 90 //8行,16字节;
0A 90 0C 60 18 40 68 A0 09 20 0A 14 28 14 10 0C //8行,16字节;

问题:HZK16 16*16的点阵的每个汉字的数据的内部分布?
答1:HZK16 16*16的点阵数据在HZK16文件所在内存中,将还原成二进制01代码存储,其开始的内存地址是hzkmem!
没有找到HZK16 16*16的数组源码,搞不清楚其内部数据分布,暂且放下,按照视频所述使用!
猜测1:根据理论每个汉字的数据是16个2字节数据,其在内存的保存方式是一行一行地保存,共16行,每行2个字节, 共32个字节。
但是程序中,对每一行数据,是先对bit7~0进行分配,然后是bit15~8,由此可见对LCD画点是一字节一字节画的,先画0x04,然后0x80,
而非是直接0x8004?直接画点。


问题:字符的ASCII码或GB2312码与fontdata_8x16字符点阵或HZK16字符点阵的关系?
答:正如,当英文字符c = 'c'时,ASCII码0x63,字符'c'的8x16点阵的绝对偏移量offset=0x63*16,其点阵数据是以fontdata_8x16[0x63*16]为首的16个8位数据;
当中文字符c = "我"时,GB2312码0XCED2,汉字"我"在HZK16的16x16点阵的绝对偏移位置:offset=(94*(区码-1)+(位码-1))*32=(94*0xcd+0xd1)*32,其点阵数据
是HZK16内存地址 hzkmem + (94*0xcd+0xd1)*32 为首的16个2字节数据;
也即字符或汉字的ASCII码或GB2312码充其量也就是字符点阵的索引值,除此之外,与点阵别无关系。

2、主函数
int main(int argc, char **argv)
{
unsigned char str[] = "中";

/* 1.LCD初始化 *********************************/
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
printf("can't open /dev/fb0\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) //问题:问什么不直接使用ioctl函数获得struct fb_info结构体的信息,然后直接使用
{ //其元素?例如:fix, var等等。可以试试吗?
printf("can't get var\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
{
printf("can't get fix\n");
return -1;
}
line_width = var.xres * var.bits_per_pixel / 8;
pixel_width = var.bits_per_pixel / 8;
screen_size = var.xres * var.yres * var.bits_per_pixel / 8; //可以不要这些东西,直接调用fix.smem_len?答:可以的
fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0); //将一个文件或者其它对象映射进内存;
if (fbmem == (unsigned char *)-1)
{
printf("can't mmap\n");
return -1;
}

/* 2、汉字库初始化 *********************************/
fd_hzk16 = open("HZK16", O_RDONLY);
if (fd_hzk16 < 0)
{
printf("can't open HZK16\n");
return -1;
}
if(fstat(fd_hzk16, &hzk_stat))
{
printf("can't get fstat\n");
return -1;
}
hzkmem = (unsigned char *)mmap(NULL , hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0);
if (hzkmem == (unsigned char *)-1)
{
printf("can't mmap for hzk16\n");
return -1;
}

/*3、清屏,然后使用LCD显示字符和汉字*/
memset(fbmem, 0, screen_size); /*清屏: 全部设为黑色 */
lcd_put_ascii(var.xres/2, var.yres/2, 'A'); //在LCD屏*显示字符'A';

printf("chinese code: %02x %02x\n", str[0], str[1]); //汉字“中”的GB2312数据;
lcd_put_chinese(var.xres/2 + 8, var.yres/2, str); //在LCD屏*显示汉字“中”;

return 0;
}

 


=====快捷键=====:
nfs 32000000 192.168.1.105:/work/nfs_root/uImage_lcd_3th; bootm 32000000
nfs 32000000 192.168.0.101:/work/nfs_root/uImage_lcd_3th; bootm 32000000

 

3、初始化unsigned char *fb_base, hzk16_base; 与unsigned char *fb_base;unsigned char *hzk16_base; 比较?
答:unsigned char *fb_base, hzk16_base;等于unsigned char *fb_base; unsigend char hzk16_base;
unsigned char *fb_base, *hzk16_base;等于unsigned char *fb_base;unsigned char *hzk16_base;