AC63_BT_SDK/apps/common/device/key/matrix_keyboard.c

352 lines
12 KiB
C
Raw Permalink Normal View History

2025-02-18 15:40:42 +08:00
#include "matrix_keyboard.h"
#include "key_driver.h"
#include "gpio.h"
#include "system/event.h"
#include "app_config.h"
#include "timer.h"
#include "asm/power_interface.h"
#include "asm/power/p33.h"
#if TCFG_MATRIX_KEY_ENABLE && !TCFG_EX_MCU_ENABLE
#define MATRIX_NO_KEY 0x1 //没有按键时的IO电平
#define MATRIX_LONG_TIME 35
#define MATRIX_HOLD_TIME 35+10
#define MATRIX_KEY_FILTER_TIME 2
int (*matrix_key_data_send)(u8 report_id, u8 *data, u16 len) = NULL;
static volatile u8 is_key_active = 0;
static volatile u8 keep_wakeup_hold = 0;
static int matrix_key_wakeup_hold_timer = 0;
static int matrix_key_timer = 0;
static matrix_key_st key_st[ROW_MAX][COL_MAX];
static matrix_key_param *this = NULL;
u8 notify_key_array[8] = {0};
u8 key_map[COL_MAX] = {0};
static u16 key_status_array[6] = {0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff};
static void full_key_array(u8 row, u8 col, u8 st)
{
u8 offset = 0;
u8 mark = 0, mark_offset = 0;
//最多推6个按键出来如果需要推多个按键需要自行修改每个u16 低八位标识row 高八位标识col
u16 key_value = (row | col << 8);
for (offset = 0; offset < sizeof(key_status_array) / sizeof(u16); offset++) {
if (key_status_array[offset] == key_value && st == MATRIX_KEY_UP) { //找到列表中有当前按键且按键状态抬起,则将按键移除列表
mark = 1; //记录相同键值所在位置
mark_offset = offset;
if (mark_offset == sizeof(key_status_array) / sizeof(u16) - 1) {
key_status_array[mark_offset] = 0xffff;
break;
}
} else if (key_status_array[offset] == 0xffff) {
if (mark) {
memcpy(key_status_array + mark_offset, key_status_array + mark_offset + 1, (offset - mark_offset - 1) * 2);
key_status_array[offset - 1] = 0xffff;
} else if (st == MATRIX_KEY_SHORT) { //需要状态为短按才会把键值填充到数组中,防止按键满了之后把抬起也填充进去
key_status_array[offset] = key_value;
}
break;
} else if (mark && (offset == sizeof(key_status_array) / sizeof(u16) - 1)) {
memcpy(key_status_array + mark_offset, key_status_array + mark_offset + 1, (sizeof(key_status_array) / sizeof(u16) - mark_offset - 1) * 2);
key_status_array[sizeof(key_status_array) / sizeof(u16) - 1] = 0xffff;
break;
}
}
}
void full_key_map(u8 row, u8 col, u8 st)
{
if (st) {
key_map[col] |= BIT(row);
} else {
key_map[col] &= ~BIT(row);
}
}
void P33_AND_WKUP_EDGE(u8 data);
void P33_OR_WKUP_EDGE(u8 data);
enum {
IO_STATUS_HIGH_DRIVER = 0,
IO_STATUS_OUTPUT_HIGH,
IO_STATUS_OUTPUT_LOW,
IO_STATUS_INPUT_PULL_DOWN,
IO_STATUS_INPUT_PULL_UP,
};
void matrix_key_set_io_state(u8 state, u32 *io_table, u8 len)
{
u8 i = 0;
u32 porta_value = 0;
u32 portb_value = 0;
u32 portc_value = 0;
u32 portd_value = 0;
for (; i < len; i++) {
switch (io_table[i] / IO_GROUP_NUM) {
case 0: //JL_PORTA
porta_value |= BIT(io_table[i] % IO_GROUP_NUM);
break;
case 1: //JL_PORTB
portb_value |= BIT(io_table[i] % IO_GROUP_NUM);
break;
case 2: //JL_PORTC
portc_value |= BIT(io_table[i] % IO_GROUP_NUM);
break;
case 3: //JL_PORTD
portd_value |= BIT(io_table[i] % IO_GROUP_NUM);
break;
default://USB
break;
}
}
switch (state) {
case IO_STATUS_HIGH_DRIVER:
JL_PORTA->DIR |= porta_value;
JL_PORTA->DIE |= porta_value;
JL_PORTA->PU &= ~porta_value;
JL_PORTA->PD &= ~porta_value;
JL_PORTB->DIR |= portb_value;
JL_PORTB->DIE |= portb_value;
JL_PORTB->PU &= ~portb_value;
JL_PORTB->PD &= ~portb_value;
JL_PORTC->DIR |= portc_value;
JL_PORTC->DIE |= portc_value;
JL_PORTC->PU &= ~portc_value;
JL_PORTC->PD &= ~portc_value;
JL_PORTD->DIR |= portd_value;
JL_PORTD->DIE |= portd_value;
JL_PORTD->PU &= ~portd_value;
JL_PORTD->PD &= ~portd_value;
break;
case IO_STATUS_OUTPUT_HIGH:
JL_PORTA->DIR &= ~porta_value;
JL_PORTA->OUT |= porta_value;
JL_PORTB->DIR &= ~portb_value;
JL_PORTB->OUT |= portb_value;
JL_PORTC->DIR &= ~portc_value;
JL_PORTC->OUT |= portc_value;
JL_PORTD->DIR &= ~portd_value;
JL_PORTD->OUT |= portd_value;
break;
case IO_STATUS_OUTPUT_LOW:
JL_PORTA->DIR &= ~porta_value;
JL_PORTA->OUT &= ~porta_value;
JL_PORTB->DIR &= ~portb_value;
JL_PORTB->OUT &= ~portb_value;
JL_PORTC->DIR &= ~portc_value;
JL_PORTC->OUT &= ~portc_value;
JL_PORTD->DIR &= ~portd_value;
JL_PORTD->OUT &= ~portd_value;
break;
case IO_STATUS_INPUT_PULL_DOWN:
JL_PORTA->DIR |= porta_value;
JL_PORTA->DIE |= porta_value;
JL_PORTA->PU &= ~porta_value;
JL_PORTA->PD |= porta_value;
JL_PORTB->DIR |= portb_value;
JL_PORTB->DIE |= portb_value;
JL_PORTB->PU &= ~portb_value;
JL_PORTB->PD |= portb_value;
JL_PORTC->DIR |= portc_value;
JL_PORTC->DIE |= portc_value;
JL_PORTC->PU &= ~portc_value;
JL_PORTC->PD |= portc_value;
JL_PORTD->DIR |= portd_value;
JL_PORTD->DIE |= portd_value;
JL_PORTD->PU &= ~portd_value;
JL_PORTD->PD |= portd_value;
break;
case IO_STATUS_INPUT_PULL_UP:
JL_PORTA->DIR |= porta_value;
JL_PORTA->DIE |= porta_value;
JL_PORTA->PU |= porta_value;
JL_PORTA->PD &= ~porta_value;
JL_PORTB->DIR |= portb_value;
JL_PORTB->DIE |= portb_value;
JL_PORTB->PU |= portb_value;
JL_PORTB->PD &= ~portb_value;
JL_PORTC->DIR |= portc_value;
JL_PORTC->DIE |= portc_value;
JL_PORTC->PU |= portc_value;
JL_PORTC->PD &= ~portc_value;
JL_PORTD->DIR |= portd_value;
JL_PORTD->DIE |= portd_value;
JL_PORTD->PU |= portd_value;
JL_PORTD->PD &= ~portd_value;
break;
}
}
#if 1
void port_edge_wakeup_control(u16 data);
void matrix_key_scan(void)
{
u8 row, col;
u8 cur_key_value = MATRIX_NO_KEY, notify = 0;
static u8 wk_toggle = 0;
//g_printf("scan...\n");
//P33_LEVEL_WKUP_EN(0);
port_edge_wakeup_control(0);
//p33_tx_1byte(P3_WKUP_EN, 0); //关掉wakeup防止扫描过程一直唤醒
//不直接调用gpio.c的接口主要是由于想控制扫描时间。这样能把时间从1.8ms缩减到800us
matrix_key_set_io_state(IO_STATUS_HIGH_DRIVER, this->col_pin_list, this->col_num);
is_key_active = 0;
for (col = 0; col < this->col_num; col ++) {
matrix_key_set_io_state((MATRIX_NO_KEY) ? IO_STATUS_OUTPUT_LOW : IO_STATUS_OUTPUT_HIGH, &(this->col_pin_list[col]), 1);
//gpio_direction_output(this->col_pin_list[col], !MATRIX_NO_KEY);
for (row = 0; row < this->row_num; row ++) {
cur_key_value = gpio_read(this->row_pin_list[row]);
if (cur_key_value != MATRIX_NO_KEY) {
is_key_active = 1;
}
if (cur_key_value != (key_st[row][col]).last_st) {
if (cur_key_value == MATRIX_NO_KEY) { //按键抬起判断
(key_st[row][col]).press_cnt = 0;
//printf("row:%d col:%d [UP]\n", row, col);
(MATRIX_NO_KEY) ? P33_OR_WKUP_EDGE(row) : P33_AND_WKUP_EDGE(row);
//P33_AND_WKUP_EDGE(row);
full_key_map(row, col, 0);
notify = 1;
} else { //按键初次按下
printf("row:%d col:%d [SHORT]\n", row, col);
full_key_map(row, col, 1);
(MATRIX_NO_KEY) ? P33_AND_WKUP_EDGE(row) : P33_OR_WKUP_EDGE(row);
//P33_OR_WKUP_EDGE(row);
notify = 1;
}
} else { //判断是否按键抬起
if (cur_key_value != MATRIX_NO_KEY) {
(key_st[row][col]).press_cnt ++;
if ((key_st[row][col]).press_cnt == MATRIX_LONG_TIME) {
/* printf("row:%d col:%d [LONG]\n", row, col); */
} else if ((key_st[row][col]).press_cnt == MATRIX_HOLD_TIME) {
/* printf("row:%d col:%d [HOLD]\n", row, col); */
(key_st[row][col]).press_cnt = MATRIX_LONG_TIME;
} else {
//press_cnt还未累加够
}
}
}
(key_st[row][col]).last_st = cur_key_value;
}
//gpio_direction_input(this->col_pin_list[col]);
matrix_key_set_io_state(IO_STATUS_HIGH_DRIVER, &(this->col_pin_list[col]), 1);
}
matrix_key_set_io_state((MATRIX_NO_KEY == 1) ? IO_STATUS_OUTPUT_LOW : IO_STATUS_OUTPUT_HIGH, this->col_pin_list, this->col_num);
port_edge_wakeup_control(0xff);
//p33_tx_1byte(P3_WKUP_EN, 0xff);
//P33_LEVEL_WKUP_EN(1);
if (notify) {
struct sys_event e;
e.type = SYS_MATRIX_KEY_EVENT;
e.u.matrix_key.map = key_map;
e.arg = (void *)DEVICE_EVENT_FROM_KEY;
sys_event_notify(&e);
}
}
#else
void matrix_key_scan(void)
{
u8 row, col;
u8 cur_key_value = MATRIX_NO_KEY, notify = 0;
for (row = 0; row < this->row_num; row ++) {
gpio_direction_input(this->row_pin_list[row]);
gpio_set_die(this->row_pin_list[row], 1);
gpio_set_pull_up(this->row_pin_list[row], 1);
gpio_set_pull_down(this->row_pin_list[row], 0);
}
for (col = 0; col < this->col_num; col ++) {
gpio_direction_output(this->col_pin_list[col], 0);
gpio_set_die(this->col_pin_list[col], 1);
for (row = 0; row < this->row_num; row ++) {
if (gpio_read(this->row_pin_list[row]) == 0) {
printf("col:%x row:%x\n", col, row);
}
}
gpio_direction_input(this->col_pin_list[col]);
gpio_set_pull_up(this->col_pin_list[col], 0);
gpio_set_pull_up(this->col_pin_list[col], 0);
gpio_set_die(this->col_pin_list[col], 0);
}
}
#endif
void matrix_key_wakeup_timeout_handler(void *arg)
{
is_key_active = 1;
}
void matrix_key_wakeup_keep(void)
{
is_key_active = 1;
if (matrix_key_wakeup_hold_timer) {
sys_s_hi_timeout_del(matrix_key_wakeup_hold_timer);
}
matrix_key_wakeup_hold_timer = sys_s_hi_timerout_add(NULL, matrix_key_wakeup_timeout_handler, 10);
}
void matrix_key_wakeup(u8 idx, u32 gpio)
{
r_printf("matrix_key wkup!\n");
matrix_key_wakeup_keep();
}
int matrix_key_init(matrix_key_param *param)
{
if (!param) {
return -1; //参数无效
}
this = param;
u8 row, col;
for (row = 0; row < this->row_num; row ++) {
(MATRIX_NO_KEY) ? P33_OR_WKUP_EDGE(row) : P33_AND_WKUP_EDGE(row);
for (col = 0; col < this->col_num; col ++) {
(key_st[row][col]).last_st = MATRIX_NO_KEY;
(key_st[row][col]).press_cnt = 0;
}
}
printf("key_row:%d rol:%d\n", this->row_num, this->col_num);
if (matrix_key_timer) {
sys_s_hi_timer_del(matrix_key_timer);
}
matrix_key_set_io_state((MATRIX_NO_KEY) ? IO_STATUS_OUTPUT_LOW : IO_STATUS_OUTPUT_HIGH, this->col_pin_list, this->col_num);
matrix_key_set_io_state((MATRIX_NO_KEY) ? IO_STATUS_INPUT_PULL_UP : IO_STATUS_INPUT_PULL_DOWN, this->row_pin_list, this->row_num);
port_edge_wkup_set_callback(matrix_key_wakeup);
matrix_key_timer = sys_s_hi_timer_add(NULL, matrix_key_scan, 10);
return 0;
}
static u8 matrix_key_idle_query(void)
{
return !is_key_active;
}
REGISTER_LP_TARGET(matrix_key_lp_target) = {
.name = "matrix_key",
.is_idle = matrix_key_idle_query,
};
#endif