[电子书]项目储备一: 字符编码与LCD显示单个字母/汉字

  • 1 字符编码
  • 2 LCD显示单个字母/汉字
    • 2.1 编程思路
    • 2.2 代码实现
    • 2.3 编译、测试与bug
  • 3 总结与下文预告

1 字符编码

对于字符编码,网上一堆好文章,写得更详细。这里只是提及一些关键点。
字符编码的发展历程概括为:ASCII码--> 各国/地区独立编码(如GB2312/GBK、BIG5等)--->统一编码:unicode码在面对同一字符可有不同编码,或者同一编码值可解码为不同字符,为统一编码,
出现了unicode码,值得注意的是,unicode码只是给出了每个字符对应的编码值,并没有明确
每个字符的存储字长、存储方式(大端/小端)。因此出现了utf-8、utf-16等“表示”unicode码,换句话说
utf-8/16/32等这些指定了存储unicode码的字长、存储方式。
普遍使用utf-8, 它是变长存储的(1-4字节),如何实现变长存储参考下文的一个bug的解释。而GB2312/GBK是根据区码+位码索引某个字的,下面的“编程思路”有讲。对于字符编码,了解这些就够了,起码对于下文的理解是没问题的。
值得注意的是在window下用记事本/notepad++打开/编辑源码文件,尤其注意文件编码。
出现乱码时。可查看编码值再看看字符编码是什么,就能锁定问题了。另外,gcc/arm-linux-gcc 有两个实用选项:
-finput_charset : 指定输入源文件的字符编码,默认UTF-8
-fexec_charset: 指定输出elf文件的字符编码, 默认UTF-8
使用这两个选项可把不同编码的源文件编译成指定编码的可执行程序下面看看如何实现在LCD显示单个字母和汉字

2 LCD显示单个字母/汉字

硬件:s3c2440A开发板,4.3寸LCD,型号:AT043TN24
软件:使用交叉编译工具链:arm-linux-gcc (3.4.5版本),linux2.6.22.6内核版本
目标:在LCD中央显示“A中”
2.1 编程思路
1)对于显示单个字母,从2.6.22.6内核版本中获得 font_8x16.c文件,当然也可使用其它大小的点阵(如8x8 16x16,内核都有)怎么显示单个字母呢?很简单,font_8x16用数组保存字母的编码值,根据索引获得编码值后,再逐个像素显示即可。具体来讲,需先获得LCD的硬件参数(如bpp、分辨率、framebuffer基址),然后根据这些参数计算出某点像素的坐标,再填充颜色2)对于显示单个汉字,需从HZK16这个字体文件获取,HZK16文件已上传到我的代码集合,有需要下载即可。HZK16字库是符合GB2312标准的16×16点阵字库,HZK16的GB2312-80支持的汉字有6763个,符号682个问题一:如何获取某个字(字模)呢?某个字的索引 = (区码, 位码)。什么是区码、位码呢?具体如何索引呢?举个例子“中国”的“中”字,在GBK编码中,编码值为 D6 D0,区码就是D6, 位码就是D0 "中"个字模的索引/偏移量按如下计算(以16x16为例)offset =【(区码-0xa1)×0x5e + (位码-0xa1)】×0x20 (0x20=32,即取2个字节, 16x16)即 offset = ( (0xD6 - 0xA1) x 0x5e + (0xD0- 0xA1)) x 0x20 = =0x274A0 = 160928问题二:为啥区码和位码要减去0xA1呢?因为汉字编码是从0xa0区开始的,所以文件最前面就是从0xa0区开始,要算出相对区码问题三:有了字模的偏移量,如何显示呢?答:与显示单个字母一样,逐个像素显示即可,只是字模是16x16而已。GBK2312简体中文编码:https://wenku.baidu.com/view/0ef57bfb04a1b0717fd5dd1a.html更详细关于HZK16的使用可参考:https://wenku.baidu.com/view/0774d20c52ea551810a68768.html
2.2 代码实现

font_8x16.c

#define FONTDATAMAX 4096const unsigned char fontdata_8x16[FONTDATAMAX] = {/* 0 0x00 '^@' *//* 1 0x01 '^A' */0x00, /* 00000000 */0x00, /* 00000000 */0x7e, /* 01111110 */0x81, /* 10000001 */0xa5, /* 10100101 */0x81, /* 10000001 */0x81, /* 10000001 */0xbd, /* 10111101 */0x99, /* 10011001 */0x81, /* 10000001 */0x81, /* 10000001 */0x7e, /* 01111110 */0x00, /* 00000000 */0x00, /* 00000000 */0x00, /* 00000000 */0x00, /* 00000000 *//* ... 其它省略 */
}

备注:font_8x16.c文件太大,限于篇幅,只贴出要显示的’A’,完整文件已上传到我的代码集合

show_font.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>extern const unsigned char fontdata_8x16[];
/* 变量定义 */
int fd_fb, fd_hzk;
static line_width, pixel_width, screen_size;
static unsigned char* fbmem, *hzk16mem;
static struct fb_var_screeninfo var;
static struct fb_fix_screeninfo fix;
static struct stat phzk_stat;/* 函数声明(最好放在头文件) */
void lcd_put_pixel(unsigned int x, unsigned y, unsigned int color);
void lcd_put_ascii(unsigned int x, unsigned int y, unsigned char c);
void lcd_put_chinese(unsigned int x, unsigned int y, unsigned char*str);int main(int argc, char** argv)
{char str[] = "жа";if(argc != 2){printf("Usage: \n");printf("%s <字库文件>\n", argv[0]);return 0;}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)){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 * pixel_width;fbmem=(unsigned char*)mmap(NULL, screen_size,PROT_READ | PROT_WRITE, MAP_SHARED,fd_fb, 0);// NULL表示内核会自动确定分配内存的位置,返回映射内存的起始地址if(fbmem == (unsigned char*)-1){printf("can't map fbmem\n");return -1;}fd_hzk = open("HZK16", O_RDONLY);if(fd_hzk < 0){printf("can't open HZK16\n");return -1;}if(fstat(fd_hzk, &phzk_stat)){printf("can't get hzk_stat");}hzk16mem = (unsigned char*)mmap(NULL, phzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk, 0);if(hzk16mem == (unsigned char*)-1){printf("can't map hzk16mem\n");return -1;}memset(fbmem, 0, screen_size); // clear screen lcd_put_ascii(var.xres/2, var.yres/2, 'A');printf("chinese code: %02x %02x\n", str[0], str[1]);lcd_put_chinese(var.xres/2 + 8, var.yres/2, str);// remember to releasemunmap(hzk16mem, phzk_stat.st_size);munmap(fbmem, screen_size);return 0;
}void lcd_put_pixel(unsigned int x, unsigned y, unsigned int color)
{unsigned char* pen_8 = fbmem + y*line_width + x*pixel_width;unsigned short* pen_16 = NULL;unsigned int* pen_32 = NULL;unsigned int red, green, blue;pen_16 = (unsigned short*)pen_8;pen_32 = (unsigned int*)pen_8;switch(var.bits_per_pixel){case 8:{*pen_8 = color;break;}case 16:{red = (color >> 16) & 0xff;green = (color >> 8) & 0xff;blue = (color >> 0) & 0xff;color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);*pen_16 = color;break;}case 32:{*pen_32 = color ;break;}default:{printf("can't surport %dbpp\n",var.bits_per_pixel);return ;}}
}
void lcd_put_ascii(unsigned int x, unsigned int y, unsigned char c)
{const unsigned char *dots = &fontdata_8x16[c*16];unsigned char byte;int i, j;for(i = 0; i < 16; ++i){byte = dots[i];for(j = 7; j >=0; --j){if(byte & (1<<j)){lcd_put_pixel(x + 7 - j, y + i, 0xffffff);}}}
}
void lcd_put_chinese(unsigned int x, unsigned int y, unsigned char*str)
{unsigned int area = str[0] - 0xA1;unsigned int  where = str[1] - 0xA1;unsigned char *offset = hzk16mem + (area*94 + where)*32;unsigned char byte;int i, j, b;for(i = 0; i < 16; ++i){for(j = 0; j < 2; ++j){byte = offset[i*2 + j];for(b = 7; b >= 0; --b){if(byte & (1<<b)){lcd_put_pixel(x+j*8+7- b, y + i, 0xffffff);}}}}
}

上面文件解释一下:
1.随便在内存某个位置映射一块screen_size大小的内存当作framebuffer显存使用
2.映射HZK16文件到内存某一位置,是方便随机读取某一个字,提高读取效率。
3.注意,映射的内存最后记得释放呀

2.3 编译、测试与bug

编译:arm-linux-gcc -o show_font font_8x16.c show_font.c
注意:LCD驱动文件替换为我的part19文章写的LCD驱动文件,修改drivers/video/Makefile,如下

注意:先把 lcd_drv.c复制到drivers/vides/目录下
然后再重新编译make uImage ,并把uImage烧写到0x3000,0000,重启后加载驱动(参考part19)

一个bug:在LCD屏幕中间显示"A涓"
为啥“中”字变为“涓”,看下图

发现字符编码都不对,肯定是源文件编码有问题。
因为我在notpad++编写,发现源文件编码是UTF-8,而“中字”的编码是“E4 B8 AD”,我们使用HZK16
是16位的,也就是两个字节。而UTF-8是变长编码(1-4bytes),表示为三个字节,肯定不行。
顺便讲下UTF-8的编码,解释如下
E4 B8 AD展开来1110 0100 1011 1000 1010 1101, 如下图

解决办法:
在notepad++ 修改字符编码为"ANSI",这样"中"就用两个字节存储。
(ANSI:如果在简体中文windows下,表示“GBK”编码,因此“中”就会用两字节存储)

修改后,正常显示,如下图

其它字也可以,只要不超过HZK16范围

3 总结与下文预告

LCD显示单个字母/汉字的编程分两步1)获得字母(字体文件)/汉字(HZK16文件)2)在LCD逐个像素显示难点在于确定开始描绘的起点,且字母和汉字点阵大小不同。逐个像素显示时需计算像素的位置。这里只是显示单个字母/汉字,下文讲讲解如何显示多行文字和图片

[电子书]项目储备一: 字符编码与LCD显示单个字母/汉字相关推荐

  1. [电子书]项目储备二:用FreeType2绘制矢量字体、多行文字

    [电子书]项目储备二:用FreeType2绘制矢量字体.多行文字 0 矢量字体 VS 点阵字体 1 FreeType2的介绍与基本使用 2 在LCD显示一个矢量字体,且可旋转 3 在LCD任意位置显示 ...

  2. python 之 字符编码 和 中文显示

    关于编码 (首先了解一下ascii.gb2312.gbk.utf-8.unicode的关系 http://www.cnblogs.com/skynet/archive/2011/05/03/20351 ...

  3. Linux电子书项目之freetype实现矢量字体的显示(2)

    上一篇博文我们通过点阵数组实现了字母的显示:http://blog.csdn.net/shenhuan1104/article/details/79476053 现在我们通过freetype这个框架在 ...

  4. vue项目 el-input输入框字符限制,只显示英文及数字

    element的el-input没有限制输入的内容,想要限制输入内容就需要自己来开发,我使用的方式是正则来判断进行再次赋值实现的, 不废话上代码: <el-input v-model=" ...

  5. 关于Unicode,字符集,字符编码

    基本概念 字符[character] 字符代表了字母表中的字符,标点符号和其他的一些符号.在计算机中,文本是由字符组成的. 字符集合[character set] 由一套用于特定用途的字符组成,例如支 ...

  6. python基础之字符编码

    阅读目录 一 了解字符编码的知识储备 二 字符编码介绍 三 字符编码应用之文件编辑器 3.1 文本编辑器之nodpad++ 3.2 文本编辑器之pycharm 3.3 文本编辑器之python解释器 ...

  7. 字符集、字符编码、国际化、本地化简要总结(UNICODE/UTF/ASCII/GB2312/GBK/GB18030)

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 环境说明   普通的linux 和 普通的windows.    ...

  8. 【字符编码】 简洁理解ANSI,UTF8,Unicode,ASCII编码的差别

    目录 简略 说明 1.  ASCII和ANSI编码 2. UNICODE 4.  UTF-8 参考 简略 ASCII             1个字节=1个文字 ANSI                ...

  9. 拼音转汉字 和 字符编码测试

    获得汉字的unicode编码 #define   _UNICODE   #include   <tchar.h>   #include   <locale.h>   #incl ...

最新文章

  1. fastjson又被发现漏洞,这次危害可导致服务瘫痪!
  2. 使用juery在iframe内部访问父页面元素
  3. hexo博客配置阿里云oss对象存储
  4. 批处理启动和关闭VMware
  5. Properties和IO流相结合的方法
  6. Python模块之uuid
  7. [Java][内存模型]
  8. Aspx页面内 成员变量丢失的问题
  9. 两种方式从Mac计算机上的启动板菜单中删除应用程序?
  10. linux下设备树spi的节点查询,已解决: 关于ZYNQ-Linux中设备树SPI节点的疑问 - Community Forums...
  11. 大厂面试为什么总考算法
  12. 计算机网络(第七版)谢希仁编著(转载请注明出处---https://www.cnblogs.com/qingl)...
  13. macbook linux 双显卡,网友支招:苹果笔记本也能双显卡切换
  14. ObjC开发-常用第三方开源框架介绍
  15. 电脑远程开机控制实现 免拆机安装
  16. mysql 获取某个时间段每一天、每一个小时的统计数据
  17. 交换安全----局域网安全简介
  18. EasyUi 快速入门
  19. 计算机桌面体验,平板电脑Aero桌面体验
  20. 值得销售人员看的书籍

热门文章

  1. 绝地求生2月26服务器维护吗,绝地求生2月26日维护更新内容 绝地求生2月26日更新了什么...
  2. 关于数据库的卸载及服务残留问题
  3. 网站运营推广:网站取名与做好定位很关健
  4. 餐厅预订APP有哪些?餐厅预订APP怎么选择?
  5. uefi装完系统后无法引导_uefi u盘无法引导怎么办
  6. 怎样清理苹果电脑磁盘空间_Mac怎么清理磁盘垃圾
  7. 基于docker搭建redis哨兵模式
  8. python web服务器部署
  9. 前端笔试题记录【1】 + JS内置对象复习
  10. win10文件误删除怎么恢复?3种实用方法帮您找回