通过前两节的介绍,使ucgui成功运行在stm32板子的ecos系统,实现了让ucgui在ecos中跑起来的目标。接下来就是实现触摸功能。
ucgui触摸功能的移植也相对比较容易,前提是你的触摸驱动已经调试好了。我的STM32板子使用的是TSC2046触摸驱动芯片,其驱动在前面章节已经介绍过了。详情点击:http://velep.com/archives/617.html
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目录下的同名文件。
在很多ucgui移植说明中,并未明确说明GUI_X_Touch.c文件应该放在哪里,所以我就把这个文件放到ucgui的Config目录下,使之与GUI_X_eCos.c同在一个目录下。
GUITouchConf.h文件
#ifndef GUITOUCH_CONF_H #define GUITOUCH_CONF_H #define GUI_TOUCH_AD_LEFT 128 #define GUI_TOUCH_AD_RIGHT 3856 #define GUI_TOUCH_AD_TOP 128 #define GUI_TOUCH_AD_BOTTOM 3729 #define GUI_TOUCH_SWAP_XY 0 #define GUI_TOUCH_MIRROR_X 0 #define GUI_TOUCH_MIRROR_Y 0 #endif /* GUITOUCH_CONF_H */
这个文件主要是定义了触摸AD采样边界值。这4个AD采样边界值是个模糊值,不必太精确。
GUI_X_Touch.c文件
#include "GUI.h" #include "GUI_X.h" #if ECOS #include <unistd.h> #include <fcntl.h> #endif static int touch_screen_read(short * px, short * py, short * pb) { #if ECOS static int pd_fd = -1; short data[4];/* read a data point */ int bytes_read; if (pd_fd < 0) { if ((pd_fd = open("/dev/ts", O_RDONLY | O_NONBLOCK)) < 0) { return 0; } } bytes_read = read(pd_fd, data, sizeof(data)); if (bytes_read != sizeof(data)) { return 0; } *px = data[1]; *py = data[2]; *pb = (data[0] ? 0x01 : 0); if (! *pb ) { return 3; /* only have button data */ } else { return 2; /* have full set of data */ } #endif return 0; } void GUI_TOUCH_X_ActivateX(void) { } void GUI_TOUCH_X_ActivateY(void) { } int GUI_TOUCH_X_MeasureX(void) { short px = 0, py = 0, pb = 0; touch_screen_read(&px, &py, &pb); //diag_printf("MeasureX: px = %d, py = %d\n", px, py); return px; } int GUI_TOUCH_X_MeasureY(void) { short px = 0, py = 0, pb = 0; touch_screen_read(&px, &py, &pb); //diag_printf("MeasureY: px = %d, py = %d\n", px, py); return py; }
这文件也很简单,实质就是读取触摸屏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采样值,如下代码:
#if GUI_SUPPORT_TOUCH static void gui_touch_thread(cyg_addrword_t data) { while (1) { GUI_TOUCH_Exec(); GUI_Exec(); GUI_Delay(10); } } #endif /* Thread variables */ #define UCGUI_THREAD_STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL/* * 2 */) static cyg_handle_t UCGUI_thread_handle; static cyg_thread UCGUI_thread_block; static char UCGUI_thread_stack[UCGUI_THREAD_STACK_SIZE]; #if GUI_SUPPORT_TOUCH #define UCGUI_TOUCH_THREAD_STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL/* * 2 */) static cyg_handle_t UCGUI_TOUCH_thread_handle; static cyg_thread UCGUI_TOUCH_thread_block; static char UCGUI_TOUCH_thread_stack[UCGUI_TOUCH_THREAD_STACK_SIZE]; #endif void cyg_user_start(void) { /* Create GUI thread */ cyg_thread_create(7, /* Priority */ gui_thread, 0, "GUI thread", UCGUI_thread_stack, sizeof(UCGUI_thread_stack), &UCGUI_thread_handle, &UCGUI_thread_block); cyg_thread_resume(UCGUI_thread_handle);/* Starting thread */ #if GUI_SUPPORT_TOUCH /* Create GUI touch thread */ cyg_thread_create(8, /* Priority, must lower than gui_thread */ gui_touch_thread, 0, "GUI touch thread", UCGUI_TOUCH_thread_stack, sizeof(UCGUI_TOUCH_thread_stack), &UCGUI_TOUCH_thread_handle, &UCGUI_TOUCH_thread_block); cyg_thread_resume(UCGUI_TOUCH_thread_handle);/* Starting thread */ #endif cyg_scheduler_start();/* Scheduler start */ } #endif
在调试过程中发现,触摸线程的优先级(Priority)必须比GUI主线程的优先级低,否则程序会挂掉,原因暂时未知。
添加多线程支持
由于存在多个地方同时调用ucgui的API接口,所以必须为ucgui的API接口加上锁功能。因此,必须完成GUI_X_eCos.c文件的多任务接口函数,如下代码:
void GUI_X_InitOS (void) { GUI_MUTEX_init(); } void GUI_X_Lock (void) { GUI_MUTEX_LOCK(); if (++GUI_mutex_cnt > 1) { diag_printf("Error in GUITASK.c module ...\n"); } } void GUI_X_Unlock (void) { GUI_mutex_cnt--; GUI_MUTEX_UNLOCK(); } U32 GUI_X_GetTaskId (void) { return (U32)cyg_thread_get_id(cyg_thread_self()); }
其中,相关宏定义在该文件的开头,如下代码:
static cyg_uint32 GUI_mutex_cnt = 0; // For debugging only ... Not required static cyg_mutex_t GUI_mutex; #define GUI_MUTEX_init() cyg_mutex_init(&GUI_mutex) #define GUI_MUTEX_LOCK() cyg_mutex_lock(&GUI_mutex) #define GUI_MUTEX_UNLOCK() cyg_mutex_unlock(&GUI_mutex)
触摸功能测试
在测试程序中,添加GUI_CURSOR_Show();函数调用,显示鼠标,用于测试触摸屏。注意:必须打开窗口功能,即把宏GUI_WINSUPPORT定义为1。
在LCD显示界面中,点击LCD,则可以看到鼠标会移动到点击地点,说明触摸功能移植成功。