所学来自百问网
目录
1.项目框架
2.显示系统
架构说明:通过disp_manager.c文件作为中间件,去调用底层文件(Framebuffer或web显示)封装好的函数包括设备初始化、资源释放等,顶层文件通过调用中间件去实现所需功能,这样子设计能达到充分的解耦,也能让代码变得更具有可维护性和健壮性。
2.1 数据结构的抽象
disp_manager.h:该头文件对设备进行各种操作(初始化、数据获取、区域刷新等)进行数据结构的抽象,DispBuff是用于对显示像素的大小的获取,Region是用于对刷新区域的设置
#ifndef _DISP_MANAGER_H
#define _DISP_MANAGER_H
#ifndef NULL
#define NULL (void *)0
#endif
// 缓冲区地址
typedef struct DispBuff {
int iXres;
int iYres;
int iBpp;
char *buff;
}DispBuff, *PDispBuff;
// 显示区域 包括xy坐标和长 高
typedef struct Region {
int iLeftUpX;
int iLeftUpY;
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);
PDispBuff GetDisplayBuffer(void);
#endif
2.2 底层的管理
disp_manager.c:该文件主要是对底层的管理即中间层,目前web未加入,通过链表对设备进行添加、选择,同时对framebuffer封装函数的调用,进一步的解耦,让代码能更好的维护和拓展
#include <stdio.h>
#include <string.h>
#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 = (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)
{
ptDispOpr->ptNext = g_DispDevs;
g_DispDevs = ptDispOpr;
}
// 选择设备
int SelectDefaultDisplay(char *name)
{
PDispOpr pTmp = g_DispDevs;
while (pTmp)
{
// 从链表中找设备
if (strcmp(name, pTmp->name) == 0)
{
g_DispDefault = pTmp; //将被选择的设备赋值给g_DispDefault
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;
}
// 获取显示缓存
PDispBuff GetDisplayBuffer(void)
{
return &g_tDispBuff;
}
// 将刷新区域
int FlushDisplayRegion(PRegion ptRegion, PDispBuff ptDispBuff)
{
return g_DispDefault->FlushRegion(ptRegion, ptDispBuff);
}
// 对framebuffer进行初始化 也可扩展对各种显示系统进行初始化(web)
void DisplayInit(void)
{
extern void FramebufferInit(void);
FramebufferInit();
}
2.3 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;
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;
// 设备初始化
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)) //对触摸屏参数获取并存入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;
}
/* 可以返回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;
}
// 对设备抽象的实现
static DispOpr g_tFramebufferOpr = {
.name = "fb",
.DeviceInit = FbDeviceInit,
.DeviceExit = FbDeviceExit,
.GetBuffer = FbGetBuffer,
.FlushRegion = FbFlushRegion,
};
// 初始化即加入链表中
void FramebufferInit(void)
{
RegisterDisplay(&g_tFramebufferOpr);
}
2.4 显示系统的单元测试
#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
static const unsigned char fontdata_8x16[FONTDATAMAX] = {
/* 0 0x00 '^@' */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
/* 1 0x01 '^A' */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x7e, /* 01111110 */
0x81, /* 10000001 */
0xa5, /* 10100101 */
0x81, /* 10000001 */
0x81, /* 10000001 */
0xbd, /* 10111101 */
0x99, /* 10011001 */
0x81, /* 10000001 */
0x81, /* 10000001 */
0x7e, /* 01111110 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
... ...
以下省略
};
/**********************************************************************
* 函数名称: 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(101, 100, 'A'); /*在屏幕中间显示8*16的字母A*/
//显示区域
region.iLeftUpx = 100;
region.iLeftUpY = 100;
region.iWidth = 8;
region.iHeigh = 16;
// 获取参数的大小
ptBuffer = GetDisplayBuffer();
FlushDisplayRegion(®ion,ptBuffer);//刷入
return 0;
}