智能魔法棒(手势控制器)———嵌入式篇

Posted 三明治开发社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智能魔法棒(手势控制器)———嵌入式篇相关的知识,希望对你有一定的参考价值。

  在《 智能魔法棒———硬件篇 》中已经为大家介绍了魔法棒的硬件方案和结构设计,接下来介绍一下嵌入式软件设计方案。
  注:本文涉及了有关 MPU6050 的使用方法、姿态解算方法、卡尔曼滤波建模及公式推导过程的介绍,有兴趣的同学可以通过目录快速定位到相关内容进行参考。


1 环境搭建

1.1 产品创建

  在进行魔法棒的固件开发之前,我们需要先在 涂鸦IoT平台 上创建一个智能产品,还不熟悉产品创建的同学可以通过涂鸦开发者平台的文档中心进行了解,详情点击【快速入门】。

  魔法棒产品可以选择【找不到品类】进行自定义创建。本 Demo 选用的是涂鸦 Bluetooth LE 模组 TYBN1,其芯片平台是 nRF52832 。因此,选择通讯协议为【蓝牙】,云端接入方式为【TuyaOS】,云端接入硬件为【Nordic nRF52832】。当然也可以选择其他模组,可以参考 涂鸦云模组规格书 进行选型。比如选择了 BP3L 模组,那么在【硬件开发】页面选择“云端接入硬件”时选择【BP3L 模组】即可。

  为实现云端接收魔法棒识别结果的目的,需在【功能定义】页面自定义一个DP点,参数如下:

DP ID功能点名称标识符数据传输类型数据类型功能点属性
101手势gesture只上报 (ro)枚举型 (Enum)枚举值:none,ges1,ges2 …

1.2 SDK 获取

  • 原厂 SDK

  涂鸦 Bluetooth LE 云模组 TYBN1 使用的芯片平台是 Nordic 的 nRF52832,我们可以在 Nordic 官网找到 nRF5 系列的 SDK (点击进入下载页面:nRF5 SDK downloads),选择 15.3.0 版本,SoftDevices 选择版本介绍中涉及 nRF52832 的即可,最后点击 Download files (.zip)

  • 涂鸦 SDK

  涂鸦提供了适用于 nRF52832 平台的 SDK,可以在创建好的魔法棒产品的 硬件开发 页面左下角找到最新版的 SDK 。下载完成后,需将涂鸦 SDK 文件 tuya-ble-sdk-demo-project-nrf52832 拷贝到原厂 SDK 的nRF5_SDK_15.3.0_59ac345\\examples\\ble_peripheral目录下。( 开发指南:蓝牙 SDK 开发 )



1.3 IDE 准备

  涂鸦 SDK 中的工程是使用 MDK-ARM Keil µVision 创建的,还未安装的同学可以前往官网下载。安装好 IDE 后,双击涂鸦 SDK 中的工程文件ble_app_uart_pca10040_s132.uvprojx,打开时会自动安装软件包。如果安装失败,可以在 Pack Installer 中找到 nRF52832 芯片对应的软件包进行安装,如下图所示:



1.4 烧录授权

  • 模组购买

  如果同学们手边没有涂鸦云模组的话,可以在【硬件开发】页面先【新增自定义固件】,然后点击右侧的【立即购买】 进入模组购买流程。涂鸦提供多种烧录授权方式,可根据不同的开发阶段、芯片类型和生产方式进行选择,详情点击 固件烧录授权

  • 激活码领取

  单个模组也可以通过购买单个激活码进行授权。新用户可以在【硬件开发】页面免费领取 2 个激活码:


  交付形式选择【授权码清单】后提交订单,就可以获得2组激活码,包括 uuidauthkeymac 地址

  • 授权信息修改

  将产品 ID (PID) 和激活码 (选取 1 组) 填入 tuya_ble_sdk_demo.h 文件的以下位置:

#define TY_DEVICE_NAME        "demo"
#define TY_DEVICE_PID         "xxxxxxxx" /* PID */
#define TY_DEVICE_MAC         "xxxxxxxx" /* mac */
#define TY_DEVICE_DID         "xxxxxxxx" /* uuid */
#define TY_DEVICE_AUTH_KEY    "xxxxxxxx" /* authkey */

  然后还需要在 tuya_ble_sdk_demo.c 文件中将初始化参数 use_ext_license_keydevice_id_len 的值分别修改为 1 和 16,以使激活码生效:

static tuya_ble_device_param_t tuya_ble_device_param = 
    .use_ext_license_key = 1,	// 1-info in tuya_ble_sdk_demo.h, 0-auth info
    .device_id_len       = 16,	// DEVICE_ID_LEN,
    .p_type              = TUYA_BLE_PRODUCT_ID_TYPE_PID,
    .product_id_len      = 8,
    .adv_local_name_len  = 4,
    .firmware_version    = TY_DEVICE_FVER_NUM,
    .hardware_version    = TY_DEVICE_HVER_NUM,
;
  • 硬件连接

  编译通过后,将 J-Link 烧录器连接到开发板,连线方式如下:

模组引脚J-Link 引脚
3.3VVCC
GNDGND
SWDIOSWDIO
SWCSWCLK
  • 固件下载

  在 Demo 固件下载前,必须先下载协议栈固件(同一块模组只需操作 1 次)。在 J-Link 官网下载 J-Link 软件开发包。安装完成后,打开 J-Flash 软件,点击 File - New Project 创建工程,芯片选择 nRF52832_xxAA 后点击 OK,如下图所示:

  点击 File - Open data file 打开涂鸦 SDK \\pca10040\\s132\\arm5_no_packs\\hex\\material 目录下的 s132_nrf52_6.1.1_softdevice.hex 文件,如下图所示:

  点击 Target - Connect 连接芯片,成功连接后点击 Target - Production Programming 开始下载,下载完成后点击 Target - Disonnect 断开连接。Demo 固件可以直接通过 Keil 进行下载。


1.5 日志查看

  开发过程中可以通过查看日志来进行功能调试,日志功能默认关闭,开启需对代码作如下修改:

#define TUYA_APP_LOG_ENABLE	1	/* 位于custom_tuya_ble_config.h文件中,0-关闭,1-开启 */
#define TY_LOG_ENABLE		1	/* 位于board.h文件,0-关闭,1-开启 */

  将修改后的固件编译并烧录至开发板,然后打开 J-link RTT Viewer 软件,会自动弹出以下对话框:

  按上图内容完成设置后,点击 OK。成功连接就可以看到设备日志了,软件日志窗口也会出现如下提示:



2 固件设计

2.1 功能定义

  魔法棒要实现的功能定义如下:

功能说明
手势动作识别可识别出下列手势动作:
- 手势动作 1:向上甩动;
- 手势动作 2:向下甩动;
- 手势动作 3:向左甩动;
- 手势动作 4:向右甩动;
- 手势动作 5:顺时针翻转;
- 手势动作 6:逆时针翻转;
识别功能开关可通过按键控制识别功能打开或关闭:
- 按键按下时,手势识别功能打开;
- 按键释放时,手势识别功能关闭。
手势数据上报可将手势数据上报至云端,以实现手势动作对其他设备的控制。
配网状态重置可通过配网键实现设备端解绑:
- 长按配网键3秒,设备主动解绑。
配网等待时限可在等待配网超过1分钟后结束等待,禁止用户绑定设别:
- 上电时设备状态为未绑定或通过配网键重置时,设备进入配网等待;
- 1分钟后仍未被绑定,则结束等待。
配网状态指示可通过指示灯提示设备状态:
- 等待配网时,指示灯快闪;
- 配网结束时,指示灯关闭。
手电筒功能可通过配网键短按切换指示灯开/关。

2.2 软件方案

1)模块划分

  魔法棒的核心功能是识别不同的手势动作,从而达到通过涂鸦云平台控制其他设备的目的。这次硬件方案采取了 MPU6050 六轴惯性传感器来采集手势数据,包括 3 轴加速度3 轴角速度,由此可计算出设备的姿态角,也可以通过内置的数字运动处理器 (DMP) 获取四元数来计算姿态。结合这些数据对手势特征进行提取,就可以实现特定手势识别。根据魔法棒的硬件方案和功能定义,可以将 Demo 程序划分为以下 5 个模块:

模块处理内容
数据采集模块MPU6050 传感器的数据采集处理
姿态解算模块根据角速度、加速度计算姿态角,并用卡尔曼滤波法进行数据融合处理
手势识别模块应用采集到的数据和姿态解算结果,实现各个手势动作识别
联网处理模块设备配网与解绑、配网状态指示、手势数据上报等
其他功能模块手电筒功能、低功耗处理等

  各模块的具体方案将在 4.3 功能开发 中进行介绍。


2)软件框图

  结合涂鸦 Bluetooth LE SDK 的软件架构和应用功能设计,魔法棒的软件框图如下:


3)软件流程

  魔法棒的基本工作流程如下:


4)文件结构

  应用入口:tuya_ble_sdk_demo.c -> tuya_gesture_controller.c

├── include
|    ├── common
|    |    └── tuya_common.h                /* 通用类型和宏定义 */
|    ├── driver
|    |    ├── tuya_key.h                   /* 按键驱动 */
|    |    ├── tuya_led.h                   /* LED驱动 */
|    |    └── tuya_mpu6050.h               /* MPU6050驱动 */
|    ├── platform
|    |    └── tuya_gpio.h                  /* 平台关联GPIO驱动 */
|    ├── sdk
|    |    ├── tuya_ble_bulk_data_demo.h    /* 大数据通道例程 */
|    |    ├── tuya_ble_product_test_demo.h /* 整机产测例程 */
|    |    └── tuya_ble_sdk_test.h          /* 实现tuya_ble_sdk测试的串口指令 */
|    ├── tuya_ble_sdk_demo.h               /* 实现tuya_ble_sdk的初始化,应用程序入口 */
|    ├── tuya_imu_daq.h                    /* 传感数据采集 */
|    ├── tuya_gesture_controller.h         /* 手势控制器管理中心 */
|    ├── tuya_ges_act_rec.h                /* 手势动作识别 */
|    ├── tuya_net_proc.h                   /* 设备联网处理 */
|    └── tuya_svc_angle_calc.h             /* 姿态解算服务 */
└── src
     ├── driver
     |    ├── tuya_key.c                   /* 按键驱动 */
     |    ├── tuya_led.c                   /* LED驱动 */
     |    └── tuya_mpu6050.c               /* MPU6050驱动 */
     ├── platform
     |    └── tuya_gpio_nRF52832.c         /* nRF52832平台关联GPIO驱动 */
     ├── sdk
     |    ├── tuya_ble_bulk_data_demo.c    /* 大数据通道例程 */
     |    ├── tuya_ble_product_test_demo.c /* 整机产测例程 */
     |    └── tuya_ble_sdk_test.c          /* SDK测试程序 */
     ├── tuya_ble_sdk_demo.c               /* 实现tuya_ble_sdk的初始化,应用程序入口 */
     ├── tuya_gesture_controller.c         /* 手势控制器管理中心 */
     ├── tuya_imu_daq.c                    /* 传感数据采集 */
     ├── tuya_ges_act_rec.c                /* 手势动作识别 */
     ├── tuya_net_proc.c                   /* 设备联网处理 */
     └── tuya_svc_angle_calc.c             /* 姿态解算服务 */

3 功能开发

  下面详细介绍魔法棒各项功能的实现过程。Demo 仓库:tuya-iotos-embeded-demo-ble-gesture-controller

3.1 数据采集模块

  这次开发采用的惯性测量单元是 MPU6050,其主要功能已经在硬件设计方案中进行了介绍。数据采集模块的目标是:通过 I2C 接口从 MPU6050 中读取设备的加速度数据和角速度数据。

1)I2C 通信

  我们先来解决通信问题,在 MPU6050 产品规格书 的 9.2~9.4 节可以找到关于 I2C 的介绍。

  • 从机地址

  MPU6050 的 7 位从机地址由 AD0 引脚决定,当 AD0 = 0 时,从机地址为 0x68;AD0 = 1 时,从机地址为 0x69。我们采用的方案是 AD0 接地,所以从机地址为 0x68

  • 读写时序

  我们可以在产品规格书的 9.3 节找到关于 MPU6050 的寄存器读写时序的描述,包括单字节读写和多字节读写。其中,命令字节由从机地址 (bit[7:1]) 和读/写命令 (bit0) 组成,写命令为 0,读命令为 1。

  • 代码实现

  涂鸦SDK提供了一些有关 I2C 通信的函数接口,需要包含 ty_i2c.h 文件。我们使用软件 I2C 来实现,编写寄存器读写函数:

#include "tuya_mpu6050.h"
#include "ty_i2c.h"

/* slave address */
#define MPU6050_DEV_ADDR_AD0_LOW  0x68
#define MPU6050_DEV_ADDR_AD0_HIGH 0x69
#define MPU6050_DEV_ADDR          MPU6050_DEV_ADDR_AD0_LOW

/* I2C R/W command */
#define I2C_CMD_WRITE             0
#define I2C_CMD_READ              1
#define MPU6050_ADDR_CMD_WRITE    ((MPU6050_DEV_ADDR << 1) | I2C_CMD_WRITE)
#define MPU6050_ADDR_CMD_READ     ((MPU6050_DEV_ADDR << 1) | I2C_CMD_READ)

/**
 * @brief read data of MPU6050
 * @param[in] reg_addr: register address
 * @param[in] len: data length
 * @param[out] data: data buffer
 * @return none
 */
STATIC VOID_T __mpu6050_read_data(_IN UCHAR_T reg_addr, _IN CONST UCHAR_T len, _OUT UCHAR_T *data)

    i2c_start();
    i2c_send_bytes(MPU6050_ADDR_CMD_WRITE, &reg_addr, 1);
    i2c_start();
    i2c_rcv_bytes(MPU6050_ADDR_CMD_READ, data, len);
    i2c_stop();


/**
 * @brief read register of MPU6050
 * @param[in] reg_addr: register address
 * @return register value
 */
STATIC UCHAR_T __mpu6050_read_register(_IN UCHAR_T reg_addr)

    UCHAR_T reg_val;
    i2c_start();
    i2c_send_bytes(MPU6050_ADDR_CMD_WRITE, &reg_addr, 1);
    i2c_start();
    i2c_rcv_bytes(MPU6050_ADDR_CMD_READ, &reg_val, 1);
    i2c_stop();
    return reg_val;


/**
 * @brief write register of MPU6050
 * @param[in] reg_addr: register address
 * @param[in] reg_val: value to be written
 * @return none
 */
STATIC VOID_T __mpu6050_write_register(_IN CONST UCHAR_T reg_addr, _IN UCHAR_T reg_val)

    i2c_soft_cfg(MPU6050_ADDR_CMD_WRITE, reg_addr, reg_val);


/**
 * @brief write register of MPU6050
 * @param[in] reg_addr: register address
 * @param[in] data: data to be written
 * @param[in] valid_bit: the code of valid bits
 * @return none
 */
STATIC VOID_T __mpu6050_write_register_bit(_IN CONST UCHAR_T reg_addr, _IN CONST UCHAR_T data, _IN CONST UCHAR_T valid_bit)

    UCHAR_T reg_val;
    if (valid_bit == 0xFF) 
        reg_val = data;
     else 
        reg_val = __mpu6050_read_register(reg_addr);
        reg_val = (reg_val & (~valid_bit)) | (data & valid_bit);
    
    i2c_soft_cfg(MPU6050_ADDR_CMD_WRITE, reg_addr, reg_val);

  此外,还需要确认配置的 I2C 引脚是否符合硬件设计,在ty_i2c_nRF52832.c中修改:

#define TY_I2C_PIN_SCL    14
#define TY_I2C_PIN_SDA    11

2)传感器初始化

  • 初始化步骤

  MPU6050 初始化的基本步骤和需要配置的寄存器如下表所示:

No.步骤操作方法
1初始化 I2C 接口软件 I2C:初始化 SCL 和 SDA 引脚;
2复位设备设置寄存器 PWR_MGMT_1 的 bit7,复位后需延时至少100ms,否则初始化不成功;
3检查设备连接读取寄存器 WHO_AM_I 进行校验,0x68 表示已连接;
4解除休眠设置寄存器 PWR_MGMT_1 的 bit6,初值为 1,即休眠模式;
5选择时钟源设置寄存器 PWR_MGMT_1 的 bit2:0,一般选择陀螺仪某一轴的时钟源,以保证数据精度;
6设置陀螺仪满量程范围设置寄存器 GYRO_CONFIG 的 bit4:3,4种可选;
7设置加速度计满量程范围设置寄存器 ACCEL_CONFIG 的 bit4:3,4种可选;
8设置陀螺仪输出分频设置寄存器 SMPRT_DIV;
9设置数字低通滤波器设置寄存器 CONFIG 的 bit2:0;
10启用数据就绪中断设置寄存器 INT_PIN_CFG 的 bit7:4 和寄存器 INT_ENABLE 的 bit0。

  下面对初始化涉及到的寄存器做简单说明,也可以直接查看 MPU6050 寄存器手册,用上表中的寄存器名称进行搜索即可。注:括号内表示寄存器地址、寄存器名称、可读写情况和初值;省略的位为保留位,始终为 0。

  >> 电源管理寄存器1(0x6B,PWR_MGMT_1,R/W,0x40)

Bit标识符说明
7DEVICE_RESET设备复位。
0:复位完成;
1:将所有内部寄存器重置为其默认值,复位完成后,该位自动清除为0。
6SLEEP睡眠模式设置。
0:设备正常工作;
1:进入低功耗睡眠模式。
5CYCLE循环模式设置。
0:禁用循环模式;
1:SLEEP=0时,MPU-60X0将进入循环模式,设备在睡眠和唤醒之间循环。
3TEMP_DIS温度传感器设置。
0:启用温度传感器;
1:禁用温度传感器。
2:0CLKSEL[2:0]系统时钟源选择。
000:内部 8M RC 晶振;
001:PLL,使用X轴陀螺仪作为参考;
010:PLL,使用Y轴陀螺仪作为参考;
011:PLL,使用Z轴陀螺仪作为参考;
100:PLL,使用外部 32.768kHz 作为参考;
101:PLL,使用外部 19.2MHz 作为参考;
110:保留;
111:关闭时钟,保持时序产生电路复位状态。

  >> 设备ID校验寄存器(0x75,WHO_AM_I,R,0x68)

Bit标识符说明
6:1WHO_AM_I[6:1]设备地址高 6 位,默认为 0x68。

  >> 陀螺仪配置寄存器(0x1B,GYRO_CONFIG,R/W,0x00)

Bit标识符说明
7:5XYZG_ST陀螺仪 X/Y/Z 轴自检控制。
0:关闭自检;
1:激活自检。
4:3FS_SEL[1:0]陀螺仪满量程范围设置。
00:±250°/s;
01:±500°/s;
10:±1000°/s;
11:±2000°/s。

  >> 加速度计配置寄存器(0x1C,ACCEL_CONFIG,R/W,0x00)

Bit标识符说明
7:5XYZA_ST加速度 X/Y/Z 轴自检控制。
0:关闭自检;
1:激活自检。
4:3AFS_SEL[1:0]加速度计满量程范围设置。
00:±2g;
01:±4g;
10:±8g;
11:±16g。

  >> 采样率分频寄存器(0x19,SMPRT_DIV,R/W,0x00)

Bit标识符说明
7:0SMPLRT_DIV[7:0]陀螺仪输出速率分频设置。
- 采样率 = 陀螺仪输出速率 / (1 + SMPLRT_DIV);
- 禁用 DLPF (DLPF_CFG=0或7) 时,陀螺仪输出速率为 8kHz;
- 启用 DLPF 时,陀螺仪输出速率为 1kHz。

  >> 配置寄存器(0x1A,CONFIG,R/W,0x00)

Bit标识符说明
5:3EXT_SYNC_SET[2:0]外部帧同步 (FSYNC) 引脚采样设置。
2:0DLPF_CFG[2:0]数字低通滤波器 (DLPF) 设置。

  >> 中断引脚配置寄存器(0x37,INT_PIN_CFG,R/W,0x00)

Bit标识符说明
7INT_LEVELINT 引脚中断电平设置。
0:高电平有效;
1:低电平有效。
6INT_OPENINT 引脚输出模式设置。
0:推挽输出;
1:开漏输出。
5LATCH_INT_EN中断保持方式设置。
0:产生 50us 脉冲;
1:保持高电平直到中断清除。
4INT_RD_CLEAR中断状态清除方式设置。
0:仅通过读取 INT_STATUS 来清除中断状态位;
1:通过任何读操作清除中断状态位。
3FSYNC_INT_LEVELFSYNC 引脚中断电平设置。
0:高电平有效;
1:低电平有效。
2FSYNC_INT_ENFSYNC 引脚中断功能设置。
0:禁止 FSYNC 引脚作为主处理器的中断引脚;
1:允许 FSYNC 引脚作为主处理器的中断引脚。
1I2C_BYPASS_EN辅助 I2C 总线访问权限设置。
0:禁止主处理器直接访问辅助 I2C 总线;
1:I2C_MST_EN = 0 时,允许主处理器直接访问辅助 I2C 总线。

  >> 中断使能寄存器(0x38,INT_ENABLE,R/W,0x00)

Bit标识符说明
6MOT_EN运动中断设置。
0:禁用中断;
1:启用中断。
4FIFO_OFLOW_ENFIFO缓存区溢出中断设置。
0:禁用中断;
1:启用中断。
3I2C_MST_INT_ENI2C主机中断设置。
0:禁用中断;
1:启用中断。
0DATA_RDY_EN数据就绪中断设置。
0:禁用中断;
1:启用中断。
  • 代码实现
#include "tuya_mpu6050.h"
#include "ty_i2c.h"

/* register map */
#define MPU6050_RA_SMPRT_DIV            0x19
#define MPU6050_RA_CONFIG               0x1A
#define MPU6050_RA_GYRO_CONFIG          0x1B
#define MPU6050_RA_ACCEL_CONFIG         0x1C
#define MPU6050_RA_INT_PIN_CFG          0x37
#define MPU6050_RA_INT_ENABLE           0x38
#define MPU6050_RA_PWR_MGMT_1           0x6B
#define MPU6050_RA_WHO_AM_I             0x75

/* MPU6050 Gyro full-scale range */
typedef BYTE_T MPU_GYRO_FSR_E;
#define MPU_GYRO_FS_250             	0x00    /* 250dps */
#define MPU_GYRO_FS_500             	0x01    /* 500dps */
#define MPU_GYRO_FS_1000            	0x02    /* 1000dps */
#define MPU_GYRO_FS_2000            	0x03    /* 2000dps */

/* MPU6050 Accel full-scale range */
typedef BYTE_T MPU_ACCEL_FSR_E;
#define MPU_ACCEL_FS_2              	0x00    /* 2g */
#define MPU_ACCEL_FS_4              	0x01    /* 4g */
#define MPU_ACCEL_FS_8              	0x02    /* 8g */
#define MPU_ACCEL_FS_16             	0x03    /* 16g */

STATIC FLOAT_T sg_gyro_sens = 0.0f;
STATIC USHORT_T sg_accel_sens = 0;

/**
 * @brief reset MPU6050
 * @param[in] none
 * @return none
 */
STATIC VOID_T __mpu6050_reset(VOID_T)

    __mpu6050_write_register_bit(MPU6050_RA_PWR_MGMT_1, MPU_RA_BIT_DEVICE_RESET, MPU_RA_BIT_DEVICE_RESET);
    tuya_ble_device_delay_ms(100);


/**
 * @brief get the identity of the device (default: 0x68)
 * @param[in] none
 * @return device id
 */
STATIC UCHAR_T __mpu6050_get_device_id(VOID_T)

    return __mpu6050_read_register(MPU6050_RA_WHO_AM_I);


/**
 * @brief check if MPU6050 is connected
 * @param[in] none
 * @return TRUE - connected, FALSE - unconnected
 */
STATIC BOOL_T __mpu6050_is_connected(VOID_T)

    if (__mpu6050_get_device_id() == MPU6050_DEV_ID) 
        return TRUE;
     else 
        return FALSE;
    


/**
 * @brief enable or disable sleep mode
 * @param[in] enabled: TRUE - sleep, FALSE - work
 * @return none
 */
STATIC VOID_T __mpu6050_set_sleep_mode(_IN CONST BOOL_T enabled)

    if (enabled) 
        __mpu6050_write_register_bit(MPU6050_RA_PWR_MGMT_1, MPU_RA_BIT_SLEEP, MPU_RA_BIT_SLEEP);
     else 
        __mpu6050_write_register_bit(MPU6050_RA_PWR_MGMT_1, ~MPU_RA_BIT_SLEEP, MPU_RA_BIT_SLEEP);
    


/**
 * @brief enable or disable sleep mode
 * @param[in] enabled: TRUE - sleep, FALSE - work
 * @return none
 */
VOID_T tuya_mpu6050_set_sleep_mode(_IN CONST BOOL_T enabled)

    __mpu6050_set_sleep_mode(enabled);


/**
 * @brief set clock source
 * @param[in] src: clock source
 * @return none
 */
STATIC VOID_T __mpu6050_set_clk_src(UCHAR_T src)

    __mpu6050_write_register_bit(MPU6050_RA_PWR_MGMT_1, src, MPU_RA_BIT_CLKSEL);


/**
 * @brief set gyroscope's full-scale range
 * @param[in] range: gyroscope's full-scale range value
 * @return none
 */
STATIC VOID_T __mpu6050_set_gyro_fsr(_IN CONST MPU_GYRO_FSR_E range)

    __mpu6050_write_register_bit(MPU6050_RA_GYRO_CONFIG, range<<3, MPU_RA_BIT_FS_SEL);


/**
 * @brief set accelerometer's full-scale range
 * @param[in] range: new full-scale accelerometer range value
 * @return none
 */
STATIC VOID_T __mpu6050_set_accel_fsr(_IN CONST MPU_ACCEL_FSR_E range)

    __mpu6050_write_register_bit(MPU6050_RA_ACCEL_CONFIG, range<<3, MPU_RA_BIT_AFS_SEL);


/**
 * @brief set MPU6050's sample rate
 * @param[in] sr: sample rate gyroscope output rate divider value
 * @return none
 */
STATIC VOID_T __mpu6050_set_sample_rate(_IN USHORT_T sr)

    UCHAR_T div;
    if (sr > MPU_GYRO_OUTPUT_RATE) 
        sr = MPU_GYRO_OUTPUT_RATE;
    
    if (sr < (MPU_GYRO_OUTPUT_RATE/MPU_SMPRT_DIV_MAX)) 
        sr = (MPU_GYRO_OUTPUT_RATE/MPU_SMPRT_DIV_MAX);
    
    div = MPU_GYRO_OUTPUT_RATE / sr - 1;
    __mpu6050_write_register(MPU6050_RA_SMPRT_DIV, div);


/**
 * @brief set MPU6050's DLPF
 * @param[in] bw: baud width
 * @return none
 */
STATIC VOID_T __mpu6050_set_dlpf(_IN CONST USHORT_T bw)

    UCHAR_T cfg = 0;
    if (bw >= MPU_DLPF_BW_CFG_1) 
        cfg = 1;
     else if (bw >= MPU_DLPF_BW_CFG_2) 
        cfg = 2;
     else if (bw >= MPU_DLPF_BW_CFG_3) 
        cfg = 3;
     else if (bw >= MPU_DLPF_BW_CFG_4) 
        cfg = 4;
     else if (bw >= MPU_DLPF_BW_CFG_5) 
        cfg = 5;
     else 
        cfg = 6;
    
    __mpu6050_write_register(MPU6050_RA_CONFIG, cfg);


/**
 * @brief set intterupt
 * @param[in] active_low: TRUE - active low, FALSE - active high
 * @return none
 */
STATIC VOID_T __mpu6050_set_int(BOOL_T active_low)

    UCHAR_T reg_value = 0;
    if (active_low) 
        __mpu6050_write_register(MPU6050_RA_INT_PIN_CFG, 0x90);
     else 
        __mpu6050_write_register(MPU6050_RA_INT_PIN_CFG, 0x50);
    
    __mpu6050_write_register(MPU6050_RA_INT_ENABLE, 0x01);


/**
 * @brief MPU6050 sensor driver init
 * @param[in] clk: clock source
 * @param[in] g_fsr: gyroscope's full-scale range
 * @param[in] a_fsr: accelerometer's full-scale range
 * @param[in] smp_rt: sample rate
 * @param[in] pin: interrupt pin
 * @param[in] type: interrupt type
 * @param[in] int_cb: interrupt callback function
 * @return operation result
 */
MPU_RET tuya_mpu6050_init(_IN CONST MPU_CLK_E clk, _IN CONST MPU_GYRO_FSR_E g_fsr, _IN CONST MPU_ACCEL_FSR_E a_fsr,
                          _IN CONST USHORT_T smp_rt, _IN CONST TY_GPIO_PORT_E pin, _IN CONST TY_GPIO_IRQ_TYPE_E type,
                          _IN TY_GPIO_IRQ_CB int_cb)

    /* I2C init */
    i2c_soft_gpio_init();
    /* reset MPU6050 */
    __mpu6050_reset();
    /* check communication */
    if (!__mpu6050_is_connected()) 
        return MPU_ERR_UNCONN;
    

    /* MPU6050 init */
    __mpu6050_set_sleep_mode(FALSE);    /* wakeup MPU6050 */
    __mpu6050_set_clk_src(clk);         /* set clock source */
    __mpu6050_set_gyro_fsr(g_fsr);      /* set gyroscope's full-scale range */
    __mpu6050_set_accel_fsr(a_fsr);     /* set accelerometer's full-scale range */
    __mpu6050_set_sample_rate(smp_rt);  /* set sample rate */
    __mpu6050_set_dlpf(smp_rt/2);       /* set DLPF */

    /* save sensitivity scale factor */以上是关于智能魔法棒(手势控制器)———嵌入式篇的主要内容,如果未能解决你的问题,请参考以下文章

智能魔法棒———硬件篇

智能魔法棒———硬件篇

什么样的魔法棒,能让AI魔法师一夜成名?

《安富莱嵌入式周报》第302期:芯片内部Flash读保护攻击,开源智能手表设计,超棒静电学手册中文版,65W USB电源适配器方案,历届Matlab挑战赛

嵌入式 GCC 优化魔法

改造一台可以计算滤芯使用寿命的智能空气净化器——嵌入式功能实现篇