一、显示子模块介绍
本文要实现display文件夹下的两个文件: fb.c disp_manager.c, 以及include文件夹下的 disp_manager.h、config.h
他们之间的关系如下图所示

二、显示子模块代码实现
config.h
#ifndef _CONFIG_H
#define _CONFIG_H
#define DBG_PRINTF printf
#define FB_DEVICE_NAME "/dev/fb0"
#endif
disp_manager.h
#ifndef _DISP_MANAGER_H
#define _DISP_MANAGER_H
typedef struct DispOpr{
char *name;
int iXres;
int iYres;
int iBpp;
int (*DeviceInit)(void);
int (*ShowPixel)(int iPenX, int iPenY, unsigned int dwColor);
int (*CleanScreen)(unsigned int dwBkgdColor);
struct DispOpr *ptNext;
}T_DispOpr, *PT_DispOpr;
#endif
disp_manager.c
#include <disp_manager.h>
#include <config.h>
#include <string.h>
static PT_DispOpr g_ptDispOprHead;
int RegisterDispOpr(PT_DispOpr ptDispOpr)
{
PT_DispOpr ptTmp;
if(!g_ptDispOprHead)
{
g_ptDispOprHead = ptDispOpr;
g_ptDispOprHead->ptNext = NULL;
}
else
{
ptTmp = g_ptDispOprHead;
while (ptTmp->ptNext)
{
ptTmp = ptTmp->ptNext;
}
ptTmp->ptNext = ptDispOpr;
ptDispOpr->ptNext = NULL;
}
ptTmp = NULL;
return 0;
}
void ShowDispOpr(void)
{
int i = 0;
PT_DispOpr ptTmp = g_ptDispOprHead;
while(ptTmp)
{
printf("%02d %s\n", i++, ptTmp->name);
ptTmp = ptTmp->ptNext;
}
ptTmp = NULL;
}
PT_DispOpr GetDispOpr(char* pcName)
{
PT_DispOpr ptTmp = g_ptDispOprHead;
while(ptTmp)
{
if(strcmp(ptTmp->name, pcName) == 0)
{
return ptTmp;
}
ptTmp = ptTmp->ptNext;
}
ptTmp = NULL;
return NULL;
}
int DisplayInit(void)
{
int iError;
iError = FBInit();
return iError;
}
fb.c
#include <config.h>
#include <disp_manager.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <string.h>
#include <sys/mman.h>
static int FBDeviceInit(void);
static int FBShowPixel(int iX, int iY, unsigned int dwColor);
static int FBCleanScreen(void);
static int g_fd;
static unsigned int g_dwLineWidth ;
static unsigned int g_dwPixelWidth;
static unsigned int g_dwScreenSize;
static unsigned char* g_pucFbMem;
static struct fb_var_screeninfo g_tFBVar;
static struct fb_fix_screeninfo g_tFBFix;
static T_DispOpr g_tFBOpr = {
.name = "fb",
.DeviceInit = FBDeviceInit,
.ShowPixel = FBShowPixel,
.CleanScreen = FBCleanScreen,
};
static int FBDeviceInit(void)
{
g_fd = open(FB_DEVICE_NAME, O_RDWR);
if(g_fd < 0)
{
DBG_PRINTF("can't open /dev/fb0\n");
return -1;
}
if(ioctl(g_fd, FBIOGET_VSCREENINFO, &g_tFBVar))
{
DBG_PRINTF("can't get var\n");
return -1;
}
if(ioctl(g_fd, FBIOGET_FSCREENINFO, &g_tFBFix))
{
DBG_PRINTF("can't get fix\n");
return -1;
}
g_dwLineWidth = g_tFBVar.xres * g_tFBVar.bits_per_pixel / 8;
g_dwPixelWidth = g_tFBVar.bits_per_pixel / 8;
g_dwScreenSize = g_tFBVar.xres * g_tFBVar.yres * g_tFBVar.bits_per_pixel / 8;
g_pucFbMem = (unsigned char*)mmap(NULL, g_dwScreenSize, \
PROT_READ | PROT_WRITE, MAP_SHARED, g_fd, 0);
if(g_pucFbMem == (unsigned char*)-1)
{
DBG_PRINTF("can't map g_pucFbMem\n");
return -1;
}
g_tFBOpr.iXres = g_tFBVar.xres;
g_tFBOpr.iYres = g_tFBVar.yres;
g_tFBOpr.iBpp = g_tFBVar.bits_per_pixel;
return 0;
}
static int FBShowPixel(int iX, int iY, unsigned int dwColor)
{
unsigned char* pucPen8pp = NULL;
unsigned short* pwPen16pp = NULL;
unsigned int* pdwPen32pp = NULL;
unsigned short wColor16bpp;
unsigned int ired;
unsigned int igreen;
unsigned int iblue;
if ((iX >= g_tFBVar.xres) || (iY >= g_tFBVar.yres))
{
DBG_PRINTF("out of region\n");
return -1;
}
pucPen8pp = g_pucFbMem + iY*g_dwLineWidth + iX*g_dwPixelWidth;
pwPen16pp = (unsigned short*)pucPen8pp;
pdwPen32pp = (unsigned int*)pucPen8pp;
switch(g_tFBVar.bits_per_pixel)
{
case 8:
{
*pucPen8pp = (unsigned char)dwColor;
break;
}
case 16:
{
ired = (dwColor >> (16+3)) & 0x1f;
igreen = (dwColor >> (8+2)) & 0x3f;
iblue = (dwColor >> 3) & 0x1f;
wColor16bpp = ((ired << 11) | (igreen << 5) | iblue);
*pwPen16pp = wColor16bpp;
break;
}
case 32:
{
*pdwPen32pp = dwColor;
break;
}
default:
{
DBG_PRINTF("can't surport %dbpp\n",var.bits_per_pixel);
return -1;
}
}
return 0;
}
static int FBCleanScreen(unsigned int dwBackColor)
{
unsigned char *pucFB;
unsigned short *pwFB16bpp;
unsigned int *pdwFB32bpp;
unsigned short wColor16bpp;
int iRed;
int iGreen;
int iBlue;
int i = 0;
pucFB = g_pucFbMem;
pwFB16bpp = (unsigned short *)pucFB;
pdwFB32bpp = (unsigned int *)pucFB;
switch (g_tFBVar.bits_per_pixel)
{
case 8:
{
memset(g_pucFbMem, dwBackColor, g_dwScreenSize);
break;
}
case 16:
{
iRed = (dwBackColor >> (16+3)) & 0x1f;
iGreen = (dwBackColor >> (8+2)) & 0x3f;
iBlue = (dwBackColor >> 3) & 0x1f;
wColor16bpp = (iRed << 11) | (iGreen << 5) | iBlue;
while (i < g_dwScreenSize)
{
*pwFB16bpp = wColor16bpp;
pwFB16bpp++;
i += 2;
}
break;
}
case 32:
{
while (i < g_dwScreenSize)
{
*pdwFB32bpp = dwBackColor;
pdwFB32bpp++;
i += 4;
}
break;
}
default :
{
DBG_PRINTF("can't support %d bpp\n", g_tFBVar.bits_per_pixel);
return -1;
}
}
return 0;
}
int FBInit(void)
{
return RegisterDispOpr(&g_tFBOpr);
}
编译检查有无语法或书写错误,如下

从上面源码,要掌握什么?
1)编程规范
对应变量命名,要见名知意。不仅要标识该变量类型,也要有意义。
如g_pucFbMem 表示是一个unsigned char * 类型的全局变量,且表示指向FB的显存起始映射地址
2)static 的 作用
用static修饰的变量或函数表示仅限本文件内使用,这样,对其它源文件就看不见了。
3)头文件那么多,怎知道要包含哪些?
方法一:对于glibc提供的函数, man -2 函数名 ,或到glibc目录下查找 grep "标识符" -nR
方法二:对于第三方库提供的函数,可到对应第三方库目录下搜索: grep "标志位或函数名等标识符" -nR
举个例子,查找“PROT_READ”标志位需包含的头文件,如下图

4)注释
对于函数,需在函数前注释函数功能、输入/输出参数、返回值等,如上面源码
对于全局变量,其实也要加以注释的(上面源码未加),因为全局变量的含义最难理清(可被本文件任何地方使用
(如果是static的话),或者被任何文件使用(非static)),因此很有必要注释
此外,除了函数名和变量,在每个文件(.h .c .config等)头部都需注释(然而,上面源码还是没注释,原谅我lan : )
OK,本文到此结束,下文讲解如何获取不同的点阵数据(位图)(点阵数据来源有点阵文件、HZK16、矢量字体(freetype))