Linux基础项目开发1:量产工具——显示系统

文章目录

数据结构抽象

我们添加的显示管理器中有Framebufferweb输出,对于两个不同的设别我们需要抽象出同一个结构体类型!

使用场景

① 我们主要是将其分为了两层,上层要获得下层某个结构体,通过这个结构体中的函数来初始化、操作、回执、刷新上层的界面。

②所以我们需要定义一个统一的结构体DisOpr

disp_mannger.h

c 复制代码
//定义一个结构体,用于表示显示操作的相关信息
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;

这个结构体是一步一步通过补充的得到的,缺啥就补啥!

再定义一个结构体表示某快区域的信息(刷到硬件上去用到的):

cpp 复制代码
//定义一个结构体,用于表示屏幕上的一块区域的信息
typedef struct Region {
	int iLeftUpX;//区域左上角的X坐标
	int iLeftUpY;//区域左上角的Y坐标
	int iWidth;//区域的宽度
	int iHeigh;//区域的高度
}Region, *PRegion;

Framebuffer编程

Framebuffer.c

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

#include "disp_manager.h"

static int fd_fb; //Framebuffer描述符
static struct fb_var_screeninfo var; // 当前屏幕信息
static int screen_size; //Framebuffer长度
static unsigned char *fb_base; // Framebuffer地址
static unsigned int line_width; //行宽度
static unsigned int pixel_width; //像素宽度

//初始化设备函数
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; //计算行宽度
	pixel_width = var.bits_per_pixel / 8; //计算像素宽度
	screen_size = var.xres * var.yres * var.bits_per_pixel / 8;//计算屏幕大小
	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;
}

//退出设备函数
static int FbDeviceExit(void)
{
	munmap(fb_base, screen_size); //取消内存映射
	close(fd_fb); //关闭文件描述符
	return 0;
}
/*
//定义一个结构体,缓存屏幕的信息
typedef struct DispBuff {
	int iXres; //行分辨率
	int iYres; //竖分标绿
	int iBpp; //Bpp
	char *buff; //保存mmap函数返回的映射区域地址
}DispBuff, *PDispBuff;
*/

//获取Framebuffer缓冲区信息
static int FbGetBuffer(PDispBuff ptDispBuff);//PDispBuff 结构体如上所述
{
	ptDispBuff->iXres = var.xres; //设置X分辨率
	ptDispBuff->iYres = var.yres; //设置Y分辨率
	ptDispBuff->iBpp  = var.bits_per_pixel; //设置每像素点位数
	ptDispBuff->buff  = fb_base; // 设置缓冲区的地址
	return 0;
}

//刷新指定区域到Framebuffer,具体功能没实现
static int FbFlushRegion(PRegion ptRegion, PDispBuff ptDispBuff)
{
	return 0;
}

//定义一个DispOpr 结构体,用于描述Framebuffer操作
static DispOpr g_tFramebufferOpr = {
	.name        = "fb", //操作名称
	.DeviceInit  = FbDeviceInit, //初始化函数指针
	.DeviceExit  = FbDeviceExit, //退出函数指针
	.GetBuffer   = FbGetBuffer, //获取缓冲区信息函数指针
	.FlushRegion = FbFlushRegion, //刷新区域函数指针
};

//初始化Framebuffer
void FramebufferInit(void)
{
	RegisterDisplay(&g_tFramebufferOpr);
}

有些还没有定义的参数名称详细介绍在后面介绍,在disp_manager.c中!

显示管理

上面所写的是要选择哪个设备进行显示,需要中间加一个函数进行选择,起到承上启下的作用,用于管理显示管理,是操作Framebuffer还是web设备!

最终disp_manager.h

c 复制代码
#ifndef _DISP_MANAGER_H
#define _DISP_MANAGER_H

//定义一个结构体,缓存屏幕的信息
typedef struct DispBuff {
	int iXres; //行分辨率
	int iYres; //竖分标绿
	int iBpp; //Bpp
	char *buff; //保存mmap函数返回的映射区域地址
}DispBuff, *PDispBuff;

//定义一个结构体,用于表示屏幕上的一块区域的信息
typedef struct Region {
	int iLeftUpX;//区域左上角的X坐标
	int iLeftUpY;//区域左上角的Y坐标
	int iWidth;//区域的宽度
	int iHeigh;//区域的高度
}Region, *PRegion;

//定义一个结构体,用于表示显示操作的相关信息
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);


#endif

disp_manager.c

cpp 复制代码
#include "disp_manager.h"

/* 管理底层的LCD、WEB */
static PDispOpr g_DispDevs = NULL; //指向第一个显示设备的指针
static PDispOpr g_DispDefault = NULL; //指向默认显示设备的指针
static DispBuff g_tDispBuff; //存储默认显示缓冲区的信息
static int line_width; //每行像素的宽度(字节数)
static int pixel_width; //每个像素的宽度(字节数)

//在屏幕上画一个点
int PutPixel(int x, int y, unsigned int dwColor)
{
    // 根据屏幕的映射地址极计算要画的点的地址
	unsigned char *pen_8 = 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;

    //根究BPP的位数不同来选择表示颜色方式
	switch (g_tDispBuff.iBpp)
	{
		case 8: //8bpp
		{
			*pen_8 = dwColor;
			break;
		}
		case 16: //16bpp
		{
			/* 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: //32bpp
		{
			*pen_32 = dwColor;
			break;
		}
		default:
		{
			printf("can't surport %dbpp\n", g_tDispBuff.iBpp);
			break;
			return -1;
		}
	}
	return 0;
}


//注册一个显示区域设备
void RegisterDisplay(PDispOpr ptDispOpr)
{
	ptDispOpr->ptNext = g_DispDevs;//将要注册的区域设备的next指向头
	g_DispDevs = ptDispOpr;//将头改为ptDispOpr,下一个的next就会指向它
}

//选择一个默认的显示区域设备
int SelectDefaultDisplay(char *name)
{
	PDispOpr pTmp = g_DispDevs;
	while (pTmp) 
	{
		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;
	}

	line_width  = g_tDispBuff.iXres * g_tDispBuff.iBpp/8;
	pixel_width = g_tDispBuff.iBpp/8;

	return 0;
}

//刷新屏幕上一个区域
int FlushDisplayRegion(PRegion ptRegion, PDispBuff ptDispBuff)
{
	return g_DispDefault->FlushRegion(ptRegion, ptDispBuff);
}


void DisplayInit(void)
{
	//FramebufferInit();
}

测试单元

测试代码

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

#include <disp_manager.h>

#define FONTDATAMAX 4096

/**********************************************************************
 * 函数名称: 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;
		
	DisplayInit(); //初始化显示系统

	SelectDefaultDisplay("fb");//选择默认的显示设备

	InitDefaultDisplay();//初始化选定的显示设备

	lcd_put_ascii(100, 100, 'A');

	region.iLeftUpX = 100;
	region.iLeftUpY = 100;
	region.iWidth   = 8;
	region.iHeigh   = 16;

	ptBuffer = GetDisplayBuffer();//获取显示缓冲区
	FlushDisplayRegion(&region, ptBuffer);//刷新指定区域的显示内容
	
	return 0;	
}

测试代码分析:

相关推荐
运维-大白同学18 分钟前
将django+vue项目发布部署到服务器
服务器·vue.js·django
糖豆豆今天也要努力鸭26 分钟前
torch.__version__的torch版本和conda list的torch版本不一致
linux·pytorch·python·深度学习·conda·torch
烦躁的大鼻嘎35 分钟前
【Linux】深入理解GCC/G++编译流程及库文件管理
linux·运维·服务器
乐大师35 分钟前
Deepin登录后提示“解锁登陆密钥环里的密码不匹配”
运维·服务器
ac.char42 分钟前
在 Ubuntu 上安装 Yarn 环境
linux·运维·服务器·ubuntu
敲上瘾42 分钟前
操作系统的理解
linux·运维·服务器·c++·大模型·操作系统·aigc
长弓聊编程1 小时前
Linux系统使用valgrind分析C++程序内存资源使用情况
linux·c++
cherub.1 小时前
深入解析信号量:定义与环形队列生产消费模型剖析
linux·c++
梅见十柒2 小时前
wsl2中kali linux下的docker使用教程(教程总结)
linux·经验分享·docker·云原生
Koi慢热2 小时前
路由基础(全)
linux·网络·网络协议·安全