s3c2440裸机-LCD编程-3-框架准备和LCD初始化

2024-07-04  

1.准备框架

为了让程序更加好扩展,体现出”高内聚、低耦合"的特点,能够兼容各种不同型号的lcd,假如有两款尺寸大小的lcd,如何快速的在两个lcd上切换?

首先我们抽象出lcd_3.5.c和lcd_4.3.c的共同点,比如都有初始化函数init(),我们可以新建一个lcd.c,然后定义一个结构体:

struct lcd_opr{
    void (*init)(void);
};

用户不接触lcd_3.5.c和lcd_4.3.c,只需要在lcd.c里通过指针访问对应的结构体的函数,也就调用了不同init(),如下图所示:



我们的目的是在LCD显示屏上画线、画圆(geomentry.c)和写字(font.c)其核心是画点(farmebuffer.c),这些都属于纯软件。此外还需要一个lcd_test.c测试程序提供操作菜单,调用画线、画圆和写字操作。

往下操作的是LCD相关的内容,不同的LCD,其配置的参数也会不一样,通过lcd_3.5.c或lcd_4.3.c来设置属性参数。

根据LCD的特性,来设置LCD控制器,首先编写lcd_controller.c,它向上要接收不同LCD的参数,向下要使用这些参数设置对应具体的某一款LCD控制器。

对于我们开发板,就是s3c2440_lcd_controller.c,假如希望在其它开发板上也实现LCD显示,只需添加相应的代码文件即可。文件自上而下的框架如下:


1)构造LCD结构属性

我们知道LCD的参数属性有:引脚的极性、时序、数据的格式bpp、分辨率等,使用面向对象的思维方式,将这些封装成结构体放在lcd.h中:


enum {

    NORMAL = 0,

    INVERT = 1,

};


/* NORMAL : 正常极性

 * INVERT : 反转极性

 */

typedef struct pins_polarity {

    int vclk;  /* normal: 在下降沿获取数据 */

    int rgb;   /* normal: 高电平表示1 */

    int hsync; /* normal: 高脉冲 */

    int vsync; /* normal: 高脉冲 */

}pins_polarity, *p_pins_polarity;


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;



typedef struct lcd_params {

    /* 引脚极性 */

    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;

2)构造LCD行为方法

我们知道在c++中是面向对象编程的,那么一个对象就有它的属性和方法,LCD属性我们上面已经定义好了,那么方法我们可以定义一个lcd_controller.c用来控制管理LCD,定义个一个lcd_controller.h, struct lcd_controller结构体放置lcd对象的一些成员函数,即对象的方法,或者称之为对象的行为:


typedef struct lcd_controller {

    char *name;

    void (*init)(p_lcd_params plcdparams);

    void (*enable)(void);

    void (*disable)(void);

    void (*init_palette)(void);

}lcd_controller, *p_lcd_controller;

那么lcd_controller.c相当于一个管理者,会去选择具体型号的LCD对象去执行具体的成员函数,比如管理s3c2440_lcd_controller.c,它向上接受传入的LCD参数,向下传给具体的LCD控制器。


void lcd_controller_init(p_lcd_params plcdparams)

{

    /* 调用2440的LCD控制器的初始化函数,lcd_controller是一个被选中的对象,即s3c2440_lcd_controller*/

    lcd_controller.init(plcdparams);

}

这样在s3c2440_lcd_controller.c再构造一个具体的lcd对象:


struct lcd_controller s3c2440_lcd_controller = {

    .name    = xxx,

    .init    = xxx,

    .enalbe  = xxx,

    .disable = xxx,

};

lcd_controller.c代码框架如下:


#include "lcd_controller.h"

#define LCD_CONTROLLER_NUM 10


static p_lcd_controller p_array_lcd_controller[LCD_CONTROLLER_NUM];

static p_lcd_controller g_p_lcd_controller_selected;


int register_lcd_controller(p_lcd_controller plcdcon)

{

    int i;

    for (i = 0; i < LCD_CONTROLLER_NUM; i++)

    {

        if (!p_array_lcd_controller[i])

        {

            p_array_lcd_controller[i] = plcdcon;

            return i;

        }

    }

    return -1;      

}


int select_lcd_controller(char *name)

{

    int i;

    for (i = 0; i < LCD_CONTROLLER_NUM; i++)

    {

        if (p_array_lcd_controller[i] && !strcmp(p_array_lcd_controller[i]->name, name))

        {

            g_p_lcd_controller_selected = p_array_lcd_controller[i];

            return i;

        }

    }

    return -1;      

}



/* 向上: 接收不同LCD的参数

 * 向下: 使用这些参数设置对应的LCD控制器

 */

int lcd_controller_init(p_lcd_params plcdparams)

{

    /* 调用所选择的LCD控制器的初始化函数 */

    if (g_p_lcd_controller_selected)

    {

        g_p_lcd_controller_selected->init(plcdparams);

        return 0;

    }

    return -1;

}


void lcd_controller_enable(void)

{

    if (g_p_lcd_controller_selected)

    {

        g_p_lcd_controller_selected->enable();

    }

}


void lcd_controller_disable(void)

{

    if (g_p_lcd_controller_selected)

    {

        g_p_lcd_controller_selected->disable();

    }

}

下面详细分析lcd_controller.c框架的含义以及作用:


1.开始定义了一个p_array_lcd_controller数组和g_p_lcd_controller_selected,p_array_lcd_controller数组表示lcd控制器的集合,g_p_lcd_controller_selected表示被选中的那一个lcd_controller;


2.当我们初始化时要先调用register_lcd_controller,select_lcd_controller选中具体的lcd_controller;


3.然后才能调用lcd_controller_init初始化具体的lcd_controller,去控制具体型号的lcd。


同理,也通过lcd.c去管理lcd_4.3.c,思路如下:


a. 有一个数组存放各类lcd的参数;

b. 有一个register_lcd给下面的lcd程序来设置数组;

c. 有一个select_lcd,供上层选择某款LCD;

参考前面的lcd_controller.c编辑lcd.c如下:


#define LCD_NUM 10

static p_lcd_params p_array_lcd[LCD_NUM];

static p_lcd_params g_p_lcd_selected;


int register_lcd(p_lcd_params plcd)

{

    int i;

    for (i = 0; i < LCD_NUM; i++)

    {

        if (!p_array_lcd[i])

        {

            p_array_lcd[i] = plcd;

            return i;

        }

    }

    return -1;      

}


int select_lcd(char *name)

{

    int i;

    for (i = 0; i < LCD_NUM; i++)

    {

        if (p_array_lcd[i] && !strcmp(p_array_lcd[i]->name, name))

        {

            g_p_lcd_selected = p_array_lcd[i];

            return i;

        }

    }

    return -1;      

}


void get_lcd_params(unsigned int *fb_base, int *xres, int *yres, int *bpp)

{

    *fb_base = g_p_lcd_selected->fb_base;

    *xres = g_p_lcd_selected->xres;

    *yres = g_p_lcd_selected->yres;

    *bpp = g_p_lcd_selected->bpp;

}


2. LCD初始化

1)初始化lcd控制器

①初始化引脚: 我们配置LCD的背光引脚成输出模式:



GPBCON &= ~0x3;
GPBCON |= 0x01;

然后再配置LCD的控制引脚和数据引脚,LCD控制引脚和数据引脚分别复用了GPC和GPD,如下图所示:



设置GPC, GPD均为0xaaaa,aaaa。

/* LCD专用引脚 */
GPCCON = 0xaaaaaaaa;
GPDCON = 0xaaaaaaaa;

设置GPG4成PWREN引脚


GPGCON |= (3<<8);

②初始化LCD控制寄存器、地址寄存器:


按照上一节s3c2440裸机-LCD编程(二、LCD控制器)中的介绍去设置LCDCON1,LCDCON2,LCDCON3...LCDSADDR1等寄存器,代码如下:


void s3c2440_lcd_controller_init(p_lcd_params plcdparams)

{

    /* [17:8]: CLKVAL, vclk = HCLK / [(CLKVAL+1) x 2]

     *                   如:9   = 100M /[(CLKVAL+1) x 2], 所以CLKVAL = 4.5 = 5

     *                 CLKVAL = 100/vclk/2-1

     * [6:5]: 0b11, tft lcd

     * [4:1]: bpp mode

     * [0]  : LCD video output and the logic enable/disable

     */

    int clkval = (double)HCLK/plcdparams->time_seq.vclk/2-1+0.5;

    int bppmode = plcdparams->bpp == 8  ? 0xb :

                  plcdparams->bpp == 16 ? 0xc :

                  0xd;  /* 0xd: 24bpp */

    LCDCON1 = (clkval<<8) | (3<<5) | (bppmode<<1) ;


    /* [31:24] : VBPD    = tvb - 1

     * [23:14] : LINEVAL = line - 1

     * [13:6]  : VFPD    = tvf - 1

     * [5:0]   : VSPW    = tvp - 1

     */

    LCDCON2 =   ((plcdparams->time_seq.tvb - 1)<<24) |

                ((plcdparams->yres - 1)<<14)         |

                ((plcdparams->time_seq.tvf - 1)<<6)  |

                ((plcdparams->time_seq.tvp - 1)<<0);


    /* [25:19] : HBPD    = thb - 1

     * [18:8]  : HOZVAL  = 列 - 1

     * [7:0]   : HFPD    = thf - 1

     */

    LCDCON3 =   ((plcdparams->time_seq.thb - 1)<<19) |

                ((plcdparams->xres - 1)<<8)           |

                ((plcdparams->time_seq.thf - 1)<<0);


    /* 

     * [7:0]   : HSPW    = thp - 1

     */

    LCDCON4 =   ((plcdparams->time_seq.thp - 1)<<0);


    /* 用来设置引脚极性, 设置16bpp, 设置内存中象素存放的格式

     * [12] : BPP24BL

     * [11] : FRM565, 1-565

     * [10] : INVVCLK, 0 = The video data is fetched at VCLK falling edge

     * [9]  : HSYNC是否反转

     * [8]  : VSYNC是否反转

     * [7]  : INVVD, rgb是否反转

     * [6]  : INVVDEN

     * [5]  : INVPWREN

     * [4]  : INVLEND

     * [3]  : PWREN, LCD_PWREN output signal enable/disable

     * [2]  : ENLEND

     * [1]  : BSWP

     * [0]  : HWSWP

     */


    pixelplace = plcdparams->bpp == 24 ? (0) : |

                 plcdparams->bpp == 16 ? (1) : |

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。