嵌入式Linux项目实践——数码相框1

1 框架

(1)需求分析
  1)上电,LCD显示界面
  2)根据配置文件,停留在当前界面,或者自动播放下一幅
  3)点击,出现对话框,选择手动/自动播放
  4)滑动:上——放大,下——缩小,左——上一幅,右——下一幅
  5)左右移动速度较快,显示下下一幅

在这里插入图片描述
(2)包含进程
  1)输入进程:
​  主控线程:得到事件,socket。
​  ts线程:使用tslib读触摸屏,封装上报。
​  按键线程:获取按键值。
  2)显示进程
  socket线程:上报事件
  提前准备好的图片:上一幅、下一幅等。
(3)驱动:触摸屏、LCD,DMA,mmap

2 显示文字

2.1 文字编码方式

  根据存储的数字找到对应的字符,并以相应的字体表示出来,即关注:数字–>字符–>字体。
(1)字符表示方法
1)ASCII
  一个字节,只包含英文等少量字符。
2)GB2312
  2个字节,与ASCII码兼容,一个小于127的字符意义与原来的相同,但是两个大于127的字符连在一起时,就表示一个汉字。
  将字符进行分区处理,共有94个区,每个区有94个位。第一个字节代表区,第二个字节代表位。
  以GB2312字符集的第一个汉字“啊”字为例,它的区号16,位号01,则区位码是1601,在大多数计算机程序中,高字节和低字节分别加0xA0得到程序的汉字处理编码0xB0A1。计算公式是:0xB0=0xA0+16,0xA1=0xA0+1。
3)BIG5
  繁体字使用。
4)Unicode
  将世界上所有的符号都纳入其中。
  Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。常用UTF-8、UTF-16LE、UTF-16BE等表示。
  由于存储的编码不同,所以显示的结果不同。编译时指定字符集:

-finput-charset=charset
-fexec-charset=charset
//例:gcc -finput-charset=GBK -fexec-charset=UTF-8 -o utf ansi.c

2.2 英文字母显示

2.2.1 概述

(1)点阵字符
  英文字符点阵:在内核中,有font_8x16.c等文件描述了ASCII码字库的点阵。
  汉字点阵:使用HZK16来表示汉字,HZK16是按分区表排列的点阵文件。对于一个汉字的位置,需要计算。
  区码:区号(汉字的第一个字节)-0xa0 (因为汉字编码是从0xa0区开始的,所以文件最前面就是从0xa0区开始,要算出相对区码)
  位码:位号(汉字的第二个字节)-0xa0
  这样我们就可以得到汉字在HZK16中的绝对偏移位置:
  offset=(94*(区码-1)+(位码-1))*32
(2)fbmem
  mamp()函数:申请一段用户空间的内存区域,并映射到内核空间某个内存区域。
  使用mamp申请一块内存映射到fb0文件,然后应用程序直接向内存写数据,即可直接写入fb0文件(显存地址)。

2.2.2 程序

  功能:在LCD中间显示字符“A中”。

#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] = {
	//内容太多,与内核中fontdata_8x16中相同
};

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

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



/* color : 0x00RRGGBB */
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;
		}
	}
}

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++)
	{
		byte = dots[i];
		for (b = 7; b >= 0; b--)
		{
			if (byte & (1<<b))
			{
				/* show */
				lcd_put_pixel(x+7-b, y+i, 0xffffff); /* 白 */
			}
			else
			{
				/* hide */
				lcd_put_pixel(x+7-b, y+i, 0); /* 黑 */
			}
		}
	}
}

void lcd_put_chinese(int x, int y, unsigned char *str)
{
	unsigned int area  = str[0] - 0xA1;
	unsigned int where = str[1] - 0xA1;
	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++)
		{
			byte = dots[i*2 + j];
			for (b = 7; b >=0; b--)
			{
				if (byte & (1<<b))
				{
					/* show */
					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;
	}

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

	/* 清屏: 全部设为黑色 */
	memset(fbmem, 0, screen_size);

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

	return 0;	
}

2.3 freetype理论

2.3.1 介绍

(1)矢量字体
  问题:上面的测试程序,字体不能缩放。
  使用矢量字体。矢量字体,记录关键点,例如
在这里插入图片描述
矢量字体:
  1)存若干条闭合曲线的关键点。
  2)使用数学曲线(贝塞尔曲线等方式)连接关键点。
  3)填充内部空间。
(2)文字显示过程
  1)给定文字,对于不同的编码,确定编码值
  2)根据编码值,从字体文件中找到glyph
  3)设置字体大小
  4)用某些函数把glyph里的点缩放为字体大小
  5)转换为位图点阵
​   6)显示出来
(3)Freetype
  文字的存储由两部分组成,一部分是汉字的索引信息,一部分是汉字的字形(glyph)数据。查看freetype文档上述文字显示过程,对应程序:
   1)初始化,FT_Init_Freetype
  2)加载字体Face,从文件/内存,FT_New_Face
  3)设置字体大小,FT_Set_Char_size、FT_Set_Pixel_Sizes
  4)根据编码值,加载glyph
    a.选择charmap:FT_Select_charmap
    b.找到索引:glyph_index = FT_Get_char_Index
    c.取出:FT_Load_Glyph
    a\b\c等同FT_Load_Char
    转成位图:FT_Render_Glyph
  5)变换,移动、旋转……,

2.3.2 在PC上运行

  在PC上安装

tar xjf freetype-2.4.10.tar.bz2 //解压
./config
make
sudo make install

  找到freetype-doc-2.4.10.tar.bz2/freetype-2.4.10/docs/tutorial/example1.c。
  修改example1.c显示大小。

#define WIDTH   80
#define HEIGHT  80

  将C:/Windows/Fonts下的simsun.ttc(宋体)字体文件拷到虚拟机里,运行

gcc -o test example1.c -I/usr/local/include/freetype2 -lfreetype -lm
./test ./simsun.ttc abc

(1)问题1:中文+英文
  上述程序问题:若是中文+英文,出错。
  原因:中文和英文字符占用内存不同,字符串要为wchar_t类型。
(2)问题2:显示的中文和英文的对齐。
  freetype给出的每个字符的显示示意图。
在这里插入图片描述

  参考: /freetype-2.4.10/docs/reference/ft2-index.html,获取字符的坐标。

FT_Glyph_Get_CBox( FT_Glyph  glyph,                 //该值通过FT_Get_Glyph()来获取
                     FT_UInt   bbox_mode,        //模式,填入FT_GLYPH_BBOX_TRUNCATE即可
                     FT_BBox  *acbox );        //用来存放获取到的xMin, xMax, yMin, yMax信息
2.3.3 在LCD上运行

(1)安装freetype

#交叉编译:
tar xjf freetype-2.4.10.tar.bz2 
./configure --host=arm-linux
make
make DESTDIR=$PWD/tmp install

#编译出来的头文件应该放入:
/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include

#编译出来的库文件应该放入:
/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib

#把tmp/usr/local/lib/*  复制到 /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib
sudo cp * /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib -d -rf
#nfs根文件系统下放入so链接文件
cp *so* /work/nfs_root/fs_mini_mdev_new/lib -d


#把tmp/usr/local/include/*  复制到 /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include
cp * /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include -rf
cd /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include
mv freetype2/freetype .

(2)在开发板上运行
  在上次代码上修改。
  得到的点阵在LCD上要做转换,因为LCD的坐标是:
在这里插入图片描述


void
draw_bitmap( FT_Bitmap*  bitmap,
             FT_Int      x,
             FT_Int      y)
{
  FT_Int  i, j, p, q;
  FT_Int  x_max = x + bitmap->width;
  FT_Int  y_max = y + bitmap->rows;

	//printf("x = %d, y = %d\n", x, y);

  for ( i = x, p = 0; i < x_max; i++, p++ )
  {
    for ( j = y, q = 0; j < y_max; j++, q++ )
    {
      if ( i < 0      || j < 0       ||
           i >= var.xres || j >= var.yres )
        continue;

      //image[j][i] |= bitmap->buffer[q * bitmap->width + p];
      lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]);
    }
  }
}


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

	FT_Library	  library;
	FT_Face 	  face;
	int error;
    FT_Vector     pen;
	FT_GlyphSlot  slot;

	if (argc != 2)
	{
		printf("Usage : %s <font_file>\n", argv[0]);
		return -1;
	}
		

	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 * var.bits_per_pixel / 8;
	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;
	}

	/* 清屏: 全部设为黑色 */
	memset(fbmem, 0, screen_size);

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


	/* 显示矢量字体 */
	error = FT_Init_FreeType( &library );			   /* initialize library */
	/* error handling omitted */
	
	error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
	/* error handling omitted */	
	slot = face->glyph;

	FT_Set_Pixel_Sizes(face, 24, 0);

	/* 确定座标:
	 * lcd_x = var.xres/2 + 8 + 16
	 * lcd_y = var.yres/2 + 16
	 * 笛卡尔座标系:
	 * x = lcd_x = var.xres/2 + 8 + 16
	 * y = var.yres - lcd_y = var.yres/2 - 16
	 */
	pen.x = (var.xres/2 + 8 + 16) * 64;
	pen.y = (var.yres/2 - 16) * 64;

    /* set transformation */
    FT_Set_Transform( face, 0, &pen);

    /* load glyph image into the slot (erase previous one) */
    error = FT_Load_Char( face, chinese_str[0], FT_LOAD_RENDER );
	if (error)
	{
		printf("FT_Load_Char error\n");
		return -1;
	}
	
    draw_bitmap( &slot->bitmap,
                 slot->bitmap_left,
                 var.yres - slot->bitmap_top);

	return 0;	
}

  编译运行

arm-linux-gcc -finput-charset=GBK -o example1 example1.c  -lfreetype -lm
./example1 ./simsun.ttc

(3) 显示多行文字
  需要考虑显示的位置,防止重叠。
  1)从左显示:先描画,算出边框
  2)居中显示:算出需要显示内容的边框,再描画
  通过FT_Glyph_Get_CBox获取位置。

typedef struct  FT_BBox_
{
    FT_Pos xMin, yMin;
    FT_Pos xMax, yMax;
} FT_BBox;

(待续…)

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值