CH59x CH58x 触摸按键应用开发实例讲解(二)

发布于:2025-08-31 ⋅ 阅读:(19) ⋅ 点赞:(0)

前段时间,我们根据当时在做的一个项目,写了篇《CH592/CH582 触摸按键应用开发实例讲解》的博文,在那篇博文中我们简单介绍了触摸按键的原理,所以这里我们就讲触摸按键的原理,而侧重讲解触摸按键的实现,官方提供的触摸库的添加及应用。

想要了解触摸按键的原理的朋友可以自行搜索相关资料,也可以参考我们之前的博文:

CH592/CH582 触摸按键应用开发实例讲解-CSDN博客

一、在现有工程中添加触摸库

本博文以CH584M为例讲解,我们在官方提供的CH585例程中找到以下目录文件并复制到我们现有的工程目录中。

在工程中设置include目录并添加触摸库。

按上图所示添加相应的文件目录并修改设置好工程,那我们就完成了在现有的工程中添加触摸功能的软件框架。

二、初步理解例程中触摸功能的实现

从上图中我们可以看出,使用官方提供的触摸库还是比较简单的,我们需要做的只是简单几个步骤:

1、初始化触摸库的基本参数:

/*********************************************************************
 * @fn      touch_on_TMOS_init
 *
 * @brief   触摸初始化函数(基于TMOS)
 *
 * @return  none
 */
void touch_on_TMOS_init(void)
{
    TouchKey_TaskID = TMOS_ProcessEventRegister(Touch_Key_ProcessEvent);
    TKY_PeripheralInit();       /* 初始外设,例如背光和蜂鸣器等 */
    touch_Init(&touch_cfg);				/* 初始化触摸库  */

    wakeUpCount = 50; // 唤醒后持续时间,单位TRIGGER_TIME(100ms)
    wakeupflag = 1;   // 置成唤醒状态
    triggerTime = TRIGGER_TIME;
    TKY_SetSleepStatusValue(~tkyQueueAll);
#if TKY_SLEEP_EN
    tky_DealData_start();
#else
    tky_DealData_stop();
#endif

#if PRINT_EN
    tmos_set_event(TouchKey_TaskID, DEBUG_PRINT_EVENT);
#endif

    tmos_set_event(TouchKey_TaskID, WAKEUP_DATA_DEAL_EVT);
    tmos_set_event(TouchKey_TaskID, TKY_KEEPALIVE_EVENT);

//    TMR0_TimerInit(FREQ_SYS/1000);               //定时周期为1ms
//    TMR0_ITCfg(ENABLE, TMR0_3_IT_CYC_END);
//    PFIC_EnableIRQ( TMR0_IRQn );

    dg_log("Touch Key init Finish!\n");
}
/********************************************************************************************************
 * @fn      touch_Init
 * 
 * @brief   初始化按键. 该函数被 TKY_Init() 调用。
 *
 * @return  none
 */
void touch_Init(touch_cfg_t *p)
{
    touch_InitHard();             /* 初始化Touch硬件和库基本参数 */
    touch_InitVar(p);           /* 初始化按键变量 */
}


/********************************************************************************************************
 * @fn      touch_InitHard
 * @brief   初始化触摸按键
 * @param   无
 * @return  无
 */
static void touch_InitHard (void)
{
    touch_Regcfg();
    touch_Baseinit();
    touch_Channelinit();
}

/********************************************************************************************************
 * @fn      touch_Baseinit
 * @brief   触摸基础库初始化
 * @param   无
 * @return  无
 */
static void touch_Baseinit(void)
{
    uint8_t sta=0xff;
    TKY_BaseInitTypeDef TKY_BaseInitStructure = {0};
    for(uint8_t i = 0; i < TKY_MAX_QUEUE_NUM; i++)  //初始化tkyQueueAll变量
    {
        tkyQueueAll |= 1<<i;
    }
    dg_log("tQ : %04x\n",tkyQueueAll);

    //----------触摸按键基础设置初始化--------
    TKY_BaseInitStructure.filterMode = TKY_FILTER_MODE;
    TKY_BaseInitStructure.shieldEn = TKY_SHIELD_EN;
    TKY_BaseInitStructure.singlePressMod = TKY_SINGLE_PRESS_MODE;
    TKY_BaseInitStructure.filterGrade = TKY_FILTER_GRADE;
    TKY_BaseInitStructure.maxQueueNum = TKY_MAX_QUEUE_NUM;
    TKY_BaseInitStructure.baseRefreshOnPress = TKY_BASE_REFRESH_ON_PRESS;
    //---基线更新速度,baseRefreshSampleNum和filterGrade,与基线更新速度成反比,基线更新速度还与代码结构相关,可通过函数GetCurQueueBaseLine来观察---
    TKY_BaseInitStructure.baseRefreshSampleNum = TKY_BASE_REFRESH_SAMPLE_NUM;
    TKY_BaseInitStructure.baseUpRefreshDouble = TKY_BASE_UP_REFRESH_DOUBLE;
    TKY_BaseInitStructure.baseDownRefreshSlow = TKY_BASE_DOWN_REFRESH_SLOW;
    TKY_BaseInitStructure.tkyBufP = TKY_MEMBUF;
    sta = TKY_BaseInit( TKY_BaseInitStructure );
    dg_log("TKY_BaseInit:%02X\r\n",sta);
}

2、初始化触摸通道:

#define GEN_TKY_CH_INIT(qNum,chNum,chTime,disChTime,slpmode,chBaseline,trs,trs2) \
    {\
     .queueNum=qNum,.channelNum=chNum,.chargeTime=chTime,.disChargeTime=disChTime,\
     .sleepStatus=slpmode,\
     .baseLine = chBaseline,\
     .threshold=trs,\
     .threshold2=trs2\
    }


#define TKY_CHS_INIT \
                GEN_TKY_CH_INIT(TKY_QUEUE_0,0,0,3,0,3382,   85,  65),\
                GEN_TKY_CH_INIT(TKY_QUEUE_1,5,0,3,0,3382,   88,  68),\
                GEN_TKY_CH_INIT(TKY_QUEUE_2,9,0,3,0,3382,  94,  74),\
                GEN_TKY_CH_INIT(TKY_QUEUE_3,4,0,3,0,3382,   68,  48),\
                GEN_TKY_CH_INIT(TKY_QUEUE_4,6,0,3,0,3382,   83,  63),\
                GEN_TKY_CH_INIT(TKY_QUEUE_5,10,0,3,0,3382,   90,  70),\
                GEN_TKY_CH_INIT(TKY_QUEUE_6,3,0,3,0,3382,   67,  47),\
                GEN_TKY_CH_INIT(TKY_QUEUE_7,7,0,3,0,3382,   88,  68),\
                GEN_TKY_CH_INIT(TKY_QUEUE_8,1,0,3,0,3382,   74,  64),\
                GEN_TKY_CH_INIT(TKY_QUEUE_9,2,0,3,0,3382,   68,  48),\
                GEN_TKY_CH_INIT(TKY_QUEUE_10,8,0,3,0,3382, 84,  64),\
                GEN_TKY_CH_INIT(TKY_QUEUE_11,11,0,3,0,3382, 64,  44)



static const TKY_ChannelInitTypeDef my_tky_ch_init[TKY_QUEUE_END] = {TKY_CHS_INIT};


static const uint32_t TKY_Pin[ TKY_QUEUE_END ] = {
    GPIO_Pin_4, GPIO_Pin_15, GPIO_Pin_0, GPIO_Pin_14,GPIO_Pin_3, GPIO_Pin_6, GPIO_Pin_13,
    GPIO_Pin_2, GPIO_Pin_5, GPIO_Pin_12,GPIO_Pin_1, GPIO_Pin_7,
};


/********************************************************************************************************
 * @fn      touch_InitHard
 * @brief   初始化触摸按键
 * @param   无
 * @return  无
 */
static void touch_InitHard (void)
{
    touch_Regcfg();
    touch_Baseinit();
    touch_Channelinit();
}


/********************************************************************************************************
 * @fn      touch_Channelinit
 * @brief   触摸通道初始化
 * @param   无
 * @return  无
 */
static void touch_Channelinit(void)
{
    uint8_t error_flag = 0;
    uint16_t chx_mean = 0,chx_mean_last = 0;
    for(uint8_t i = 0; i < TKY_MAX_QUEUE_NUM; i++)
    {
    	TKY_CHInit(my_tky_ch_init[i]);
    }

    for(uint8_t i = 0; i < TKY_MAX_QUEUE_NUM; i++)
    {

    	chx_mean = TKY_GetCurChannelMean(my_tky_ch_init[i].channelNum, my_tky_ch_init[i].chargeTime,
										 my_tky_ch_init[i].disChargeTime, 1000);

    	if(chx_mean < 3000 || chx_mean > 3800)
    	{
    		error_flag = 1;
    	}
    	else
    	{
    		TKY_SetCurQueueBaseLine(i, chx_mean);
    	}
    }
    //充放电基线值异常,重新校准基线值
    if(error_flag != 0)
    {
        dg_log("\n\nCharging parameters error, preparing for recalibration ...\n\n");
        uint16_t charge_time;
        for (uint8_t i = 0; i < TKY_MAX_QUEUE_NUM; i++)
        { 
          charge_time = 0,chx_mean = 0;
          while (1)
          {
              chx_mean = TKY_GetCurChannelMean(my_tky_ch_init[i].channelNum, charge_time,3, 1000);

//              dg_log("testing .... chg : %d, baseline : %d\n",charge_time,chx_mean);//打印基线值

              if ((charge_time == 0) && ((chx_mean > 3800))) {//低于最小充电参数
                  dg_log("Error, %u KEY%u Too small Cap,Please check the hardware !\r\n",chx_mean,i);
                  break;
              }
              else {
                  if ((chx_mean > 3000) &&(chx_mean < 3800)) {//充电参数正常
                      TKY_SetCurQueueBaseLine(i, chx_mean);
                      TKY_SetCurQueueChargeTime(i,charge_time,3);
                      dg_log("channel:%u, chargetime:%u,BaseLine:%u\r\n",
                            i, charge_time, chx_mean);
                      break;
                  }else if(chx_mean >= 3800)
                  {
                	  TKY_SetCurQueueBaseLine (i, chx_mean_last);
                      TKY_SetCurQueueChargeTime(i,charge_time-1,3);
                	  dg_log("Warning,channel:%u Too large Current, chargetime:%u,BaseLine:%u\r\n",
                	                              i, charge_time, chx_mean);
                	  break;
                  }
                  charge_time++;
                  chx_mean_last = chx_mean;
                  if (charge_time > 0x1f) {    //超出最大充电参数
                      dg_log("Error, Chargetime Max,KEY%u Too large Cap,Please check the hardware !\r\n",i);
                      break;
                  }
              }
          }
        }
    }
    TKY_SaveAndStop();
}

void touch_GPIOModeCfg (GPIOModeTypeDef mode, uint32_t channel)
{
    switch(mode)
    {
        case GPIO_ModeIN_Floating:
            R32_PA_PD_DRV &= ~TKY_Pin[ channel ];
            R32_PA_PU &= ~TKY_Pin[ channel ];
            R32_PA_DIR &= ~TKY_Pin[ channel ];
            break;

        case GPIO_ModeOut_PP_5mA://推挽接地放电放电
            R32_PA_PU &= ~TKY_Pin[ channel ];
            R32_PA_PD_DRV &= ~TKY_Pin[ channel ];
            R32_PA_DIR |= TKY_Pin[ channel ];
            R32_PA_CLR |= TKY_Pin[ channel ];
            break;
        default:
            break;
    }
}

3、扫描按键并根据按键执行相应的动作


/*********************************************************************
 * @fn      Touch_Key_ProcessEvent
 *
 * @brief   触摸按键处理函数
 *
 * @return  none
 */
tmosEvents Touch_Key_ProcessEvent(tmosTaskID task_id, tmosEvents events)
{
    uint16_t res;

    if (events & WAKEUP_DATA_DEAL_EVT)
    {
        touch_Scan();
        tky_on_TMOS_dataProcess();
#if TKY_SLEEP_EN
        if (wakeupflag)
#endif
        tmos_start_task (TouchKey_TaskID, WAKEUP_DATA_DEAL_EVT, WAKEUP_TIME);
        return (events ^ WAKEUP_DATA_DEAL_EVT);
    }

    if (events & DEALDATA_EVT)
    {
        PeriodicDealData();
#if TKY_SLEEP_EN
        if (!advState || wakeupflag)
#endif
            tmos_start_task(TouchKey_TaskID, DEALDATA_EVT, triggerTime);
        return (events ^ DEALDATA_EVT);
    }

#if PRINT_EN
    if (events & DEBUG_PRINT_EVENT)
    {
        touch_InfoDebug();

        tmos_start_task(TouchKey_TaskID, DEBUG_PRINT_EVENT,SLEEP_TRIGGER_TIME);
        return (events ^ DEBUG_PRINT_EVENT);
    }
#endif

    if(events & TKY_KEEPALIVE_EVENT)
    {
        return events;
    }

    return 0;
}

/********************************************************************************************************
 * @fn      touch_Scan
 * @brief   扫描所有按键。非阻塞,被systick中断周期性的调用
 * @param   无
 * @return  无
 */
void touch_Scan(void)
{
    uint8_t i;

    TKY_LoadAndRun();           //---载入休眠前保存的部分设置---
    keyData = TKY_PollForFilter();
    TKY_SaveAndStop();          //---对相关寄存器进行保存---
#if TKY_SLEEP_EN
    if (keyData)
    {
        wakeUpCount = WAKEUPTIME; //---唤醒时间---
    }
#endif

    touch_DetectKey(p_touch_cfg->touch_button_cfg);

//    WheelData = touch_DetectWheelSlider(p_touch_cfg->touch_wheel_cfg);

//    SilderData = touch_DetecLineSlider(p_touch_cfg->touch_slider_cfg);
}

char displaystring[16];
uint8_t keyidx = 0;
/*********************************************************************
 * @fn      tky_on_TMOS_dataProcess
 *
 * @brief   触摸数据处理函数(基于TMOS),蓝牙连接成功后将获取到键值以通知的形式上报给上位机蓝牙
 *
 * @return  none
 */
__HIGH_CODE
void tky_on_TMOS_dataProcess(void)
{
    uint8_t key_val = 0x00;

    key_val = touch_GetKey();

    if (key_val != 0x00)
    {
        switch (key_val)
         {
             case KEY_NONE   :   break;
             case KEY_0_DOWN :   TKY_KeyBacklightOut(0,!getBacklightState(0));  displaystring[keyidx++]='0'; break;
             case KEY_0_LONG :   TKY_KeyBacklightOut(0,!getBacklightState(0));  PRINT("KEY_1_LONG !\n");break;
             case KEY_0_UP   :   TKY_KeyBacklightOut(0,DISABLE); PRINT("KEY_1_UP   !\n");break;
             case KEY_1_DOWN :   TKY_KeyBacklightOut(1,!getBacklightState(1));  displaystring[keyidx++]='1';break;
             case KEY_1_LONG :   TKY_KeyBacklightOut(1,!getBacklightState(1));  PRINT("KEY_2_LONG !\n");break;
             case KEY_1_UP   :   TKY_KeyBacklightOut(1,DISABLE); PRINT("KEY_2_UP   !\n");break;
             case KEY_2_DOWN :   TKY_KeyBacklightOut(2,!getBacklightState(2));  displaystring[keyidx++]='2';break;
             case KEY_2_LONG :   TKY_KeyBacklightOut(2,!getBacklightState(2));  PRINT("KEY_3_LONG !\n");break;
             case KEY_2_UP   :   TKY_KeyBacklightOut(2,DISABLE); PRINT("KEY_3_UP   !\n");break;
             case KEY_3_DOWN :   TKY_KeyBacklightOut(3,!getBacklightState(3));  displaystring[keyidx++]='3';break;
             case KEY_3_LONG :   TKY_KeyBacklightOut(3,!getBacklightState(3));  PRINT("KEY_4_LONG !\n");break;
             case KEY_3_UP   :   TKY_KeyBacklightOut(3,DISABLE); PRINT("KEY_4_UP   !\n");break;
             case KEY_4_DOWN :   TKY_KeyBacklightOut(4,!getBacklightState(4));  displaystring[keyidx++]='4';break;
             case KEY_4_LONG :   TKY_KeyBacklightOut(4,!getBacklightState(4));  PRINT("KEY_5_LONG !\n");break;
             case KEY_4_UP   :   TKY_KeyBacklightOut(4,DISABLE); PRINT("KEY_5_UP   !\n");break;
             case KEY_5_DOWN :   TKY_KeyBacklightOut(5,!getBacklightState(5));  displaystring[keyidx++]='5';break;
             case KEY_5_LONG :   TKY_KeyBacklightOut(5,!getBacklightState(5));  PRINT("KEY_6_LONG !\n");break;
             case KEY_5_UP   :   TKY_KeyBacklightOut(5,DISABLE); PRINT("KEY_6_UP   !\n");break;
             case KEY_6_DOWN :   TKY_KeyBacklightOut(6,!getBacklightState(6));  displaystring[keyidx++]='6';break;
             case KEY_6_LONG :   TKY_KeyBacklightOut(6,!getBacklightState(6));  PRINT("KEY_7_LONG !\n");break;
             case KEY_6_UP   :   TKY_KeyBacklightOut(6,DISABLE); PRINT("KEY_7_UP   !\n");break;
             case KEY_7_DOWN :   TKY_KeyBacklightOut(7,!getBacklightState(7));  displaystring[keyidx++]='7';break;
             case KEY_7_LONG :   TKY_KeyBacklightOut(7,!getBacklightState(7));  PRINT("KEY_8_LONG !\n");break;
             case KEY_7_UP   :   TKY_KeyBacklightOut(7,DISABLE); PRINT("KEY_8_UP   !\n");break;
             case KEY_8_DOWN :   TKY_KeyBacklightOut(8,!getBacklightState(8));  displaystring[keyidx++]='8';break;
             case KEY_8_LONG :   TKY_KeyBacklightOut(8,!getBacklightState(8));  PRINT("KEY_6_LONG !\n");break;
             case KEY_8_UP   :   TKY_KeyBacklightOut(8,DISABLE); PRINT("KEY_6_UP   !\n");break;
             case KEY_9_DOWN :   TKY_KeyBacklightOut(9,!getBacklightState(9));  displaystring[keyidx++]='9';break;
             case KEY_9_LONG :   TKY_KeyBacklightOut(9,!getBacklightState(9));  PRINT("KEY_7_LONG !\n");break;
             case KEY_9_UP   :   TKY_KeyBacklightOut(9,DISABLE); PRINT("KEY_7_UP   !\n");break;
             case KEY_10_DOWN :   TKY_KeyBacklightOut(10,!getBacklightState(10));  displaystring[keyidx++]='#';break;
             case KEY_10_LONG :   TKY_KeyBacklightOut(10,!getBacklightState(10));  PRINT("KEY_8_LONG !\n");break;
             case KEY_10_UP   :   TKY_KeyBacklightOut(10,DISABLE); PRINT("KEY_8_UP   !\n");break;
             case KEY_11_DOWN :   TKY_KeyBacklightOut(11,!getBacklightState(11));  displaystring[keyidx++]='*';break;
             case KEY_11_LONG :   TKY_KeyBacklightOut(11,!getBacklightState(11));  PRINT("KEY_8_LONG !\n");break;
             case KEY_11_UP   :   TKY_KeyBacklightOut(11,DISABLE); PRINT("KEY_8_UP   !\n");break;
             default : break;
         }
        if(keyidx >= 16)
            keyidx = 0;
    }
}



void TKY_KeyBacklightOut (uint8_t key, FunctionalState s)
{
    // PRINT("%d,%d",key,s);
    if (DISABLE == s)
    {
        BacklightArray[ key ].BacklightStates = 0;
        MBI5020_Display(0);
    }
    else
    {
        BacklightArray[ key ].BacklightStates = 1;
        MBI5020_Display(Key_Led_Array[key]);
        sc5080_soundplay(SOUND_DI);
    }
}

uint8_t getBacklightState(uint8_t key)
{
    return BacklightArray[ key ].BacklightStates;
}

void TKY_KeyBacklightOut (uint8_t key, FunctionalState s)
{
    // PRINT("%d,%d",key,s);
    if (DISABLE == s)
    {
        BacklightArray[ key ].BacklightStates = 0;
        MBI5020_Display(0);
    }
    else
    {
        BacklightArray[ key ].BacklightStates = 1;
        MBI5020_Display(Key_Led_Array[key]);
        sc5080_soundplay(SOUND_DI);
    }
}

uint8_t getBacklightState(uint8_t key)
{
    return BacklightArray[ key ].BacklightStates;
}

以上代码并不是在一个文件中,只是为了方便,放在一起,大家可以根据函数名在官方例程中搜索获取更多信息。

官方例程基本上拿过来就能使用,不需要作太多的修改,所以对例程中的代码我们不会作过多的讲解,事实上只要把触摸按键的几个数据结构弄清楚了,代码基本上也就明明白白了。

比如以下几个数据结构:


touch_button_cfg_t p_selfkey = 
{
    .num_elements = TOUCH_KEY_ELEMENTS,
    .p_elem_index = touch_key_ch,
    .p_stbtn = s_tBtn
};


touch_cfg_t touch_cfg = 
{
    .touch_button_cfg = &p_selfkey,
    .touch_slider_cfg = NULL,
    .touch_wheel_cfg = NULL
};


typedef struct
{
	uint8_t maxQueueNum;				    //--测试队列数量--
	uint8_t singlePressMod;                	//--单按键模式---
	uint8_t shieldEn;                   	//--屏蔽使能---
	uint8_t filterMode;				        //--滤波器模式--
	uint8_t filterGrade;				    //--滤波器等级--
	uint8_t peakQueueNum;                	//--按键最大偏移队列---
	uint8_t peakQueueOffset;             	//--按键最大偏移队列的偏移值---
	uint8_t baseRefreshOnPress;			    //--基线在按键按下时是否进行--
	uint8_t baseUpRefreshDouble;        	//--基线向上刷新倍速参数---
	uint8_t baseDownRefreshSlow;       		//--基线向下更新降速参数---
	uint8_t rfu[2];
    uint32_t baseRefreshSampleNum;     		//--基线刷新采样次数--
	uint32_t *tkyBufP;					 	//--测试通道数据缓冲区指针--
}TKY_BaseInitTypeDef;

typedef struct
{
	uint8_t queueNum;                 		//--该通道在测试队列的序号--
	uint8_t channelNum;               		//--该通道对应的ADC通道标号--
	uint16_t chargeTime;		            //--该通道充电时间--
	uint16_t disChargeTime;            		//--该通道放电时间--
	uint16_t baseLine;  	   	        	//--基线--
	uint16_t threshold;		            	//--阈值--
	uint16_t threshold2;              		//--阈值2--
	uint8_t sleepStatus;		          	//--休眠--
}TKY_ChannelInitTypeDef;

三、触摸通道的修改

官方例程中提供了8个触摸通道,我们实际应用中往往需要修改这部分内容来满足我们的项目需要。

比如我们的项目需要12个触摸按键,而且触摸通道的顺序也可能与例程中不一样。

根据项目实际需要的触摸按键数量修改就可以。

这里需要根据实际需要的触摸按键数量修改数量并调整触摸序列与通道的对应关系。

TKY_Pin需要根据TKY_CHS_INIT中的通道的顺序调整。比如TKY_CHS_INIT中第一个的通道为ADC 0通道,对应的引脚为PA4,其余以此类推。

这段代码就是扫描后获取按键,并根据按键执行相应的动作,这里只是简单的点亮对应的LED指示灯。

对比前后两个项目,两篇博文,需要说明的是,CH584/CH585与CH582及CH592系列在引脚的功能方面有所区别,比如CH584/CH585的ADC通道全部为PA引脚上,而CH582及CH592系列的ADC通道分布在PA及PB引脚上,所以在触摸按键的数据结构及代码中涉及到这点的地方要注意区别。

四、完整代码请参考:

CH584/CH585触摸按键Touchkey功能12触摸按键功能演示程序资源-CSDN下载


网站公告

今日签到

点亮在社区的每一天
去签到