目录
前言
在实现LCD的程序下实现面向对象的一种编程思路
1.程序框架:
程序的框架大致如上所述:
问题1:LCD控制管理层(④层)如何去管理下面不同LCD控制器的代码呢?
答:提取出不同lcd控制器共同的特点(比如:控制器的名字、控制器初始化、控制器使能和失能),然后把其写成一个结构体封装起来,供给下一层调用(第⑤层),在(第⑤层)中也需要去分别实现(自己的控制器初始化、控制器使能和失能,例如说:s3c2440控制器、TI控制器),然后在本层(第④层)也有初始化函数,当需要使用s3c2440的控制器的时候,就把这个结构变量指向s3c2440,如果使用的Ti控制器就执行TI,这样一来,我们就可以忽略底层硬件的差异,当使用特定控制器的时候把指针变量指向特定的控制器就OK了。
在管理层(④层)的结构体如下:
在LCD控制器相关层(⑤层)生成一个结构体变量,然后实现结构体里面的函数(其实这里只有一个s3c2440的控制器)
问题2:LCD控制器管理层(第④)和LCD控制器相关层(第⑤)如何通信?
答:在第④层中创建一个结构体数组A,A数组就保存了已经就绪的LCD控制器,在第④层中提供一个注册函数,给第⑤层调用,在第⑤层中某个lcd控制器注册了以后,就会把这些控制器的信息保存在A数组中,从而实现不同层之间的通信。
在管理层中(第④)创建一个存储已经注册的LCD控制器的数组:
在管理层中(第④)也会提供一个函数供给下一层(第⑤)调用用于注册LCD控制器
在LCD控制器相关层(第⑤)把自己注册到数组里面,以便可以被调用
通过问题1和问题2就可以很清楚的知道如何通过lcd_controller.c文件是如何去管理下面的控制器了。
2.LCD管理层和控制器相关层的代码分析
使用代码更详细的说明:
在LCD控制器相关层(第⑤层)中有以下的函数:
如果还有其他的控制器的话比如:ti的lcd控制器,函数结构应该是类似的。
解析:在这一层中主要是涉及到寄存器的操作,需要初始化使用到的引脚,通过接受p_lcd_params类型变量的参数来初始化LCD显示相关的寄存器,也有LCD的使能失能函数,同时比较重要的是最后一个函数,注册函数,只有调用了注册函数,对于管理层来说这个控制器才是可见状态。
- 如果这时还有一个TI的LCD控制器的话,此时应该新建一个 ti_lcd_controller.c和ti_lcd_controller.h文件,其中里面的函数结构大致如下:
当你注册完了之后,这个LCD控制器的信息就会保存在结构体数组里面,如果你想使用哪个控制器的话可以直接使用名字来找到这个结构体:
在lcd_controller.c中这样子做:
解释:外部调用的时候,这个程序就会在已经注册的数组里面通过 name这个结构体变量去找到被选择的这个lcd控制器的信息,如果找到匹配的就把这个控制器的信息保存全局变量中,
例如:调用函数select_lcd_controller("s3c2440");的就是选择s3c2440这个控制器
那么它所调用的初始化函数就自然是s3c2440的初始化函数了,因为此时这个指针变量就指向s3c2440。
- 在lcd_controller中(第④层)有哪些函数?
其中lcd_controller.c的代码如下:
#include "lcd.h"
#include "lcd_controller.h"
#include "s3c2440_lcd_controller.h"
#include "../string_utils.h"
/*这个文件的功能用于管理不同的lcd控制器*/
//定义lcd控制器的个数,最大为5个
#define LCD_CONTROLLER_NUM 5
//保存已经注册的LCD控制器的信息
static p_lcd_controller p_array_lcd_controller[LCD_CONTROLLER_NUM];
//保存选中的lcd控制器信息的数组
static p_lcd_controller p_beselected_lcdcontroller;
//注册某款lcd控制器:供给下层的程序调用:例如2440或者 ti控制器
int register_lcd_controller(p_lcd_controller plcdcontroller)
{
/*放置到数组的哪一项呢?
*从数组的头开始扫描,如果发现哪一项是空的就直接放进去
*/
int i;
for (i=0;i<LCD_CONTROLLER_NUM;i++)
{
if(!p_array_lcd_controller[i]) /*此项没有数据*/
{
//存放
p_array_lcd_controller[i] = plcdcontroller;
return i;
}
}
return -1; //失败
}
//选择某一款LCD:
/*根据控制器的名字,在已经注册的控制器数组(LCD_CONTROLLER_NUM)
找出和所选中的控制器名字一样的具体lcd控制器的的参数。
如果在已注册的数组未找到,说明没有这款控制器
*/
int select_lcd_controller(char *name)
{
int i;
for (i=0;i<LCD_CONTROLLER_NUM;i++)
{
/*从头开始扫描数组,如果这个数组项不为空,而且控制器
的名字和我们选择的是一致的话:找到相对应的具体某款lcd控制器
*/
if(p_array_lcd_controller[i] && !strcmp(p_array_lcd_controller[i]->name,name) );
{
//找到了这个控制器的信息,存取全局变量中,以便使用
p_beselected_lcdcontroller = p_array_lcd_controller[i];
}
}
return -1; //失败
}
/*向上:接收不同LCD的参数:
例如:引脚的极性,时序,数据格式bpp,
分辨率和frambuffer的地址
向下:使用这些参数设置对应的LCD控制器
例如:s3c2440_lcd_controller
*/
int lcd_controller_init(p_lcd_params plcd_params)
{
/*调用被选中的LCD控制器初始化函数*/
if(p_beselected_lcdcontroller) //变量非空,确保已经有选中的lcd控制器才调用
{
p_beselected_lcdcontroller ->init(plcd_params);
return 0;
}
return -1;
}
/*lcd控制器使能*/
void lcd_controller_enable(void)
{
if(p_beselected_lcdcontroller) //变量非空,确保已经有选中的lcd控制器才调用
{
p_beselected_lcdcontroller->enable();
}
}
/*lcd控制器使能*/
void lcd_controller_disable(void)
{
if(p_beselected_lcdcontroller) //变量非空,确保已经有选中的lcd控制器才调用
{
p_beselected_lcdcontroller->disable();
}
}
/*lcd控制器注册函数
注意:这里注册的是:s3c2440的控制器,如果需要
调用别的控制器在这里直接修改就OK*/
void lcd_controller_add(void)
{
s3c2440_lcd_controller_add2array();
}
备注:如果此时你想使用某款LCD控制器的话,通过函数的封装,就可以只调用两个函数:
在管理层的用户就可以
3.LCD管理层和LCD屏幕相关层
他们直接的联系和上面的逻辑是一样了,lcd.c同样忽略了不同尺寸LCD的差异,在这里使用了4.2寸的LCD屏幕。
- 提取处不同LCD共同的特点
其实不同LCD区别,无非就是引脚极性,分辨率,bpp等的不同,frambuffer地址。
因此在lcd.h中新建一个结构体用于保存LCD详细的参数。
如下:
/*建立一个枚举类型,用于极性的判断*/
enum {
/* NORMAL : 正常极性:通过原理图才可知正常极性
* INVERT : 反转极性
*/
NORMAL = 0,
INVERT = 1,
};
/*创建一个结构体用于定义LCD的极性*/
typedef struct pins_polarity {
int de; /* normal: 高电平时可以传输数据 */
int pwren; /* normal: 高电平有效 */
int vclk; /* normal: 在下降沿获取数据 */
int rgb; /* normal: 高电平表示1 */
int hsync; /* normal: 高脉冲 */
int vsync; /* normal: 高脉冲 */
}pins_polarity, *p_pins_polarity;
/*LCD时序结构体*/
typedef struct time_sequence {
/* 垂直方向 */
int tvp; /* vysnc脉冲宽度 */
int tvb; /* 上边黑框, Vertical Back porch */
int tvf; /* 下边黑框, Vertical Front porch */
/* 水平方向 */
int thp; /* hsync脉冲宽度 */
int thb; /* 左边黑框, Horizontal Back porch */
int thf; /* 右边黑框, Horizontal Front porch */
int vclk;
}time_sequence, *p_time_sequence;
/*LCD参数,包括极性,时序,分辨率,bpp,frambuffer地址
* 这些参数是我们需要告诉LCD控制器的参数,用一个
* 结构体把他们保存起来。
*/
typedef struct lcd_params {
//别名
char *name;
/* 引脚极性 */
pins_polarity pins_pol;
/* 时序 */
time_sequence time_seq;
/* 分辨率, bpp */
int xres;
int yres;
int bpp;
/* framebuffer的地址 */
unsigned int fb_base;
}lcd_params, *p_lcd_params;
同样在LCD管理层中也会提供注册函数,给下一层去注册不同尺寸的LCD,同时也有选中函数,根据lcd屏幕的名字来选择不同尺寸的LCD:
在lcd中有以下函数:
- 在LCD屏幕相关层去定义一个结构体变量,设置4.3寸LCD屏幕特有的性质:
因此在lcd_4.3.c中也会提供一个注册函数,把自己注册到LCD列表里面:
调用的是上一层管理层所提供的注册函数
4.LCD的初始化
在lcd.c提供了一个初始化函数
如下:
可以直接在测试函数中去调用这个函数就能直接初始化LCD了。
5.测试LCD
步骤:
1.初始化
2.使能
/*LCD测试函数*/
void lcd_test(void)
{
unsigned int fb_base;
int xres, yres, bpp;
int x, y;
unsigned short *p;
/*初始化LCD*/
lcd_init();
/*使能LCD*/
lcd_enable();
/*获得参数*/
//这个函数获取得到的数值是在lcd_test里面使用的
get_lcd_params(&fb_base,&xres,&yres,&bpp);
//这个函数中获取的值是在画点函数中用的,
//fb_get_lcd_params();
/*获得字体需要用的LCD参数*/
//font_init();
/*判断bpp的数值*/
if (bpp == 16)
{
/* 让LCD输出整屏的红色 */
/* 565: 0xf800 */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0xf800;
/* green */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0x7e0;
/* blue */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0x1f;
/* black */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0;
}
}