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字符和中文字符的显示的主要内容,如果未能解决你的问题,请参考以下文章

ASCII字符和中文字符的显示

在ASCII码字符编码中,啥字符无法显示或打印出来

在ASCII码字符编码中,啥字符无法显示或打印出来

在ASCII码字符编码中,啥字符无法显示或打印出来

c语言编程中如何显示字符的ASCII码值?

指出ASCII代码表中字符排列规律