RT-Thread 学习笔记(十四)--- 开启基于RTGUI的LCD显示功能(4)<demo组件的按键响应和焦点支持>

软件环境: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】焦点支持

未完,待续。。。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aping_cs_dn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值