文章目录
- 数据结构抽象
- 使用场景
- disp_mannger.h
- Framebuffer编程
- Framebuffer.c
- 显示管理
- 最终disp_manager.h
- disp_manager.c
- 测试单元
- 测试代码
数据结构抽象
我们添加的显示管理器中有Framebuffer和web输出,对于两个不同的设别我们需要抽象出同一个结构体类型!
使用场景
① 我们主要是将其分为了两层,上层要获得下层某个结构体,通过这个结构体中的函数来初始化、操作、回执、刷新上层的界面。
②所以我们需要定义一个统一的结构体DisOpr
disp_mannger.h
//定义一个结构体,用于表示显示操作的相关信息
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;
这个结构体是一步一步通过补充的得到的,缺啥就补啥!
再定义一个结构体表示某快区域的信息(刷到硬件上去用到的):
//定义一个结构体,用于表示屏幕上的一块区域的信息
typedef struct Region {
int iLeftUpX;//区域左上角的X坐标
int iLeftUpY;//区域左上角的Y坐标
int iWidth;//区域的宽度
int iHeigh;//区域的高度
}Region, *PRegion;
Framebuffer编程
Framebuffer.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
#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
#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();
}
测试单元
测试代码
#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(®ion, ptBuffer);//刷新指定区域的显示内容
return 0;
}
测试代码分析: