数码相册——电子书的实现
- 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3)
- 软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.04_x64 系统
- 参考资料:《嵌入式Linux应用开发手册》、《嵌入式Linux应用开发手册第2版》
- 开发环境:Linux 3.4.2内核、arm-linux-gcc 4.3.2工具链
- 源码仓库:https://gitee.com/d_1254436976/Embedded-Linux-Phase-3
目录
一、大致框架
1、框架图
对于在LCD中显示一个文件,总体上进行下面几件事:
- 去文件中获得编码信息,对于每个文件,保存的时候,系统对自动或手动的根据编码规范,对文件的信息进行编码:如保存为
ASCII、GBK、UTF-8、UTF16LE、UTF16BE
; - 根据获得的编码信息得到字体数据(LCD上表示为点阵);
- 把字体数据(点阵)显示在LCD上。
所以根据这个信息,可以总结出上述的框架。
2、代码间的有效管理
对于上述完成主要功能的4个部分:encoding编码部分、fonts获取字体点阵部分、display显示部分、draw协调部分,会仿照Linux内核的方式采用模块化进行管理,对于每个部分:
- 在代码上实现一个
manager
,向下支持各种文件的拓展,把所支持的文件放入链表中进行管理;向上提供各种接口,使其可以根据实际情况进行从链表中选择不同的拓展文件来进行功能的实现。 - 每个部分都会定义一个结构体变量,拓展文件可以根据自己的情况:分配结构体、设置结构体、注册结构体。
- 对于每个部分之间进行数据的沟通,可以通过
main.c和draw.c
的协调,通过获得各个部分的结构体变量,在draw.c
文件中进行交互。
二、config.h
文件分析
对于这个文件,从名字就知道,它是一个配置文件,定义了一些宏定义,供所有文件调用
- 源码
对于#define DBG_PRINTF(...) 与 #define DBG_PRINTF printf
:
当我们调试的时候,可以//#define DBG_PRINTF(...) #define DBG_PRINTF printf
,打印调试信息;
当我们程序发布时,可以#define DBG_PRINTF(...) //#define DBG_PRINTF printf
,屏蔽调试信息的打印。
#ifndef _CONFIG_H
#define _CONFIG_H
#include <stdio.h>
#define FB_DEVICE_NAME "/dev/fb0"
#define COLOR_BACKGROUND 0xE7DBB5 /* 泛黄的纸 */
#define COLOR_FOREGROUND 0x514438 /* 褐色字体 */
//#define DBG_PRINTF(...)
#define DBG_PRINTF printf
#endif /* _CONFIG_H */
三、挑display显示部分来进行分析
1、disp_manager.h
文件分析
在这个文件中,定义了一个结构体和相关函数,供拓展文件来分配结构体、设置结构体、注册结构体
#ifndef _DISP_MANAGER_H
#define _DISP_MANAGER_H
typedef struct DispOpr {
char *name;
int Xres;
int Yres;
int Bpp;
int (*DeviceInit)(void);
int (*ShowPixel)(int PenX, int PenY, unsigned int Color);
int (*CleanScreen)(unsigned int BackColor);
struct DispOpr *ptNext;
}T_DispOpr, *PT_DispOpr;
int RegisterDispOpr(PT_DispOpr ptDispOpr);
void ShowDispOpr(void);
int DisplayInit(void);
int FBInit(void);
#endif /* _DISP_MANAGER_H */
2、disp_manager.c
文件分析
这个文件就是负责上述disp_manager.h
文件的函数实现:
#include <config.h>
#include <disp_manager.h>
#include <string.h>
static PT_DispOpr s_ptDispOprHead;
/* 函数名: 注册函数
* 函数功能:构建一个链表:把多个拓展文件的结构体“串”起来
* 函数实现:根据传入的结点,首先判断该链表头是否为空
* 空则,头结点指向传入的节点,且把节点的ptNext域指向NULL
* 不空则,尾插法插入链表
*/
int RegisterDispOpr(PT_DispOpr ptDispOpr)
{
PT_DispOpr ptTmp;
if (!s_ptDispOprHead)
{
s_ptDispOprHead = ptDispOpr;
ptDispOpr->ptNext = NULL;
}
else
{
ptTmp = s_ptDispOprHead;
while (ptTmp->ptNext)
{
ptTmp = ptTmp->ptNext;
}
ptTmp->ptNext = ptDispOpr;
ptDispOpr->ptNext = NULL;
}
return 0;
}
/* 显示支持拓展文件的名字 */
void ShowDispOpr(void)
{
int i = 0;
PT_DispOpr ptTmp = s_ptDispOprHead;
while (ptTmp)
{
printf("%02d %s\n", i++, ptTmp->name);
ptTmp = ptTmp->ptNext;
}
}
/* 获取指定的名字的拓展文件 */
PT_DispOpr GetDispOpr(char *pName)
{
PT_DispOpr ptTmp = s_ptDispOprHead;
while (ptTmp)
{
if (strcmp(ptTmp->name, pName) == 0)
{
return ptTmp;
}
ptTmp = ptTmp->ptNext;
}
return NULL;
}
/* 初始化函数 */
int DisplayInit(void)
{
int error;
error = FBInit();
return error;
}
3、fb.c
文件分析
3.1 分配结构体
static T_DispOpr s_tFBDispOpr = {
.name = "fb",
.DeviceInit = FBDeviceInit,
.ShowPixel = FBShowPixel,
.CleanScreen = FBCleanScreen,
};
3.2 设置结构体
/* LCD设备初始化函数 */
static int FBDeviceInit(void)
{
/* 打开设备:支持读写 */
s_FBFD = open(FB_DEVICE_NAME, O_RDWR);
if (s_FBFD < 0) {
DBG_PRINTF("can not open %s , err code :%d\n", FB_DEVICE_NAME, s_FBFD);
return -1;
}
/* 获得可变信息 */
if (ioctl(s_FBFD, FBIOGET_VSCREENINFO, &s_tVar)) {
DBG_PRINTF("can not get s_tVar\n");
return -1;
}
/* 获得固定信息 */
if (ioctl(s_FBFD, FBIOGET_FSCREENINFO, &s_tFix)) {
DBG_PRINTF("can not get s_tVar\n");
return -1;
}
s_tFBDispOpr.Xres = s_tVar.xres;
s_tFBDispOpr.Yres = s_tVar.yres;
s_tFBDispOpr.Bpp = s_tVar.bits_per_pixel;
s_ScreenSize = s_tVar.xres * s_tVar.yres * s_tVar.bits_per_pixel / 8; // 屏幕总像素所占的字节数
s_LineWidth = s_tVar.xres * s_tVar.bits_per_pixel / 8; // 每行像素所占的字节数
s_PixelWidth = s_tVar.bits_per_pixel / 8; // 每个像素所占字节数
/* 直接映射到内存的Framebuffer */
s_pFbmem = (unsigned char *)mmap(NULL, s_ScreenSize, PROT_READ | PROT_WRITE, MAP_SHARED, s_FBFD, 0);
if (s_pFbmem == (unsigned char *)-1) {
DBG_PRINTF("can not mmap\n");
return -1;
}
return 0;
}
/* LCD像素显示函数 */
static int FBShowPixel(int PenX, int PenY, unsigned int Color)
{
unsigned char *pPen8;
unsigned short *pPen16;
unsigned int *pPen32;
unsigned int red, green, blue;
if ((PenX >= s_tVar.xres) || (PenY >= s_tVar.yres))
{
DBG_PRINTF("out of region\n");
return -1;
}
/* 该坐标在内存中对应像素的位置 */
pPen8 = s_pFbmem + PenY * s_LineWidth + PenX * s_PixelWidth;
pPen16 = (unsigned short *)pPen8;
pPen32 = (unsigned int *)pPen8;
switch (s_tFBDispOpr.Bpp) {
case 8:
*pPen8 = (unsigned char)Color;
break;
case 16:
/* RGB:565 */
red = ((Color >> 16) & 0xffff) >> 3;
green = ((Color >> 8 ) & 0xffff) >> 2;
blue = ((Color >> 0 ) & 0xffff) >> 3;
*pPen16 = (red << 11) | (green << 5) | blue;
break;
case 32:
*pPen32 = Color;
break;
default:
DBG_PRINTF("can not surport %d bpp\n", s_tFBDispOpr.Bpp);
return -1;
}
return 0;
}
/* 清屏函数 */
static int FBCleanScreen(unsigned int BackColor)
{
int i;
unsigned char *pPen8;
unsigned short *pPen16;
unsigned int *pPen32;
unsigned int red, green, blue;
pPen8 = s_pFbmem;
pPen16 = (unsigned short *)pPen8;
pPen32 = (unsigned int *)pPen8;
switch (s_tFBDispOpr.Bpp) {
case 8:
memset(pPen8, BackColor, s_ScreenSize);
break;
case 16:
/* RGB:565 */
red = ((BackColor >> 16) & 0xffff) >> 3;
green = ((BackColor >> 8 ) & 0xffff) >> 2;
blue = ((BackColor >> 0 ) & 0xffff) >> 3;
BackColor = (red << 11) | (green << 5) | blue;
for (i = 0; i < s_ScreenSize;) {
*pPen16 = BackColor;
pPen16++;
i += 2;
}
break;
case 32:
for (i = 0; i < s_ScreenSize;) {
*pPen32 = BackColor;
pPen32++;
i += 4;
}
break;
default:
DBG_PRINTF("can not surport %dbpp\n", s_tFBDispOpr.Bpp);
return -1;
}
return 0;
}
3.3 注册结构体
int FBInit(void)
{
return RegisterDispOpr(&s_tFBDispOpr);
}
3.4 完整文件
#include <config.h>
#include <disp_manager.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <string.h>
static int s_FBFD;
static int s_ScreenSize;
static int s_LineWidth;
static int s_PixelWidth;
static struct fb_var_screeninfo s_tVar;
static struct fb_fix_screeninfo s_tFix;
static unsigned char *s_pFbmem;
static int FBDeviceInit(void);
static int FBShowPixel(int PenX, int PenY, unsigned int Color);
static int FBCleanScreen(unsigned int BackColor);
static T_DispOpr s_tFBDispOpr = {
.name = "fb",
.DeviceInit = FBDeviceInit,
.ShowPixel = FBShowPixel,
.CleanScreen = FBCleanScreen,
};
/* LCD设备初始化函数 */
static int FBDeviceInit(void)
{
/* 打开设备:支持读写 */
s_FBFD = open(FB_DEVICE_NAME, O_RDWR);
if (s_FBFD < 0) {
DBG_PRINTF("can not open %s , err code :%d\n", FB_DEVICE_NAME, s_FBFD);
return -1;
}
/* 获得可变信息 */
if (ioctl(s_FBFD, FBIOGET_VSCREENINFO, &s_tVar)) {
DBG_PRINTF("can not get s_tVar\n");
return -1;
}
/* 获得固定信息 */
if (ioctl(s_FBFD, FBIOGET_FSCREENINFO, &s_tFix)) {
DBG_PRINTF("can not get s_tVar\n");
return -1;
}
s_tFBDispOpr.Xres = s_tVar.xres;
s_tFBDispOpr.Yres = s_tVar.yres;
s_tFBDispOpr.Bpp = s_tVar.bits_per_pixel;
s_ScreenSize = s_tVar.xres * s_tVar.yres * s_tVar.bits_per_pixel / 8; // 屏幕总像素所占的字节数
s_LineWidth = s_tVar.xres * s_tVar.bits_per_pixel / 8; // 每行像素所占的字节数
s_PixelWidth = s_tVar.bits_per_pixel / 8; // 每个像素所占字节数
/* 直接映射到内存的Framebuffer */
s_pFbmem = (unsigned char *)mmap(NULL, s_ScreenSize, PROT_READ | PROT_WRITE, MAP_SHARED, s_FBFD, 0);
if (s_pFbmem == (unsigned char *)-1) {
DBG_PRINTF("can not mmap\n");
return -1;
}
return 0;
}
/* LCD像素显示函数 */
static int FBShowPixel(int PenX, int PenY, unsigned int Color)
{
unsigned char *pPen8;
unsigned short *pPen16;
unsigned int *pPen32;
unsigned int red, green, blue;
if ((PenX >= s_tVar.xres) || (PenY >= s_tVar.yres))
{
DBG_PRINTF("out of region\n");
return -1;
}
/* 该坐标在内存中对应像素的位置 */
pPen8 = s_pFbmem + PenY * s_LineWidth + PenX * s_PixelWidth;
pPen16 = (unsigned short *)pPen8;
pPen32 = (unsigned int *)pPen8;
switch (s_tFBDispOpr.Bpp) {
case 8:
*pPen8 = (unsigned char)Color;
break;
case 16:
/* RGB:565 */
red = ((Color >> 16) & 0xffff) >> 3;
green = ((Color >> 8 ) & 0xffff) >> 2;
blue = ((Color >> 0 ) & 0xffff) >> 3;
*pPen16 = (red << 11) | (green << 5) | blue;
break;
case 32:
*pPen32 = Color;
break;
default:
DBG_PRINTF("can not surport %d bpp\n", s_tFBDispOpr.Bpp);
return -1;
}
return 0;
}
/* 清屏函数 */
static int FBCleanScreen(unsigned int BackColor)
{
int i;
unsigned char *pPen8;
unsigned short *pPen16;
unsigned int *pPen32;
unsigned int red, green, blue;
pPen8 = s_pFbmem;
pPen16 = (unsigned short *)pPen8;
pPen32 = (unsigned int *)pPen8;
switch (s_tFBDispOpr.Bpp) {
case 8:
memset(pPen8, BackColor, s_ScreenSize);
break;
case 16:
/* RGB:565 */
red = ((BackColor >> 16) & 0xffff) >> 3;
green = ((BackColor >> 8 ) & 0xffff) >> 2;
blue = ((BackColor >> 0 ) & 0xffff) >> 3;
BackColor = (red << 11) | (green << 5) | blue;
for (i = 0; i < s_ScreenSize;) {
*pPen16 = BackColor;
pPen16++;
i += 2;
}
break;
case 32:
for (i = 0; i < s_ScreenSize;) {
*pPen32 = BackColor;
pPen32++;
i += 4;
}
break;
default:
DBG_PRINTF("can not surport %dbpp\n", s_tFBDispOpr.Bpp);
return -1;
}
return 0;
}
int FBInit(void)
{
return RegisterDispOpr(&s_tFBDispOpr);
}
四、程序的运行
1、编译
执行make
命令,编译出show_file
可执行文件,把show_file、MSYH.TTF、hz.txt、utf8.txt utf16be.txt、utf16le.txt
传输到开发板的根文件系统中
2、执行
执行./show_file -s 16 -h HZK16 -f ./MSYH.TTF hz.txt
命令
./show_file
:编译出来可执行程序-s 16
:设置字体的大小为16-h HZK16
:字库文件为当前目录下的HZK16
,根据里面的信息获取点阵-f ./MSYH.TTF
:字体文件为当前目录下的MSYH.TTF
,供FreeType来设置字体大小hz.txt
:所要显示的文件为当前目录下的hz.txt
3、现象
- 在开发板上:
- 根文件系统上:
循环根据输入信息进行对应操作:
n: 显示下一页信息
u: 显示上一页信息
q: 退出程序