├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── Kconfig ├── Kconfig.projbuild ├── LICENSE ├── Makefile.projbuild ├── README.md ├── component.mk ├── docs ├── Makefile ├── doxygen │ ├── Doxyfile │ ├── DoxygenLayout.xml │ ├── customdoxygen.css │ ├── doxy-boot.js │ ├── footer.html │ ├── header.html │ └── mainpage.dox ├── make.bat └── source │ ├── _static │ ├── MPUdriver.jpg │ └── menuconfig_mpu-driver.png │ ├── conf.py │ ├── index.rst │ └── requirements.txt ├── examples ├── README.md ├── mpu_i2c │ ├── Makefile │ └── main │ │ ├── component.mk │ │ └── mpu_i2c.cpp ├── mpu_real │ ├── Makefile │ └── main │ │ ├── component.mk │ │ └── mpu_real.cpp └── mpu_spi │ ├── Makefile │ └── main │ ├── component.mk │ └── mpu_spi.cpp ├── include ├── MPU.hpp ├── MPUdmp.hpp └── mpu │ ├── log.hpp │ ├── math.hpp │ ├── registers.hpp │ ├── types.hpp │ └── utils.hpp ├── src ├── MPU.cpp └── MPUdmp.cpp ├── test ├── README.md ├── mpu-tests │ ├── Kconfig │ ├── component.mk │ └── test_mpu.cpp └── unit-test-app │ ├── Makefile │ └── main │ ├── app_main.cpp │ └── component.mk └── tools └── ci ├── build.sh ├── gen_doxygen_docs.sh ├── setup_esp_idf.sh └── setup_ext_libs.sh /.gitignore: -------------------------------------------------------------------------------- 1 | ### ESP-IDF 2 | **/build/ 3 | **/sdkconfig 4 | **/sdkconfig.old 5 | 6 | ## This partition table is generated 7 | test/unit-test-app/partition_table_unit_test_app.csv 8 | 9 | ### Documentation build artifacts 10 | docs/build/ 11 | docs/doxygen/xml/ 12 | docs/doxygen/man/ 13 | docs/doxygen/html/ 14 | docs/doxygen/doxygen-warning-log.txt 15 | 16 | ### Miscellaneous 17 | NOTES.md 18 | tools.mk 19 | .clang-format 20 | 21 | ### Visual Studio Code 22 | .vscode/ 23 | 24 | ### CLion 25 | .idea/ 26 | cmake-build-*/ 27 | 28 | 29 | ### temporarily 30 | include/mpu/fusion.hpp 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: cpp 3 | 4 | # Environment variables 5 | env: 6 | global: 7 | - PATH: $PATH:$TRAVIS_BUILD_DIR/esp/xtensa-esp32-elf/bin 8 | - IDF_PATH: $TRAVIS_BUILD_DIR/esp/esp-idf 9 | - GH_REPO_NAME: esp32-MPU-driver 10 | - GH_REPO_REF: github.com/natanaeljr/esp32-MPU-driver.git 11 | - DOXYFILE_PATH: docs/doxygen 12 | - DOXYFILE: Doxyfile 13 | - MAKEFLAGS: "-j2" 14 | 15 | stages: 16 | - test 17 | - name: docs deploy 18 | if: branch = dev 19 | 20 | # Install dependencies 21 | addons: 22 | apt: 23 | packages: 24 | - bash 25 | - libncurses-dev 26 | - flex 27 | - bison 28 | - gperf 29 | - python 30 | - python-serial 31 | 32 | # setup to run script 33 | before_script: 34 | - $TRAVIS_BUILD_DIR/tools/ci/setup_esp_idf.sh 35 | - $TRAVIS_BUILD_DIR/tools/ci/setup_ext_libs.sh 36 | 37 | jobs: 38 | include: 39 | - script: ./tools/ci/build.sh test/unit-test-app --protocols=I2C --chips=all 40 | env: 41 | - UNIT_TEST_I2C: 1 42 | - script: ./tools/ci/build.sh test/unit-test-app --protocols=SPI --chips=all 43 | env: 44 | - UNIT_TEST_SPI: 1 45 | - script: ./tools/ci/build.sh examples/mpu_real --protocols=all --chips=all 46 | env: 47 | - EXAMPLE_MPU_REAL: 1 48 | - script: ./tools/ci/build.sh examples/mpu_i2c --protocols=I2C --chips=all 49 | env: 50 | - EXAMPLE_MPU_I2C: 1 51 | - script: ./tools/ci/build.sh examples/mpu_spi --protocols=SPI --chips=all 52 | env: 53 | - EXAMPLE_MPU_SPI: 1 54 | - stage: docs deploy 55 | addons: 56 | apt: 57 | packages: 58 | - bash 59 | - doxygen 60 | - doxygen-doc 61 | - doxygen-latex 62 | - doxygen-gui 63 | - graphviz 64 | before_script: skip 65 | script: ./tools/ci/gen_doxygen_docs.sh 66 | deploy: 67 | provider: pages 68 | skip-cleanup: true 69 | github-token: $GITHUB_TOKEN 70 | keep-history: true 71 | target-branch: gh-pages 72 | local-dir: gh-pages 73 | on: 74 | branch: dev 75 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(COMPONENT_SRCS 2 | "src/MPU.cpp") 3 | set(COMPONENT_ADD_INCLUDEDIRS "include") 4 | set(COMPONENT_REQUIRES "I2Cbus") 5 | 6 | register_component() 7 | 8 | component_compile_options(PUBLIC -DMPU_COMPONENT_TRUE=1) 9 | 10 | ## Defines needed for library code implementation 11 | # MPU9250 is the same as MPU6500 + AK8963 12 | # MPU9150 is the same as MPU6050 + AK8975 13 | # MPU6000 code is the same as MPU6050 code 14 | # MPU6555 equals MPU6500, MPU9255 equals MPU9250 15 | if(${CONFIG_MPU_CHIP_MODEL} STREQUAL "MPU9250") 16 | component_compile_options(PUBLIC -DCONFIG_MPU6500) 17 | elseif (${CONFIG_MPU_CHIP_MODEL} STREQUAL "MPU9150") 18 | component_compile_options(PUBLIC -DCONFIG_MPU6050) 19 | elseif (${CONFIG_MPU_CHIP_MODEL} STREQUAL "MPU6000") 20 | component_compile_options(PUBLIC -DCONFIG_MPU6050) 21 | elseif (${CONFIG_MPU_CHIP_MODEL} STREQUAL "MPU6555") 22 | component_compile_options(PUBLIC -DCONFIG_MPU6500) 23 | elseif (${CONFIG_MPU_CHIP_MODEL} STREQUAL "MPU9255") 24 | component_compile_options(PUBLIC -DCONFIG_MPU9250 -DCONFIG_MPU6500) 25 | endif() 26 | -------------------------------------------------------------------------------- /Kconfig: -------------------------------------------------------------------------------- 1 | menu "MPU driver" 2 | 3 | # 4 | # Options defined in this menu 5 | # 6 | # CONFIG_MPU_CHIP_MODEL 7 | # CONFIG_MPU6000 8 | # CONFIG_MPU6050 9 | # CONFIG_MPU6500 10 | # CONFIG_MPU6555 11 | # CONFIG_MPU9150 12 | # CONFIG_MPU9250 13 | # CONFIG_MPU9255 14 | # CONFIG_MPU_AK8963 15 | # CONFIG_MPU_AK8975 16 | # CONFIG_MPU_AK89xx 17 | # CONFIG_MPU_COMM_PROTOCOL 18 | # CONFIG MPU_I2C 19 | # CONFIG MPU_SPI 20 | # CONFIG_MPU_ENABLE_DMP 21 | # CONFIG_MPU_FIFO_CORRUPTION_CHECK 22 | # CONFIG_MPU_LOG_LEVEL 23 | # CONFIG_MPU_LOG_LEVEL_DEFAULT 24 | # CONFIG_MPU_LOG_LEVEL_NONE 25 | # CONFIG_MPU_LOG_LEVEL_ERROR 26 | # CONFIG_MPU_LOG_LEVEL_WARN 27 | # CONFIG_MPU_LOG_LEVEL_INFO 28 | # CONFIG_MPU_LOG_LEVEL_DEBUG 29 | # CONFIG_MPU_LOG_LEVEL_VERBOSE 30 | # 31 | 32 | 33 | choice MPU_CHIP_MODEL 34 | prompt "MPU chip model" 35 | default MPU6050 36 | help 37 | Select MPU chip model which will be used with MPU library. 38 | 39 | config MPU6000 40 | bool "MPU6000" 41 | config MPU6050 42 | bool "MPU6050" 43 | config MPU6500 44 | bool "MPU6500" 45 | config MPU6555 46 | bool "MPU6555" 47 | config MPU9150 48 | bool "MPU9150" 49 | config MPU9250 50 | bool "MPU9250" 51 | config MPU9255 52 | bool "MPU9255" 53 | 54 | endchoice 55 | 56 | 57 | config MPU_CHIP_MODEL 58 | string 59 | default "MPU6000" if MPU6000 60 | default "MPU6050" if MPU6050 61 | default "MPU6500" if MPU6500 62 | default "MPU6555" if MPU6555 63 | default "MPU9150" if MPU9150 64 | default "MPU9250" if MPU9250 65 | default "MPU9255" if MPU9255 66 | 67 | 68 | config MPU_AK8963 69 | bool 70 | default "y" 71 | depends on MPU9250 || MPU9255 72 | 73 | config MPU_AK8975 74 | bool 75 | default "y" 76 | depends on MPU9150 77 | 78 | config MPU_AK89xx 79 | bool 80 | default "y" 81 | depends on MPU_AK8963 || MPU_AK8975 82 | 83 | 84 | choice MPU_COMM_PROTOCOL 85 | prompt "Communication Protocol" 86 | default MPU_I2C 87 | help 88 | Select MPU communication protocol. Available options are I2C and SPI, according to the MPU model. 89 | 90 | config MPU_I2C 91 | bool "I2C" 92 | help 93 | Inter-Integrated Circuit (I2C) 94 | 95 | config MPU_SPI 96 | bool "SPI" 97 | depends on MPU6000 || MPU6500 || MPU6555 || MPU9250 || MPU9255 98 | help 99 | Serial Peripheral Interface bus (SPI) 100 | 101 | endchoice 102 | 103 | 104 | config MPU_COMM_PROTOCOL 105 | string 106 | default "I2C" if MPU_I2C 107 | default "SPI" if MPU_SPI 108 | 109 | 110 | choice MPU_LOG_LEVEL 111 | prompt "Log verbosity" 112 | default MPU_LOG_LEVEL_DEFAULT 113 | help 114 | Specify how much output to see in logs from MPU library. 115 | Options depend on default log level, change on Log output. 116 | 117 | config MPU_LOG_LEVEL_DEFAULT 118 | bool "Default" 119 | help 120 | Log level Default is whatever is set in default esp32 log output verbosity. 121 | 122 | config MPU_LOG_LEVEL_NONE 123 | bool "No output" 124 | config MPU_LOG_LEVEL_ERROR 125 | bool "Error" 126 | depends on LOG_DEFAULT_LEVEL_ERROR || LOG_DEFAULT_LEVEL_WARN || LOG_DEFAULT_LEVEL_INFO || LOG_DEFAULT_LEVEL_DEBUG || LOG_DEFAULT_LEVEL_VERBOSE 127 | config MPU_LOG_LEVEL_WARN 128 | bool "Warning" 129 | depends on LOG_DEFAULT_LEVEL_WARN || LOG_DEFAULT_LEVEL_INFO || LOG_DEFAULT_LEVEL_DEBUG || LOG_DEFAULT_LEVEL_VERBOSE 130 | config MPU_LOG_LEVEL_INFO 131 | bool "Info" 132 | depends on LOG_DEFAULT_LEVEL_INFO || LOG_DEFAULT_LEVEL_DEBUG || LOG_DEFAULT_LEVEL_VERBOSE 133 | config MPU_LOG_LEVEL_DEBUG 134 | bool "Debug" 135 | depends on LOG_DEFAULT_LEVEL_DEBUG || LOG_DEFAULT_LEVEL_VERBOSE 136 | config MPU_LOG_LEVEL_VERBOSE 137 | bool "Verbose" 138 | depends on LOG_DEFAULT_LEVEL_VERBOSE 139 | 140 | endchoice 141 | 142 | config MPU_LOG_LEVEL 143 | int 144 | default 0 if MPU_LOG_LEVEL_NONE || (MPU_LOG_LEVEL_DEFAULT && LOG_DEFAULT_LEVEL_NONE) 145 | default 1 if MPU_LOG_LEVEL_ERROR || (MPU_LOG_LEVEL_DEFAULT && LOG_DEFAULT_LEVEL_ERROR) 146 | default 2 if MPU_LOG_LEVEL_WARN || (MPU_LOG_LEVEL_DEFAULT && LOG_DEFAULT_LEVEL_WARN) 147 | default 3 if MPU_LOG_LEVEL_INFO || (MPU_LOG_LEVEL_DEFAULT && LOG_DEFAULT_LEVEL_INFO) 148 | default 4 if MPU_LOG_LEVEL_DEBUG || (MPU_LOG_LEVEL_DEFAULT && LOG_DEFAULT_LEVEL_DEBUG) 149 | default 5 if MPU_LOG_LEVEL_VERBOSE || (MPU_LOG_LEVEL_DEFAULT && LOG_DEFAULT_LEVEL_VERBOSE) 150 | 151 | 152 | config MPU_ENABLE_DMP 153 | bool "Digital Motion Processor (DMP)" 154 | default "n" 155 | help 156 | Enable DMP (Digital Motion Processor) code to be compiled. 157 | 158 | # config MPU_FIFO_CORRUPTION_CHECK 159 | # bool "Enable FIFO packet corruption check (DMP only)" 160 | # depends on MPU_ENABLE_DMP 161 | # default "y" 162 | # help 163 | # Check for corrupted FIFO packet by monitoring the quaternion data and 164 | # ensuring that the magnitude is always normalized to one. This 165 | # shouldn't happen in normal operation, but if an I2C error occurs, 166 | # the FIFO reads might become misaligned. 167 | 168 | 169 | config MPU_LOG_ERROR_TRACES 170 | bool "Log communication error traces" 171 | depends on MPU_LOG_LEVEL > 0 172 | default "y" 173 | help 174 | Display on console communication error traces that occur within MPU driver code. 175 | Mostly for debugging. 176 | 177 | 178 | endmenu 179 | -------------------------------------------------------------------------------- /Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "MPU driver" 2 | 3 | # 4 | # Options defined in this menu 5 | # 6 | # CONFIG_MPU_CHIP_MODEL 7 | # CONFIG_MPU6000 8 | # CONFIG_MPU6050 9 | # CONFIG_MPU6500 10 | # CONFIG_MPU6555 11 | # CONFIG_MPU9150 12 | # CONFIG_MPU9250 13 | # CONFIG_MPU9255 14 | # CONFIG_MPU_AK8963 15 | # CONFIG_MPU_AK8975 16 | # CONFIG_MPU_AK89xx 17 | # CONFIG_MPU_COMM_PROTOCOL 18 | # CONFIG MPU_I2C 19 | # CONFIG MPU_SPI 20 | # CONFIG_MPU_ENABLE_DMP 21 | # CONFIG_MPU_FIFO_CORRUPTION_CHECK 22 | # CONFIG_MPU_LOG_LEVEL 23 | # CONFIG_MPU_LOG_LEVEL_DEFAULT 24 | # CONFIG_MPU_LOG_LEVEL_NONE 25 | # CONFIG_MPU_LOG_LEVEL_ERROR 26 | # CONFIG_MPU_LOG_LEVEL_WARN 27 | # CONFIG_MPU_LOG_LEVEL_INFO 28 | # CONFIG_MPU_LOG_LEVEL_DEBUG 29 | # CONFIG_MPU_LOG_LEVEL_VERBOSE 30 | # 31 | 32 | 33 | choice MPU_CHIP_MODEL 34 | prompt "MPU chip model" 35 | default MPU6050 36 | help 37 | Select MPU chip model which will be used with MPU library. 38 | 39 | config MPU6000 40 | bool "MPU6000" 41 | config MPU6050 42 | bool "MPU6050" 43 | config MPU6500 44 | bool "MPU6500" 45 | config MPU6555 46 | bool "MPU6555" 47 | config MPU9150 48 | bool "MPU9150" 49 | config MPU9250 50 | bool "MPU9250" 51 | config MPU9255 52 | bool "MPU9255" 53 | 54 | endchoice 55 | 56 | 57 | config MPU_CHIP_MODEL 58 | string 59 | default "MPU6000" if MPU6000 60 | default "MPU6050" if MPU6050 61 | default "MPU6500" if MPU6500 62 | default "MPU6555" if MPU6555 63 | default "MPU9150" if MPU9150 64 | default "MPU9250" if MPU9250 65 | default "MPU9255" if MPU9255 66 | 67 | 68 | config MPU_AK8963 69 | bool 70 | default "y" 71 | depends on MPU9250 || MPU9255 72 | 73 | config MPU_AK8975 74 | bool 75 | default "y" 76 | depends on MPU9150 77 | 78 | config MPU_AK89xx 79 | bool 80 | default "y" 81 | depends on MPU_AK8963 || MPU_AK8975 82 | 83 | 84 | choice MPU_COMM_PROTOCOL 85 | prompt "Communication Protocol" 86 | default MPU_I2C 87 | help 88 | Select MPU communication protocol. Available options are I2C and SPI, according to the MPU model. 89 | 90 | config MPU_I2C 91 | bool "I2C" 92 | help 93 | Inter-Integrated Circuit (I2C) 94 | 95 | config MPU_SPI 96 | bool "SPI" 97 | depends on MPU6000 || MPU6500 || MPU6555 || MPU9250 || MPU9255 98 | help 99 | Serial Peripheral Interface bus (SPI) 100 | 101 | endchoice 102 | 103 | 104 | config MPU_COMM_PROTOCOL 105 | string 106 | default "I2C" if MPU_I2C 107 | default "SPI" if MPU_SPI 108 | 109 | 110 | choice MPU_LOG_LEVEL 111 | prompt "Log verbosity" 112 | default MPU_LOG_LEVEL_DEFAULT 113 | help 114 | Specify how much output to see in logs from MPU library. 115 | Options depend on default log level, change on Log output. 116 | 117 | config MPU_LOG_LEVEL_DEFAULT 118 | bool "Default" 119 | help 120 | Log level Default is whatever is set in default esp32 log output verbosity. 121 | 122 | config MPU_LOG_LEVEL_NONE 123 | bool "No output" 124 | config MPU_LOG_LEVEL_ERROR 125 | bool "Error" 126 | depends on LOG_DEFAULT_LEVEL_ERROR || LOG_DEFAULT_LEVEL_WARN || LOG_DEFAULT_LEVEL_INFO || LOG_DEFAULT_LEVEL_DEBUG || LOG_DEFAULT_LEVEL_VERBOSE 127 | config MPU_LOG_LEVEL_WARN 128 | bool "Warning" 129 | depends on LOG_DEFAULT_LEVEL_WARN || LOG_DEFAULT_LEVEL_INFO || LOG_DEFAULT_LEVEL_DEBUG || LOG_DEFAULT_LEVEL_VERBOSE 130 | config MPU_LOG_LEVEL_INFO 131 | bool "Info" 132 | depends on LOG_DEFAULT_LEVEL_INFO || LOG_DEFAULT_LEVEL_DEBUG || LOG_DEFAULT_LEVEL_VERBOSE 133 | config MPU_LOG_LEVEL_DEBUG 134 | bool "Debug" 135 | depends on LOG_DEFAULT_LEVEL_DEBUG || LOG_DEFAULT_LEVEL_VERBOSE 136 | config MPU_LOG_LEVEL_VERBOSE 137 | bool "Verbose" 138 | depends on LOG_DEFAULT_LEVEL_VERBOSE 139 | 140 | endchoice 141 | 142 | config MPU_LOG_LEVEL 143 | int 144 | default 0 if MPU_LOG_LEVEL_NONE || (MPU_LOG_LEVEL_DEFAULT && LOG_DEFAULT_LEVEL_NONE) 145 | default 1 if MPU_LOG_LEVEL_ERROR || (MPU_LOG_LEVEL_DEFAULT && LOG_DEFAULT_LEVEL_ERROR) 146 | default 2 if MPU_LOG_LEVEL_WARN || (MPU_LOG_LEVEL_DEFAULT && LOG_DEFAULT_LEVEL_WARN) 147 | default 3 if MPU_LOG_LEVEL_INFO || (MPU_LOG_LEVEL_DEFAULT && LOG_DEFAULT_LEVEL_INFO) 148 | default 4 if MPU_LOG_LEVEL_DEBUG || (MPU_LOG_LEVEL_DEFAULT && LOG_DEFAULT_LEVEL_DEBUG) 149 | default 5 if MPU_LOG_LEVEL_VERBOSE || (MPU_LOG_LEVEL_DEFAULT && LOG_DEFAULT_LEVEL_VERBOSE) 150 | 151 | 152 | config MPU_ENABLE_DMP 153 | bool "Digital Motion Processor (DMP)" 154 | default "n" 155 | help 156 | Enable DMP (Digital Motion Processor) code to be compiled. 157 | 158 | # config MPU_FIFO_CORRUPTION_CHECK 159 | # bool "Enable FIFO packet corruption check (DMP only)" 160 | # depends on MPU_ENABLE_DMP 161 | # default "y" 162 | # help 163 | # Check for corrupted FIFO packet by monitoring the quaternion data and 164 | # ensuring that the magnitude is always normalized to one. This 165 | # shouldn't happen in normal operation, but if an I2C error occurs, 166 | # the FIFO reads might become misaligned. 167 | 168 | 169 | config MPU_LOG_ERROR_TRACES 170 | bool "Log communication error traces" 171 | depends on MPU_LOG_LEVEL > 0 172 | default "y" 173 | help 174 | Display on console communication error traces that occur within MPU driver code. 175 | Mostly for debugging. 176 | 177 | 178 | endmenu 179 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2017-2018 Natanael Josue Rabello. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to 7 | deal in the Software without restriction, including without limitation the 8 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | sell copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.projbuild: -------------------------------------------------------------------------------- 1 | # 2 | # Top-level component makefile 3 | # 4 | 5 | CPPFLAGS += -DMPU_COMPONENT_TRUE=1 6 | 7 | 8 | ## Defines needed for library code implementation 9 | # MPU9250 is the same as MPU6500 + AK8963 10 | # MPU9150 is the same as MPU6050 + AK8975 11 | # MPU6000 code is the same as MPU6050 code 12 | # MPU6555 equals MPU6500, MPU9255 equals MPU9250 13 | ifeq ($(CONFIG_MPU_CHIP_MODEL),"MPU9250") 14 | CPPFLAGS += -DCONFIG_MPU6500 15 | else 16 | ifeq ($(CONFIG_MPU_CHIP_MODEL),"MPU9150") 17 | CPPFLAGS += -DCONFIG_MPU6050 18 | else 19 | ifeq ($(CONFIG_MPU_CHIP_MODEL),"MPU6000") 20 | CPPFLAGS += -DCONFIG_MPU6050 21 | else 22 | ifeq ($(CONFIG_MPU_CHIP_MODEL),"MPU6555") 23 | CPPFLAGS += -DCONFIG_MPU6500 24 | else 25 | ifeq ($(CONFIG_MPU_CHIP_MODEL),"MPU9255") 26 | CPPFLAGS += -DCONFIG_MPU9250 -DCONFIG_MPU6500 27 | endif 28 | endif 29 | endif 30 | endif 31 | endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Documentation](https://readthedocs.org/projects/esp32-mpu-driver/badge/?version=latest "Documentation Status")](https://esp32-mpu-driver.readthedocs.io/en/latest) 2 | [![Build Status](https://travis-ci.org/natanaeljr/esp32-MPU-driver.svg?branch=master)](https://travis-ci.org/natanaeljr/esp32-MPU-driver) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) 4 | 5 | ![MPU Driver][Banner] 6 | 7 | [Banner]: docs/source/_static/MPUdriver.jpg 8 | 9 | A library for _Invensense_ MPU chips. 10 | It is written in C++ and designed for working with **[ESP32]** microcontroller _[esp-idf]_ framework. 11 | Supports both SPI and I2C protocols interchangeably, selectable bus port, and even multiple connected MPUs. 12 | 13 | [ESP32]: https://www.espressif.com/en/products/hardware/esp32/overview 14 | [esp-idf]: https://github.com/espressif/esp-idf/ 15 | 16 | ## Models 17 | 18 | | part | sensors | protocol | 19 | | ------------: | :----------------: | :----------- | 20 | | **[MPU6000]** | Gyro/Accel | _I2C_, _SPI_ | 21 | | **[MPU6050]** | Gyro/Accel | _I2C_ | 22 | | **[MPU6500]** | Gyro/Accel | _I2C_, _SPI_ | 23 | | **MPU6555** | Gyro/Accel | _I2C_, _SPI_ | 24 | | **[MPU9150]** | Gyro/Accel/Compass | _I2C_ | 25 | | **[MPU9250]** | Gyro/Accel/Compass | _I2C_, _SPI_ | 26 | | **MPU9255** | Gyro/Accel/Compass | _I2C_, _SPI_ | 27 | 28 | [MPU6000]: https://www.invensense.com/products/motion-tracking/6-axis/mpu-6050/ 29 | [MPU6050]: https://www.invensense.com/products/motion-tracking/6-axis/mpu-6050/ 30 | [MPU6500]: https://www.invensense.com/products/motion-tracking/6-axis/mpu-6500/ 31 | [MPU9150]: https://www.invensense.com/products/motion-tracking/9-axis/mpu-9150/ 32 | [MPU9250]: https://www.invensense.com/products/motion-tracking/9-axis/mpu-9250/ 33 | 34 | ## Features 35 | 36 | - [x] Support to SPI and I2C protocol (with selectable port) 37 | - [x] Basic configurations (sample rate _(4Hz~32KHz)_, clock source, full-scale, standby mode, offsets, interrupts, DLPF, etc..) 38 | - [x] Burst reading for all sensors 39 | - [x] Low Power Accelerometer mode _(various rates, e.g. 8.4μA at 0.98Hz)_ 40 | - [x] Low Power Wake-on-motion mode _(with motion detection interrupt)_ 41 | - [x] FIFO buffer access for all internal and external sensors 42 | - [x] Complete Auxiliary I2C support for external sensors _(up to 4)_ 43 | - [x] External Frame Synchronization _(FSYNC)_ pass-through interrupt 44 | - [x] Motion, Zero-motion and Free-Fall detection _(as motion detection interrupt)_ 45 | - [x] Total access to the Magnetometer _(even when MPU connected by SPI protocol)_ 46 | - [x] Calibration for Gyro and Accel 47 | - [x] Self-Test _(true implementation from MotionApps)_ 48 | 49 | #### DMP 50 | 51 | - [ ] Quaternion (3-axis Gyroscope) 52 | - [ ] Quaternion (6-axis Gyroscope and Accelerometer) 53 | - [ ] Screen Orientation (Android's screen rotation algorithm) 54 | - [ ] Tap Detection 55 | - [ ] Pedometer 56 | - [ ] Gyroscope calibrated data 57 | 58 | # Getting Started 59 | 60 | ## Prerequisites 61 | 62 | MPU driver depends on the following protocol libraries to communicate with the chip with ease: [ [I2Cbus] | [SPIbus] ]. 63 | You must download the one according to the protocol you'll use and place within your components directory as well. 64 | 65 | ``` 66 | I2Cbus: git clone https://github.com/natanaeljr/esp32-I2Cbus.git I2Cbus 67 | SPIbus: git clone https://github.com/natanaeljr/esp32-SPIbus.git SPIbus 68 | ``` 69 | 70 | **_Note:_** At least one of these libraries must be installed as components for the MPU library to work. It won't work otherwise. 71 | 72 | [I2Cbus]: https://github.com/natanaeljr/esp32-I2Cbus 73 | [SPIbus]: https://github.com/natanaeljr/esp32-SPIbus 74 | 75 | ## Installation 76 | 77 | Download the repository [here](https://github.com/natanaeljr/esp32-MPU-driver/archive/master.zip), 78 | or clone it right into your project components directory with the following command. 79 | 80 | ``` 81 | git clone https://github.com/natanaeljr/esp32-MPU-driver.git MPUdriver 82 | ``` 83 | 84 | This way you can easily update the library with `git pull` whenever a update is available. 85 | 86 | ## Usage 87 | 88 | First of all, make sure MPU Driver is a component in you project, then run `make menuconfig`, select your chip model and communication protocol you'll use browsing through to `Component config` -> `MPU Driver`. 89 | 90 | ![Menuconfig](docs/source/_static/menuconfig_mpu-driver.png "Menuconfig -> MPU Driver") 91 | 92 | Now, in your source code, include the mpu main header `MPU.hpp`, the communication library `I2Cbus.hpp` or `SPIbus.hpp` and any other mpu headers that you'll use. Then get the bus ready as shown below. 93 | 94 | ```C++ 95 | #include "MPU.hpp" // main file, provides the class itself 96 | #include "mpu/math.hpp" // math helper for dealing with MPU data 97 | #include "mpu/types.hpp" // MPU data types and definitions 98 | #include "I2Cbus.hpp" 99 | // ... 100 | i2c0.begin(SDA, SCL, CLOCK); // initialize the I2C bus 101 | ``` 102 | 103 | And for SPI: 104 | 105 | ```C++ 106 | #include "MPU.hpp" // main file, provides the class itself 107 | #include "mpu/math.hpp" // math helper for dealing with MPU data 108 | #include "mpu/types.hpp" // MPU data types and definitions 109 | #include "SPIbus.hpp" 110 | // ... 111 | hspi.begin(MOSI, MISO, SCLK); // initialize the SPI bus 112 | spi_device_handle_t mpu_spi_handle; 113 | hspi.addDevice(SPIMODE, CLOCK, CS_PIN, &mpu_spi_handle); 114 | ``` 115 | 116 | **Note**: You can initialize/configure the bus through the _esp-idf_ API normally, it should work just fine too. 117 | 118 | Create a MPU object, setup and initialize it. 119 | 120 | ```C++ 121 | MPU_t MPU; // create an object 122 | MPU.setBus(i2c0); // set communication bus, for SPI -> pass 'hspi' 123 | MPU.setAddr(mpud::MPU_I2CADDRESS_AD0_LOW); // set address or handle, for SPI -> pass 'mpu_spi_handle' 124 | MPU.testConnection() // test connection with the chip, return is a error code 125 | MPU.initialize(); // this will initialize the chip and set default configurations 126 | ``` 127 | 128 | Call `set` functions to configure the chip as needed. 129 | 130 | ```C++ 131 | MPU.setSampleRate(250); // in (Hz) 132 | MPU.setAccelFullScale(mpud::ACCEL_FS_4G); 133 | MPU.setGyroFullScale(mpud::GYRO_FS_500DPS); 134 | MPU.setDigitalLowPassFilter(mpud::DLPF_42HZ); // smoother data 135 | MPU.setInterruptEnabled(mpud::INT_EN_RAWDATA_READY); // enable INT pin 136 | ``` 137 | 138 | Read sensor data: 139 | 140 | ```C++ 141 | mpud::raw_axes_t accelRaw; // holds x, y, z axes as int16 142 | mpud::raw_axes_t gyroRaw; // holds x, y, z axes as int16 143 | MPU.acceleration(&accelRaw); // fetch raw data from the registers 144 | MPU.rotation(&gyroRaw); // fetch raw data from the registers 145 | printf("accel: %+d %+d %+d\n", accelRaw.x, accelRaw.y, accelRaw.z); 146 | printf("gyro: %+d %+d %+d\n", gyroRaw[0], gyroRaw[1], gyroRaw[2]); 147 | ``` 148 | 149 | Convert to more readable formats. 150 | 151 | ```C++ 152 | mpud::float_axes_t accelG = mpud::accelGravity(accelRaw, mpud::ACCEL_FS_4G); // raw data to gravity 153 | mpud::float_axes_t gyroDPS = mpud::gyroDecPerSec(gyroRaw, mpud::GYRO_FS_500DPS); // raw data to º/s 154 | printf("accel: %+.2f %+.2f %+.2f\n", accelG[0], accelG[1], accelG[2]); 155 | printf("gyro: %+.2f %+.2f %+.2f\n", gyroDPS.x, gyroDPS.y, gyroDPS.z); 156 | ``` 157 | 158 | The API provides many other functions to manage and operate the sensor in its full potencial. See 159 | 160 | API Reference 161 | . 162 | 163 | ## Tests 164 | 165 | See [MPU Unit Test] for more information. 166 | 167 | [MPU Unit Test]: test/README.md 168 | 169 | ## License 170 | 171 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 172 | 173 | Copyright © 2017-2018, Natanael Josue Rabello [_natanael.rabello@outlook.com_] 174 | 175 | --- 176 | -------------------------------------------------------------------------------- /component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Main component makefile. 3 | # 4 | 5 | COMPONENT_SRCDIRS := src . 6 | 7 | 8 | ## Compile DMP code only if enabled in menuconfig 9 | $(call compile_only_if,$(CONFIG_MPU_ENABLE_DMP),src/MPUdmp.o) 10 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = MPUDriver 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/doxygen/DoxygenLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /docs/doxygen/customdoxygen.css: -------------------------------------------------------------------------------- 1 | h1, .h1, h2, .h2, h3, .h3{ 2 | font-weight: 200 !important; 3 | } 4 | 5 | #navrow1, #navrow2, #navrow3, #navrow4, #navrow5{ 6 | border-bottom: 1px solid #EEEEEE; 7 | } 8 | 9 | .adjust-right { 10 | margin-left: 30px !important; 11 | font-size: 1.15em !important; 12 | } 13 | .navbar{ 14 | border: 0px solid #222 !important; 15 | } 16 | table{ 17 | white-space:pre-wrap !important; 18 | } 19 | /* 20 | =========================== 21 | */ 22 | 23 | 24 | /* Sticky footer styles 25 | -------------------------------------------------- */ 26 | html, 27 | body { 28 | height: 100%; 29 | /* The html and body elements cannot have any padding or margin. */ 30 | } 31 | 32 | /* Wrapper for page content to push down footer */ 33 | #wrap { 34 | min-height: 100%; 35 | height: auto; 36 | /* Negative indent footer by its height */ 37 | margin: 0 auto -60px; 38 | /* Pad bottom by footer height */ 39 | padding: 0 0 60px; 40 | } 41 | 42 | /* Set the fixed height of the footer here */ 43 | #footer { 44 | font-size: 0.9em; 45 | padding: 8px 0px; 46 | background-color: #f5f5f5; 47 | } 48 | 49 | .footer-row { 50 | line-height: 44px; 51 | } 52 | 53 | #footer > .container { 54 | padding-left: 15px; 55 | padding-right: 15px; 56 | } 57 | 58 | .footer-follow-icon { 59 | margin-left: 3px; 60 | text-decoration: none !important; 61 | } 62 | 63 | .footer-follow-icon img { 64 | width: 20px; 65 | } 66 | 67 | .footer-link { 68 | padding-top: 5px; 69 | display: inline-block; 70 | color: #999999; 71 | text-decoration: none; 72 | } 73 | 74 | .footer-copyright { 75 | text-align: center; 76 | } 77 | 78 | 79 | @media (min-width: 992px) { 80 | .footer-row { 81 | text-align: left; 82 | } 83 | 84 | .footer-icons { 85 | text-align: right; 86 | } 87 | } 88 | @media (max-width: 991px) { 89 | .footer-row { 90 | text-align: center; 91 | } 92 | 93 | .footer-icons { 94 | text-align: center; 95 | } 96 | } 97 | 98 | /* DOXYGEN Code Styles 99 | ----------------------------------- */ 100 | 101 | 102 | a.qindex { 103 | font-weight: bold; 104 | } 105 | 106 | a.qindexHL { 107 | font-weight: bold; 108 | background-color: #9CAFD4; 109 | color: #ffffff; 110 | border: 1px double #869DCA; 111 | } 112 | 113 | .contents a.qindexHL:visited { 114 | color: #ffffff; 115 | } 116 | 117 | a.code, a.code:visited, a.line, a.line:visited { 118 | color: #4665A2; 119 | } 120 | 121 | a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { 122 | color: #4665A2; 123 | } 124 | 125 | /* @end */ 126 | 127 | dl.el { 128 | margin-left: -1cm; 129 | } 130 | 131 | pre.fragment { 132 | border: 1px solid #C4CFE5; 133 | background-color: #FBFCFD; 134 | padding: 4px 6px; 135 | margin: 4px 8px 4px 2px; 136 | overflow: auto; 137 | word-wrap: break-word; 138 | font-size: 9pt; 139 | line-height: 125%; 140 | font-family: monospace, fixed; 141 | font-size: 105%; 142 | } 143 | 144 | div.fragment { 145 | padding: 4px 6px; 146 | margin: 4px 8px 4px 2px; 147 | border: 1px solid #C4CFE5; 148 | } 149 | 150 | div.line { 151 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; 152 | font-size: 12px; 153 | min-height: 13px; 154 | line-height: 1.0; 155 | text-wrap: unrestricted; 156 | white-space: -moz-pre-wrap; /* Moz */ 157 | white-space: -pre-wrap; /* Opera 4-6 */ 158 | white-space: -o-pre-wrap; /* Opera 7 */ 159 | white-space: pre-wrap; /* CSS3 */ 160 | word-wrap: normal; /* IE 5.5+ */ 161 | text-indent: -53px; 162 | padding-left: 53px; 163 | padding-bottom: 0px; 164 | margin: 0px; 165 | -webkit-transition-property: background-color, box-shadow; 166 | -webkit-transition-duration: 0.5s; 167 | -moz-transition-property: background-color, box-shadow; 168 | -moz-transition-duration: 0.5s; 169 | -ms-transition-property: background-color, box-shadow; 170 | -ms-transition-duration: 0.5s; 171 | -o-transition-property: background-color, box-shadow; 172 | -o-transition-duration: 0.5s; 173 | transition-property: background-color, box-shadow; 174 | transition-duration: 0.5s; 175 | } 176 | div.line:hover{ 177 | background-color: #FBFF00; 178 | } 179 | 180 | div.line.glow { 181 | background-color: cyan; 182 | box-shadow: 0 0 10px cyan; 183 | } 184 | 185 | 186 | span.lineno { 187 | padding-right: 4px; 188 | text-align: right; 189 | color:rgba(0,0,0,0.3); 190 | border-right: 1px solid #EEE; 191 | border-left: 1px solid #EEE; 192 | background-color: #FFF; 193 | white-space: pre; 194 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace ; 195 | } 196 | span.lineno a { 197 | background-color: #FAFAFA; 198 | cursor:pointer; 199 | } 200 | 201 | span.lineno a:hover { 202 | background-color: #EFE200; 203 | color: #1e1e1e; 204 | } 205 | 206 | div.groupHeader { 207 | margin-left: 16px; 208 | margin-top: 12px; 209 | font-weight: bold; 210 | } 211 | 212 | div.groupText { 213 | margin-left: 16px; 214 | font-style: italic; 215 | } 216 | 217 | /* @group Code Colorization */ 218 | 219 | span.keyword { 220 | color: #008000 221 | } 222 | 223 | span.keywordtype { 224 | color: #604020 225 | } 226 | 227 | span.keywordflow { 228 | color: #e08000 229 | } 230 | 231 | span.comment { 232 | color: #800000 233 | } 234 | 235 | span.preprocessor { 236 | color: #806020 237 | } 238 | 239 | span.stringliteral { 240 | color: #002080 241 | } 242 | 243 | span.charliteral { 244 | color: #008080 245 | } 246 | 247 | span.vhdldigit { 248 | color: #ff00ff 249 | } 250 | 251 | span.vhdlchar { 252 | color: #000000 253 | } 254 | 255 | span.vhdlkeyword { 256 | color: #700070 257 | } 258 | 259 | span.vhdllogic { 260 | color: #ff0000 261 | } 262 | 263 | blockquote { 264 | background-color: #F7F8FB; 265 | border-left: 2px solid #9CAFD4; 266 | margin: 0 24px 0 4px; 267 | padding: 0 12px 0 16px; 268 | } 269 | 270 | /*---------------- Search Box */ 271 | 272 | #search-box { 273 | margin: 10px 0px; 274 | } 275 | #search-box .close { 276 | display: none; 277 | position: absolute; 278 | right: 0px; 279 | padding: 6px 12px; 280 | z-index: 5; 281 | } 282 | 283 | /*---------------- Search results window */ 284 | 285 | #search-results-window { 286 | display: none; 287 | } 288 | 289 | iframe#MSearchResults { 290 | width: 100%; 291 | height: 15em; 292 | } 293 | 294 | .SRChildren { 295 | padding-left: 3ex; padding-bottom: .5em 296 | } 297 | .SRPage .SRChildren { 298 | display: none; 299 | } 300 | a.SRScope { 301 | display: block; 302 | } 303 | a.SRSymbol:focus, a.SRSymbol:active, 304 | a.SRScope:focus, a.SRScope:active { 305 | text-decoration: underline; 306 | } 307 | span.SRScope { 308 | padding-left: 4px; 309 | } 310 | .SRResult { 311 | display: none; 312 | } 313 | 314 | /* class and file list */ 315 | .directory .icona, 316 | .directory .arrow { 317 | height: auto; 318 | } 319 | .directory .icona .icon { 320 | height: 16px; 321 | } 322 | .directory .icondoc { 323 | background-position: 0px 0px; 324 | height: 20px; 325 | } 326 | .directory .iconfopen { 327 | background-position: 0px 0px; 328 | } 329 | .directory td.entry { 330 | padding: 7px 8px 6px 8px; 331 | } 332 | 333 | .table > tbody > tr > td.memSeparator { 334 | line-height: 0; 335 | .table-hover; 336 | 337 | } 338 | 339 | .memItemLeft, .memTemplItemLeft { 340 | white-space: normal; 341 | } 342 | 343 | /* enumerations */ 344 | .panel-body thead > tr { 345 | background-color: #e0e0e0; 346 | } 347 | 348 | /* todo lists */ 349 | .todoname, 350 | .todoname a { 351 | font-weight: bold; 352 | } 353 | 354 | /* Class title */ 355 | .summary { 356 | margin-top: 25px; 357 | } 358 | .page-header { 359 | margin: 20px 0px !important; 360 | } 361 | .page-header .title { 362 | display: inline-block; 363 | } 364 | .page-header .pull-right { 365 | margin-top: 0.3em; 366 | margin-left: 0.5em; 367 | } 368 | .page-header .label { 369 | font-size: 50%; 370 | } 371 | -------------------------------------------------------------------------------- /docs/doxygen/doxy-boot.js: -------------------------------------------------------------------------------- 1 | $( document ).ready(function() { 2 | 3 | $("div.headertitle").addClass("page-header"); 4 | $("div.title").addClass("h1"); 5 | 6 | $('li > a[href="index.html"] > span').before(" "); 7 | $('li > a[href="modules.html"] > span').before(" "); 8 | $('li > a[href="namespaces.html"] > span').before(" "); 9 | $('li > a[href="annotated.html"] > span').before(" "); 10 | $('li > a[href="classes.html"] > span').before(" "); 11 | $('li > a[href="inherits.html"] > span').before(" "); 12 | $('li > a[href="functions.html"] > span').before(" "); 13 | $('li > a[href="functions_func.html"] > span').before(" "); 14 | $('li > a[href="functions_vars.html"] > span').before(" "); 15 | $('li > a[href="functions_enum.html"] > span').before(" "); 16 | $('li > a[href="functions_eval.html"] > span').before(" "); 17 | $('img[src="ftv2ns.png"]').replaceWith('N '); 18 | $('img[src="ftv2cl.png"]').replaceWith('C '); 19 | 20 | $("ul.tablist").addClass("nav nav-pills nav-justified"); 21 | $("ul.tablist").css("margin-top", "0.5em"); 22 | $("ul.tablist").css("margin-bottom", "0.5em"); 23 | $("li.current").addClass("active"); 24 | $("iframe").attr("scrolling", "yes"); 25 | 26 | $("#nav-path > ul").addClass("breadcrumb"); 27 | 28 | $("table.params").addClass("table"); 29 | $("div.ingroups").wrapInner(""); 30 | $("div.levels").css("margin", "0.5em"); 31 | $("div.levels > span").addClass("btn btn-default btn-xs"); 32 | $("div.levels > span").css("margin-right", "0.25em"); 33 | 34 | $("table.directory").addClass("table table-striped"); 35 | $("div.summary > a").addClass("btn btn-default btn-xs"); 36 | $("table.fieldtable").addClass("table"); 37 | $(".fragment").addClass("well"); 38 | $(".memitem").addClass("panel panel-default"); 39 | $(".memproto").addClass("panel-heading"); 40 | $(".memdoc").addClass("panel-body"); 41 | $("span.mlabel").addClass("label label-info"); 42 | 43 | $("table.memberdecls").addClass("table"); 44 | $("[class^=memitem]").addClass("active"); 45 | 46 | $("div.ah").addClass("btn btn-default"); 47 | $("span.mlabels").addClass("pull-right"); 48 | $("table.mlabels").css("width", "100%") 49 | $("td.mlabels-right").addClass("pull-right"); 50 | 51 | $("div.ttc").addClass("panel panel-primary"); 52 | $("div.ttname").addClass("panel-heading"); 53 | $("div.ttname a").css("color", 'white'); 54 | $("div.ttdef,div.ttdoc,div.ttdeci").addClass("panel-body"); 55 | 56 | $('div.fragment.well div.line:first').css('margin-top', '2px'); 57 | $('div.fragment.well div.line:last').css('margin-bottom', '2px'); 58 | 59 | $('table.doxtable').removeClass('doxtable').addClass('table table-striped table-bordered').each(function(){ 60 | $(this).prepend(''); 61 | $(this).find('tbody > tr:first').prependTo($(this).find('thead')); 62 | 63 | $(this).find('td > span.success').parent().addClass('success'); 64 | $(this).find('td > span.warning').parent().addClass('warning'); 65 | $(this).find('td > span.danger').parent().addClass('danger'); 66 | }); 67 | 68 | 69 | 70 | if($('div.fragment.well div.ttc').length > 0) 71 | { 72 | $('div.fragment.well div.line:first').parent().removeClass('fragment well'); 73 | } 74 | 75 | $('table.memberdecls').find('.memItemRight').each(function(){ 76 | $(this).contents().appendTo($(this).siblings('.memItemLeft')); 77 | $(this).siblings('.memItemLeft').attr('align', 'left'); 78 | }); 79 | 80 | $('table.memberdecls').find('.memTemplItemRight').each(function(){ 81 | $(this).contents().appendTo($(this).siblings('.memTemplItemLeft')); 82 | $(this).siblings('.memTemplItemLeft').attr('align', 'left'); 83 | }); 84 | 85 | function getOriginalWidthOfImg(img_element) { 86 | var t = new Image(); 87 | t.src = (img_element.getAttribute ? img_element.getAttribute("src") : false) || img_element.src; 88 | return t.width; 89 | } 90 | 91 | $('div.dyncontent').find('img').each(function(){ 92 | if(getOriginalWidthOfImg($(this)[0]) > $('#content>div.container').width()) 93 | $(this).css('width', '100%'); 94 | }); 95 | 96 | 97 | /* responsive search box */ 98 | $('#MSearchBox').parent().remove(); 99 | 100 | var nav_container = $('
'); 101 | $('#navrow1').parent().prepend(nav_container); 102 | 103 | var left_nav = $('
'); 104 | for (i = 0; i < 6; i++) { 105 | var navrow = $('#navrow' + i + ' > ul.tablist').detach(); 106 | left_nav.append(navrow); 107 | $('#navrow' + i).remove(); 108 | } 109 | var right_nav = $('
').append('\ 110 | '); 121 | $(nav_container).append(left_nav); 122 | $(nav_container).append(right_nav); 123 | 124 | $('#MSearchSelectWindow .SelectionMark').remove(); 125 | var search_selectors = $('#MSearchSelectWindow .SelectItem'); 126 | for (var i = 0; i < search_selectors.length; i += 1) { 127 | var element_a = $('').text($(search_selectors[i]).text()); 128 | 129 | element_a.click(function(){ 130 | $('#search-box .dropdown-menu li').removeClass('active'); 131 | $(this).parent().addClass('active'); 132 | searchBox.OnSelectItem($('#search-box li a').index(this)); 133 | searchBox.Search(); 134 | return false; 135 | }); 136 | 137 | var element = $('
  • ').append(element_a); 138 | $('#search-box .dropdown-menu').append(element); 139 | } 140 | $('#MSearchSelectWindow').remove(); 141 | 142 | $('#search-box .close').click(function (){ 143 | searchBox.CloseResultsWindow(); 144 | }); 145 | 146 | $('body').append('
    '); 147 | $('body').append('
    '); 148 | $('body').append('
    '); 149 | 150 | searchBox.searchLabel = ''; 151 | searchBox.DOMSearchField = function() { 152 | return document.getElementById("search-field"); 153 | } 154 | searchBox.DOMSearchClose = function(){ 155 | return document.getElementById("search-close"); 156 | } 157 | 158 | 159 | /* search results */ 160 | var results_iframe = $('#MSearchResults').detach(); 161 | $('#MSearchResultsWindow') 162 | .attr('id', 'search-results-window') 163 | .addClass('panel panel-default') 164 | .append( 165 | '
    \ 166 |

    Search Results

    \ 167 |
    \ 168 |
    ' 169 | ); 170 | $('#search-results-window .panel-body').append(results_iframe); 171 | 172 | searchBox.DOMPopupSearchResultsWindow = function() { 173 | return document.getElementById("search-results-window"); 174 | } 175 | 176 | function update_search_results_window() { 177 | $('#search-results-window').removeClass('panel-default panel-success panel-warning panel-danger') 178 | var status = $('#MSearchResults').contents().find('.SRStatus:visible'); 179 | if (status.length > 0) { 180 | switch(status.attr('id')) { 181 | case 'Loading': 182 | case 'Searching': 183 | $('#search-results-window').addClass('panel-warning'); 184 | break; 185 | case 'NoMatches': 186 | $('#search-results-window').addClass('panel-danger'); 187 | break; 188 | default: 189 | $('#search-results-window').addClass('panel-default'); 190 | } 191 | } else { 192 | $('#search-results-window').addClass('panel-success'); 193 | } 194 | } 195 | $('#MSearchResults').load(function() { 196 | $('#MSearchResults').contents().find('link[href="search.css"]').attr('href','../doxygen.css'); 197 | $('#MSearchResults').contents().find('head').append( 198 | ''); 199 | 200 | update_search_results_window(); 201 | 202 | // detect status changes (only for search with external search backend) 203 | var observer = new MutationObserver(function(mutations) { 204 | update_search_results_window(); 205 | }); 206 | var config = { attributes: true}; 207 | 208 | var targets = $('#MSearchResults').contents().find('.SRStatus'); 209 | for (i = 0; i < targets.length; i++) { 210 | observer.observe(targets[i], config); 211 | } 212 | }); 213 | 214 | 215 | /* enumerations */ 216 | $('table.fieldtable').removeClass('fieldtable').addClass('table table-striped table-bordered').each(function(){ 217 | $(this).prepend(''); 218 | $(this).find('tbody > tr:first').prependTo($(this).find('thead')); 219 | 220 | $(this).find('td > span.success').parent().addClass('success'); 221 | $(this).find('td > span.warning').parent().addClass('warning'); 222 | $(this).find('td > span.danger').parent().addClass('danger'); 223 | }); 224 | 225 | /* todo list */ 226 | var todoelements = $('.contents > .textblock > dl.reflist > dt, .contents > .textblock > dl.reflist > dd'); 227 | for (var i = 0; i < todoelements.length; i += 2) { 228 | $('.contents > .textblock').append( 229 | '
    ' 230 | + "
    " + $(todoelements[i]).html() + "
    " 231 | + "
    " + $(todoelements[i+1]).html() + "
    " 232 | + '
    '); 233 | } 234 | $('.contents > .textblock > dl').remove(); 235 | 236 | 237 | $(".memitem").removeClass('memitem'); 238 | $(".memproto").removeClass('memproto'); 239 | $(".memdoc").removeClass('memdoc'); 240 | $("span.mlabel").removeClass('mlabel'); 241 | $("table.memberdecls").removeClass('memberdecls'); 242 | $("[class^=memitem]").removeClass('memitem'); 243 | $("span.mlabels").removeClass('mlabels'); 244 | $("table.mlabels").removeClass('mlabels'); 245 | $("td.mlabels-right").removeClass('mlabels-right'); 246 | $(".navpath").removeClass('navpath'); 247 | $("li.navelem").removeClass('navelem'); 248 | $("a.el").removeClass('el'); 249 | $("div.ah").removeClass('ah'); 250 | $("div.header").removeClass("header"); 251 | 252 | $('.mdescLeft').each(function(){ 253 | if($(this).html()==" ") { 254 | $(this).siblings('.mdescRight').attr('colspan', 2); 255 | $(this).remove(); 256 | } 257 | }); 258 | $('td.memItemLeft').each(function(){ 259 | if($(this).siblings('.memItemRight').html()=="") { 260 | $(this).attr('colspan', 2); 261 | $(this).siblings('.memItemRight').remove(); 262 | } 263 | }); 264 | $('td.memTemplItemLeft').each(function(){ 265 | if($(this).siblings('.memTemplItemRight').html()=="") { 266 | $(this).attr('colspan', 2); 267 | $(this).siblings('.memTemplItemRight').remove(); 268 | } 269 | }); 270 | searchBox.CloseResultsWindow(); 271 | }); 272 | -------------------------------------------------------------------------------- /docs/doxygen/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /docs/doxygen/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | $projectname: $title 15 | $title 16 | 17 | 18 | $treeview 19 | $search 20 | $mathjax 21 | 22 | $extrastylesheet 23 | 24 | 25 | 26 | 27 | 28 | 29 | 36 |
    37 |
    38 |
    39 |
    40 |
    41 |
    42 | 43 | -------------------------------------------------------------------------------- /docs/doxygen/mainpage.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | \mainpage MPU Drive - API Reference 3 | 4 | 5 | 6 |
    7 | 8 | MPU Driver is a C++ library developed for ESP32 Microcontroller. 9 | It is meant to provide a complete interface for working with Invensense MPU chips. 10 | 11 | All classes, types, enums and functions are involved within the namespace `mpud` which stands for mpu-driver. 12 | 13 | The MPU class from `MPU.hpp` file contains lots of useful information. **See** mpud::MPU first, then mpud::types. 14 | 15 |
    16 | 17 |
    18 | 19 | Main Documentation can be acessed here 20 | and GitHub repository here. 21 | 22 | _Created by Natanael Josue Rabello. Copyright © 2017-2018._ 23 | 24 | */ -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | set SPHINXPROJ=MPUDriver 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/source/_static/MPUdriver.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natanaeljr/esp32-MPU-driver/c82b00502eb4c101a3f6b8134cd9b4a13f88e016/docs/source/_static/MPUdriver.jpg -------------------------------------------------------------------------------- /docs/source/_static/menuconfig_mpu-driver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natanaeljr/esp32-MPU-driver/c82b00502eb4c101a3f6b8134cd9b4a13f88e016/docs/source/_static/menuconfig_mpu-driver.png -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/stable/config 8 | 9 | import os 10 | import sys 11 | from subprocess import call 12 | 13 | # -- Path setup -------------------------------------------------------------- 14 | 15 | # If extensions (or modules to document with autodoc) are in another directory, 16 | # add these directories to sys.path here. If the directory is relative to the 17 | # documentation root, use os.path.abspath to make it absolute, like shown here. 18 | # 19 | 20 | sys.path.insert(0, os.path.abspath('.')) 21 | 22 | # Call Doxygen to get XML files from the header files 23 | print "Calling Doxygen to generate latest XML files" 24 | call('cd ../doxygen; doxygen', shell=True) 25 | 26 | # -- Project information ----------------------------------------------------- 27 | 28 | project = 'ESP32 MPU Driver' 29 | copyright = u'2018, Natanael Josué Rabello' 30 | author = 'Natanael Rabello' 31 | 32 | # The short X.Y version 33 | version = '1.0' 34 | # The full version, including alpha/beta/rc tags 35 | release = '1.0-beta' 36 | 37 | 38 | # -- General configuration --------------------------------------------------- 39 | 40 | # If your documentation needs a minimal Sphinx version, state it here. 41 | # 42 | # needs_sphinx = '1.0' 43 | 44 | # Add any Sphinx extension module names here, as strings. They can be 45 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 46 | # ones. 47 | extensions = [ 48 | 'breathe' 49 | ] 50 | 51 | # Breathe extension variables 52 | breathe_projects = { 'mpu-driver': '../doxygen/xml/' } 53 | breathe_default_project = 'mpu-driver' 54 | 55 | # Add any paths that contain templates here, relative to this directory. 56 | templates_path = ['_templates'] 57 | 58 | # The suffix(es) of source filenames. 59 | # You can specify multiple suffix as a list of string: 60 | # 61 | # source_suffix = ['.rst', '.md'] 62 | source_suffix = '.rst' 63 | 64 | # The master toctree document. 65 | master_doc = 'index' 66 | 67 | # The language for content autogenerated by Sphinx. Refer to documentation 68 | # for a list of supported languages. 69 | # 70 | # This is also used if you do content translation via gettext catalogs. 71 | # Usually you set "language" from the command line for these cases. 72 | language = None 73 | 74 | # List of patterns, relative to source directory, that match files and 75 | # directories to ignore when looking for source files. 76 | # This pattern also affects html_static_path and html_extra_path . 77 | exclude_patterns = [] 78 | 79 | # The name of the Pygments (syntax highlighting) style to use. 80 | pygments_style = 'sphinx' 81 | 82 | 83 | # -- Options for HTML output ------------------------------------------------- 84 | 85 | # The theme to use for HTML and HTML Help pages. See the documentation for 86 | # a list of builtin themes. 87 | # 88 | html_theme = 'default' 89 | 90 | # Theme options are theme-specific and customize the look and feel of a theme 91 | # further. For a list of options available for each theme, see the 92 | # documentation. 93 | # 94 | # html_theme_options = {} 95 | 96 | # Add any paths that contain custom static files (such as style sheets) here, 97 | # relative to this directory. They are copied after the builtin static files, 98 | # so a file named "default.css" will overwrite the builtin "default.css". 99 | html_static_path = ['_static'] 100 | 101 | # Custom sidebar templates, must be a dictionary that maps document names 102 | # to template names. 103 | # 104 | # The default sidebars (for documents that don't match any pattern) are 105 | # defined by theme itself. Builtin themes are using these templates by 106 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 107 | # 'searchbox.html']``. 108 | # 109 | # html_sidebars = {} 110 | 111 | 112 | # -- Options for HTMLHelp output --------------------------------------------- 113 | 114 | # Output file base name for HTML help builder. 115 | htmlhelp_basename = 'MPUDriverdoc' 116 | 117 | 118 | # -- Options for LaTeX output ------------------------------------------------ 119 | 120 | latex_elements = { 121 | # The paper size ('letterpaper' or 'a4paper'). 122 | # 123 | # 'papersize': 'letterpaper', 124 | 125 | # The font size ('10pt', '11pt' or '12pt'). 126 | # 127 | # 'pointsize': '10pt', 128 | 129 | # Additional stuff for the LaTeX preamble. 130 | # 131 | # 'preamble': '', 132 | 133 | # Latex figure (float) alignment 134 | # 135 | # 'figure_align': 'htbp', 136 | } 137 | 138 | # Grouping the document tree into LaTeX files. List of tuples 139 | # (source start file, target name, title, 140 | # author, documentclass [howto, manual, or own class]). 141 | latex_documents = [ 142 | (master_doc, 'MPUDriver.tex', 'MPU Driver Documentation', 143 | 'Natanael Rabello', 'manual'), 144 | ] 145 | 146 | 147 | # -- Options for manual page output ------------------------------------------ 148 | 149 | # One entry per manual page. List of tuples 150 | # (source start file, name, description, authors, manual section). 151 | man_pages = [ 152 | (master_doc, 'mpudriver', 'MPU Driver Documentation', 153 | [author], 1) 154 | ] 155 | 156 | 157 | # -- Options for Texinfo output ---------------------------------------------- 158 | 159 | # Grouping the document tree into Texinfo files. List of tuples 160 | # (source start file, target name, title, author, 161 | # dir menu entry, description, category) 162 | texinfo_documents = [ 163 | (master_doc, 'MPUDriver', 'MPU Driver Documentation', 164 | author, 'MPUDriver', 'One line description of project.', 165 | 'Miscellaneous'), 166 | ] 167 | 168 | 169 | # -- Use sphinx_rtd_theme for local builds -------------------------------- 170 | # ref. https://github.com/snide/sphinx_rtd_theme#using-this-theme-locally-then-building-on-read-the-docs 171 | # 172 | # on_rtd is whether we are on readthedocs.org 173 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 174 | 175 | if not on_rtd: # only import and set the theme if we're building docs locally 176 | import sphinx_rtd_theme 177 | html_theme = 'sphinx_rtd_theme' 178 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 179 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | MPU Driver - Documentation 3 | ========================== 4 | 5 | .. important:: 6 | 7 | This documentation is a work in progress. 8 | Improvements, suggestions and corrections are very welcome. 9 | 10 | Introduction 11 | ============ 12 | 13 | `MPU Driver` is a C++ library developed for ESP32 Microcontroller. 14 | It is meant to provide a complete interface for working with Invensense MPU chips. 15 | 16 | API Reference 17 | ============= 18 | 19 | |api_reference_link|. 20 | 21 | .. |api_reference_link| raw:: html 22 | 23 | Click Here 24 | 25 | .. * :ref:`genindex` 26 | .. * :ref:`search` 27 | 28 | Test Section 29 | ============ 30 | 31 | .. doxygentypedef:: MPU_t -------------------------------------------------------------------------------- /docs/source/requirements.txt: -------------------------------------------------------------------------------- 1 | # This is a list of python packages used to generate documentation. This file is used with pip: 2 | # pip install -r requirements.txt 3 | # 4 | sphinx >= 1.7.0 5 | sphinx-rtd-theme 6 | breathe >= 4.7.3 7 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | To run this examples, do not forget to add `I2Cbus/SPIbus` library path in the Makefile of each example. 4 | 5 | + **MPU-I2C**: 6 | Setup MPU through I2C for basic usage. 7 | 8 | + **MPU-SPI**: 9 | Setup MPU through SPI for basic usage. 10 | 11 | + **MPU-Real**: 12 | A more 'elaborated' example, shows how to: 13 | \- Use either SPI or I2C in the same code 14 | \- Use the MPU with interrupt signal 15 | \- Read sensor data from FIFO 16 | \- Perform Self-Test check 17 | \- Calibrate sensor data output using offset registers 18 | \- Calculate Tilt Angles 19 | -------------------------------------------------------------------------------- /examples/mpu_i2c/Makefile: -------------------------------------------------------------------------------- 1 | # Project Makefile # 2 | 3 | PROJECT_NAME := mpu-i2c 4 | 5 | # Path to MPU Driver 6 | EXTRA_COMPONENT_DIRS += $(abspath ../..) 7 | # Path to I2Cbus 8 | EXTRA_COMPONENT_DIRS += ${HOME}/esp/libraries/I2Cbus 9 | 10 | 11 | include $(IDF_PATH)/make/project.mk 12 | -------------------------------------------------------------------------------- /examples/mpu_i2c/main/component.mk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natanaeljr/esp32-MPU-driver/c82b00502eb4c101a3f6b8134cd9b4a13f88e016/examples/mpu_i2c/main/component.mk -------------------------------------------------------------------------------- /examples/mpu_i2c/main/mpu_i2c.cpp: -------------------------------------------------------------------------------- 1 | // ========================================================================= 2 | // Released under the MIT License 3 | // Copyright 2017-2018 Natanael Josue Rabello. All rights reserved. 4 | // For the license information refer to LICENSE file in root directory. 5 | // ========================================================================= 6 | 7 | /** 8 | * @file mpu_i2c.cpp 9 | * Example on how to setup MPU through I2C for basic usage. 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "driver/gpio.h" 17 | #include "driver/i2c.h" 18 | #include "esp_err.h" 19 | #include "esp_log.h" 20 | #include "freertos/FreeRTOS.h" 21 | #include "freertos/portmacro.h" 22 | #include "freertos/task.h" 23 | #include "sdkconfig.h" 24 | 25 | #include "I2Cbus.hpp" 26 | #include "MPU.hpp" 27 | #include "mpu/math.hpp" 28 | #include "mpu/types.hpp" 29 | 30 | static const char* TAG = "example"; 31 | 32 | static constexpr gpio_num_t SDA = GPIO_NUM_22; 33 | static constexpr gpio_num_t SCL = GPIO_NUM_23; 34 | static constexpr uint32_t CLOCK_SPEED = 400000; // range from 100 KHz ~ 400Hz 35 | 36 | extern "C" void app_main() { 37 | printf("$ MPU Driver Example: MPU-I2C\n"); 38 | fflush(stdout); 39 | 40 | // Initialize I2C on port 0 using I2Cbus interface 41 | i2c0.begin(SDA, SCL, CLOCK_SPEED); 42 | 43 | // Or directly with esp-idf API 44 | /* 45 | i2c_config_t conf; 46 | conf.mode = I2C_MODE_MASTER; 47 | conf.sda_io_num = SDA; 48 | conf.scl_io_num = SCL; 49 | conf.sda_pullup_en = GPIO_PULLUP_ENABLE; 50 | conf.scl_pullup_en = GPIO_PULLUP_ENABLE; 51 | conf.master.clk_speed = CLOCK_SPEED; 52 | ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &conf)); 53 | ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0)); 54 | */ 55 | 56 | MPU_t MPU; // create a default MPU object 57 | MPU.setBus(i2c0); // set bus port, not really needed since default is i2c0 58 | MPU.setAddr(mpud::MPU_I2CADDRESS_AD0_LOW); // set address, default is AD0_LOW 59 | 60 | // Great! Let's verify the communication 61 | // (this also check if the connected MPU supports the implementation of chip selected in the component menu) 62 | while (esp_err_t err = MPU.testConnection()) { 63 | ESP_LOGE(TAG, "Failed to connect to the MPU, error=%#X", err); 64 | vTaskDelay(1000 / portTICK_PERIOD_MS); 65 | } 66 | ESP_LOGI(TAG, "MPU connection successful!"); 67 | 68 | // Initialize 69 | ESP_ERROR_CHECK(MPU.initialize()); // initialize the chip and set initial configurations 70 | // Setup with your configurations 71 | // ESP_ERROR_CHECK(MPU.setSampleRate(50)); // set sample rate to 50 Hz 72 | // ESP_ERROR_CHECK(MPU.setGyroFullScale(mpud::GYRO_FS_500DPS)); 73 | // ESP_ERROR_CHECK(MPU.setAccelFullScale(mpud::ACCEL_FS_4G)); 74 | 75 | // Reading sensor data 76 | printf("Reading sensor data:\n"); 77 | mpud::raw_axes_t accelRaw; // x, y, z axes as int16 78 | mpud::raw_axes_t gyroRaw; // x, y, z axes as int16 79 | mpud::float_axes_t accelG; // accel axes in (g) gravity format 80 | mpud::float_axes_t gyroDPS; // gyro axes in (DPS) º/s format 81 | while (true) { 82 | // Read 83 | MPU.acceleration(&accelRaw); // fetch raw data from the registers 84 | MPU.rotation(&gyroRaw); // fetch raw data from the registers 85 | // MPU.motion(&accelRaw, &gyroRaw); // read both in one shot 86 | // Convert 87 | accelG = mpud::accelGravity(accelRaw, mpud::ACCEL_FS_4G); 88 | gyroDPS = mpud::gyroDegPerSec(gyroRaw, mpud::GYRO_FS_500DPS); 89 | // Debug 90 | printf("accel: [%+6.2f %+6.2f %+6.2f ] (G) \t", accelG.x, accelG.y, accelG.z); 91 | printf("gyro: [%+7.2f %+7.2f %+7.2f ] (º/s)\n", gyroDPS[0], gyroDPS[1], gyroDPS[2]); 92 | vTaskDelay(100 / portTICK_PERIOD_MS); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /examples/mpu_real/Makefile: -------------------------------------------------------------------------------- 1 | # Project Makefile # 2 | 3 | PROJECT_NAME := mpu-real 4 | 5 | # Path to MPU Driver 6 | EXTRA_COMPONENT_DIRS += $(abspath ../..) 7 | # Path to I2Cbus 8 | EXTRA_COMPONENT_DIRS += ${HOME}/esp/libraries/I2Cbus 9 | # Path to SPIbus 10 | EXTRA_COMPONENT_DIRS += ${HOME}/esp/libraries/SPIbus 11 | 12 | 13 | include $(IDF_PATH)/make/project.mk 14 | -------------------------------------------------------------------------------- /examples/mpu_real/main/component.mk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natanaeljr/esp32-MPU-driver/c82b00502eb4c101a3f6b8134cd9b4a13f88e016/examples/mpu_real/main/component.mk -------------------------------------------------------------------------------- /examples/mpu_real/main/mpu_real.cpp: -------------------------------------------------------------------------------- 1 | // ========================================================================= 2 | // Released under the MIT License 3 | // Copyright 2017-2018 Natanael Josue Rabello. All rights reserved. 4 | // For the license information refer to LICENSE file in root directory. 5 | // ========================================================================= 6 | 7 | /** 8 | * @file mpu_real.cpp 9 | * A more 'elaborated' example. Shows how to: 10 | * - Use either SPI or I2C in the same code 11 | * - Use the MPU with interrupt signal 12 | * - Read sensor data from FIFO 13 | * - Perform Self-Test check 14 | * - Calibrate sensor data output using offset registers 15 | * - Calculate Tilt Angles 16 | * 17 | * @note 18 | * To try this example: \n 19 | * Set the I2C/SPI pins in 'Bus configuration' and the interrupt pin in 'MPU configuration'. 20 | * 21 | * @todo Document the steps 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "driver/gpio.h" 30 | #include "driver/i2c.h" 31 | #include "driver/spi_master.h" 32 | #include "esp_err.h" 33 | #include "esp_log.h" 34 | #include "freertos/FreeRTOS.h" 35 | #include "freertos/portmacro.h" 36 | #include "freertos/task.h" 37 | #include "sdkconfig.h" 38 | 39 | #include "MPU.hpp" 40 | #include "mpu/math.hpp" 41 | #include "mpu/types.hpp" 42 | 43 | /* Bus configuration */ 44 | 45 | // This MACROS are defined in "skdconfig.h" and set through 'menuconfig'. 46 | // Can use to check which protocol has been selected. 47 | #if defined CONFIG_MPU_I2C 48 | #include "I2Cbus.hpp" 49 | static I2C_t& i2c = i2c0; // i2c0 or i2c1 50 | static constexpr gpio_num_t SDA = GPIO_NUM_14; 51 | static constexpr gpio_num_t SCL = GPIO_NUM_26; 52 | static constexpr uint32_t CLOCK_SPEED = 400000; // 400 KHz 53 | #elif defined CONFIG_MPU_SPI 54 | #include "SPIbus.hpp" 55 | static SPI_t& spi = hspi; // hspi or vspi 56 | static constexpr int MOSI = 22; 57 | static constexpr int MISO = 21; 58 | static constexpr int SCLK = 23; 59 | static constexpr int CS = 16; 60 | static constexpr uint32_t CLOCK_SPEED = 1000000; // 1MHz 61 | #endif 62 | 63 | /* MPU configuration */ 64 | 65 | static constexpr int kInterruptPin = 17; // GPIO_NUM 66 | static constexpr uint16_t kSampleRate = 250; // Hz 67 | static constexpr mpud::accel_fs_t kAccelFS = mpud::ACCEL_FS_4G; 68 | static constexpr mpud::gyro_fs_t kGyroFS = mpud::GYRO_FS_500DPS; 69 | static constexpr mpud::dlpf_t kDLPF = mpud::DLPF_98HZ; 70 | static constexpr mpud::int_config_t kInterruptConfig{ 71 | .level = mpud::INT_LVL_ACTIVE_HIGH, 72 | .drive = mpud::INT_DRV_PUSHPULL, 73 | .mode = mpud::INT_MODE_PULSE50US, 74 | .clear = mpud::INT_CLEAR_STATUS_REG // 75 | }; 76 | 77 | /*-*/ 78 | 79 | static const char* TAG = "example"; 80 | 81 | static void mpuISR(void*); 82 | static void mpuTask(void*); 83 | static void printTask(void*); 84 | 85 | // Main 86 | extern "C" void app_main() 87 | { 88 | printf("$ MPU Driver Example: Advanced\n"); 89 | fflush(stdout); 90 | // Initialize bus through either the Library API or esp-idf API 91 | #if defined CONFIG_MPU_I2C 92 | i2c.begin(SDA, SCL, CLOCK_SPEED); 93 | #elif defined CONFIG_MPU_SPI 94 | spi.begin(MOSI, MISO, SCLK); 95 | #endif 96 | // Create a task to setup mpu and read sensor data 97 | xTaskCreate(mpuTask, "mpuTask", 4 * 1024, nullptr, 6, nullptr); 98 | // Create a task to print angles 99 | xTaskCreate(printTask, "printTask", 2 * 1024, nullptr, 5, nullptr); 100 | } 101 | 102 | /* Tasks */ 103 | 104 | static MPU_t MPU; 105 | float roll{0}, pitch{0}, yaw{0}; 106 | 107 | static void mpuTask(void*) 108 | { 109 | // Let MPU know which bus and address to use 110 | #if defined CONFIG_MPU_I2C 111 | MPU.setBus(i2c); 112 | MPU.setAddr(mpud::MPU_I2CADDRESS_AD0_LOW); 113 | #elif defined CONFIG_MPU_SPI 114 | MPU.setBus(spi); 115 | spi_device_handle_t mpu_spi_handle; 116 | spi.addDevice(0, CLOCK_SPEED, CS, &mpu_spi_handle); 117 | MPU.setAddr(mpu_spi_handle); 118 | #endif 119 | 120 | // Verify connection 121 | while (esp_err_t err = MPU.testConnection()) { 122 | ESP_LOGE(TAG, "Failed to connect to the MPU, error=%#X", err); 123 | vTaskDelay(1000 / portTICK_PERIOD_MS); 124 | } 125 | ESP_LOGI(TAG, "MPU connection successful!"); 126 | 127 | // Initialize 128 | ESP_ERROR_CHECK(MPU.initialize()); 129 | 130 | // Self-Test 131 | mpud::selftest_t retSelfTest; 132 | while (esp_err_t err = MPU.selfTest(&retSelfTest)) { 133 | ESP_LOGE(TAG, "Failed to perform MPU Self-Test, error=%#X", err); 134 | vTaskDelay(1000 / portTICK_PERIOD_MS); 135 | } 136 | ESP_LOGI(TAG, "MPU Self-Test result: Gyro=%s Accel=%s", // 137 | (retSelfTest & mpud::SELF_TEST_GYRO_FAIL ? "FAIL" : "OK"), 138 | (retSelfTest & mpud::SELF_TEST_ACCEL_FAIL ? "FAIL" : "OK")); 139 | 140 | // Calibrate 141 | mpud::raw_axes_t accelBias, gyroBias; 142 | ESP_ERROR_CHECK(MPU.computeOffsets(&accelBias, &gyroBias)); 143 | ESP_ERROR_CHECK(MPU.setAccelOffset(accelBias)); 144 | ESP_ERROR_CHECK(MPU.setGyroOffset(gyroBias)); 145 | 146 | // Configure 147 | ESP_ERROR_CHECK(MPU.setAccelFullScale(kAccelFS)); 148 | ESP_ERROR_CHECK(MPU.setGyroFullScale(kGyroFS)); 149 | ESP_ERROR_CHECK(MPU.setSampleRate(kSampleRate)); 150 | ESP_ERROR_CHECK(MPU.setDigitalLowPassFilter(kDLPF)); 151 | 152 | // Setup FIFO 153 | ESP_ERROR_CHECK(MPU.setFIFOConfig(mpud::FIFO_CFG_ACCEL | mpud::FIFO_CFG_GYRO)); 154 | ESP_ERROR_CHECK(MPU.setFIFOEnabled(true)); 155 | constexpr uint16_t kFIFOPacketSize = 12; 156 | 157 | // Setup Interrupt 158 | constexpr gpio_config_t kGPIOConfig{ 159 | .pin_bit_mask = (uint64_t) 0x1 << kInterruptPin, 160 | .mode = GPIO_MODE_INPUT, 161 | .pull_up_en = GPIO_PULLUP_DISABLE, 162 | .pull_down_en = GPIO_PULLDOWN_ENABLE, 163 | .intr_type = GPIO_INTR_POSEDGE // 164 | }; 165 | gpio_config(&kGPIOConfig); 166 | gpio_install_isr_service(ESP_INTR_FLAG_IRAM); 167 | gpio_isr_handler_add((gpio_num_t) kInterruptPin, mpuISR, xTaskGetCurrentTaskHandle()); 168 | ESP_ERROR_CHECK(MPU.setInterruptConfig(kInterruptConfig)); 169 | ESP_ERROR_CHECK(MPU.setInterruptEnabled(mpud::INT_EN_RAWDATA_READY)); 170 | 171 | // Ready to start reading 172 | ESP_ERROR_CHECK(MPU.resetFIFO()); // start clean 173 | 174 | // Reading Loop 175 | while (true) { 176 | // Wait for notification from mpuISR 177 | uint32_t notificationValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY); 178 | if (notificationValue > 1) { 179 | ESP_LOGW(TAG, "Task Notification higher than 1, value: %d", notificationValue); 180 | MPU.resetFIFO(); 181 | continue; 182 | } 183 | // Check FIFO count 184 | uint16_t fifocount = MPU.getFIFOCount(); 185 | if (esp_err_t err = MPU.lastError()) { 186 | ESP_LOGE(TAG, "Error reading fifo count, %#X", err); 187 | MPU.resetFIFO(); 188 | continue; 189 | } 190 | if (fifocount > kFIFOPacketSize * 2) { 191 | if (!(fifocount % kFIFOPacketSize)) { 192 | ESP_LOGE(TAG, "Sample Rate too high!, not keeping up the pace!, count: %d", fifocount); 193 | } 194 | else { 195 | ESP_LOGE(TAG, "FIFO Count misaligned! Expected: %d, Actual: %d", kFIFOPacketSize, fifocount); 196 | } 197 | MPU.resetFIFO(); 198 | continue; 199 | } 200 | // Burst read data from FIFO 201 | uint8_t buffer[kFIFOPacketSize]; 202 | if (esp_err_t err = MPU.readFIFO(kFIFOPacketSize, buffer)) { 203 | ESP_LOGE(TAG, "Error reading sensor data, %#X", err); 204 | MPU.resetFIFO(); 205 | continue; 206 | } 207 | // Format 208 | mpud::raw_axes_t rawAccel, rawGyro; 209 | rawAccel.x = buffer[0] << 8 | buffer[1]; 210 | rawAccel.y = buffer[2] << 8 | buffer[3]; 211 | rawAccel.z = buffer[4] << 8 | buffer[5]; 212 | rawGyro.x = buffer[6] << 8 | buffer[7]; 213 | rawGyro.y = buffer[8] << 8 | buffer[9]; 214 | rawGyro.z = buffer[10] << 8 | buffer[11]; 215 | // Calculate tilt angle 216 | // range: (roll[-180,180] pitch[-90,90] yaw[-180,180]) 217 | constexpr double kRadToDeg = 57.2957795131; 218 | constexpr float kDeltaTime = 1.f / kSampleRate; 219 | float gyroRoll = roll + mpud::math::gyroDegPerSec(rawGyro.x, kGyroFS) * kDeltaTime; 220 | float gyroPitch = pitch + mpud::math::gyroDegPerSec(rawGyro.y, kGyroFS) * kDeltaTime; 221 | float gyroYaw = yaw + mpud::math::gyroDegPerSec(rawGyro.z, kGyroFS) * kDeltaTime; 222 | float accelRoll = atan2(-rawAccel.x, rawAccel.z) * kRadToDeg; 223 | float accelPitch = atan2(rawAccel.y, sqrt(rawAccel.x * rawAccel.x + rawAccel.z * rawAccel.z)) * kRadToDeg; 224 | // Fusion 225 | roll = gyroRoll * 0.95f + accelRoll * 0.05f; 226 | pitch = gyroPitch * 0.95f + accelPitch * 0.05f; 227 | yaw = gyroYaw; 228 | // correct yaw 229 | if (yaw > 180.f) 230 | yaw -= 360.f; 231 | else if (yaw < -180.f) 232 | yaw += 360.f; 233 | } 234 | vTaskDelete(nullptr); 235 | } 236 | 237 | static void printTask(void*) 238 | { 239 | vTaskDelay(2000 / portTICK_PERIOD_MS); 240 | while (true) { 241 | printf("Pitch: %+6.1f \t Roll: %+6.1f \t Yaw: %+6.1f \n", pitch, roll, yaw); 242 | vTaskDelay(50 / portTICK_PERIOD_MS); 243 | } 244 | } 245 | 246 | static IRAM_ATTR void mpuISR(TaskHandle_t taskHandle) 247 | { 248 | BaseType_t HPTaskWoken = pdFALSE; 249 | vTaskNotifyGiveFromISR(taskHandle, &HPTaskWoken); 250 | if (HPTaskWoken == pdTRUE) portYIELD_FROM_ISR(); 251 | } 252 | -------------------------------------------------------------------------------- /examples/mpu_spi/Makefile: -------------------------------------------------------------------------------- 1 | # Project Makefile # 2 | 3 | PROJECT_NAME := mpu-spi 4 | 5 | # Path to MPU Driver 6 | EXTRA_COMPONENT_DIRS += $(abspath ../..) 7 | # Path to SPIbus 8 | EXTRA_COMPONENT_DIRS += ${HOME}/esp/libraries/SPIbus 9 | 10 | 11 | include $(IDF_PATH)/make/project.mk 12 | -------------------------------------------------------------------------------- /examples/mpu_spi/main/component.mk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natanaeljr/esp32-MPU-driver/c82b00502eb4c101a3f6b8134cd9b4a13f88e016/examples/mpu_spi/main/component.mk -------------------------------------------------------------------------------- /examples/mpu_spi/main/mpu_spi.cpp: -------------------------------------------------------------------------------- 1 | // ========================================================================= 2 | // Released under the MIT License 3 | // Copyright 2017-2018 Natanael Josue Rabello. All rights reserved. 4 | // For the license information refer to LICENSE file in root directory. 5 | // ========================================================================= 6 | 7 | /** 8 | * @file mpu_spi.cpp 9 | * Example on how to setup MPU through SPI for basic usage. 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "driver/gpio.h" 17 | #include "driver/spi_master.h" 18 | #include "esp_err.h" 19 | #include "esp_log.h" 20 | #include "freertos/FreeRTOS.h" 21 | #include "freertos/portmacro.h" 22 | #include "freertos/task.h" 23 | #include "sdkconfig.h" 24 | 25 | #include "MPU.hpp" 26 | #include "SPIbus.hpp" 27 | #include "mpu/math.hpp" 28 | #include "mpu/types.hpp" 29 | 30 | static const char* TAG = "example"; 31 | 32 | static constexpr int MOSI = 22; 33 | static constexpr int MISO = 21; 34 | static constexpr int SCLK = 23; 35 | static constexpr int CS = 16; 36 | static constexpr uint32_t CLOCK_SPEED = 1000000; // up to 1MHz for all registers, and 20MHz for sensor data registers only 37 | 38 | extern "C" void app_main() { 39 | printf("$ MPU Driver Example: MPU-SPI\n"); 40 | fflush(stdout); 41 | 42 | spi_device_handle_t mpu_spi_handle; 43 | // Initialize SPI on HSPI host through SPIbus interface: 44 | hspi.begin(MOSI, MISO, SCLK); 45 | hspi.addDevice(0, CLOCK_SPEED, CS, &mpu_spi_handle); 46 | 47 | // Or directly with esp-idf API: 48 | /* 49 | spi_bus_config_t spi_config; 50 | spi_config.mosi_io_num = MOSI; 51 | spi_config.miso_io_num = MISO; 52 | spi_config.sclk_io_num = SCLK; 53 | spi_config.quadwp_io_num = -1; 54 | spi_config.quadhd_io_num = -1; 55 | spi_config.max_transfer_sz = SPI_MAX_DMA_LEN; 56 | ESP_ERROR_CHECK(spi_bus_initialize(HSPI_HOST, &spi_config, 0)); 57 | spi_device_interface_config_t dev_config; 58 | dev_config.command_bits = 0; 59 | dev_config.address_bits = 8; 60 | dev_config.dummy_bits = 0; 61 | dev_config.mode = 0; 62 | dev_config.duty_cycle_pos = 128; 63 | dev_config.cs_ena_pretrans = 0; 64 | dev_config.cs_ena_posttrans = 0; 65 | dev_config.clock_speed_hz = CLOCK_SPEED; 66 | dev_config.spics_io_num = CS; 67 | dev_config.flags = 0; 68 | dev_config.queue_size = 1; 69 | dev_config.pre_cb = NULL; 70 | dev_config.post_cb = NULL; 71 | ESP_ERROR_CHECK(spi_bus_add_device(HSPI_HOST, &dev_config, &mpu_spi_handle)); 72 | */ 73 | 74 | MPU_t MPU; // create a default MPU object 75 | MPU.setBus(hspi); // set bus port, not really needed here since default is HSPI 76 | MPU.setAddr(mpu_spi_handle); // set spi_device_handle, always needed! 77 | 78 | // Great! Let's verify the communication 79 | // (this also check if the connected MPU supports the implementation of chip selected in the component menu) 80 | while (esp_err_t err = MPU.testConnection()) { 81 | ESP_LOGE(TAG, "Failed to connect to the MPU, error=%#X", err); 82 | vTaskDelay(1000 / portTICK_PERIOD_MS); 83 | } 84 | ESP_LOGI(TAG, "MPU connection successful!"); 85 | 86 | // Initialize 87 | ESP_ERROR_CHECK(MPU.initialize()); // initialize the chip and set initial configurations 88 | // Setup with your configurations 89 | // ESP_ERROR_CHECK(MPU.setSampleRate(50)); // set sample rate to 50 Hz 90 | // ESP_ERROR_CHECK(MPU.setGyroFullScale(mpud::GYRO_FS_500DPS)); 91 | // ESP_ERROR_CHECK(MPU.setAccelFullScale(mpud::ACCEL_FS_4G)); 92 | 93 | // Reading sensor data 94 | printf("Reading sensor data:\n"); 95 | mpud::raw_axes_t accelRaw; // x, y, z axes as int16 96 | mpud::raw_axes_t gyroRaw; // x, y, z axes as int16 97 | mpud::float_axes_t accelG; // accel axes in (g) gravity format 98 | mpud::float_axes_t gyroDPS; // gyro axes in (DPS) º/s format 99 | while (true) { 100 | // Read 101 | MPU.acceleration(&accelRaw); // fetch raw data from the registers 102 | MPU.rotation(&gyroRaw); // fetch raw data from the registers 103 | // MPU.motion(&accelRaw, &gyroRaw); // read both in one shot 104 | // Convert 105 | accelG = mpud::accelGravity(accelRaw, mpud::ACCEL_FS_4G); 106 | gyroDPS = mpud::gyroDegPerSec(gyroRaw, mpud::GYRO_FS_500DPS); 107 | // Debug 108 | printf("accel: [%+6.2f %+6.2f %+6.2f ] (G) \t", accelG.x, accelG.y, accelG.z); 109 | printf("gyro: [%+7.2f %+7.2f %+7.2f ] (º/s)\n", gyroDPS[0], gyroDPS[1], gyroDPS[2]); 110 | vTaskDelay(100 / portTICK_PERIOD_MS); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /include/MPU.hpp: -------------------------------------------------------------------------------- 1 | // ========================================================================= 2 | // This library is placed under the MIT License 3 | // Copyright 2017-2018 Natanael Josue Rabello. All rights reserved. 4 | // For the license information refer to LICENSE file in root directory. 5 | // ========================================================================= 6 | 7 | /** 8 | * @file MPU.hpp 9 | * MPU library main file. Declare MPU class. 10 | * 11 | * @attention 12 | * MPU library requires I2Cbus or SPIbus library. 13 | * Select the communication protocol in `menuconfig` 14 | * and include the corresponding library to project components. 15 | * 16 | * @note 17 | * The following is taken in the code: 18 | * - MPU9250 is the same as MPU6500 + AK8963 19 | * - MPU9150 is the same as MPU6050 + AK8975 20 | * - MPU6000 code equals MPU6050 21 | * - MPU6555 code equals MPU6500 22 | * - MPU9255 code equals MPU9250 23 | * */ 24 | 25 | #ifndef _MPU_HPP_ 26 | #define _MPU_HPP_ 27 | 28 | #include 29 | #include "esp_err.h" 30 | #include "sdkconfig.h" 31 | 32 | #ifdef CONFIG_MPU_I2C 33 | #if !defined I2CBUS_COMPONENT_TRUE 34 | #error ''MPU component requires I2Cbus library. \ 35 | Make sure the I2Cbus library is included in your components directory. \ 36 | See MPUs README.md for more information.'' 37 | #endif 38 | 39 | #include "I2Cbus.hpp" 40 | 41 | #elif CONFIG_MPU_SPI 42 | #if !defined SPIBUS_COMPONENT_TRUE 43 | #error ''MPU component requires SPIbus library. \ 44 | Make sure the SPIbus library is included in your components directory. \ 45 | See MPUs README.md for more information.'' 46 | #endif 47 | #include "SPIbus.hpp" 48 | #else 49 | #error ''MPU communication protocol not specified'' 50 | #endif 51 | 52 | #include "mpu/types.hpp" 53 | 54 | /*! MPU Driver namespace */ 55 | namespace mpud 56 | { 57 | class MPU; 58 | } 59 | 60 | /*! Easy alias for MPU class */ 61 | typedef mpud::MPU MPU_t; 62 | 63 | namespace mpud 64 | { 65 | /*! Motion Processing Unit */ 66 | class MPU 67 | { 68 | public: 69 | //! \name Constructors / Destructor 70 | //! \{ 71 | MPU(); 72 | explicit MPU(mpu_bus_t& bus); 73 | MPU(mpu_bus_t& bus, mpu_addr_handle_t addr); 74 | ~MPU(); 75 | //! \} 76 | //! \name Basic 77 | //! \{ 78 | MPU& setBus(mpu_bus_t& bus); 79 | MPU& setAddr(mpu_addr_handle_t addr); 80 | mpu_bus_t& getBus(); 81 | mpu_addr_handle_t getAddr(); 82 | esp_err_t lastError(); 83 | //! \} 84 | //! \name Setup 85 | //! \{ 86 | esp_err_t initialize(); 87 | esp_err_t reset(); 88 | esp_err_t setSleep(bool enable); 89 | esp_err_t testConnection(); 90 | esp_err_t selfTest(selftest_t* result); 91 | esp_err_t resetSignalPath(); 92 | uint8_t whoAmI(); 93 | bool getSleep(); 94 | //! \} 95 | //! \name Main configurations 96 | //! \{ 97 | esp_err_t setSampleRate(uint16_t rate); 98 | esp_err_t setClockSource(clock_src_t clockSrc); 99 | esp_err_t setDigitalLowPassFilter(dlpf_t dlpf); 100 | uint16_t getSampleRate(); 101 | clock_src_t getClockSource(); 102 | dlpf_t getDigitalLowPassFilter(); 103 | //! \} 104 | //! \name Power management 105 | //! \{ 106 | esp_err_t setLowPowerAccelMode(bool enable); 107 | esp_err_t setLowPowerAccelRate(lp_accel_rate_t rate); 108 | lp_accel_rate_t getLowPowerAccelRate(); 109 | bool getLowPowerAccelMode(); 110 | esp_err_t setStandbyMode(stby_en_t mask); 111 | stby_en_t getStandbyMode(); 112 | //! \} 113 | //! \name Full-Scale Range 114 | //! \{ 115 | esp_err_t setGyroFullScale(gyro_fs_t fsr); 116 | esp_err_t setAccelFullScale(accel_fs_t fsr); 117 | gyro_fs_t getGyroFullScale(); 118 | accel_fs_t getAccelFullScale(); 119 | //! \} 120 | //! \name Offset / Bias 121 | //! \{ 122 | esp_err_t setGyroOffset(raw_axes_t bias); 123 | esp_err_t setAccelOffset(raw_axes_t bias); 124 | raw_axes_t getGyroOffset(); 125 | raw_axes_t getAccelOffset(); 126 | esp_err_t computeOffsets(raw_axes_t* accel, raw_axes_t* gyro); 127 | //! \} 128 | //! \name Interrupt 129 | //! \{ 130 | esp_err_t setInterruptConfig(int_config_t config); 131 | esp_err_t setInterruptEnabled(int_en_t mask); 132 | int_stat_t getInterruptStatus(); 133 | int_config_t getInterruptConfig(); 134 | int_en_t getInterruptEnabled(); 135 | //! \} 136 | //! \name FIFO 137 | //! \{ 138 | esp_err_t setFIFOMode(fifo_mode_t mode); 139 | esp_err_t setFIFOConfig(fifo_config_t config); 140 | esp_err_t setFIFOEnabled(bool enable); 141 | esp_err_t resetFIFO(); 142 | uint16_t getFIFOCount(); 143 | esp_err_t readFIFO(size_t length, uint8_t* data); 144 | esp_err_t writeFIFO(size_t length, const uint8_t* data); 145 | fifo_mode_t getFIFOMode(); 146 | fifo_config_t getFIFOConfig(); 147 | bool getFIFOEnabled(); 148 | //! \} 149 | //! \name Auxiliary I2C Master 150 | //! \{ 151 | esp_err_t setAuxI2CConfig(const auxi2c_config_t& config); 152 | esp_err_t setAuxI2CEnabled(bool enable); 153 | esp_err_t setAuxI2CSlaveConfig(const auxi2c_slv_config_t& config); 154 | esp_err_t setAuxI2CSlaveEnabled(auxi2c_slv_t slave, bool enable); 155 | esp_err_t setAuxI2CBypass(bool enable); 156 | esp_err_t readAuxI2CRxData(size_t length, uint8_t* data, size_t skip = 0); 157 | esp_err_t restartAuxI2C(); 158 | auxi2c_stat_t getAuxI2CStatus(); 159 | auxi2c_config_t getAuxI2CConfig(); 160 | auxi2c_slv_config_t getAuxI2CSlaveConfig(auxi2c_slv_t slave); 161 | bool getAuxI2CEnabled(); 162 | bool getAuxI2CSlaveEnabled(auxi2c_slv_t slave); 163 | bool getAuxI2CBypass(); 164 | esp_err_t auxI2CWriteByte(uint8_t devAddr, uint8_t regAddr, uint8_t data); 165 | esp_err_t auxI2CReadByte(uint8_t devAddr, uint8_t regAddr, uint8_t* data); 166 | //! \} 167 | //! \name Motion Detection Interrupt 168 | //! \{ 169 | esp_err_t setMotionDetectConfig(mot_config_t& config); 170 | mot_config_t getMotionDetectConfig(); 171 | esp_err_t setMotionFeatureEnabled(bool enable); 172 | bool getMotionFeatureEnabled(); 173 | #if defined CONFIG_MPU6000 || defined CONFIG_MPU6050 || defined CONFIG_MPU9150 174 | esp_err_t setZeroMotionConfig(zrmot_config_t& config); 175 | zrmot_config_t getZeroMotionConfig(); 176 | esp_err_t setFreeFallConfig(ff_config_t& config); 177 | ff_config_t getFreeFallConfig(); 178 | mot_stat_t getMotionDetectStatus(); 179 | #endif 180 | //! \} 181 | //! \name Compass | Magnetometer 182 | //! \{ 183 | #if defined CONFIG_MPU_AK89xx 184 | esp_err_t compassInit(); 185 | esp_err_t compassTestConnection(); 186 | esp_err_t compassSetMode(mag_mode_t mode); 187 | esp_err_t compassGetAdjustment(uint8_t* x, uint8_t* y, uint8_t* z); 188 | mag_mode_t compassGetMode(); 189 | uint8_t compassWhoAmI(); 190 | uint8_t compassGetInfo(); 191 | esp_err_t compassReadByte(uint8_t regAddr, uint8_t* data); 192 | esp_err_t compassWriteByte(uint8_t regAddr, uint8_t data); 193 | bool compassSelfTest(raw_axes_t* result = nullptr); 194 | #endif 195 | #if defined CONFIG_MPU_AK8963 196 | esp_err_t compassReset(); 197 | esp_err_t compassSetSensitivity(mag_sensy_t sensy); 198 | mag_sensy_t compassGetSensitivity(); 199 | #endif 200 | //! \} 201 | //! \name Miscellaneous 202 | //! \{ 203 | esp_err_t setFsyncConfig(int_lvl_t level); 204 | esp_err_t setFsyncEnabled(bool enable); 205 | int_lvl_t getFsyncConfig(); 206 | bool getFsyncEnabled(); 207 | #if defined CONFIG_MPU6500 || defined CONFIG_MPU9250 208 | esp_err_t setFchoice(fchoice_t fchoice); 209 | fchoice_t getFchoice(); 210 | #endif 211 | #if defined CONFIG_MPU9150 || (defined CONFIG_MPU6050 && !defined CONFIG_MPU6000) 212 | esp_err_t setAuxVDDIOLevel(auxvddio_lvl_t level); 213 | auxvddio_lvl_t getAuxVDDIOLevel(); 214 | #endif 215 | //! \} 216 | //! \name Read / Write 217 | //! Functions to perform direct read or write operation(s) to registers. 218 | //! \{ 219 | esp_err_t readBit(uint8_t regAddr, uint8_t bitNum, uint8_t* data); 220 | esp_err_t readBits(uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t* data); 221 | esp_err_t readByte(uint8_t regAddr, uint8_t* data); 222 | esp_err_t readBytes(uint8_t regAddr, size_t length, uint8_t* data); 223 | esp_err_t writeBit(uint8_t regAddr, uint8_t bitNum, uint8_t data); 224 | esp_err_t writeBits(uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data); 225 | esp_err_t writeByte(uint8_t regAddr, uint8_t data); 226 | esp_err_t writeBytes(uint8_t regAddr, size_t length, const uint8_t* data); 227 | esp_err_t registerDump(uint8_t start = 0x0, uint8_t end = 0x7F); 228 | //! \} 229 | //! \name Sensor readings 230 | //! \{ 231 | esp_err_t acceleration(raw_axes_t* accel); 232 | esp_err_t acceleration(int16_t* x, int16_t* y, int16_t* z); 233 | esp_err_t rotation(raw_axes_t* gyro); 234 | esp_err_t rotation(int16_t* x, int16_t* y, int16_t* z); 235 | esp_err_t temperature(int16_t* temp); 236 | esp_err_t motion(raw_axes_t* accel, raw_axes_t* gyro); 237 | #if defined CONFIG_MPU_AK89xx 238 | esp_err_t heading(raw_axes_t* mag); 239 | esp_err_t heading(int16_t* x, int16_t* y, int16_t* z); 240 | esp_err_t motion(raw_axes_t* accel, raw_axes_t* gyro, raw_axes_t* mag); 241 | #endif 242 | esp_err_t sensors(raw_axes_t* accel, raw_axes_t* gyro, int16_t* temp); 243 | esp_err_t sensors(sensors_t* sensors, size_t extsens_len = 0); 244 | //! \} 245 | 246 | protected: 247 | esp_err_t accelSelfTest(raw_axes_t& regularBias, raw_axes_t& selfTestBias, uint8_t* result); 248 | esp_err_t gyroSelfTest(raw_axes_t& regularBias, raw_axes_t& selfTestBias, uint8_t* result); 249 | esp_err_t getBiases(accel_fs_t accelFS, gyro_fs_t gyroFS, raw_axes_t* accelBias, raw_axes_t* gyroBias, 250 | bool selftest); 251 | 252 | mpu_bus_t* bus; /*!< Communication bus pointer, I2C / SPI */ 253 | mpu_addr_handle_t addr; /*!< I2C address / SPI device handle */ 254 | uint8_t buffer[16]; /*!< Commom buffer for temporary data */ 255 | esp_err_t err; /*!< Holds last error code */ 256 | }; 257 | 258 | } // namespace mpud 259 | 260 | // ============== 261 | // Inline methods 262 | // ============== 263 | namespace mpud 264 | { 265 | /*! Default Constructor. */ 266 | inline MPU::MPU() : MPU(MPU_DEFAULT_BUS){}; 267 | /** 268 | * @brief Contruct a MPU in the given communication bus. 269 | * @param bus Bus protocol object of type `I2Cbus` or `SPIbus`. 270 | */ 271 | inline MPU::MPU(mpu_bus_t& bus) : MPU(bus, MPU_DEFAULT_ADDR_HANDLE) {} 272 | /** 273 | * @brief Construct a MPU in the given communication bus and address. 274 | * @param bus Bus protocol object of type `I2Cbus` or `SPIbus`. 275 | * @param addr I2C address (`mpu_i2caddr_t`) or SPI device handle (`spi_device_handle_t`). 276 | */ 277 | inline MPU::MPU(mpu_bus_t& bus, mpu_addr_handle_t addr) : bus{&bus}, addr{addr}, buffer{0}, err{ESP_OK} {} 278 | /** Default Destructor, does nothing. */ 279 | inline MPU::~MPU() = default; 280 | /** 281 | * @brief Set communication bus. 282 | * @param bus Bus protocol object of type `I2Cbus` or `SPIbus`. 283 | */ 284 | inline MPU& MPU::setBus(mpu_bus_t& bus) 285 | { 286 | this->bus = &bus; 287 | return *this; 288 | } 289 | /** 290 | * @brief Return communication bus object. 291 | */ 292 | inline mpu_bus_t& MPU::getBus() 293 | { 294 | return *bus; 295 | } 296 | /** 297 | * @brief Set I2C address or SPI device handle. 298 | * @param addr I2C address (`mpu_i2caddr_t`) or SPI device handle (`spi_device_handle_t`). 299 | */ 300 | inline MPU& MPU::setAddr(mpu_addr_handle_t addr) 301 | { 302 | this->addr = addr; 303 | return *this; 304 | } 305 | /** 306 | * @brief Return I2C address or SPI device handle. 307 | */ 308 | inline mpu_addr_handle_t MPU::getAddr() 309 | { 310 | return addr; 311 | } 312 | /*! Return last error code. */ 313 | inline esp_err_t MPU::lastError() 314 | { 315 | return err; 316 | } 317 | /*! Read a single bit from a register*/ 318 | inline esp_err_t MPU::readBit(uint8_t regAddr, uint8_t bitNum, uint8_t* data) 319 | { 320 | return err = bus->readBit(addr, regAddr, bitNum, data); 321 | } 322 | /*! Read a range of bits from a register */ 323 | inline esp_err_t MPU::readBits(uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t* data) 324 | { 325 | return err = bus->readBits(addr, regAddr, bitStart, length, data); 326 | } 327 | /*! Read a single register */ 328 | inline esp_err_t MPU::readByte(uint8_t regAddr, uint8_t* data) 329 | { 330 | return err = bus->readByte(addr, regAddr, data); 331 | } 332 | /*! Read data from sequence of registers */ 333 | inline esp_err_t MPU::readBytes(uint8_t regAddr, size_t length, uint8_t* data) 334 | { 335 | return err = bus->readBytes(addr, regAddr, length, data); 336 | } 337 | /*! Write a single bit to a register */ 338 | inline esp_err_t MPU::writeBit(uint8_t regAddr, uint8_t bitNum, uint8_t data) 339 | { 340 | return err = bus->writeBit(addr, regAddr, bitNum, data); 341 | } 342 | /*! Write a range of bits to a register */ 343 | inline esp_err_t MPU::writeBits(uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data) 344 | { 345 | return err = bus->writeBits(addr, regAddr, bitStart, length, data); 346 | } 347 | /*! Write a value to a register */ 348 | inline esp_err_t MPU::writeByte(uint8_t regAddr, uint8_t data) 349 | { 350 | return err = bus->writeByte(addr, regAddr, data); 351 | } 352 | /*! Write a sequence to data to a sequence of registers */ 353 | inline esp_err_t MPU::writeBytes(uint8_t regAddr, size_t length, const uint8_t* data) 354 | { 355 | return err = bus->writeBytes(addr, regAddr, length, data); 356 | } 357 | 358 | } // namespace mpud 359 | 360 | #endif /* end of include guard: _MPU_HPP_ */ 361 | -------------------------------------------------------------------------------- /include/MPUdmp.hpp: -------------------------------------------------------------------------------- 1 | // ========================================================================= 2 | // This library is placed under the MIT License 3 | // Copyright 2017-2018 Natanael Josue Rabello. All rights reserved. 4 | // For the license information refer to LICENSE file in root directory. 5 | // ========================================================================= 6 | 7 | /** 8 | * @file MPUdmp.hpp 9 | * Declare MPU class with DMP interface. 10 | */ 11 | 12 | #ifndef _MPU_DMP_HPP_ 13 | #define _MPU_DMP_HPP_ 14 | 15 | #include "MPU.hpp" 16 | 17 | #if !defined CONFIG_MPU_ENABLE_DMP 18 | #warning ''You must enable the option DMP in \ 19 | menuconfig -> components -> MPU driver, to compile the DMP source code'' 20 | #endif 21 | 22 | /*! MPU Driver namespace */ 23 | namespace mpud 24 | { 25 | /*! DMP namespace */ 26 | namespace dmp 27 | { 28 | /*! MPU with DMP interface */ 29 | class MPUdmp : public mpud::MPU 30 | { 31 | }; 32 | 33 | typedef MPUdmp MPUdmp_t; 34 | 35 | } // namespace dmp 36 | 37 | } // namespace mpud 38 | 39 | #endif /* end of include guard: _MPU_DMP_HPP_ */ 40 | -------------------------------------------------------------------------------- /include/mpu/log.hpp: -------------------------------------------------------------------------------- 1 | // ========================================================================= 2 | // This library is placed under the MIT License 3 | // Copyright 2017-2018 Natanael Josue Rabello. All rights reserved. 4 | // For the license information refer to LICENSE file in root directory. 5 | // ========================================================================= 6 | 7 | /** 8 | * @file mpu/log.hpp 9 | * @brief MPU Log System 10 | * 11 | * @attention 12 | * This header is intended to be used ONLY inside the library itself 13 | * Do not include this header in your application. 14 | * */ 15 | 16 | #ifndef _MPU_LOG_HPP_ 17 | #define _MPU_LOG_HPP_ 18 | 19 | #include "esp_err.h" 20 | #include "esp_log.h" 21 | #include "sdkconfig.h" 22 | 23 | // Note: declare TAG before include this header 24 | // Note: include only in .cpp files from this library 25 | 26 | #define MPU_LOGE(format, ...) if (CONFIG_MPU_LOG_LEVEL >= ESP_LOG_ERROR) { ESP_LOGE(TAG, format, ##__VA_ARGS__); } 27 | #define MPU_LOGW(format, ...) if (CONFIG_MPU_LOG_LEVEL >= ESP_LOG_WARN) { ESP_LOGW(TAG, format, ##__VA_ARGS__); } 28 | #define MPU_LOGI(format, ...) if (CONFIG_MPU_LOG_LEVEL >= ESP_LOG_INFO) { ESP_LOGI(TAG, format, ##__VA_ARGS__); } 29 | #define MPU_LOGD(format, ...) if (CONFIG_MPU_LOG_LEVEL >= ESP_LOG_DEBUG) { ESP_LOGD(TAG, format, ##__VA_ARGS__); } 30 | #define MPU_LOGV(format, ...) if (CONFIG_MPU_LOG_LEVEL >= ESP_LOG_VERBOSE) { ESP_LOGV(TAG, format, ##__VA_ARGS__); } 31 | 32 | #define MPU_LOGEMSG(msg, format, ...) MPU_LOGE("%s()-> %s" format, __FUNCTION__, msg, ##__VA_ARGS__) 33 | #define MPU_LOGWMSG(msg, format, ...) MPU_LOGW("%s()-> %s" format, __FUNCTION__, msg, ##__VA_ARGS__) 34 | #define MPU_LOGIMSG(msg, format, ...) MPU_LOGI("%s()-> %s" format, __FUNCTION__, msg, ##__VA_ARGS__) 35 | #define MPU_LOGDMSG(msg, format, ...) MPU_LOGD("%s()-> %s" format, __FUNCTION__, msg, ##__VA_ARGS__) 36 | #define MPU_LOGVMSG(msg, format, ...) MPU_LOGV("%s()-> %s" format, __FUNCTION__, msg, ##__VA_ARGS__) 37 | 38 | #ifdef CONFIG_MPU_LOG_ERROR_TRACES 39 | #define MPU_ERR_CHECK(x) mpud::log::errorCheckLogger(x, __ASSERT_FUNC, __LINE__, #x) 40 | #else 41 | #define MPU_ERR_CHECK(x) (x) 42 | #endif 43 | 44 | /*! MPU Driver namespace */ 45 | namespace mpud 46 | { 47 | /*! Log namespace */ 48 | inline namespace log 49 | { 50 | /*! Messages namespace */ 51 | namespace msgs 52 | { 53 | static const char INVALID_ARG[] = "Invalid Argument"; 54 | static const char INVALID_STATE[] = "Invalid State"; 55 | static const char INVALID_LENGTH[] = "Invalid length"; 56 | static const char INVALID_FIFO_RATE[] = "Invalid FIFO rate"; 57 | static const char INVALID_SAMPLE_RATE[] = "Invalid Sample rate"; 58 | static const char INVALID_TAP_THRESH[] = "Invalid Tap threshold"; 59 | static const char DMP_LOAD_FAIL[] = "Failed to load DMP firmware"; 60 | static const char DMP_NOT_LOADED[] = "DMP firmware has not been loaded"; 61 | static const char UNKNOWN_DMP_CFG_STATE[] = "Unknown DMP config state"; 62 | static const char NO_AXIS_PASSED[] = "No Axis passed"; 63 | static const char BANK_BOUNDARIES[] = "Bank boundaries overpass"; 64 | static const char FIFO_CORRUPTION[] = "FIFO Corruption. Quaternion data outside of the acceptable threshold"; 65 | static const char AUX_I2C_DISABLED[] = "Auxiliary I2C is disabled"; 66 | static const char AUX_I2C_SLAVE_NACK[] = "Auxiliary I2C Slave NACK"; 67 | static const char AUX_I2C_LOST_ARB[] = "Auxiliary I2C Master loose abitraion of the bus"; 68 | static const char COMPASS_DISABLED[] = "Compass is disabled"; 69 | static const char NOT_SUPPORTED[] = "Not supported"; 70 | static const char TIMEOUT[] = "Timeout"; 71 | static const char EMPTY[] = ""; 72 | 73 | } // namespace msgs 74 | 75 | static inline esp_err_t errorCheckLogger(esp_err_t x, const char* func, const int line, const char* expr) 76 | { 77 | if (x) MPU_LOGE("func:%s @ line:%d, expr:\"%s\", error:0x%X ", func, line, expr, x); 78 | return x; 79 | } 80 | 81 | } // namespace log 82 | 83 | } // namespace mpud 84 | 85 | #endif /* end of include guard: _MPU_LOG_HPP_ */ 86 | -------------------------------------------------------------------------------- /include/mpu/math.hpp: -------------------------------------------------------------------------------- 1 | // ========================================================================= 2 | // This library is placed under the MIT License 3 | // Copyright 2017-2018 Natanael Josue Rabello. All rights reserved. 4 | // For the license information refer to LICENSE file in root directory. 5 | // ========================================================================= 6 | 7 | /** 8 | * @file mpu/math.hpp 9 | * @brief MPU Math helper file 10 | */ 11 | 12 | #ifndef _MPU_MATH_HPP_ 13 | #define _MPU_MATH_HPP_ 14 | 15 | #include 16 | #include 17 | #include "mpu/types.hpp" 18 | #include "sdkconfig.h" 19 | 20 | /*! MPU Driver namespace */ 21 | namespace mpud 22 | { 23 | /*! Math namespace */ 24 | inline namespace math 25 | { 26 | // 27 | inline uint8_t accelFSRvalue(const accel_fs_t fs) 28 | { 29 | return 2 << fs; 30 | } 31 | 32 | inline uint16_t gyroFSRvalue(const gyro_fs_t fs) 33 | { 34 | return 250 << fs; 35 | } 36 | 37 | inline uint16_t accelSensitivity(const accel_fs_t fs) 38 | { 39 | return 16384 >> fs; 40 | } 41 | 42 | inline float gyroSensitivity(const gyro_fs_t fs) 43 | { 44 | return 131.f / (1 << fs); 45 | } 46 | 47 | inline float accelResolution(const accel_fs_t fs) 48 | { 49 | return static_cast(accelFSRvalue(fs)) / INT16_MAX; 50 | } 51 | 52 | inline float gyroResolution(const gyro_fs_t fs) 53 | { 54 | return static_cast(gyroFSRvalue(fs)) / INT16_MAX; 55 | } 56 | 57 | inline float accelGravity(const int16_t axis, const accel_fs_t fs) 58 | { 59 | return axis * accelResolution(fs); 60 | } 61 | 62 | inline float_axes_t accelGravity(const raw_axes_t& raw_axes, const accel_fs_t fs) 63 | { 64 | float_axes_t axes; 65 | axes.x = raw_axes.x * accelResolution(fs); 66 | axes.y = raw_axes.y * accelResolution(fs); 67 | axes.z = raw_axes.z * accelResolution(fs); 68 | return axes; 69 | } 70 | 71 | inline float gyroDegPerSec(const int16_t axis, const gyro_fs_t fs) 72 | { 73 | return axis * gyroResolution(fs); 74 | } 75 | 76 | inline float_axes_t gyroDegPerSec(const raw_axes_t& raw_axes, const gyro_fs_t fs) 77 | { 78 | float_axes_t axes; 79 | axes.x = raw_axes.x * gyroResolution(fs); 80 | axes.y = raw_axes.y * gyroResolution(fs); 81 | axes.z = raw_axes.z * gyroResolution(fs); 82 | return axes; 83 | } 84 | 85 | inline float gyroRadPerSec(const int16_t axis, const gyro_fs_t fs) 86 | { 87 | return (M_PI / 180) * gyroDegPerSec(axis, fs); 88 | } 89 | 90 | inline float_axes_t gyroRadPerSec(const raw_axes_t& raw_axes, const gyro_fs_t fs) 91 | { 92 | float_axes_t axes; 93 | axes.x = (M_PI / 180) * gyroDegPerSec(raw_axes.x, fs); 94 | axes.y = (M_PI / 180) * gyroDegPerSec(raw_axes.y, fs); 95 | axes.z = (M_PI / 180) * gyroDegPerSec(raw_axes.z, fs); 96 | return axes; 97 | } 98 | 99 | #if defined CONFIG_MPU6500 || defined CONFIG_MPU9250 100 | constexpr int16_t kRoomTempOffset = 0; // LSB 101 | constexpr float kCelsiusOffset = 21.f; // ºC 102 | constexpr float kTempSensitivity = 333.87f; // LSB/ºC 103 | #elif defined CONFIG_MPU6000 || defined CONFIG_MPU6050 || defined CONFIG_MPU9150 104 | constexpr int16_t kRoomTempOffset = -521; // LSB 105 | constexpr float kCelsiusOffset = 35.f; // ºC 106 | constexpr float kTempSensitivity = 340.f; // LSB/ºC 107 | #endif 108 | 109 | constexpr float kTempResolution = 98.67f / INT16_MAX; 110 | constexpr float kFahrenheitOffset = kCelsiusOffset * 1.8f + 32; // ºF 111 | 112 | inline float tempCelsius(const int16_t temp) 113 | { 114 | // TEMP_degC = ((TEMP_OUT – RoomTemp_Offset)/Temp_Sensitivity) + DegreesCelsius_Offset 115 | return (temp - kRoomTempOffset) * kTempResolution + kCelsiusOffset; 116 | } 117 | 118 | inline float tempFahrenheit(const int16_t temp) 119 | { 120 | return (temp - kRoomTempOffset) * kTempResolution * 1.8f + kFahrenheitOffset; 121 | } 122 | 123 | #if defined CONFIG_MPU_AK89xx 124 | inline int16_t magAdjust(const int16_t axis, const uint8_t adjValue) 125 | { 126 | // Hadj = H * ((((ASA - 128) * 0.5) / 128) + 1) 127 | // return axis * ((((adjValue - 128) * 0.5f) / 128) + 1); 128 | constexpr float factor = 0.5f / 128; 129 | return axis * ((adjValue - 128) * factor + 1); 130 | } 131 | #endif 132 | 133 | } // namespace math 134 | 135 | } // namespace mpud 136 | 137 | #endif /* end of include guard: _MPU_MATH_HPP_ */ 138 | -------------------------------------------------------------------------------- /include/mpu/registers.hpp: -------------------------------------------------------------------------------- 1 | // ========================================================================= 2 | // This library is placed under the MIT License 3 | // Copyright 2017-2018 Natanael Josue Rabello. All rights reserved. 4 | // For the license information refer to LICENSE file in root directory. 5 | // ========================================================================= 6 | 7 | /** 8 | * @file mpu/registers.hpp 9 | * Define registers' for all MPU models. 10 | */ 11 | 12 | #ifndef _MPU_REGISTERS_HPP_ 13 | #define _MPU_REGISTERS_HPP_ 14 | 15 | #include 16 | #include "sdkconfig.h" 17 | 18 | /*! MPU Driver namespace */ 19 | namespace mpud 20 | { 21 | /*! Registers namespace */ 22 | namespace regs 23 | { 24 | /******************************************************************************* 25 | * MPU commom registers for all models 26 | ******************************************************************************/ 27 | constexpr uint8_t XG_OFFSET_H = (0x13); 28 | constexpr uint8_t XG_OFFSET_L = (0x14); 29 | constexpr uint8_t YG_OFFSET_H = (0x15); 30 | constexpr uint8_t YG_OFFSET_L = (0x16); 31 | constexpr uint8_t ZG_OFFSET_H = (0x17); 32 | constexpr uint8_t ZG_OFFSET_L = (0x18); 33 | constexpr uint8_t SMPLRT_DIV = (0x19); // [7:0] 34 | //------------------------------------------------------------------------------ 35 | constexpr uint8_t CONFIG = (0x1A); 36 | constexpr uint8_t CONFIG_FIFO_MODE_BIT = (6); 37 | constexpr uint8_t CONFIG_EXT_SYNC_SET_BIT = (5); // [5:3] 38 | constexpr uint8_t CONFIG_EXT_SYNC_SET_LENGTH = (3); 39 | constexpr uint8_t CONFIG_DLPF_CFG_BIT = (2); // [2:0] 40 | constexpr uint8_t CONFIG_DLPF_CFG_LENGTH = (3); 41 | //------------------------------------------------------------------------------ 42 | constexpr uint8_t GYRO_CONFIG = (0x1B); 43 | constexpr uint8_t GCONFIG_XG_ST_BIT = (7); 44 | constexpr uint8_t GCONFIG_YG_ST_BIT = (6); 45 | constexpr uint8_t GCONFIG_ZG_ST_BIT = (5); 46 | constexpr uint8_t GCONFIG_FS_SEL_BIT = (4); // [4:3] 47 | constexpr uint8_t GCONFIG_FS_SEL_LENGTH = (2); 48 | constexpr uint8_t GCONFIG_FCHOICE_B = (1); // [1:0] 49 | constexpr uint8_t GCONFIG_FCHOICE_B_LENGTH = (2); 50 | //------------------------------------------------------------------------------ 51 | constexpr uint8_t ACCEL_CONFIG = (0x1C); 52 | constexpr uint8_t ACONFIG_XA_ST_BIT = (7); 53 | constexpr uint8_t ACONFIG_YA_ST_BIT = (6); 54 | constexpr uint8_t ACONFIG_ZA_ST_BIT = (5); 55 | constexpr uint8_t ACONFIG_FS_SEL_BIT = (4); // [4:3] 56 | constexpr uint8_t ACONFIG_FS_SEL_LENGTH = (2); 57 | constexpr uint8_t ACONFIG_HPF_BIT = (2); // [2:0] 58 | constexpr uint8_t ACONFIG_HPF_LENGTH = (3); 59 | //------------------------------------------------------------------------------ 60 | constexpr uint8_t FF_THR = (0x1D); 61 | constexpr uint8_t FF_DUR = (0x1E); 62 | constexpr uint8_t MOTION_THR = (0x1F); // [7:0] // MPU9250_REG_WOM_THR 63 | constexpr uint8_t MOTION_DUR = (0x20); 64 | constexpr uint8_t ZRMOTION_THR = (0x21); 65 | constexpr uint8_t ZRMOTION_DUR = (0x22); 66 | //------------------------------------------------------------------------------ 67 | constexpr uint8_t FIFO_EN = (0x23); 68 | constexpr uint8_t FIFO_TEMP_EN_BIT = (7); 69 | constexpr uint8_t FIFO_XGYRO_EN_BIT = (6); 70 | constexpr uint8_t FIFO_YGYRO_EN_BIT = (5); 71 | constexpr uint8_t FIFO_ZGYRO_EN_BIT = (4); 72 | constexpr uint8_t FIFO_ACCEL_EN_BIT = (3); 73 | constexpr uint8_t FIFO_SLV_2_EN_BIT = (2); 74 | constexpr uint8_t FIFO_SLV_1_EN_BIT = (1); 75 | constexpr uint8_t FIFO_SLV_0_EN_BIT = (0); 76 | //------------------------------------------------------------------------------ 77 | constexpr uint8_t I2C_MST_CTRL = (0x24); 78 | constexpr uint8_t I2CMST_CTRL_MULT_EN_BIT = (7); 79 | constexpr uint8_t I2CMST_CTRL_WAIT_FOR_ES_BIT = (6); 80 | constexpr uint8_t I2CMST_CTRL_SLV_3_FIFO_EN_BIT = (5); 81 | constexpr uint8_t I2CMST_CTRL_P_NSR_BIT = (4); 82 | constexpr uint8_t I2CMST_CTRL_CLOCK_BIT = (3); // [3:0] 83 | constexpr uint8_t I2CMST_CTRL_CLOCK_LENGTH = (4); 84 | //------------------------------------------------------------------------------ 85 | constexpr uint8_t I2C_SLV0_ADDR = (0x25); 86 | constexpr uint8_t I2C_SLV_RNW_BIT = (7); // same for all I2C_SLV registers 87 | constexpr uint8_t I2C_SLV_ID_BIT = (6); // [6:0] 88 | constexpr uint8_t I2C_SLV_ID_LENGTH = (7); 89 | //------------------------------------------------------------------------------ 90 | constexpr uint8_t I2C_SLV0_REG = (0x26); // [7:0] 91 | //------------------------------------------------------------------------------ 92 | constexpr uint8_t I2C_SLV0_CTRL = (0x27); 93 | constexpr uint8_t I2C_SLV_EN_BIT = (7); // same for all I2C_SLV registers 94 | constexpr uint8_t I2C_SLV_BYTE_SW_BIT = (6); 95 | constexpr uint8_t I2C_SLV_REG_DIS_BIT = (5); 96 | constexpr uint8_t I2C_SLV_GRP_BIT = (4); 97 | constexpr uint8_t I2C_SLV_LEN_BIT = (3); // [3:0] 98 | constexpr uint8_t I2C_SLV_LEN_LENGTH = (4); 99 | //------------------------------------------------------------------------------ 100 | constexpr uint8_t I2C_SLV1_ADDR = (0x28); // see SLV0 for bit defines 101 | constexpr uint8_t I2C_SLV1_REG = (0x29); 102 | constexpr uint8_t I2C_SLV1_CTRL = (0x2A); 103 | constexpr uint8_t I2C_SLV2_ADDR = (0x2B); // see SLV0 for bit defines 104 | constexpr uint8_t I2C_SLV2_REG = (0x2C); 105 | constexpr uint8_t I2C_SLV2_CTRL = (0x2D); 106 | constexpr uint8_t I2C_SLV3_ADDR = (0x2E); // see SLV0 for bit defines 107 | constexpr uint8_t I2C_SLV3_REG = (0x2F); 108 | constexpr uint8_t I2C_SLV3_CTRL = (0x30); 109 | constexpr uint8_t I2C_SLV4_ADDR = (0x31); // see SLV0 for bit defines 110 | constexpr uint8_t I2C_SLV4_REG = (0x32); 111 | constexpr uint8_t I2C_SLV4_DO = (0x33); // [7:0] 112 | //------------------------------------------------------------------------------ 113 | constexpr uint8_t I2C_SLV4_CTRL = (0x34); 114 | constexpr uint8_t I2C_SLV4_EN_BIT = (7); 115 | constexpr uint8_t I2C_SLV4_DONE_INT_BIT = (6); 116 | constexpr uint8_t I2C_SLV4_REG_DIS_BIT = (5); 117 | constexpr uint8_t I2C_SLV4_MST_DELAY_BIT = (4); // [4:0] 118 | constexpr uint8_t I2C_SLV4_MST_DELAY_LENGTH = (5); 119 | //------------------------------------------------------------------------------ 120 | constexpr uint8_t I2C_SLV4_DI = (0x35); // [7:0] 121 | //------------------------------------------------------------------------------ 122 | constexpr uint8_t I2C_MST_STATUS = (0x36); 123 | constexpr uint8_t I2CMST_STAT_PASS_THROUGH_BIT = (7); 124 | constexpr uint8_t I2CMST_STAT_SLV4_DONE_BIT = (6); 125 | constexpr uint8_t I2CMST_STAT_LOST_ARB_BIT = (5); 126 | constexpr uint8_t I2CMST_STAT_SLV4_NACK_BIT = (4); 127 | constexpr uint8_t I2CMST_STAT_SLV3_NACK_BIT = (3); 128 | constexpr uint8_t I2CMST_STAT_SLV2_NACK_BIT = (2); 129 | constexpr uint8_t I2CMST_STAT_SLV1_NACK_BIT = (1); 130 | constexpr uint8_t I2CMST_STAT_SLV0_NACK_BIT = (0); 131 | //------------------------------------------------------------------------------ 132 | constexpr uint8_t INT_PIN_CONFIG = (0x37); 133 | constexpr uint8_t INT_CFG_LEVEL_BIT = (7); 134 | constexpr uint8_t INT_CFG_OPEN_BIT = (6); 135 | constexpr uint8_t INT_CFG_LATCH_EN_BIT = (5); 136 | constexpr uint8_t INT_CFG_ANYRD_2CLEAR_BIT = (4); 137 | constexpr uint8_t INT_CFG_FSYNC_LEVEL_BIT = (3); 138 | constexpr uint8_t INT_CFG_FSYNC_INT_MODE_EN_BIT = (2); 139 | constexpr uint8_t INT_CFG_I2C_BYPASS_EN_BIT = (1); 140 | constexpr uint8_t INT_CFG_CLOCKOUT_EN_BIT = (0); 141 | //------------------------------------------------------------------------------ 142 | constexpr uint8_t INT_ENABLE = (0x38); 143 | constexpr uint8_t INT_ENABLE_FREEFALL_BIT = (7); 144 | constexpr uint8_t INT_ENABLE_MOTION_BIT = (6); 145 | constexpr uint8_t INT_ENABLE_ZEROMOT_BIT = (5); 146 | constexpr uint8_t INT_ENABLE_FIFO_OFLOW_BIT = (4); 147 | constexpr uint8_t INT_ENABLE_I2C_MST_FSYNC_BIT = (3); 148 | constexpr uint8_t INT_ENABLE_PLL_RDY_BIT = (2); 149 | constexpr uint8_t INT_ENABLE_DMP_RDY_BIT = (1); 150 | constexpr uint8_t INT_ENABLE_RAW_DATA_RDY_BIT = (0); 151 | //------------------------------------------------------------------------------ 152 | constexpr uint8_t DMP_INT_STATUS = (0x39); 153 | constexpr uint8_t DMP_INT_STATUS_0 = (0); 154 | constexpr uint8_t DMP_INT_STATUS_1 = (1); 155 | constexpr uint8_t DMP_INT_STATUS_2 = (2); 156 | constexpr uint8_t DMP_INT_STATUS_3 = (3); 157 | constexpr uint8_t DMP_INT_STATUS_4 = (4); 158 | constexpr uint8_t DMP_INT_STATUS_5 = (5); 159 | //------------------------------------------------------------------------------ 160 | constexpr uint8_t INT_STATUS = (0x3A); 161 | constexpr uint8_t INT_STATUS_FREEFALL_BIT = (7); 162 | constexpr uint8_t INT_STATUS_MOTION_BIT = (6); 163 | constexpr uint8_t INT_STATUS_ZEROMOT_BIT = (5); 164 | constexpr uint8_t INT_STATUS_FIFO_OFLOW_BIT = (4); 165 | constexpr uint8_t INT_STATUS_I2C_MST_BIT = (3); 166 | constexpr uint8_t INT_STATUS_PLL_RDY_BIT = (2); 167 | constexpr uint8_t INT_STATUS_DMP_RDY_BIT = (1); 168 | constexpr uint8_t INT_STATUS_RAW_DATA_RDY_BIT = (0); 169 | //------------------------------------------------------------------------------ 170 | constexpr uint8_t ACCEL_XOUT_H = (0x3B); // [15:0] 171 | constexpr uint8_t ACCEL_XOUT_L = (0x3C); 172 | constexpr uint8_t ACCEL_YOUT_H = (0x3D); // [15:0] 173 | constexpr uint8_t ACCEL_YOUT_L = (0x3E); 174 | constexpr uint8_t ACCEL_ZOUT_H = (0x3F); // [15:0] 175 | constexpr uint8_t ACCEL_ZOUT_L = (0x40); 176 | constexpr uint8_t TEMP_OUT_H = (0x41); // [15:0] 177 | constexpr uint8_t TEMP_OUT_L = (0x42); 178 | constexpr uint8_t GYRO_XOUT_H = (0x43); // [15:0] 179 | constexpr uint8_t GYRO_XOUT_L = (0x44); 180 | constexpr uint8_t GYRO_YOUT_H = (0x45); // [15:0] 181 | constexpr uint8_t GYRO_YOUT_L = (0x46); 182 | constexpr uint8_t GYRO_ZOUT_H = (0x47); // [15:0] 183 | constexpr uint8_t GYRO_ZOUT_L = (0x48); 184 | constexpr uint8_t EXT_SENS_DATA_00 = (0x49); // Stores data read from Slave 0, 1, 2, and 3 185 | constexpr uint8_t EXT_SENS_DATA_01 = (0x4A); 186 | constexpr uint8_t EXT_SENS_DATA_02 = (0x4B); 187 | constexpr uint8_t EXT_SENS_DATA_03 = (0x4C); 188 | constexpr uint8_t EXT_SENS_DATA_04 = (0x4D); 189 | constexpr uint8_t EXT_SENS_DATA_05 = (0x4E); 190 | constexpr uint8_t EXT_SENS_DATA_06 = (0x4F); 191 | constexpr uint8_t EXT_SENS_DATA_07 = (0x50); 192 | constexpr uint8_t EXT_SENS_DATA_08 = (0x51); 193 | constexpr uint8_t EXT_SENS_DATA_09 = (0x52); 194 | constexpr uint8_t EXT_SENS_DATA_10 = (0x53); 195 | constexpr uint8_t EXT_SENS_DATA_11 = (0x54); 196 | constexpr uint8_t EXT_SENS_DATA_12 = (0x55); 197 | constexpr uint8_t EXT_SENS_DATA_13 = (0x56); 198 | constexpr uint8_t EXT_SENS_DATA_14 = (0x57); 199 | constexpr uint8_t EXT_SENS_DATA_15 = (0x58); 200 | constexpr uint8_t EXT_SENS_DATA_16 = (0x59); 201 | constexpr uint8_t EXT_SENS_DATA_17 = (0x5A); 202 | constexpr uint8_t EXT_SENS_DATA_18 = (0x5B); 203 | constexpr uint8_t EXT_SENS_DATA_19 = (0x5C); 204 | constexpr uint8_t EXT_SENS_DATA_20 = (0x5D); 205 | constexpr uint8_t EXT_SENS_DATA_21 = (0x5E); 206 | constexpr uint8_t EXT_SENS_DATA_22 = (0x5F); 207 | constexpr uint8_t EXT_SENS_DATA_23 = (0x60); 208 | constexpr uint8_t I2C_SLV0_DO = (0x63); 209 | constexpr uint8_t I2C_SLV1_DO = (0x64); 210 | constexpr uint8_t I2C_SLV2_DO = (0x65); 211 | constexpr uint8_t I2C_SLV3_DO = (0x66); 212 | //------------------------------------------------------------------------------ 213 | constexpr uint8_t I2C_MST_DELAY_CRTL = (0x67); 214 | constexpr uint8_t I2CMST_DLY_ES_SHADOW_BIT = (7); 215 | constexpr uint8_t I2CMST_DLY_SLV4_EN_BIT = (4); 216 | constexpr uint8_t I2CMST_DLY_SLV3_EN_BIT = (3); 217 | constexpr uint8_t I2CMST_DLY_SLV2_EN_BIT = (2); 218 | constexpr uint8_t I2CMST_DLY_SLV1_EN_BIT = (1); 219 | constexpr uint8_t I2CMST_DLY_SLV0_EN_BIT = (0); 220 | //------------------------------------------------------------------------------ 221 | constexpr uint8_t SIGNAL_PATH_RESET = (0x68); 222 | constexpr uint8_t SPATH_GYRO_RST_BIT = (2); 223 | constexpr uint8_t SPATH_ACCEL_RST_BIT = (1); 224 | constexpr uint8_t SPATH_TEMP_RST_BIT = (0); 225 | //------------------------------------------------------------------------------ 226 | constexpr uint8_t USER_CTRL = (0x6A); 227 | constexpr uint8_t USERCTRL_DMP_EN_BIT = (7); 228 | constexpr uint8_t USERCTRL_FIFO_EN_BIT = (6); 229 | constexpr uint8_t USERCTRL_I2C_MST_EN_BIT = (5); 230 | constexpr uint8_t USERCTRL_I2C_IF_DIS_BIT = (4); 231 | constexpr uint8_t USERCTRL_DMP_RESET_BIT = (3); 232 | constexpr uint8_t USERCTRL_FIFO_RESET_BIT = (2); 233 | constexpr uint8_t USERCTRL_I2C_MST_RESET_BIT = (1); 234 | constexpr uint8_t USERCTRL_SIG_COND_RESET_BIT = (0); 235 | //------------------------------------------------------------------------------ 236 | constexpr uint8_t PWR_MGMT1 = (0x6B); 237 | constexpr uint8_t PWR1_DEVICE_RESET_BIT = (7); 238 | constexpr uint8_t PWR1_SLEEP_BIT = (6); 239 | constexpr uint8_t PWR1_CYCLE_BIT = (5); 240 | constexpr uint8_t PWR1_GYRO_STANDBY_BIT = (4); 241 | constexpr uint8_t PWR1_TEMP_DIS_BIT = (3); 242 | constexpr uint8_t PWR1_CLKSEL_BIT = (2); 243 | constexpr uint8_t PWR1_CLKSEL_LENGTH = (3); 244 | //------------------------------------------------------------------------------ 245 | constexpr uint8_t PWR_MGMT2 = (0x6C); 246 | constexpr uint8_t PWR2_LP_WAKE_CTRL_BIT = (7); 247 | constexpr uint8_t PWR2_LP_WAKE_CTRL_LENGTH = (2); 248 | constexpr uint8_t PWR2_STBY_XA_BIT = (5); 249 | constexpr uint8_t PWR2_STBY_YA_BIT = (4); 250 | constexpr uint8_t PWR2_STBY_ZA_BIT = (3); 251 | constexpr uint8_t PWR2_STBY_XG_BIT = (2); 252 | constexpr uint8_t PWR2_STBY_YG_BIT = (1); 253 | constexpr uint8_t PWR2_STBY_ZG_BIT = (0); 254 | constexpr uint8_t PWR2_STBY_XYZA_BITS = (1 << PWR2_STBY_XA_BIT | 1 << PWR2_STBY_YA_BIT | 1 << PWR2_STBY_ZA_BIT); 255 | constexpr uint8_t PWR2_STBY_XYZG_BITS = (1 << PWR2_STBY_XG_BIT | 1 << PWR2_STBY_YG_BIT | 1 << PWR2_STBY_ZG_BIT); 256 | //------------------------------------------------------------------------------ 257 | constexpr uint8_t BANK_SEL = (0x6D); 258 | constexpr uint8_t BANKSEL_PRFTCH_EN_BIT = (6); 259 | constexpr uint8_t BANKSEL_CFG_USER_BANK_BIT = (5); 260 | constexpr uint8_t BANKSEL_MEM_SEL_BIT = (4); 261 | constexpr uint8_t BANKSEL_MEM_SEL_LENGTH = (5); 262 | //------------------------------------------------------------------------------ 263 | constexpr uint8_t MEM_START_ADDR = (0x6E); 264 | constexpr uint8_t MEM_R_W = (0x6F); 265 | constexpr uint8_t PRGM_START_H = (0x70); 266 | constexpr uint8_t PRGM_START_L = (0x71); 267 | constexpr uint8_t FIFO_COUNT_H = (0x72); // [15:0] 268 | constexpr uint8_t FIFO_COUNT_L = (0x73); 269 | constexpr uint8_t FIFO_R_W = (0x74); 270 | constexpr uint8_t WHO_AM_I = (0x75); 271 | 272 | /******************************************************************************* 273 | * MPU6000, MPU6050 and MPU9150 registers 274 | ******************************************************************************/ 275 | #if defined CONFIG_MPU6050 276 | constexpr uint8_t XG_OTP_OFFSET_TC = (0x00); // [7] PWR_MODE, [6:1] XG_OFFS_TC, [0] OTP_BNK_VLD 277 | //------------------------------------------------------------------------------ 278 | constexpr uint8_t YG_OTP_OFFSET_TC = (0x01); // [7] PWR_MODE, [6:1] YG_OFFS_TC, [0] OTP_BNK_VLD 279 | constexpr uint8_t TC_PWR_MODE_BIT = (7); // note: TC = temperature compensation, i think 280 | //------------------------------------------------------------------------------ 281 | constexpr uint8_t ZG_OTP_OFFSET_TC = (0x02); // [7] PWR_MODE, [6:1] ZG_OFFS_TC, [0] OTP_BNK_VLD 282 | constexpr uint8_t X_FINE_GAIN = (0x03); // [7:0] X_FINE_GAIN 283 | constexpr uint8_t Y_FINE_GAIN = (0x04); // [7:0] Y_FINE_GAIN 284 | constexpr uint8_t Z_FINE_GAIN = (0x05); // [7:0] Z_FINE_GAIN 285 | constexpr uint8_t XA_OFFSET_H = (0x06); // [15:1] XA_OFFS 286 | constexpr uint8_t XA_OFFSET_L = (0x07); // note: TC: bit [0] 287 | constexpr uint8_t YA_OFFSET_H = (0x08); // [15:1] YA_OFFS 288 | constexpr uint8_t YA_OFFSET_L = (0x09); // note: TC: bit [0] 289 | constexpr uint8_t ZA_OFFSET_H = (0x0A); // [15:1] ZA_OFFS 290 | constexpr uint8_t ZA_OFFSET_L = (0x0B); // note: TC: bit [0] 291 | constexpr uint8_t SELF_TEST_X = (0x0D); 292 | constexpr uint8_t SELF_TEST_Y = (0x0E); 293 | constexpr uint8_t SELF_TEST_Z = (0x0F); 294 | constexpr uint8_t SELF_TEST_A = (0x10); 295 | //------------------------------------------------------------------------------ 296 | constexpr uint8_t MOTION_DETECT_STATUS = (0x61); 297 | constexpr uint8_t MOT_STATUS_X_NEG_BIT = (7); 298 | constexpr uint8_t MOT_STATUS_X_POS_BIT = (6); 299 | constexpr uint8_t MOT_STATUS_Y_NEG_BIT = (5); 300 | constexpr uint8_t MOT_STATUS_Y_POS_BIT = (4); 301 | constexpr uint8_t MOT_STATUS_Z_NEG_BIT = (3); 302 | constexpr uint8_t MOT_STATUS_Z_POS_BIT = (2); 303 | constexpr uint8_t MOT_STATUS_ZRMOT_BIT = (0); 304 | //------------------------------------------------------------------------------ 305 | constexpr uint8_t MOTION_DETECT_CTRL = (0x69); 306 | constexpr uint8_t MOTCTRL_ACCEL_ON_DELAY_BIT = (5); // [5:4] 307 | constexpr uint8_t MOTCTRL_ACCEL_ON_DELAY_LENGTH = (2); 308 | constexpr uint8_t MOTCTRL_FF_COUNT_BIT = (3); // [3:2] 309 | constexpr uint8_t MOTCTRL_FF_COUNT_LENGTH = (2); 310 | constexpr uint8_t MOTCTRL_MOT_COUNT_BIT = (1); // [1:0] 311 | constexpr uint8_t MOTCTRL_MOT_COUNT_LENGTH = (2); 312 | //------------------------------------------------------------------------------ 313 | #endif 314 | 315 | /******************************************************************************* 316 | * MPU6500 and MPU9250 registers 317 | ******************************************************************************/ 318 | #if defined CONFIG_MPU6500 319 | constexpr uint8_t SELF_TEST_X_GYRO = (0x00); // XG_ST_DATA[7:0] 320 | constexpr uint8_t SELF_TEST_Y_GYRO = (0x01); // YG_ST_DATA[7:0] 321 | constexpr uint8_t SELF_TEST_Z_GYRO = (0x02); // ZG_ST_DATA[7:0] 322 | constexpr uint8_t SELF_TEST_X_ACCEL = (0x0D); 323 | constexpr uint8_t SELF_TEST_Y_ACCEL = (0x0E); 324 | constexpr uint8_t SELF_TEST_Z_ACCEL = (0x0F); 325 | //------------------------------------------------------------------------------ 326 | constexpr uint8_t ACCEL_CONFIG2 = (0x1D); 327 | constexpr uint8_t ACONFIG2_FIFO_SIZE_BIT = (7); // [7:6] 328 | constexpr uint8_t ACONFIG2_FIFO_SIZE_LENGTH = (2); 329 | constexpr uint8_t ACONFIG2_ACCEL_FCHOICE_B_BIT = (3); 330 | constexpr uint8_t ACONFIG2_A_DLPF_CFG_BIT = (2); // [2:0] 331 | constexpr uint8_t ACONFIG2_A_DLPF_CFG_LENGTH = (3); 332 | //------------------------------------------------------------------------------ 333 | constexpr uint8_t LP_ACCEL_ODR = (0x1E); 334 | constexpr uint8_t LPA_ODR_CLKSEL_BIT = (3); // [3:0] 335 | constexpr uint8_t LPA_ODR_CLKSEL_LENGTH = (4); 336 | //------------------------------------------------------------------------------ 337 | constexpr uint8_t ACCEL_INTEL_CTRL = (0x69); 338 | constexpr uint8_t ACCEL_INTEL_EN_BIT = (7); 339 | constexpr uint8_t ACCEL_INTEL_MODE_BIT = (6); 340 | //------------------------------------------------------------------------------ 341 | constexpr uint8_t XA_OFFSET_H = (0x77); 342 | constexpr uint8_t XA_OFFSET_L = (0x78); 343 | constexpr uint8_t YA_OFFSET_H = (0x7A); 344 | constexpr uint8_t YA_OFFSET_L = (0x7B); 345 | constexpr uint8_t ZA_OFFSET_H = (0x7D); 346 | constexpr uint8_t ZA_OFFSET_L = (0x7E); 347 | #endif 348 | 349 | /******************************************************************************* 350 | * MPU9150 and MPU9250 Magnetometer registers (AK89xx) 351 | ******************************************************************************/ 352 | #if defined CONFIG_MPU_AK89xx 353 | /*! Magnetometer Registers namespace */ 354 | namespace mag 355 | { 356 | constexpr uint8_t WHO_I_AM = (0x00); 357 | constexpr uint8_t INFO = (0x01); 358 | //------------------------------------------------------------------------------ 359 | constexpr uint8_t STATUS1 = (0x02); 360 | constexpr uint8_t STATUS1_DATA_RDY_BIT = (0); 361 | //------------------------------------------------------------------------------ 362 | constexpr uint8_t HXL = (0x03); 363 | constexpr uint8_t HXH = (0x04); 364 | constexpr uint8_t HYL = (0x05); 365 | constexpr uint8_t HYH = (0x06); 366 | constexpr uint8_t HZL = (0x07); 367 | constexpr uint8_t HZH = (0x08); 368 | //------------------------------------------------------------------------------ 369 | constexpr uint8_t STATUS2 = (0x09); 370 | constexpr uint8_t STATUS2_OVERFLOW_BIT = (3); 371 | //------------------------------------------------------------------------------ 372 | constexpr uint8_t CONTROL1 = (0x0A); 373 | constexpr uint8_t CONTROL1_MODE_BIT = (3); 374 | constexpr uint8_t CONTROL1_MODE_LENGTH = (4); 375 | //------------------------------------------------------------------------------ 376 | constexpr uint8_t ASTC = (0x0C); 377 | constexpr uint8_t ASTC_SELF_TEST_BIT = (6); 378 | //------------------------------------------------------------------------------ 379 | constexpr uint8_t TEST1 = (0x0D); 380 | constexpr uint8_t TEST2 = (0x0E); 381 | //------------------------------------------------------------------------------ 382 | constexpr uint8_t I2CDIS = (0x0F); 383 | constexpr uint8_t I2CDIS_DISABLE_VALUE = (0x1B); 384 | //------------------------------------------------------------------------------ 385 | constexpr uint8_t ASAX = (0x10); 386 | constexpr uint8_t ASAY = (0x11); 387 | constexpr uint8_t ASAZ = (0x12); 388 | 389 | /******************************************************************************* 390 | * MPU9150 Magnetometer (AK8975) 391 | ******************************************************************************/ 392 | #if defined CONFIG_MPU_AK8975 393 | constexpr uint8_t STATUS2_DATA_ERROR_BIT = (2); 394 | #endif 395 | 396 | /******************************************************************************* 397 | * MPU9250 Magnetometer (AK8963) 398 | ******************************************************************************/ 399 | #if defined CONFIG_MPU_AK8963 400 | constexpr uint8_t STATUS1_DATA_OVERRUN_BIT = (1); 401 | constexpr uint8_t STATUS2_BIT_OUTPUT_M_BIT = (4); 402 | constexpr uint8_t CONTROL1_BIT_OUTPUT_BIT = (4); 403 | //------------------------------------------------------------------------------ 404 | constexpr uint8_t CONTROL2 = (0x0B); 405 | constexpr uint8_t CONTROL2_SOFT_RESET_BIT = (0); 406 | //------------------------------------------------------------------------------ 407 | #endif 408 | 409 | } // namespace mag 410 | #endif // defined AK89xx 411 | 412 | } // namespace regs 413 | 414 | } // namespace mpud 415 | 416 | #endif /* end of include guard: _MPU_REGISTERS_HPP_ */ 417 | -------------------------------------------------------------------------------- /include/mpu/types.hpp: -------------------------------------------------------------------------------- 1 | // ========================================================================= 2 | // This library is placed under the MIT License 3 | // Copyright 2017-2018 Natanael Josue Rabello. All rights reserved. 4 | // For the license information refer to LICENSE file in root directory. 5 | // ========================================================================= 6 | 7 | /** 8 | * @file mpu/types.hpp 9 | * Declare Types and Definitions used within `mpud` namespace. 10 | */ 11 | 12 | #ifndef _MPU_TYPES_HPP_ 13 | #define _MPU_TYPES_HPP_ 14 | 15 | #include 16 | #include "mpu/registers.hpp" 17 | #include "sdkconfig.h" 18 | 19 | /*! MPU Driver namespace */ 20 | namespace mpud 21 | { 22 | /*! Types namespace */ 23 | inline namespace types 24 | { 25 | /*! MPU's possible I2C slave addresses */ 26 | typedef enum { // 27 | MPU_I2CADDRESS_AD0_LOW = 0x68, 28 | MPU_I2CADDRESS_AD0_HIGH = 0x69 29 | } mpu_i2caddr_t; 30 | static constexpr mpu_i2caddr_t MPU_DEFAULT_I2CADDRESS = MPU_I2CADDRESS_AD0_LOW; 31 | 32 | #ifdef CONFIG_MPU_I2C 33 | typedef I2C_t mpu_bus_t; /*!< Communication bus type, `I2Cbus` or `SPIbus`. */ 34 | typedef mpu_i2caddr_t mpu_addr_handle_t; /*!< MPU Address/Handle type, `mpu_i2caddr_t` or `spi_device_handle_t` */ 35 | static constexpr mpu_bus_t& MPU_DEFAULT_BUS = i2c0; 36 | static constexpr mpu_addr_handle_t MPU_DEFAULT_ADDR_HANDLE = MPU_DEFAULT_I2CADDRESS; 37 | #elif CONFIG_MPU_SPI 38 | typedef SPI_t mpu_bus_t; 39 | typedef spi_device_handle_t mpu_addr_handle_t; 40 | static constexpr mpu_bus_t& MPU_DEFAULT_BUS = hspi; 41 | static constexpr mpu_addr_handle_t MPU_DEFAULT_ADDR_HANDLE = nullptr; 42 | #endif 43 | 44 | #if defined CONFIG_MPU6050 45 | static constexpr uint16_t SAMPLE_RATE_MAX = 8000; 46 | #elif defined CONFIG_MPU6500 47 | static constexpr uint16_t SAMPLE_RATE_MAX = 32000; 48 | #endif 49 | 50 | /*! Gyroscope full-scale range */ 51 | typedef enum { 52 | GYRO_FS_250DPS = 0, //!< +/- 250 º/s -> 131 LSB/(º/s) 53 | GYRO_FS_500DPS = 1, //!< +/- 500 º/s -> 65.5 LSB/(º/s) 54 | GYRO_FS_1000DPS = 2, //!< +/- 1000 º/s -> 32.8 LSB/(º/s) 55 | GYRO_FS_2000DPS = 3 //!< +/- 2000 º/s -> 16.4 LSB/(º/s) 56 | } gyro_fs_t; 57 | 58 | /*! Accel full-scale range */ 59 | typedef enum { 60 | ACCEL_FS_2G = 0, //!< +/- 2 g -> 16.384 LSB/g 61 | ACCEL_FS_4G = 1, //!< +/- 4 g -> 8.192 LSB/g 62 | ACCEL_FS_8G = 2, //!< +/- 8 g -> 4.096 LSB/g 63 | ACCEL_FS_16G = 3 //!< +/- 16 g -> 2.048 LSB/g 64 | } accel_fs_t; 65 | 66 | /*! Digital low-pass filter (based on gyro bandwidth) */ 67 | typedef enum { 68 | DLPF_256HZ_NOLPF = 0, 69 | DLPF_188HZ = 1, 70 | DLPF_98HZ = 2, 71 | DLPF_42HZ = 3, 72 | DLPF_20HZ = 4, 73 | DLPF_10HZ = 5, 74 | DLPF_5HZ = 6, 75 | #ifdef CONFIG_MPU6050 76 | DLPF_2100HZ_NOLPF = 7 77 | #elif CONFIG_MPU6500 78 | DLPF_3600HZ_NOLPF = 7 79 | #endif 80 | } dlpf_t; 81 | 82 | /*! Clock Source */ 83 | typedef enum { 84 | CLOCK_INTERNAL = 0, //!< Internal oscillator: 20MHz for MPU6500 and 8MHz for MPU6050 85 | CLOCK_PLL = 3, //!< Selects automatically best pll source (recommended) 86 | #if defined CONFIG_MPU6050 87 | CLOCK_EXT32KHZ = 4, //!< PLL with external 32.768kHz reference 88 | CLOCK_EXT19MHZ = 5, //!< PLL with external 19.2MHz reference 89 | #endif 90 | CLOCK_KEEP_RESET = 7 //!< Stops the clock and keeps timing generator in reset 91 | } clock_src_t; 92 | 93 | #ifdef CONFIG_MPU6500 94 | /*! Fchoice (Frequency choice maybe ?) [MPU6500 and MPU9250 only] */ 95 | typedef enum { // 96 | FCHOICE_0 = 0, 97 | FCHOICE_1 = 1, 98 | FCHOICE_2 = 2, 99 | FCHOICE_3 = 3 100 | } fchoice_t; 101 | #endif 102 | 103 | /*! Low-Power Accelerometer wake-up rates */ 104 | typedef enum { 105 | #if defined CONFIG_MPU6000 || defined CONFIG_MPU6050 || defined CONFIG_MPU9150 106 | LP_ACCEL_RATE_1_25HZ = 0, 107 | LP_ACCEL_RATE_5HZ = 1, 108 | LP_ACCEL_RATE_20HZ = 2, 109 | LP_ACCEL_RATE_40HZ = 3 110 | #elif defined CONFIG_MPU6500 || defined CONFIG_MPU9250 111 | LP_ACCEL_RATE_0_24HZ = 0, 112 | LP_ACCEL_RATE_0_49HZ = 1, 113 | LP_ACCEL_RATE_0_98HZ = 2, 114 | LP_ACCEL_RATE_1_95HZ = 3, 115 | LP_ACCEL_RATE_3_91HZ = 4, 116 | LP_ACCEL_RATE_7_81HZ = 5, 117 | LP_ACCEL_RATE_15_63HZ = 6, 118 | LP_ACCEL_RATE_31_25HZ = 7, 119 | LP_ACCEL_RATE_62_50HZ = 8, 120 | LP_ACCEL_RATE_125HZ = 9, 121 | LP_ACCEL_RATE_250HZ = 10, 122 | LP_ACCEL_RATE_500HZ = 11 123 | #endif 124 | } lp_accel_rate_t; 125 | 126 | /*! Accelerometer Digital High Pass Filter (only for motion detection modules) */ 127 | typedef enum { 128 | ACCEL_DHPF_RESET = 0, /**< This effectively disables the high pass filter. This mode may be toggled to quickly 129 | * settle the filter. */ 130 | ACCEL_DHPF_5HZ = 1, //!< ON state, the high pass filter will pass signals above the cut off frequency. 131 | ACCEL_DHPF_2_5HZ = 2, //!< ON state, the high pass filter will pass signals above the cut off frequency. 132 | ACCEL_DHPF_1_25HZ = 3, //!< ON state, the high pass filter will pass signals above the cut off frequency. 133 | ACCEL_DHPF_0_63HZ = 4, //!< ON state, the high pass filter will pass signals above the cut off frequency. 134 | ACCEL_DHPF_HOLD = 7, /**< The filter holds the present sample. The output will be the difference between the 135 | * input sample and the held sample. */ 136 | } accel_dhpf_t; 137 | 138 | #if defined CONFIG_MPU6000 || defined CONFIG_MPU6050 || defined CONFIG_MPU9150 139 | /*! Motion Detection counter decrement rate (Motion and FreeFall) */ 140 | typedef enum { 141 | MOT_COUNTER_RESET = 0, //!< When set, any non-qualifying sample will reset the corresponding counter to 0 142 | MOT_COUNTER_DEC_1 = 1, //!< Decrement counter in 1 143 | MOT_COUNTER_DEC_2 = 2, //!< Decrement counter in 2 144 | MOT_COUNTER_DEC_4 = 3 //!< Decrement counter in 4 145 | } mot_counter_t; 146 | #endif 147 | 148 | /*! Motion Detection configuration */ 149 | typedef struct 150 | { 151 | uint8_t threshold; /**< Motion threshold in LSB. 152 | * For MPU6000 / MPU6050 / MPU9150: 1LSB = 32mg, 255LSB = 8160mg. 153 | * For MPU6500 / MPU9250: 1LSB = 4mg, 255LSB = 1020mg. */ 154 | #if defined CONFIG_MPU6000 || defined CONFIG_MPU6050 || defined CONFIG_MPU9150 155 | uint8_t time; /**< Duration in milliseconds that the accel data must exceed 156 | * the threshold before motion is reported. MAX = 255ms. */ 157 | uint8_t accel_on_delay : 2; /**< Specifies in milliseconds the additional power-on delay applied to accelerometer 158 | * data path modules. MAX = 3ms. 159 | * More: The signal path contains filters which must be flushed on wake-up with new 160 | * samples before the detection modules begin operations. 161 | * There is already a default built-in 4ms delay. */ 162 | mot_counter_t counter : 2; //!< Configures the detection counter decrement rate. 163 | #endif 164 | } mot_config_t; 165 | 166 | #if defined CONFIG_MPU6000 || defined CONFIG_MPU6050 || defined CONFIG_MPU9150 167 | /*! Zero-motion configuration */ 168 | typedef struct 169 | { 170 | uint8_t threshold; //!< Motion threshold in LSB. 1LSB = 1mg, 255LSB = 1020mg 171 | uint8_t time; /**< Duration in milliseconds that the accel data must exceed 172 | * the threshold before motion is reported. MAX = 255ms. */ 173 | } zrmot_config_t; 174 | #endif 175 | 176 | #if defined CONFIG_MPU6000 || defined CONFIG_MPU6050 || defined CONFIG_MPU9150 177 | /*! Free-fall configuration */ 178 | typedef struct 179 | { 180 | uint8_t threshold; //!< Motion threshold in LSB. 1LSB = 1mg, 255LSB = 1020mg 181 | uint8_t time; /*!< Duration in milliseconds that the accel data must exceed 182 | * the threshold before motion is reported. MAX = 255ms. */ 183 | uint8_t accel_on_delay : 2; /**< Specifies in milliseconds the additional power-on delay applied to accelerometer 184 | * data path modules. MAX = 3ms. 185 | * More: The signal path contains filters which must be flushed on wake-up with new 186 | * samples before the detection modules begin operations. 187 | * There is already a default built-in 4ms delay. */ 188 | mot_counter_t counter : 2; //!< Configures the detection counter decrement rate. 189 | } ff_config_t; 190 | #endif 191 | 192 | #if defined CONFIG_MPU6000 || defined CONFIG_MPU6050 || defined CONFIG_MPU9150 193 | /*! Motion Detection Status (MPU6000, MPU6050, MPU9150) */ 194 | typedef uint8_t mot_stat_t; 195 | static constexpr mot_stat_t MOT_STAT_XNEG = (1 << regs::MOT_STATUS_X_NEG_BIT); 196 | static constexpr mot_stat_t MOT_STAT_XPOS = (1 << regs::MOT_STATUS_X_POS_BIT); 197 | static constexpr mot_stat_t MOT_STAT_YNEG = (1 << regs::MOT_STATUS_Y_NEG_BIT); 198 | static constexpr mot_stat_t MOT_STAT_YPOS = (1 << regs::MOT_STATUS_Y_POS_BIT); 199 | static constexpr mot_stat_t MOT_STAT_ZNEG = (1 << regs::MOT_STATUS_Z_NEG_BIT); 200 | static constexpr mot_stat_t MOT_STAT_ZPOS = (1 << regs::MOT_STATUS_Z_POS_BIT); 201 | static constexpr mot_stat_t MOT_STAT_ZEROMOTION = (1 << regs::MOT_STATUS_ZRMOT_BIT); 202 | #endif 203 | 204 | /*! Standby Mode */ 205 | typedef uint8_t stby_en_t; 206 | static constexpr stby_en_t STBY_EN_NONE = (0x0); 207 | static constexpr stby_en_t STBY_EN_ACCEL_X = (1 << regs::PWR2_STBY_XA_BIT); 208 | static constexpr stby_en_t STBY_EN_ACCEL_Y = (1 << regs::PWR2_STBY_YA_BIT); 209 | static constexpr stby_en_t STBY_EN_ACCEL_Z = (1 << regs::PWR2_STBY_ZA_BIT); 210 | static constexpr stby_en_t STBY_EN_ACCEL = (STBY_EN_ACCEL_X | STBY_EN_ACCEL_Y | STBY_EN_ACCEL_Z); 211 | static constexpr stby_en_t STBY_EN_GYRO_X = (1 << regs::PWR2_STBY_XG_BIT); 212 | static constexpr stby_en_t STBY_EN_GYRO_Y = (1 << regs::PWR2_STBY_YG_BIT); 213 | static constexpr stby_en_t STBY_EN_GYRO_Z = (1 << regs::PWR2_STBY_ZG_BIT); 214 | static constexpr stby_en_t STBY_EN_GYRO = (STBY_EN_GYRO_X | STBY_EN_GYRO_Y | STBY_EN_GYRO_Z); 215 | static constexpr stby_en_t STBY_EN_TEMP = (1 << 6); 216 | /*! This is a low power mode that allows quick enabling of the gyros. 217 | * \note: When set, the gyro drive and pll circuitry are enabled, but the sense paths are disabled. */ 218 | static constexpr stby_en_t STBY_EN_LOWPWR_GYRO_PLL_ON = (1 << 7); 219 | 220 | /*! Auxiliary I2C Master clock speed */ 221 | typedef enum { 222 | AUXI2C_CLOCK_348KHZ = 0, 223 | AUXI2C_CLOCK_333KHZ = 1, 224 | AUXI2C_CLOCK_320KHZ = 2, 225 | AUXI2C_CLOCK_308KHZ = 3, 226 | AUXI2C_CLOCK_296KHZ = 4, 227 | AUXI2C_CLOCK_286KHZ = 5, 228 | AUXI2C_CLOCK_276KHZ = 6, 229 | AUXI2C_CLOCK_267KHZ = 7, 230 | AUXI2C_CLOCK_258KHZ = 8, 231 | AUXI2C_CLOCK_500KHZ = 9, 232 | AUXI2C_CLOCK_471KHZ = 10, 233 | AUXI2C_CLOCK_444KHZ = 11, 234 | AUXI2C_CLOCK_421KHZ = 12, 235 | AUXI2C_CLOCK_400KHZ = 13, 236 | AUXI2C_CLOCK_381KHZ = 14, 237 | AUXI2C_CLOCK_364KHZ = 15 238 | } auxi2c_clock_t; 239 | 240 | /*! Auxiliary I2C Master’s transition from one slave read to the next slave read */ 241 | typedef enum { AUXI2C_TRANS_RESTART = 0, AUXI2C_TRANS_STOP = 1 } auxi2c_trans_t; 242 | 243 | /** 244 | * @brief Auxiliary I2C Slaves slots 245 | * @note For MPU9150 & MPU9250: \n 246 | * The MPU uses SLAVE0 and SLAVE1 to read Compass data so do not use this slave slots when compass is enabled. 247 | * */ 248 | typedef enum { AUXI2C_SLAVE_0 = 0, AUXI2C_SLAVE_1 = 1, AUXI2C_SLAVE_2 = 2, AUXI2C_SLAVE_3 = 3 } auxi2c_slv_t; 249 | 250 | /*! Auxiliary I2C operation */ 251 | typedef enum { AUXI2C_WRITE = 0, AUXI2C_READ = 1 } auxi2c_rw_t; 252 | 253 | /** 254 | * @brief Auxiliary I2C, EOW = end of word, use for swap 255 | * @details This allows byte swapping of registers that are grouped starting at any address. 256 | * @note External sensor data typically comes in as groups of two bytes. 257 | * This bit is used to determine if the groups are from the slave’s 258 | * register address 0 and 1, 2 and 3, etc.., or if the groups are address 1 and 2, 3 and 4, etc.. 259 | */ 260 | typedef enum { 261 | AUXI2C_EOW_ODD_NUM = 0, /**< Indicates slave register addresses 0 and 1 are grouped together 262 | * (odd numbered register ends the word). */ 263 | AUXI2C_EOW_EVEN_NUM = 1 /**< Indicates slave register addresses 1 and 2 are grouped together 264 | * (even numbered register ends the word). */ 265 | } auxi2c_eow_t; 266 | 267 | /*! Auxiliary I2C Master configuration struct */ 268 | typedef struct 269 | { 270 | auxi2c_clock_t clock : 4; //!< Clock signal speed 271 | bool multi_master_en : 1; //!< Enable if there is an another master driving the bus too. 272 | uint8_t sample_delay : 5; /**< Number of samples to delay on Aux i2c trasactions, max = 31, 273 | * formula: rate = (sample_rate / delay + 1). 274 | * (e.g. sample_delay = 4, aux i2c transaction will occour after 4 samples. 275 | * If sample rate is 1KHz, the Aux i2c transaction rate will be 1000 / (4 + 1) = 200 Hz) 276 | * set zero if no delay is needed, so the transaction rate will be the same as 277 | * sample rate. */ 278 | bool shadow_delay_en : 1; //!< Delays shadowing of external sensor data until all data has been received. 279 | bool wait_for_es : 1; /**< Delays the data ready interrupt until external sensor data is loaded. 280 | * (if data ready interrupt is enabled). */ 281 | auxi2c_trans_t transition : 1; /**< Transition condition from one slave read to the next slave read, 282 | * default is `restart`. */ 283 | } auxi2c_config_t; 284 | 285 | /** 286 | * @brief Auxiliary I2C Slave configuration struct. 287 | * Note on SWAP: \n 288 | * Swap bytes may be needed when external data is in another order. \n 289 | * The option swap_en, swaps bytes when reading both the low and high byte of a word. \n 290 | * 291 | * For example, if `rxlength = 4`, and if `reg_addr = 0x1`, and group = `ODD_NUM`: 292 | * 1. The first byte read from address 0x1 will be stored at `EXT_SENS_DATA_00`; 293 | * 2. the second and third bytes will be read and swapped, so the data read from address 0x2 294 | * will be stored at `EXT_SENS_DATA_02`, and the data read from address 0x3 will be stored at `EXT_SENS_DATA_03`; 295 | * 3. The last byte read from address 0x4 will be stored at `EXT_SENS_DATA_04`. 296 | * 297 | * Note: there is nothing to swap after reading the first byte if `reg_addr[bit 0] = 1`, 298 | * or if the last byte read has a register address `[bit 0] = 0`. The opposite is true for 'group' = `EVEN_NUM`. 299 | * */ 300 | typedef struct 301 | { 302 | auxi2c_slv_t slave; //!< Slave slot 303 | uint8_t addr : 7; //!< Slave device address 304 | auxi2c_rw_t rw : 1; //!< Read/write flag 305 | uint8_t reg_addr; //!< Register address to read/write to 306 | bool reg_dis : 1; /**< When set, the transaction does not write the register address, it will only read data, 307 | * or write data. */ 308 | bool sample_delay_en : 1; //!< enable delay specifided in master config, sample_delay, for this slave in specific. 309 | union 310 | { 311 | // when reading 312 | struct 313 | { 314 | bool swap_en : 1; //!< Enable swap of bytes when reading both the low and high byte of a word. See the 315 | //!< note. 316 | auxi2c_eow_t end_of_word : 1; /**< Define at which register address a word ends, for swap low and high 317 | * bytes of the word (when swap enabled). */ 318 | uint8_t rxlength : 4; //!< Number of bytes to read, when set to read, max = 15. 319 | }; 320 | // when writing 321 | uint8_t txdata; //!< Data to transfer when slave is set to write. 322 | }; 323 | } auxi2c_slv_config_t; 324 | 325 | /*! Auxiliary I2C master status register data */ 326 | typedef uint8_t auxi2c_stat_t; 327 | static constexpr auxi2c_stat_t AUXI2C_STAT_FSYNC = (1 << regs::I2CMST_STAT_PASS_THROUGH_BIT); 328 | static constexpr auxi2c_stat_t AUXI2C_STAT_LOST_ARB = (1 << regs::I2CMST_STAT_LOST_ARB_BIT); 329 | static constexpr auxi2c_stat_t AUXI2C_STAT_SLV4_DONE = (1 << regs::I2CMST_STAT_SLV4_DONE_BIT); 330 | static constexpr auxi2c_stat_t AUXI2C_STAT_SLV4_NACK = (1 << regs::I2CMST_STAT_SLV4_NACK_BIT); 331 | static constexpr auxi2c_stat_t AUXI2C_STAT_SLV3_NACK = (1 << regs::I2CMST_STAT_SLV3_NACK_BIT); 332 | static constexpr auxi2c_stat_t AUXI2C_STAT_SLV2_NACK = (1 << regs::I2CMST_STAT_SLV2_NACK_BIT); 333 | static constexpr auxi2c_stat_t AUXI2C_STAT_SLV1_NACK = (1 << regs::I2CMST_STAT_SLV1_NACK_BIT); 334 | static constexpr auxi2c_stat_t AUXI2C_STAT_SLV0_NACK = (1 << regs::I2CMST_STAT_SLV0_NACK_BIT); 335 | 336 | #if defined CONFIG_MPU9150 || (defined CONFIG_MPU6050 && !defined CONFIG_MPU6000) 337 | /*! Auxiliary I2C bus VDDIO level [MPU6050 / MPU9150 only] */ 338 | typedef enum { AUXVDDIO_LVL_VLOGIC = 0, AUXVDDIO_LVL_VDD = 1 } auxvddio_lvl_t; 339 | #endif 340 | 341 | /*! Interrupt active level */ 342 | typedef enum { INT_LVL_ACTIVE_HIGH = 0, INT_LVL_ACTIVE_LOW = 1 } int_lvl_t; 343 | 344 | /*! Interrupt drive state */ 345 | typedef enum { INT_DRV_PUSHPULL = 0, INT_DRV_OPENDRAIN = 1 } int_drive_t; 346 | 347 | /*! Interrupt mode */ 348 | typedef enum { INT_MODE_PULSE50US = 0, INT_MODE_LATCH = 1 } int_mode_t; 349 | 350 | /*! Interrupt clear mode */ 351 | typedef enum { INT_CLEAR_STATUS_REG = 0, INT_CLEAR_ANYREAD = 1 } int_clear_t; 352 | 353 | /*! Interrupt configuration struct */ 354 | typedef struct 355 | { 356 | int_lvl_t level : 1; 357 | int_drive_t drive : 1; 358 | int_mode_t mode : 1; 359 | int_clear_t clear : 1; 360 | } int_config_t; 361 | 362 | /** 363 | * @brief Enable features to generate signal at Interrupt pin 364 | * @note Freefall and zero motion only available to MPU6000 / MPU6050 / MPU9150 365 | * */ 366 | typedef uint8_t int_en_t; 367 | static constexpr int_en_t INT_EN_NONE = (0x0); 368 | static constexpr int_en_t INT_EN_MOTION_DETECT = (1 << regs::INT_ENABLE_MOTION_BIT); 369 | static constexpr int_en_t INT_EN_FIFO_OVERFLOW = (1 << regs::INT_ENABLE_FIFO_OFLOW_BIT); 370 | static constexpr int_en_t INT_EN_I2C_MST_FSYNC = (1 << regs::INT_ENABLE_I2C_MST_FSYNC_BIT); // int from I2C_MST_STATUS 371 | static constexpr int_en_t INT_EN_PLL_READY = (1 << regs::INT_ENABLE_PLL_RDY_BIT); 372 | static constexpr int_en_t INT_EN_DMP_READY = (1 << regs::INT_ENABLE_DMP_RDY_BIT); 373 | static constexpr int_en_t INT_EN_RAWDATA_READY = (1 << regs::INT_ENABLE_RAW_DATA_RDY_BIT); 374 | #if defined CONFIG_MPU6000 || defined CONFIG_MPU6050 || defined CONFIG_MPU9150 375 | static constexpr int_en_t INT_EN_FREE_FALL = (1 << regs::INT_ENABLE_FREEFALL_BIT); 376 | static constexpr int_en_t INT_EN_ZERO_MOTION = (1 << regs::INT_ENABLE_ZEROMOT_BIT); 377 | #endif 378 | 379 | /** 380 | * @brief Interrupt Status 381 | * @note Freefall and zero motion only available to MPU6000 / MPU6050 / MPU9150 382 | * */ 383 | typedef uint8_t int_stat_t; 384 | static constexpr int_stat_t INT_STAT_MOTION_DETECT = (1 << regs::INT_STATUS_MOTION_BIT); 385 | static constexpr int_stat_t INT_STAT_FIFO_OVERFLOW = (1 << regs::INT_STATUS_FIFO_OFLOW_BIT); 386 | static constexpr int_stat_t INT_STAT_I2C_MST_FSYNC = (1 << regs::INT_STATUS_I2C_MST_BIT); // int from I2C_MST_STATUS 387 | static constexpr int_stat_t INT_STAT_PLL_READY = (1 << regs::INT_STATUS_PLL_RDY_BIT); 388 | static constexpr int_stat_t INT_STAT_DMP_READY = (1 << regs::INT_STATUS_DMP_RDY_BIT); 389 | static constexpr int_stat_t INT_STAT_RAWDATA_READY = (1 << regs::INT_STATUS_RAW_DATA_RDY_BIT); 390 | #if defined CONFIG_MPU6000 || defined CONFIG_MPU6050 || defined CONFIG_MPU9150 391 | static constexpr int_stat_t INT_STAT_FREE_FALL = (1 << regs::INT_STATUS_FREEFALL_BIT); 392 | static constexpr int_stat_t INT_STAT_ZERO_MOTION = (1 << regs::INT_STATUS_ZEROMOT_BIT); 393 | #endif 394 | 395 | #ifdef CONFIG_MPU6500 396 | /*! MPU6500 Fifo size */ 397 | typedef enum { FIFO_SIZE_512B = 0, FIFO_SIZE_1K = 1, FIFO_SIZE_2K = 2, FIFO_SIZE_4K = 3 } fifo_size_t; 398 | #endif 399 | 400 | /*! DMP Interrupt mode */ 401 | typedef enum { DMP_INT_MODE_PACKET = 0, DMP_INT_MODE_GESTURE = 1 } dmp_int_mode_t; 402 | 403 | /*! FIFO mode */ 404 | typedef enum { 405 | FIFO_MODE_OVERWRITE = 0, //!< when fifo full, additional writes will be written to fifo, replacing the oldest data. 406 | FIFO_MODE_STOP_FULL = 1 //!< when fifo full, additional writes will not be written to fifo. 407 | } fifo_mode_t; 408 | 409 | /*! FIFO configuration, enable sensors to be written to FIFO */ 410 | typedef uint16_t fifo_config_t; 411 | static constexpr fifo_config_t FIFO_CFG_NONE = (0x0); 412 | static constexpr fifo_config_t FIFO_CFG_GYRO = 413 | (1 << regs::FIFO_XGYRO_EN_BIT | 1 << regs::FIFO_YGYRO_EN_BIT | 1 << regs::FIFO_ZGYRO_EN_BIT); 414 | static constexpr fifo_config_t FIFO_CFG_ACCEL = (1 << regs::FIFO_ACCEL_EN_BIT); 415 | static constexpr fifo_config_t FIFO_CFG_TEMPERATURE = (1 << regs::FIFO_TEMP_EN_BIT); 416 | static constexpr fifo_config_t FIFO_CFG_SLAVE0 = (1 << regs::FIFO_SLV_0_EN_BIT); 417 | static constexpr fifo_config_t FIFO_CFG_SLAVE1 = (1 << regs::FIFO_SLV_1_EN_BIT); 418 | static constexpr fifo_config_t FIFO_CFG_SLAVE2 = (1 << regs::FIFO_SLV_2_EN_BIT); 419 | static constexpr fifo_config_t FIFO_CFG_SLAVE3 = (1 << 8); 420 | #if defined CONFIG_MPU_AK89xx 421 | static constexpr fifo_config_t FIFO_CFG_COMPASS = (FIFO_CFG_SLAVE0); // 8 bytes 422 | #endif 423 | 424 | // Enable DMP features 425 | /* @note DMP_FEATURE_LP_QUAT and DMP_FEATURE_6X_LP_QUAT are mutually exclusive. 426 | * @note DMP_FEATURE_SEND_RAW_GYRO and DMP_FEATURE_SEND_CAL_GYRO are also 427 | * mutually exclusive. 428 | * @note DMP_FEATURE_PEDOMETER is always enabled. 429 | */ 430 | 431 | /* typedef uint16_t dmp_features_t; 432 | static constexpr dmp_features_t DMP_FEATURE_TAP = {0x001}; 433 | static constexpr dmp_features_t DMP_FEATURE_ANDROID_ORIENT = {0x002}; 434 | static constexpr dmp_features_t DMP_FEATURE_LP_QUAT = {0x004}; 435 | static constexpr dmp_features_t DMP_FEATURE_PEDOMETER = {0x008}; 436 | static constexpr dmp_features_t DMP_FEATURE_6X_LP_QUAT = {0x010}; 437 | static constexpr dmp_features_t DMP_FEATURE_GYRO_CAL = {0x020}; 438 | static constexpr dmp_features_t DMP_FEATURE_SEND_RAW_ACCEL = {0x040}; 439 | static constexpr dmp_features_t DMP_FEATURE_SEND_RAW_GYRO = {0x080}; 440 | static constexpr dmp_features_t DMP_FEATURE_SEND_CAL_GYRO = {0x100}; 441 | 442 | // DMP Tap axes 443 | typedef uint8_t dmp_tap_axis_t; 444 | static constexpr dmp_tap_axis_t DMP_TAP_X {0x30}; 445 | static constexpr dmp_tap_axis_t DMP_TAP_Y {0x0C}; 446 | static constexpr dmp_tap_axis_t DMP_TAP_Z {0x03}; 447 | static constexpr dmp_tap_axis_t DMP_TAP_XYZ {0x3F}; 448 | */ 449 | 450 | /*! Generic axes struct to store sensors' data */ 451 | template 452 | struct axes_t 453 | { 454 | union 455 | { 456 | type_t xyz[3] = {0}; 457 | struct 458 | { 459 | type_t x; 460 | type_t y; 461 | type_t z; 462 | }; 463 | }; 464 | type_t& operator[](int i) { return xyz[i]; } 465 | }; 466 | // Ready-to-use axes types 467 | typedef axes_t raw_axes_t; //!< Axes type to hold gyroscope, accelerometer, magnetometer raw data. 468 | typedef axes_t float_axes_t; //!< Axes type to hold converted sensor data. 469 | 470 | /*! Sensors struct for fast reading all sensors at once */ 471 | typedef struct 472 | { 473 | raw_axes_t accel; //!< accelerometer 474 | raw_axes_t gyro; //!< gyroscope 475 | int16_t temp; //!< temperature 476 | uint8_t* extsens; //!< external sensor buffer 477 | #if defined CONFIG_MPU_AK89xx 478 | raw_axes_t mag; //!< magnetometer 479 | #endif 480 | } sensors_t; 481 | 482 | // ============ 483 | // MAGNETOMETER 484 | // ============ 485 | #ifdef CONFIG_MPU_AK89xx 486 | static constexpr uint8_t COMPASS_I2CADDRESS = 0xC; 487 | static constexpr uint8_t COMPASS_SAMPLE_RATE_MAX = 100; // 100 Hz 488 | 489 | /*! Magnetometer operation modes */ 490 | typedef enum { 491 | MAG_MODE_POWER_DOWN = 0x0, 492 | MAG_MODE_SINGLE_MEASURE = 0x1, 493 | MAG_MODE_SELF_TEST = 0x8, 494 | MAG_MODE_FUSE_ROM = 0xF, 495 | #ifdef CONFIG_MPU_AK8963 496 | MAG_MODE_CONTINUOUS_8HZ = 0x2, //!< @warning Not yet supported. 497 | MAG_MODE_CONTINUOUS_100HZ = 0x6, //!< @warning Not yet supported. 498 | MAG_MODE_EXTERNAL_TRIGGER = 0x4 //!< @warning Not supported. 499 | #endif 500 | } mag_mode_t; 501 | 502 | /*! Magnetometer sensor status 1 */ 503 | typedef uint8_t mag_stat1_t; 504 | static constexpr mag_stat1_t MAG_STAT1_DATA_RDY = {1 << regs::mag::STATUS1_DATA_RDY_BIT}; 505 | #ifdef CONFIG_MPU_AK8963 506 | static constexpr mag_stat1_t MAG_STAT1_DATA_OVERRUN = {1 << regs::mag::STATUS1_DATA_OVERRUN_BIT}; 507 | #endif 508 | 509 | /*! Magnetometer sensor status 2 */ 510 | typedef uint8_t mag_stat2_t; 511 | static constexpr mag_stat2_t MAG_STAT2_SENSOR_OVERFLOW = {1 << regs::mag::STATUS2_OVERFLOW_BIT}; 512 | #ifdef CONFIG_MPU_AK8963 513 | static constexpr mag_stat2_t MAG_STAT2_BIT_OUTPUT_SETTING = {1 << regs::mag::STATUS2_BIT_OUTPUT_M_BIT}; 514 | #elif CONFIG_MPU_AK8975 515 | static constexpr mag_stat2_t MAG_STAT2_DATA_ERROR{1 << regs::mag::STATUS2_DATA_ERROR_BIT}; 516 | #endif 517 | 518 | #ifdef CONFIG_MPU_AK8963 519 | /*! Magnetometer sensitivity */ 520 | typedef enum { 521 | MAG_SENSITIVITY_0_6_uT = 0, //!< 0.6 uT/LSB = 14-bit output 522 | MAG_SENSITIVITY_0_15_uT = 1, //!< 0.15 uT/LSB = 16-bit output 523 | } mag_sensy_t; 524 | #endif 525 | 526 | // Auxiliary I2C slaves that operate the Magnetometer (do not change) 527 | static constexpr auxi2c_slv_t MAG_SLAVE_READ_DATA = AUXI2C_SLAVE_0; // read measurement data 528 | static constexpr auxi2c_slv_t MAG_SLAVE_CHG_MODE = AUXI2C_SLAVE_1; // change mode to single measure 529 | 530 | static constexpr uint8_t MAG_DATA_LENGTH = 8; // bytes 531 | 532 | #endif // end of Magnetometer stuff 533 | 534 | /*! Self-Test results */ 535 | typedef uint8_t selftest_t; 536 | static constexpr selftest_t SELF_TEST_PASS{0x0}; 537 | static constexpr selftest_t SELF_TEST_GYRO_FAIL{1 << 0}; // 0x1 538 | static constexpr selftest_t SELF_TEST_ACCEL_FAIL{1 << 1}; // 0x2 539 | 540 | } // namespace types 541 | 542 | } // namespace mpud 543 | 544 | #endif /* end of include guard: _MPU_TYPES_HPP_ */ 545 | -------------------------------------------------------------------------------- /include/mpu/utils.hpp: -------------------------------------------------------------------------------- 1 | // ========================================================================= 2 | // This library is placed under the MIT License 3 | // Copyright 2017-2018 Natanael Josue Rabello. All rights reserved. 4 | // For the license information refer to LICENSE file in root directory. 5 | // ========================================================================= 6 | 7 | /** 8 | * @file mpu/utils.hpp 9 | * @brief MPU Utilities 10 | */ 11 | 12 | #ifndef _MPU_UTILS_HPP_ 13 | #define _MPU_UTILS_HPP_ 14 | 15 | #include 16 | #include "sdkconfig.h" 17 | 18 | /*! MPU Driver namespace */ 19 | namespace mpud 20 | { 21 | /*! Utilities namespace */ 22 | inline namespace utils 23 | { 24 | } // namespace utils 25 | 26 | } // namespace mpud 27 | 28 | #endif /* end of include guard: _MPU_UTILS_HPP_ */ 29 | -------------------------------------------------------------------------------- /src/MPUdmp.cpp: -------------------------------------------------------------------------------- 1 | // ========================================================================= 2 | // This library is placed under the MIT License 3 | // Copyright 2017-2018 Natanael Josue Rabello. All rights reserved. 4 | // For the license information refer to LICENSE file in root directory. 5 | // ========================================================================= 6 | 7 | /** 8 | * @file MPUdmp.cpp 9 | * Implement MPU class with DMP interface. 10 | */ 11 | 12 | #include "MPUdmp.hpp" 13 | #include "MPU.hpp" 14 | 15 | /*! MPU Driver namespace */ 16 | namespace mpud 17 | { 18 | /*! DMP namespace */ 19 | namespace dmp 20 | { 21 | } // namespace dmp 22 | 23 | } // namespace mpud 24 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # MPU Unit Test 2 | 3 | **Structure:** 4 | 5 | + `mpu-tests` contains the tests themselves. 6 | + `unit-test-app` is the project to flash and run the tests. Command: `make flash monitor`. Works the same as the default unit-test-app from _esp-idf_. 7 | 8 | There is a test configuration menu \[MPU test config\] in menuconfig -> component config, to setup the connection pins, speed, etc. 9 | 10 | **Note:** 11 | 12 | Before trying to build the unit-test-app, add the I2Cbus/SPIbus library path to `EXTRA_COMPONENT_DIRS` in the unit-test-app/Makefile. 13 | 14 | The mpu tests also work with the default unit-test-app from _esp-idf_. If you \'unit-test\' from there, do not forget to add the MPU component path as well as MPU/test/mpu-tests directory to `EXTRA_COMPONENT_DIRS` in the Makefile. (and the above dependencies). 15 | 16 | See [Unit Testing in ESP32] for more information. 17 | 18 | **Current tests:** 19 | 20 | 1. basic test 21 | 1. sample rate measurement 22 | 1. max sample rate test 23 | 1. low power accelerometer mode 24 | 1. interrupt configuration 25 | 1. basic auxiliary I2C configuration 26 | 1. slave 4 transfers 27 | 1. external frame synchronization (FSYNC pin) 28 | 1. sensor data test 29 | 1. standby mode 30 | 1. FIFO buffer 31 | 1. offset test 32 | 1. self-test check 33 | 1. motion detection and wake-on-motion mode 34 | 1. free-fall detection 35 | 1. zero-motion detection 36 | 1. compass configuration 37 | 38 | --- 39 | 40 | [Unit Testing in ESP32]: https://esp-idf.readthedocs.io/en/latest/api-guides/unit-tests.html -------------------------------------------------------------------------------- /test/mpu-tests/Kconfig: -------------------------------------------------------------------------------- 1 | menu "MPU Test config" 2 | 3 | comment "MPU Test configuration depends on MPU Component configuration" 4 | 5 | # 6 | # I2CBUS configuration 7 | # 8 | 9 | choice MPU_TEST_I2CBUS_PORT 10 | prompt "Main I2C controller port" 11 | default MPU_TEST_I2C0 12 | depends on MPU_I2C 13 | 14 | config MPU_TEST_I2C0 15 | bool "I2C0" 16 | config MPU_TEST_I2C1 17 | bool "I2C1" 18 | 19 | endchoice 20 | 21 | config MPU_TEST_I2CBUS_PORT 22 | int 23 | default 0 if MPU_TEST_I2C0 24 | default 1 if MPU_TEST_I2C1 25 | depends on MPU_I2C 26 | 27 | choice MPU_TEST_I2CBUS_ADDR 28 | prompt "I2C Address" 29 | default MPU_TEST_ADDR_ADO_LOW 30 | depends on MPU_I2C 31 | 32 | config MPU_TEST_ADDR_AD0_LOW 33 | bool "AD0 low" 34 | config MPU_TEST_ADDR_AD0_HIGH 35 | bool "AD0 high" 36 | 37 | endchoice 38 | 39 | config MPU_TEST_I2CBUS_ADDR 40 | int 41 | default 0 if MPU_TEST_ADDR_AD0_LOW 42 | default 1 if MPU_TEST_ADDR_AD0_HIGH 43 | depends on MPU_I2C 44 | 45 | 46 | config MPU_TEST_I2CBUS_CLOCK_HZ 47 | int "Main I2C clock speed (hz)" 48 | default 400000 49 | depends on MPU_I2C 50 | 51 | config MPU_TEST_I2CBUS_SDA_PIN 52 | int "Main I2C SDA pin" 53 | range 0 33 54 | default 21 55 | depends on MPU_I2C 56 | 57 | config MPU_TEST_I2CBUS_SCL_PIN 58 | int "Main I2C SCL pin" 59 | range 0 33 60 | default 22 61 | depends on MPU_I2C 62 | 63 | 64 | # 65 | # SPIBUS configuration 66 | # 67 | 68 | choice MPU_TEST_SPIBUS_HOST 69 | prompt "SPI device host" 70 | default MPU_TEST_HSPI 71 | depends on MPU_SPI 72 | 73 | config MPU_TEST_HSPI 74 | bool "HSPI" 75 | config MPU_TEST_VSPI 76 | bool "VSPI" 77 | 78 | endchoice 79 | 80 | config MPU_TEST_SPIBUS_HOST 81 | int 82 | default 1 if MPU_TEST_HSPI 83 | default 2 if MPU_TEST_VSPI 84 | depends on MPU_SPI 85 | 86 | 87 | config MPU_TEST_SPIBUS_CLOCK_HZ 88 | int "SPI clock speed (Hz)" 89 | default 1000000 90 | depends on MPU_SPI 91 | 92 | config MPU_TEST_SPIBUS_MOSI_PIN 93 | int "SPI MOSI pin" 94 | range 0 33 95 | default 5 96 | depends on MPU_SPI 97 | 98 | config MPU_TEST_SPIBUS_MISO_PIN 99 | int "SPI MISO pin" 100 | range 0 39 101 | default 17 102 | depends on MPU_SPI 103 | 104 | config MPU_TEST_SPIBUS_SCLK_PIN 105 | int "SPI SCLK pin" 106 | range 0 33 107 | default 23 108 | depends on MPU_SPI 109 | 110 | config MPU_TEST_SPIBUS_CS_PIN 111 | int "SPI CS pin" 112 | range 0 33 113 | default 16 114 | depends on MPU_SPI 115 | 116 | 117 | # 118 | # MPU Test 119 | # 120 | 121 | config MPU_TEST_AUXI2C_SDA_PIN 122 | int "Auxiliary I2C SDA pin" 123 | range 0 33 124 | default 33 125 | 126 | config MPU_TEST_AUXI2C_SCL_PIN 127 | int "Auxiliary I2C SCL pin" 128 | range 0 33 129 | default 26 130 | 131 | config MPU_TEST_INTERRUPT_PIN 132 | int "Interrupt pin" 133 | range 0 40 134 | default 35 135 | 136 | config MPU_TEST_FSYNC_PIN 137 | int "FSYNC pin" 138 | range 0 33 139 | default 14 140 | 141 | 142 | endmenu -------------------------------------------------------------------------------- /test/mpu-tests/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Component makefile. 3 | # 4 | 5 | COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive 6 | -------------------------------------------------------------------------------- /test/mpu-tests/test_mpu.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file test_mpu.cpp 3 | * Test file for MPU Driver 4 | */ 5 | 6 | #include "stdio.h" 7 | #include "stdint.h" 8 | #include "sdkconfig.h" 9 | #include "esp_attr.h" 10 | #include "esp_err.h" 11 | #include "esp_log.h" 12 | #include "driver/i2c.h" 13 | #include "driver/gpio.h" 14 | #include "driver/spi_master.h" 15 | #include "esp_intr_alloc.h" 16 | #include "freertos/FreeRTOS.h" 17 | #include "freertos/task.h" 18 | #include "unity.h" 19 | #include "unity_config.h" 20 | #include "MPU.hpp" 21 | #include "mpu/registers.hpp" 22 | #include "mpu/types.hpp" 23 | #include "mpu/utils.hpp" 24 | #include "mpu/math.hpp" 25 | 26 | namespace test { 27 | /** 28 | * Bus type 29 | * */ 30 | #ifdef CONFIG_MPU_I2C 31 | I2C_t& i2c = getI2C((i2c_port_t)CONFIG_MPU_TEST_I2CBUS_PORT); 32 | #elif CONFIG_MPU_SPI 33 | SPI_t& spi = getSPI((spi_host_device_t)CONFIG_MPU_TEST_SPIBUS_HOST); 34 | spi_device_handle_t mpuSpiHandle; 35 | #endif 36 | /** 37 | * Hold state of bus init. 38 | * If a test fail, isBusInit stays true, so is not initialized again 39 | * */ 40 | bool isBusInit = false; 41 | /** 42 | * MPU class modified to initialize the bus automaticaly when 43 | * instantiated, and close when object is destroyed. 44 | * Also, resets MPU on construction and destruction. 45 | * */ 46 | class MPU : public mpud::MPU { 47 | public: 48 | MPU() : mpud::MPU() { 49 | #ifdef CONFIG_MPU_I2C 50 | if (!isBusInit) { 51 | i2c.begin((gpio_num_t)CONFIG_MPU_TEST_I2CBUS_SDA_PIN, (gpio_num_t)CONFIG_MPU_TEST_I2CBUS_SCL_PIN, 52 | CONFIG_MPU_TEST_I2CBUS_CLOCK_HZ); 53 | } 54 | this->setBus(i2c); 55 | this->setAddr((mpud::mpu_i2caddr_t)(CONFIG_MPU_TEST_I2CBUS_ADDR + mpud::MPU_I2CADDRESS_AD0_LOW)); 56 | 57 | #elif CONFIG_MPU_SPI 58 | if (!isBusInit) { 59 | spi.begin(CONFIG_MPU_TEST_SPIBUS_MOSI_PIN, CONFIG_MPU_TEST_SPIBUS_MISO_PIN, 60 | CONFIG_MPU_TEST_SPIBUS_SCLK_PIN); 61 | spi.addDevice(0, CONFIG_MPU_TEST_SPIBUS_CLOCK_HZ, CONFIG_MPU_TEST_SPIBUS_CS_PIN, &mpuSpiHandle); 62 | } 63 | this->setBus(spi); 64 | this->setAddr(mpuSpiHandle); 65 | #endif 66 | 67 | if (!isBusInit) isBusInit = true; 68 | this->reset(); 69 | } 70 | 71 | ~MPU() { 72 | this->reset(); 73 | #ifdef CONFIG_MPU_I2C 74 | (void)0; 75 | #elif CONFIG_MPU_SPI 76 | spi.removeDevice(this->getAddr()); 77 | #endif 78 | this->bus->close(); 79 | isBusInit = false; 80 | } 81 | }; 82 | using MPU_t = MPU; 83 | } // namespace test 84 | 85 | 86 | 87 | /** Setup gpio and ISR for interrupt pin (ISR must IRAM_ATTR)*/ 88 | static esp_err_t mpuConfigInterrupt(void (*isr)(void*), void * arg) { 89 | esp_err_t ret = ESP_OK; 90 | gpio_config_t io_config{}; 91 | io_config.pin_bit_mask = ((uint64_t) 1 << CONFIG_MPU_TEST_INTERRUPT_PIN); 92 | io_config.mode = GPIO_MODE_INPUT; 93 | io_config.pull_up_en = GPIO_PULLUP_DISABLE; 94 | io_config.pull_down_en = GPIO_PULLDOWN_DISABLE; 95 | io_config.intr_type = GPIO_INTR_POSEDGE; 96 | ret = gpio_config(&io_config); 97 | if (ret) return ret; 98 | ret = gpio_install_isr_service(ESP_INTR_FLAG_IRAM); 99 | if (ret) return ret; 100 | ret = gpio_isr_handler_add((gpio_num_t)CONFIG_MPU_TEST_INTERRUPT_PIN, isr, arg); 101 | return ret; 102 | } 103 | 104 | static esp_err_t mpuRemoveInterrupt() { 105 | esp_err_t ret; 106 | ret = gpio_isr_handler_remove((gpio_num_t)CONFIG_MPU_TEST_INTERRUPT_PIN); 107 | if (ret) return ret; 108 | gpio_uninstall_isr_service(); 109 | return ret; 110 | } 111 | 112 | /** ISR to notify a task */ 113 | static void IRAM_ATTR mpuTaskNotifier(void *arg) { 114 | TaskHandle_t taskhandle = (TaskHandle_t) arg; 115 | BaseType_t HPTaskWoken = pdFALSE; 116 | vTaskNotifyGiveFromISR(taskhandle, &HPTaskWoken); 117 | if (HPTaskWoken == pdTRUE) 118 | portYIELD_FROM_ISR(); 119 | } 120 | 121 | /** ISR to measure sample rate */ 122 | static void IRAM_ATTR mpuInterruptCounterISR(void * arg) { 123 | int& count = *((int*) arg); 124 | count++; 125 | } 126 | 127 | /** Routine to measure sample rate */ 128 | static void mpuMeasureSampleRate(test::MPU_t& mpu, uint16_t rate, int numOfSamples) { 129 | const int threshold = 0.05 * rate; // percentage, 0.05 = 5% 130 | int count = 0; 131 | printf("> Sample rate set to %d Hz\n", rate); 132 | printf("> Now measuring true interrupt rate... wait %d secs\n", numOfSamples); 133 | TEST_ESP_OK( mpuConfigInterrupt(mpuInterruptCounterISR, (void*) &count)); 134 | // enable raw-sensor-data-ready interrupt to propagate to interrupt pin. 135 | TEST_ESP_OK( mpu.writeByte(mpud::regs::INT_ENABLE, (1 << mpud::regs::INT_ENABLE_RAW_DATA_RDY_BIT))); 136 | vTaskDelay((numOfSamples * 1000) / portTICK_PERIOD_MS); 137 | TEST_ESP_OK( mpuRemoveInterrupt()); 138 | uint16_t finalRate = count / numOfSamples; 139 | printf("> Final measured rate is %d Hz\n", finalRate); 140 | const int minRate = rate - threshold; 141 | const int maxRate = rate + threshold; 142 | TEST_ASSERT ((finalRate >= minRate) && (finalRate <= maxRate)); 143 | } 144 | 145 | 146 | 147 | 148 | /****************************************** 149 | * TESTS ---------------------------------- 150 | * ***************************************/ 151 | 152 | TEST_CASE("MPU basic test", "[MPU]") 153 | { 154 | test::MPU_t mpu; 155 | TEST_ESP_OK( mpu.testConnection()); 156 | // sleep 157 | TEST_ESP_OK( mpu.setSleep(true)); 158 | TEST_ASSERT_TRUE( mpu.getSleep()); 159 | TEST_ESP_OK( mpu.lastError()); 160 | TEST_ESP_OK( mpu.setSleep(false)); 161 | TEST_ASSERT_FALSE( mpu.getSleep()) 162 | TEST_ESP_OK( mpu.lastError()); 163 | // initialize 164 | TEST_ESP_OK( mpu.initialize()); 165 | // clock source 166 | mpud::clock_src_t clock_src = mpud::CLOCK_INTERNAL; 167 | TEST_ESP_OK( mpu.setClockSource(clock_src)); 168 | TEST_ASSERT_EQUAL_INT(clock_src, mpu.getClockSource()); 169 | TEST_ESP_OK( mpu.lastError()); 170 | clock_src = mpud::CLOCK_PLL; 171 | TEST_ESP_OK( mpu.setClockSource(clock_src)); 172 | TEST_ASSERT_EQUAL_INT(clock_src, mpu.getClockSource()); 173 | TEST_ESP_OK( mpu.lastError()); 174 | // digital low pass filter 175 | mpud::dlpf_t dlpf = mpud::DLPF_10HZ; 176 | TEST_ESP_OK( mpu.setDigitalLowPassFilter(dlpf)); 177 | TEST_ASSERT_EQUAL_INT(dlpf, mpu.getDigitalLowPassFilter()); 178 | TEST_ESP_OK( mpu.lastError()); 179 | dlpf = mpud::DLPF_188HZ; 180 | TEST_ESP_OK( mpu.setDigitalLowPassFilter(dlpf)); 181 | TEST_ASSERT_EQUAL_INT(dlpf, mpu.getDigitalLowPassFilter()); 182 | TEST_ESP_OK( mpu.lastError()); 183 | // full scale range 184 | mpud::gyro_fs_t gyro_fs = mpud::GYRO_FS_500DPS; 185 | TEST_ESP_OK( mpu.setGyroFullScale(gyro_fs)); 186 | TEST_ASSERT_EQUAL_INT(gyro_fs, mpu.getGyroFullScale()); 187 | TEST_ESP_OK( mpu.lastError()); 188 | mpud::accel_fs_t accel_fs = mpud::ACCEL_FS_16G; 189 | TEST_ESP_OK( mpu.setAccelFullScale(accel_fs)); 190 | TEST_ASSERT_EQUAL_INT(accel_fs, mpu.getAccelFullScale()); 191 | TEST_ESP_OK( mpu.lastError()); 192 | } 193 | 194 | 195 | 196 | TEST_CASE("MPU sample rate measurement", "[MPU]") 197 | { 198 | test::MPU_t mpu; 199 | TEST_ESP_OK( mpu.testConnection()); 200 | TEST_ESP_OK( mpu.initialize()); 201 | /** invalid rate/state check */ 202 | TEST_ESP_OK( mpu.setSampleRate(0)); 203 | TEST_ESP_OK( mpu.setSampleRate(1)); 204 | TEST_ESP_OK( mpu.setSampleRate(1001)); 205 | TEST_ESP_OK( mpu.setSampleRate(4000)); 206 | TEST_ESP_OK( mpu.setSampleRate(512)); 207 | TEST_ESP_OK( mpu.setSampleRate(258)); 208 | #ifdef CONFIG_MPU6500 209 | TEST_ESP_OK( mpu.setFchoice(mpud::FCHOICE_2)); 210 | TEST_ASSERT_EQUAL_INT( mpud::FCHOICE_2, mpu.getFchoice()); 211 | TEST_ESP_OK( mpu.lastError()); 212 | TEST_ESP_OK( mpu.setSampleRate(25)); 213 | TEST_ASSERT_NOT_EQUAL( 25, mpu.getSampleRate()); 214 | TEST_ESP_OK( mpu.lastError()); 215 | TEST_ESP_OK( mpu.setFchoice(mpud::FCHOICE_3)); 216 | TEST_ASSERT_EQUAL_INT( mpud::FCHOICE_3, mpu.getFchoice()); 217 | TEST_ESP_OK( mpu.lastError()); 218 | #endif 219 | /** rate measurement */ 220 | constexpr int numOfSamples = 5; 221 | constexpr uint16_t rates[] = {5, 50, 100, 250, 500, 1000}; 222 | for (auto rate : rates) { 223 | TEST_ESP_OK( mpu.setSampleRate(rate)); 224 | mpuMeasureSampleRate(mpu, rate, numOfSamples); 225 | } 226 | } 227 | 228 | 229 | 230 | TEST_CASE("MPU max sample rate test", "[MPU]") 231 | { 232 | test::MPU_t mpu; 233 | TEST_ESP_OK( mpu.testConnection()); 234 | TEST_ESP_OK( mpu.setSleep(false)); 235 | #ifdef CONFIG_MPU6500 236 | TEST_ESP_OK( mpu.setFchoice(mpud::FCHOICE_0)); 237 | TEST_ASSERT_EQUAL_INT( mpud::FCHOICE_0, mpu.getFchoice()); 238 | TEST_ESP_OK( mpu.lastError()); 239 | #endif 240 | /* measure maximum sample rate consistency */ 241 | uint16_t rate = mpud::SAMPLE_RATE_MAX; 242 | constexpr int numOfSamples = 5; 243 | mpuMeasureSampleRate(mpu, rate, numOfSamples); 244 | } 245 | 246 | 247 | TEST_CASE("MPU low power accelerometer mode", "[MPU]") 248 | { 249 | test::MPU_t mpu; 250 | TEST_ESP_OK( mpu.testConnection()); 251 | TEST_ESP_OK( mpu.initialize()); 252 | /* assert possible configuration */ 253 | TEST_ESP_OK( mpu.setLowPowerAccelMode(true)); 254 | TEST_ASSERT_TRUE( mpu.getLowPowerAccelMode()); 255 | TEST_ESP_OK( mpu.lastError()); 256 | TEST_ESP_OK( mpu.setLowPowerAccelMode(false)); 257 | TEST_ASSERT_FALSE( mpu.getLowPowerAccelMode()); 258 | TEST_ESP_OK( mpu.lastError()); 259 | TEST_ESP_OK( mpu.setLowPowerAccelMode(true)); 260 | TEST_ASSERT_TRUE( mpu.getLowPowerAccelMode()); 261 | TEST_ESP_OK( mpu.lastError()); 262 | /* assert sample rate */ 263 | #if defined CONFIG_MPU6050 264 | constexpr mpud::lp_accel_rate_t lp_accel_rates[] = { 265 | mpud::LP_ACCEL_RATE_5HZ, 266 | mpud::LP_ACCEL_RATE_20HZ, 267 | mpud::LP_ACCEL_RATE_40HZ 268 | }; 269 | constexpr uint16_t rates[] = {5, 20, 40}; 270 | #elif defined CONFIG_MPU6500 271 | constexpr mpud::lp_accel_rate_t lp_accel_rates[] = { 272 | mpud::LP_ACCEL_RATE_1_95HZ, 273 | mpud::LP_ACCEL_RATE_31_25HZ, 274 | mpud::LP_ACCEL_RATE_125HZ 275 | }; 276 | constexpr uint16_t rates[] = {2, 31, 125}; 277 | #endif 278 | constexpr int numOfSamples = 5; 279 | for (int i = 0; i < (sizeof(rates) / sizeof(rates[0])); i++) { 280 | TEST_ESP_OK( mpu.setLowPowerAccelRate(lp_accel_rates[i])); 281 | TEST_ASSERT_EQUAL_INT(lp_accel_rates[i], mpu.getLowPowerAccelRate()); 282 | TEST_ESP_OK( mpu.lastError()); 283 | mpuMeasureSampleRate(mpu, rates[i], numOfSamples); 284 | } 285 | } 286 | 287 | 288 | 289 | TEST_CASE("MPU interrupt configuration", "[MPU]") 290 | { 291 | test::MPU_t mpu; 292 | TEST_ESP_OK( mpu.testConnection()); 293 | TEST_ESP_OK( mpu.initialize()); 294 | // configurations 295 | mpud::int_config_t intconfig = { 296 | .level = mpud::INT_LVL_ACTIVE_LOW, 297 | .drive = mpud::INT_DRV_PUSHPULL, 298 | .mode = mpud::INT_MODE_LATCH, 299 | .clear = mpud::INT_CLEAR_STATUS_REG 300 | }; 301 | TEST_ESP_OK( mpu.setInterruptConfig(intconfig)); 302 | mpud::int_config_t retIntconfig = mpu.getInterruptConfig(); 303 | TEST_ESP_OK( mpu.lastError()); 304 | TEST_ASSERT( retIntconfig.level == intconfig.level); 305 | TEST_ASSERT( retIntconfig.drive == intconfig.drive); 306 | TEST_ASSERT( retIntconfig.mode == intconfig.mode); 307 | TEST_ASSERT( retIntconfig.clear == intconfig.clear); 308 | // test all interrupt setups 309 | unsigned int interrupts; 310 | mpud::int_en_t retInterrupts; 311 | for (interrupts = 0; interrupts <= 0xFF; ++interrupts) { 312 | TEST_ESP_OK( mpu.setInterruptEnabled(interrupts)); 313 | vTaskDelay(20 / portTICK_PERIOD_MS); 314 | retInterrupts = mpu.getInterruptEnabled(); 315 | TEST_ESP_OK( mpu.lastError()); 316 | if (interrupts == retInterrupts) { 317 | printf("(0x%X) > OK\n", interrupts); 318 | } else { 319 | printf("(0x%X) > Incompatible interrupt setup, actual: 0x%X\n", interrupts, retInterrupts); 320 | } 321 | } 322 | } 323 | 324 | 325 | 326 | TEST_CASE("MPU basic auxiliary I2C configuration", "[MPU]") 327 | { 328 | test::MPU_t mpu; 329 | TEST_ESP_OK( mpu.testConnection()); 330 | TEST_ESP_OK( mpu.initialize()); 331 | /* master configs */ 332 | mpud::auxi2c_config_t auxi2cConfig{}; 333 | auxi2cConfig.clock = mpud::AUXI2C_CLOCK_258KHZ; 334 | auxi2cConfig.multi_master_en = true; 335 | auxi2cConfig.transition = mpud::AUXI2C_TRANS_STOP; 336 | auxi2cConfig.sample_delay = 31; 337 | auxi2cConfig.shadow_delay_en = true; 338 | auxi2cConfig.wait_for_es = false; 339 | TEST_ESP_OK( mpu.setAuxI2CConfig(auxi2cConfig)); 340 | // check config 341 | mpud::auxi2c_config_t retAuxi2cConfig{}; 342 | retAuxi2cConfig = mpu.getAuxI2CConfig(); 343 | TEST_ESP_OK( mpu.lastError()); 344 | TEST_ASSERT( auxi2cConfig.clock == retAuxi2cConfig.clock); 345 | TEST_ASSERT( auxi2cConfig.multi_master_en == retAuxi2cConfig.multi_master_en); 346 | TEST_ASSERT( auxi2cConfig.transition == retAuxi2cConfig.transition); 347 | TEST_ASSERT( auxi2cConfig.sample_delay == retAuxi2cConfig.sample_delay); 348 | TEST_ASSERT( auxi2cConfig.shadow_delay_en == retAuxi2cConfig.shadow_delay_en); 349 | TEST_ASSERT( auxi2cConfig.wait_for_es == retAuxi2cConfig.wait_for_es); 350 | // i2c enabling/bypass 351 | TEST_ESP_OK( mpu.setAuxI2CBypass(true)); 352 | TEST_ASSERT_TRUE( mpu.getAuxI2CBypass()); 353 | TEST_ESP_OK( mpu.lastError()); 354 | TEST_ESP_OK( mpu.setAuxI2CEnabled(true)); 355 | TEST_ASSERT_TRUE( mpu.getAuxI2CEnabled()); 356 | TEST_ASSERT_FALSE( mpu.getAuxI2CBypass()); 357 | TEST_ESP_OK( mpu.lastError()); 358 | /* slaves configs */ 359 | mpud::auxi2c_slv_config_t slv0config{}; 360 | slv0config.slave = mpud::AUXI2C_SLAVE_0; 361 | slv0config.addr = 0x1F; 362 | slv0config.rw = mpud::AUXI2C_READ; 363 | slv0config.reg_addr = 0x07; 364 | slv0config.reg_dis = 0; 365 | slv0config.swap_en = 0; 366 | slv0config.rxlength = 14; 367 | slv0config.sample_delay_en = 0; 368 | TEST_ESP_OK( mpu.setAuxI2CSlaveConfig(slv0config)); 369 | mpud::auxi2c_slv_config_t slv1config{}; 370 | slv1config.slave = mpud::AUXI2C_SLAVE_1; 371 | slv1config.addr = 0x19; 372 | slv1config.rw = mpud::AUXI2C_WRITE; 373 | slv1config.reg_addr = 0x50; 374 | slv1config.reg_dis = 1; 375 | slv1config.txdata = 0xFA; 376 | slv1config.sample_delay_en = 1; 377 | TEST_ESP_OK( mpu.setAuxI2CSlaveConfig(slv1config)); 378 | // check configs 379 | mpud::auxi2c_slv_config_t retSlvconfig{}; 380 | retSlvconfig = mpu.getAuxI2CSlaveConfig(slv0config.slave); 381 | TEST_ESP_OK( mpu.lastError()); 382 | TEST_ASSERT( slv0config.slave == retSlvconfig.slave); 383 | TEST_ASSERT( slv0config.addr == retSlvconfig.addr); 384 | TEST_ASSERT( slv0config.rw == retSlvconfig.rw); 385 | TEST_ASSERT( slv0config.reg_addr == retSlvconfig.reg_addr); 386 | TEST_ASSERT( slv0config.reg_dis == retSlvconfig.reg_dis); 387 | TEST_ASSERT( slv0config.swap_en == retSlvconfig.swap_en); 388 | TEST_ASSERT( slv0config.end_of_word == retSlvconfig.end_of_word); 389 | TEST_ASSERT( slv0config.rxlength == retSlvconfig.rxlength); 390 | TEST_ASSERT( slv0config.sample_delay_en == retSlvconfig.sample_delay_en); 391 | retSlvconfig = mpu.getAuxI2CSlaveConfig(slv1config.slave); 392 | TEST_ESP_OK( mpu.lastError()); 393 | TEST_ASSERT( slv1config.slave == retSlvconfig.slave); 394 | TEST_ASSERT( slv1config.addr == retSlvconfig.addr); 395 | TEST_ASSERT( slv1config.rw == retSlvconfig.rw); 396 | TEST_ASSERT( slv1config.reg_addr == retSlvconfig.reg_addr); 397 | TEST_ASSERT( slv1config.reg_dis == retSlvconfig.reg_dis); 398 | TEST_ASSERT( slv1config.txdata == retSlvconfig.txdata); 399 | TEST_ASSERT( slv1config.sample_delay_en == retSlvconfig.sample_delay_en); 400 | // enable/disable slaves 401 | TEST_ESP_OK( mpu.setAuxI2CSlaveEnabled(slv0config.slave, true)); 402 | TEST_ASSERT_TRUE(mpu.getAuxI2CSlaveEnabled(slv0config.slave)); 403 | TEST_ESP_OK( mpu.lastError()); 404 | TEST_ESP_OK( mpu.setAuxI2CSlaveEnabled(slv1config.slave, true)); 405 | TEST_ASSERT_TRUE(mpu.getAuxI2CSlaveEnabled(slv1config.slave)); 406 | TEST_ESP_OK( mpu.lastError()); 407 | TEST_ESP_OK( mpu.setAuxI2CSlaveEnabled(slv0config.slave, false)); 408 | TEST_ASSERT_FALSE(mpu.getAuxI2CSlaveEnabled(slv0config.slave)); 409 | TEST_ESP_OK( mpu.lastError()); 410 | TEST_ESP_OK( mpu.setAuxI2CSlaveEnabled(slv1config.slave, false)); 411 | TEST_ASSERT_FALSE(mpu.getAuxI2CSlaveEnabled(slv1config.slave)); 412 | TEST_ESP_OK( mpu.lastError()); 413 | } 414 | 415 | 416 | 417 | TEST_CASE("MPU slave 4 tranfers", "[MPU]") 418 | { 419 | test::MPU_t mpu; 420 | TEST_ESP_OK( mpu.testConnection()); 421 | TEST_ESP_OK( mpu.initialize()); 422 | // config master first 423 | mpud::auxi2c_config_t auxi2cConfig{}; 424 | auxi2cConfig.clock = mpud::AUXI2C_CLOCK_400KHZ; 425 | auxi2cConfig.multi_master_en = true; 426 | auxi2cConfig.transition = mpud::AUXI2C_TRANS_RESTART; 427 | auxi2cConfig.sample_delay = 0; 428 | auxi2cConfig.shadow_delay_en = false; 429 | auxi2cConfig.wait_for_es = false; 430 | TEST_ESP_OK( mpu.setAuxI2CConfig(auxi2cConfig)); 431 | TEST_ESP_OK( mpu.setAuxI2CEnabled(true)); 432 | // check for error codes 433 | uint8_t slaveAddr = 0x40; 434 | uint8_t slaveReg = 0x00; 435 | uint8_t slaveOutput = 0x16; 436 | uint8_t slaveInput = 0x00; 437 | TEST_ESP_ERR( ESP_ERR_NOT_FOUND, mpu.auxI2CReadByte(slaveAddr, slaveReg, &slaveInput)); 438 | TEST_ESP_ERR( ESP_ERR_NOT_FOUND, mpu.auxI2CWriteByte(slaveAddr, slaveReg, slaveOutput)); 439 | // try transfers with compass if there is 440 | #if defined CONFIG_MPU9250 || defined CONFIG_MPU9150 441 | constexpr uint8_t compassWIA = 0x48; 442 | slaveAddr = 0xC; 443 | slaveReg = 0x0; 444 | TEST_ESP_OK( mpu.auxI2CReadByte(slaveAddr, slaveReg, &slaveInput)); 445 | TEST_ASSERT_EQUAL_UINT8( compassWIA, slaveInput); 446 | #endif 447 | } 448 | 449 | 450 | 451 | TEST_CASE("MPU external frame synchronization (FSYNC pin)", "[MPU]") 452 | { 453 | test::MPU_t mpu; 454 | TEST_ESP_OK( mpu.testConnection()); 455 | TEST_ESP_OK( mpu.initialize()); 456 | // config esp32 gpio for FSYNC signal simulation 457 | gpio_config_t fsyncIOconfig{}; 458 | fsyncIOconfig.pin_bit_mask = ((uint64_t) 1 << CONFIG_MPU_TEST_FSYNC_PIN); 459 | fsyncIOconfig.mode = GPIO_MODE_OUTPUT; 460 | fsyncIOconfig.pull_up_en = GPIO_PULLUP_DISABLE; 461 | fsyncIOconfig.pull_down_en = GPIO_PULLDOWN_ENABLE; 462 | fsyncIOconfig.intr_type = GPIO_INTR_DISABLE; 463 | TEST_ESP_OK( gpio_config(&fsyncIOconfig)); 464 | // check fsync configs 465 | mpud::int_lvl_t fsyncLevel = mpud::INT_LVL_ACTIVE_LOW; 466 | TEST_ESP_OK( mpu.setFsyncConfig(fsyncLevel)); 467 | TEST_ASSERT_EQUAL_INT( fsyncLevel, mpu.getFsyncConfig()); 468 | TEST_ESP_OK( mpu.lastError()); 469 | fsyncLevel = mpud::INT_LVL_ACTIVE_HIGH; 470 | TEST_ESP_OK( mpu.setFsyncConfig(fsyncLevel)); 471 | TEST_ASSERT_EQUAL_INT( fsyncLevel, mpu.getFsyncConfig()); 472 | TEST_ESP_OK( mpu.lastError()); 473 | // enable fsync to cause an interrupt on register I2C_MST_STATUS 474 | TEST_ESP_OK( mpu.setFsyncEnabled(true)); 475 | TEST_ASSERT_TRUE( mpu.getFsyncEnabled()); 476 | TEST_ESP_OK( mpu.lastError()); 477 | // enable fsync to propagate to INT pin and register INT_STATUS 478 | mpud::int_en_t intmask = mpud::INT_EN_I2C_MST_FSYNC; 479 | TEST_ESP_OK( mpu.setInterruptEnabled(intmask)); 480 | TEST_ASSERT_EQUAL_UINT8( intmask, mpu.getInterruptEnabled()); 481 | TEST_ESP_OK( mpu.lastError()); 482 | 483 | // Output a FSYNC signal, then 484 | // check for the interrupt in INT_STATUS and I2C_MST_STATUS 485 | mpud::auxi2c_stat_t auxI2Cstatus = 0; 486 | mpud::int_stat_t intStatus = 0; 487 | for (size_t i = 0; i < 10; i++) { 488 | gpio_set_level((gpio_num_t)CONFIG_MPU_TEST_FSYNC_PIN, 1); 489 | auxI2Cstatus = mpu.getAuxI2CStatus(); 490 | TEST_ESP_OK( mpu.lastError()); 491 | intStatus = mpu.getInterruptStatus(); 492 | TEST_ESP_OK( mpu.lastError()); 493 | TEST_ASSERT(auxI2Cstatus & mpud::AUXI2C_STAT_FSYNC); 494 | TEST_ASSERT(intStatus & intmask); 495 | gpio_set_level((gpio_num_t)CONFIG_MPU_TEST_FSYNC_PIN, 0); 496 | auxI2Cstatus = mpu.getAuxI2CStatus(); 497 | TEST_ESP_OK( mpu.lastError()); 498 | intStatus = mpu.getInterruptStatus(); 499 | TEST_ESP_OK( mpu.lastError()); 500 | TEST_ASSERT(! (auxI2Cstatus & mpud::AUXI2C_STAT_FSYNC)); 501 | TEST_ASSERT(! (intStatus & intmask)); 502 | vTaskDelay(50 / portTICK_PERIOD_MS); 503 | } 504 | } 505 | 506 | 507 | 508 | TEST_CASE("MPU sensor data test", "[MPU]") 509 | { 510 | test::MPU_t mpu; 511 | TEST_ESP_OK( mpu.testConnection()); 512 | TEST_ESP_OK( mpu.initialize()); 513 | // TODO.. 514 | } 515 | 516 | 517 | 518 | TEST_CASE("MPU standby mode", "[MPU]") 519 | { 520 | test::MPU_t mpu; 521 | TEST_ESP_OK( mpu.testConnection()); 522 | TEST_ESP_OK( mpu.initialize()); 523 | // check setups 524 | mpud::stby_en_t stbySensors[] = { 525 | mpud::STBY_EN_ACCEL_X | mpud::STBY_EN_GYRO_Y | mpud::STBY_EN_TEMP | mpud::STBY_EN_LOWPWR_GYRO_PLL_ON, 526 | mpud::STBY_EN_ACCEL_Y | mpud::STBY_EN_ACCEL_Z | mpud::STBY_EN_TEMP, 527 | mpud::STBY_EN_ACCEL_Z | mpud::STBY_EN_GYRO_X | mpud::STBY_EN_GYRO_Y, 528 | mpud::STBY_EN_TEMP | mpud::STBY_EN_LOWPWR_GYRO_PLL_ON, 529 | mpud::STBY_EN_ACCEL | mpud::STBY_EN_GYRO | mpud::STBY_EN_TEMP | mpud::STBY_EN_LOWPWR_GYRO_PLL_ON 530 | }; 531 | for (auto stby : stbySensors) { 532 | TEST_ESP_OK( mpu.setStandbyMode(stby)); 533 | mpud::stby_en_t retStbySensors = mpu.getStandbyMode(); 534 | printf("stby: %#X, retStbySensors: %#X", stby, retStbySensors); 535 | TEST_ASSERT( stby == retStbySensors); 536 | uint8_t data[2]; 537 | TEST_ESP_OK( mpu.readByte(mpud::regs::PWR_MGMT1, data)); 538 | TEST_ESP_OK( mpu.readByte(mpud::regs::PWR_MGMT2, data+1)); 539 | printf(" -> PWR_MGMT1: %#X, PWR_MGMT2: %#X\n", data[0], data[1]); 540 | TEST_ASSERT( ((stby & mpud::STBY_EN_TEMP) >> 3) == (data[0] & (1 << mpud::regs::PWR1_TEMP_DIS_BIT))); 541 | TEST_ASSERT( ((stby & mpud::STBY_EN_LOWPWR_GYRO_PLL_ON) >> 3) == (data[0] & (1 << mpud::regs::PWR1_GYRO_STANDBY_BIT))); 542 | TEST_ASSERT( (stby & mpud::STBY_EN_ACCEL) == (data[1] & mpud::regs::PWR2_STBY_XYZA_BITS)); 543 | TEST_ASSERT( (stby & mpud::STBY_EN_GYRO) == (data[1] & mpud::regs::PWR2_STBY_XYZG_BITS)); 544 | } 545 | } 546 | 547 | 548 | 549 | TEST_CASE("MPU FIFO buffer", "[MPU]") 550 | { 551 | test::MPU_t mpu; 552 | TEST_ESP_OK( mpu.testConnection()); 553 | TEST_ESP_OK( mpu.initialize()); 554 | TEST_ESP_OK( mpu.setSampleRate(4)); 555 | // config mode 556 | mpud::fifo_mode_t fifoMode = mpud::FIFO_MODE_STOP_FULL; 557 | TEST_ESP_OK( mpu.setFIFOMode(fifoMode)); 558 | TEST_ASSERT_EQUAL_INT( fifoMode, mpu.getFIFOMode()); 559 | TEST_ESP_OK( mpu.lastError()); 560 | // enable fifo 561 | TEST_ESP_OK( mpu.setFIFOEnabled(true)); 562 | TEST_ASSERT_TRUE( mpu.getFIFOEnabled()); 563 | TEST_ESP_OK( mpu.lastError()); 564 | /* prepare for test */ 565 | #ifdef CONFIG_MPU_AK89xx 566 | // free slaves 0 and 1 in case of MPU9150 or MPU9250 567 | TEST_ESP_OK( mpu.compassSetMode(mpud::MAG_MODE_POWER_DOWN)); 568 | #endif 569 | mpud::auxi2c_slv_config_t slvconfig{}; 570 | slvconfig.slave = mpud::AUXI2C_SLAVE_0; 571 | slvconfig.rw = mpud::AUXI2C_READ; 572 | slvconfig.rxlength = 2; 573 | TEST_ESP_OK( mpu.setAuxI2CSlaveConfig(slvconfig)); 574 | TEST_ESP_OK( mpu.setAuxI2CSlaveEnabled(slvconfig.slave, true)); 575 | TEST_ESP_OK( mpu.setAuxI2CEnabled(true)); 576 | TEST_ESP_OK( mpu.setInterruptEnabled(mpud::INT_EN_RAWDATA_READY)); 577 | // sets of configs 578 | mpud::fifo_config_t fifoConfigs[] = { 579 | mpud::FIFO_CFG_ACCEL | mpud::FIFO_CFG_GYRO | mpud::FIFO_CFG_TEMPERATURE, 580 | mpud::FIFO_CFG_ACCEL | mpud::FIFO_CFG_TEMPERATURE, 581 | mpud::FIFO_CFG_GYRO, 582 | mpud::FIFO_CFG_SLAVE0 | mpud::FIFO_CFG_SLAVE1| mpud::FIFO_CFG_SLAVE2 | mpud::FIFO_CFG_SLAVE3 583 | }; 584 | uint16_t countArray[] = { 14, 8, 6, 2}; 585 | /* test configs */ 586 | for (int i = 0; i < sizeof(fifoConfigs) / sizeof(fifoConfigs[0]); i++) { 587 | // set and check config 588 | TEST_ESP_OK( mpu.setFIFOConfig(fifoConfigs[i])); 589 | mpud::fifo_config_t retConfig = mpu.getFIFOConfig(); 590 | TEST_ASSERT( fifoConfigs[i] == retConfig); 591 | TEST_ESP_OK( mpu.lastError()); 592 | // check count 593 | TEST_ESP_OK( mpu.resetFIFO()); // zero count first 594 | mpu.getInterruptStatus(); // clear status first 595 | while(!(mpu.getInterruptStatus() & mpud::INT_STAT_RAWDATA_READY) && !mpu.lastError()) {} 596 | uint16_t count = mpu.getFIFOCount(); 597 | printf("FIFO config: 0x%X, real packet count: %d\n", fifoConfigs[i], count); 598 | TEST_ASSERT( countArray[i] == count); 599 | } 600 | } 601 | 602 | 603 | 604 | TEST_CASE("MPU offset test", "[MPU]") 605 | { 606 | test::MPU_t mpu; 607 | TEST_ESP_OK( mpu.testConnection()); 608 | TEST_ESP_OK( mpu.initialize()); 609 | TEST_ESP_OK( mpu.setAccelFullScale(mpud::ACCEL_FS_16G)); 610 | TEST_ESP_OK( mpu.setGyroFullScale(mpud::GYRO_FS_1000DPS)); 611 | // test 612 | printf("This test computes the offsets for a MPU device\n" 613 | "For the test to succed, the chip has to remain as horizontal as possible.\n" 614 | "Note: All output results are LSB in +-16G and +-1000DPS format\n"); 615 | for (int i = 1; i < 10; i++) { 616 | printf("%d.. ", i); 617 | fflush(stdout); 618 | vTaskDelay(1000 / portTICK_PERIOD_MS); 619 | } 620 | printf("10\n"); 621 | fflush(stdout); 622 | vTaskDelay(1000 / portTICK_PERIOD_MS); 623 | // without offsets 624 | mpud::raw_axes_t accelRaw, gyroRaw; 625 | printf(">> Sensor data without offsets cancellation:\n"); 626 | for (int i = 0; i < 6; i++) { 627 | TEST_ESP_OK( mpu.motion(&accelRaw, &gyroRaw)); 628 | printf("accel: [ %+d %+d %+d ] \t gyro: [ %+d %+d %+d ]\n", 629 | accelRaw.x, accelRaw.y, accelRaw.z, gyroRaw.x, gyroRaw.y, gyroRaw.z); 630 | vTaskDelay(50 / portTICK_PERIOD_MS); 631 | } 632 | // factory offsets 633 | mpud::raw_axes_t accelFactoryOffset, gyroFactoryOffset; 634 | accelFactoryOffset = mpu.getAccelOffset(); 635 | TEST_ESP_OK( mpu.lastError()); 636 | gyroFactoryOffset = mpu.getGyroOffset(); 637 | TEST_ESP_OK( mpu.lastError()); 638 | printf(">> Factory offsets:\n"); 639 | printf("accel: [ %+d %+d %+d ] \t gyro: [ %+d %+d %+d ]\n", 640 | accelFactoryOffset.x, accelFactoryOffset.y, accelFactoryOffset.z, 641 | gyroFactoryOffset.x, gyroFactoryOffset.y, gyroFactoryOffset.z); 642 | // calculate offsets 643 | mpud::raw_axes_t accelOffset, gyroOffset; 644 | TEST_ESP_OK( mpu.computeOffsets(&accelOffset, &gyroOffset)); 645 | printf(">> Computed offsets:\n"); 646 | printf("accel: [ %+d %+d %+d ] \t gyro: [ %+d %+d %+d ]\n", 647 | accelOffset.x, accelOffset.y, accelOffset.z, gyroOffset.x, gyroOffset.y, gyroOffset.z); 648 | // set offsets 649 | TEST_ESP_OK( mpu.setAccelOffset(accelOffset)); 650 | TEST_ESP_OK( mpu.setGyroOffset(gyroOffset)); 651 | mpud::raw_axes_t retAccelOffset, retGyroOffset; 652 | retAccelOffset = mpu.getAccelOffset(); 653 | TEST_ESP_OK( mpu.lastError()); 654 | retGyroOffset = mpu.getGyroOffset(); 655 | TEST_ESP_OK( mpu.lastError()); 656 | printf(">> Offsets returned:\n"); 657 | printf("accel: [ %+d %+d %+d ] \t gyro: [ %+d %+d %+d ]\n", 658 | retAccelOffset.x, retAccelOffset.y, retAccelOffset.z, 659 | retGyroOffset.x, retGyroOffset.y, retGyroOffset.z); 660 | // assert offsets 661 | TEST_ASSERT( (accelFactoryOffset.x + (accelOffset.x & ~1)) == retAccelOffset.x); 662 | TEST_ASSERT( (accelFactoryOffset.y + (accelOffset.y & ~1)) == retAccelOffset.y); 663 | TEST_ASSERT( (accelFactoryOffset.z + (accelOffset.z & ~1)) == retAccelOffset.z); 664 | TEST_ASSERT( gyroOffset.x == retGyroOffset.x); 665 | TEST_ASSERT( gyroOffset.y == retGyroOffset.y); 666 | TEST_ASSERT( gyroOffset.z == retGyroOffset.z); 667 | // show results 668 | vTaskDelay(200 / portTICK_PERIOD_MS); // let sensors stabilize again 669 | printf(">> Resulted sensor data with computed offsets cancellation:\n"); 670 | for (int i = 0; i < 6; i++) { 671 | TEST_ESP_OK( mpu.motion(&accelRaw, &gyroRaw)); 672 | printf("accel: [ %+d %+d %+d ] \t gyro: [ %+d %+d %+d ]\n", 673 | accelRaw.x, accelRaw.y, accelRaw.z, gyroRaw.x, gyroRaw.y, gyroRaw.z); 674 | vTaskDelay(50 / portTICK_PERIOD_MS); 675 | } 676 | } 677 | 678 | 679 | 680 | TEST_CASE("MPU self-test check", "[MPU]") 681 | { 682 | test::MPU_t mpu; 683 | TEST_ESP_OK( mpu.testConnection()); 684 | TEST_ESP_OK( mpu.initialize()); 685 | /* test */ 686 | mpud::selftest_t selfTestResult; 687 | TEST_ESP_OK( mpu.selfTest(&selfTestResult)); 688 | printf("[%s] SELF-TEST result: 0x%X\n", 689 | (selfTestResult == mpud::SELF_TEST_PASS) ? (LOG_COLOR_I " OK " LOG_RESET_COLOR) : (LOG_COLOR_E "FAIL" LOG_RESET_COLOR), 690 | selfTestResult); 691 | } 692 | 693 | 694 | 695 | TEST_CASE("MPU motion detection and wake-on-motion mode", "[MPU]") 696 | { 697 | test::MPU_t mpu; 698 | TEST_ESP_OK( mpu.testConnection()); 699 | TEST_ESP_OK( mpu.initialize()); 700 | /* assert possible configuration */ 701 | mpud::mot_config_t motConfig{}; 702 | #if defined CONFIG_MPU6050 703 | motConfig.threshold = 20; 704 | motConfig.time = 2; 705 | motConfig.accel_on_delay = 1; 706 | motConfig.counter = mpud::MOT_COUNTER_DEC_1; 707 | #elif defined CONFIG_MPU6500 708 | motConfig.threshold = 150; 709 | #endif 710 | TEST_ESP_OK( mpu.setMotionDetectConfig(motConfig)); 711 | TEST_ESP_OK( mpu.setMotionFeatureEnabled(true)); 712 | TEST_ASSERT_TRUE( mpu.getMotionFeatureEnabled()); 713 | TEST_ESP_OK( mpu.lastError()); 714 | mpud::mot_config_t retmotConfig{}; 715 | retmotConfig = mpu.getMotionDetectConfig(); 716 | TEST_ASSERT( motConfig.threshold == retmotConfig.threshold); 717 | #if defined CONFIG_MPU6050 718 | TEST_ASSERT( motConfig.time == retmotConfig.time); 719 | TEST_ASSERT( motConfig.accel_on_delay == retmotConfig.accel_on_delay); 720 | TEST_ASSERT( motConfig.counter == motConfig.counter); 721 | #endif 722 | /* enter low power mode */ 723 | TEST_ESP_OK( mpu.setLowPowerAccelMode(true)); 724 | #if defined CONFIG_MPU6050 725 | TEST_ESP_OK( mpu.setLowPowerAccelRate(mpud::LP_ACCEL_RATE_20HZ)); 726 | #elif defined CONFIG_MPU6500 727 | TEST_ESP_OK( mpu.setLowPowerAccelRate(mpud::LP_ACCEL_RATE_250HZ)); 728 | #endif 729 | /* test motion interrupt */ 730 | #if defined CONFIG_MPU6050 731 | uint16_t thresholdMg = motConfig.threshold * 32; 732 | uint16_t rate = 20; 733 | printf(">> Motion-Detect Config:: threshold: %d mg, rate: %d Hz, time: %d ms\n", thresholdMg, rate, motConfig.time); 734 | #elif defined CONFIG_MPU6500 735 | uint16_t thresholdMg = motConfig.threshold * 4; 736 | uint16_t rate = 250; 737 | printf(">> Motion-Detect Config:: threshold: %d mg, rate: %d Hz\n", thresholdMg, rate); 738 | #endif 739 | // configure interrupt 740 | TEST_ESP_OK( mpu.setInterruptEnabled(mpud::INT_EN_MOTION_DETECT)); 741 | TEST_ESP_OK( mpuConfigInterrupt(mpuTaskNotifier, xTaskGetCurrentTaskHandle())); 742 | // check interrupt for a period 743 | TickType_t startTick = xTaskGetTickCount(); 744 | TickType_t endTick = startTick + (pdMS_TO_TICKS(10000)); // 10 seconds of test 745 | printf(">> Waiting for Motion interrupt. Shake it to generate. (10 secs..)!\n"); 746 | while (xTaskGetTickCount() < endTick) { 747 | uint32_t cnt = ulTaskNotifyTake(pdTRUE, endTick - xTaskGetTickCount()); 748 | if (cnt) { 749 | printf(">>> WOM interrupt detected!"); 750 | #if defined CONFIG_MPU6050 751 | uint8_t status = mpu.getMotionDetectStatus(); 752 | TEST_ESP_OK( mpu.lastError()); 753 | printf(" Status reg: 0x%X", status); 754 | #endif 755 | printf("\n"); 756 | } 757 | } 758 | TEST_ESP_OK( mpu.setLowPowerAccelMode(false)); 759 | TEST_ESP_OK( mpu.setMotionFeatureEnabled(false)); 760 | TEST_ASSERT_FALSE( mpu.getMotionFeatureEnabled()); 761 | TEST_ESP_OK( mpu.lastError()); 762 | // free interrupt 763 | TEST_ESP_OK( mpuRemoveInterrupt()); 764 | } 765 | 766 | 767 | 768 | #if defined CONFIG_MPU6050 769 | TEST_CASE("MPU free-fall detection", "[MPU]") 770 | { 771 | test::MPU_t mpu; 772 | TEST_ESP_OK( mpu.testConnection()); 773 | TEST_ESP_OK( mpu.initialize()); 774 | /* assert possible configs */ 775 | mpud::ff_config_t FFConfig{}; 776 | FFConfig.threshold = 160; 777 | FFConfig.time = 100; 778 | FFConfig.accel_on_delay = 0; 779 | FFConfig.counter = mpud::MOT_COUNTER_DEC_2; 780 | TEST_ESP_OK( mpu.setFreeFallConfig(FFConfig)); 781 | TEST_ESP_OK( mpu.setMotionFeatureEnabled(true)); 782 | TEST_ASSERT_TRUE( mpu.getMotionFeatureEnabled()); 783 | TEST_ESP_OK( mpu.lastError()); 784 | mpud::ff_config_t retFFConfig{}; 785 | retFFConfig = mpu.getFreeFallConfig(); 786 | TEST_ASSERT( FFConfig.threshold == retFFConfig.threshold); 787 | TEST_ASSERT( FFConfig.time == retFFConfig.time); 788 | TEST_ASSERT( FFConfig.accel_on_delay == retFFConfig.accel_on_delay); 789 | TEST_ASSERT( FFConfig.counter == FFConfig.counter); 790 | /* test motion interrupt */ 791 | uint16_t thresholdMg = FFConfig.threshold * 4; 792 | printf(">> Free-Fall Config:: threshold: %d mg, time: %d ms\n", thresholdMg, FFConfig.time); 793 | // configure interrupt 794 | TEST_ESP_OK( mpu.setInterruptEnabled(mpud::INT_EN_FREE_FALL)); 795 | TEST_ESP_OK( mpuConfigInterrupt(mpuTaskNotifier, xTaskGetCurrentTaskHandle())); 796 | // check interrupt for a period 797 | TickType_t startTick = xTaskGetTickCount(); 798 | TickType_t endTick = startTick + (pdMS_TO_TICKS(10000)); // 10 seconds of test 799 | printf(">> Waiting for Free-Fall interrupt. Drop it to generate. (10 secs..)!\n"); 800 | while (xTaskGetTickCount() < endTick) { 801 | uint32_t cnt = ulTaskNotifyTake(pdTRUE, endTick - xTaskGetTickCount()); 802 | if (cnt) { 803 | printf(">>> FF interrupt detected!\n"); 804 | } 805 | } 806 | TEST_ESP_OK( mpu.setMotionFeatureEnabled(false)); 807 | TEST_ASSERT_FALSE( mpu.getMotionFeatureEnabled()); 808 | TEST_ESP_OK( mpu.lastError()); 809 | // free interrupt 810 | TEST_ESP_OK( mpuRemoveInterrupt()); 811 | } 812 | #endif 813 | 814 | 815 | 816 | #if defined CONFIG_MPU6050 817 | TEST_CASE("MPU zero-motion detection", "[MPU]") 818 | { 819 | test::MPU_t mpu; 820 | TEST_ESP_OK( mpu.testConnection()); 821 | TEST_ESP_OK( mpu.initialize()); 822 | /* assert possible configs */ 823 | mpud::zrmot_config_t ZRMotConfig{}; 824 | ZRMotConfig.threshold = 200; 825 | ZRMotConfig.time = 20; 826 | TEST_ESP_OK( mpu.setZeroMotionConfig(ZRMotConfig)); 827 | TEST_ESP_OK( mpu.setMotionFeatureEnabled(true)); 828 | TEST_ASSERT_TRUE( mpu.getMotionFeatureEnabled()); 829 | TEST_ESP_OK( mpu.lastError()); 830 | mpud::zrmot_config_t retZRMotConfig{}; 831 | retZRMotConfig = mpu.getZeroMotionConfig(); 832 | TEST_ASSERT( ZRMotConfig.threshold == retZRMotConfig.threshold); 833 | TEST_ASSERT( ZRMotConfig.time == retZRMotConfig.time); 834 | /* test motion interrupt */ 835 | uint16_t thresholdMg = ZRMotConfig.threshold * 4; 836 | printf(">> Zero-Motion Config:: threshold: %d mg, time: %d ms\n", thresholdMg, ZRMotConfig.time); 837 | // configure interrupt 838 | TEST_ESP_OK( mpu.setInterruptEnabled(mpud::INT_EN_ZERO_MOTION)); 839 | TEST_ESP_OK( mpuConfigInterrupt(mpuTaskNotifier, xTaskGetCurrentTaskHandle())); 840 | // check interrupt for a period 841 | TickType_t startTick = xTaskGetTickCount(); 842 | TickType_t endTick = startTick + (pdMS_TO_TICKS(10000)); // 10 seconds of test 843 | printf(">> Waiting for Zero-Motion interrupt. Generate it. (10 secs..)!\n"); 844 | while (xTaskGetTickCount() < endTick) { 845 | uint32_t cnt = ulTaskNotifyTake(pdTRUE, endTick - xTaskGetTickCount()); 846 | if (cnt) { 847 | printf(">>> ZRMOT interrupt detected!\n"); 848 | } 849 | } 850 | TEST_ESP_OK( mpu.setMotionFeatureEnabled(false)); 851 | TEST_ASSERT_FALSE( mpu.getMotionFeatureEnabled()); 852 | TEST_ESP_OK( mpu.lastError()); 853 | // free interrupt 854 | TEST_ESP_OK( mpuRemoveInterrupt()); 855 | } 856 | #endif 857 | 858 | 859 | 860 | #if defined CONFIG_MPU_AK89xx 861 | TEST_CASE("MPU compass configuration", "[MPU]") 862 | { 863 | test::MPU_t mpu; 864 | TEST_ESP_OK( mpu.testConnection()); 865 | TEST_ESP_OK( mpu.initialize()); 866 | // test 867 | TEST_ESP_OK( mpu.compassTestConnection()); 868 | TEST_ASSERT( mpu.compassGetMode() == mpud::MAG_MODE_SINGLE_MEASURE); 869 | // check sensitivity 870 | #ifdef CONFIG_MPU_AK8963 871 | mpud::mag_sensy_t magSensy = mpud::MAG_SENSITIVITY_0_6_uT; 872 | TEST_ESP_OK( mpu.compassSetSensitivity(magSensy)); 873 | TEST_ASSERT( magSensy == mpu.compassGetSensitivity()); 874 | TEST_ESP_OK( mpu.lastError()); 875 | magSensy = mpud::MAG_SENSITIVITY_0_15_uT; 876 | TEST_ESP_OK( mpu.compassSetSensitivity(magSensy)); 877 | TEST_ASSERT( magSensy == mpu.compassGetSensitivity()); 878 | TEST_ESP_OK( mpu.lastError()); 879 | #endif 880 | // self-test 881 | mpud::raw_axes_t magSelfTest; 882 | bool selftest = mpu.compassSelfTest(&magSelfTest); 883 | printf("[%s] self-test: %+d %+d %+d\n", 884 | selftest ? (LOG_COLOR_I " OK " LOG_RESET_COLOR) : (LOG_COLOR_E "FAIL" LOG_RESET_COLOR), 885 | magSelfTest.x, magSelfTest.y, magSelfTest.z); 886 | // sensitivity adjustment 887 | uint8_t magAdj[3]; 888 | TEST_ESP_OK( mpu.compassGetAdjustment(magAdj, magAdj+1, magAdj+2)); 889 | // heading 890 | mpud::raw_axes_t mag; 891 | for (int i = 0; i < 5; i++) { 892 | TEST_ESP_OK( mpu.heading(&mag)); 893 | mag.x = mpud::math::magAdjust(mag.x, magAdj[0]); 894 | mag.y = mpud::math::magAdjust(mag.y, magAdj[1]); 895 | mag.z = mpud::math::magAdjust(mag.z, magAdj[2]); 896 | printf("heading: %+d %+d %+d\n", mag.x, mag.y, mag.z); 897 | vTaskDelay(100 / portTICK_PERIOD_MS); 898 | } 899 | } 900 | #endif 901 | -------------------------------------------------------------------------------- /test/unit-test-app/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Project Makefile. 3 | # 4 | 5 | PROJECT_NAME := unit-test-app 6 | 7 | # ########### NOTE ############### 8 | # Must add here the I2Cbus / SPIbus library path for the TESTS TO WORK 9 | EXTRA_COMPONENT_DIRS += ${HOME}/esp/libraries/I2Cbus 10 | EXTRA_COMPONENT_DIRS += ${HOME}/esp/libraries/SPIbus 11 | 12 | 13 | MPU_COMPONENT_PATH := $(abspath ../..) 14 | # Add MPU library path 15 | EXTRA_COMPONENT_DIRS += $(MPU_COMPONENT_PATH) 16 | # Add mpu-tests directory as component 17 | EXTRA_COMPONENT_DIRS += $(MPU_COMPONENT_PATH)/test/mpu-tests 18 | # Add ESP-IDF unity-test-app path 19 | EXTRA_COMPONENT_DIRS += $(IDF_PATH)/tools/unit-test-app/components 20 | 21 | # Enable MPU testing 22 | # Note: should be mpu-tests directory 23 | override TEST_COMPONENTS += $(notdir $(MPU_COMPONENT_PATH)/test/mpu-tests) 24 | 25 | # Follow the same sdkconfig.defaults from esp-idf unit-test-app 26 | SDKCONFIG_DEFAULTS := $(IDF_PATH)/tools/unit-test-app/sdkconfig.defaults 27 | 28 | # ESP-IDF project makefile 29 | include $(IDF_PATH)/make/project.mk 30 | 31 | # Target to copy the partition table file from esp-idf/tools/unit-test-app 32 | # Note: must be defined after the "include $(IDF_PATH)/make/project.mk", so it's not the first target 33 | $(PROJECT_PATH)/partition_table_unit_test_app.csv: $(IDF_PATH)/tools/unit-test-app/partition_table_unit_test_app.csv 34 | @cp -u $(IDF_PATH)/tools/unit-test-app/partition_table_unit_test_app.csv ./ 35 | -------------------------------------------------------------------------------- /test/unit-test-app/main/app_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "freertos/FreeRTOS.h" 3 | #include "freertos/task.h" 4 | #include "unity.h" 5 | #include "unity_config.h" 6 | #include "sdkconfig.h" 7 | 8 | #ifdef CONFIG_LOG_COLORS 9 | #define ESC(x) "\033" x 10 | #else 11 | #define ESC(x) "" 12 | #endif 13 | 14 | 15 | void unityTask(void *pvParameters) 16 | { 17 | vTaskDelay(1000 / portTICK_PERIOD_MS); 18 | unity_run_menu(); 19 | while(1); 20 | } 21 | 22 | 23 | extern "C" void app_main() 24 | { 25 | printf("\n" 26 | ESC("[37;40m") " MPU " 27 | ESC("[0m") " " 28 | ESC("[32;40m") " Unit " 29 | ESC("[42;30m") " test " 30 | ESC("[0m") "\n" ); 31 | fflush(stdout); 32 | 33 | /* Uncomment to auto-start all tests */ 34 | // UNITY_BEGIN(); 35 | // unity_run_tests_with_filter("[MPU]"); 36 | // UNITY_END(); 37 | 38 | // Note: if unpinning this task, change the way run times are calculated in unity_platform 39 | xTaskCreatePinnedToCore(unityTask, "unityTask", 8192, NULL, 40 | UNITY_FREERTOS_PRIORITY, NULL, UNITY_FREERTOS_CPU); 41 | } 42 | -------------------------------------------------------------------------------- /test/unit-test-app/main/component.mk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natanaeljr/esp32-MPU-driver/c82b00502eb4c101a3f6b8134cd9b4a13f88e016/test/unit-test-app/main/component.mk -------------------------------------------------------------------------------- /tools/ci/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script for Building Unit-Tests and Examples 3 | 4 | set -e # exit with nonzero exit code if anything fails 5 | 6 | # Variables 7 | directory= 8 | chips= 9 | protocols= 10 | valid_chips="MPU6000 MPU6050 MPU6500 MPU6555 MPU9150 MPU9250 MPU9255" 11 | valid_protocols="I2C SPI" 12 | 13 | # Functions 14 | 15 | abort() { 16 | [ "$1" ] && echo -e "Error: $1" >&2 17 | exit 1 18 | } 19 | 20 | print_help() { 21 | echo "Script for Building Unit-Tests and Examples." 22 | echo "Usage: sh build_unit-test.sh [--chips=a,b..] [--protocols=a,b]" 23 | echo "- Communication protocols = ${valid_protocols// /,}" 24 | echo "- Chip models = ${valid_chips// /,}" 25 | } 26 | 27 | check_flag_value() { 28 | if [ "$2" == "" ] || [ "${2:0:1}" == "-" ]; then 29 | abort "Missing input in flag '$1'." 30 | fi 31 | } 32 | 33 | parse_directory() { 34 | if [ "$directory" ]; then abort "Multiple directories input."; fi 35 | if ! [ -d "$1" ]; then 36 | abort "Directory '$1' does not exist." 37 | else 38 | directory="$1" 39 | fi 40 | } 41 | 42 | parse_chips() { 43 | if [ "$1" == "all" ]; then 44 | chips="$valid_chips" 45 | return 46 | fi 47 | temp_chips="${1//,/ }" 48 | for chip in $temp_chips; do 49 | check=0 50 | for vc in $valid_chips; do 51 | if [ "$chip" == "$vc" ]; then 52 | check=1 53 | break 54 | fi 55 | done 56 | if [ $check == 0 ]; then abort "Invalid chip model '$chip'"; fi 57 | for this in $chips; do 58 | if [ "$this" == "$chip" ]; then abort "Chip '$chip' is duplicate"; fi 59 | done 60 | chips="$chips$chip " 61 | done 62 | } 63 | 64 | parse_protocols() { 65 | if [ "$1" == "all" ]; then 66 | protocols="$valid_protocols" 67 | return 68 | fi 69 | temp_protocols="${1//,/ }" 70 | for pro in $temp_protocols; do 71 | if [ "$pro" != "I2C" ] && [ "$pro" != "SPI" ]; then abort "Invalid protocol '$pro'"; fi 72 | for this in $protocols; do 73 | if [ "$this" == "$pro" ]; then abort "Protocol '$pro' is duplicate"; fi 74 | done 75 | protocols="$protocols$pro " 76 | done 77 | } 78 | 79 | parse_arguments() { 80 | while [ "$1" != "" ]; do 81 | case "$1" in 82 | "-h" | "--help") 83 | print_help 84 | exit 85 | ;; 86 | "--chips="*) 87 | check_flag_value "${1:0:8}" "${1:8}" 88 | parse_chips "${1:8}" 89 | ;; 90 | "--protocols="*) 91 | check_flag_value "${1:0:12}" "${1:12}" 92 | parse_protocols "${1:12}" 93 | ;; 94 | *) 95 | if [ "${1:0:1}" == "-" ]; then abort "Unknown option '$1'."; fi 96 | parse_directory "$1" 97 | ;; 98 | esac 99 | shift 100 | done 101 | } 102 | 103 | validate_config() { 104 | if [ -z "$directory" ]; then abort "Missing directory input"; fi 105 | if [ -z "$chips" ]; then abort "Missing chip models input"; fi 106 | if [ -z "$protocols" ]; then abort "Missing protocols input"; fi 107 | } 108 | 109 | print_config() { 110 | echo "Directory: $directory" 111 | echo "Chip models: $chips" 112 | echo "Comm Protocols: $protocols" 113 | } 114 | 115 | build() { 116 | chip="$1" 117 | pro="$2" 118 | echo "################################################################################" 119 | echo ">>> Building $directory: chip '$chip' and protocol '$pro'" 120 | echo "################################################################################" 121 | prev_pwd=$(pwd) 122 | cd "$directory" 123 | echo "Adjusting Makefile..." 124 | sed -i "s/\${HOME}/\${TRAVIS_BUILD_DIR}/g" Makefile 125 | if grep -q "MPU_COMPONENT_PATH" Makefile; then 126 | sed -i "s/MPU_COMPONENT_PATH :=.*\$/MPU_COMPONENT_PATH := \${TRAVIS_BUILD_DIR}\\n/" Makefile 127 | fi 128 | if ! grep -q "EXTRA_COMPONENT_DIRS+=\${TRAVIS_BUILD_DIR}" Makefile; then 129 | sed -i "1s/^/EXTRA_COMPONENT_DIRS+=\${TRAVIS_BUILD_DIR}\\n/" Makefile 130 | fi 131 | # if ! grep -q "EXTRA_COMPONENT_DIRS+=\${HOME}/esp/libraries/MPUdriver" Makefile; then 132 | # sed -i "1s/^/EXTRA_COMPONENT_DIRS+=\${HOME}\\/esp\\/libraries\\/MPUdriver\\n/" Makefile 133 | # fi 134 | echo "Creating default 'sdkconfig'..." 135 | make defconfig >>build.log.txt 2>&1 136 | # sed -i "s/\"python\"/\"python2\"/" sdkconfig 137 | echo "Changing chip model to $chip..." 138 | sed -i "s/CONFIG_MPU6050=y/CONFIG_MPU6050=/" sdkconfig 139 | sed -i "s/CONFIG_$chip=/CONFIG_$chip=y/" sdkconfig 140 | echo "Changing comm protocol to $pro..." 141 | sed -i "s/CONFIG_MPU_I2C=/CONFIG_MPU_$pro=/" sdkconfig 142 | echo "Running 'defconfig' to adjust to changes..." 143 | make defconfig >>build.log.txt 2>&1 144 | echo "Final 'skdconfig':" 145 | grep "CONFIG_MPU" sdkconfig 146 | echo "Building..." 147 | make all -j3 1>>build.log.txt 148 | echo "Success." 149 | echo "Cleaning..." 150 | rm -f sdkconfig sdkconfig.old 151 | cd "$prev_pwd" 152 | echo "Done." 153 | } 154 | 155 | build_all() { 156 | for chip in $chips; do 157 | for pro in $protocols; do 158 | if ([ "$chip" == "MPU6050" ] || [ "$chip" == "MPU9150" ]) && [ "$pro" == "SPI" ]; then continue; fi 159 | build "$chip" "$pro" 160 | done 161 | done 162 | echo ">>> Built All successfully !!!" 163 | } 164 | 165 | # Main 166 | parse_arguments "$@" 167 | validate_config 168 | print_config 169 | build_all 170 | -------------------------------------------------------------------------------- /tools/ci/gen_doxygen_docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ################################################################################ 3 | ##### Setup this script and get the current gh-pages branch. ##### 4 | echo 'Setting up the script...' 5 | # Exit with nonzero exit code if anything fails 6 | set -e 7 | 8 | # Get the current gh-pages branch 9 | cd "$TRAVIS_BUILD_DIR" 10 | git clone --recursive -b gh-pages "https://$GH_REPO_REF" gh-pages 11 | cd gh-pages 12 | 13 | # Need to create a .nojekyll file to allow filenames starting with an underscore 14 | # to be seen on the gh-pages site. Therefore creating an empty .nojekyll file. 15 | # Presumably this is only needed when the SHORT_NAMES option in Doxygen is set 16 | # to NO, which it is by default. So creating the file just in case. 17 | echo "" > .nojekyll 18 | 19 | ################################################################################ 20 | ##### Generate the Doxygen code documentation and log the output. ##### 21 | echo 'Generating Doxygen code documentation...' 22 | cd "$TRAVIS_BUILD_DIR/$DOXYFILE_PATH" 23 | # Redirect both stderr and stdout to the log file AND the console. 24 | doxygen "$DOXYFILE" 2>&1 | tee "$TRAVIS_BUILD_DIR/gh-pages/doxygen.log" 25 | 26 | ################################################################################ 27 | ##### Upload the documentation to the gh-pages branch of the repository. ##### 28 | # Only upload if Doxygen successfully created the documentation. 29 | # Check this by verifying that the html directory and the file html/index.html 30 | # both exist. This is a good indication that Doxygen did it's work. 31 | if [ -d "html" ] && [ -f "html/index.html" ]; then 32 | echo 'Doxygen successfully generated the docs.. copying to gh-pages..' 33 | rm -rf "$TRAVIS_BUILD_DIR/gh-pages/html" 34 | cp -rf html "$TRAVIS_BUILD_DIR/gh-pages/" 35 | else 36 | echo '' >&2 37 | echo 'Warning: Doxygen failed to generate the docs.. no html build found!' >&2 38 | exit 1 39 | fi -------------------------------------------------------------------------------- /tools/ci/setup_esp_idf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script for Seting up ESP-IDF 3 | 4 | # Exit with nonzero exit code if anything fails 5 | set -e 6 | 7 | # Create dir to work 8 | cd "$TRAVIS_BUILD_DIR" 9 | mkdir -p esp 10 | cd esp 11 | 12 | echo "Setting up Toolchain..." 13 | if [ -d xtensa-esp32-elf ]; then 14 | echo "Already up to date." 15 | else 16 | echo 'Downloading Toolchain...' 17 | wget -c "https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz" --tries=20 --no-verbose 18 | echo 'Uncompressing Toolchain...' 19 | tar -xzf xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz 20 | if [ -d xtensa-esp32-elf ] && [ -f xtensa-esp32-elf/bin/xtensa-esp32-elf-gcc ]; then 21 | echo 'Toolchain successfully setup!' 22 | else 23 | echo 'Failed to setup Toolchain!' 24 | exit 1 25 | fi 26 | fi 27 | 28 | echo "Setting up ESP-IDF..." 29 | if [ -d esp-idf ]; then 30 | ( 31 | cd esp-idf 32 | git pull 33 | git submodule update --recursive 34 | cd .. 35 | ) 36 | else 37 | git clone --recursive --depth=50 https://github.com/espressif/esp-idf.git 38 | fi 39 | 40 | # Debug 41 | # echo '' 42 | # echo 'Current Directory' && pwd 43 | # echo 'Directory Contents' && ls 44 | # echo 'IDF_PATH is: ' $IDF_PATH 45 | 46 | echo '' 47 | echo 'Done.' -------------------------------------------------------------------------------- /tools/ci/setup_ext_libs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script for Setting up External Libraries 3 | echo 'Setting up External Libraries...' 4 | 5 | # Exit with nonzero exit code if anything fails 6 | set -e 7 | 8 | # Download libs to esp/libraries 9 | cd "$TRAVIS_BUILD_DIR" 10 | mkdir -p esp/libraries 11 | cd esp/libraries 12 | 13 | echo "Setting up I2Cbus..." 14 | if [ -d I2Cbus ]; then 15 | ( 16 | cd I2Cbus 17 | git pull 18 | cd .. 19 | ) 20 | else 21 | git clone --recursive https://github.com/natanaeljr/esp32-I2Cbus I2Cbus 22 | fi 23 | 24 | echo "Setting up SPIbus.." 25 | if [ -d SPIbus ]; then 26 | ( 27 | cd SPIbus 28 | git pull 29 | cd .. 30 | ) 31 | else 32 | git clone --recursive https://github.com/natanaeljr/esp32-SPIbus SPIbus 33 | fi 34 | 35 | # Debug 36 | # echo '' 37 | # echo 'Libraries:' && ls 38 | 39 | echo '' 40 | echo 'Done.' --------------------------------------------------------------------------------