Wednesday, October 7, 2015

Character Bitmap display - 2

2.2 LCD上顯示固定大小中英字體
需先安裝 LCD Driver,接著撰寫應用程式,導入HZK16漢字庫,將要顯示的字元,透過漢字庫,尋找到相應字元,輸出到 LCD 螢幕上。

  • HZK16重點摘要:
  1. HZK16是符合GB23121UL 標準,每個漢字用16*16 BIT来表示,也就是說需要32個字元來顯示一個漢字
  2. 每個漢字由兩個字元編碼,範圍是 A1A1~FEFE,分為區碼和位碼,如圖a:
  3. 圖a所示每區有94個漢字,每區用32字元表示,所以所尋找漢字偏移量是: (94*(區碼-0xA1) +位碼-0xA1)*32
fig. a
實作:LCD上顯示中英字體
=====================================================================
show_font.c

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>


#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 0x02 '^B' */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x7e, /* 01111110 */
0xff, /* 11111111 */
0xdb, /* 11011011 */
0xff, /* 11111111 */
0xff, /* 11111111 */
0xc3, /* 11000011 */
0xe7, /* 11100111 */
0xff, /* 11111111 */
0xff, /* 11111111 */
0x7e, /* 01111110 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */

.................................

/* 255 0xff '' */
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 */

};


int fd_fb;
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;

unsigned char *hzkmem;
int fd_hzk16;
struct stat hzk_stat;


// display pixel
void  lcd_put_pixel(int x, int y, unsigned int color)
{
    unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width;
    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:
            // 565
            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);
            break;
    }
}

// input ascii
void lcd_put_ascii(int x, int y, unsigned char c)
{
    unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];
    int i,b;
    unsigned char byte;

    for(i=0; i<16; i++)     // y axiel
    {
        byte = dots[i];     // 一個 i 表示一個 byte 
        for(b=7; b>=0; b--) // x axiel
        {
            if(byte & (1<<b))
            {
                lcd_put_pixel(x+7-b, y+i, 0xffffff);
            }
            else
            {
                lcd_put_pixel(x+7-b, y+i, 0);
            }
        }
    }
}

// input 漢字 
void lcd_put_chinese(int x, int y, unsigned char *str)
{

    unsigned int  area = str[0] - 0xA1;          // HZK16 區碼第一個為空字元 
    unsigned int  where = str[1] - 0xA1;         // HZK16 位碼第一個為空字元
    unsigned char *dots = hzkmem + (area * 94 + where)*32;
    unsigned char byte;
    int i, j, b;

    
    for(i=0; i<16; i++)
    {
        for(j=0; j<2; j++)                      // y axiel
        {
            byte = dots[i*2+j];                 // 一個 i or j 表示一個 byte 
            for(b=7; b>=0; b--)                 // x axiel
            {
                // show
                if(byte & (1<<b))
                {
                    lcd_put_pixel(x+j*8+7-b, y+i, 0xffffff);
                }
                else    // hide
                {
                    lcd_put_pixel(x+j*8+7-b, y+i, 0);
                }
            }
        }
    }
}
    

int main(int argc, char **argv)
{
    
    unsigned char str[] = "中";

    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;
    }
    
    // 這裡可以獲得LCD的可變參數
    if(ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
    {
        printf("can't get fix\n");
        return -1;
    }

    line_width = var.xres * var.bits_per_pixel / 8;            // Representative of the number of bytes per row
    pixel_width = var.bits_per_pixel / 8;                      // Representative of the number of bytes per pixel
    screen_size = var.xres * var.yres * var.bits_per_pixel / 8;   // Representative of the number of bytes per frame
    // 映射一塊顯存,第一個參數代表映射的位址,NULL表示自由分配 
    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;
    }

    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;
    }

    // clear lcd screen
    memset(fbmem, 0, screen_size);


    lcd_put_ascii(var.xres/2, var.yres/2, 'A');             // display "A"
    printf("chinese code: %02x %02x\n", str[0], str[1]);    // print 0xXX of "中" 
    lcd_put_chinese(var.xres/2 + 8, var.yres/2,str);        // display "中" 

    return 0;
}

===================================================================
測試:
1. 安裝 lcd driver

  1. 配置、修改内核支持把lcd.c编译进去
  2. 把驱动程序lcd.c拷贝到内核的drivers/video/目录下
  3. 修改drivers/video/Makefile
    -----------------------
    #obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o
    obj-$(CONFIG _FB_S3C2410) += lcd.o
    ------------------------
  4. make menuconfig选择上对lcd的配置
    Device Drivers ---> 

    Graphics support ---> 
    <*> Support for frame buffer devices ---> 
    <*> S3C2410 LCD framebuffer support 

  5. make uImage
  6. 新kernel啟動

2. 編譯應用程式
$ arm-linux-gcc -o show_font show_font.c
./show_font

3. 結果
console 顯示如下:

/mnt/nfs # ./show_font
chinese code: d6 d0

JZ2440 開發版顯示如下:



參考資料:
  1. 嵌入式Linux應用開發完全手冊
  2. 韋東山視頻三期
  3. http://wenku.baidu.com/view/170f49d049649b6648d7478e.html (hzk16 介紹)
  4. http://wenku.baidu.com/view/0774d20c52ea551810a68768.html (hzk16範例)

No comments:

Post a Comment