1. 程式人生 > >數碼相框(三)電子書

數碼相框(三)電子書

一、整體思路

學習框架,就是程式設計的思想。架構很重要。採用分層的思想,面向物件的程式設計思想。

面向物件的程式設計的主要思想是把構成問題的各個事物分解成各個物件,建立物件的目的不是為了完成一個步驟,而是為了描述一個事物在解決問題的過程中經歷的步驟和行為。物件作為程式的基本單位,將程式和資料封裝其中,以提高程式的重用性,靈活性和可擴充套件性。類是建立物件的模板,一個類可以建立多個物件。物件是類的例項化。


類是 抽象的,不佔用儲存空間;而物件是具體的,佔用儲存空間。


面向物件有三大特性:封裝,繼承,多型

1、怎樣在LCD上顯示一個檔案

2、如何組織程式碼

分層的結構

main --- draw --- XXX_manager --- fb.c等

面向物件的思想編寫程式(模組化
1. 分配一個結構體
2. 設定結構體
3. 註冊結構體

二、分層編寫-底層實現

1、顯示部分程式碼編寫

show_file\display\fb.c  
show_file\display\disp_manager.c  
show_file\include\config.h  
show_file\include\disp_manager.h  
show_file\draw\draw.c  

先寫fb.c ——面向物件程式設計思想

設定構造一個結構體結構體成員:

顯示部分,一定有一個顯示函式,FB要顯示就要初始化,所以有一個fb初始化函式,換頁的時候我們要進行清屏操作,要有一個清屏函式,所以要有3個函式。

(1) fb初始化

(2) 顯示函式

(3) 清屏函式

只有函式還不全,還需要一些屬性,如定義一個名字“fb”,X座標,Y座標,多少位表示一個畫素等屬性。

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 dwBackColor);
	struct DispOpr *ptNext;
}T_DispOpr, *PT_DispOpr;

fb.c初始化

結構體,並給出對應的函式

static T_DispOpr g_tFBOpr = {
	.name        = "fb",
	.DeviceInit  = FBDeviceInit,
	.ShowPixel   = FBShowPixel,
	.CleanScreen = FBCleanScreen,
};

結構體用到這些函式,需要事先宣告,都是static所以只能在本檔案中使用,外面想使用只能通過上一層。

static int FBDeviceInit(void);
static int FBShowPixel(int iX, int iY, unsigned int dwColor);
static int FBCleanScreen(unsigned int dwBackColor);

在哪註冊?

int FBInit(void)
{
	return RegisterDispOpr(&g_tFBOpr);
}

這裡就不能寫成static了。註冊就是把結構體加入連結串列,連結串列比陣列更靈活,大小隨意。一開始連結串列頭是空的,所以讓指標指向這個結構體。註冊就讓next指向新結構體再

實現三個函式:注意這裡是應用程式,所以是一些open、read等函式。如g_fd = open(FB_DEVICE_NAME, O_RDWR);

這裡不應該把裝置名字定死,所以寫一個config檔案

#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 */

DBG_PRINT:用於控制開關列印

顯示做好後需要考慮顯示的內容,但是現實內容在哪裡呢?需要設定顯示字型,得到顯示的點陣。如顯示ASCII還是GBK,還是freetype。同理寫出這部分程式碼

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 <sys/mman.h>
#include <linux/fb.h>
#include <string.h>
 
static int FBDeviceInit(void);
static int FBShowPixel(int iX, int iY, unsigned int dwColor);
static int FBCleanScreen(unsigned int dwBackColor);
 
 
static int g_fd;
 
static struct fb_var_screeninfo g_tFBVar;  //可變引數
static struct fb_fix_screeninfo g_tFBFix;	 //固定引數
static unsigned char *g_pucFBMem;          //記憶體對映
static unsigned int g_dwScreenSize;        //FB螢幕大小
 
static unsigned int g_dwLineWidth;         //一行寬度:一行的畫素*畫素位數/8位元組    
static unsigned int g_dwPixelWidth;        //每個畫素佔據多少位元組:BPP/8=2位元組(一個畫素佔據2位元組)
 
static T_DispOpr g_tFBOpr = {
	.name        = "fb",
	.DeviceInit  = FBDeviceInit,
	.ShowPixel   = FBShowPixel,
	.CleanScreen = FBCleanScreen,
};
 
static int FBDeviceInit(void)
{
	int ret;
	
	g_fd = open(FB_DEVICE_NAME, O_RDWR);
	if (0 > g_fd)
	{
		DBG_PRINTF("can't open %s\n", FB_DEVICE_NAME);
	}
 
	ret = ioctl(g_fd, FBIOGET_VSCREENINFO, &g_tFBVar);
	if (ret < 0)
	{
		DBG_PRINTF("can't get fb's var\n");
		return -1;
	}
 
	ret = ioctl(g_fd, FBIOGET_FSCREENINFO, &g_tFBFix);
	if (ret < 0)
	{
		DBG_PRINTF("can't get fb's fix\n");
		return -1;
	}
	
	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 (0 > g_pucFBMem)	
	{
		DBG_PRINTF("can't mmap\n");
		return -1;
	}
 
	g_tFBOpr.iXres       = g_tFBVar.xres;
	g_tFBOpr.iYres       = g_tFBVar.yres;
	g_tFBOpr.iBpp        = g_tFBVar.bits_per_pixel;  //設定座標和多少位表示一個畫素
 
	g_dwLineWidth  = g_tFBVar.xres * g_tFBVar.bits_per_pixel / 8;
	g_dwPixelWidth = g_tFBVar.bits_per_pixel / 8;
	
	return 0;
}
 
//RRGGBB  16: 24轉成565
static int FBShowPixel(int iX, int iY, unsigned int dwColor)
{
	unsigned char *pucFB;
	unsigned short *pwFB16bpp;
	unsigned int *pdwFB32bpp;
	unsigned short wColor16bpp; /* 565 */
	int iRed;
	int iGreen;
	int iBlue;
 
	if ((iX >= g_tFBVar.xres) || (iY >= g_tFBVar.yres))
	{
		DBG_PRINTF("out of region\n");
		return -1;
	}
 
	pucFB      = g_pucFBMem + g_dwLineWidth * iY + g_dwPixelWidth * iX;
	pwFB16bpp  = (unsigned short *)pucFB;
	pdwFB32bpp = (unsigned int *)pucFB;
	
	switch (g_tFBVar.bits_per_pixel)
	{
		case 8:
		{
			*pucFB = (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;
			*pwFB16bpp	= wColor16bpp;
			break;
		}
		case 32:
		{
			*pdwFB32bpp = dwColor;
			break;
		}
		default :
		{
			DBG_PRINTF("can't support %d bpp\n", g_tFBVar.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; /* 565 */
	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);
}

disp_anager.c:重點是連結串列操作

#include <config.h>
#include <disp_manager.h>
#include <string.h>
 
static PT_DispOpr g_ptDispOprHead;
 
int RegisterDispOpr(PT_DispOpr ptDispOpr)
{
	PT_DispOpr ptTmp;
 
	if (!g_ptDispOprHead)
	{
		g_ptDispOprHead   = ptDispOpr;
		ptDispOpr->ptNext = NULL;
	}
	else
	{
		ptTmp = g_ptDispOprHead;
		while (ptTmp->ptNext)
		{
			ptTmp = ptTmp->ptNext;
		}
		ptTmp->ptNext	  = ptDispOpr;
		ptDispOpr->ptNext = 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;
	}
}
 
PT_DispOpr GetDispOpr(char *pcName)
{
	PT_DispOpr ptTmp = g_ptDispOprHead;
	
	while (ptTmp)
	{
		if (strcmp(ptTmp->name, pcName) == 0)
		{
			return ptTmp;
		}
		ptTmp = ptTmp->ptNext;
	}
	return NULL;
}
 
int DisplayInit(void)
{
	int iError;
	
	iError = FBInit();
 
	return iError;
}

2、字型部分編寫

思路如上一樣,分配、設定、註冊結構體。

3、編碼部分編寫

思路如上一樣,分配、設定、註冊結構體。

三、分層編寫-上層實現

1、draw.c

組織底層程式碼

2、main.c

解析命令 -- 開啟文字 -- 顯示文字 -- 換頁 -- 返回上一頁等

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <config.h>
#include <draw.h>
#include <encoding_manager.h>
#include <fonts_manager.h>
#include <disp_manager.h>
#include <string.h>
 
 
/* ./show_file [-s Size] [-f freetype_font_file] [-h HZK] <text_file> */
int main(int argc, char **argv)
{
	int iError;
	unsigned int dwFontSize = 16;
	char acHzkFile[128];
	char acFreetypeFile[128];
	char acTextFile[128];
 
	char acDisplay[128];
 
	char cOpr;
	int bList = 0;
 
	acHzkFile[0]  = '\0';
	acFreetypeFile[0] = '\0';
	acTextFile[0] = '\0';
 
	strcpy(acDisplay, "fb");
	
	while ((iError = getopt(argc, argv, "ls:f:h:d:")) != -1)
	{
		switch(iError)
		{
			case 'l':
			{
				  bList = 1;
				  break;
			}
			case 's':
			{
				  dwFontSize = strtoul(optarg, NULL, 0);
				  break;
			}
			case 'f':
			{
				  strncpy(acFreetypeFile, optarg, 128);
				  acFreetypeFile[127] = '\0';
				  break;
			}			
			case 'h':
			{
					strncpy(acHzkFile, optarg, 128);
					acHzkFile[127] = '\0';
					break;
			}
			case 'd':
			{
				strncpy(acDisplay, optarg, 128);
				acDisplay[127] = '\0';
				break;
			}
			default:
			{
					printf("Usage: %s [-s Size] [-d display] [-f font_file] [-h HZK] <text_file>\n", argv[0]);
					printf("Usage: %s -l\n", argv[0]);
					return -1;
					break;
			}
		}
	}
 
	if (!bList && (optind >= argc))
	{
		printf("Usage: %s [-s Size] [-d display] [-f font_file] [-h HZK] <text_file>\n", argv[0]);
		printf("Usage: %s -l\n", argv[0]);
		return -1;
	}
		
	iError = DisplayInit();
	if (iError)
	{
		printf("DisplayInit error!\n");
		return -1;
	}
 
	iError = FontsInit();
	if (iError)
	{
		printf("FontsInit error!\n");
		return -1;
	}
 
	iError = EncodingInit();
	if (iError)
	{
		printf("EncodingInit error!\n");
		return -1;
	}
 
	if (bList)
	{
		printf("supported display:\n");
		ShowDispOpr();
 
		printf("supported font:\n");
		ShowFontOpr();
 
		printf("supported encoding:\n");
		ShowEncodingOpr();
		return 0;
	}
 
	strncpy(acTextFile, argv[optind], 128);
	acTextFile[127] = '\0';
		
	iError = OpenTextFile(acTextFile);
	if (iError)
	{
		printf("OpenTextFile error!\n");
		return -1;
	}
 
	iError = SetTextDetail(acHzkFile, acFreetypeFile, dwFontSize);
	if (iError)
	{
		printf("SetTextDetail error!\n");
		return -1;
	}
 
	DBG_PRINTF("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
 
	iError = SelectAndInitDisplay(acDisplay);
	if (iError)
	{
		printf("SelectAndInitDisplay error!\n");
		return -1;
	}
	
	DBG_PRINTF("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	iError = ShowNextPage();
	DBG_PRINTF("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	if (iError)
	{
		printf("Error to show first page\n");
		return -1;
	}
 
	while (1)
	{
		printf("Enter 'n' to show next page, 'u' to show previous page, 'q' to exit: ");
 
		do {
			cOpr = getchar();			
		} while ((cOpr != 'n') && (cOpr != 'u') && (cOpr != 'q'));
 
		if (cOpr == 'n')
		{
			ShowNextPage();
		}
		else if (cOpr == 'u')
		{
			ShowPrePage();			
		}
		else 
		{
			return 0;
		}			
	}
	return 0;
}

(1)解析命令引數

getopt(argc, argv, "ls:f:h:d:")
:需要帶引數
-l 不需要帶引數
strncpy(acTextFile, argv[optind], 128);得到檔名

(2) 初始化顯示、字型、編碼--》註冊相關結構體

	iError = DisplayInit();
	iError = FontsInit();
	iError = EncodingInit();

(3)開啟檔案

OpenTextFile(acTextFile)

(4)開啟字型檔案

SetTextDetail(acHzkFile, acFreetypeFile, dwFontSize)

(5)根據名字選擇初始化一個顯示器

SelectAndInitDisplay(acDisplay)

(6)顯示第一頁或者下一頁

ShowNextPage()

(7)等待命令引數,顯示下一頁,上一頁等