Linux基础项目开发1:量产工具——显示系统
文章目录
- 数据结构抽象
- 使用场景
- 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; //Bppchar *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; //Bppchar *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;
}
测试代码分析: