软件环境:Win7,Keil MDK 4.72a, IAR EWARM 7.2, GCC 4.2,Python 2.7 ,SCons 2.3.2
硬件环境:Armfly STM32F103ZE-EK v3.0开发板
参考文章:RT-Thread编程指南
RT-Thread_1.2.0+lwip+rtgui0.8.0 移植心得
RT-Thread RTOS组件:RTGUI教程 Hello World
前面基本解决了触摸屏的驱动和校正问题,但是还有些问题,比如按键响应,触摸屏校正数据的存储,打开多个Demo组件时会有问题存在等,接下来就要研究这些问题。
【1】按键响应
(1)查看原理图硬件接口电路,如下图:
从原理图可以看到,一个五向摇杆有五个按键,已经能够满足LCD显示屏上操作的需要。可以根据原理图上的标出的硬件接口修改RTGUI底层硬件接口代码。
(2)对于RT-Thread来讲,对于底层硬件,需要将按键信息包装成事件信息传递个GUI server处理,然后server发给LCD 作为响应。所以需要新建一个文件key.c,处做这件事情。参考realtouch和魔笛F1开发板的按键例程代码修改而得到key.c的代码如下:
<pre name="code" class="cpp">#include <rtthread.h>
#include <board.h>
#include <rtgui/event.h>
#include <rtgui/rtgui_server.h>
//常规按键处理使用
#define DEBOUNCE_SHORT_TIME 3 // 轻触按键消抖时间5(单位:20毫秒)
#define DEBOUNCE_LONG_TIME 5 // 长按键时间DEBOUNCE_COUT_FIRST+DEBOUNCE_COUT_INTERVAL*DEBOUNCE_LONG_TIME(单位:10毫秒)
#define DEBOUNCE_COUT_FIRST 500//50 // 连按键间隔时间100(单位:20毫秒)
#define DEBOUNCE_COUT_INTERVAL 10 // 连按键间隔时间50(单位:20毫秒)
//特殊按键处理使用
#define C_RELASE_COUT 3
#define C_SHORT_COUT 3 //3*20ms
#define C_SPECIAL_LONG_COUT 60 //60*20ms
//按键标志
#define C_FLAG_SHORT 0x00000001
#define C_FLAG_COUNT 0x00000002
#define C_FLAG_LONG 0x00000004
#define C_FLAG_RELASE 0x00000008
//按键键值
#define C_UP_KEY 0x01
#define C_DOWN_KEY 0x02
#define C_LEFT_KEY 0x04
#define C_RIGHT_KEY 0x08
#define C_ENTER_KEY 0x10
#define C_SPECIAL_KEY C_ENTER_KEY
/*enter键长按 我们定义为home键*/
#define C_HOME_KEY C_SPECIAL_KEY+0x44
/*
KEY_UP PG15
KEY_DOWN PD3
KEY_LEFT PG14
KEY_RIGHT PG13
KEY_ENTER PG7
*/
#define KEY_UP_GETVALUE() GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_15)//PG15
#define KEY_DOWN_GETVALUE() GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_3)//PD3
#define KEY_LEFT_GETVALUE() GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_14)//PG14
#define KEY_RIGHT_GETVALUE() GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_13)//PG13
#define KEY_ENTER_GETVALUE() GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_7)//PG7
/* 使用面向对象的方式,将按键所用到的所有元素进行打包 */
struct rtgui_key
{
rt_timer_t poll_timer;
struct rtgui_event_kbd kbd_event;
rt_uint32_t key_last;
rt_uint32_t key_current;
//检测到的按键值
rt_uint32_t key_get;
//常规按键使用
rt_uint32_t key_debounce_count;
rt_uint32_t key_long_count;
//特殊按键使用
rt_uint32_t key_special_count;
rt_uint32_t key_relase_count;
//按键标志
rt_uint32_t key_flag;
};
static struct rtgui_key *key;
static void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG|RCC_APB2Periph_GPIOD,ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = (GPIO_Pin_15|GPIO_Pin_14|GPIO_Pin_13|GPIO_Pin_7);
GPIO_Init(GPIOG, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = (GPIO_Pin_3);
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
static void key_timeout(void* parameter)
{
//按键未按下时,每位读入的值为1
key->key_current = KEY_UP_GETVALUE();
key->key_current |= KEY_DOWN_GETVALUE()<<1;
key->key_current |= KEY_LEFT_GETVALUE()<<2;
key->key_current |= KEY_RIGHT_GETVALUE()<<3;
key->key_current |= KEY_ENTER_GETVALUE()<<4;
key->key_current =~ (key->key_current);//取反
key->key_current &= 0x0000001f; //过滤掉低五位以外的值,因为只有五个按键
key->key_flag &= ~C_FLAG_SHORT;
key->key_flag &= ~C_FLAG_COUNT;
key->key_flag &= ~C_FLAG_LONG;
key->key_get = 0;
//
/*对于有长按和短按的特殊键做处理*/
if ((key->key_flag)&C_FLAG_RELASE)
{ //检查放键
if (key->key_current == 0)
{
if ((++(key->key_relase_count)) >= C_RELASE_COUT)
{ //按键已经放开
key->key_relase_count = 0;
key->key_flag &= ~C_FLAG_RELASE;
}
}
else
{
key->key_relase_count = 0;
}
}
else
{ //检查按键
if (key->key_current == C_SPECIAL_KEY)
{
if ((++(key->key_special_count)) >= C_SPECIAL_LONG_COUT)
{
key->key_special_count = 0;
key->key_get = C_HOME_KEY;
key->key_flag |= C_FLAG_LONG; //特殊键 长按键按下
key->key_flag |= C_FLAG_RELASE; //按下后要求检测放键
}
}
else
{ //放开键后才检测短按
if ((key->key_special_count >= C_SHORT_COUT) && (key->key_special_count <C_SHORT_COUT+30))
{
key->key_get = C_SPECIAL_KEY;
key->key_flag |= C_FLAG_SHORT; //特殊键 短按键按下
}
key->key_special_count = 0;
}
}
// 普通按键处理
if((key->key_current == 0)||(key->key_current != key->key_last)|| (key->key_current == C_SPECIAL_KEY))
{
key->key_debounce_count = 0; //第一次
key->key_long_count = 0; //清除长按键计数器
}
else
{
if(++(key->key_debounce_count) == DEBOUNCE_SHORT_TIME)
{
key->key_get = key->key_current;
key->key_flag |= C_FLAG_SHORT; //短按键按下
}
if(key->key_debounce_count == DEBOUNCE_COUT_FIRST + DEBOUNCE_COUT_INTERVAL)
{
key->key_get = key->key_current;
key->key_flag |= C_FLAG_COUNT; //连按键 按键按下
key->key_debounce_count = DEBOUNCE_COUT_FIRST;
++(key->key_long_count);
}
if(key->key_long_count == DEBOUNCE_LONG_TIME)
{
key->key_get = key->key_current;
key->key_flag |= C_FLAG_LONG; //短按键按下
key->key_long_count=DEBOUNCE_LONG_TIME+1;
}
}
key->key_last = key->key_current; // 保存本次键值
/* 检测到按键后,向系统上报键值 */
key->kbd_event.key = RTGUIK_UNKNOWN;
key->kbd_event.type = RTGUI_KEYDOWN;
if (key->key_get)
{
rt_kprintf("key = %x \n",key->key_get);
if (((key->key_get)==C_UP_KEY) && ((key->key_flag) & C_FLAG_SHORT))
key->kbd_event.key = RTGUIK_UP;
if (((key->key_get)==C_DOWN_KEY) && ((key->key_flag) & C_FLAG_SHORT))
key->kbd_event.key = RTGUIK_DOWN;
if (((key->key_get)==C_LEFT_KEY) && ((key->key_flag) & C_FLAG_SHORT))
key->kbd_event.key = RTGUIK_LEFT;
if (((key->key_get)==C_RIGHT_KEY) && ((key->key_flag) & C_FLAG_SHORT))
key->kbd_event.key = RTGUIK_RIGHT;
//if (((key->key_get)==C_STOP_KEY) && ((key->key_flag) & C_FLAG_SHORT))
// key->kbd_event.key = RTGUIK_UP;
//if (((key->key_get)==C_MENU_KEY) && ((key->key_flag) & C_FLAG_SHORT))
// key->kbd_event.key = RTGUIK_UP;
if ((key->key_get)==C_ENTER_KEY)
key->kbd_event.key = RTGUIK_RETURN;
if ((key->key_get)==C_HOME_KEY)
key->kbd_event.key = RTGUIK_HOME;
}
if (key->kbd_event.key != RTGUIK_UNKNOWN)
{
/* 先上报按键按下*/
rtgui_server_post_event(&(key->kbd_event.parent), sizeof(key->kbd_event));
/* delay to post up event */
rt_thread_delay(2);
/* 再上报按键松开,完成一个从按下到松开的组合*/
key->kbd_event.type = RTGUI_KEYUP;
rtgui_server_post_event(&(key->kbd_event.parent), sizeof(key->kbd_event));
}
}
int rt_hw_key_init(void)
{
GPIO_Configuration();
key = (struct rtgui_key*)rt_malloc (sizeof(struct rtgui_key));
if (key == RT_NULL)
return -1; /* no memory yet */
/* init keyboard event */
RTGUI_EVENT_KBD_INIT(&(key->kbd_event));
key->kbd_event.wid = RT_NULL;
key->kbd_event.mod = RTGUI_KMOD_NONE;
key->kbd_event.unicode = 0;
key->key_last = 0;
key->key_current = 0;
key->key_get = 0;
key->key_debounce_count = 0;
key->key_long_count = 0;
key->key_special_count = 0;
key->key_relase_count = 0;
key->key_flag = 0;
/* create 1/50=20ms timer */
key->poll_timer = rt_timer_create("key", key_timeout, RT_NULL,
RT_TICK_PER_SECOND/50, RT_TIMER_FLAG_PERIODIC);
/* 启动定时器 */
if (key->poll_timer != RT_NULL)
rt_timer_start(key->poll_timer);
return 0;
}
INIT_DEVICE_EXPORT(rt_hw_key_init);
需要说明的是,key.c是启用了五向摇杆的五个按键,而在demo_application.c中,使用了RTGUIK_LEFT和RTGUI_RIGHT两个方向的按键,代码如下:
static rt_bool_t demo_handle_key(struct rtgui_object *object, struct rtgui_event *event)
{
struct rtgui_event_kbd *ekbd = (struct rtgui_event_kbd *)event;
if (ekbd->type == RTGUI_KEYUP)
{
if (ekbd->key == RTGUIK_RIGHT)
{
demo_view_next(RT_NULL, RT_NULL);
return RT_TRUE;
}
else if (ekbd->key == RTGUIK_LEFT)
{
demo_view_prev(RT_NULL, RT_NULL);
return RT_TRUE;
}
}
return RT_TRUE;
}
(3)把key.c添加到drivers目录下以后,还需要修改下drivers目录下Sconscript脚本,打开脚本文件定位到30行附近,加入如下代码:
# add RTGUI drvers.
if GetDepend('RT_USING_RTGUI'):
src += ['key.c']
if rtconfig.RT_USING_LCD_TYPE == 'ILI932X' :
src += ['ili_lcd_general.c']
elif rtconfig.RT_USING_LCD_TYPE == 'SSD1289':
src += ['ssd1289.c']
elif rtconfig.RT_USING_LCD_TYPE == 'OTM4001A':
src += ['otm4001a.c']
CPPPATH = [cwd]
别忘了,书写格式按四个空格或者tab键递进,否则会出现执行脚本时会出现错误。然后保存。
(4)在demo_application.c中先加入三个demo文件,代码如下:
/* demo_view_benchmark();
demo_view_dc();
#ifdef RTGUI_USING_TTF
demo_view_ttf();
#endif
#ifndef RTGUI_USING_SMALL_SIZE
demo_view_dc_buffer();
#endif
demo_view_animation();
#ifndef RTGUI_USING_SMALL_SIZE
demo_view_buffer_animation();
demo_view_instrument_panel();
#endif
demo_view_window();
*/ demo_view_label();
demo_view_button();
demo_view_checkbox();
/* demo_view_progressbar();
demo_view_scrollbar();
demo_view_radiobox();
demo_view_textbox();
demo_view_listbox();
demo_view_menu();
demo_view_listctrl();
demo_view_combobox();
demo_view_slider();
demo_view_notebook();
demo_view_mywidget();
demo_plot();
demo_view_digtube();
*/
然后保存并scons编译,下载,复位后,五向摇杆向右摇下,会切换到下一个页面,向左摇下,会切换到上一个页面,因为我们的demo页面还没打开完,左右按两下是可以看到效果的。
【2】屏幕中文支持
(1)在rtconfig.h文件中打开RTGUI_USING_FONTHZ向关编译选项,定位到179行附近,代码修改如下:
#define RTGUI_USING_FONT16
/* support Chinese font */
#define RTGUI_USING_FONTHZ
/* use DFS as file interface */
#define RTGUI_USING_DFS_FILERW
/* use font file as Chinese font */
#define RTGUI_USING_HZ_FILE
/* use Chinese bitmap font */
#define RTGUI_USING_HZ_BMP
/* use small size in RTGUI */
#define RTGUI_USING_SMALL_SIZE
(2)修改rtgui_system_server_init()的初始化调用
如果按默认调用,RTGUI在打开上面的RTGUI_USING_FONTHZ选项后,显示中文时会出现乱码。
<1>打开rtgui/common/rtgui_system.c文件,定位到58行附近,注释掉组件加载的宏,代码修改如下:
int rtgui_system_server_init(void)
{
rt_mutex_init(&_screen_lock, "screen", RT_IPC_FLAG_FIFO);
/* the graphic device driver must be set before initialization */
RT_ASSERT(rtgui_graphic_driver_get_default() != RT_NULL);
/* init image */
rtgui_system_image_init();
/* init font */
rtgui_font_system_init();
/* set the rect of main window to full screen */
rtgui_graphic_driver_get_rect(rtgui_graphic_driver_get_default(), &_mainwin_rect);
/* init rtgui server */
rtgui_topwin_init();
rtgui_server_init();
/* use driver rect for main window */
rtgui_graphic_driver_get_rect(rtgui_graphic_driver_get_default(), &_mainwin_rect);
/* init theme */
rtgui_system_theme_init();
return 0;
}
//INIT_APP_EXPORT(rtgui_system_server_init);
<2>在application.c的RTGUI初始化部分加入rtgui_system_server_init()调用,定位到83行附近,确认代码修改和下面一致:
void rt_init_thread_entry(void* parameter)
{
/* initialization RT-Thread Components */
rt_components_init();
#if defined(RT_USING_DFS) && defined(RT_USING_DFS_ELMFAT)
/* mount SPI flash as root directory */
if (dfs_mount("flash0", "/", "elm", 0, 0) == 0)
{
rt_kprintf("flash0 mount to /.\n");
}
else
rt_kprintf("flash0 mount to / failed.\n");
/* mount SD Card as /dev/sd directory */
if (dfs_mount("sd0", "/dev", "elm", 0, 0) == 0)
{
rt_kprintf("sd0 mount to /dev.\n");
}
else
rt_kprintf("sd0 mount to /dev failed.\n");
#endif /* RT_USING_DFS */
#ifdef RT_USING_RTGUI
{
rt_device_t lcd;
/* find lcd device */
lcd = rt_device_find("lcd");
/* set lcd device as rtgui graphic driver */
rtgui_graphic_set_device(lcd);
rtgui_system_server_init();
gui_application_init();
#ifdef RTGUI_USING_CALIBRATION
calibration_set_restore(calibration_restore);//initialize the pointer to load user data
calibration_set_after(calibration_store); //initialize the poiter to save user data
calibration_init();
#endif /* RTGUI_USING_CALIBRATION */
}
#endif /* RT_USING_RTGUI */
需要强调下,rt_components_init()需要在开始部分调用,因为硬件初始化都是在这个函数中进行的。
(3)确认各硬件初始化代码是否加入了组件调用导入的宏INIT_DEVICE_EXPORT(xxx_xxx_init);
<1>otm4001a.c文件的int rt_hw_lcd_init(void)的后面,代码如下:
int rt_hw_lcd_init(void)
{
/* register lcd device */
_lcd_device.type = RT_Device_Class_Graphic;
_lcd_device.init = lcd_init;
_lcd_device.open = lcd_open;
_lcd_device.close = lcd_close;
_lcd_device.control = lcd_control;
_lcd_device.read = RT_NULL;
_lcd_device.write = RT_NULL;
_lcd_device.user_data = &otm4001_ops;
otm4001_hw_init();
lcd_set_backlight(200);
/* register graphic device driver */
rt_device_register(&_lcd_device, "lcd",
RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
return 0;
}
INIT_DEVICE_EXPORT(rt_hw_lcd_init);
<2>touch_driver.c的468行附近在int rtgui_touch_hw_init(void)的末尾处,代码如下:
/* register touch device to RT-Thread */
rt_device_register(&(touch->parent), "touch", RT_DEVICE_FLAG_RDWR);
return 0;
}
INIT_DEVICE_EXPORT(rtgui_touch_hw_init);
<3>在key.c文件的262行附近的int rt_hw_key_init(void)的末尾处,代码如下:
/* startup the timer */
if (key->poll_timer != RT_NULL)
rt_timer_start(key->poll_timer);
return 0;
}
INIT_DEVICE_EXPORT(rt_hw_key_init);
(4)加载汉字库,可参考下面步骤进行,如果文件系统已经挂载,下面步骤2的前面两步可不必进行。
1. 准备资源文件:
把sdcard目录下所有目录复制到SD卡根目录,
然后把SD卡插到开发板上面备用。
2. finsh执行:
2.1 格式化flash,然后重新启动。
finsh>>mkfs("elm", "flash0")
2.2 创建目录用于挂载SD卡及资源文件目录,因需要挂载SD卡,所以最后需要重新启动。
finsh>>mkdir("/dev")
finsh>>mkdir("/resource")
2.3 准备就绪后,启动会显示根目录和SD卡都挂载成功,然后就可以开始复制资源文件了。
finsh>>copy("/dev/resource/gbk2uni.tbl", "/resource/gbk2uni.tbl")
finsh>>copy("/dev/resource/uni2gbk.tbl", "/resource/uni2gbk.tbl")
finsh>>copy("/dev/resource/hzk12.fnt", "/resource/hzk12.fnt")
finsh>>copy("/dev/resource/hzk16.fnt", "/resource/hzk16.fnt")
2.4 所有资源文件复制完成后,重启动即可
(5)如果上面修改完成并保存了,执行scons编译,下载的开发板上后可以看到之前的乱码变成中文了。
【3】焦点支持
未完,待续。。。