图形驱动库开发之底层驱动函数的编写

       一口气转两个文档,发现对不起原著啊.必须先把人家放头位.

转自wang312的博客:点击打开链接


在上一个文档中我给大家介绍了TI图形驱动库的结构,分为三层:显示驱动层,图形基元层,小工具层。其中显示驱动层是需要我们程序员自己补充的。现在我们来具体谈谈如果来补充和书写底层驱动函数。


       从ti的文档中我们可以看出,我们需要补充的就是tDisplay这样一个结构体。这个结构体的具体定义如下:

//*****************************************************************************
//
//! This structure defines the characteristics of a display driver.
//
//*****************************************************************************
typedef struct
{
    //
    //! The size of this structure.
    //
    int lSize;

    //
    //! A pointer to display driver-specific data.
    //
    void *pvDisplayData;

    //
    //! The width of this display.
    //
    unsigned short usWidth;

    //
    //! The height of this display.
    //
    unsigned short usHeight;

    //
    //! A pointer to the function to draw a pixel on this display.
    //
    void (*pfnPixelDraw)(void *pvDisplayData, int lX, int lY,
                         unsigned int ulValue);

    //
    //! A pointer to the function to draw multiple pixels on this display.
    //
    void (*pfnPixelDrawMultiple)(void *pvDisplayData, int lX, int lY,
                                 int lX0, int lCount, int lBPP,
                                 const unsigned char *pucData,
                                 const unsigned char *pucPalette);

    //
    //! A pointer to the function to draw a horizontal line on this display.
    //
    void (*pfnLineDrawH)(void *pvDisplayData, int lX1, int lX2, int lY,
                         unsigned int ulValue);

    //
    //! A pointer to the function to draw a vertical line on this display.
    //
    void (*pfnLineDrawV)(void *pvDisplayData, int lX, int lY1, int lY2,
                         unsigned int ulValue);

    //
    //! A pointer to the function to draw a filled rectangle on this display.
    //
    void (*pfnRectFill)(void *pvDisplayData, const tRectangle *pRect,
                        unsigned int ulValue);

    //
    //! A pointer to the function to translate 24-bit RGB colors to
    //! display-specific colors.
    //
    unsigned int (*pfnColorTranslate)(void *pvDisplayData,
                                       unsigned int ulValue);

    //
    //! A pointer to the function to flush any cached drawing operations on
    //! this display.
    //
    void (*pfnFlush)(void *pvDisplayData);
}
tDisplay;


每个成员的具体含义如下:
(1)  long lSize  这是一个存储这个结构体大小的数据,通常我们直接赋值为sizeof(tDisplay).
(2)  void *pvDisplayData; 指向驱动层的数据指针。这个可以先跳过。
(3)  unsigned short usWidth; LCD的水平方向像素值。
(4)  unsigned short usHeight; LCD垂直方向像素值。
(5)  void *pvDisplayData 一个指向驱动层数据的指针,这个指针可以用作LCD显示RAM外buffer之用。但是我们没有用到这种方式,所以在结构里可以直接赋值为0.
(6)  void (*pfnPixelDrawMultiple)(void *pvDisplayData, long lX, long lY,long lX0, long lCount, long lBPP,
       const unsigned char *pucData,const unsigned char *pucPalette);

     很显然,这是一个函数指针,指向了一个绘制多像素点的函数,这个函数使用补充的调色板来绘制水平像素序列。实际是被驱动库里绘制图像的函数调用(GrimageDraw()).

(7)  void (*pfnLineDrawH)(void *pvDisplayData, long lX1, long lX2, long lY,unsigned long ulValue);
     绘制水平线的函数指针,指向的函数需要传入的是这个线的起始x,y坐标,线的终止x坐标。以及绘制的颜色(ulValue)。

(8)  void (*pfnLineDrawV)(void *pvDisplayData, long lX, long lY1, long lY2,unsigned long ulValue);
     绘制竖直线的函数指针,指向的函数需要传入的是这个线的起始x,y坐标,线的终止y坐标。以及绘制的颜色(ulValue)。
(9)  void (*pfnRectFill)(void *pvDisplayData, const tRectangle *pRect, unsigned long ulValue);
     绘制矩形填充的函数指针。指向的函数需要传入一个指向tRectangle的结构指针。同时传入这个矩形的绘制颜色ulValue.其中tRectangle的定义如下:

typedef struct
{
    //
    //! The minimum X coordinate of the rectangle.
    //
    short sXMin;

    //
    //! The minimum Y coordinate of the rectangle.
    //
    short sYMin;

    //
    //! The maximum X coordinate of the rectangle.
    //
    short sXMax;

    //
    //! The maximum Y coordinate of the rectangle.
    //
    short sYMax;
}
tRectangle;
     可以看出这个结构体里的参数就是这个矩形的四个顶点的确定,通过四个点,可以确定这个矩形的范围。
(10) unsigned long (*pfnColorTranslate)(void *pvDisplayData,unsigned long ulValue);

     这个函数指针指向的函数实现了颜色的转换,为何要转换?原因是上层API传来的颜色数据都是24-bit的RGB888,但是现在我们用的LCD用24-bit真彩的还是很少。以RGB565为多。也就是16bit位一个像素点,6.5k色。那么我们需要补充这样一个函数,可以实现颜色的转换。
(11) void (*pfnFlush)(void *pvDisplayData);

       清出缓冲区的数据。这个函数出来清除屏外buffer中的数据。

重要说明:大家可能看到了有void *pvDisplayData这个参数。这个实用于把lcd直接映射到CPU的清况。因为现在8962或6911上没有引出内部总线。不能完成LCD存储映射。所以,这个参数可以以0直接代替。



        那么以上是对tDisplay这个结构体的一个介绍。那么现在让我以LM3S8962为例子,说明具体的函数如何编写
我的lcd是SSD1963的驱动。控制和数据接口为:RS、RD、WR、CS、RST、DB0-DB18。由于8962上I/O资源有限。所以我选用了8位接口方式。我们建立320x240_lcd_ssd1963.h和320x240_lcd_ssd1963.c这个两个文件。在320x240_lcd_ssd1963.h中对硬件的接口做一个描述:

//****************************************************************************
//                              LCD接口定义
//****************************************************************************

#define  TFT_RST                GPIO_PORTB_BASE
#define  TFT_RST_PIN            GPIO_PIN_0

#define  TFT_RS                 GPIO_PORTE_BASE
#define  TFT_RS_PIN             GPIO_PIN_7

#define  TFT_CS                 GPIO_PORTE_BASE 
#define  TFT_CS_PIN             GPIO_PIN_6

#define  TFT_WR                 GPIO_PORTE_BASE
#define  TFT_WR_PIN             GPIO_PIN_5

#define  TFT_RD                 GPIO_PORTE_BASE
#define  TFT_RD_PIN             GPIO_PIN_4


#define  LCD_RST_H              //GPIOPinWrite(TFT_RST, TFT_RST_PIN, 0xff); 
#define  LCD_RST_L              //GPIOPinWrite(TFT_RST, TFT_RST_PIN, 0);
#define  LCD_RD_H               GPIOPinWrite(TFT_RD, TFT_RD_PIN, 0xff); 
#define  LCD_RD_L               GPIOPinWrite(TFT_RD, TFT_RD_PIN, 0);

#define  LCD_WR_H               GPIOPinWrite(TFT_WR, TFT_WR_PIN, 0xff); 
#define  LCD_WR_L               GPIOPinWrite(TFT_WR, TFT_WR_PIN, 0);

#define  LCD_RS_H               GPIOPinWrite(TFT_RS, TFT_RS_PIN, 0xff); 
#define  LCD_RS_L               GPIOPinWrite(TFT_RS, TFT_RS_PIN, 0);

#define  LCD_CS_H               GPIOPinWrite(TFT_CS, TFT_CS_PIN, 0xff); 
#define  LCD_CS_L               GPIOPinWrite(TFT_CS, TFT_CS_PIN, 0);

#define  TFT_DATA_PORT          GPIO_PORTD_BASE      
#define  TFT_DATA               0x000000FF


强烈建议大家做开发的时候把硬件接口的定义以宏的形式保存在头文件中,这样以后的硬 件连接变了就只需要改变宏定义就可以了。非常方便。

同时在这个文件中对我们要定义的函数做extern 声明。这要那个c文件中包含这个头文件,就可以引用这里的函数了。声明如下:

extern void SSD1963_SetBacklight(unsigned char Brightness);

extern void LCDClearScreen(unsigned long color);

extern void SetPosition(unsigned long ulX,unsigned long ulY);

extern void LCDPixelDraw(void *pvDisplayData,long lX,
                         long lY, unsigned long ulValue);

extern void LCDlineDrawH(void *pvDisplayData,long lX1, long lX2,
                   long lY, unsigned long ulValue);

extern void LCDlineDrawV(void *pvDisplayData,long lX, long lY1,
                  long lY2, unsigned long ulValue);
extern void LCDRectFill(void *pvDisplayData, const tRectangle *pRect,
                                  unsigned long ulValue);

其中SSD1963_SetBacklight和LCDClearScreen这是自己补充的一个函数,对硬件初始化的时候很用得着。SetPosition也是补充的一个函数,可以对lcd当前绘制坐标进行设置。现在我就以一个void LCDRectFill(void *pvDisplayData, const tRectangle *pRect, unsigned long ulValue);这个函数做一个说明。这个函数完成的是在lcd上以指定的颜色完成矩形填充。在SSD1963上具体驱动代码如下:

//*****************************************************************************
//画一个填充的矩形
//*****************************************************************************
void LCDRectFill(void *pvDisplayData, const tRectangle *pRect,
                                  unsigned long ulValue)
{  
   
unsigned long count;  
unsigned long pixelsize=((pRect->sXMax)-(pRect->sXMin)+1)\
                        *((pRect->sYMax)-(pRect->sYMin)+1);



//设置x坐标 
LCD_WRITE_CMD(0x2A);        
        LCD_WRITE_DATA(((pRect->sXMin)>>8)&0xff);            
        LCD_WRITE_DATA(pRect->sXMin);
        LCD_WRITE_DATA(((pRect->sXMax)>>8)&0xff);            
        LCD_WRITE_DATA(pRect->sXMax);

//设置y坐标 
LCD_WRITE_CMD(0x2b);        
        LCD_WRITE_DATA(((pRect->sYMin)>>8)&0xff);            
        LCD_WRITE_DATA(pRect->sYMin);
        LCD_WRITE_DATA(((pRect->sYMax)>>8)&0xff);            
        LCD_WRITE_DATA(pRect->sYMax);  

   LCD_WRITE_CMD(0x2c); //准备写数据  

   for(count=0;count<pixelsize;count++)
  {
  
     LCD_WRITE_COLOR(ulValue);
  
  }
}

可以看出这就是一个确定要写多少个像素点,在LCD上设置起始坐标,并做一个循环在lcd上把像素点打出来的过程。还是很容易理解的。
同时我还要向大家说明一下unsigned long (*pfnColorTranslate)(void *pvDisplayData,unsigned long ulValue);
这个函数。在我这个LCD上是完成RGB888到RGB66的转换,函数体如下:

unsigned long LCDColorTranslate(void *pvDisplayData, unsigned long ulValue)
{
    //
    // Translate from a 24-bit RGB color to a 5-6-5 RGB color.
    //
    return(DPYCOLORTRANSLATE(ulValue));
}
而DPYCOLORTRANSLATE是一个宏。原型如下:
//****************************************************************************
//              传输颜色数据到BUFFER 24bit to 18 bit
//****************************************************************************
#define DPYCOLORTRANSLATE(c)    ((((c) & 0x00fc0000) >> 6) |               \
                                 (((c) & 0x0000fc00) >> 4) |               \
                                 (((c) & 0x000000fc) >> 2))
可以看到通过这个宏,就可以实现RGB888到RGB66的转换。其他的部分直接依此编写就好。当大家完成了这些驱动函数的编写,那么你离使用驱动库还有一步之遥。就是把这些函数的指针添加到tDisplay这样的结构体来。那么我自己命名了一个MyDisplay这个结构体,并以函数指针和其他的一些参数做为填充。如下:

const tDisplay MyDisplay=
{
sizeof(tDisplay),
0,
320,       // LCD width
240,       // LCD height
LCDPixelDraw,//画点函数
LCDPixelDrawMultiple, //像素点子像素调色函数
LCDlineDrawH,
LCDlineDrawV,
LCDRectFill,
LCDColorTranslate,
LCDFlush,
};

只要补充好这个结构体,恭喜你,你已经可以使用TI的图形驱动库了。在下个文档里。我会向大家讲述如何用ti的库在lcd上显示基本的图形和字符。同时教会大家用ti提供的图形工具来生成自己的字库和图像。

//****************************************************************************
//              传输颜色数据到BUFFER 24bit to 18 bit
//****************************************************************************
#define DPYCOLORTRANSLATE(c)    ((((c) & 0x00fc0000) >> 6) |               \
                                 (((c) & 0x0000fc00) >> 4) |               \
                                 (((c) & 0x000000fc) >> 2))
const tDisplay MyDisplay=
{
sizeof(tDisplay),
0,
320,       // LCD width
240,       // LCD height
LCDPixelDraw,//画点函数
LCDPixelDrawMultiple, //像素点子像素调色函数
LCDlineDrawH,
LCDlineDrawV,
LCDRectFill,
LCDColorTranslate,
LCDFlush,
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程解决的问题: 作为游戏行业或者图形学从业者,你是否面临以下问题: 到底openGL底层如何实现的? 到底矩阵操作变换是怎么做到的? 到底光栅化的算法以及原理是什么? 到底如何才能从3D世界投射到2D屏幕呢? 图形学有这么多的矩阵操作,到底如何推导如何应用呢? 学完这门课程,你应该就可以从底层了解一个初级的openGL图形接口如何实现,图形学最底层的封装到底面临哪些挑战;跟随我们一行一行写完代码,你就会得到一个迷你版本的openGL图形,你可以深度体会图形从模型变换,观察矩阵变换,投影矩阵变换一直到光栅化纹理操作的全套模拟流程。 课程介绍: 本课程将带领学员不使用任何图形,实现从0到1的图形学接口封装以及算法讲解,并且带领大家手敲代码,一行一行进行实现。 涵盖了(环境搭建,绘制点,Bresenham算法绘制完美直线,三角形拆分绘制算法,颜色插值算法,图片操作,图片二次插值放缩算法,纹理系统接口搭建及封装,矩阵操作理论以及实践,openGL类似接口封装,3D世界的图形学理论及接口封装等) 最终将带领大家通过C++实现一个3D世界的图形接口,方便所有人入门图形学,进行接下来的openGL接口以及GPU编程的学习   本课程为系列课程的第一步入门,且带领所有人进行实现,更加实用,可以让大家打牢图形学的基础知识及编程技能

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值