OV-Watch 项目为 MPU6050 传感器实施了复杂的电源管理,以优化电池寿命,同时保持手腕检测和计步功能。以下是对睡眠和唤醒机制的详细分析:
MPU6050 有两个主要功能来控制其电源状态:
void MPU_Sleep()
{
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0x48);//sleep=1,cycle=0,temp_dis=1,internal 8MHz
}
void MPU_Wakeup()
{
//low power modes
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0x28);//sleep=0,cycle=1,temp_dis=1,internal 8MHz
}
这些函数写入 MPU6050 的电源管理寄存器(MPU_PWR_MGMT1_REG,地址 0x6B)以配置:
【STM32】MPU6050初始化常用寄存器说明及示例代码
MPU6050配置为在初始化期间实现节能运行:
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X28); //SET the internal 8MHz,sleep=0,cycle=1,TEMP_DIS=1//low power modes
MPU_Write_Byte(MPU_PWR_MGMT2_REG,0X87); //enable accelerometer,disanable gyroscope,set the wake up frequence=20Hz
MPU_Set_Rate(50); //采样率50Hz
此初始配置:
OV-Watch 通过数字运动处理器 (DMP) 功能进一步优化了功耗:
u8 mpu_dmp_init(void)
{
u8 res=0;
MPU_Bus_Init();
if(mpu_init()==0) //初始化MPU6050
{
res = mpu_lp_accel_mode(20);
if(res)return -1;
res=mpu_configure_fifo(INV_XYZ_ACCEL);//配置FIFO
if(res)return 2;
res=mpu_set_sample_rate(DEFAULT_MPU_HZ); //设置采样率
if(res)return 3;
res=dmp_load_motion_driver_firmware(); //加载dmp固件
if(res)return 4;
res=dmp_enable_feature(DMP_FEATURE_TAP| DMP_FEATURE_SEND_RAW_ACCEL| DMP_FEATURE_PEDOMETER);//使能dmp功能
if(res)return 6;
res=dmp_set_fifo_rate(DEFAULT_MPU_HZ); //设置DMP输出速率(最大不超过200Hz)
if(res)return 7;
res=mpu_set_dmp_state(1); //使能DMP
if(res)return 9;
}else return 10;
return 0;
}
此初始化:
mpu_lp_accel_mode 功能对于电源效率至关重要:
int mpu_lp_accel_mode(unsigned char rate)
{
unsigned char tmp[2];
if (rate > 40)
return -1;
if (!rate) {
mpu_set_int_latched(0);
tmp[0] = 0;
tmp[1] = BIT_STBY_XYZG;
if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 2, tmp))
return -1;
st.chip_cfg.lp_accel_mode = 0;
return 0;
}
/* For LP accel, we automatically configure the hardware to produce latched
* interrupts. In LP accel mode, the hardware cycles into sleep mode before
* it gets a chance to deassert the interrupt pin; therefore, we shift this
* responsibility over to the MCU.
*/
mpu_set_int_latched(1);
#if defined MPU6050
//tmp[0] = BIT_LPA_CYCLE;
tmp[0] = 0x28;
此功能:
sleep=1
:将设备置于睡眠模式cycle=0
:禁用循环模式(周期性唤醒)temp_dis=1
:禁用温度传感器以节省电量使用内部 8MHz 振荡器进行定时OV-Watch 使用MPU6050进行手腕检测,以确定何时唤醒设备:
//MPU Check
if(HWInterface.IMU.wrist_is_enabled)
{
uint8_t hor;
hor = MPU_isHorizontal();
if(hor && HWInterface.IMU.wrist_state == WRIST_DOWN)
{
HWInterface.IMU.wrist_state = WRIST_UP;
Wrist_Flag = 1;
//resume, go on
}
else if(!hor && HWInterface.IMU.wrist_state == WRIST_UP)
{
HWInterface.IMU.wrist_state = WRIST_DOWN;
IdleTimerCount = 0;
goto sleep;
}
}
此机制:
水平位置检测使用加速度计数据来计算滚动和俯仰:
uint8_t MPU_isHorizontal(void)
{
float roll,pitch;
MPU_Get_Angles(&roll,&pitch);
if(roll<=0.50 && roll>=-0.50 && pitch<=0.50 && pitch>=-0.50)
{return 1;}
return 0;
}
OV-Watch 实现了一个硬件抽象层来管理手腕检测:
void HW_MPU_Wrist_Enable(void)
{
#if HW_USE_IMU
HWInterface.IMU.wrist_is_enabled = 1;
#endif
}
void HW_MPU_Wrist_Disable(void)
{
#if HW_USE_IMU
HWInterface.IMU.wrist_is_enabled = 0;
#endif
}
这些函数控制手腕检测功能是否处于活动状态,这直接影响何时调用 MPU6050 睡眠/唤醒函数。
OV-Watch 中的 MPU6050 实现通过以下方式最大限度地降低功耗:
手腕检测功能允许手表保持睡眠模式,直到用户抬起手腕,在保持可用性的同时显着延长电池寿命。
数字运动处理器 (DMP) 可高效处理复杂的运动检测任务,使主处理器保持低功耗状态。
OV-Watch 智能手表使用 MPU6050 IMU 传感器来保持计步功能,即使设备处于睡眠模式也是如此。这种方法是手表能效战略的核心。
OV-Watch 有三种不同的电源模式:
在初始化期间,MPU6050 专门配置为在低功耗模式下运行,同时保持步数功能:
u8 mpu_dmp_init(void)
{
u8 res=0;
MPU_Bus_Init();
if(mpu_init()==0)//初始化MPU6050
{
res = mpu_lp_accel_mode(20);
if(res)return -1;
//...
res=dmp_enable_feature(DMP_FEATURE_TAP| DMP_FEATURE_SEND_RAW_ACCEL| DMP_FEATURE_PEDOMETER);//enable dmp features
if(res)return 6;
//...
}
return 0;
}
关键部分是调用,它将 MPU6050 配置为在低功耗模式下以 20Hz 采样率运行。此功能专门将加速度计设置为继续运行,同时最大限度地降低功耗。mpu_lp_accel_mode(20)
当设备进入睡眠模式时,MCU 进入 STOP 模式,同时保持 MPU6050 处于活动状态:
void StopEnterTask(void *argument)
{
//...
if(osMessageQueueGet(Stop_MessageQueue,&Stopstr,NULL,0)==osOK)
{
//...
//enter stop mode
HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON,PWR_STOPENTRY_WFI);
//here is the sleep period
//...
}
//...
}
值得注意的是,虽然 LCD 和 UART 等其他外设在进入睡眠模式之前被取消初始化,但 MPU6050 有意保持运行状态,允许它在 MCU 处于 STOP 模式时继续计算步数。
步数通过 EEPROM 存储在整个休眠周期中保留:
void DataSaveTask(void *argument)
{
while(1)
{
//...
RTC_DateTypeDef nowdate;
HAL_RTC_GetDate(&hrtc,&nowdate,RTC_FORMAT_BIN);
SettingGet(dat,0x20,3);
if(dat[0] != nowdate.Date)
{
//...
}
else
{
uint16_t temp = HWInterface.IMU.GetSteps();
dat[0] = nowdate.Date;
dat[2] = temp & 0xff;
dat[1] = temp>>8 & 0xff;
SettingSave(dat,0x20,3);
}
//...
}
}
当系统初始化时,它会从 EEPROM 恢复上一步计数:
// EEPROM
EEPROM_Init();
if(!EEPROM_Check())
{
//...
SettingGet(recbuf,0x20,3);
if(recbuf[0] == nowdate.Date)
{
uint16_t steps=0;
steps = recbuf[1]&0x00ff;
steps = steps<<8 | recbuf[2];
if(!HWInterface.IMU.ConnectionError)
dmp_set_pedometer_step_count((unsigned long)steps);
}
}
MPU6050 使用其 DMP(数字运动处理器)在内部计算步数。OV-Watch 仅在需要时检索此计数:
uint16_t HW_MPU_Get_Steps(void)
{
#if HW_USE_IMU
unsigned long STEPS = 0;
if(!HWInterface.IMU.ConnectionError)
dmp_get_pedometer_step_count(&STEPS);
return (uint16_t)STEPS;
#endif
return 0;
}
该项目需要对 MPU6050 DMP 库进行自定义修改,以实现低功耗。如 README 中所述:“MPU6050 不能直接使用 DMP 库。初始化后,功耗很高,需要进行一些修改以降低功耗。
这种实现方式允许 OV-Watch 在睡眠模式下保持连续计步,与正常工作时的 70-80mA 相比,实现了约 800μA 的极低功耗。
查看 OV-Watch 项目中的相关代码:
steps = recbuf[1]&0x00ff; steps = steps<<8 | recbuf[2];
此代码从数组中存储的两个 8 位字节重建一个 16 位整数 (steps)。让我解释一下为什么它是这样写的:
此代码出现在硬件初始化任务中,系统正在从 EEPROM 内存中检索之前保存的步数。步数存储为 16 位值(可表示 0-65535 步),但作为两个独立的 8 位字节保存在 EEPROM 中。
steps = recbuf[1]&0x00ff;
&0x00ff
steps = steps<<8 | recbuf[2];
该器件中的 EEPROM 存储使用简单的字节可寻址格式。保存 16 位步数时,它被分成两个字节:
dat[0] = nowdate.Date;
dat[2] = temp & 0xff; // Low byte of step count
dat[1] = temp>>8 & 0xff; // High byte of step count
SettingSave(dat,0x20,3);
检索数据时,该过程将相反,以重建原始的 16 位值。
此实现使用 big-endian 格式进行存储(最高有效字节在前):
这是嵌入式系统中的常见约定,尤其是在 EEPROM 或其他字节可寻址存储中存储多字节值时。
此代码是设备初始化期间步数恢复过程的一部分。如果当前日期与存储的日期匹配,则系统会检索之前保存的步数并在MPU6050的计步器中进行设置:
if(recbuf[0] == nowdate.Date)
{
uint16_t steps=0;
steps = recbuf[1]&0x00ff;
steps = steps<<8 | recbuf[2];
if(!HWInterface.IMU.ConnectionError)
dmp_set_pedometer_step_count((unsigned long)steps);
}
这可确保步数在设备重启或睡眠周期中保持不变,从而保持准确的每日步数。
代码可以更简洁地编写为:
steps = (recbuf[1] << 8) | recbuf[2];
但是,对于某些开发人员,尤其是那些不太熟悉按位运算的开发人员,两步方法可能更具可读性。
您询问的是为什么MPU6050(一个运动传感器)会有点击检测功能,而这看起来应该是触摸屏的功能。这是一个很好的问题,让我来解释一下。
在OV-Watch项目中,MPU6050确实被配置为启用了点击检测(tap detection)功能。这可以在初始化代码中看到: inv_mpu.c:2974
这里启用了三个DMP功能:
DMP_FEATURE_TAP
- 点击检测DMP_FEATURE_SEND_RAW_ACCEL
- 原始加速度数据DMP_FEATURE_PEDOMETER
- 计步器功能虽然OV-Watch确实有触摸屏(在功能列表中可以看到),但MPU6050的点击检测功能与触摸屏的触摸检测是完全不同的:
触摸屏检测:检测用户在屏幕表面的触摸动作,是基于电容或电阻变化的。
MPU6050点击检测:检测的是整个设备受到的物理冲击或敲击,是基于加速度传感器检测到的突然加速度变化。
MPU6050的点击检测是通过其内置的DMP(数字运动处理器)实现的,它能够识别出特定的加速度模式,这些模式对应于设备被敲击或轻拍的动作。在代码中,这个功能被设置为检测X、Y、Z三个轴向的敲击:
在智能手表中,点击检测功能可以用于:
唤醒设备:当手表处于睡眠模式时,轻敲手表可以唤醒它,而不需要按按钮。
手势控制:可以通过不同方向的敲击来实现简单的手势控制,例如双击表面来确认操作。
节省电量:与持续监听触摸屏输入相比,使用加速度传感器检测敲击可能更省电。
在OV-Watch的低功耗设计中,MPU6050在手表睡眠模式下仍然保持活跃,以便继续计步和检测手腕动作:
虽然最终OV-Watch项目选择使用RTC定时中断而不是MPU6050的运动检测来唤醒设备(因为需要较大的动作才能触发中断),但点击检测功能仍然被保留在代码中,可能用于其他交互功能。
OV-Watch同时拥有触摸屏和MPU6050的点击检测功能,它们服务于不同的用途。
虽然在提供的代码片段中没有明确看到MPU6050的点击检测功能被如何使用,但该功能在初始化时被启用,表明它可能在某些交互场景中被使用。
值得注意的是,MPU6050的DMP功能被特别优化以降低功耗,这对于电池供电的智能手表来说是非常重要的。