ASCII字符和中文字符的显示
Posted Heavy sea
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ASCII字符和中文字符的显示相关的知识,希望对你有一定的参考价值。
ASCII字符和中文字符的显示基于Framebuffer进行编程,Framebuffer应用编程可参考这里
一、显示ASCII字符
需要在LCD中显示ASCII字符,即英文字母这些字符,需要先找到字符对应的点阵,再根据Framebuffer的应用编程将字符显示到LCD上即可。
1. ASCII字符点阵文件
Linux内核源码下lib\\fonts\\font_8x16.c有字符对应的点阵这一文件,该点阵为为8X16的点阵,即每一行有八位即一个字节,一共有16行,每个字符占据16个字节。
像素从右边数起,bit0对应第0个像素,bit1对应第1个像素,……,bit7对应第7个像素。某位的值为1时,表示对应的像素要被点亮;值为0时表示对应的像素要熄灭。
// 部分点阵代码
#define FONTDATAMAX 4096
static const unsigned char fontdata_8x16[FONTDATAMAX] = {
/* 0 0x00 '^@' */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
/* 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 */
...........................
.............................
}
2.确定字符地址
若想显示字符A,因为字符A的ASCII值是0x41,那么从fontdata_8x16[0x41*16]开始取其点阵数据。
3.示例代码
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <linux/fb.h>
struct fb_var_screeninfo var;
unsigned char *fb_base;
#define FONTDATAMAX 4096
static const unsigned char fontdata_8x16[FONTDATAMAX] = {
// 字符对应点阵代码略
// 可在linux源码下lib\\fonts\\font_8x16.c自行寻找
};
// 描点
void put_pixel(int x,int y,unsigned int color)
{
unsigned char *pen_8 = fb_base+y*var.xres*var.bits_per_pixel/8+x*var.bits_per_pixel/8;
unsigned short *pen_16;
unsigned int *pen_32;
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 & 0xff;
color = (red>>3)<<11 | (green>>2)<<5 | (blue>>3);
*pen_16 = color;
break;
case 32:
*pen_32 = color;
break;
}
}
//显示ASCII字符
void show_ascii(int x,int y,unsigned char c)
{
int i,j;
unsigned char byte;
unsigned char *dots = (unsigned char*)&fontdata_8x16[c*16];
for(i=0;i<16;i++){
byte = dots[i];
for(j=0;j<8;j++){
if(byte &(1<<j)){
// 红色
// 设每个点阵的最左上方那个点坐标为(x,y)
put_pixel(x+7-j,y+i,0xFF0000);
}else{
// 黑色
put_pixel(x+7-j,y+i,0);
}
}
}
}
// 显示字符串
void show_str(int x,int y,char *str)
{
int i;
int tmp = x;
for(i=0;str[i]!='\\0';i++){
show_ascii(x,y,str[i]);
// 每5个自动换行
if((i+1)%5==0){
y = y+16;
x = tmp;
}else{
x = x+8;
}
}
}
int main(int argc, char * * argv)
{
char *str = "Heavysea";
int fd_fb = open("/dev/fb0",O_RDWR);
if(fd_fb==-1){
printf("open failed\\n");
}
if(ioctl(fd_fb,FBIOGET_VSCREENINFO,&var)==-1){
printf("can not get var\\n");
}
unsigned int screen_size = var.xres*var.yres*var.bits_per_pixel/8;
fb_base = (unsigned char*)mmap(NULL,screen_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd_fb,0);
if(fb_base==(unsigned char*)-1){
printf("error\\n");
}
memset(fb_base,0,screen_size);
// show_ascii(var.xres/2,var.yres/2,'H');
show_str(var.xres/2, var.yres/2, str);
munmap(fb_base,screen_size);
close(fd_fb);
return 0;
}
二、显示中文字符
1.中文点阵字库
显示中文字符,同样需要找到对应的中文点阵字库,这里使用的是HZK16这一文件,每个汉字使用32字节来描述。
每个字节中每一位用来表示一个像素,位值等于1时表示对应像素被点亮,位值等于0时表示对应像素被熄灭。
2.确定字符地址
mmap映射字库文件,将mmap的返回结果作为字库的基地址。
以“中”字为例,它的编码值是“0xd6 0xd0”,其中的0xd6表示“区码”,表示在哪一个区:第“0xd6 - 0xa1”区;其中的0xd0表示“位码”,表示它是这个区里的哪一个字符:第“0xd0 - 0xa1”个。每一个区有94个汉字。区位码从0xa1而不是从0开始,是为了兼容ASCII码。
所以,我们要显示的“中”字,它的GB2312编码是d6d0,它是HZK16里第“(0xd6-0xa1)*94+(0xd0-0xa1)”个字符。每个字符占32个字节,故“中“字的偏移地址为( (0xd6-0xa1)*94+(0xd0-0xa1) )*32
需要注意的是HZK16中是以GB2312编码值来查找点阵的,故在编译时需要注意文件的编码格式。
3.示例代码
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <linux/fb.h>
#include <stdlib.h>
struct fb_var_screeninfo var;
unsigned char *fb_base;
#define FONTDATAMAX 4096
static const unsigned char fontdata_8x16[FONTDATAMAX] = {
....................
.......................
};
struct stat hzk_stat;
unsigned char *hzkmem;
void put_pixel(int x,int y,unsigned int color)
{
unsigned char *pen_8 = fb_base+y*var.xres*var.bits_per_pixel/8+x*var.bits_per_pixel/8;
unsigned short *pen_16;
unsigned int *pen_32;
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 & 0xff;
color = (red>>3)<<11 | (green>>2)<<5 | (blue>>3);
*pen_16 = color;
break;
case 32:
*pen_32 = color;
break;
}
}
void show_ascii(int x,int y,unsigned char c)
{
int i,j;
unsigned char byte;
unsigned char *dots = (unsigned char*)&fontdata_8x16[c*16];
for(i=0;i<16;i++){
byte = dots[i];
for(j=0;j<8;j++){
if(byte &(1<<j)){
// 红色
put_pixel(x+7-j,y+i,0xFF0000);
}else{
put_pixel(x+7-j,y+i,0);
}
}
}
}
// 显示中文字符
void show_chinese(int x,int y,unsigned char *s)
{
// 区码
unsigned int area = s[0] - 0xa1;
// 位码
unsigned int where = s[1] - 0xa1;
// 字符地址 基地址+偏移地址
unsigned char *dots = hzkmem+(area*94+where)*32;
unsigned char byte;
int i,j,m;
for(i=0;i<16;i++){
for(j=0;j<2;j++){
byte = dots[2*i+j];
for(m=0;m<8;m++){
if(byte & (1<<m)){
put_pixel(8*j+7-m+x,y+i,0xFF0000);
}else{
put_pixel(8*j+7-m+x,y+i,0);
}
}
}
}
}
// 显示中英文字符串
void show_chinese_str(int x,int y,unsigned char *s)
{
int i;
int j;
int cnt = 0;
int tmp = x;
char *str = (char*)malloc(sizeof(3));
for(i=0;s[i]!='\\0';i++){
if(s[i]<0xa1){
show_ascii(x,y,s[i]);
x += 8;
cnt++;
}else{
memset(str,'\\0',3);
str[0] = s[i];
str[1] = s[i+1];
i++;
show_chinese(x,y,str);
x += 16;
cnt++;
}
// 换行
if(cnt%10==0){
y = y+16;
x = tmp;
}
}
}
int main(int argc, char * * argv)
{
char str[] = "中";
int fd_fb = open("/dev/fb0",O_RDWR);
if(fd_fb==-1){
printf("open failed\\n");
}
if(ioctl(fd_fb,FBIOGET_VSCREENINFO,&var)==-1){
printf("can not get var\\n");
}
unsigned int screen_size = var.xres*var.yres*var.bits_per_pixel/8;
fb_base = (unsigned char*)mmap(NULL,screen_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd_fb,0);
if(fb_base==(unsigned char*)-1){
printf("error\\n");
}
int fd_hzk16 = open("HZK16",O_RDONLY);
if(fd_hzk16==-1){
printf("open hzk16 failed\\n");
}
// 获得文件的状态信息,里面含有文件长度
if(fstat(fd_hzk16,&hzk_stat)){
printf("can not get fstat\\n");
}
// 使用mmap映射文件,以后就可以像访问内存一样读取文件内容
// mmap的返回结果保存在hzkmem中,它将作为字库的基地址
hzkmem = (unsigned char*)mmap(NULL,hzk_stat.st_size,PROT_READ,MAP_SHARED,fd_hzk16,0);
if(hzkmem==(unsigned char*)-1){
printf("hzkmem error\\n");
}
memset(fb_base,0,screen_size);
// show_ascii(var.xres/2,var.yres/2,'A');
// show_chinese(var.xres/2+8, var.yres/2, str);
show_chinese_str(var.xres/2,var.yres/2,"庐州月光 梨花雨凉 如今的你又在谁的身旁 by VAE");
munmap(fb_base,screen_size);
close(fd_fb);
return 0;
}
编译程序
运行平台:100ask-imx6ull开发板
因为这里程序编码是UTF-8,而HZK16编码是GB2312,故需要在编译时将UTF-8格式的中文转换为GB2312。
arm-linux-gnueabihf-gcc -o chinese chinese.c -finput-charset=UTF-8 -fexec-charset=GB2312
如果不指定“-finput-charset”,GCC就会默认C程序的编码方式为UTF-8,即使你是以ANSI格式保存,也会被当作UTF-8来对待。
如果不指定“-fexec-charset”,GCC就会默认编译出的可执行程序中字符的编码方式为UTF-8。
以上是关于ASCII字符和中文字符的显示的主要内容,如果未能解决你的问题,请参考以下文章