1.9. key
1.9.1. 代码目录和相关文件
apps/app/bsp/common/key/
key/
- key.c
- key.h
- key_drv_ad.c
- key_drv_ad.h
- key_drv_io.c
- key_drv_io.h
- key_drv_matrix.c
- key_drv_matrix.h
目前sdk中包含的按键有:
IOKEY
ADKEY
矩阵按键
1.9.2. key使用说明
Note
在hid和trans工程的板级中都会有相关的板级宏控制,以board_aw31n_demo_cfg.h
为例,可以自行选择相关的key配置,打开对应的宏,接入相关的IO即可
//*********************************************************************************//
// adkey 配置 //
//*********************************************************************************//
#define AD_KEY_IO IO_PORTA_08 // 可用的IO见adc_ch_io_table
#define KEY_AD_EN ENABLE //<AD按键使能
#define TCFG_ADKEY_VALUE0 0
#define TCFG_ADKEY_VALUE1 1
#define TCFG_ADKEY_VALUE2 2
#define TCFG_ADKEY_VALUE3 3
#define TCFG_ADKEY_VALUE4 4
#define TCFG_ADKEY_VALUE5 5
#define TCFG_ADKEY_VALUE6 6
#define TCFG_ADKEY_VALUE7 7
#define TCFG_ADKEY_VALUE8 8
#define TCFG_ADKEY_VALUE9 9
#define TCFG_ADC_VBAT_CH_EN ENABLE
#define TCFG_ADC_VTEMP_CH_EN ENABLE
//*********************************************************************************//
// iokey 配置 //
//*********************************************************************************//
#define KEY_IO_EN DISABLE //<IO按键使能
#define MOUSE_KEY_SCAN_MODE DISABLE_THIS_MOUDLE
#define TCFG_IOKEY_POWER_CONNECT_WAY ONE_PORT_TO_LOW //按键一端接低电平一端接IO
#define TCFG_IOKEY_POWER_ONE_PORT IO_PORTA_00 //IO按键端口
#define TCFG_IOKEY_POWER_ONE_PORT_VALUE 0x0 //power port键值
#define TCFG_IOKEY_PREV_CONNECT_WAY ONE_PORT_TO_LOW //按键一端接低电平一端接IO
#define TCFG_IOKEY_PREV_ONE_PORT IO_PORTA_01
#define TCFG_IOKEY_PREV_ONE_PORT_VALUE 0x1 //prev port键值
//*********************************************************************************//
// matrix key 配置 //
//*********************************************************************************//
#define KEY_MATRIX_EN DISABLE
#define MATRIX_KEY_ROW1 IO_PORTA_00
#define MATRIX_KEY_ROW2 IO_PORTA_01
#define MATRIX_KEY_ROW3 IO_PORTA_02
#define MATRIX_KEY_ROL1 IO_PORTA_08
#define MATRIX_KEY_ROL2 IO_PORTA_09
#define MATRIX_KEY_ROL3 IO_PORTA_10
1.9.3. key总系统驱动原理
Note
在key.c
文件中有两个接口key_init
和key_driver_scan
,其中key_init
做所有按键的初始化操作,key_driver_scan
则用来扫描所有注册的按键驱动,并且key_driver_scan
函数在tick_timer中断中调用,通过轮询或中断的方式周期性地检测按键的状态,最终检测到按键值后发送SYS_KEY_EVENT
消息到应用层处理响应,流程图如下
相关接口(key.c):
void key_init(void)
:按键初始化
key_io_t get_key_value(void)
:获取按键值
void key_driver_scan(void *_scan_para)
:按键扫描函数
1.9.4. iokey
Note
当按键未被按下时,上拉或下拉电阻确保微控制器的输入引脚保持在高电平或低电平状态。当按键被按下时,它闭合电路,改变微控制器引脚的电平状态。微控制器通过检测这个电平变化来识别按键操作,并执行预设的函数或命令。
相关接口(key_drv_io.c):
int iokey_init(const struct iokey_platform_data *iokey_data)
:按键初始化
u8 get_iokey_value(void)
:获取iokey按键值
1.9.5. adkey
ADKey的基本原理是通过使用模数转换器(ADC)将模拟按键的电压信号转换为数字值。以下是ADKey的一般工作原理:
输入信号:ADKey通过连接到模拟输入引脚的方式接收模拟按键的电压信号。按键的状态通常会导致引脚上的电压发生变化。
模数转换:ADKey使用内部的模数转换器(ADC)来将模拟信号转换为数字值。ADC将输入引脚上的电压进行采样,并将其转换为对应的数字值。转换的精度和范围取决于ADC的位数和参考电压等参数。
数字处理:一旦ADC完成模数转换,ADKey将获得一个数字值,表示模拟按键的状态。这个数字值可以被传递到微控制器、处理器或其他数字系统中进行进一步的处理和判断。
按键映射:ADKey可以根据应用的需要,将数字值映射到相应的按键操作或功能。例如,可以将数字值与预定义的按键映射表进行比较,以确定按下的是哪个按键或执行相应的操作。
相较于iokey,ADKey可以支持多个模拟按键同时输入和检测。每个按键的电压信号可以通过不同的引脚连接到ADC,以实现多键输入。而IOKey通常需要为每个物理按键分配一个GPIO引脚。
相关内容和接口(key_drv_ad.c):
#define ADC10_30 (ADC10_33 * 1000 / (1000 + R_UP)) //100K
#define ADC10_27 (ADC10_33 * 510 / (510 + R_UP)) //51K
#define ADC10_23 (ADC10_33 * 240 / (240 + R_UP)) //24K
#define ADC10_20 (ADC10_33 * 150 / (150 + R_UP)) //15K
#define ADC10_17 (ADC10_33 * 100 / (100 + R_UP)) //10K
#define ADC10_13 (ADC10_33 * 68 / (68 + R_UP)) //6.8K
#define ADC10_10 (ADC10_33 * 47 / (47 + R_UP)) //4.7K
#define ADC10_07 (ADC10_33 * 22 / (22 + R_UP)) //2.2K
#define ADC10_04 (ADC10_33 * 10 / (10 + R_UP)) //1K
#define ADC10_00 (0)
上面的宏定义是 AD 按键不同电阻根据电阻分压原理计算出来的电压值转换后的 ADC 值
#define AD_NOKEY ((ADC10_33 + ADC10_30) / 2)
#define ADKEY1_0 ((ADC10_30 + ADC10_27) / 2)
#define ADKEY1_1 ((ADC10_27 + ADC10_23) / 2)
#define ADKEY1_2 ((ADC10_23 + ADC10_20) / 2)
#define ADKEY1_3 ((ADC10_20 + ADC10_17) / 2)
#define ADKEY1_4 ((ADC10_17 + ADC10_13) / 2)
#define ADKEY1_5 ((ADC10_13 + ADC10_10) / 2)
#define ADKEY1_6 ((ADC10_10 + ADC10_07) / 2)
#define ADKEY1_7 ((ADC10_07 + ADC10_04) / 2)
#define ADKEY1_8 ((ADC10_04 + ADC10_00) / 2)
Note
上面为 ADC 取中间值的宏定义,这个也好理解,假如 KEY0 的键值 ADC 对应的是 920,KEY1 的是 846,那么就取两个的中间值,当大于这个中间值我们就可以认为是 KEY0,小于这个就认为是 KEY1
可以使用的adkey io可以见(adc_api.c):
static u8 adc_ch_io_table[16] = { //gpio->adc_ch 表
IO_PORTA_00,
IO_PORTA_01,
IO_PORTA_02,
IO_PORTA_03,
IO_PORTA_04,
IO_PORTA_05,
IO_PORTA_06,
IO_PORTF_02,
IO_PORTA_08,
IO_PORTA_09,
IO_PORTA_10,
IO_PORTA_11,
/* IO_PORT_FSPG, */
IO_PORT_DP,
IO_PORT_DM,
};
1.9.6. 矩阵key
sdk的矩阵key采用的是三行三列的矩阵按键驱动
以下是三行三列矩阵按键的基本原理:
行线和列线:矩阵按键布局中,按键被组织成一个二维的行列结构。行线和列线是导线或导轨,分别沿着矩阵的行和列方向布置。通常,行线和列线是平行排列的,形成一个网格。
按键连接:每个按键的一个端口连接到行线,另一个端口连接到列线。按下按键时,行线和列线之间形成了通路,允许电流流过。
按键检测:通过扫描行线和列线的连接状态,可以检测按键的按下。检测过程通常由微控制器或其他控制电路完成。它会依次选定每一行,并读取与之相连的列线上的状态。如果检测到行线与列线之间的连接,则表示相应的按键被按下。
下面是一个简单的示意图,下面是一个简单的示意图,展示了一个三行三列的矩阵按键布局和连接方式:
┌───────┬───────┬───────┐
│ │ │ │
│ 1 │ 2 │ 3 │
│ │ │ │
├───────┼───────┼───────┤
│ │ │ │
│ 4 │ 5 │ 6 │
│ │ │ │
├───────┼───────┼───────┤
│ │ │ │
│ 7 │ 8 │ 9 │
│ │ │ │
└───────┴───────┴───────┘
在这个示意图中,有九个按键(编号1到9),按键按行列布局。行线和列线以平行的方式连接到按键的引脚上。
例如,按键1的一个端口连接到行线1,另一个端口连接到列线1。同样,按键2连接到行线1和列线2,按键3连接到行线1和列线3,以此类推。
通过扫描行线和列线的连接状态,可以检测按键的按下。通过逐一选定每一行并读取与之相连的列线上的状态,可以确定哪个按键被按下。
希望这个示意图能够帮助你更好地理解三行三列的矩阵按键布局。如果还有其他问题,请随时提问。
相关接口(key_drv_matrix.c):
void matrix_key_init(void)
:矩阵按键初始化
u8 matrix_key_scan()
:获取iokey按键值
1.9.7. 按键唤醒配置
Note
按键唤醒包括软关机保护配置和低功耗唤醒配置,配置位于board_aw31n_demo.c
(以aw31n板级为例)中,根据需要可以添加你需要的GPIO的软关机和低功耗保护。值得注意的是KEY_WAKE_MAX_NUMS的配置需要大于等于的你唤醒口的数量
/************************** SOFTOFF IO PROTECT****************************/
void gpio_config_soft_poweroff(void)
{
PORT_TABLE(g);
#if KEY_AD_EN
PORT_PROTECT(AD_KEY_IO);
#endif
#if KEY_IO_EN
PORT_PROTECT(TCFG_IOKEY_POWER_ONE_PORT);
PORT_PROTECT(TCFG_IOKEY_PREV_ONE_PORT);
#endif
__port_init((u32)gpio_config);
}
/************************** SOFTOFF IO PROTECT****************************/
/************************** IO WAKE UP CONFIG****************************/
#define WAKE_IO_MAX_NUMS 3
static struct _p33_io_wakeup_config keys_config[WAKE_IO_MAX_NUMS];
static void init_key_io_wakeup(const struct _p33_io_wakeup_config *config)
{
p33_io_wakeup_port_init(config);
p33_io_wakeup_enable(config->gpio, 1);
}
struct _p33_io_wakeup_config create_key_io_wakeup_config(u32 gpio, enum gpio_mode pullup_down_mode,
P33_IO_WKUP_FLT filter, P33_IO_WKUP_EDGE edge, void (*callback)(P33_IO_WKUP_EDGE edge))
{
return (struct _p33_io_wakeup_config) {
.gpio = gpio,
.pullup_down_mode = pullup_down_mode,
.filter = filter,
.edge = edge,
.callback = callback,
};
}
void key_wakeup_init()
{
u8 index = 0;
#if KEY_IO_EN
keys_config[index++] = create_key_io_wakeup_config(TCFG_IOKEY_POWER_ONE_PORT,
PORT_INPUT_PULLUP_10K, PORT_FLT_DISABLE, FALLING_EDGE, key_active_set);
keys_config[index++] = create_key_io_wakeup_config(TCFG_IOKEY_PREV_ONE_PORT, PORT_INPUT_PULLUP_10K,
PORT_FLT_DISABLE, FALLING_EDGE, key_active_set);
#endif
#if KEY_AD_EN
keys_config[index++] = create_key_io_wakeup_config(AD_KEY_IO, PORT_INPUT_FLOATING,
PORT_FLT_DISABLE, FALLING_EDGE, key_active_set);
#endif
// 初始化和启用IO唤醒
for (u8 i = 0; i < index; i++) {
init_key_io_wakeup(&keys_config[i]);
}
}
/************************** IO WAKE UP CONFIG****************************/