R16开发板tina系统LCD调试

http://blog.csdn.net/u013686019/article/details/78934023

一、环境准备

1、开发板简介

一款名为PARROT的板子,长相如下:


LCD的基本参数:1280*800,LVDS接口,具体参数参见:

tina/target/allwinner/astar-parrot/configs/sys_config.fex


2、Tina SDK V2.1代码下载

$ curl https://raw.githubusercontent.com/tinalinux/repo/stable/repo > ~/bin/repo
$ chmod +x ~/bin/repo
$ export PATH=$PATH:~/bin/
$ mkdir tina && cd tina
$ repo init -u https://github.com/tinalinux/manifest -b r16-v2.1.y -m r16/v2.1.y.xml
$ repo sync

3、编译环境

主机:

$ uname -ra
Linux 4.10.0-42-generic #46~16.04.1-Ubuntu SMP Mon Dec 4 15:57:59 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

make版本:

$ make -v
GNU Make 4.1
make downgrading:  make_3.81-8.2_amd64.deb
gcc版本:

$ gcc -v
gcc version 4.8.5
注:gcc版本太高出现“error: environment variable SOURCE_DATE_EPOCH must expand to a non-negative integer less than or equal to 253402300799”的错误

4、编译

准备环境变量:

$ . build/envsetup.sh
$ lunch astar_parrot-tina

生成kernel的config文件(否则编译出错):

$ make kernel_menuconfig
直接"Exit"即可,在./lichee/linux-3.4/目录下生成.config文件


编译、打包:

$ make -jN
$ pack -d


编译uboot(可选):

$ ./build.sh -p sun8iw5p1

编译uboot过程中提示缺少libcrypto.a\libssl.a,解决方式:

$ cd lichee/brandy/extern-libs/
$ tar -jxvf openssl-secure-system.tar.bz2
$ cd openssl-1.0.1g-for-secure-system/openssl-1.0.1g
$ vi Makefile

修改Makefile中目标“build_libs”下的路径信息至正确的路径下,如SDK编译环境下的“~/tina/lichee/brandy/gcc-linaro/lib/gcc/arm-linux-gnueabi/4.6.3/”目录:


之后编译该库文件:

$make build_libs –j8

编译成功后,重新编译u-boot可以通过,并生成u-boot-sun8iw5p1.bin文件。


二、LCD测试

系统启动过程中uboot logo:


系统启动完成进入Linux系统,通过操作Framebuffer读写LCD数据。

Tina自带Framebuffer测试代码位于:package/utils/fbtest/

以下测试代码参考自“基于嵌入式Linux的LCD液晶点阵显示的基本实现”,未做优化。

1、测试demo主函数

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <fcntl.h>

#include <linux/fb.h>
#include "font_8x16.h" // 该文件来自Linux的drivers/video/console/font_8x16.c文件

#define SCREEN_WIDTH	(1280)
#define SCREEN_HEIGHT	(800)
#define ASCII_WIDTH		(8) /* font 8x16 */
#define ASCII_HEIGHT	(16) /* font 8x16 */

// B G R A
#define WHITE (0xffffffff)
#define RED   (0x0000ff00)
#define GREEN (0x00ff0000)
#define BLUE  (0xff000000)
#define BLACK (0x00000000)

#define FBDEV "/dev/fb0"

int main(int argc, char **argv)
{
	int ret;
	ret = fb_init();
	assert(ret == 0);

	ret = font_init();
	assert(ret == 0);

	clear_screen(WHITE);
	sleep(1);
	disp_char(100, 50, '9'); // 1、测试单个字符显示
	disp_string(100, 100, "abcdefghijklmnopqrstuvwxyz1234567890"); // 2、测试字符串
	disp_string(1000, 200, "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"); // 3、测试换行

	sleep(1); // 在编辑中文时,一定要注意编辑器使用的字符集
	disp_single_hzk16(100, 300, "我");	// 4、测试单个汉字,"我", 在HZK16字库中偏移:136928
	disp_hzk16(100, 400, "拦路雨偏似雪花,富士山"); // 5、测试汉字字串
	disp_mix(100, 500, "Eason: 拦路雨偏似雪花,富士山"); // 6、测试中英混合
	
	disp_bmp(1000, 600, BMP_PATH); // 7、测试图片
	deinit();
	return 0;
}
显示:



2、全局变量

int fd_fb; // FB设备的fd
struct fb_var_screeninfo var; // 定义LCD的可变参数
struct fb_fix_screeninfo fix; // 定义LCD的固定参数
int screen_size; // 表示整个屏幕所占显存的大小
int line_width; // 表示屏幕每一行所占显存的大小
int pixel_width; // 表示每个像素点所占显存的大小
char *fbmem; // 表示显存的起始地址

int fd_hzk16; // HZK16汉字库的文件句柄
struct stat hzk16_stat; // 描述HZK16这个文件的状态信息
char *hzk16mem; // HZK16这个汉字库映射到内存的起始地址

3、framebuffer初始化
static int fb_init()
{
	int ret = -1;
	fd_fb = open(FBDEV, O_RDWR);
	if (fd_fb < 0) {
		return -1;
	}

	ret = ioctl(fd_fb, FBIOGET_VSCREENINFO, &var);
	if(ret == -1) {
		printf("can't ioctl for /dev/fb0!\n");
		return -1;
	}

	ret = ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix);
	if(ret == -1) {
		printf("can't ioctl for /dev/fb0!\n");
		return -1;
	}

	/* 获取液晶显存,每一行显存, */
	pixel_width = var.bits_per_pixel / 8; // 每一个像素显存的大小,bytes
	screen_size = var.xres * var.yres * pixel_width; // 整个LCD显存的大小,bytes
	line_width  = var.xres * pixel_width; // 每一行显存的大小,bytes
	printf("LCD res: %d x %d\n", var.xres, var.yres);
	printf("    pixel_width: %d\n", pixel_width);
	printf("    screen_size: %d\n", screen_size);
	printf("    line_width: %d\n", line_width);

	/* 将LCD显存映射到用户空间 */
	fbmem = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
	if(fbmem == (char *)-1)	{
		printf("mmap for /dev/fb0 error!\n");
		return -1;
	}

	return 0;
}

4、HZK16字库初始化
static int font_init()
{
	int ret = -1;
	fd_hzk16 = open("/usr/sbin/HZK16", O_RDONLY);
	if (fd_hzk16 < 0) return -1;

	ret = fstat(fd_hzk16, &hzk16_stat);
	if (ret < 0) return -1;

	hzk16mem = mmap(NULL, hzk16_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0);
	if(hzk16mem == (char *)-1) {
		printf("mmap for HZK16 error!\n");
		return -1;
	}

	return 0;
}

5、清屏
static void clear_screen(int color)
{
	memset(fbmem, color, screen_size);
}

6、资源回收
static void deinit()
{
	munmap(fbmem, screen_size);
	close(fd_fb);

	munmap(hzk16mem, hzk16_stat.st_size);
	close(fd_hzk16);
}

7、显示单个像素
static void disp_pixel(int x, int y, int color)
{
	__u8 *pen8 = fbmem + y * line_width + x * pixel_width;
	//__u16 *pen16 = (__u16 *) pen8;
	__u32 *pen32 = (__u32 *) pen8;

	switch (var.bits_per_pixel) {
	case 32:
		*pen32 = color;
		break;
	default:
		printf("Unsupported bits_per_pixel!\n");
		printf("bits_per_pixel must be 32 bits!\n");
		break;
	}
}

8、字符显示

字符fontdata_8x16[]数组来自Linux的drivers/video/console/font_8x16.c文件

static void disp_char(int x, int y, char c)
{
	unsigned char *buffer = (unsigned char *)&fontdata_8x16[c * 16];
	unsigned char data;
	int i, j;

	/* 循环操作将整个字符写入到显存指定位置中,达到在指定位置显示字�?*/
	for(i = 0; i < 16; i++) {
		data = buffer[i];
		for(j = 0; j < 8; j++) {
			if(data & 0x80) {
				disp_pixel(x + j, y + i, WHITE);  /*白色*/
			} else {
				disp_pixel(x + j, y + i, BLACK);  /*黑色*/
			}
			data = data << 1;
		}
	}
}

static void disp_string(int x, int y, const char *str)
{
	assert(str);
	//assert(x < (SCREEN_WIDTH - 8));
	//assert(y < (SCREEN_HEIGHT - 16));
	while (*str) {
		disp_char(x, y, *str);
		str ++;
		x += 8;
		if (x > (SCREEN_WIDTH - ASCII_WIDTH)) {
			x = 0;
			y += ASCII_HEIGHT;
		}

		if (y > (SCREEN_HEIGHT - ASCII_HEIGHT)) {
			y = 0;
		}
	}
}

9、汉字显示

字库下载地址:http://pan.baidu.com/share/link?shareid=2514580636&uk=320828865

:在编辑中文时,一定要注意编辑器使用的字符集!如果和字库的不一致,将一直是乱码。

经测,使用VS2008编辑的中文可以正常显示。

static void disp_single_hzk16(int x, int y, char *str)
{
	int area = str[0] - 0xa1 ;
	int where = str[1] - 0xa1;
	int offset = (area * 94 + where) * 32;
	__u8 *buffer = (__u8 *) (hzk16mem + offset);
	__u16 data;
	int i, j;

	for (i = 0; i < 16; i ++) {
		data = (buffer[i * 2] << 8) | buffer[2 * i + 1];
		for (j = 0; j < 16; j ++) {
			if (data & 0x8000)
				disp_pixel(x + j, y + i, WHITE); /*白色*/
			else
				disp_pixel(x + j, y + i, BLACK);  /*黑色*/
			data <<= 1;
		}
	}
}

static void disp_hzk16(int x, int y, char *str)
{
	assert(str);
	//assert(x < (SCREEN_WIDTH - 16));
	//assert(y < (SCREEN_HEIGHT - 16));
	while (*str) {
		disp_single_hzk16(x, y, str);
		str += 2;
		x += 16;
		if (x > (SCREEN_WIDTH - 16)) {
			x = 0;
			y += 16;
		}

		if (y > (SCREEN_HEIGHT - 16)) {
			y = 0;
		}
	}
}

static void disp_mix(int x, int y, char *str)
{
	assert(str);
	while (*str) {
		if (*str & 0x80) { // chinese
			disp_single_hzk16(x, y, str);
			str += 2;
			x += 16;
			if (x > (SCREEN_WIDTH - 16)) {
				x = 0;
				y += 16;
			}
		} else {
			disp_char(x, y, *str);
			str += 1;
			x += 8;
			if (x > (SCREEN_WIDTH - ASCII_WIDTH)) {
				x = 0;
				y += ASCII_HEIGHT;
			}
		}

		if (y > (SCREEN_HEIGHT - ASCII_HEIGHT)) {
			y = 0;
		}
	}
}


10、汉字显示原理

HZK16汉字16*16点阵字库的使用及示例程序

以上原文。

HZK16字库是符合GB2312标准的16×16点阵字库,HZK16的GB2312-80支持的汉字有6763个,符号682个。其中一级汉字有 3755个,按声序排列,二级汉字有3008个,按偏旁部首排列。

我们在一些应用场合根本用不到这么多汉字字模,所以在应用时就可以只提取部分字体作为己用。

HZK16字库里的16×16汉字一共需要256个点来显示,也就是说需要32个字节才能达到显示一个普通汉字的目的。

我们知道一个GB2312汉字是由两个字节编码的,范围为0xA1A1~0xFEFEA1-A9为符号区,B0-F7为汉字区。每一个区有94个字符(注意:这只是编码的许可范围,不一定都有字型对应,比如符号区就有很多编码空白区域)。

下面以汉字"我"为例,介绍如何在HZK16文件中找到它对应的32个字节的字模数据。前面说到一个汉字占两个字节,这两个中前一个字节为该汉字的区号,后一个字节为该字的位号。其中,每个区记录94个汉字,位号为该字在该区中的位置。所以要找到"我"在hzk16库中的位置就必须得到它的区码和位码。

区码:汉字的第一个字节-0xA0 (因为汉字编码是从0xA0区开始的, 所以文件最前面就是从0xA0区开始, 要算出相对区码)
位码:汉字的第二个字节-0xA0

这样我们就可以得到汉字在HZK16中的绝对偏移位置:

offset=(94*(区码-1)+(位码-1))*32

注解:

  • 区码减1是因为数组是以0为开始而区号位号是以1为开始的
  • (94*(区号-1)+位号-1)是一个汉字字模占用的字节数
  • 最后乘以32是因为汉字库文应从该位置起的32字节信息记录该字的字模信息(前面提到一个汉字要有32个字节显示)

我画的图示:


11、显示图片

#define BMP_PATH "/usr/sbin/ram.bmp"
#define BMP_WIDTH (128)
#define BMP_HEIGHT (128)
#define BMP_INDEX (BMP_HEIGHT-1)
#define BMP_SIZE (BMP_WIDTH*BMP_HEIGHT*3+54) // bmp header 54 bytes
int disp_bmp(int x, int y, const char *path)
{
	int sx, sy, location;

	const int bytes_per_pixel = pixel_width;
	const int screensize = screen_size;
	char *map_mem = fbmem;
	const int start_location = x * bytes_per_pixel + y * line_width;

	FILE *file_fd = fopen(path, "r");
	char buffer[BMP_SIZE];
	int readCnt = fread(buffer, sizeof(buffer), 1, file_fd);
	fclose(file_fd);

	for(sx = 0; sx < BMP_WIDTH; sx ++) {
		for(sy = 0; sy < BMP_HEIGHT; sy++) {
			location = start_location + sx * bytes_per_pixel + sy * line_width;

			// 矫正BMP显示和LCD显示的上下反 BMP_INDEX-y
			*(map_mem + location + 0) = buffer[54+(BMP_WIDTH*(BMP_INDEX-sy)+sx)*3+0];// 蓝色的色深
			*(map_mem + location + 1) = buffer[54+(BMP_WIDTH*(BMP_INDEX-sy)+sx)*3+1];// 绿色的色深
			*(map_mem + location + 2) = buffer[54+(BMP_WIDTH*(BMP_INDEX-sy)+sx)*3+2];// 红色的色深
			//*(map_mem + location + 3) = 0;// 是否透明
		}
	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/u013686019/article/details/78934023