首页 资讯 应用 高压 设计 行业 低压 电路图 关于

嵌入式

旗下栏目: PLC 嵌入式 单片机 DCS

stm32移植ecos,移植ucgui到ecos

嵌入式 | 发布时间:2018-10-25 | 人气: | #评论# | 本文关键字:stm32,ecos,ucgui,移植
摘要:ucgui是一个开源的轻型嵌入式GUI,使用广泛,资料丰富,功能完整,尤其适合嵌入式产品的GUI。只是它开源但不免费。仅管如此,移植来玩玩还是可以的。 从这节开始,介绍ucgui移植到eCos方法

ucgui是一个开源的轻型嵌入式GUI,使用广泛,资料丰富,功能完整,尤其适合嵌入式产品的GUI。只是它开源但不免费。仅管如此,移植来玩玩还是可以的。

从这节开始,介绍ucgui移植到eCos方法与步骤。大抵分为3章节:建立ucgui编译工程、让ucgui在eCos中运行起来和触摸功能移植。本节主要介绍如何建立ucgui的编译工程。

前言^有感

从写eCos LCD驱动开始,就一直在寻找一个即开源免费又是针对嵌入式产品设计的GUI,可惜直到今天都未能如愿,我想这也是很多嵌入式工程师在选择GUI时所遇到的困惑吧。

关于ucgui,这里就不多介绍了,网上一大把。之前未曾接触这个GUI,移植完成后,发现这个GUI确实挺好用,麻雀虽小,五脏俱全,移植也方便。下面是按照本人的一个移植过程来进行介绍的。

关于ucgui版本

网上有很多ucgui源码版本,一开始我纠结于使用哪个版本更好。最后开源的版本是3.98,本应该使用这个版本,但下载下来的源码包没有JPEG功能代码,所以退而求其次,使用3.90版本的ucgui。

使用ucgui仿真工程image

以前编写单色LCD程序的时候,也有使用windows下的仿真,模拟出一个LCD屏,然后在windows下写显示程序并调试,最后再下到板子上,节省了大把程序下载调试的时间,大大提高了开发效率。

ucgui亦提供了windows下的仿真工程,使用VC6建立程,这是个很老的工程,还好可以很方便的使用Microsoft Visual Studio 2008打开并转为VS2008工程。

用VS2008直接打开ucgui3.90版本源码中的Simulation.dsw工程后,会对工程进行转换。工程转换没问题,编译也没问题,但在链接时会提示找不到LIBC.lib。

解决方法:在VS2008中忽略它就可以了,找到“项目->属性->链接器->输入->忽略特定库”,可以看到工程默认忽略的是libcmt.lib,我们把它替换为忽略LIBC.lib,这样链接就可以通过了。如果是把LIBC.lib添加到忽略特定库中,链接还是不能通过,这点需要注意。

在windows上运行ucgui的demo程序,如下图所示:

image

ucgui移植eCos步骤

  1. 建立编译工程,编写makefile;

  2. 配置ucgui,按照模板填充ucgui LCD驱动接口函数;

  3. 编写ucgui测试程序并调试;

  4. 移植ucgui触摸功能;

本节介绍建立编译工程,2、3步骤放到第二章节,4步骤放到第三章节。

移植前,如果还不太熟悉ucgui的移植,可以先阅读下面两个链接提供的介绍:

  • http://www.cnblogs.com/CodeHXH/archive/2011/07/27/2117659.html

  • http://www.ichanging.org/stm32-ucgui.html

ucgui交叉编译工程

网上绝大部分ucgui的移植是基于IDE环境,如MDK、IAR等。交叉编译ucgui的例子甚少。本次交叉编译工程设计思路如下:

  • 以goAhead eCos的makefile为模板,编写ucgui eCos的makefile,把ucgui交叉编译为静态库文件;

  • 设计工程目录结构,保留ucgui源码的完整性,不破坏其代码结构;

  • 提供ucgui交叉编译测试程序,用户可以方便选择是否编译测试程序;

工程目录结构image

工程目录结构如右图所示。

  • bin目录:存放编译目标文件;

  • obj目录:存放.o和.d文件;

  • test目录:存放测试程序;

  • UCGUI3.90:ucgui源码包;

其中,要说的是,把ucgui编译的.o和.d文件放到指定目录中,会增加makefile的设计难度,但这样设计的话,会让整体工程更加清晰,更加干净整洁。

test目录下,有测试程序源文件test_ucgui.c和UCGUI带的demo程序:GUIDemo。

makefile编写

编写makefile应当是移植ucgui到eCos工作难度比较大的一部分。但我们绝不用从0开始,可以goAhead eCos的makefile为模板,在此基础上进行修改。

这也提供了一种应用程序设计思路:使用第3方代码时,我们可以把它编译成静态库,最后链接到我们的应用程序中。实际上这也是一种通用的设计方法。很多第3方代码,虽然开源,但是想把代码加入到用户应用程序中却可能是有条件的,比如公开你的代码等。这种情况下,可能允许你使用静态库的方法而无需公开你的代码。

所以,这也是把ucgui编译为一个静态库的缘由。

具体怎么编写和修改makefile,这里就不做多介绍,因为makefile涉及较多知识内容。这里仅介绍makefile中几个变量和编译目标:

makefile中的几个变量:

1> HOST:设定你的编译主机,是LINUX还是CYGWIN;
2> 根据编译主机,修改ECOS_REPOSITORY,即ECOS仓库路径;
3> 根据编译主机,修改PKG_INSTALL_DIR,即ECOS安装路径;

编译目标:

make:     只生成ucgui静态库,名称为libucgui.a;
make test:除生成ucgui静态库外,还生成一个ucgui的测试程序映像文件:test_ucgui

建立ucgui的交叉编译工程后,就可以真正展开ucgui的移植工作了。在此之前,需要完成了LCD驱动工作,这代表你对eCos的framebuf框架有所了解。

本节主要介绍在eCos系统中如何让ucgui跑起来。ucgui设计优秀,具有良好移植性,配合eCos framebuf优秀框架,可以让你很快见到ucgui呈现的精美显示界面,let’s go…

eCos framebuf

关于eCos LCD驱动

ucgui源码结构

image    image

解压ucgui源码包后,含3个文件夹:Sample、Start、Tool。如上左图所示。

  • Sample:一些ucgui使用实例和模板,如GUIDemo程序;

  • Start:ucgui源码;

  • Tool:一些开发用户图形程序的实用工具;

Start目录结构如上右图所示。其中,Config和GUI是ucgui的程序目录,Application应该设计为存放用户的图形程序,System是windows下ucgui的仿真程序,Output存放仿真程序的编译输出,Simulation.dsw是ucgui的仿真工程。

我们主要关注Config和GUI两目录。相关文件介绍如下:

image

ucgui配置

ucgui的配置位于Config目录,分为GUI层配置和LCD层配置,前者配置GUI核心,对应GUIConf.h文件;后者配置LCD设备和触摸设备属性,对应LCDConf.h和GUITouchConf.h文件。

配置GUI

打开GUIConf.h文件,其配置项如下图所示,具体含义不做具体介绍(注释已经很详细了)。

image

配置LCD设备属性

image

对于LCDConf.h文件,只考虑上图中的配置项,其它内容忽略。根据LCD,定义其X、Y值、每个像素位数等。LCD_CONTROLLER的值,浏览了下代码,似乎是用于预编译用的,暂时保持为默认值1375。

LCD_SWAP_RB配置项,是本次移植时加上的,用于交换LCD的R和B两个颜色组,具体下面再详细描述。

ucgui驱动接口

LCDDriver下面的文件是ucgui与LCD驱动的接口文件,源码包中提供了3个模板,分别是:LCDWin.c、LCDNull.c、LCDDummy.c。移植时,按照模板填充相关函数。

网上很多移植教程是以LCDDummy.c模板来说明的。拷贝该文件并重命名为LCDeCos.c,然后修改该文件。这里,简要说下修改哪些地方,具体的代码请看http://52ecos.net/thread-648-1-1.html提供的源文件。

1. 修改头文件开头的编译宏开关,如下代码:

#if defined(ECOS) && !defined(LCD_SIMCONTROLLER)。

2. 包含相关头文件:

01#include "LCD_Private.h"      /* private modul definitions & config */
02#include "GUI_Private.h"
03#include "GUIDebug.h"
04
05#include <pkgconf/system.h>
06#include <pkgconf/hal.h>
07#include <cyg/kernel/kapi.h>
08#include <cyg/infra/diag.h>
09#include <cyg/io/io.h>                  /* I/O functions */
10#include <cyg/io/framebuf.h>

3. LCD framebuf设备宏定义:

#define FRAMEBUF fb0

4. 完成下面几个接口函数,分别是:

  • LCD_L0_SetPixelIndex();

  • LCD_L0_GetPixelIndex();

  • LCD_L0_XorPixel;

  • LCD_L0_DrawHLine();

  • LCD_L0_DrawVLine();

  • LCD_L0_FillRect();

  • LCD_On();

  • LCD_Off();

  • LCD_L0_Init();

在ecos/packages/io/framebuf/ecos_version/include/framebuf.h文件可找到对应宏来完成上述接口函数。以LCD_L0_SetPixelIndex()函数为例进行说明。

LCD_L0_SetPixelIndex(),完成在LCD上画点操作。在eCos中对应的接口宏为:CYG_FB_WRITE_PIXEL()。开始移植时,我纠结CYG_FB_WRITE_PIXEL()第4个参数_colour_与LCD_L0_SetPixelIndex()第3个参数PixelIndex的对应关系。是不是要转换?经过分析ucgui代码发现,PixelIndex参数实际就是个RGB颜色索引值。因此,可以直接使用PixelIndex作为CYG_FB_WRITE_PIXEL()宏的第4个实参。

再说下LCD_L0_DrawHLine()接口函数。eCos中对应的接口宏为:CYG_FB_WRITE_HLINE()。它同样有个_colour_参数,但LCD_L0_DrawHLine()接口则没有相应的参数,怎么办?不急,ucgui提供了一个宏LCD_COLORINDEX,用于获取当前RGB颜色索引值。

打开LCD framebuf设备和LCD背光可以放在LCD_On()接口函数中,也可以放在LCD_L0_Init()接口函数。

至此,完成了ucgui的移植,使用其提供的demo作为测试程序,编译后下载程序到板子上运行,开始调试。

ucgui调试

只要你的LCD驱动没问题的话,初始编译后就可以看到显示界面。我的调试主要集中在LCD的颜色。

如上所述,开始移植时不明白eCos framebuf宏接口中_colour_参数的含义,实际上当初弄LCD驱动时也没明白这个参数的含义。导致LCD显示颜色不正确。

_colour_参数是个颜色值,通过查看eCos framebuf和LCD驱动代码发现,它直接写入到LCD GRAM寄存器。查看LCD驱动芯片SPFD5420A的数据手册,写入到LCD GRAM寄存器的值,是一个RGB565值,16-bit,代表65536种颜色。但注意,16-bit值到底是RBG还是BGR,可以通过设置LCD驱动芯片R0001寄存器的SS bit和R0003寄存器BGR bit来控制。

SS bit控制数据移位方向,BGR bit标示RGB反转,见SPFD5420A数据手册14和15页的说明。组合关系如下:

image

在LCD驱动中,SS=0,BGR=1(采用安富莱开发板的初始化配置),因此,写入GRAM的值对应的是BGR颜色值。在ucgui中,默认的是RGB颜色值,这就出现了蓝色和红色相反的问题。

1#elif (FIXEDPALETTE == 565) && (LCD_SWAP_RB==0)
2  #define COLOR2INDEX(Color) LCD_Color2Index_565(Color)
3  #define INDEX2COLOR(Index) LCD_Index2Color_565(Index)
4  #define GETINDEXMASK()     LCD_GetIndexMask_565()
5#elif (FIXEDPALETTE == 565) && (LCD_SWAP_RB)
6  #define COLOR2INDEX(Color) LCD_Color2Index_M565(Color)
7  #define INDEX2COLOR(Index) LCD_Index2Color_M565(Index)

上面是ucgui LCD_L0_Generic.c文件一段关于把颜色转换成颜色值的代码,从中可看到,ucgui支持RGB颜色R和B调换,免去了修改LCD驱动,前提是有LCD_SWAP_RB宏的定义,于是我把该宏定义在了LCDConf.h头文件。解决了LCD颜色显示不正确的问题。

关于优化

根据ucgui提供的demo程序GUISpeed,测试出来的速度大概是每秒画80万个PIXs。这个速度,结合具体的显示画面来看,我是可以接受的,不过,看了网上其他人的测试结果,居然达到两三百个PIXs。这应该是做了很多优化的动作,看他们的介绍,严重的破坏了程序结构。

我个人认为,不必刻意去追求高速度,力求在用户体验与代码结构上找到一个平衡点即可。

再来看eCos framebuf驱动框架。其实前面已经提到,如果你追踪framebuf驱动框架代码,你可以发现,ucgui的驱动接口函数中调用的eCos framebuf接口,实际上是一系列的宏定义。也就是说,ucgui驱动接口函数中调用的实际上是直接操作LCD驱动芯片的函数,你别看eCos把它封装了一层又一层,实际上这些封装对性能没有任何副作用,而且,大大提高了程序的层次结构。

通过前两节的介绍,使ucgui成功运行在stm32板子的ecos系统,实现了让ucgui在ecos中跑起来的目标。接下来就是实现触摸功能。

ucgui触摸功能的移植也相对比较容易,前提是你的触摸驱动已经调试好了。我的STM32板子使用的是TSC2046触摸驱动芯片,其驱动在前面章节已经介绍过了。

TSC2046触摸驱动

eCos TSC2046触摸驱动芯片源码下载地址:http://52ecos.net/thread-647-1-1.html

这个驱动是参考eCos MINI STM32上TSC2046触摸驱动修改的。主要是修改了读取触摸AD采样值函数read_ts()。一个非常奇怪的问题是:相同的触摸驱动芯片,在MINI STM32和在我的STM32板子上,AD采样值的计算有不同。当前驱动的AD采样值是参考安富莱例程提供的计算公式。

ucgui添加触摸支持

在ucgui中,除了把GUI_SUPPORT_TOUCH宏的值定义为1表示ucgui支持触摸功能外,还需要两个源文件,分别是GUITouchConf.h和GUI_X_Touch.c。其中,GUI_X_Touch.c拷贝于ucgui源码Sample/GUI_X目录下的同名文件。

image

在很多ucgui移植说明中,并未明确说明GUI_X_Touch.c文件应该放在哪里,所以我就把这个文件放到ucgui的Config目录下,使之与GUI_X_eCos.c同在一个目录下。

GUITouchConf.h文件

01#ifndef GUITOUCH_CONF_H
02#define GUITOUCH_CONF_H
03
04#define GUI_TOUCH_AD_LEFT        128
05#define GUI_TOUCH_AD_RIGHT        3856
06#define GUI_TOUCH_AD_TOP        128
07#define GUI_TOUCH_AD_BOTTOM        3729
08
09#define GUI_TOUCH_SWAP_XY        0
10#define GUI_TOUCH_MIRROR_X        0
11#define GUI_TOUCH_MIRROR_Y        0
12
13#endif /* GUITOUCH_CONF_H */

这个文件主要是定义了触摸AD采样边界值。这4个AD采样边界值是个模糊值,不必太精确。

GUI_X_Touch.c文件

01#include "GUI.h"
02#include "GUI_X.h"
03
04#if ECOS
05#include <unistd.h>
06#include <fcntl.h>
07#endif
08
09static int touch_screen_read(short * px, short * py, short * pb)
10{
11#if ECOS
12    static int pd_fd = -1;
13    short data[4];/* read a data point */
14    int bytes_read;
15
16    if (pd_fd < 0) {
17        if ((pd_fd = open("/dev/ts", O_RDONLY | O_NONBLOCK)) < 0) {
18           return 0;
19        }
20    }
21
22    bytes_read = read(pd_fd, data, sizeof(data));
23    if (bytes_read != sizeof(data)) {
24        return 0;
25    }
26
27    *px = data[1];
28    *py = data[2];
29
30    *pb = (data[0] ? 0x01 : 0);
31
32    if (! *pb ) {
33        return 3;          /* only have button data */
34    } else {
35        return 2;          /* have full set of data */
36    }
37
38#endif
39    return  0;
40}
41
42void GUI_TOUCH_X_ActivateX(void) {
43}
44
45void GUI_TOUCH_X_ActivateY(void) {
46}
47
48int  GUI_TOUCH_X_MeasureX(void)
49{
50    short px = 0, py = 0, pb = 0;
51    touch_screen_read(&px, &py, &pb);
52    //diag_printf("MeasureX: px = %d, py = %d\n", px, py);
53    return px;
54}
55
56int  GUI_TOUCH_X_MeasureY(void)
57{
58    short px = 0, py = 0, pb = 0;
59    touch_screen_read(&px, &py, &pb);
60    //diag_printf("MeasureY: px = %d, py = %d\n", px, py);
61    return py;
62}

这文件也很简单,实质就是读取触摸屏AD采样X、Y值。注意,返回的是物理值。

根据别人的移植说明,读取触摸屏AD采样X、Y值,是由用户程序触发的。在eCos中,有3种方法可用:

  • 设定触摸屏的一个引脚为外部触发,触摸点击时,电平变化触发中断,在中断函数中调用GUI_TOUCH_Exec()函数,让UCGUI更新TOUCH时间数据。

  • 设定一个10ms的定时器中断不断查询,在中断函数中调用GUI_TOUCH_Exec()。

  • 开启一个线程,周期调用GUI_TOUCH_Exec()函数;

第1个方案,看似更为合适,不占用CPU,让CPU可以处理其他事情。但是UCGUI的触摸事件,一次触摸只会读取一个轴的AD值,也就是说一次读取X轴AD,下一次再读取Y轴AD值。这样导致获得的数据都是错误的。uCGUI 有处理抖动的函数_StoreUnstable(x, y),会将误差较大的数据过滤,两次点击事件时间很短的话,也至少会是一次正确坐标,一次错误坐标。而且外部中断的方法,只能获得触摸点击的事件,无法获得触摸移动的事件。

本次移植为求简单,采用了第3种方法,在用户测试程序中,开启一个线程不断轮询触摸AD采样值,如下代码:

01#if GUI_SUPPORT_TOUCH
02static void gui_touch_thread(cyg_addrword_t data)
03{
04    while (1) {
05        GUI_TOUCH_Exec();
06        GUI_Exec();
07        GUI_Delay(10);
08    }
09}
10#endif
11
12/* Thread variables */
13#define UCGUI_THREAD_STACK_SIZE         (CYGNUM_HAL_STACK_SIZE_TYPICAL/* * 2 */)
14static cyg_handle_t UCGUI_thread_handle;
15static cyg_thread   UCGUI_thread_block;
16static char         UCGUI_thread_stack[UCGUI_THREAD_STACK_SIZE];
17
18#if GUI_SUPPORT_TOUCH
19#define UCGUI_TOUCH_THREAD_STACK_SIZE   (CYGNUM_HAL_STACK_SIZE_TYPICAL/* * 2 */)
20static cyg_handle_t UCGUI_TOUCH_thread_handle;
21static cyg_thread   UCGUI_TOUCH_thread_block;
22static char         UCGUI_TOUCH_thread_stack[UCGUI_TOUCH_THREAD_STACK_SIZE];
23#endif
24
25void cyg_user_start(void)
26{
27    /* Create GUI thread */
28    cyg_thread_create(7,    /* Priority */
29                      gui_thread,
30                      0,
31                      "GUI thread",
32                      UCGUI_thread_stack,
33                      sizeof(UCGUI_thread_stack),
34                      &UCGUI_thread_handle,
35                      &UCGUI_thread_block);
36    cyg_thread_resume(UCGUI_thread_handle);/* Starting thread */
37
38#if GUI_SUPPORT_TOUCH
39    /* Create GUI touch thread */
40    cyg_thread_create(8,    /* Priority, must lower than gui_thread */
41                      gui_touch_thread,
42                      0,
43                      "GUI touch thread",
44                      UCGUI_TOUCH_thread_stack,
45                      sizeof(UCGUI_TOUCH_thread_stack),
46                      &UCGUI_TOUCH_thread_handle,
47                      &UCGUI_TOUCH_thread_block);
48    cyg_thread_resume(UCGUI_TOUCH_thread_handle);/* Starting thread */
49#endif
50
51    cyg_scheduler_start();/* Scheduler start */
52}
53#endif

在调试过程中发现,触摸线程的优先级(Priority)必须比GUI主线程的优先级低,否则程序会挂掉,原因暂时未知。

添加多线程支持

由于存在多个地方同时调用ucgui的API接口,所以必须为ucgui的API接口加上锁功能。因此,必须完成GUI_X_eCos.c文件的多任务接口函数,如下代码:

01void  GUI_X_InitOS (void)
02{
03    GUI_MUTEX_init();
04}
05
06void  GUI_X_Lock (void)
07{
08    GUI_MUTEX_LOCK();
09    if (++GUI_mutex_cnt > 1) {
10        diag_printf("Error in GUITASK.c module ...\n");
11    }
12}
13
14void  GUI_X_Unlock (void)
15{
16    GUI_mutex_cnt--;
17    GUI_MUTEX_UNLOCK();
18}
19
20U32  GUI_X_GetTaskId (void)
21{
22    return (U32)cyg_thread_get_id(cyg_thread_self());
23}

其中,相关宏定义在该文件的开头,如下代码:

1static cyg_uint32  GUI_mutex_cnt = 0;   // For debugging only ... Not required
2static cyg_mutex_t GUI_mutex;
3#define GUI_MUTEX_init()       cyg_mutex_init(&GUI_mutex)
4#define GUI_MUTEX_LOCK()       cyg_mutex_lock(&GUI_mutex)
5#define GUI_MUTEX_UNLOCK()     cyg_mutex_unlock(&GUI_mutex)

触摸功能测试

在测试程序中,添加GUI_CURSOR_Show();函数调用,显示鼠标,用于测试触摸屏。注意:必须打开窗口功能,即把宏GUI_WINSUPPORT定义为1。

在LCD显示界面中,点击LCD,则可以看到鼠标会移动到点击地点,说明触摸功能移植成功。

责任编辑:stm32,ecos,ucgui移植

热门文章

首页 | 电气资讯 | 应用技术 | 高压电器 | 电气设计 | 行业应用 | 低压电器 | 电路图 | 关于我们 | 版权声明

Copyright 2017-2018 电气自动化网 版权所有 辽ICP备17010593号-1

电脑版 | 移动版 原创声明:本站大部分内容为原创,转载请注明电气自动化网转载;部分内容来源网络,如侵犯您的权益请发送邮件到[email protected]联系我们删除。