BES(恒玄) 平台 复杂按键 实现

昨天 有说到 LED 的配置 ,关于UI 的三大组件  :LED 、按键 、提示音。今天就说下按键部分.

BES 平台里面 包含了 大部分按键代码 : 按键l类型分power 、普通GPIO按键 ADC 按键

按键事件有单击 双击 长按 长长按 重复等。 先分析下 默认按键的实现逻辑。

一、普通按键实现逻辑分析:

1.先看下普通按键:

从上结构体 和按键注册表 可以得知 按键FN1对应的GPIO为 PIO_02,并且拉低(按下)有效,默认是高电平。

按键实现原理 :

按键的按下和弹起会产生相应中断 通知到 按键处理函数(hal_gpiokey_irqhandler / hal_key.c)

所有的按键抖动 都会通过硬件定时器 计时 消抖触发并在hal_gpiokey_irqhandler 函数里面处理。处理后的按键事件通过 函数

static int send_key_event(enum HAL_KEY_CODE_T code, enum HAL_KEY_EVENT_T event) 广播到应用层。

下面的 宏定义 表示 按键时间 ,用户可以根据自定义修改按键事件长 
#define CFG_SW_KEY_LLPRESS_THRESH_MS        4000
#define CFG_SW_KEY_LPRESS_THRESH_MS         1500
#define CFG_SW_KEY_REPEAT_THRESH_MS         500
#define CFG_SW_KEY_DBLCLICK_THRESH_MS       500
#define CFG_SW_KEY_INIT_DOWN_THRESH_MS      200
#define CFG_SW_KEY_INIT_LPRESS_THRESH_MS    6500
#define CFG_SW_KEY_INIT_LLPRESS_THRESH_MS   10000
#define CFG_SW_KEY_CHECK_INTERVAL_MS        25

//common key define  //  hardware ticks conversion to  ms.
#define KEY_LONGLONGPRESS_THRESHOLD         MS_TO_TICKS(CFG_SW_KEY_LLPRESS_THRESH_MS)
#define KEY_LONGPRESS_THRESHOLD             MS_TO_TICKS(CFG_SW_KEY_LPRESS_THRESH_MS)
#define KEY_DOUBLECLICK_THRESHOLD           MS_TO_TICKS(CFG_SW_KEY_DBLCLICK_THRESH_MS)
#define KEY_LONGPRESS_REPEAT_THRESHOLD      MS_TO_TICKS(CFG_SW_KEY_REPEAT_THRESH_MS)

#define KEY_INIT_DOWN_THRESHOLD             MS_TO_TICKS(CFG_SW_KEY_INIT_DOWN_THRESH_MS)
#define KEY_INIT_LONGPRESS_THRESHOLD        MS_TO_TICKS(CFG_SW_KEY_INIT_LPRESS_THRESH_MS)
#define KEY_INIT_LONGLONGPRESS_THRESHOLD    MS_TO_TICKS(CFG_SW_KEY_INIT_LLPRESS_THRESH_MS)

#define KEY_CHECKER_INTERVAL                MS_TO_TICKS(CFG_SW_KEY_CHECK_INTERVAL_MS)

#define KEY_DEBOUNCE_INTERVAL               (KEY_CHECKER_INTERVAL * 2)

 

2. 增加特殊按键事件 比如:单击/双击/三击/四击 后长按事件 :

a. 在结构体中 中增加 两个数据表示当前按下次数:

struct HAL_KEY_STATUS_T {
    enum HAL_KEY_CODE_T code_down;
    enum HAL_KEY_CODE_T code_ready;
    enum HAL_KEY_CODE_T code_click;
    enum HAL_KEY_EVENT_T event;
    uint32_t time_updown;
    uint32_t time_click;
    uint8_t cnt_repeat;
    uint8_t cnt_click;
	
    uint8_t click_flag; // Record the current press times and count the subsequent long press events.
    uint8_t cnt_click_long_repeat;//Record the current press times and count the subsequent repeat events.
};

b. 修改按键弹起事件时候清除的上次按键事件类型:

if (up_new) {
        if (key_status.event == HAL_KEY_EVENT_LONGPRESS || key_status.event == HAL_KEY_EVENT_LONGLONGPRESS) {
            // LongPress is finished when all of the LongPress keys are released
            if ((code_down & key_status.code_ready) == 0) {
                key_status.event = HAL_KEY_EVENT_NONE;
            }
        } 
		else if (key_status.event == HAL_KEY_EVENT_DOWN) {
            // Enter click handling if not in LongPress
            key_status.event = HAL_KEY_EVENT_UP;
        } 
      /***The key pops up to clear the last recorded event type***/
		else if(key_status.event >= HAL_KEY_EVENT_LONGPRESS&&key_status.event <= HAL_KEY_EVENT_LONGLONGPRESS_AFTER_RCLICK) {
          key_status.cnt_repeat = 0;           
          key_status.cnt_click = 0;
          key_status.click_flag = 0;          
          key_status.code_click = HAL_KEY_CODE_NONE;         
          key_status.event = HAL_KEY_EVENT_NONE;
        }
/***The key pops up to clear the last recorded event type***/
    }

c. 在按键弹起时候,记录按键次数,为后续长按事件做准备:

if (key_status.event == HAL_KEY_EVENT_UP) {
        if (key_status.code_click == HAL_KEY_CODE_NONE || key_status.code_click != key_status.code_ready) {
            if (key_status.code_click != HAL_KEY_CODE_NONE) {
                send_key_event(key_status.code_click, HAL_KEY_EVENT_CLICK + key_status.cnt_click);
            }
            key_status.code_click = key_status.code_ready;
            key_status.cnt_click = 0;
            key_status.click_flag = 1;
            key_status.time_click = time;
        } else if (up_new && (up_new | key_status.code_down) == key_status.code_click) {
            key_status.cnt_click++;
            key_status.click_flag++; /*Each time the button pops up, it increases by 1, which in turn means click/double click...*/
            key_status.time_click = time;
        }
        if (time - key_status.time_click >= KEY_DOUBLECLICK_THRESHOLD || key_status.cnt_click >= MAX_KEY_CLICK_COUNT) {
            if(key_status.cnt_click >= MAX_KEY_CLICK_COUNT)
                send_key_event(key_status.code_click, HAL_KEY_EVENT_RAMPAGECLICK);
            else
                send_key_event(key_status.code_click, HAL_KEY_EVENT_CLICK + key_status.cnt_click);
            key_status.code_click = HAL_KEY_CODE_NONE;
            key_status.cnt_click = 0;
            key_status.click_flag = 0; /*over max times*/
            key_status.event = HAL_KEY_EVENT_NONE;
        }
    }

d. 在按键按下去的时候 修改如下代码:

if (key_status.event == HAL_KEY_EVENT_DOWN){
        if ((time - key_status.time_updown) >= MS_TO_TICKS(2000)&&(time - key_status.time_updown) <=MS_TO_TICKS(3500)){
           if (key_status.click_flag){
               key_status.cnt_click_long_repeat = 0;
               key_status.event = (HAL_KEY_EVENT_LONGPRESS_AFTER_CLICK+key_status.click_flag-1);
               send_key_event(key_status.code_ready, key_status.event);
			   TRACE("press_time over 2s,KEY_CODE=%d\n",key_status.event);
           } 
        }
    }
    if(key_status.event>HAL_KEY_EVENT_LONGPRESS&&key_status.event<HAL_KEY_EVENT_LONGLONGPRESS){
		if (time - key_status.time_updown >= KEY_LONGPRESS_THRESHOLD){
			key_status.event =key_status.event+(HAL_KEY_EVENT_LONGLONGPRESS-HAL_KEY_EVENT_LONGPRESS);
        	send_key_event(key_status.code_ready, key_status.event);
			TRACE("press_time over 4s,KEY_CODE=%d\n",key_status.event);
		}
		key_status.cnt_repeat = 0;           
        key_status.cnt_click = 0;
        key_status.click_flag = 0;          
        key_status.code_click = HAL_KEY_CODE_NONE;         
        key_status.event = HAL_KEY_EVENT_NONE;
	}
    /*if(key_status.event == HAL_KEY_EVENT_LONGPRESS_AFTER_CLICK)
    {
       key_status.cnt_click_long_repeat++;
       if(key_status.cnt_click_long_repeat == MS_TO_TICKS(500)/KEY_CHECKER_INTERVAL)
       {
            key_status.cnt_click_long_repeat = 0;
            key_status.event = HAL_KEY_EVENT_LONGPRESS_AFTER_CLICK;
            send_key_event(key_status.code_ready, key_status.event);
       }
    }*/ /* If don't need to repeat the event after the click/double click, this section can give up.*/

    

按键底层修改整体代码 如下 链接 :

https://share.weiyun.com/mwR5yWUO

 

二 、按键应用层的实现原理、对接、修改

1. BES 按键的应用层衔接 介绍:

类似于线程处理,考虑到按键的并发处理,所以所有的按键事件放在一个list 链表里面 存储,处理完就 删除当前节点。

hal_key_open(checkPwrKey, key_event_process);

这个函数开启了 所有底层按键驱动 ,并且注册回调函数 (key_detected_callback 即上面send_key_event)。即 后续的按键事件都会通过 主线程下的app_key_handle_process  这个任务处理

 

 

 

2.单边(头戴式 挂脖)按键配置表:

有多个按键可能会遇到组合按键,其实组合按键也可以说时现成的 ,我们看下 按键的定义:

直接修改案件注册表就好了 

3.TWS模式 按键执行注册表:

  

值得说下的是 ,TWS耳机的按键 很多时候时候需要由从耳转发到主耳:

 

三、外设触摸IC Senor检测按键

很多时候 耳机不仅仅用自带的GPIO按键  ,还需要外设辅助按键

有了上面的分析 ,其实外设按键操作就比较简单了 ,在 原有的按键体系中 添加按键注册 ,执行 销毁的相关操作就好了 。

比如: 触摸按键的 初始化:

然后我们看下触摸按键的 处理:

利用现有的案件处理 如果触摸按键那边有动作 ,就会触发GPIO 中断 然后通过I2C 读取按键状态值,利用回调函数 广播按键事件。

#include "cmsis_os.h"
#include "hal_timer.h"
#include "app_key.h"
#include "apps.h"
#include "hal_trace.h"
//#include "btapp.h"

#include "string.h"
#include "app_thread.h"
#include <hal_i2c.h>

extern "C"{
#include "SX921x.h"
}

struct info{
uint8_t keycode;
uint8_t keyevent;
};
struct info keyinfo[2];

//common key define
#define TOUCHKEY_SINGLEPRESS_THRESHOLD          MS_TO_TICKS(1500)			//1500mS
#define TOUCHKEY_DEBOUNCE_INTERVAL			MS_TO_TICKS(40)			//40mS,中断延迟约50mS
#define TOUCHKEY_LONGPRESS_THRESHOLD		3000	///3S
#define TOUCHKEY_CLICK_INTERVAL           		400		///400mS
#define TOUCHKEY_STEADY_THRESHOLD      		500		///500mS

///=============mack:add for touchkey=======================
void app_touchkey_handler(void const *param);
void app_longpress_handler(void const *param);
void app_inear_handler(void const *param);

osTimerId click_interval_timer = NULL;
osTimerDef (CLICKINTV, app_touchkey_handler);
osTimerId longpress_timer = NULL;
osTimerDef (LPRESS,app_longpress_handler);

osTimerId steady_timer = NULL;
osTimerDef (STEADY, app_inear_handler);
///=============end=========================================

uint16_t keycode[2];		//閿€煎垵濮嬪寲	keyinfo[0].keycode = HAL_KEY_CODE_FN3;	keyinfo[1].keycode = HAL_KEY_CODE_FN4; 
uint8_t  keyevent;	//閿€煎垵濮嬪寲涓篐AL_KEY_EVENT_ULTRACLICK // APP灞傛墠鏈夐敭鍊奸€夐」:鍗曞嚮锛屽弻鍑伙紝涓夊嚮锛岄暱鎸夛紝鍗曞嚮+闀挎寜;閿€?閫夐」:鍏ヨ€筹紝鍑鸿€?
bool keystatus[2];		//1涓烘寜涓?鑰冲唴)锛?涓烘澗寮€(鑰冲)

//extern int key_event_process(uint32_t key_code, uint8_t key_event);
extern int (*key_detected_callback)(uint32_t, uint8_t);

static void SX921x_processing(uint8_t data)
{   /*******club 750 disable ph1  ,using ph0 +ph2 ********/

	if(data==0x05){		///1:in_ear
		if(!keystatus[1]){	//鑻ヤ箣鍓嶇姸鎬佹槸out_ear,鍒欐湰娆℃寜涓嬫湁鏁堬紱鍚﹀垯蹇界暐
			keystatus[1] =true;
			key_detected_callback(keycode[1],keyevent);
			TRACE(0,"The proximity sensor detects in-ear movement!\n");
		}
	}
	else{				//0:out_ear
		if(keystatus[1]){	//鑻ヤ箣鍓嶇姸鎬佹槸in_ear,鍒欐湰娆℃寜涓嬫湁鏁堬紱鍚﹀垯蹇界暐
			 keystatus[1] =false;
			 key_detected_callback(keycode[1],keyevent);
			 TRACE(0,"The proximity sensor detects out-ear movement!\n");
		}
	}
}

static void message_send_handle(void){
	APP_MESSAGE_BLOCK msg;

    msg.mod_id = APP_MODUAL_SX9210;
    msg.msg_body.message_id = 0x00;
	msg.msg_body.message_ptr = (u32)NULL;
   	msg.msg_body.message_Param0 = 0x00;/**clear IRQ**/
	msg.msg_body.message_Param1 = 0x01;/**read touthkey status**/
    app_mailbox_put(&msg);
}

static int sx921x_irq_readreg(APP_MESSAGE_BODY *msg_body){
	u8 temp_data=0;
    if(msg_body->message_id!=0x00)  return -1;

	ReadButtons((u8)(msg_body->message_Param0)); //clear NIRQ to Hight
	temp_data=ReadButtons((u8)(msg_body->message_Param1));
	TRACE(1,"%s STAT0_REG=0x%x", __func__,temp_data);  
	SX921x_processing(temp_data);
	//SX921x_Calibration();
	return 0;
}

static void app_SX921x_irqhandler(enum HAL_GPIO_PIN_T pin){
	(void)pin;
	//ReadButtons(SX921x_IRQSTAT_REG); //clear NIRQ to Hight
	//temp_data=ReadButtons(SX921x_STAT0_REG); /*Indicates if proximity is currently being detected for the corresponding phase. */
	/*(i.e. set when phase鈥檚 PROXDIFF value is above detection threshold; Cf. FARCOND for clearing) */
	/*[3:0] = [PH3, PH2, PH1, PH0]*/
	//TRACE("%s STAT0_REG=0x%x", __func__,temp_data);  
	//SX921x_processing(temp_data);
	message_send_handle();
}

static void touch_proximity_sensor_irq_enable(void){

	struct HAL_GPIO_IRQ_CFG_T gpiocfg;
    gpiocfg.irq_enable = true;
    gpiocfg.irq_debounce = true;
    gpiocfg.irq_type = HAL_GPIO_IRQ_TYPE_EDGE_SENSITIVE;
    gpiocfg.irq_polarity =HAL_GPIO_IRQ_POLARITY_LOW_FALLING;
    gpiocfg.irq_handler = app_SX921x_irqhandler;
    hal_gpio_setup_irq((enum HAL_GPIO_PIN_T)TOUTH_IRQ_PIN, &gpiocfg);
}

static void touchkey_init(void){
	keycode[0]=HAL_KEY_CODE_FN3;	//HAL_KEY_CODE_FN3;		//artificial defined to app layer recognation
	keycode[1]=HAL_KEY_CODE_FN4;
	keyevent=HAL_KEY_EVENT_ULTRACLICK;  
	app_set_threadhandle(APP_MODUAL_SX9210, sx921x_irq_readreg);//open message box
}

void hal_touchkey_open(void){
	
	static struct HAL_I2C_CONFIG_T sx9210_i2c_cfg;
	struct HAL_IOMUX_PIN_FUNCTION_MAP SX921x_cfg_gpio={TOUTH_IRQ_PIN, HAL_IOMUX_FUNC_AS_GPIO,\
		HAL_IOMUX_PIN_VOLTAGE_VIO, HAL_IOMUX_PIN_NOPULL};
		
	hal_iomux_set_i2c1();
	sx9210_i2c_cfg.mode = HAL_I2C_API_MODE_TASK;
    sx9210_i2c_cfg.use_dma  = 0;
    sx9210_i2c_cfg.use_sync = 1;
    sx9210_i2c_cfg.speed = 20000;
    sx9210_i2c_cfg.as_master = 1;
    hal_i2c_open(HAL_I2C_ID_1, &sx9210_i2c_cfg);

	hal_iomux_init(&SX921x_cfg_gpio, 1);
    hal_gpio_pin_set_dir((enum HAL_GPIO_PIN_T) SX921x_cfg_gpio.pin, HAL_GPIO_DIR_IN, 1);
	touch_proximity_sensor_irq_enable();
	
	InitSX921x();
	osDelay(10);
	ReadButtons(SX921x_IRQSTAT_REG); //clear NIRQ to Hight
	touchkey_init();
	REL_TRACE(1,"hal_touchkey_open successful!");
}

void hal_touchkey_close(void){
	/*hal_gpiokey_disable_irq(HAL_IOMUX_PIN_P0_2);*/
	struct HAL_GPIO_IRQ_CFG_T gpiocfg = {false,false,HAL_GPIO_IRQ_TYPE_EDGE_SENSITIVE,\
	HAL_GPIO_IRQ_POLARITY_HIGH_RISING,NULL};

	SX921x_suspend();		//disable SX9210
	app_set_threadhandle(APP_MODUAL_SX9210, NULL);//close message box
    hal_gpio_setup_irq((enum HAL_GPIO_PIN_T)TOUTH_IRQ_PIN, &gpiocfg);
 /*===================关闭 I2C通讯======================*/
	hal_i2c_close(HAL_I2C_ID_1);	//close I2C
	REL_TRACE(0,"hal_touchkey_close !");
}

void app_touch_key(APP_KEY_STATUS *status, void *param)
{
	static uint32_t period[3];		//用于按键计时,period[0]按下时间;period[1]松开后间隔时间;
	uint32_t time;

	switch(status->code){
		case HAL_KEY_CODE_FN3:
			if(keystatus[0] ==true){///1:pressed
				osTimerStop(click_interval_timer);//400mS double click interval timer;
				osTimerStart(longpress_timer,TOUCHKEY_LONGPRESS_THRESHOLD);	//3S longpresstimer;
				time = hal_sys_timer_get();
				period[0]=time;				//record pressed time stamp;
				period[2] =time-period[1];		//record key intv period;
				period[1] =0;					//clear released time stamp;
			}
			else{		///0:released
				osTimerStop(longpress_timer);		//清1.5S长按timer;
				time = hal_sys_timer_get();
				period[1] =time;				//record released time stamp;
				TRACE(1,"time-period[0]=%d ms\n",TICKS_TO_MS(time-period[0]));
				if((time-period[0]) >TOUCHKEY_DEBOUNCE_INTERVAL && (time-period[0]) <TOUCHKEY_SINGLEPRESS_THRESHOLD) 	//80mS~1.5S
				{
					if(keyinfo[0].keyevent<3){	///3 鍑讳簨浠?
						keyinfo[0].keyevent++;
					}
					if(keyinfo[0].keyevent>2){
						if((period[2]*13>(time-period[1])*10)&&(period[2]*7<(time-period[1])*10)){	//+-30%余度
							osTimerStart(click_interval_timer,40);	//40mS后即上传键值,不必再等按键间隔;
							period[2]=0;		//clear intv period;
						}
					}
					period[0] =0;		//clear pressed time stamp;
					osTimerStart(click_interval_timer,TOUCHKEY_CLICK_INTERVAL);	//启动400mS muticlicktimer;
					TRACE(1,"click_interval_timer start\n");
				}	
			}
		break;
///==========================================================	
		case HAL_KEY_CODE_FN4:
//			app_voice_report(APP_STATUS_INDICATION_GSOUND_MIC_OPEN,0);	
			if(keystatus[1] ==true){///1:pressed///1:in_ear
				keyinfo[1].keyevent =true;
				 osTimerStop(steady_timer);
				 osTimerStart(steady_timer, TOUCHKEY_STEADY_THRESHOLD);	//启动500mS steady_timer;
			}
			else{						///0:out_ear
				 keyinfo[1].keyevent =false;
				 osTimerStop(steady_timer);
				 osTimerStart(steady_timer, TOUCHKEY_STEADY_THRESHOLD);	//启动500mS steady_timer;
			}
			break;
		}
	/*if(status->code==HAL_KEY_CODE_FN4&&status->event=HAL_KEY_EVENT_CLICK)
		bt_key_handle_func_click();*/
}

void app_longpress_handler(void const *param)
{
	///send key_code key_event to app layer(by key_event_process())
	if(keyinfo[0].keyevent ==1){	//单击1次+长按
		//TRACE(2,"keycode=0x%X  keyevent =0x%X  ========click+longpress========\n",keyinfo[0].keycode,keyinfo[0].keyevent+16);
			key_detected_callback(HAL_KEY_CODE_FN3,HAL_KEY_EVENT_UP_AFTER_LONGPRESS);
	}
	else{
		//TRACE(2,"keycode=0x%X  keyevent =0x%X  =============longpress===========\n",keyinfo[0].keycode,16);	//仅长按
		key_detected_callback(HAL_KEY_CODE_FN3,HAL_KEY_EVENT_LONGPRESS);
	}	
	keyinfo[0].keyevent=0;
	keystatus[0]=0;

}

void app_touchkey_handler(void const *param)
{
	if(keyinfo[0].keyevent){
		if(keyinfo[0].keyevent==1){
			//TRACE(2,"keycode=0x%X  keyevent =0x%X    =========single click==========\n ",keyinfo[0].keycode,keyinfo[0].keyevent);
			key_detected_callback(keyinfo[0].keycode,HAL_KEY_EVENT_CLICK);
		}
		else if(keyinfo[0].keyevent==2){
			//TRACE(2,"keycode=0x%X  keyevent =0x%X   =========double click==========\n ",keyinfo[0].keycode,keyinfo[0].keyevent);
			key_detected_callback(keyinfo[0].keycode,HAL_KEY_EVENT_DOUBLECLICK);
		}
		else{
			//TRACE(2,"keycode=0x%X  keyevent =0x%X  ========== triple click==========\n ",keyinfo[0].keycode,keyinfo[0].keyevent);
			key_detected_callback(keyinfo[0].keycode,HAL_KEY_EVENT_TRIPLECLICK);
		}
	}
	keyinfo[0].keyevent=0;
	keystatus[0]=0;
}

void app_inear_handler(void const *param)
{
	if(keyinfo[1].keyevent){
		//TRACE(2,"keycode=0x%X  keystatus =0x%X  =========== in ear===========\n",keyinfo[1].keycode,keyinfo[1].keyevent);
		key_detected_callback(keyinfo[1].keycode,HAL_KEY_EVENT_DOWN);
	}
	else{
		//TRACE(2,"keycode=0x%X  keystatus =0x%X  ==========out ear==========\n",keyinfo[1].keycode,keyinfo[1].keyevent);
		key_detected_callback(keyinfo[1].keycode,HAL_KEY_EVENT_UP);
	}
}

void app_touchkey_open(void)
{
	keyinfo[0].keycode = HAL_KEY_CODE_FN3;
	keyinfo[1].keycode = HAL_KEY_CODE_FN4;
	click_interval_timer 	=osTimerCreate(osTimer(CLICKINTV),osTimerOnce,NULL);
	steady_timer 		=osTimerCreate(osTimer(STEADY),osTimerOnce,NULL);
	longpress_timer 	=osTimerCreate(osTimer(LPRESS),osTimerOnce,NULL);

	hal_touchkey_open();
//    app_set_threadhandle(APP_MODUAL_KEY, app_key_handle_process);
}

void app_touchkey_close(void){
	hal_touchkey_close();
	/*==================关闭timer====================*/
    if (click_interval_timer) {
        osTimerStop(click_interval_timer);
        osTimerDelete(click_interval_timer);
        click_interval_timer = NULL;
    }
    if (longpress_timer) {
        osTimerStop(longpress_timer);
        osTimerDelete(longpress_timer);
        longpress_timer = NULL;
    }
	if (steady_timer) {
	    osTimerStop(steady_timer);
	    osTimerDelete(steady_timer);
	    steady_timer = NULL;
    }
    /*key_detected_callback = NULL;
    return 0;*/
}

触摸按键的销毁:

 

 

 

按键的大致类型介绍和讲解就到这里了。欢迎大家继续关注 ,下一个章节介绍 提示音相关问题和解决办法。

QQ:1902026113

 

  • 10
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值