#include "app_config.h" #include "asm/ctmu.h" #include "asm/gpio.h" #if TCFG_CTMU_TOUCH_KEY_ENABLE #define LOG_TAG_CONST CTMU #define LOG_TAG "[ctmu]" #define LOG_ERROR_ENABLE #define LOG_DEBUG_ENABLE #define LOG_INFO_ENABLE /* #define LOG_DUMP_ENABLE */ #define LOG_CLI_ENABLE #include "debug.h" #define CTMU_CON0 JL_CTM->CON0 #define CTMU_CON1 JL_CTM->CON1 #define CTMU_ADR JL_CTM->ADR #define CTMU_MAX_CH 16 static u32 ctm_buf[CTMU_MAX_CH * 2] = {0}; static u8 port_index_mapping_talbe[CTMU_KEY_CH_MAX] = {0}; static u32 Touchkey_pre_value[CTMU_KEY_CH_MAX] = {0}; static u32 Touchkey_normal_value[CTMU_KEY_CH_MAX] = {0}; #define CALIBRATE_CYCLE 200//周期大约1秒 static u32 Touchkey_calibrate_cnt[CTMU_KEY_CH_MAX] = {0}; static u32 Touchkey_calibrate_tmp_value[CTMU_KEY_CH_MAX] = {0}; static u8 Touchkey_state = 0; static const struct ctmu_touch_key_platform_data *user_data = NULL; //=================================================================================// /* ctmu原理: 1.三个时钟源: 1)闸门时钟源(可选), 分频值可以选 2)放电时钟源(固定是lsb), 分频值可以选 3)充电时钟源(可选), 分频值不可选 2.闸门时间内 _________________________________ __| |__ __ __ __ __ __ 放电时计数 __| |__| |__| |__| |__| |__ 3. 过程: 放电 --> 充电(计数) --> 放电[该过程可选] 4.分析计数值 */ //=================================================================================// //时钟闸门时钟源选择, 可分频 enum { GATE_SOURCE_OSC = 0, GATE_SOURCE_LRC, GATE_SOURCE_PLL128M, GATE_SOURCE_PLL192M, }; enum { GATE_SOURCE_PRE_DIV8 = 0, GATE_SOURCE_PRE_DIV16, GATE_SOURCE_PRE_DIV32, GATE_SOURCE_PRE_DIV64, GATE_SOURCE_PRE_DIV8192, GATE_SOURCE_PRE_DIV16384, GATE_SOURCE_PRE_DIV32768, GATE_SOURCE_PRE_DIV65536, }; //放电时钟源硬件上默认是lsb, 可分频 enum { DISCHARGE_SOURCE_PRE_DIV1 = 0, DISCHARGE_SOURCE_PRE_DIV2, DISCHARGE_SOURCE_PRE_DIV4, DISCHARGE_SOURCE_PRE_DIV8, DISCHARGE_SOURCE_PRE_DIV16, DISCHARGE_SOURCE_PRE_DIV32, DISCHARGE_SOURCE_PRE_DIV64, DISCHARGE_SOURCE_PRE_DIV128, }; //充电时钟源选择, 不可分频 enum { CHARGE_SOURCE_OSC = 0, CHARGE_SOURCE_LRC, CHARGE_SOURCE_PLL128M, CHARGE_SOURCE_PLL192M, }; static const u8 ctmu_ch_table[] = { IO_PORTA_03, IO_PORTA_04, IO_PORTA_05, IO_PORTA_06, IO_PORTA_07, IO_PORTA_08, IO_PORTB_05, IO_PORTB_06, }; static u32 get_ctmu_ch_val(u8 ch_index, u32 *ctm_buf) { if (ch_index >= user_data->num) { return 0; } return ctm_buf[port_index_mapping_talbe[ch_index]]; } static u32 ctm_flt(u8 ch, u32 value) { if (Touchkey_pre_value[ch] == 0) { Touchkey_pre_value[ch] = value; } if (value >= Touchkey_pre_value[ch]) { value = Touchkey_pre_value[ch] + ((value - Touchkey_pre_value[ch]) * 0.5f); } else { value = Touchkey_pre_value[ch] - ((Touchkey_pre_value[ch] - value) * 0.2f); } Touchkey_pre_value[ch] = value; return value; } static void scan_capkey(u8 ch_index, u32 *ctmu_buf) { if (user_data == NULL || user_data->num == 0) { return; } u32 Touchkey_cur_value = get_ctmu_ch_val(ch_index, ctmu_buf); //当前采到的值 /* printf("ch: %d val: %d\n", ch_index, Touchkey_cur_value); */ u32 Touchkey_flt_value = ctm_flt(ch_index, Touchkey_cur_value); //滤波后的值 /* printf("ch: %d val: %d\n", ch_index, Touchkey_flt_value); */ if (Touchkey_flt_value > (Touchkey_normal_value[ch_index] + user_data->port_list[ch_index].press_delta)) { Touchkey_state |= BIT(ch_index); Touchkey_calibrate_cnt[ch_index] = 0; } else { Touchkey_state &= ~BIT(ch_index); Touchkey_calibrate_cnt[ch_index] ++; } //定期更新常态下的基准值 if (Touchkey_calibrate_cnt[ch_index] > CALIBRATE_CYCLE) { Touchkey_normal_value[ch_index] = Touchkey_calibrate_tmp_value[ch_index] / 20; Touchkey_calibrate_tmp_value[ch_index] = 0; Touchkey_calibrate_cnt[ch_index] = 0; } else if (Touchkey_calibrate_cnt[ch_index] >= (CALIBRATE_CYCLE / 2)) { if (Touchkey_calibrate_cnt[ch_index] < ((CALIBRATE_CYCLE / 2) + 20)) { Touchkey_calibrate_tmp_value[ch_index] += Touchkey_flt_value; } } else { Touchkey_calibrate_tmp_value[ch_index] = 0; } /* if (ch_index == 2) { */ /* printf("%d %d %d\n", Touchkey_cur_value, Touchkey_flt_value, Touchkey_normal_value[ch_index]); */ /* } */ } ___interrupt static void ctmu_isr_handle(void) { u32 *rbuf = NULL; if (CTMU_CON0 & BIT(7)) { CTMU_CON0 |= BIT(6); } if (CTMU_CON0 & BIT(9)) { rbuf = (u32 *)(&ctm_buf[0]); } else { rbuf = (u32 *)(&ctm_buf[CTMU_MAX_CH]); } for (u8 i = 0; i < user_data->num; i++) { scan_capkey(i, rbuf); } } static void ctmu_port_init(const struct ctmu_key_port *port_list, u8 port_num) { u8 i, j; for (i = 0; i < port_num; i++) { for (j = 0; j < ARRAY_SIZE(ctmu_ch_table); j++) { if (ctmu_ch_table[j] == port_list[i].port) { CTMU_CON1 |= (BIT(j) << 0); port_index_mapping_talbe[i] = j; log_info("ctmu_ch_table[%d] %x", j, ctmu_ch_table[j]); gpio_set_pull_down(ctmu_ch_table[j], 0); gpio_set_pull_up(ctmu_ch_table[j], 0); gpio_set_die(ctmu_ch_table[j], 0); gpio_set_direction(ctmu_ch_table[j], 1); break; } } if (j > sizeof(ctmu_ch_table)) { log_e("port err!!!"); return; } } } static void ctmu_buf_init(void) { memset((u8 *)&ctm_buf, 0x00, sizeof(ctm_buf)); CTMU_ADR = (u32)&ctm_buf; } static void log_ctmu_info(void) { log_info("CTMU_CON0 = 0x%x", CTMU_CON0); log_info("CTMU_CON1 = 0x%x", CTMU_CON1); } static void touch_ctmu_init(const struct ctmu_key_port *port, u8 num) { log_info("%s", __func__); CTMU_CON0 = 0; CTMU_CON1 = 0; ctmu_port_init(port, num); ctmu_buf_init(); //充电时钟选择 CTMU_CON0 |= (CHARGE_SOURCE_PLL192M << 10); //放电时钟分频选择, 时钟源固定是lsb CTMU_CON0 |= (DISCHARGE_SOURCE_PRE_DIV128 << 12); //要求 > 2uS //闸门时钟选择 CTMU_CON0 |= (GATE_SOURCE_PLL192M << 4); //192 MHz CTMU_CON0 |= (GATE_SOURCE_PRE_DIV65536 << 1); //0.34ms //充电电流 CTMU_CON0 |= 3 << 18; //0 ~ 7 //comparator reference voltage CTMU_CON0 |= 2 << 16; //0 ~ 3 //放电模式(在充电完成后是否放电, 这样会放电更彻底) CTMU_CON0 |= 1 << 23; CTMU_CON0 |= 1 << 6; // Clear Pending } static void touch_ctmu_enable(u8 en) { if (en) { CTMU_CON0 |= BIT(8); //Int Enable CTMU_CON0 |= BIT(0); //Moudle Enable } else { CTMU_CON0 &= ~BIT(8); //Int Disable CTMU_CON0 &= ~BIT(0); //Moudle Disable } } //API: int ctmu_init(void *_data) { if (_data == NULL) { return -1; } user_data = (const struct ctmu_touch_key_platform_data *)_data; if (user_data->num > CTMU_KEY_CH_MAX) { log_error("ctm key num config err!!!"); return -1; } touch_ctmu_init(user_data->port_list, user_data->num); request_irq(IRQ_CTM_IDX, 1, ctmu_isr_handle, 0); for (u8 i = 0; i < user_data->num; i++) { Touchkey_normal_value[i] = 0 - (2 * user_data->port_list[i].press_delta); } touch_ctmu_enable(1); log_ctmu_info(); return 0; } u8 get_ctmu_value(void) { static u8 pre_i = 0; if (Touchkey_state & BIT(pre_i)) { /* printf("i:%d\n", pre_i); */ return pre_i; } for (u8 i = 0; i < user_data->num; i++) { if (Touchkey_state & BIT(i)) { /* printf("i:%d\n", i); */ pre_i = i; return i; } } return 0xff; } #endif /* #if TCFG_CTMU_TOUCH_KEY_ENABLE */