现在市场上所使用的vibrator一共有两种,他们是LRA与EMB。
这次主要为大家介绍的是LRA
LRA是在单个轴上产生振荡力的振动电机,与直流偏心旋转质量电机(EMB)不同,LRA依靠交流电压来驱动压靠与弹簧连接的移动质量块的音圈。当音圈在弹簧的共振频率下被驱动时,整个传动器以可感知的力振动。虽可通过改变交流输入来调节LRA的频率和振幅,但传动器必须在其谐振频率下被驱动以产生大电流有意义的力
在产生振动,压靠移动质量块时,音圈在装置内部保持静止。通过相对于弹簧向上和向下驱动磁体,LRA作为整体发生移位,产生振动。基础机制类似于扬声器产生声音。在扬声器中,通过将交流电频率和振幅转变为振动频率和振幅使空气通过锥体并且以不同的频率发生位移。
从高通的spec上可以看到可以LRA是被方波或者正弦波所驱动的。
一般来讲在手机中,vibrator是被PMI芯片所驱动的,通过HAP_OUT_P/HAP_OUT_N驱动。
高通指出了它就是User Interface中的haptic.
流程图如下:
简单说明:target_init函数调用vib_timed_turn_on进入函数pm_vib_turn_on,配置相关寄存器,设置定时器,启动vib_timer_func,定时器结束回调vib_turn_off
#if PON_VIB_SUPPORT
#include
#define VIBRATE_TIME 250
#endif
在init.c中会引用vibrator.h
#if PON_VIB_SUPPORT
vib_timed_turn_on(VIBRATE_TIME);
#endif
target_init(void)中会调用vibrator.h的vib_timed_turn_on函数
在lk/dev/vib/vibrator.c中
/*
* Function to turn on vibrator.
* vibrate_time - the time of phone vibrate.
*/
void vib_timed_turn_on(const uint32_t vibrate_time)
{
if(!vib_timeout){
dprintf(CRITICAL,"vibrator already turn on\n");
return;
}
vib_turn_on();
vib_timeout = 0;
#if !USE_VIB_THREAD
timer_initialize(&vib_timer);
timer_set_oneshot(&vib_timer, vibrate_time, vib_timer_func, NULL);
#else
vib_time = (vibrate_time/CHECK_VIB_TIMER_FREQUENCY)+1;
thread_resume(thread_create("vibrator_thread", &vibrator_thread,
NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
#endif
}
当vibrator的传进来的时间不为0时会启动vibrator.c中vib_turn_on()函数
/* Function to turn on vibrator */
void vib_turn_on()
{
pm_vib_turn_on();
}
调用lk/dev/vib/qpnp_haptic.c
#define HAPTIC_BASE (PMI_SECOND_SLAVE_ADDR_BASE+ 0xC000)
#define QPNP_HAP_EN_CTL_REG (HAPTIC_BASE + 0x46)
#define QPNP_HAP_EN_CTL2_REG (HAPTIC_BASE + 0x48)
#define QPNP_HAP_ACT_TYPE_REG (HAPTIC_BASE + 0x4C)
#define QPNP_HAP_WAV_SHAPE_REG (HAPTIC_BASE + 0x4D)
#define QPNP_HAP_PLAY_MODE_REG (HAPTIC_BASE + 0x4E)
#define QPNP_HAP_LRA_AUTO_RES_REG (HAPTIC_BASE + 0x4F)
#define QPNP_HAP_VMAX_REG (HAPTIC_BASE + 0x51)
#define QPNP_HAP_ILIM_REG (HAPTIC_BASE + 0x52)
#define QPNP_HAP_SC_DEB_REG (HAPTIC_BASE + 0x53)
#define QPNP_HAP_RATE_CFG1_REG (HAPTIC_BASE + 0x54)
#define QPNP_HAP_RATE_CFG2_REG (HAPTIC_BASE + 0x55)
#define QPNP_HAP_INT_PWM_REG (HAPTIC_BASE + 0x56)
#define QPNP_HAP_PWM_CAP_REG (HAPTIC_BASE + 0x58)
#define QPNP_HAP_BRAKE_REG (HAPTIC_BASE + 0x5C)
#define QPNP_HAP_PLAY_REG (HAPTIC_BASE + 0x70)
#define QPNP_HAP_ACT_TYPE_MASK 0x01
#define QPNP_HAP_PLAY_MODE_MASK 0x3F
#define QPNP_HAP_DIRECT 0x0
#define QPNP_HAP_VMAX_MASK 0x3F
#define QPNP_HAP_VMAX 0x14
#define QPNP_HAP_ILIM_MASK 0x01
#define QPNP_HAP_ILIM 0x01
#define QPNP_HAP_SC_DEB_MASK 0x07
#define QPNP_HAP_SC_DEB_8CLK 0x01
#define QPNP_HAP_INT_PWM_MASK 0x03
#define QPNP_HAP_INT_PWM_505KHZ 0x01
#define QPNP_HAP_WAV_SHAPE_MASK 0x01
#define QPNP_HAP_WAV_SHAPE_SQUARE 0x01
#define QPNP_HAP_PWM_CAP_MASK 0x03
#define QPNP_HAP_PWM_CAP_13PF 0x01
#define QPNP_HAP_RATE_CFG1_MASK 0xFF
#define QPNP_HAP_RATE_CFG2_MASK 0x0F
#define QPNP_HAP_EN_BRAKE_EN_MASK 0x01
#define QPNP_HAP_EN_BRAKING_EN 0x01
#define QPNP_HAP_BRAKE_VMAX_MASK 0xFF
#define QPNP_HAP_BRAKE_VMAX 0xF
#define QPNP_HAP_ERM 0x1
#define QPNP_HAP_LRA 0x0
#define QPNP_HAP_PLAY_MASK 0x80
#define QPNP_HAP_PLAY_EN 0x80
#define QPNP_HAP_MASK 0x80
#define QPNP_HAP_EN 0x80
#define QPNP_HAP_PLAY_DIS 0x00
#define QPNP_HAP_DIS 0x00
#define QPNP_HAP_BRAKE_MASK 0xFE
#define QPNP_HAP_LRA_AUTO_DISABLE 0x00
#define QPNP_HAP_LRA_AUTO_MASK 0x70
/* Turn on vibrator */
void pm_vib_turn_on(void)
{
struct qpnp_hap vib_config = {0};
get_vibration_type(&vib_config);
/* Configure the ACTUATOR TYPE register as ERM*/
pmic_spmi_reg_mask_write(QPNP_HAP_ACT_TYPE_REG,
QPNP_HAP_ACT_TYPE_MASK,
VIB_ERM_TYPE == vib_config.vib_type ? QPNP_HAP_ERM
: QPNP_HAP_LRA);
/* Disable auto resonance for ERM */
pmic_spmi_reg_mask_write(QPNP_HAP_LRA_AUTO_RES_REG,
QPNP_HAP_LRA_AUTO_MASK,
QPNP_HAP_LRA_AUTO_DISABLE);
/* Configure the PLAY MODE register as direct*/
pmic_spmi_reg_mask_write(QPNP_HAP_PLAY_MODE_REG,
QPNP_HAP_PLAY_MODE_MASK,
QPNP_HAP_DIRECT);
/* Configure the VMAX register */
pmic_spmi_reg_mask_write(QPNP_HAP_VMAX_REG,
QPNP_HAP_VMAX_MASK, QPNP_HAP_VMAX);
/* Sets current limit to 800mA*/
pmic_spmi_reg_mask_write(QPNP_HAP_ILIM_REG,
QPNP_HAP_ILIM_MASK, QPNP_HAP_ILIM);
/* Configure the short circuit debounce register as DEB_8CLK*/
pmic_spmi_reg_mask_write(QPNP_HAP_SC_DEB_REG,
QPNP_HAP_SC_DEB_MASK, QPNP_HAP_SC_DEB_8CLK);
/* Configure the INTERNAL_PWM register as 505KHZ and 13PF*/
pmic_spmi_reg_mask_write(QPNP_HAP_INT_PWM_REG,
QPNP_HAP_INT_PWM_MASK, QPNP_HAP_INT_PWM_505KHZ);
pmic_spmi_reg_mask_write(QPNP_HAP_PWM_CAP_REG,
QPNP_HAP_PWM_CAP_MASK, QPNP_HAP_PWM_CAP_13PF);
/* Configure the WAVE SHAPE register as SQUARE*/
pmic_spmi_reg_mask_write(QPNP_HAP_WAV_SHAPE_REG,
QPNP_HAP_WAV_SHAPE_MASK, QPNP_HAP_WAV_SHAPE_SQUARE);
/* Configure RATE_CFG1 and RATE_CFG2 registers for haptic rate. */
pmic_spmi_reg_mask_write(QPNP_HAP_RATE_CFG1_REG,
QPNP_HAP_RATE_CFG1_MASK, vib_config.hap_rate_cfg1);
pmic_spmi_reg_mask_write(QPNP_HAP_RATE_CFG2_REG,
QPNP_HAP_RATE_CFG2_MASK, vib_config.hap_rate_cfg2);
/* Configure BRAKE register, PATTERN1 & PATTERN2 as VMAX. */
pmic_spmi_reg_mask_write(QPNP_HAP_EN_CTL2_REG,
QPNP_HAP_EN_BRAKE_EN_MASK, QPNP_HAP_EN_BRAKING_EN);
pmic_spmi_reg_mask_write(QPNP_HAP_BRAKE_REG,
QPNP_HAP_BRAKE_VMAX_MASK, QPNP_HAP_BRAKE_VMAX);
/* Enable control register */
pmic_spmi_reg_mask_write(QPNP_HAP_EN_CTL_REG,
QPNP_HAP_PLAY_MASK, QPNP_HAP_PLAY_EN);
/* Enable play register */
pmic_spmi_reg_mask_write(QPNP_HAP_PLAY_REG, QPNP_HAP_MASK, QPNP_HAP_EN);
}
确认vib的类型调用lk/target/init.c 中的 void get_vibration_type(struct qpnp_hap *config)
#if PON_VIB_SUPPORT
void get_vibration_type(struct qpnp_hap *config)
{
uint32_t hw_id = board_hardware_id();
uint32_t platform = board_platform_id();
config->vib_type = VIB_ERM_TYPE;
config->hap_rate_cfg1 = QPNP_HAP_RATE_CFG1_1c;
config->hap_rate_cfg2 = QPNP_HAP_RATE_CFG2_04;
switch(hw_id){
case HW_PLATFORM_MTP:
switch(platform){
case MSM8952:
config->vib_type = VIB_ERM_TYPE;
break;
case MSM8976:
case MSM8956:
case APQ8056:
config->vib_type = VIB_LRA_TYPE;
break;
case MSM8937:
case APQ8037:
case MSM8917:
case MSM8217:
case MSM8617:
case APQ8017:
case MSM8953:
case APQ8053:
config->vib_type = VIB_LRA_TYPE;
config->hap_rate_cfg1 = QPNP_HAP_RATE_CFG1_41;
config->hap_rate_cfg2 = QPNP_HAP_RATE_CFG2_03;
break;
default:
dprintf(CRITICAL,"Unsupported platform id\n");
break;
}
break;
case HW_PLATFORM_QRD:
config->vib_type = VIB_ERM_TYPE;
break;
default:
dprintf(CRITICAL,"Unsupported hardware id\n");
break;
}
}
#endif
然后可以看到设置一个定时器,告诉定时器时间到时的回调函数
vibrate_time之后才回调vib_timer_func
/* Function to turn off vibrator when the vib_timer is expired. */
static enum handler_return vib_timer_func(struct timer *v_timer, time_t t, void *arg)
{
timer_cancel(&vib_timer);
if(!vib_timeout){
vib_turn_off();
vib_timeout = 1;
}
return INT_RESCHEDULE;
}
然后调用 vib_turn_off
/* Turn off vibrator */
void pm_vib_turn_off(void)
{
/* Disable control register */
pmic_spmi_reg_mask_write(QPNP_HAP_EN_CTL_REG,
QPNP_HAP_PLAY_MASK, QPNP_HAP_PLAY_DIS);
/* Disable play register */
pmic_spmi_reg_mask_write(QPNP_HAP_PLAY_REG, QPNP_HAP_MASK, QPNP_HAP_DIS);
}
至此整个流程就已经通了
至于具体的配置寄存器,在此也列出了一张表,当然各个人可以对此表进行修改各类数据。