#include "Touch_Lib.h"

/* 静态函数声明 */
static void gpio_set_mode(u8 b_PortPin, u8 mode);
static void TKL_AddDataToBuf(u16 dat);
static void TKL_AcquisitionAll(void);
static void TKL_ProcessAll(void);
static void TKL_WaterProof(void);
static void TKL_ECS(void);
static void TKL_Key_SetFindIcomState(u8 chn);
static void TKL_Key_SetCalibrationState(u8 chn);

void  _nop_  (void);
u16 xdata SendTouch_Number[25]={0};
u16 Touch_Number_sum=0;


/* iic*/
#define IIC_SCL_HIGH P1_2=1  //SCL(P12) 
#define IIC_SCL_LOW  P1_2=0
#define IIC_SDA_HIGH P1_3=1  //SDA(P13)
#define IIC_SDA_LOW  P1_3=0
#define IIC_SDA_READ P1_DR&0x08
#define DATA_LENGTH  54
#define IIC_SLAVE_ADDR 0xA0   //从机地址


/* 宏定义 */
#define TKL_CHN_NUM_MAX                         (25)

// Acquisition values limits
#define SCKEY_MIN_ACQUISITION                   (200)       /**< Single channel key minimum acquisition value */
#define SCKEY_MAX_ACQUISITION                   (16000)      /**< Single channel key maximum acquisition value */


// Integrators
#define DETECTION_INTEGRATOR_DEFAULT            (3)         /**< Detection Integrator = Debounce Filter (value from 0 to 255) */
#define END_DETECTION_INTEGRATOR_DEFAULT        (3)         /**< End detection Integrator = Debounce Filter (from 0 to 255) */
#define RECALIBRATION_INTEGRATOR_DEFAULT        (10)        /**< Calibration integrator (value from 1 to 255) */
#define ECS_INTEGRATOR_DEFAULT                  (4)         /**< ECS integrator (value from 1 to 255) */

// IIR Filter
#define ECS_TIME_STEP_DEFAULT                   (200)       /**< Sampling frequency, multiple of 1ms */
#define ECS_TEMPO_DEFAULT                       (2000)      /**< Delay after detection, multiple of 1ms */
#define ECS_IIR_KFAST_DEFAULT                   (128)       /**< K factor for fast filtering */
#define ECS_IIR_KSLOW_DEFAULT                   (5)         /**< K factor for slow filtering */

// 校准时采样数据量
#define SCKEY_CALIBRATION_COUNT_DEFAULT         (8)

// 防水阈值
#define PERCENT_WPF_TH_IN                       (42)        // 256 * 15% = 38
#define PERCENT_WPF_TH_OUT                      (20)        // 256 * 8%
#define DEBOUNCE_WPF_CNT                        (3)


#define CSD_SHADE_SCAN_IMS                      (255)
#define CSD_WIN_CONF                            (0x3FUL)
#define CSD_G_FREQ_3M                           (2-1)
#define CSD_TARGET_PERCENT                      (30)        // 理论上不应该大于 50%，还应该预留干扰的裕量

#define SAMPLING_NB_LOOPS                       (8)         // 滤波窗口大小

/* 引脚宏定义 */
#define GPIO_MODE_WPF                           0           // 防水模式
#define GPIO_MODE_SW                            1           // 电容检测模式
#define GPIO_MODE_SO                            2           // 模拟高阻

/* 按键状态定义 */
enum
{
    KEY_STA_FIND_ICOM,
    KEY_STA_PRE_CALIBRATION,
    KEY_STA_CALIBRATION,
    KEY_STA_IDLE,
    KEY_STA_PRE_DETECTED,
    KEY_STA_DETECTED,
    KEY_STA_POST_DETECTED,
    KEY_STA_ERROR,
    KEY_STA_DISABLED,
};


/* 按键通道数据结构定义 */
typedef struct
{
    // 设置信息
    u8              ChnId                   :3; // 通道号
    u8              KeyId                   :5; // 通道对应的按键。（0~24）表示（TK1~25）
    u8              IntegratorCounter       :4; // 状态需要保持的时间，0~7，0和1的效果一样，都是一个有效数据间隔。不同的状态需要保持相应的时间后才能切换到下一个状态
    u8              PreDetectCounter        :4; // 处于 PreDetected 状态计数器。如果超过设定计数还没成功进入 Detected 状态，则进入 Idle 状态
	
    // 检测参数
    s16             PreDetectThreshold;         // 进入检测状态的阈值
    s16             DetectThreshold;            // 进入检测状态的阈值
    s16             PreEndDetectThreshold;      // 退出检测状态的阈值
    s16             EndDetectThreshold;         // 退出检测状态的阈值
    s16             RecalibrationThreshold;     // 重新校准的阈值
    u16             BaseLine;                   // 无水状态下的校准基线

    // 防水检测参数
    s16             WPDetectThreshold;          // 进入检测状态的阈值
    s16             WPPreDetectThreshold;       // 进入检测状态的阈值
    s16             WPEndDetectThreshold;       // 退出检测状态的阈值
    s16             WPPreEndDetectThreshold;    // 退出检测状态的阈值
    u16             WaterThreshold;             // 有水判断阈值
    u16             WPPercentRef;               // 防水判断的百分比参考值

    // 运行信息
    u16             Counter;                    // 检测状态超时计数器/校准采样数据计数器
    u8              KeyState;                   // 按键当前状态
    u16             LastMeas;                   // 最后一次的有效测量数据
    u32             Reference;                  // 当前参考值
    u8              ECSRefRest;                 // ECS 计算时需要用到的余数
    u32             MeasSum;                    // 测量值之和
    u16             DetectMaxRef;               // Detected 状态下最大的基线值
    u8              Water                   :1; // 当前是否有水的标志位
    u8              WPUsed                  :1; // 记录通道是否已经做过防水处理了，处理了的通道不再处理

    // 数据
    u8              Idac;                       // 通道 Idac 设置
    u8              Icom;                       // 扫描时的 Icom 值
    u8              Icomfirst;                 // 扫描时的 Icom 值	
    u8              Icomlast;                  // 扫描时的 Icom 值	
		u8              Icommid;                   // 扫描时的 Icom 值	
    u16             DatIcom;                    // 找到 Icom 时的测量值
    u16             DatBuf[SAMPLING_NB_LOOPS];  // 采样数据缓存
}TKL_Chn_t;



/* 数据结构定义 */
typedef struct
{
    // 标志
    u8              DatFull     : 1;            // 数据缓存满标志，一旦满了，后续一直置位，用于滤波参考
    u8              DatLoopEnd  : 1;            // 一轮数据读取完成的标志。最后一个通道的最后一个频率扫描完成后置位，数据处理后清零
//		u8							Group				: 1;

    // 设定值
    u8              ChnNum      : 5;            // 通道数(1~25)
    TKL_Chn_t*      Chns;                       // 通道相关信息。需要由用户传入数组指针
    u16             DTO;                        // 按下超时时间，以中断时间为单位。

    // 当前值
    u8              ChnIndex    : 5;            // 当前扫描的通道序号。0~(ChnNum-1)表示当前通道；
                                                // 等于 ChnNum 表示当前正在给 Cmod 充电
                                                // 大于 ChnNum 表示当前空闲
    u8              DatIndex                    // 当前缓存的写入位置，位数与 buffer 长度有关，写到最后一个再 +1 会自动溢出归零
#if SAMPLING_NB_LOOPS == 4
                                : 2;
#elif SAMPLING_NB_LOOPS == 8
                                : 3;
#else
    #error "Error SAMPLING_NB_LOOPS. (4 or 8 or 16)"
#endif

    u8              PreDetCnt;                  // 全局 PreDetected 状态倒计时，
                                                // 每次有通道进入此状态就会重置倒计时，只有该计时为 0 完成，通道才能进入 Detected 状态，
                                                // 保证同时触摸或有水的通道可以同时进入 Detected 状态，方便做防水处理

    // 计算结果
    s16             Delta;                      // 通道测量值与参考值的差
    u32             KeyValue;                   // 按位表示通道是否有触摸。置 1 的位表示对应的通道有触摸
    
    // 系统相关
    u16             Tick;                       // 系统时间戳
    u16             EcsStepTs;                  // 环境值管理最后一次调整时间戳
} TKL_Info_t;
/* 变量定义 */
TKL_Info_t TKLInfo = {0};


typedef struct
{
    u8  ChnId;
    u8  KeyId;
    u8  Idac;
    u16 Baseline;
    u16 Touchline;
    u16 Waterline;
}TKLChnCfg;



//======================================================================================     
void SDA_OUT(void)  //P13
{
	P1_DM2   &= 0xf7;	
	P1_DM1   &= 0xf7;	
	P1_DM0   |= 0x08;
}

void SDA_IN(void)  //P13
{
	P1_DM2   |= 0x08;
	P1_DM1   &= 0xf7;	
	P1_DM0   &= 0xf7;
}

//====================================================================================== 
void Delay_Us()
{
	_nop_();
	_nop_();
}

void Delay_Us1(u8 us)
{
	u8 a;
	for (a=0; a<us; a++)
	{
		_nop_();
		_nop_();
		_nop_();
		
	}	
}

//====================================================================================== 
void I2C_Init(void)
{
	P1_DM2       = 0x00;  //P12 P13输出
	P1_DM1       = 0x00;
	P1_DM0       = 0x0c;	
	IIC_SCL_HIGH;
	IIC_SDA_HIGH;
}

//====================================================================================== 
void iic_start(void)
{
	SDA_OUT();
	IIC_SDA_HIGH;
	IIC_SCL_HIGH;
	Delay_Us1(1);
	IIC_SDA_LOW;
	Delay_Us1(1);
	IIC_SCL_LOW;
}

//====================================================================================== 
void iic_stop(void)
{
	SDA_OUT();
	IIC_SCL_LOW;
	IIC_SDA_LOW;
	Delay_Us1(1);
	IIC_SCL_HIGH;
	Delay_Us1(1);
	IIC_SDA_HIGH;
	Delay_Us();
}
//====================================================================================== 
//void iic_ack(void)
//{
//	IIC_SCL_LOW;
//	SDA_OUT();
//	IIC_SDA_LOW;
//	Delay_Us();
//	IIC_SCL_HIGH;
//	Delay_Us();
//	IIC_SCL_LOW;
//}

//====================================================================================== 
//void iic_nack(void)
//{
//	IIC_SCL_LOW;
//	SDA_OUT();
//	IIC_SDA_HIGH;
//	Delay_Us();
//	IIC_SCL_HIGH;
//	Delay_Us();
//	IIC_SCL_LOW;
//}
//====================================================================================== 
u8 iic_wait_ack(void)
{
	u16 time=0;
	SDA_IN();
	IIC_SDA_HIGH;
	Delay_Us();
	IIC_SCL_HIGH;
	Delay_Us1(1);
	while(IIC_SDA_READ)
	{
		time++;
		if(time>100)
		{
			iic_stop();
			return 1;
		}
	}
//	Delay_Us(4);
	SDA_OUT();
	IIC_SDA_LOW;		
	IIC_SCL_LOW;
	return 0;
}
//====================================================================================== 
void iic_send_byte(u8 dat)
{
	u8 i;
	SDA_OUT();
	IIC_SCL_LOW;
	for(i=0; i<8; i++)
	{
		if((dat&0x80)>>7) IIC_SDA_HIGH;
		else IIC_SDA_LOW;
		dat<<=1;
		Delay_Us();
		IIC_SCL_HIGH;
		Delay_Us1(2);
		IIC_SCL_LOW;
//		Delay_Us();
	}
	iic_wait_ack();
}

//====================================================================================== 
//u8 iic_read_byte(u8 i)
//{
//	u8 j;
//	u8 temp;
//	SDA_IN();
//	for(j=0; j<8; j++)
//	{
//		IIC_SCL_LOW;
//		Delay_Us();
//		IIC_SCL_HIGH;
//		temp<<=1;
//		if(IIC_SDA_READ)
//		temp++;
//		Delay_Us();
//	}
//	if(i)
//		iic_ack();
//	else
//		iic_nack();
//	return temp;
//}

/*======================================================================================      
函数名：I2C_Read_Buffer
功  能：函数说明：IIC主机读操作(跟24C02通信)
输  入：寄存器地址,长度,数据
输  出：无
======================================================================================*/
//void I2C_Read_Buffer(u8 addr, u8 length, u8 *pbuf)
//{
//	u8 i;
//	iic_start();
//	iic_send_byte(IIC_SLAVE_ADDR); //发送从机地址
//	iic_wait_ack();
//	iic_send_byte(addr);           //发送存储器地址
//	iic_wait_ack();

//	iic_start();
//	iic_send_byte(IIC_SLAVE_ADDR | 1);   //发送从机地址+读命令
//	iic_wait_ack();
//	for(i = 0; i < length - 1; i++)
//	{
//		pbuf[i] = iic_read_byte(1);        //需要回ACK
//	}
//	pbuf[length - 1] = iic_read_byte(0); //最后1 Byte不需要回ACK
//	iic_stop();
//}

/*======================================================================================      
函数名：I2C_Write_Buffer
功  能：函数说明：IIC主机写操作(跟24C02通信)
输  入：寄存器地址,长度,数据
输  出：无
======================================================================================*/
void I2C_Write_Buffer()
{
	u8 Count;
	iic_start();
	iic_send_byte(IIC_SLAVE_ADDR); //发送从机地址
	 
	iic_send_byte(0x00);
	iic_send_byte(0x55);
	iic_send_byte(0xAA);
	iic_send_byte(0x46);
	iic_send_byte(0x43);	
  iic_send_byte(DATA_LENGTH);		

  for(Count = 0;Count<=25;Count++)
  {
    iic_send_byte(SendTouch_Number[Count]>>8);   
	  iic_send_byte(SendTouch_Number[Count]);
  }
	
 	iic_send_byte(Touch_Number_sum>>8);   
  iic_send_byte(Touch_Number_sum); 

	iic_stop();
}






/**
 * @brief 初始化 CSD 外设
 * 
 */
void TKL_CSDInit(void)
{
    // set CMOD pin
    P1_DM0 |= 0x01;
    P1_DM1 |= 0x01;
    P1_DM2 |= 0x01;			
	
    // 开启 CSD 模块，开启比较器，Idac 使能，Icom 使能
    CSD_RX0_CR = 0x0F;
    CSD_SIN_CKSEL = 0x07;   // sync clk = 24M，计数时钟频率
    CSD_CKSEL = CSD_G_FREQ_3M;
    CSD_VREF_CR = 0x01;     // 0.25uA, 1.504v
                            // bit0 CSD_VREF_EN CSD参考电压buffer使能控制位
                            // bit1 IUnit. 0: 0.5uA; 1: 0.25uA
                            // bit2~3 CSD_VREF_SEL 比较器参考电压选择  00:1.2v 01:1.504v 10:1.719v 11:2.062v
                            // bit4 CSD_IDFT 0:正常工作模式（默认）1：常开IDAC
}


void ThresholdInit(u8 chn, TKLChnCfg* cfg)
{
    TKLInfo.Chns[chn].ChnId = cfg->ChnId - 1;
    TKLInfo.Chns[chn].KeyId = cfg->KeyId;
    TKLInfo.Chns[chn].Idac = cfg->Idac;

    TKLInfo.Chns[chn].BaseLine = cfg->Baseline;
    TKLInfo.Chns[chn].DetectThreshold = cfg->Waterline + ((cfg->Touchline-cfg->Waterline)/2)- cfg->Baseline;
    TKLInfo.Chns[chn].PreDetectThreshold = TKLInfo.Chns[chn].DetectThreshold / 2;

    TKLInfo.Chns[chn].WPDetectThreshold = (cfg->Touchline + ((cfg->Touchline-cfg->Waterline)/2) - cfg->Waterline)/2;
    TKLInfo.Chns[chn].WPPreDetectThreshold = TKLInfo.Chns[chn].WPDetectThreshold/2;
    TKLInfo.Chns[chn].WPPercentRef = cfg->Touchline + ((cfg->Touchline-cfg->Waterline)/2) - cfg->Baseline;

    TKLInfo.Chns[chn].WPPreEndDetectThreshold = -(TKLInfo.Chns[chn].WPPreDetectThreshold);
    TKLInfo.Chns[chn].WPEndDetectThreshold = -(TKLInfo.Chns[chn].WPDetectThreshold);

    TKLInfo.Chns[chn].PreEndDetectThreshold = TKLInfo.Chns[chn].WPPreEndDetectThreshold;
    TKLInfo.Chns[chn].EndDetectThreshold = TKLInfo.Chns[chn].WPEndDetectThreshold;

    TKLInfo.Chns[chn].WaterThreshold = cfg->Waterline/2;
    
    TKLInfo.Chns[chn].RecalibrationThreshold = -20000;
}


/**
 * @brief 往数据缓存区写入数据
 * 
 * @param dat 要写入的数据
 * @return 返回 0 表示写入成功；其他表示写入失败，数据被丢弃
 */
static void TKL_AddDataToBuf(u16 dat)
{
    // 如果缓存区满，则和值要先减去即将被覆盖的值
    if(TKLInfo.DatFull)
    {
        TKLInfo.Chns[TKLInfo.ChnIndex].MeasSum -= TKLInfo.Chns[TKLInfo.ChnIndex].DatBuf[TKLInfo.DatIndex];
    }
    TKLInfo.Chns[TKLInfo.ChnIndex].DatBuf[TKLInfo.DatIndex] = dat;
    TKLInfo.Chns[TKLInfo.ChnIndex].MeasSum += dat;

    // 更新各个索引和状态标志
    if(TKLInfo.ChnIndex == (TKLInfo.ChnNum-1))
    {
        TKLInfo.DatIndex++;

        if(TKLInfo.DatFull)
        {
            // 如果缓存区已经填满，则每次将所有通道采样一遍都处理一次
            TKLInfo.DatLoopEnd = 1;
        }
        else if(!TKLInfo.DatIndex)
        {
            // 如果缓存区未满，则判断 DatIndex 是否回到了 0，是则表示缓存区满了，置标志且需要开始处理数据
            TKLInfo.DatLoopEnd = 1;
            TKLInfo.DatFull = 1;
        }
    }
}


/**
 * @brief 开始一轮扫描
 * 
 */
void TKL_ScanStartLoop(void)
{
    TKLInfo.Tick++;

    // 如果 TKIndex 小于等于通道数量，表示当前正在进行一轮扫描，此时不可开始新的扫描
    // 一轮数据未处理时，不开始新扫描
    if ((TKLInfo.ChnIndex <= TKLInfo.ChnNum) || TKLInfo.DatLoopEnd)
    {
        return;
    }

    // 开始新一轮扫描，首先设置为 Cmod 充电
    TKLInfo.ChnIndex = TKLInfo.ChnNum;

    // 配置 CSD
    CSD_RX0_CR = 0x0F;                  // bit0 CSD_EN(CSD电路通道使能)
                                        // bit1 CSD_CMP_EN(CMP控制比较器开关
                                        // 0  不使能，比较器关，若此时IDAC使能也为0，ICOM，IDAC无电流，默认值
                                        // 1  使能，比较器开)
                                        // bit2 CSD_IDAC_EN IDAC使能
                                        // bit3 CSD_ICOM_EN  ICOM使能
    CSD_RX0_IMS = CSD_SHADE_SCAN_IMS;   // 设置IMS(IDAC)为最大，快速充满CMOD
    CSD_RX0_ICOM = 0;
    CSD_WIN = 0x0;                      // 设置计数窗口

    CSD_STAT = 0x91;                    // bit4 STAT_DONE(单次扫描窗口WIN time out（完成/超时）中断状态，写1清零  1已完成/超时)
                                        // bit0 STAT_GUARD(扫描结果计数值CSD_RX0_DR超出阈值CSD_RX0_TH中断状态，写1清零
                                        // 0  CSD_RX0_DR小于CSD_RX0_TH阈值
                                        // 1  CSD_RX0_DR大于等于CSD_RX0_TH阈值)
                                        // bit7 IE_DONE (单次扫描窗口WIN time out（完成/超时）中断使能)
    CSD_CR = 0x29;                      // bit0 CSD_TRIG CSD扫描开始控制信号，与CSD_EN电路通道使能配合使用
                                        // CSD_EN=0，或在CSD_EN=1时写0：停止扫描（默认值）
                                        // 在CSD_EN=1时写1：启动扫描
                                        // bit3 CSD_QUIET touch模拟电路充放电开关PHICLK控制
                                        // 0  不限制PHICLK，默认值  1  在未启动CSD扫描时停用PHICLK
}


/**
 * @brief 触摸中断回调函数
 * 
 */
void TKL_IRQCallback(void)
{
    // 清中断
    CSD_STAT = 0x91;

    if (TKLInfo.ChnIndex == TKLInfo.ChnNum)
    {
        // 如果是给 Cmod 充电，则需要判断是否充完，没充完继续充
        if (CSD_RX0_CR & (1<<7))
        {
            CSD_CR = 0x29;
        }
        else
        {
            // Cmod 充满电，从通道 0 开始扫描
//            TKLInfo.ChnIndex = TKLInfo.Group<<2;
					  TKLInfo.ChnIndex = 0;

            // 设置参数并开始扫描
            CSD_RX0_IMS = TKLInfo.Chns[TKLInfo.ChnIndex].Idac;
            // CSD_RX0_IMS = CSD_SCAN_IMS;
            CSD_RX0_ICOM = TKLInfo.Chns[TKLInfo.ChnIndex].Icom;
            CSD_WIN = CSD_WIN_CONF;
            gpio_set_mode(TKLInfo.Chns[TKLInfo.ChnIndex].KeyId, GPIO_MODE_SW);
            CSD_CR = 0x29;
        }
    }
    else
    {
        // 保存测量值到通道缓存中
        TKL_AddDataToBuf(CSD_RX0_DR);
        
        // 设置通道引脚为普通模式
        gpio_set_mode(TKLInfo.Chns[TKLInfo.ChnIndex].KeyId, GPIO_MODE_SO);

        // 增加通道计数
        TKLInfo.ChnIndex++;
//				if (!(TKLInfo.ChnIndex & 0x03))
				if (TKLInfo.ChnIndex >= TKLInfo.ChnNum)	
        {
            // 扫描完所有通道，关闭触摸，等待开启下次扫描循环
					  TKLInfo.ChnIndex++;
//					  TKLInfo.Group ^= 1;
//						TKLInfo.ChnIndex = 0x1F;
        }
        else
        {
            // 设置参数并开始下一个通道扫描
            CSD_RX0_IMS = TKLInfo.Chns[TKLInfo.ChnIndex].Idac;
            CSD_RX0_ICOM = TKLInfo.Chns[TKLInfo.ChnIndex].Icom;
            CSD_WIN = CSD_WIN_CONF;
            gpio_set_mode(TKLInfo.Chns[TKLInfo.ChnIndex].KeyId, GPIO_MODE_SW);
            CSD_CR = 0x29;
        }
    }
}



/**
 * @brief 处理数据，获取单次有效按键读数
 * 
 * @param Chn 通道号（bit4:0）
 * @param pMeasurement 返回的测量值
 * @return 读数有效返回 0，无效返回 1
 */
static u8 TKL_Acquisition(u8 Chn, u16* pMeasurement)
{
#if SAMPLING_NB_LOOPS == 4
    *pMeasurement = (TKLInfo.Chns[Chn].MeasSum+3) >> 2;
#elif SAMPLING_NB_LOOPS == 8
    u16 dat, Max1=0, Max2=0, Min1=0xFFFF, Min2=0xFFFF;
    u8  Sampling;

    for (Sampling = 0; Sampling < SAMPLING_NB_LOOPS; Sampling++)
    {
        dat = TKLInfo.Chns[Chn].DatBuf[Sampling];
        if(dat > Max1)
        {
            Max2 = Max1;
            Max1 = dat;
        }
        else if(dat > Max2)
        {
            Max2 = dat;
        }
        if(dat < Min1)
        {
            Min2 = Min1;
            Min1 = dat;
        }
        else if(dat < Min2)
        {
            Min2 = dat;
        }
    } /* for Sampling... */
    dat = TKLInfo.Chns[Chn].MeasSum - Max1 - Max2 - Min1 - Min2;
    *pMeasurement = (dat+3) >> 2;
#endif
    return 0;
}

/**
 * @brief 获取所有通道的单次有效按键读数，并更新按键状态
 * 
 */
static void TKL_AcquisitionAll(void)
{
    u8 chn;
    
    // 处理各通道数据
    for(chn = 0; chn < TKLInfo.ChnNum; chn++)
    {
        TKL_Acquisition(chn, &TKLInfo.Chns[chn].LastMeas);
    }
}


/**
 * @brief 设置按键为 找 Icom 状态
 * 
 * @param chn 通道号
 */
static void TKL_Key_SetFindIcomState(u8 chn)
{
    TKLInfo.Chns[chn].KeyState = KEY_STA_FIND_ICOM;
    TKLInfo.Chns[chn].IntegratorCounter = SAMPLING_NB_LOOPS;        // 找 Icom 时，修改 Icom 值，等刷满缓存区后计算平均值，所以次数跟缓存区大小相同
    TKLInfo.Chns[chn].Icom = 63;
	  TKLInfo.Chns[chn].Icomfirst = 0;
	  TKLInfo.Chns[chn].Icomlast = 127;
    TKLInfo.KeyValue &= ~(1<<TKLInfo.Chns[chn].ChnId);
}

/**
 * @brief 设置按键为 ERROR 状态
 * 
 * @param chn 通道号
 */
static void TKL_Key_SetErrorState(u8 chn)
{
    TKLInfo.Chns[chn].KeyState = KEY_STA_ERROR;
    TKLInfo.KeyValue &= ~(1<<TKLInfo.Chns[chn].ChnId);
}


/**
 * @brief 设置按键为 CALIBRATION 状态
 * 
 * @param chn 通道号
 */
static void TKL_Key_SetCalibrationState(u8 chn)
{
    TKLInfo.Chns[chn].KeyState = KEY_STA_CALIBRATION;
    TKLInfo.Chns[chn].Counter = SCKEY_CALIBRATION_COUNT_DEFAULT;
    TKLInfo.Chns[chn].Reference = 0;
    TKLInfo.KeyValue &= ~(1<<TKLInfo.Chns[chn].ChnId);
}


/**
 * @brief 找 Icom 状态处理
 * 
 * @param chn 通道号
 */
static void TKL_Key_FindIcomTreatment(u8 chn)
{
    if (TKLInfo.Chns[chn].IntegratorCounter)
    {
        TKLInfo.Chns[chn].IntegratorCounter--;
    }
    if (!TKLInfo.Chns[chn].IntegratorCounter)
    {
        u16 target = ((CSD_WIN_CONF << 8) | 0xFF) * CSD_TARGET_PERCENT / 100;
        u16 dat = TKLInfo.Chns[chn].MeasSum / SAMPLING_NB_LOOPS;
			
//				if(dat <= target)
//				{
//						TKLInfo.Chns[chn].DatIcom = dat;
//						TKL_Key_SetCalibrationState(chn);
//				}
//				else
//				{						
//            TKLInfo.Chns[chn].Icom++;
//						if(TKLInfo.Chns[chn].Icom > 127)
//						{
//								TKL_Key_SetErrorState(chn);
//						}
//						
//						// 重新设置计数，以新的 Icom 查找基准值
//						TKLInfo.Chns[chn].IntegratorCounter = SAMPLING_NB_LOOPS;
//				}			
					  
			  if(TKLInfo.Chns[chn].Icomfirst <= TKLInfo.Chns[chn].Icomlast)
				{
					if(dat > target)
					{
						  if(TKLInfo.Chns[chn].Icomlast - TKLInfo.Chns[chn].Icomfirst <= 1)
							{
                TKLInfo.Chns[chn].Icom += 1;								
							}
							else
							{
								TKLInfo.Chns[chn].Icomfirst = TKLInfo.Chns[chn].Icommid + 1;
								TKLInfo.Chns[chn].Icommid = (TKLInfo.Chns[chn].Icomfirst + TKLInfo.Chns[chn].Icomlast)>>1;
								TKLInfo.Chns[chn].Icom = TKLInfo.Chns[chn].Icommid;									
							}
						
							if(TKLInfo.Chns[chn].Icom > 127)
							{
									TKL_Key_SetErrorState(chn);
							}
							// 重新设置计数，以新的 Icom 查找基准值
							TKLInfo.Chns[chn].IntegratorCounter = SAMPLING_NB_LOOPS;
					}
					else
					{
						  if(TKLInfo.Chns[chn].Icomlast - TKLInfo.Chns[chn].Icomfirst <= 1)
							{
								TKLInfo.Chns[chn].DatIcom = dat;
								TKL_Key_SetCalibrationState(chn);										
							}
							else
							{
								TKLInfo.Chns[chn].Icomlast = TKLInfo.Chns[chn].Icommid - 1;
								TKLInfo.Chns[chn].Icommid = (TKLInfo.Chns[chn].Icomfirst + TKLInfo.Chns[chn].Icomlast)>>1;
								TKLInfo.Chns[chn].Icom = TKLInfo.Chns[chn].Icommid;									
							}		
							if(TKLInfo.Chns[chn].Icom <= 0)
							{
									TKL_Key_SetErrorState(chn);
							}
							// 重新设置计数，以新的 Icom 查找基准值
							TKLInfo.Chns[chn].IntegratorCounter = SAMPLING_NB_LOOPS;
					}						
				}	
    }
}




/**
 * @brief 处理所有通道的触摸数据
 * 
 */
static void TKL_ProcessAll(void)
{
    u8 chn;

    if(TKLInfo.PreDetCnt)
    {
        TKLInfo.PreDetCnt--;
    }
		
    for(chn = 0; chn < 25; chn++)
    {
      SendTouch_Number[chn] = 0;
    }     
		
    for(chn = 0; chn < TKLInfo.ChnNum; chn++)
    {
        // 如果是找 Icom 状态，则先处理
        if(TKLInfo.Chns[chn].KeyState == KEY_STA_FIND_ICOM)
        {
            TKL_Key_FindIcomTreatment(chn);
            continue;
        }
        SendTouch_Number[TKLInfo.Chns[chn].KeyId] = TKLInfo.Chns[chn].LastMeas;
    }
		Touch_Number_sum=0;
		for(chn=0;chn<24;chn++)
		{
		 Touch_Number_sum += SendTouch_Number[chn];
		}
		Touch_Number_sum=Touch_Number_sum+1;	

		I2C_Write_Buffer();						
}




static void gpio_set_mode(u8 b_PortPin, u8 mode)
{
    if (mode == GPIO_MODE_SW) // 110 --电容检测
    {
        if (b_PortPin < 8)
        {
            P0_DM1 |= 1 << b_PortPin;
            P0_DM2 |= 1 << b_PortPin;
            P0_DM0 &= ~(1 << b_PortPin);
        }
        else if ((8 <= b_PortPin) && (b_PortPin < 16))
        {
            P2_DM1 |= 1 << (b_PortPin - 8);
            P2_DM2 |= 1 << (b_PortPin - 8);
            P2_DM0 &= ~(1 << (b_PortPin - 8));
        }
        else if ((16 <= b_PortPin) && (b_PortPin < 23))
        {
            P1_DM1 |= 1 << (b_PortPin - ((b_PortPin - 16) * 2 + 9));
            P1_DM2 |= 1 << (b_PortPin - ((b_PortPin - 16) * 2 + 9));
            P1_DM0 &= ~(1 << (b_PortPin - ((b_PortPin - 16) * 2 + 9)));
        }
        else if ((23 <= b_PortPin) && (b_PortPin < 25))
        {
            P3_DM1 |= 1 << (b_PortPin - 23);
            P3_DM2 |= 1 << (b_PortPin - 23);
            P3_DM0 &= ~(1 << (b_PortPin - 23));
        }
    }
    else if (mode == GPIO_MODE_SO) // 000
    {
        if (b_PortPin < 8)
        {
            P0_DM0 &= ~(1 << b_PortPin);
            P0_DM1 &= ~(1 << b_PortPin);
            P0_DM2 &= ~(1 << b_PortPin);
        }
        else if ((8 <= b_PortPin) && (b_PortPin < 16))
        {
            P2_DM0 &= ~(1 << (b_PortPin - 8));
            P2_DM1 &= ~(1 << (b_PortPin - 8));
            P2_DM2 &= ~(1 << (b_PortPin - 8));
        }
        else if ((16 <= b_PortPin) && (b_PortPin < 23))
        {
            P1_DM0 &= ~(1 << (b_PortPin - ((b_PortPin - 16) * 2 + 9)));
            P1_DM1 &= ~(1 << (b_PortPin - ((b_PortPin - 16) * 2 + 9)));
            P1_DM2 &= ~(1 << (b_PortPin - ((b_PortPin - 16) * 2 + 9)));
        }
        else if ((23 <= b_PortPin) && (b_PortPin < 25))
        {
            P3_DM0 &= ~(1 << (b_PortPin - 23));
            P3_DM1 &= ~(1 << (b_PortPin - 23));
            P3_DM2 &= ~(1 << (b_PortPin - 23));
        }
    }
}

/**
 * @brief 初始化触摸模块
 * 
 * @param p_TKChnCfg 触摸阈值数据
 * @param ChnNum 触摸通道数
 * @param ChnInfoPtr 通道数据首地址
 */
void TKL_Init(u16 *p_TKChnCfg, u8 ChnNum, u8 *ChnInfoPtr)
{
    u8 chn;
	  TKLChnCfg cfg;
	
	  I2C_Init();    //IIC初始化
	
    if ((ChnNum == 0) || (ChnNum > TKL_CHN_NUM_MAX))
    {
        return;
    }	

    // 初始化结构体数据
    TKLInfo.Chns = (TKL_Chn_t*)ChnInfoPtr;
    TKLInfo.ChnNum = ChnNum;
    TKLInfo.ChnIndex = 0x1F;
		
	  for(chn=0; chn< ChnNum; chn++)
	  {
			cfg.ChnId  = (u8)p_TKChnCfg[(chn*6) +0];
			cfg.KeyId  = (u8)p_TKChnCfg[(chn*6) +1];
			cfg.Idac   = (u8)p_TKChnCfg[(chn*6) +2];
			cfg.Baseline   = p_TKChnCfg[(chn*6) +3];
			cfg.Touchline  = p_TKChnCfg[(chn*6) +4];
			cfg.Waterline  = p_TKChnCfg[(chn*6) +5];			
			ThresholdInit(chn, &cfg);
	  }
		
    for(chn = 0; chn < ChnNum; chn++)
    {
        TKL_Key_SetFindIcomState(chn);
    }		

    TKL_CSDInit(); // 初始化 CSD 外设
}

/**
 * @brief 驱动按键扫描逻辑，在用户程序中循环调用
 * 
 */
void TKL_Action(void)
{
    // 如果数据缓存未满，或者一轮数据未获取完，不处理
    if(!TKLInfo.DatFull || !TKLInfo.DatLoopEnd)
    {
        return;
    }

    TKL_AcquisitionAll();
    TKL_ProcessAll();
    // 清除数据标志，开始下一轮扫描
    TKLInfo.DatLoopEnd = 0;
}

