2025-02-18 15:40:42 +08:00

353 lines
10 KiB
C

#include "app_config.h"
#include "asm/ctmu.h"
#include "asm/gpio.h"
#if TCFG_CTMU_TOUCH_KEY_ENABLE
#define TOUCH_CTMU_DEBUG 0
#if TOUCH_CTMU_DEBUG
#define touch_ctmu_debug(fmt, ...) printf("[CTMU] "fmt, ##__VA_ARGS__)
#else
#define touch_ctmu_debug(fmt, ...)
#endif /* #if TOUCH_CTMU_DEBUG */
#define touch_ctmu_err(fmt, ...) printf("[CTMU ERR] "fmt, ##__VA_ARGS__)
#define CTMU_CON0 JL_CTM->CON0
#define CTMU_CON1 JL_CTM->CON1
#define CTMU_ADR JL_CTM->ADR
static u32 ctm_buf[16 * 2] = {0};
static sCTMU_KEY_VAR ctm_key_value;
static sCTMU_KEY_VAR *ctm_key_var;
static u8 port_index_mapping_talbe[CTMU_KEY_CH_MAX] = {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_PLL240M,
GATE_SOURCE_PLL480M,
};
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_PLL240M,
CHARGE_SOURCE_PLL480M,
};
static const u8 ctmu_ch_table[] = {
IO_PORTA_00, IO_PORTA_01, IO_PORTA_02, IO_PORTA_03,
IO_PORTA_04, IO_PORTA_05, IO_PORTA_06, IO_PORTA_07,
IO_PORTA_09, IO_PORTA_10, IO_PORTC_00, IO_PORTC_01,
IO_PORTC_02, IO_PORTC_03, IO_PORTC_04, IO_PORTC_05
};
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 void ctm_irq(u16 ctm_res, u8 ch)
{
u16 temp_u16_0, temp_u16_1;
s16 temp_s16_0, temp_s16_1;
s32 temp_s32_0;
//..............................................................................................
//取计数值/通道判断
//..............................................................................................
if (ctm_key_var->touch_init_cnt[ch]) {
ctm_key_var->touch_init_cnt[ch]--;
ctm_key_var->touch_cnt_buf[ch] = (u32)ctm_res << (ctm_key_var->FLT0CFG + 1);
ctm_key_var->touch_release_buf[ch] = (u32)ctm_res << (ctm_key_var->FLT1CFG0 + 1);
}
//..............................................................................................
//当前计数值去抖动滤波器
//..............................................................................................
temp_u16_0 = ctm_key_var->touch_cnt_buf[ch];
temp_u16_1 = temp_u16_0;
temp_u16_1 -= (temp_u16_1 >> ctm_key_var->FLT0CFG);
temp_u16_1 += ctm_res;
ctm_key_var->touch_cnt_buf[ch] = temp_u16_1;
temp_u16_0 += temp_u16_1;
temp_u16_0 >>= (ctm_key_var->FLT0CFG + 1);
//..............................................................................................
//各通道按键释放计数值滤波器
//..............................................................................................
temp_s32_0 = ctm_key_var->touch_release_buf[ch];
temp_u16_1 = temp_s32_0 >> ctm_key_var->FLT1CFG0; //获得滤波器之后的按键释放值
temp_s16_0 = temp_u16_0 - temp_u16_1; //获得和本次检测值的差值,按下按键为负值,释放按键为正值
temp_s16_1 = temp_s16_0;
// if(ch == 1)
// {
// printf("ch%d: %d %d", (short)ch, temp_u16_0, temp_s16_1);
// }
if (ctm_key_var->touch_key_state & BIT(ch)) { //如果本通道按键目前是处于释放状态
if (temp_s16_1 <= 0) { //当前计数值小于低通值,放大后参与运算
if (temp_s16_1 > -(ctm_key_var->FLT1CFG2 >> 3)) {
temp_s16_1 = temp_s16_1 * 8; //temp_s16_1 <<= 3; //放大后参与运算
} else {
temp_s16_1 = -(ctm_key_var->FLT1CFG2); //饱和,防止某些较大的正偏差导致错判
}
} else if (temp_s16_1 <= ctm_key_var->FLT1CFG1) { //当前计数值小于低通值不多,正常参与运算
} else { //当前计数值小于低通值很多,缩小后参与运算
temp_s16_1 = temp_s16_1 / 8; //temp_s16_1 >>= 3;(有符号数右移自动扩展符号位???)
}
} else { //如果本通道按键目前是处于按下状态, 缓慢降低释放计数值
if (temp_s16_1 >= ctm_key_var->RELEASECFG1) {
temp_s16_1 >>= 3; //缩小后参与运算
} else {
temp_s16_1 = 0;
}
}
temp_s32_0 += (s32)temp_s16_1;
ctm_key_var->touch_release_buf[ch] = temp_s32_0;
//..............................................................................................
//按键按下与释放检测
//..............................................................................................
if (temp_s16_0 >= ctm_key_var->PRESSCFG) { //按键按下
ctm_key_var->touch_key_state &= ~BIT(ch);
} else if (temp_s16_0 >= ctm_key_var->RELEASECFG0) { //按键释放
ctm_key_var->touch_key_state |= BIT(ch);
}
}
static void scan_capkey(u8 ch_index, u32 *ctmu_buf)
{
u16 Touchkey_value_delta;
if (user_data == NULL || user_data->num == 0) {
return;
}
Touchkey_value_delta = get_ctmu_ch_val(ch_index, ctmu_buf); ///获取计数值;
/* g_printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = %d",Touchkey_value_delta); */
/*调用滤波算法*/
ctm_irq(Touchkey_value_delta, ch_index);
}
___interrupt
static void ctmu_isr_handle(void)
{
u32 *rbuf = NULL;
u8 i, j;
if (CTMU_CON0 & BIT(7)) {
CTMU_CON0 |= BIT(6);
}
if (CTMU_CON0 & BIT(9)) {
rbuf = (u32 *)(&ctm_buf[0]);
} else {
rbuf = (u32 *)(&ctm_buf[16]);
}
for (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;
}
}
if (j > sizeof(ctmu_ch_table)) {
touch_ctmu_err("port err!!!");
return;
}
gpio_set_pull_down(port_list[i].port, 0);
gpio_set_pull_up(port_list[i].port, 0);
gpio_set_die(port_list[i].port, 0);
gpio_set_direction(port_list[i].port, 1);
}
}
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)
{
touch_ctmu_debug("CTMU_CON0 = 0x%x", CTMU_CON0);
touch_ctmu_debug("CTMU_CON1 = 0x%x", CTMU_CON1);
}
static void touch_ctmu_init(const struct ctmu_key_port *port, u8 num)
{
touch_ctmu_debug("%s", __func__);
CTMU_CON0 = 0;
CTMU_CON1 = 0;
//充电时钟选择
CTMU_CON0 |= (CHARGE_SOURCE_PLL480M << 10);
//放电时钟分频选择, 时钟源固定是lsb
CTMU_CON0 |= (DISCHARGE_SOURCE_PRE_DIV128 << 12); //要求 > 2uS
//闸门时钟选择
CTMU_CON0 |= (GATE_SOURCE_OSC << 4); //24 MHz
CTMU_CON0 |= (GATE_SOURCE_PRE_DIV32768 << 1); //1.36ms
//充电电流
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
ctmu_port_init(port, num);
ctmu_buf_init();
log_ctmu_info();
}
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;
memset((u8 *)&ctm_key_value, 0x0, sizeof(sCTMU_KEY_VAR));
/*触摸按键参数配置*/
ctm_key_value.FLT0CFG = 2;
ctm_key_value.FLT1CFG0 = 2;
ctm_key_value.FLT1CFG1 = 80;
ctm_key_value.FLT1CFG2 = 10 * 128; //128 = 2^7
///调节灵敏度的主要参数
ctm_key_value.PRESSCFG = user_data->press_cfg;;
ctm_key_value.RELEASECFG0 = user_data->release_cfg0;
ctm_key_value.RELEASECFG1 = user_data->release_cfg1;
memset((u8 *) & (ctm_key_value.touch_init_cnt[0]), 0x10, CTMU_KEY_CH_MAX);
ctm_key_value.touch_key_state = 0xffff; //<按键默认释放
ctm_key_var = &ctm_key_value;
if (user_data->num > CTMU_KEY_CH_MAX) {
touch_ctmu_err("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);
touch_ctmu_enable(1);
return 0;
}
u8 get_ctmu_value(void)
{
u8 key = 0xFF;
u8 i;
for (i = 0; i < user_data->num; i++) {
if (!(ctm_key_value.touch_key_state & (u16)(BIT(i)))) {
break;
}
}
key = (i < user_data->num) ? i : 0xFF;
return key;
}
#endif /* #if TCFG_CTMU_TOUCH_KEY_ENABLE */