介绍
按键设备数码管设备、蜂鸣器设备不太一样,按键设备时用GPIO输入。通过检测按键引脚电平来判断按键状态,并通过处理生成按键事件。
按键IO配置
四个按键分别接在了PA0、PE2、PE3、PE4,其中KEY_UP是高电平有效。
按键设备配置
如同数码管、蜂鸣器一样,也要完成三件事。
首先,在对应bsp的“\board\ports”目录下创建“drv_key.c”和“drv_key.h”文件。
然后,在“Kconfig”文件中添加以下配置项
config BSP_USING_KEY
bool "Enable KEY"
select BSP_USING_GPIO
default n
最后,在“SConscript”文件中添加文件依赖
if GetDepend(['BSP_USING_KEY']):
src += Glob('ports/drv_key.c')
打开图形化配置工具,进入到以下目录
Hardware Drivers Config
—> Onboard Peripheral Drivers
选中刚才新增“Enable KEY”项,保存配置,退出,重新生成工程。
按键驱动编写
大体步骤如同数码管、蜂鸣器等IO设备。这里须在按键设备结构体里面添加特定回调函数,用来通知按键状态。
/**
* key device
*/
struct drv_key_device
{
struct rt_device parent;
void (*press_handle)(uint8_t key_id);
void (*realese_handle)(uint8_t key_id);
void (*msg_handle)(struct key_press_message message);
};
这里面定义按键的ID如下
/**
* key id
*/
#define KEY_ID_UP (0)
#define KEY_ID_DOWN (1)
#define KEY_ID_LEFT (2)
#define KEY_ID_RIGHT (3)
#define KEY_ID_MAX (4)
定义按键消息如下
/**
* key message
*/
struct key_press_message
{
uint8_t key_id; // 按键ID
uint8_t state; // 按键状态
uint8_t press_type; // 按下类型,长按或者连续按,看 key press type
uint16_t long_time; // 长按时间,单位 KEY_SCAN_TIME
uint16_t series_times; // 连续按,按键次数
};
在源文件中,需完成以下接口,按键按下检测
static uint8_t get_key_pin_state(uint8_t key_id)
不管高电平还是低电平有效,按下返回1,否则返回0。
static void key_scan(void)
扫描函数,周期调用,检测按键状态,生成按键按键消息,调用回调函数。
回到设备标准函数操作集,除了写操作函数,其他函数都需进行实现。打开操作函数就实现创建线程或者定时器来周期调用扫描函数(这里就用了定时器,因为定时器最好就是不用处理太多的事情,所以在调用按键回调函数时候不宜有太多的事情去做,可以通知其他线程去做),关闭函数里面就对应关闭线程或者定时器即可,读取函数就应该能读取到按键的消息,控制函数除了获取按键状态还应留给应用程序可以设置的回调函数的命令。
初始化函数参考如下,根据引脚设置对应输入模式
rt_pin_mode(GET_PIN(A, 0), PIN_MODE_INPUT_PULLDOWN);
rt_pin_mode(GET_PIN(E, 2), PIN_MODE_INPUT_PULLUP);
rt_pin_mode(GET_PIN(E, 3), PIN_MODE_INPUT_PULLUP);
rt_pin_mode(GET_PIN(E, 4), PIN_MODE_INPUT_PULLUP);
这里特别附上读取和控制函数
static rt_size_t drv_key_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
RT_ASSERT(dev);
RT_ASSERT(size <= sizeof(struct key_press_message));
if (pos >= KEY_ID_MAX)
{
return 0;
}
*(struct key_press_message *)buffer = _key_state[pos].message;
rt_memset(&_key_state[pos].message, 0, sizeof(struct key_press_message));
return sizeof(struct key_press_message);
}
static rt_err_t drv_key_control(rt_device_t dev, int cmd, void *args)
{
struct drv_key_device *key = (struct drv_key_device *)dev;
struct key_press_message *message = (struct key_press_message*)args;
switch (cmd)
{
case KEY_CTRL_SET_PRESS_HANDLE:
{
key->press_handle = (void (*)(uint8_t))args;
}
break;
case KEY_CTRL_SET_RELEASE_HANDLE:
{
key->realese_handle = (void (*)(uint8_t))args;
}
break;
case KEY_CTRL_SET_MSG_HANDLE:
{
key->msg_handle = (void (*)(struct key_press_message))args;
}
break;
case KEY_CTRL_GET_KEY_STATE:
{
*message = _key_state[message->key_id].message;
}
break;
}
return RT_EOK;
}
其中“_key_state”是如下结构体
/**
* key inspect struct
*/
struct key_inspect
{
int16_t press_times; // 按下次数,-1为长按
uint16_t null_times; // 扫描空按周期数
uint16_t long_times; // 扫描长按周期数
uint16_t cur_state : 1; // 当前状态
uint16_t prev_state : 1; // 上个周期状态
uint16_t press_flag : 2; // 扫描标志,看 key scan status
uint16_t long_flag : 1; // 长按标志
uint16_t press_state : 1; // 扫描按下状态
};
/**
* key state
*/
struct key_state
{
struct key_press_message message;
struct key_inspect inspect;
};
“struct key_inspect”是扫描时候一些标志的结构体。
如此,这个按键驱动就可以实现按键按下和释放通知,特殊按键事件通知,如快速连续按和长按,识别按的次数和长按的时间。
文件链接
结尾
rtthread学习中,打开主页查看更多笔记