当前位置:   article > 正文

嵌入式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
  • 1
  • 2
  • 3

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189

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
  • 1
  • 2
  • 3
  • 4

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

#define WIDTH   80
#define HEIGHT  80
  • 1
  • 2

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

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

(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信息
  • 1
  • 2
  • 3
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 .
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

(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;	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138

  编译运行

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

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

typedef struct  FT_BBox_
{
    FT_Pos xMin, yMin;
    FT_Pos xMax, yMax;
} FT_BBox;
  • 1
  • 2
  • 3
  • 4
  • 5

(待续…)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/263353
推荐阅读
相关标签
  

闽ICP备14008679号