搜索
bottom↓
回复: 4

基于TI图形驱动库的嵌入式图形界面设计

[复制链接]

出0入0汤圆

发表于 2011-9-17 11:35:05 | 显示全部楼层 |阅读模式
基于TI图形驱动库的嵌入式图形界面设计
摘要:为加快嵌入式图形界面开发,TI在Stellaris平台(基于arm cortex m3内核)上推出了图形驱动库(Graphics Library)。本文在介绍TI图形驱动库特点的基础上给出了基于SSD1963 LCD控制器和TSC2046 触摸屏控制器在LM3S6911上的移植实例。通过分析图形驱动库的运行机制,给出了一种在Stellaris平台上使用TI图形驱动库进行嵌入式图形界面设计的通用方法。
关键字:嵌入式图形界面,图形驱动库,Stellaris,arm cortex m3

embedded Graphical Interface Design based on TI's Graphics Library

abstract:

1.        引言
  当前,嵌入式系统正向智能化,人性化方向发展,一个界面友好,简单易用的图形用户界面已经成为很多嵌入式系统必不可少的重要组成部分。但是图形界面的设计往往要花费设计者大量的时间和精力,对产品尽快的推向市场造成一定的阻力。所以现在很多半导体厂商在推出一款处理器时,也会推出和处理器配套的软件支持。Stellaris是TI收购Luminary Micro后推出的基于arm cortex m3内核的一系列MCU产品。为简化设计者使用Stellaris设计产品的时间。TI推出了一系列的驱动库。其中就包括图形驱动库(Graphics Library)。使用TI提供的图形驱动,设计者可以快速的设计出一个界面友好,简单易用的图形用户界面。TI提供的图形驱动库除了必须用汇编编写的地方外,所有代码完全用C编写,容易理解,方便移植到任何Stellaris处理器上。并可以支持任何大小的图形显示器。它给使用图形LCD的应用程序提供了独立于硬件的图形用户接口。开发人员只需要补充底层硬件驱动程序,就可以让图形化的应用程序在Stellaris上运行起来。
2 图形驱动库架构
从软件的角度来看,TI提供的图形驱动库采用了分层的设计方法,分别为:显示驱动层,图形基元层,控件层,越上层的调用均是建立在下层功能的基础之上以实现更为复杂、强大的功能。每一层在应用中可以单独调用。

图形驱动库三层架构
2.1 显示驱动层
显示驱动层(Display Driver layer)直接和硬件相关,设计者必须跟具自己硬件的实际情况编写层驱动函数,可实现在显示器上画点,画线,画填充矩形等操作。为结构化底层驱动程序,TI提供了tDisplayer这个结构体。设计者只需要补充这个结构体就可以完成显示驱动层的补充工作。
2.2图形基元层
   图形基元层(Graphics Primitives layer)提供了一系列低级的绘制操作,其中包括了画线,画圆,画文本,和画位图图像。同时在这一层也提供了一种在屏外缓冲区(off-screen buffer)绘制的方法,允许先把图像数据绘制到屏外缓冲区,最后一次性的更新到LCD显示器上。
2.3控件层
控件(Widget)是一种集成许多图形基元的一个实体。它用来响应使用者的输入。一个典型的控件就是屏上一个按钮。当它被按下的时候可以执行一个设计者定义的动作。控件层(Widget Framework layer)提供了一种可以处理控件消息的方法。每一个控件都有其消息的处理者来响应消息。如WIDGET_MSG_PAINT这个消息可请求控件把自身绘制到显示屏上。
  控件层被组织成了一个树型的结构,控件可以从这个树中被动态的添加和移出。控件可以在程序运行时使用各种功能函数创建,也可以在编译时使用全局结构体来定义。

3图形驱动库驱动程序开发
3.1显示驱动的编写
TI的图形驱动库为设计者提供了tDisplay这样一个结构体。其定义如下:typedef struct
{
//这个结构体的大小
    long lSize;
//指向驱动层显示数据指针
void *pvDisplayData;
//显示屏宽度(以像素点为单位)
unsigned short usWidth;
//显示屏高度(以像素点为单位)
unsigned short usHeight;
//指向在屏上画点的函数指针
    void (*pfnPixelDraw)(void *pvDisplayData, long lX, long lY,
                         unsigned long ulValue);
//指向在屏上画序列点的函数指针
    void (*pfnPixelDrawMultiple)(void *pvDisplayData, long lX, long lY,
                                 long lX0, long lCount, long lBPP,
                                 const unsigned char *pucData,
                                 const unsigned char *pucPalette);
//指向在屏上画水平线的函数指针
     void (*pfnLineDrawH)(void *pvDisplayData, long lX1, long lX2, long lY,  unsigned long ulValue);
//指向在屏上画竖直线的函数指针
    void (*pfnLineDrawV)(void *pvDisplayData, long lX, long lY1, long lY2, unsigned long ulValue);
//指向在屏上画填充矩形的函数指针
     void (*pfnRectFill)(void *pvDisplayData, const tRectangle *pRect,
                        unsigned long ulValue);
//指向从RGB888向显示屏指定的颜色格式转换的函数指针
     unsigned long (*pfnColorTranslate)(void *pvDisplayData,
                                       unsigned long ulValue);
//指向更新缓冲区数据到显示屏的函数指针。
     void (*pfnFlush)(void *pvDisplayData);
}
tDisplay;
可见,设计者需要根据自己的硬件完在上述函数。然后传递函数指针到这个结构体里。
3.1.1  LCD控制器和MCU硬件连接
笔者使用的就是SSD1963来驱动一块3.5英寸的LCD显示器。SSD1963是一个自带1215KB帧缓冲的LCD控制器,支持864X480,24bpp的图形显示内容。可以以不同的总宽度来和MCU通信。在这里我们选择8位总线宽度。MCU选用LM3S6911.一款运行频率可达50M,带以太网控制器的Stellaris处理器。MCU和LCD控制器的连接如下:



3.1.2 LCD驱动函数的编写
为加强代码的可读性,和在工程中的可移植性。笔者建立了320x240_lcd_ssd1963.h和320x240_lcd_ssd1963.c这个两个文件。
在320x240_lcd_ssd1963.c 这个文件中主要完成画点,画线,画填充矩形等函数。并以这些函数补充了tDisplay这个结构体。驱动库所有的绘制工作最终都以tDisplay类型的结构体中的函数来实现。由于这些函数又都可以通调用画点的函数来实现。所以笔者在此重点讲述画点函数的实现。其代码如下:
void LCDPixelDraw(void *pvDisplayData,long lX,long lY, unsigned long ulValue)
{
//设置x坐标
LCD_WRITE_CMD(0x2A);       
        LCD_WRITE_DATA((lX>>8)&0xff);            
        LCD_WRITE_DATA(lX);
        LCD_WRITE_DATA(((lX)>>8)&0xff);            
        LCD_WRITE_DATA(lX);
//设置y坐标
LCD_WRITE_CMD(0x2b);       
        LCD_WRITE_DATA((lY>>8)&0xff);            
        LCD_WRITE_DATA(lY);
        LCD_WRITE_DATA((lY>>8)&0xff);            
        LCD_WRITE_DATA(lY);              
LCD_WRITE_CMD(0x2c); //准备写数据   
LCD_WRITE_COLOR(ulValue);//写入颜色数据
}
pvDisplayData是一个指向显示buffer的指针,于由我们是直接对LCD屏进行操作,故没有使用,参数lX,lY分别为要绘制的像素点在屏上的x坐标和y坐标。ulValue为要写入的颜色数据。这个函数便实现了在以(lX,lY)为坐标的LCD屏上显示一个颜色为ulValue的像素点。
其中LCD_WRITE_CMD,LCD_WRITE_DATA,LCD_WRITE_COLOR分别为写命令,写数据,和写颜色数据函数。为加快LCD耍屏速度,这三个函数都被优化成宏,内联到代码之中。例如LCD_WRITE_CMD的定义如下:
//写命令
#define LCD_WRITE_CMD(c)             \
{                                    \
  HWREG(RS_ADDR)=0x00;              \                     
  HWREG(DATA_ADDR)=c;               \
HWREG(WR_ADDR)=0x00;             \                                 
  HWREG(WR_ADDR)=0xff;              \
}
完成驱动函数的编写后,我们需要定义一个tDisplay类型的结构体并跟具硬件的实际情况和做好了的绘制函数来补充这个结构体。如下:
const tDisplay MyDisplay=
{
.lSize=sizeof(tDisplay),//结构体大小
.pvDisplayData=0,//驱动层显示数据指针
.usWidth=320, // LCD 显示像素宽度
.usHeight=240,// LCD 显示像素高度
.pfnPixelDraw=LCDPixelDraw,//画点
.pfnPixelDrawMultiple=LCDPixelDrawMultiple, //画序列点
.pfnLineDrawH=LCDlineDrawH,//画水平线
.pfnLineDrawV=LCDlineDrawV,//画竖直线
.pfnRectFill=LCDRectFill,//画填充矩形
.pfnColorTranslate=LCDColorTranslate,//传输颜色
.pfnFlush=LCDFlush,//更新缓冲区数据到显示器
};
3.2触摸屏驱动代码的编写
在控件层中,传递给控件的消息必须要包含一个坐标,让驱动库知道这个消息应该传递给哪一个控件。坐标确认的一种方法就是使用触摸屏。在触摸屏的驱动之中我们要完成坐标的采集和消息的传递。触摸屏驱动程序可行驱动库传递三种消息
        WIDGET_MSG_PTR_DOWN 触摸屏被按下消息
        WIDGET_MSG_PTR_MOVE 触摸屏上滑动消息
        WIDGET_MSG_PTR_UP   触摸屏上弹起消息
消息的传递通过WidgetPointerMessage()这个函数来完成。
当控件层收到消息后,并不是马上处理消息,而是把消息把在消息队列之中,只有调用了WidgetMessageQueueProcess()这个函数才开始处理消息。把显示数据更新到显示屏上。
3.2.1触摸屏控制器和MCU硬件连接
笔者使用的是TSC2046这一款触摸屏控制器,最高可配置成12位精度进行转换。可通过配置实现单端和差分方式的数据转换。在实际应用中笔者配置为12位差分转换方式。TSC2046检测到屏幕被按下时会通过PENIRQ向MCU申请中断。在中断中获取坐标,向驱动库传递消息。触摸屏控制器和MCU硬件连如下:

3.2.2触摸屏驱动函数的编写
TSC2046为和MCU之间通讯采用了GPIO模拟SPI的方式实现。
读取坐标的过程如下:

在实际应用中,使用 getpoint()这个函数把采集到的LCD坐标值保存到g_sTouchX和g_sTouchY这两个全局变量之中。根据MCU检测到的触摸屏不同状态向控件层传递不同的消息。消息传递有如下形式:
WidgetPointerMessage(WIDGET_MSG_PTR_DOWN,g_sTouchX,g_sTouchY);在主程序中调用WidgetMessageQueueProcess()来处理消息。

4图形驱动库嵌入式图形界面设计
图形界面的设计可从TI提供的三层结构之中任意一层开始。但是从控件层开始设计可设计出可控性和结构性更强的界面。在控件层整个工作过程中,通过tContext型的结构体出维护绘制的上下文,其定义如下:typedef struct
{
//结构体大小  
long lSize;
//指向驱动层显示结构的指针
    const tDisplay *pDisplay;
//屏幕显示区域大小
    tRectangle sClipRegion;
  //绘制前景颜色
      unsigned long ulForeground;
//绘制背景颜色
        unsigned long ulBackground;
//绘制字体     
const tFont *pFont;
}
tContext;
在使用驱动库之前,都要先初始化硬件,然后调用GrContextInit()来初始化绘制上下文。添加控件到控件树的根(WIDGET_ROOT)。具体代码如下:
  LCD_Init();//lcd初始化
  TouchScreenInit();//触摸屏初始化
  GrContextInit(&MyContext,&MyDisplay);//初始化绘制上下文
  WidgetPaint(WIDGET_ROOT);//绘制控件树的根
  WidgetAdd(WIDGET_ROOT, (tWidget *)&g_sBackground);//添加背景控件到根
  WidgetAdd(WIDGET_ROOT, (tWidget *)&g_sNext);//添加下一步按键到根
while(1)
{
WidgetMessageQueueProcess();//处理消息
}
以上代码便实现了在LCD屏上绘制显示背景,并添加执行下一步的控件显示到LCD屏上,当MCU检测到有触摸事件时,通过触摸屏驱动向控件层传递消息。当处理消息的时间,如果发送消息的坐标在这个控键显示的坐标范围内,则控间会接收到这个消息,并调用事先设置的函数,执行操作。
所有复杂的界面均可用此方法实现。以响应外部的输入,执行预订的操作。
5结论
TI开发的基于Stellaris平台的图形驱动库功能强大,但简单易用。可以帮助设计人员在产品开发中快速方便的设计出漂亮美观,操作性强的图形界面。简化了设计流程,降低了设计成本。具有很强的实用价值。
参考文献
1.        Stellaris® Graphics Library user guide.   Texas Instruments
2.        Stellaris® LM3S6911 Microcontroller DATA SHEET.  Texas Instruments
3.SSD1963 product Preview.   SOLOMON SYSTECH SEMICONDUCTOR
4.TSC2046 low voltage i/0 touch screen controller.   Texas Instrument

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

发表于 2013-3-31 22:15:30 | 显示全部楼层
很好的文章,如果楼主在代码实现上再写的细致点,绝对是个经典的教程!!

出0入0汤圆

发表于 2013-8-12 20:05:02 来自手机 | 显示全部楼层
mark……
顶一个…

出0入10汤圆

发表于 2016-4-22 15:05:26 | 显示全部楼层
很好的文章

出0入0汤圆

发表于 2016-4-22 15:10:41 | 显示全部楼层
很好的文章,有用的时候
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-25 14:40

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表