当前位置:   article > 正文

【嵌入式Linux】嵌入式项目实战之七步从零编写带GUI的应用之显示系统、输入系统、文字系统_嵌入式linux项目

嵌入式linux项目

前言

韦东山项目实战之七步从零编写带GUI的应用(项目开发|论文参考|C|GUI)学习笔记
文章中大多内容来自韦东山老师的文档,还有部分个人根据自己需求补充的内容

视频教程地址: https://www.bilibili.com/video/BV1it4y1Q75z

1、显示系统

【嵌入式Linux】嵌入式Linux应用开发基础知识之Framebuffer应用编程和字符汉字显示

1.1、程序分层

在这里插入图片描述

▲程序分层

目前还没有按钮和web显示的代码

1.2、几个重要的数据结构

/*
*	显示缓冲区
*	iXres iYres:屏幕的x,y轴长度 
*	iBpp:字符的颜色Bpp(bit per pixel)信息 
*	buff:framebuffer基地址
*/
typedef struct DispBuff {
	int iXres;
	int iYres;
	int iBpp;
	char *buff;
}DispBuff, *PDispBuff;

/*
*	区域信息
*	iLeftUpX iLeftUpY:里面包含了绘制的起始坐标
*	iWidth iHeigh:字符的长宽信息
*/
typedef struct Region {
	int iLeftUpX;
	int iLeftUpY;
	int iWidth;
	int iHeigh;
}Region, *PRegion;

/*
*	Display相关函数集合结构体 用来表示一个Display设备
*	DeviceInit:初始化设备
*	DeviceExit:退出回收设备
*	GetBuffer :获得Displaybuffer
*	FlushRegio:融合区
*	ptNext:指向下一个DispOpr类型结构体指针即下一个Display设备
*/
typedef struct DispOpr {
	char *name;
	int (*DeviceInit)(void);
	int (*DeviceExit)(void);
	int (*GetBuffer)(PDispBuff ptDispBuff);
	int (*FlushRegion)(PRegion ptRegion, PDispBuff ptDispBuff);
	struct DispOpr *ptNext;
}DispOpr, *PDispOpr;
  • 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

在程序中使用了一个链表g_DispDevs来保存Display设备,LCD设备和web设备都将保存在这个链表中
g_DispDefault指向默认的Display设备
g_tDispBuff保存Dispalybuffer使用LCD时可以理解为Frambuffer

/* 管理底层的LCD、WEB */
static PDispOpr g_DispDevs = NULL;		//Display设备链表
static PDispOpr g_DispDefault = NULL;	//默认Display设备指针 从Display设备链表中获取Display设备地址
static DispBuff g_tDispBuff;			//保存Dispalybuffer 在这里可以理解为Frambuffer
  • 1
  • 2
  • 3
  • 4

1.3、程序分析

disp_test.c:使屏幕上显示字符A
注:fontdata_8x16数组见韦东山老师的程序

#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>
#include <sys/ioctl.h>

#include <disp_manager.h>

/**********************************************************************
 * 函数名称: lcd_put_ascii
 * 功能描述: 在LCD指定位置上显示一个8*16的字符
 * 输入参数: x坐标,y坐标,ascii码
 * 输出参数: 无
 * 返 回 值: 无
 * 修改日期        版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2020/05/12	     V1.0	  zh(angenao)	      创建
 ***********************************************************************/ 
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 */
				PutPixel(x+7-b, y+i, 0xffffff); /* 白 */
			}
			else
			{
				/* hide */
				PutPixel(x+7-b, y+i, 0); /* 黑 */
			}
		}
	}
}

int main(int argc, char **argv)
{
	Region region;
	PDispBuff ptBuffer;
	
	//初始化Display相关设备,LCD设备和Web设备(暂无),在Display设备链表中插入节点 
	DisplayInit();
	//设定默认Display设备,在g_DispDevs链表中寻找name == fb的设备使g_DispDefault指向它
	SelectDefaultDisplay("fb");
	//初始化默认Display设备 包含设备初始化和获得Displaybuffer
	InitDefaultDisplay();
	//向Framebuffer缓冲区中填写内容
	lcd_put_ascii(100, 100, 'A');

	/*	
	*	下面的函数及内容是为web设备准备的,由于在InitDefaultDisplay时已经获得了framebuffer的基地址
	*	所以这里不用FlushDisplayRegion
	*/
	//填写区域信息
	region.iLeftUpX = 100;
	region.iLeftUpY = 100;
	region.iWidth   = 8;
	region.iHeigh   = 16;

	//获得Framebuffer地址
	ptBuffer = GetDisplayBuffer();
	//将区域信息和Framebuffer信息融合
	FlushDisplayRegion(&region, ptBuffer);
	
	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

disp_manager.h:包含一些数据结构和函数声明

#ifndef _DISP_MANAGER_H
#define _DISP_MANAGER_H

#ifndef NULL
#define NULL (void *)0
#endif

/*
*	显示缓冲区
*	iXres iYres:屏幕的x,y轴长度 
*	iBpp:字符的颜色Bpp(bit per pixel)信息 
*	buff:framebuffer基地址
*/
typedef struct DispBuff {
	int iXres;
	int iYres;
	int iBpp;
	char *buff;
}DispBuff, *PDispBuff;

/*
*	区域信息
*	iLeftUpX iLeftUpY:里面包含了绘制的起始坐标
*	iWidth iHeigh:字符的长宽信息
*/
typedef struct Region {
	int iLeftUpX;
	int iLeftUpY;
	int iWidth;
	int iHeigh;
}Region, *PRegion;

/*
*	Display相关函数集合结构体 用来表示一个Display设备
*	DeviceInit:初始化设备
*	DeviceExit:退出回收设备
*	GetBuffer :获得Displaybuffer
*	FlushRegio:融合区
*	ptNext:指向下一个DispOpr类型结构体指针即下一个Display设备
*/
typedef struct DispOpr {
	char *name;
	int (*DeviceInit)(void);
	int (*DeviceExit)(void);
	int (*GetBuffer)(PDispBuff ptDispBuff);
	int (*FlushRegion)(PRegion ptRegion, PDispBuff ptDispBuff);
	struct DispOpr *ptNext;
}DispOpr, *PDispOpr;

void RegisterDisplay(PDispOpr ptDispOpr);

void DisplayInit(void);
int SelectDefaultDisplay(char *name);
int InitDefaultDisplay(void);
int PutPixel(int x, int y, unsigned int dwColor);
int FlushDisplayRegion(PRegion ptRegion, PDispBuff ptDispBuff);
PDispBuff GetDisplayBuffer(void);


#endif
  • 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

disp_manager.c:管理Display设备相关函数

#include <stdio.h>
#include <string.h>
#include <disp_manager.h>

/* 管理底层的LCD、WEB */
static PDispOpr g_DispDevs = NULL;		//Display设备链表
static PDispOpr g_DispDefault = NULL;	//默认Display设备指针 从Display设备链表中获取Display设备地址
static DispBuff g_tDispBuff;			//保存Dispalybuffer 在这里可以理解为Frambuffer
static int line_width;
static int pixel_width;

/*显示一个像素,涉及到颜色计算*/
int PutPixel(int x, int y, unsigned int dwColor)
{
	unsigned char *pen_8 = (unsigned char *)(g_tDispBuff.buff+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 (g_tDispBuff.iBpp)
	{
		case 8:
		{
			*pen_8 = dwColor;
			break;
		}
		case 16:
		{
			/* 565 */
			red   = (dwColor >> 16) & 0xff;
			green = (dwColor >> 8) & 0xff;
			blue  = (dwColor >> 0) & 0xff;
			dwColor = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
			*pen_16 = dwColor;
			break;
		}
		case 32:
		{
			*pen_32 = dwColor;
			break;
		}
		default:
		{
			printf("can't surport %dbpp\n", g_tDispBuff.iBpp);
			return -1;
			break;
		}
	}

	return 0;
}



void RegisterDisplay(PDispOpr ptDispOpr)
{
	//在Display设备链表中用头插法插入节点
	ptDispOpr->ptNext = g_DispDevs;	//ptNext指向原来的Display设备
	g_DispDevs = ptDispOpr;			//g_DispDevs指向新的Dispay设备
}


int SelectDefaultDisplay(char *name)
{
	PDispOpr pTmp = g_DispDevs;
	while (pTmp) 
	{
		//遍历g_DispDevs设备链表寻找name相同设备让g_DispDefault指向该Display设备
		if (strcmp(name, pTmp->name) == 0)
		{
			g_DispDefault = pTmp;
			return 0;
		}

		pTmp = pTmp->ptNext;
	}

	return -1;
}

int InitDefaultDisplay(void)
{
	int ret;
	
	ret = g_DispDefault->DeviceInit();
	if (ret)
	{
		printf("DeviceInit err\n");
		return -1;
	}

	
	ret = g_DispDefault->GetBuffer(&g_tDispBuff);
	if (ret)
	{
		printf("GetBuffer err\n");
		return -1;
	}
	//LCD行宽 单位:byte
	line_width  = g_tDispBuff.iXres * g_tDispBuff.iBpp/8;
	//像素宽度 单位:byte
	pixel_width = g_tDispBuff.iBpp/8;

	return 0;
}


PDispBuff GetDisplayBuffer(void)
{
	//返回Displaybuffer地址
	return &g_tDispBuff;
}


int FlushDisplayRegion(PRegion ptRegion, PDispBuff ptDispBuff)
{
	//将区域信息和Framebuffer信息融合
	return g_DispDefault->FlushRegion(ptRegion, ptDispBuff);
}


void DisplayInit(void)
{
	
	extern void FramebufferInit(void);
	//初始化LCD设备
	FramebufferInit();
}
  • 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

framebuffer.c:LCD的framebuffer操作的相关函数,同时也定义了LCD设备结构体g_tFramebufferOpr

#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>
#include <sys/ioctl.h>

#include <disp_manager.h>

static int fd_fb;
static struct fb_var_screeninfo var;	/* Current var */
static int screen_size;
static unsigned char *fb_base;
static unsigned int line_width;
static unsigned int pixel_width;

/*Framebuffer初始化*/
static int FbDeviceInit(void)
{
	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;
	}

	line_width  = var.xres * var.bits_per_pixel / 8;			//行宽 单位:byte
	pixel_width = var.bits_per_pixel / 8;						//像素宽度 单位:byte
	screen_size = var.xres * var.yres * var.bits_per_pixel / 8;	//屏幕尺寸 单位:byte
	fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);//申请屏幕尺寸大小的内存并返回基地址
	if (fb_base == (unsigned char *)-1)
	{
		printf("can't mmap\n");
		return  -1;
	}

	return 0;
}

/*Framebuffer回收*/
static int FbDeviceExit(void)
{
	munmap(fb_base, screen_size);
	close(fd_fb);
	return 0;
}


/* 可以返回LCD的framebuffer, 以后上层APP可以直接操作LCD, 可以不用FbFlushRegion
 * 也可以malloc返回一块无关的buffer, 要使用FbFlushRegion
 */
static int FbGetBuffer(PDispBuff ptDispBuff)
{
	ptDispBuff->iXres = var.xres;
	ptDispBuff->iYres = var.yres;
	ptDispBuff->iBpp  = var.bits_per_pixel;
	ptDispBuff->buff  = (char *)fb_base;
	return 0;
}

static int FbFlushRegion(PRegion ptRegion, PDispBuff ptDispBuff)
{
	return 0;
}

//初始化g_tFramebufferOpr结构体
static DispOpr g_tFramebufferOpr = {
	.name        = "fb",
	.DeviceInit  = FbDeviceInit,
	.DeviceExit  = FbDeviceExit,
	.GetBuffer   = FbGetBuffer,
	.FlushRegion = FbFlushRegion,
};


void FramebufferInit(void)
{
	//注册一个Display设备 
	RegisterDisplay(&g_tFramebufferOpr);
}
  • 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

2、输入系统

【嵌入式Linux】嵌入式Linux应用开发基础知识之输入系统应用编程
【嵌入式Linux】嵌入式Linux应用开发基础知识之网络通信
【嵌入式Linux】嵌入式Linux应用开发基础知识之多线程编程

2.1、程序分层

在这里插入图片描述

▲程序分层

2.2、触摸屏输入

2.2.1、几个重要的数据结构

抽象出两个结构体,数据和设备

/*
*	输入事件结构体
*	iType:输入事件类型 
*	iX iY:输入事件坐标
*	iPressure:输入事件压力值
*	str:输入事件字符串,网络编程使用
*/
typedef struct InputEvent {
	struct timeval	tTime;
	int iType;
	int iX;
	int iY;
	int iPressure;
	char str[1024];
}InputEvent, *PInputEvent;

/*
*	输入设备结构体 用来表示一个输入设备
*	DeviceInit:初始化设备
*	DeviceExit:退出回收设备
*	GetInputEvent:接收输入事件
*	ptNext:指向下一个InputDevice类型结构体指针即下一个输入设备
*/
typedef struct InputDevice {
	char *name;
	int (*GetInputEvent)(PInputEvent ptInputEvent);
	int (*DeviceInit)(void);
	int (*DeviceExit)(void);
	struct InputDevice *ptNext;
}InputDevice, *PInputDevice;

  • 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

2.2.1、程序分析

input_manager.h:包含数据结构和函数声明

#ifndef _INPUT_MANAGER_H
#define _INPUT_MANAGER_H

#include <sys/time.h>

#ifndef NULL
#define NULL (void *)0
#endif

#define INPUT_TYPE_TOUCH 1
#define INPUT_TYPE_NET   2

/*
*	输入事件结构体
*	iType:输入事件类型 
*	iX iY:输入事件坐标
*	iPressure:输入事件压力值
*	str:输入事件字符串,网络编程使用
*/
typedef struct InputEvent {
	struct timeval	tTime;
	int iType;
	int iX;
	int iY;
	int iPressure;
	char str[1024];
}InputEvent, *PInputEvent;

/*
*	输入设备结构体 用来表示一个输入设备
*	DeviceInit:初始化设备
*	DeviceExit:退出回收设备
*	GetInputEvent:接收输入事件
*	ptNext:指向下一个InputDevice类型结构体指针即下一个输入设备
*/
typedef struct InputDevice {
	char *name;
	int (*GetInputEvent)(PInputEvent ptInputEvent);
	int (*DeviceInit)(void);
	int (*DeviceExit)(void);
	struct InputDevice *ptNext;
}InputDevice, *PInputDevice;


void RegisterInputDevice(PInputDevice ptInputDev);
void InputSystemRegister(void);
void IntpuDeviceInit(void);
int GetInputEvent(PInputEvent ptInputEvent);

#endif
  • 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

touchscreen.c:监测触控屏设备事件及输出报点信息

#include <input_manager.h>
#include <tslib.h>
#include <stdio.h>

static struct tsdev *g_ts;

static int TouchscreenGetInputEvent(PInputEvent ptInputEvent)
{
	struct ts_sample samp;
	int ret;
	
	//使用ts_lib库函数读取报点信息
	ret = ts_read(g_ts, &samp, 1);
	
	if (ret != 1)
		return -1;

	//将报点信息传入ptInputEvent
	ptInputEvent->iType     = INPUT_TYPE_TOUCH;
	ptInputEvent->iX        = samp.x;
	ptInputEvent->iY        = samp.y;
	ptInputEvent->iPressure = samp.pressure;
	ptInputEvent->tTime     = samp.tv;

	return 0;
}

static int TouchscreenDeviceInit(void)
{
	//使用ts_lib函数初始化触控设备
	g_ts = ts_setup(NULL, 0);
	if (!g_ts)
	{
		printf("ts_setup err\n");
		return -1;
	}

	return 0;
}

static int TouchscreenDeviceExit(void)
{
	//使用ts_lib函数关闭触控设备
	ts_close(g_ts);
	return 0;
}

/*创建一个输入设备:触控屏*/
static InputDevice g_tTouchscreenDev ={
	.name = "touchscreen",
	.GetInputEvent  = TouchscreenGetInputEvent,
	.DeviceInit     = TouchscreenDeviceInit,
	.DeviceExit     = TouchscreenDeviceExit,
};

#if 1

int main(int argc, char **argv)
{
	InputEvent event;
	int ret;
	
	//初始化触控屏设备
	g_tTouchscreenDev.DeviceInit();

	while (1)
	{
		//获得触控屏设备上报的输入事件
		ret = g_tTouchscreenDev.GetInputEvent(&event);
		if (ret) {
			printf("GetInputEvent err!\n");
			return -1;
		}
		else
		{
			printf("Type      : %d\n", event.iType);
			printf("iX        : %d\n", event.iX);
			printf("iY        : %d\n", event.iY);
			printf("iPressure : %d\n", event.iPressure);
		}
	}
	return 0;
}

#endif
  • 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

2.3、网络输入

网络输入编程使用的数据结构和触摸屏使用的数据结构相同,这里不再赘述
在开发板上运行的是server端的程序使用udp通信,所以在创建设备的时候需要完成

/* socket
 * bind
 * sendto/recvfrom
 */
  • 1
  • 2
  • 3
  • 4

netinput.c:创建一个server设备使用udp通信协议,接收client信息并打印到console终端上

#include <input_manager.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

/* socket
 * bind
 * sendto/recvfrom
 */

#define SERVER_PORT 8888

static int g_iSocketServer;/*网络设备文件描述符*/

/*获取网络设备上报信息*/
static int NetinputGetInputEvent(PInputEvent ptInputEvent)
{
	struct sockaddr_in tSocketClientAddr;
	int iRecvLen;
	char aRecvBuf[1000];
	
	unsigned int iAddrLen = sizeof(struct sockaddr);
	
	//没有数据报到达时阻塞
	iRecvLen = recvfrom(g_iSocketServer, aRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
	if (iRecvLen > 0)
	{
		aRecvBuf[iRecvLen] = '\0';
		//printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);
		ptInputEvent->iType 	= INPUT_TYPE_NET;//设置上报信息类型为网络类型
		gettimeofday(&ptInputEvent->tTime, NULL);//获取默认时区的时间
		strncpy(ptInputEvent->str, aRecvBuf, 1000);//拷贝缓冲区的数据到ptInputEvent
		ptInputEvent->str[999] = '\0';//添加结束符
		return 0;
	}
	else
		return -1;
}


/*网络输入设备初始化*/
static int NetinputDeviceInit(void)
{
	struct sockaddr_in tSocketServerAddr;
	int iRet;
	
	//使用udp协议通信
	g_iSocketServer = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == g_iSocketServer)
	{
		printf("socket error!\n");
		return -1;
	}

	tSocketServerAddr.sin_family      = AF_INET;
	tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */
 	tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
	memset(tSocketServerAddr.sin_zero, 0, 8);
	
	//绑定socket文件描述符和socket address
	iRet = bind(g_iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
	if (-1 == iRet)
	{
		printf("bind error!\n");
		return -1;
	}

	return 0;
}

/*关闭网络设备*/
static int NetinputDeviceExit(void)
{
	close(g_iSocketServer);	
	return 0;
}

/*创建一个网络输入设备*/
static InputDevice g_tNetinputDev ={
	.name = "touchscreen",
	.GetInputEvent  = NetinputGetInputEvent,
	.DeviceInit     = NetinputDeviceInit,
	.DeviceExit     = NetinputDeviceExit,
};

#if 1

int main(int argc, char **argv)
{
	InputEvent event;
	int ret;
	
	g_tNetinputDev.DeviceInit();

	while (1)
	{
		/*接收client信息并输出*/
		ret = g_tNetinputDev.GetInputEvent(&event);
		if (ret) {
			printf("GetInputEvent err!\n");
			return -1;
		}
		else
		{
			printf("Type      : %d\n", event.iType);
			printf("str       : %s\n", event.str);
		}
	}
	return 0;
}

#endif
  • 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

client.c:测试使用的client程序,使用./client <server_ip> <str>来向server发送信息

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>

/* socket
 * connect
 * send/recv
 */

#define SERVER_PORT 8888

int main(int argc, char **argv)
{
	int iSocketClient;
	struct sockaddr_in tSocketServerAddr;
	
	int iRet;
	int iSendLen;
	int iAddrLen;

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

	iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);

	tSocketServerAddr.sin_family      = AF_INET;
	tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */
 	//tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
 	if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
 	{
		printf("invalid server_ip\n");
		return -1;
	}
	memset(tSocketServerAddr.sin_zero, 0, 8);

#if 0
	iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));	
	if (-1 == iRet)
	{
		printf("connect error!\n");
		return -1;
	}
#endif

	iAddrLen = sizeof(struct sockaddr);
	iSendLen = sendto(iSocketClient, argv[2], strlen(argv[2]), 0,
	              (const struct sockaddr *)&tSocketServerAddr, iAddrLen);

	close(iSocketClient);
	
	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

2.4、输入管理

2.4.1、框架framework

在这里插入图片描述

▲输入管理器

在输入管理器中一共向外部提供了三个函数:

  void InputInit(void)
    连接到驱动侧,驱动侧通过这个函数将注册输入设备结构体到设备链表中
  void RegisterInputDevice(PInputDevice ptInputDev) / void InpuInit(PInputDevice ptInputDev)
    连接到APP侧,APP侧可以使用这个函数初始化设备链表中所有的输入设备,并且为其创建监听线程
  int GetInputEvent(PT_InputEvent ptInputEvent)
    连接到APP侧,APP侧可以使用这个函数获取底层上报的事件信息

此外还有一个线程函数static void *input_recv_thread_func (void *data)负责完成监听线程工作

程序分析

input_manage.c:其中int GetInputEvent(PT_InputEvent ptInputEvent)static void *input_recv_thread_func (void *data) static void *input_recv_thread_func (void *data)函数没写完

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>

static PInputDevice g_InputDevs  = NULL;

void RegisterInputDevice(PInputDevice ptInputDev)
{
	ptInputDev->ptNext = g_InputDevs;
	g_InputDevs = ptInputDev;
}

/*  */
void InputInit(void)
{
	/* regiseter touchscreen */
	extern void TouchscreenRegister(void);
	TouchscreenRegister();

	/* regiseter netinput */
	extern void NetInputRegister(void);
	NetInputRegister();
}

static void *input_recv_thread_func (void *data)
{
	PInputDevice tInputDev = (PInputDevice)data;
	InputEvent tEvent;
	int ret;
	
	while (1)
	{
		/* 读数据 */
		ret = tInputDev->GetInputEvent(&tEvent);

		if (!ret)
		{	
			/* 保存数据 */

			/* 唤醒等待数据的线程 */
		
			pthread_mutex_lock(&g_tMutex);
			pthread_cond_wait(&g_tConVar, &g_tMutex);	

			pthread_mutex_unlock(&g_tMutex);
		}
	}

	return NULL;
}

void IntpuDeviceInit(void)
{
	int ret;
	pthread_t tid;
	
	/* for each inputdevice, init, pthread_create */
	PInputDevice ptTmp = g_InputDevs;
	while (ptTmp)
	{
		/* init device */
		ret = ptTmp->DeviceInit();

		/* pthread create */
		if (!ret)
		{
			ret = pthread_create(&tid, NULL, input_recv_thread_func, ptTmp);
		}

		ptTmp= ptTmp->ptNext;
	}
}

int GetInputEvent(PT_InputEvent ptInputEvent)
{
	/* 无数据则休眠 */

	/* 返回数据 */
}
  • 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

touchscreen.c:添加函数void TouchscreenRegister(void)

...
void TouchscreenRegister(void)
{
	RegisterInputDevice(&g_tTouchscreenDev);
}
  • 1
  • 2
  • 3
  • 4
  • 5

netinput.c:添加函数void NetInputRegister(void)

...
void NetInputRegister(void)
{
	RegisterInputDevice(&g_tNetinputDev);
}
  • 1
  • 2
  • 3
  • 4
  • 5

2.4.2、环形缓冲区circlebuff

建立一个环形缓冲区,APP、触摸屏设备和网络设备会互斥的访问改写这个缓冲区来让APP获得设备上报的事件信息

什么是环形缓冲区?

  圆形缓冲区(circular buffer),也称作圆形队列(circular queue),循环缓冲区(cyclic buffer),环形缓冲区(ring buffer),是一种用于表示一个固定尺寸、头尾相连的缓冲区的数据结构,适合缓存数据流。

环形缓冲区的用法

  圆形缓冲区的一个有用特性是:当一个数据元素被用掉后,其余数据元素不需要移动其存储位置。相反,一个非圆形缓冲区(例如一个普通的队列)在用掉一个数据元素后,其余数据元素需要向前搬移。换句话说,圆形缓冲区适合实现先进先出缓冲区,而非圆形缓冲区适合后进先出缓冲区。
  圆形缓冲区适合于事先明确了缓冲区的最大容量的情形。扩展一个圆形缓冲区的容量,需要搬移其中的数据。因此一个缓冲区如果需要经常调整其容量,用链表实现更为合适。
  写操作覆盖圆形缓冲区中未被处理的数据在某些情况下是允许的。特别是在多媒体处理时。例如,音频的生产者可以覆盖掉声卡尚未来得及处理的音频数据。

环形缓冲区的工作过程

  一个圆形缓冲区最初为空并有预定的长度。例如,这是一个具有七个元素空间的圆形缓冲区,其中底部的单线与箭头表示“头尾相接”形成一个圆形地址空间:
在这里插入图片描述

▲环形缓冲区

  假定1被写入缓冲区中部(对于圆形缓冲区来说,最初的写入位置在哪里是无关紧要的):
请添加图片描述

▲环形缓冲区初次写入数据

  再写入2个元素,分别是2 & 3 — 被追加在1之后:
请添加图片描述

▲环形缓冲区再次写入数据

  如果两个元素被处理,那么是缓冲区中最老的两个元素被移除。在本例中,1 & 2被移除,缓冲区中只剩下3:
请添加图片描述

▲环形缓冲区移除数据

  如果缓冲区中有7个元素,则是满的:
请添加图片描述

▲环形缓冲区满员

  如果缓冲区是满的,又要写入新的数据,一种策略是覆盖掉最老的数据。此例中,2个新数据— A & B — 写入,覆盖了3 & 4:
请添加图片描述

▲环形缓冲区覆盖写入数据

  也可以采取其他策略,禁止覆盖缓冲区的数据,采取返回一个错误码或者抛出异常。
  最终,如果从缓冲区中移除2个数据,不是3 & 4 而是 5 & 6 。因为 A & B 已经覆盖了3 & 4:
请添加图片描述

▲环形缓冲区覆盖后移除数据
环形缓冲区工作机制

由于计算机内存是线性地址空间,因此圆形缓冲区需要特别的设计才可以从逻辑上实现。
读指针与写指针
一般的,圆形缓冲区需要4个指针:

  • 在内存中实际开始位置;
  • 在内存中实际结束位置,也可以用缓冲区长度代替;(对应BUFFER_LEN)
  • 存储在缓冲区中的有效数据的开始位置(读指针);(对应g_iRead)
  • 存储在缓冲区中的有效数据的结尾位置(写指针)。(对应g_iWrite)

区分缓冲区满或者空(满指缓冲区中充满了新的数据/未读的数据,空指缓冲区中没有新的数据)
缓冲区是满、或是空,都有可能出现读指针与写指针指向同一位置:
请添加图片描述

▲读指针与写指针指向同一位置

有多种策略用于检测缓冲区是满、或是空:

  1. 总是保持一个存储单元为空
      缓冲区中总是有一个存储单元保持未使用状态。缓冲区最多存入(size - 1) 个数据。如果读写指针指向同一位置,则缓冲区为空。如果写指针位于读指针的相邻后一个位置,则缓冲区为满。这种策略的优点是简单、鲁棒;缺点是语义上实际可存数据量与缓冲区容量不一致,测试缓冲区是否满需要做取余数计算。
  2. 使用数据计数
      这种策略不使用显式的写指针,而是保持着缓冲区内存储的数据的计数。因此测试缓冲区是空是满非常简单;对性能影响可以忽略。缺点是读写操作都需要修改这个存储数据计数,对于多线程访问缓冲区需要并发控制。
  3. 镜像指示位
      缓冲区的长度如果是n,逻辑地址空间则为0至n-1;那么,规定n至2n-1为镜像逻辑地址空间。本策略规定读写指针的地址空间为0至2n-1,其中低半部分对应于常规的逻辑地址空间,高半部分对应于镜像逻辑地址空间。当指针值大于等于2n时,使其折返(wrapped)到ptr-2n。使用一位表示写指针或读指针是否进入了虚拟的镜像存储区:置位表示进入,不置位表示没进入还在基本存储区。
    请添加图片描述
▲带有镜像指示位的环形缓冲区

  在读写指针的值相同情况下,如果二者的指示位相同,说明缓冲区为空如果二者的指示位不同,说明缓冲区为满。这种方法优点是测试缓冲区满/空很简单;不需要做取余数操作;读写线程可以分别设计专用算法策略,能实现精致的并发控制。 缺点是读写指针各需要额外的一位作为指示位。
  如果缓冲区长度是2的幂,则本方法可以省略镜像指示位。如果读写指针的值相等,则缓冲区为空;如果读写指针相差n,则缓冲区为满,这可以用条件表达式(写指针 == (读指针 异或 缓冲区长度))来判断。

  1. 读/写计数
      用两个有符号整型变量分别保存写入、读出缓冲区的数据数量。其差值就是缓冲区中尚未被处理的有效数据的数量。这种方法的优点是读线程、写线程互不干扰;缺点是需要额外两个变量。
  2. 记录最后的操作
      使用一位记录最后一次操作是读还是写。读写指针值相等情况下,如果最后一次操作为写入,那么缓冲区是满的;如果最后一次操作为读出,那么缓冲区是空。 这种策略的缺点是读写操作共享一个标志位,多线程时需要并发控制。
程序分析

input_manage.c

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>

static PInputDevice g_InputDevs  = NULL;

static pthread_mutex_t g_tMutex  = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  g_tConVar = PTHREAD_COND_INITIALIZER;


/* start of 实现环形buffer */
#define BUFFER_LEN 20
static int g_iRead  = 0;
static int g_iWrite = 0;
static InputEvent g_atInputEvents[BUFFER_LEN];

/* 环形缓冲区满返回1 否则返回0 */
static int isInputBufferFull(void)
{
	return (g_iRead == ((g_iWrite + 1) % BUFFER_LEN));
}

/* 环形缓冲区空返回1 否则返回0 */
static int isInputBufferEmpty(void) 
{
	return (g_iRead == g_iWrite);
}

/* 将事件数据写入环形缓冲区 */
static void PutInputEventToBuffer(PInputEvent ptInputEvent)
{
	if (!isInputBufferFull())//如果环形缓冲区未满
	{
		g_atInputEvents[g_iWrite] = *ptInputEvent;
		g_iWrite = (g_iWrite + 1) % BUFFER_LEN;
	}
}

/* 从环形缓冲区获取事件信息 成功获得信息返回1 否则返回0*/
static int GetInputEventFromBuffer(PInputEvent ptInputEvent)
{
	if (!isInputBufferEmpty())//如果环形缓冲区不为空
	{
		*ptInputEvent = g_atInputEvents[g_iRead];
		g_iRead = (g_iRead + 1) % BUFFER_LEN;
		return 1;
	}
	else
	{
		return 0;
	}
}

/* end of 实现环形buffer */

void RegisterInputDevice(PInputDevice ptInputDev)
{
	ptInputDev->ptNext = g_InputDevs;
	g_InputDevs = ptInputDev;
}

void InputInit(void)
{
	/* regiseter touchscreen */
	extern void TouchscreenRegister(void);
	TouchscreenRegister();

	/* regiseter netinput */
	extern void NetInp utRegister(void);
	NetInputRegister();
}

/* 设备子线程函数 */
static void *input_recv_thread_func (void *data)
{
	PInputDevice ptInputDev = (PInputDevice)data;
	InputEvent tEvent;
	int ret;
	
	while (1)
	{
		/* 读数据 */
		//这里使用的是设备结构体中的GetInputEvent函数,并非下面的GetInputEvent函数
		ret = ptInputDev->GetInputEvent(&tEvent);
		
		if (!ret)
		{	
			/* 保存数据 */
			pthread_mutex_lock(&g_tMutex);
			PutInputEventToBuffer(&tEvent);

			/* 唤醒等待数据的线程 */
			pthread_cond_signal(&g_tConVar); /* 通知接收线程 */
			pthread_mutex_unlock(&g_tMutex);
		}
	}

	return NULL;
}

void IntpuDeviceInit(void)
{
	int ret;
	pthread_t tid;
	
	/* for each inputdevice, init, pthread_create */
	PInputDevice ptTmp = g_InputDevs;
	while (ptTmp)
	{
		/* init device */
		ret = ptTmp->DeviceInit();

		/* pthread create */
		if (!ret)
		{
			ret = pthread_create(&tid, NULL, input_recv_thread_func, ptTmp);
		}

		ptTmp= ptTmp->ptNext;
	}
}


/* 从环形缓冲区中获取事件信息 成功返回0 失败返回-1*/
int GetInputEvent(PT_InputEvent ptInputEvent)
{
	InputEvent tEvent;
	int ret;
	/* 无数据则休眠 */
	pthread_mutex_lock(&g_tMutex);
	if (GetInputEventFromBuffer(&tEvent))
	{
		*ptInputEvent = tEvent;
		pthread_mutex_unlock(&g_tMutex);
		return 0;
	}
	else
	{
		/* 休眠等待 等待设备线程唤醒 */
		pthread_cond_wait(&g_tConVar, &g_tMutex);	
		if (GetInputEventFromBuffer(&tEvent))
		{
			ret = 0;
		}
		else
		{
			ret = -1;
		}
		pthread_mutex_unlock(&g_tMutex);		
	}
	return ret;

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

2.5、测试程序

#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>
#include <sys/ioctl.h>

#include <input_manager.h>

int main(int argc, char **argv)
{
	int ret;
	InputEvent event;
	
	/* 设备注册、设备初始化、设备线程创建 */
	InputInit();
	IntpuDeviceInit();

	while (1)
	{
		printf("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
		ret = GetInputEvent(&event);

		printf("%s %s %d, ret = %d\n", __FILE__, __FUNCTION__, __LINE__, ret);
		if (ret) {
			printf("GetInputEvent err!\n");
			return -1;
		}
		else
		{
			printf("%s %s %d, event.iType = %d\n", __FILE__, __FUNCTION__, __LINE__, event.iType );
			if (event.iType == INPUT_TYPE_TOUCH)//touch设备
			{
				printf("Type      : %d\n", event.iType);
				printf("iX        : %d\n", event.iX);
				printf("iY        : %d\n", event.iY);
				printf("iPressure : %d\n", event.iPressure);
			}
			else if (event.iType == INPUT_TYPE_NET)//web设备
			{
				printf("Type      : %d\n", event.iType);
				printf("str       : %s\n", event.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

3、文字系统

【嵌入式Linux】嵌入式Linux应用开发基础知识之Framebuffer应用编程和字符汉字显示

在这里插入图片描述

▲程序分层

3.1、数据结构抽象

在这里插入图片描述

▲字形指标

font_manage.h:

#ifndef _FONT_MANAGER_H
#define _FONT_MANAGER_H

#ifndef NULL
#define NULL (void *)0
#endif

/* 	描述一个文字的位图 
*	iLeftUpX、iLeftUpY:左上角(x,y)坐标
*	iWidth、iRows:宽度、行数
*	iCurOriginX、iCurOriginY:当前字符的基点(x,y)坐标
*	iNextOriginX、iNextOriginY:下一个字符的基点(x,y)坐标
*	pucBuffer:位图指针
*/
typedef struct FontBitMap {
	int iLeftUpX;
	int iLeftUpY;
	int iWidth;
	int iRows;
	int iCurOriginX;
	int iCurOriginY;
	int iNextOriginX;
	int iNextOriginY;
	unsigned char *pucBuffer;
}FontBitMap, *PFontBitMap;

/*	字库的操作结构体
	name:使用freetype还是点阵
	FontInit:初始化函数
	SetFontSize:设置字体大小
	GetFontBitMap:获得字符的位图
	ptNext:下一个字库的操作结构体
*/
typedef struct FontOpr {
	char *name;
	int (*FontInit)(char *aFineName);
	int (*SetFontSize)(int iFontSize);
	int (*GetFontBitMap)(unsigned int dwCode, PFontBitMap ptFontBitMap);
	struct FontOpr *ptNext;
}FontOpr, *PFontOpr;

#endif
  • 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

3.2、实现Freetype代码

freetype.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>
#include <math.h>
#include <wchar.h>
#include <sys/ioctl.h>
#include <font_manager.h>

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H


static FT_Face g_tFace;				//矢量字体文字
static int g_iDefaultFontSize = 12;	//默认字体大小

/* 初始化FreeType 创建字形结构体 */
static int FreeTypeFontInit(char *aFineName)
{
    FT_Library    library;
    int error;

    error = FT_Init_FreeType( &library );                 /* initialize library */    
	if (error)
	{
		printf("FT_Init_FreeType err\n");
		return -1;
	}
	
    error = FT_New_Face(library, aFineName, 0, &g_tFace ); /* create face object */
	if (error)
	{
		printf("FT_New_Face err\n");
		return -1;
	}

    FT_Set_Pixel_Sizes(g_tFace, g_iDefaultFontSize, 0);

	return 0;
}

/* 设置字体大小 */
static int FreeTypeSetFontSize(int iFontSize)
{
    FT_Set_Pixel_Sizes(g_tFace, iFontSize, 0);
	return 0;
}

/* 根据编码值dwCode获得位图保存在ptFontBitMap指向的结构体中 */
static int FreeTypeGetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
{
	int error;
    FT_Vector pen;						//对应origin
    FT_Glyph  glyph;					//用来保存字体文件中保存有字符的原始关键点信息
    FT_GlyphSlot slot = g_tFace->glyph;	//字形槽,用来保存字符处理后的结果 glyph->bitmap 中包含了中文位图信息

    pen.x = ptFontBitMap->iCurOriginX * 64; /* 单位: 1/64像素 */
    pen.y = ptFontBitMap->iCurOriginY * 64; /* 单位: 1/64像素 */

	/* 转换:transformation */
	FT_Set_Transform(g_tFace, 0, &pen);

	/* 加载位图: load glyph image into the slot (erase previous one) */
	error = FT_Load_Char(g_tFace, dwCode, FT_LOAD_RENDER);
	if (error)
	{
		printf("FT_Load_Char error\n");
		return -1;
	}

	/* 将加载出来的位图数据保存到ptFontBitMap */
	ptFontBitMap->pucBuffer = slot->bitmap.buffer;
	/* 位图左上角坐标x值 */
	ptFontBitMap->iLeftUpX = slot->bitmap_left;
	/* 	
	*	iCurOriginY是LCD坐标系  bitmap_top是笛卡尔坐标系 iLeftUpY是LCD坐标系
	*	iLeftUpY = iCurOriginY - delta
	*	delta = bitmap_top - iCurOriginY 		//此处有疑问
	*	iLeftUpY = 2 * iCurOriginY - bitmap_top
	*/
	ptFontBitMap->iLeftUpY = ptFontBitMap->iCurOriginY*2 - slot->bitmap_top;
	/* 位图的总宽度即有多少列像素点 */
	ptFontBitMap->iWidth   = slot->bitmap.width;
	/* 位图的总高度即有多少行 */
	ptFontBitMap->iRows    = slot->bitmap.rows;
	/* 计算下一个文字位图的基点 */
	ptFontBitMap->iNextOriginX = ptFontBitMap->iCurOriginX + slot->advance.x / 64;
	ptFontBitMap->iNextOriginY = ptFontBitMap->iCurOriginY;

	return 0;
}

/* 初始化freetype字库的操作结构体 */
static FontOpr g_tFreetypeOpr = {
	.name          = "freetype",
	.FontInit      = FreeTypeFontInit,
	.SetFontSize   = FreeTypeSetFontSize,
	.GetFontBitMap = FreeTypeGetFontBitMap,
};
  • 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

3.3、文字管理

font_manage.c:管理文字库设备

#include <font_manager.h>



static PFontOpr g_ptFonts = NULL;			//字库设备链表头
static PFontOpr g_ptDefaulFontOpr = NULL;	//默认字库设备指针

/* 将字库的操作结构体加入链表 */
void RegisterFont(PFontOpr ptFontOpr)
{
	ptFontOpr->ptNext = g_ptFonts;
	g_ptFonts = ptFontOpr;
}

/* 承上启下 */
void FontsRegister(void)
{
	extern void FreetypeRegister(void);
	FreetypeRegister();
}

/* 把下面的各个字体注册进来 */
int SelectAndInitFont(char *aFontOprName, char *aFontFileName)
{
	PFontOpr ptTmp = g_ptFonts;
	/* 在链表中寻找指定名称的设备将其设定为默认设备 */
	while (ptTmp)
	{
		if (strcmp(ptTmp->name, aFontOprName) == 0)
			break;
		ptTmp = ptTmp->ptNext;
	}

	if (!ptTmp)
		return -1;

	g_ptDefaulFontOpr = ptTmp;
	return ptTmp->FontInit(aFontFileName);
}

/* 上层可以选择某个字体模块 */
int SetFontSize(int iFontSize)
{
	return g_ptDefaulFontOpr->SetFontSize(iFontSize);
}

/* 上层可以得到某个字符的位图 */
int GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
{
	return g_ptDefaulFontOpr->GetFontBitMap(dwCode, ptFontBitMap);
}

  • 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

3.5、单元测试

common.h:包含通用结构体及宏定义声明

#ifndef _COMMON_H
#define _COMMON_H

#ifndef NULL
#define NULL (void *)0
#endif

/*
*	区域信息
*	iLeftUpX iLeftUpY:里面包含了绘制的起始坐标
*	iWidth iHeigh:字符的长宽信息
*/
typedef struct Region {
	int iLeftUpX;
	int iLeftUpY;
	int iWidth;
	int iHeigh;
}Region, *PRegion;

#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

font_manager.h中修改struct FontBitMapRegion结构体代替原来保存区域信息的变量

typedef struct FontBitMap {
	int iLeftUpX;
	int iLeftUpY;
	int iWidth;
	int iRows;
	int iCurOriginX;
	int iCurOriginY;
	int iNextOriginX;
	int iNextOriginY;
	unsigned char *pucBuffer;
}FontBitMap, *PFontBitMap;
						
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/639181
推荐阅读
相关标签