从大一开始接触到单片机(MCU),一直都觉得挺好玩的。从8位的51单片机到16位的TI MSP系列的单片机都尝试学过。感觉还不错,不过,以前一直都觉得32位的ARM处理器距离自己很遥远。幸运的是,如今我确实有机会接触一款32位的处理器了。当然使用的是Cortext M4内核,TI Stellaris系列处理器之一。
在这几天的学习中,发现高端的处理器确实很强大,不管是内存,FLash存储空间,还是内部的各种硬件资源都极为丰富。也让我一时间不知所措了。此外,还学习了下TFT彩屏的操作。要知道,以前接触的显示器都只是数码管或者是只能显示ASCII码的1602液晶屏(而且还要动态刷新)。既然接触到了彩屏,就要玩个痛快。
在实验室的时候,看到队友们每次需要借助于显示器来编写一些小程序时,如为了绘制出一个“按钮”或者一个进度条都大费周折。虽说最终他们也做了出来,可是我总觉得那样不是特别好。有两个原因,第一,代码的可移植性不够高;第二,代码结构不够清晰。这两点我是相当忌讳的。如果要是看到main.c里面用了那么多的代码只是为了绘制几个“按钮”,看起来会相当难受。所以,我在使用的过程中就想到,为什么我不像在Windows下编写程序那样,把那些“按钮”, “进度条”什么的封装起来呢?这样我们在以后的使用中就可以轻松再创建这些对象了。
于是,就决定创建一个简易的GUI控件库,供这些小彩屏使用。
说一下大体的思路吧。高人看来自然觉得我的做法确实很笨拙,而且并不总是特别理想的。但不管怎么说,这也是我第一次尝试用C语言封装的一个库,我想还是能学到不少东西的。年轻人总会犯错误的嘛!
好了,先看下我的guilib库文件的目录结构吧:
为了方便后期的移植,大体上给分了3个层:
1、与底层相关的接口部分:
在这一层中,主要是和显示器驱动打交道的。与这一层有关的文件夹主要是"lcddriverlibs",提供了直接操作显示屏的底层驱动程序。可以看到在文件"grahic.h"中有如下几个函数:
1 void LcdInitialize(void); 2 void LcdPixelDraw(int32_t x, int32_t y, uint32_t color); 3 void LcdPixelDrawMultiple(int32_t x, int32_t y, int32_t x0, int32_t count, 4 int32_t bpp, const uint8_t *pui8Data, const uint8_t *pui8Palette); 5 void LcdLineDrawH(int32_t xStart, int32_t xStop, int32_t y, uint32_t color); 6 void LcdLineDrawV(int32_t x, int32_t yStart, int32_t yStop, uint32_t color); 7 void LcdRectFill(int32_t xStart, int32_t yStart, int32_t xStop, int32_t yStop, 8 uint32_t color); 9 uint32_t LcdColorTranslate(uint32_t color);
这些函数接口在移植时需要重新实现他们,可以在文件"grahic.c"中重新定义。通常,需要更换文件夹"lcddriverlibs"中的驱动程序,然后再根据驱动程序,重写"grahic.c"中的几个接口。这几个接口函数的命名其实是模仿了TI stellaris图形库的命名。他们提供了基础的绘点,绘线,填充等操作。此外,这些函数在重新实现时,不能够更改他们的接口,如参数,函数名称。否则上层的控件将不能正常绘制。这是必须要遵守的规则。
对了,最后那个函数主要是用来将24位的真彩色转换为显示器支持的色彩。由于并非所有的TFT彩屏都支持24位色彩,所以必须要做适当地转换。
2、基础绘图层:
在这一层中,由于目前时间有限,只是实现了几个简单的基础绘图函数。比如常用的画点,画线,画矩形,填充矩形等等。这一层是与硬件层隔离的,他们实际上调用了来自上一层中定义的那些个函数。所以,必须要保证与硬件有关的那一层的几个函数接口工作正常才可以。总的来说,如果程序中只需要绘一些简单的图形,只需要使用这里面提供的函数接口就可以了。
3、“控件”层:
这一层便是最顶层了,提供了一些常用的控件:矩形按钮,选择框,进度条,滑动条,标签等等(待时间充裕了可能还会继续完善,因为目前只需要这些就够用了,我们没有做太复杂的程序)。对于这些控件对象,都有相应的结构体类型定义,比如tRectangle等。
为了能够管理这些控件,我使用了一个顺序表来存储每一个被绘制到屏幕上的控件对象的指针。当然,也可以使用链表来实现,不过不想太麻烦,暂时使用顺序表代替,虽说占用的控件稍微多了点,但还可以接收的。然后,当我从屏幕中移除一个控件对象时,便从那个列表中删除掉这个对象的指针。为了安全起见,我为每一种控件对象都提供了单独的顺序表来存储。即每一个表只存储单一种类的控件对象。
这样做的原因有两点,第一,可以在实现文件中将表定义成为static的,这样,用户在使用库时,并不能访问我用来管理控件对象的表,也就是说不会修改掉这个表。此外,用户也不需要知道我是如何管理这些显示在屏幕上显示的对象。对他们来说就是透明的。第二,由于每个表只管理一种控件对象,我们在使用表时就无需判断控件类型了。比如,在进行触摸检测时,我们并不需要使用类型识别标志来判断到底是按钮被按下了还是选项框被按下了。因为我们每次只是对一种类型的控件进行检测。另外,我们在整体刷新所有控件时,可以轻松地调用正确的刷新函数来实现(当然,如果是用C++的话,也不用这么麻烦了)。
总结一下,大体的三层结构就是这样的,当然,分层并没有什么新意,只是为了方便说明和实现而已。此外实现中也借鉴了网上的一些方法。
基于控件库做的一个简易计算器,不过计算功能倒没实现。
后记:这个年代,想要原创一些东西出来太难了,但是可以借鉴别人的思想。既然我没有能力创造思想,那么就应该学习成熟的思想,在学习别人成熟的思想中进步,那样也挺好的。对了,关于每个“控件”的具体的代码实现,待有时间再来补充吧。就写这么些了。
版权声明:本文为博主原创文章,未经博主允许不得转载。