From 88a815b526e0636d48f0ac3e8a1831bab863fbe4 Mon Sep 17 00:00:00 2001 From: RogueMaster Date: Fri, 11 Nov 2022 16:22:38 -0500 Subject: [PATCH] Revert "Revert "Air Mouse"" This reverts commit bbc0888b45592cdd1051b19849498952798471c2. --- applications/plugins/airmouse/LICENSE | 201 + applications/plugins/airmouse/README.md | 56 + applications/plugins/airmouse/air_mouse.c | 156 + applications/plugins/airmouse/air_mouse.h | 30 + applications/plugins/airmouse/application.fam | 9 + applications/plugins/airmouse/mouse_10px.png | Bin 0 -> 1634 bytes .../airmouse/schematic/airmouse_bom.csv | 5 + .../airmouse/schematic/airmouse_cpl.csv | 8 + .../airmouse/schematic/airmouse_gerber.zip | Bin 0 -> 6687 bytes .../plugins/airmouse/schematic/flipper.jpg | Bin 0 -> 45104 bytes .../plugins/airmouse/schematic/render.png | Bin 0 -> 44448 bytes .../plugins/airmouse/schematic/schematic.png | Bin 0 -> 41159 bytes .../airmouse/tracking/calibration_data.cc | 85 + .../airmouse/tracking/calibration_data.h | 117 + .../plugins/airmouse/tracking/imu/bmi160.c | 5988 ++++++++++++++ .../plugins/airmouse/tracking/imu/bmi160.h | 992 +++ .../airmouse/tracking/imu/bmi160_defs.h | 1619 ++++ .../plugins/airmouse/tracking/imu/imu.c | 29 + .../plugins/airmouse/tracking/imu/imu.h | 18 + .../airmouse/tracking/imu/imu_bmi160.c | 88 + .../airmouse/tracking/imu/imu_lsm6ds3trc.c | 94 + .../airmouse/tracking/imu/lsm6ds3tr_c_reg.c | 7105 +++++++++++++++++ .../airmouse/tracking/imu/lsm6ds3tr_c_reg.h | 2448 ++++++ .../plugins/airmouse/tracking/main_loop.cc | 189 + .../plugins/airmouse/tracking/main_loop.h | 21 + .../airmouse/tracking/orientation_tracker.cc | 95 + .../airmouse/tracking/orientation_tracker.h | 68 + .../tracking/sensors/accelerometer_data.h | 38 + .../sensors/gyroscope_bias_estimator.cc | 313 + .../sensors/gyroscope_bias_estimator.h | 134 + .../tracking/sensors/gyroscope_data.h | 38 + .../tracking/sensors/lowpass_filter.cc | 84 + .../tracking/sensors/lowpass_filter.h | 81 + .../airmouse/tracking/sensors/mean_filter.cc | 46 + .../airmouse/tracking/sensors/mean_filter.h | 48 + .../tracking/sensors/median_filter.cc | 69 + .../airmouse/tracking/sensors/median_filter.h | 53 + .../tracking/sensors/pose_prediction.cc | 71 + .../tracking/sensors/pose_prediction.h | 55 + .../airmouse/tracking/sensors/pose_state.h | 56 + .../tracking/sensors/sensor_fusion_ekf.cc | 333 + .../tracking/sensors/sensor_fusion_ekf.h | 188 + .../plugins/airmouse/tracking/util/logging.h | 38 + .../airmouse/tracking/util/matrix_3x3.cc | 121 + .../airmouse/tracking/util/matrix_3x3.h | 138 + .../airmouse/tracking/util/matrix_4x4.cc | 87 + .../airmouse/tracking/util/matrix_4x4.h | 37 + .../airmouse/tracking/util/matrixutils.cc | 148 + .../airmouse/tracking/util/matrixutils.h | 65 + .../airmouse/tracking/util/rotation.cc | 117 + .../plugins/airmouse/tracking/util/rotation.h | 156 + .../plugins/airmouse/tracking/util/vector.h | 251 + .../airmouse/tracking/util/vectorutils.cc | 40 + .../airmouse/tracking/util/vectorutils.h | 76 + .../plugins/airmouse/views/bt_mouse.c | 157 + .../plugins/airmouse/views/bt_mouse.h | 14 + .../plugins/airmouse/views/calibration.c | 69 + .../plugins/airmouse/views/calibration.h | 12 + .../plugins/airmouse/views/usb_mouse.c | 124 + .../plugins/airmouse/views/usb_mouse.h | 12 + 60 files changed, 22690 insertions(+) create mode 100644 applications/plugins/airmouse/LICENSE create mode 100644 applications/plugins/airmouse/README.md create mode 100644 applications/plugins/airmouse/air_mouse.c create mode 100644 applications/plugins/airmouse/air_mouse.h create mode 100644 applications/plugins/airmouse/application.fam create mode 100644 applications/plugins/airmouse/mouse_10px.png create mode 100644 applications/plugins/airmouse/schematic/airmouse_bom.csv create mode 100644 applications/plugins/airmouse/schematic/airmouse_cpl.csv create mode 100644 applications/plugins/airmouse/schematic/airmouse_gerber.zip create mode 100644 applications/plugins/airmouse/schematic/flipper.jpg create mode 100644 applications/plugins/airmouse/schematic/render.png create mode 100644 applications/plugins/airmouse/schematic/schematic.png create mode 100644 applications/plugins/airmouse/tracking/calibration_data.cc create mode 100644 applications/plugins/airmouse/tracking/calibration_data.h create mode 100644 applications/plugins/airmouse/tracking/imu/bmi160.c create mode 100644 applications/plugins/airmouse/tracking/imu/bmi160.h create mode 100644 applications/plugins/airmouse/tracking/imu/bmi160_defs.h create mode 100644 applications/plugins/airmouse/tracking/imu/imu.c create mode 100644 applications/plugins/airmouse/tracking/imu/imu.h create mode 100644 applications/plugins/airmouse/tracking/imu/imu_bmi160.c create mode 100644 applications/plugins/airmouse/tracking/imu/imu_lsm6ds3trc.c create mode 100644 applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.c create mode 100644 applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.h create mode 100644 applications/plugins/airmouse/tracking/main_loop.cc create mode 100644 applications/plugins/airmouse/tracking/main_loop.h create mode 100644 applications/plugins/airmouse/tracking/orientation_tracker.cc create mode 100644 applications/plugins/airmouse/tracking/orientation_tracker.h create mode 100644 applications/plugins/airmouse/tracking/sensors/accelerometer_data.h create mode 100644 applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.cc create mode 100644 applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.h create mode 100644 applications/plugins/airmouse/tracking/sensors/gyroscope_data.h create mode 100644 applications/plugins/airmouse/tracking/sensors/lowpass_filter.cc create mode 100644 applications/plugins/airmouse/tracking/sensors/lowpass_filter.h create mode 100644 applications/plugins/airmouse/tracking/sensors/mean_filter.cc create mode 100644 applications/plugins/airmouse/tracking/sensors/mean_filter.h create mode 100644 applications/plugins/airmouse/tracking/sensors/median_filter.cc create mode 100644 applications/plugins/airmouse/tracking/sensors/median_filter.h create mode 100644 applications/plugins/airmouse/tracking/sensors/pose_prediction.cc create mode 100644 applications/plugins/airmouse/tracking/sensors/pose_prediction.h create mode 100644 applications/plugins/airmouse/tracking/sensors/pose_state.h create mode 100644 applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.cc create mode 100644 applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.h create mode 100644 applications/plugins/airmouse/tracking/util/logging.h create mode 100644 applications/plugins/airmouse/tracking/util/matrix_3x3.cc create mode 100644 applications/plugins/airmouse/tracking/util/matrix_3x3.h create mode 100644 applications/plugins/airmouse/tracking/util/matrix_4x4.cc create mode 100644 applications/plugins/airmouse/tracking/util/matrix_4x4.h create mode 100644 applications/plugins/airmouse/tracking/util/matrixutils.cc create mode 100644 applications/plugins/airmouse/tracking/util/matrixutils.h create mode 100644 applications/plugins/airmouse/tracking/util/rotation.cc create mode 100644 applications/plugins/airmouse/tracking/util/rotation.h create mode 100644 applications/plugins/airmouse/tracking/util/vector.h create mode 100644 applications/plugins/airmouse/tracking/util/vectorutils.cc create mode 100644 applications/plugins/airmouse/tracking/util/vectorutils.h create mode 100644 applications/plugins/airmouse/views/bt_mouse.c create mode 100644 applications/plugins/airmouse/views/bt_mouse.h create mode 100644 applications/plugins/airmouse/views/calibration.c create mode 100644 applications/plugins/airmouse/views/calibration.h create mode 100644 applications/plugins/airmouse/views/usb_mouse.c create mode 100644 applications/plugins/airmouse/views/usb_mouse.h diff --git a/applications/plugins/airmouse/LICENSE b/applications/plugins/airmouse/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/applications/plugins/airmouse/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/applications/plugins/airmouse/README.md b/applications/plugins/airmouse/README.md new file mode 100644 index 000000000..9df0d69b0 --- /dev/null +++ b/applications/plugins/airmouse/README.md @@ -0,0 +1,56 @@ +# Flipper Air Mouse + +## Brief + +> "You can turn anything into an air mouse if you're brave enough" + + — Piper, a.k.a. Pez + +Naturally, the quote above applies to [Flipper](https://flipperzero.one/) as well. + +## What? + +The app allows you to turn your Flipper into a USB or Bluetooth air mouse (you do need an extra module, see the Hardware section below)... + +Using it is really simple: + * Connect the Flipper via a USB cable and pick `USB`, or pick `Bluetooth` and pair it with your PC; + * Hold the Flipper in your hand with the buttons pointing towards the screen; + * Wave your Flipper like you don't care to move the cursor; + * Up button for Left mouse click; + * Down button for Right mouse click; + * Center button for Middle mouse click; + * Use calibration menu option if you notice significant drift (place your Flipper onto a level surface, make sure it doesn't move, run this option, wait 2 seconds, done). + +See early prototype [in action](https://www.youtube.com/watch?v=DdxAmmsYfMA). + +## Hardware + +The custom module is using Bosch BMI160 accelerometer/gyroscope chip connected via I2C. + +Take a look into the [schematic](https://github.com/ginkage/FlippAirMouse/tree/main/schematic) folder for Gerber, BOM and CPL files, so you can order directly from JLCPCB. + +Original idea: + +![What I thought it would look like](https://github.com/ginkage/FlippAirMouse/blob/main/schematic/schematic.png) + +Expectation: + +![What EDA though it would look like](https://github.com/ginkage/FlippAirMouse/blob/main/schematic/render.png) + +Reality: + +![What it looks like](https://github.com/ginkage/FlippAirMouse/blob/main/schematic/flipper.jpg) + + +## Software + +The code is based on the original Bosch [driver](https://github.com/BoschSensortec/BMI160_driver/) and an orientation tracking implementation from the [Cardboard](https://github.com/googlevr/cardboard/tree/master/sdk/sensors) project + +If you're familiar with Flipper applications, start in the [firmware](https://github.com/flipperdevices/flipperzero-firmware) checkout folder and do the following: +``` +cd applications/plugins +git clone https://github.com/ginkage/FlippAirMouse +cd ../.. +./fbt fap_air_mouse +``` +If you're not familiar with those, just grab a `fap` file from Releases. diff --git a/applications/plugins/airmouse/air_mouse.c b/applications/plugins/airmouse/air_mouse.c new file mode 100644 index 000000000..7a90e49f1 --- /dev/null +++ b/applications/plugins/airmouse/air_mouse.c @@ -0,0 +1,156 @@ +#include "air_mouse.h" + +#include +#include + +#include "tracking/imu/imu.h" + +#define TAG "AirMouseApp" + +enum AirMouseSubmenuIndex { + AirMouseSubmenuIndexBtMouse, + AirMouseSubmenuIndexUsbMouse, + AirMouseSubmenuIndexCalibration, +}; + +void air_mouse_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + AirMouse* app = context; + if(index == AirMouseSubmenuIndexBtMouse) { + app->view_id = AirMouseViewBtMouse; + view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewBtMouse); + } else if(index == AirMouseSubmenuIndexUsbMouse) { + app->view_id = AirMouseViewUsbMouse; + view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewUsbMouse); + } else if(index == AirMouseSubmenuIndexCalibration) { + app->view_id = AirMouseViewCalibration; + view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewCalibration); + } +} + +void air_mouse_dialog_callback(DialogExResult result, void* context) { + furi_assert(context); + AirMouse* app = context; + if(result == DialogExResultLeft) { + view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_NONE); // Exit + } else if(result == DialogExResultRight) { + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view + } else if(result == DialogExResultCenter) { + view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewSubmenu); // Menu + } +} + +uint32_t air_mouse_exit_confirm_view(void* context) { + UNUSED(context); + return AirMouseViewExitConfirm; +} + +uint32_t air_mouse_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +AirMouse* air_mouse_app_alloc() { + AirMouse* app = malloc(sizeof(AirMouse)); + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Submenu view + app->submenu = submenu_alloc(); + submenu_add_item( + app->submenu, "Bluetooth", AirMouseSubmenuIndexBtMouse, air_mouse_submenu_callback, app); + submenu_add_item( + app->submenu, "USB", AirMouseSubmenuIndexUsbMouse, air_mouse_submenu_callback, app); + submenu_add_item( + app->submenu, + "Calibration", + AirMouseSubmenuIndexCalibration, + air_mouse_submenu_callback, + app); + view_set_previous_callback(submenu_get_view(app->submenu), air_mouse_exit); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewSubmenu, submenu_get_view(app->submenu)); + + // Dialog view + app->dialog = dialog_ex_alloc(); + dialog_ex_set_result_callback(app->dialog, air_mouse_dialog_callback); + dialog_ex_set_context(app->dialog, app); + dialog_ex_set_left_button_text(app->dialog, "Exit"); + dialog_ex_set_right_button_text(app->dialog, "Stay"); + dialog_ex_set_center_button_text(app->dialog, "Menu"); + dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewExitConfirm, dialog_ex_get_view(app->dialog)); + + // Bluetooth view + app->bt_mouse = bt_mouse_alloc(app->view_dispatcher); + view_set_previous_callback(bt_mouse_get_view(app->bt_mouse), air_mouse_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewBtMouse, bt_mouse_get_view(app->bt_mouse)); + + // USB view + app->usb_mouse = usb_mouse_alloc(app->view_dispatcher); + view_set_previous_callback(usb_mouse_get_view(app->usb_mouse), air_mouse_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewUsbMouse, usb_mouse_get_view(app->usb_mouse)); + + // Calibration view + app->calibration = calibration_alloc(app->view_dispatcher); + view_set_previous_callback( + calibration_get_view(app->calibration), air_mouse_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewCalibration, calibration_get_view(app->calibration)); + + app->view_id = AirMouseViewSubmenu; + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); + + return app; +} + +void air_mouse_app_free(AirMouse* app) { + furi_assert(app); + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewExitConfirm); + dialog_ex_free(app->dialog); + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewBtMouse); + bt_mouse_free(app->bt_mouse); + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewUsbMouse); + usb_mouse_free(app->usb_mouse); + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewCalibration); + calibration_free(app->calibration); + view_dispatcher_free(app->view_dispatcher); + + // Close records + furi_record_close(RECORD_GUI); + app->gui = NULL; + + // Free rest + free(app); +} + +int32_t air_mouse_app(void* p) { + UNUSED(p); + + AirMouse* app = air_mouse_app_alloc(); + if(!imu_begin()) { + air_mouse_app_free(app); + return -1; + } + + DOLPHIN_DEED(DolphinDeedPluginStart); + view_dispatcher_run(app->view_dispatcher); + + imu_end(); + air_mouse_app_free(app); + + return 0; +} diff --git a/applications/plugins/airmouse/air_mouse.h b/applications/plugins/airmouse/air_mouse.h new file mode 100644 index 000000000..3a1ba783e --- /dev/null +++ b/applications/plugins/airmouse/air_mouse.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "views/bt_mouse.h" +#include "views/usb_mouse.h" +#include "views/calibration.h" + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + DialogEx* dialog; + BtMouse* bt_mouse; + UsbMouse* usb_mouse; + Calibration* calibration; + uint32_t view_id; +} AirMouse; + +typedef enum { + AirMouseViewSubmenu, + AirMouseViewBtMouse, + AirMouseViewUsbMouse, + AirMouseViewCalibration, + AirMouseViewExitConfirm, +} AirMouseView; diff --git a/applications/plugins/airmouse/application.fam b/applications/plugins/airmouse/application.fam new file mode 100644 index 000000000..abc3f55bb --- /dev/null +++ b/applications/plugins/airmouse/application.fam @@ -0,0 +1,9 @@ +App( + appid="Air_Mouse", + name="[BMI160] Air Mouse", + apptype=FlipperAppType.EXTERNAL, + entry_point="air_mouse_app", + stack_size=10 * 1024, + fap_icon="mouse_10px.png", + fap_category="GPIO", +) diff --git a/applications/plugins/airmouse/mouse_10px.png b/applications/plugins/airmouse/mouse_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..94c3a7a14143c00d6747ed59274c96e98d178b41 GIT binary patch literal 1634 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 D<}Ic3 literal 0 HcmV?d00001 diff --git a/applications/plugins/airmouse/schematic/flipper.jpg b/applications/plugins/airmouse/schematic/flipper.jpg new file mode 100644 index 0000000000000000000000000000000000000000..39840a3e3445d9912b803e61018be4cac79ee80a GIT binary patch literal 45104 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 Z1xFUz0F|A|Xafw|j0XcS2mSBs{{hkx3km=L literal 0 HcmV?d00001 diff --git a/applications/plugins/airmouse/schematic/render.png b/applications/plugins/airmouse/schematic/render.png new file mode 100644 index 0000000000000000000000000000000000000000..4292661014cf9e1aba29d4c66dd4be27b990b74f GIT binary patch literal 44448 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 DhCcvb literal 0 HcmV?d00001 diff --git a/applications/plugins/airmouse/schematic/schematic.png b/applications/plugins/airmouse/schematic/schematic.png new file mode 100644 index 0000000000000000000000000000000000000000..97eea29dd973ea2c196c3052eed04a362db821a7 GIT binary patch literal 41159 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 +#include + +#define TAG "tracker" + +#include "calibration_data.h" + +#include +#include + +// Student's distribution T value for 95% (two-sided) confidence interval. +static const double Tn = 1.960; + +// Number of samples (degrees of freedom) for the corresponding T values. +static const int Nn = 200; + +void CalibrationData::reset() +{ + complete = false; + count = 0; + sum = Vector::Zero(); + sumSq = Vector::Zero(); + mean = Vector::Zero(); + median = Vector::Zero(); + sigma = Vector::Zero(); + delta = Vector::Zero(); + xData.clear(); + yData.clear(); + zData.clear(); +} + +bool CalibrationData::add(Vector& data) +{ + if (complete) { + return true; + } + + xData.push_back(data[0]); + yData.push_back(data[1]); + zData.push_back(data[2]); + + sum += data; + sumSq += data * data; + count++; + + if (count >= Nn) { + calcDelta(); + complete = true; + } + + return complete; +} + +static inline double medianOf(std::vector& list) +{ + std::sort(list.begin(), list.end()); + int count = list.size(); + int middle = count / 2; + return (count % 2 == 1) ? list[middle] : (list[middle - 1] + list[middle]) / 2.0l; +} + +void CalibrationData::calcDelta() +{ + median.Set(medianOf(xData), medianOf(yData), medianOf(zData)); + + mean = sum / count; + Vector m2 = mean * mean; + Vector d = sumSq / count - m2; + Vector s2 = (d * count) / (count - 1); + sigma = Vector(std::sqrt(d[0]), std::sqrt(d[1]), std::sqrt(d[2])); + Vector s = Vector(std::sqrt(s2[0]), std::sqrt(s2[1]), std::sqrt(s2[2])); + delta = s * Tn / std::sqrt((double)count); + Vector low = mean - delta; + Vector high = mean + delta; + + FURI_LOG_I(TAG, + "M[x] = { %f ... %f } // median = %f // avg = %f // delta = %f // sigma = %f", + low[0], high[0], median[0], mean[0], delta[0], sigma[0]); + FURI_LOG_I(TAG, + "M[y] = { %f ... %f } // median = %f // avg = %f // delta = %f // sigma = %f", + low[1], high[1], median[1], mean[1], delta[1], sigma[1]); + FURI_LOG_I(TAG, + "M[z] = { %f ... %f } // median = %f // avg = %f // delta = %f // sigma = %f", + low[2], high[2], median[2], mean[2], delta[2], sigma[2]); +} \ No newline at end of file diff --git a/applications/plugins/airmouse/tracking/calibration_data.h b/applications/plugins/airmouse/tracking/calibration_data.h new file mode 100644 index 000000000..d47dab08d --- /dev/null +++ b/applications/plugins/airmouse/tracking/calibration_data.h @@ -0,0 +1,117 @@ +#pragma once + +#include +#include +#include + +#include "util/vector.h" + +#define CALIBRATION_DATA_VER (1) +#define CALIBRATION_DATA_FILE_NAME ".calibration.data" +#define CALIBRATION_DATA_PATH INT_PATH(CALIBRATION_DATA_FILE_NAME) +#define CALIBRATION_DATA_MAGIC (0x23) + +#define CALIBRATION_DATA_SAVE(x) \ + saved_struct_save( \ + CALIBRATION_DATA_PATH, \ + (x), \ + sizeof(CalibrationMedian), \ + CALIBRATION_DATA_MAGIC, \ + CALIBRATION_DATA_VER) + +#define CALIBRATION_DATA_LOAD(x) \ + saved_struct_load( \ + CALIBRATION_DATA_PATH, \ + (x), \ + sizeof(CalibrationMedian), \ + CALIBRATION_DATA_MAGIC, \ + CALIBRATION_DATA_VER) + +typedef struct { + double x; + double y; + double z; +} CalibrationMedian; + +typedef cardboard::Vector3 Vector; + +/** + * Helper class to gather some stats and store the calibration data. Right now it calculates a lot + * more stats than actually needed. Some of them are used for logging the sensors quality (and + * filing bugs), other may be required in the future, e.g. for bias. + */ +class CalibrationData { +public: + /** + * Check if the sensors were calibrated before. + * + * @return {@code true} if calibration data is available, or {@code false} otherwise. + */ + bool isComplete() { + return complete; + } + + /** Prepare to collect new calibration data. */ + void reset(); + + /** + * Retrieve the median gyroscope readings. + * + * @return Three-axis median vector. + */ + Vector getMedian() { + return median; + } + + /** + * Retrieve the mean gyroscope readings. + * + * @return Three-axis mean vector. + */ + Vector getMean() { + return mean; + } + + /** + * Retrieve the standard deviation of gyroscope readings. + * + * @return Three-axis standard deviation vector. + */ + Vector getSigma() { + return sigma; + } + + /** + * Retrieve the confidence interval size of gyroscope readings. + * + * @return Three-axis confidence interval size vector. + */ + Vector getDelta() { + return delta; + } + + /** + * Add a new gyroscope reading to the stats. + * + * @param data gyroscope values vector. + * @return {@code true} if we now have enough data for calibration, or {@code false} otherwise. + */ + bool add(Vector& data); + +private: + // Calculates the confidence interval (mean +- delta) and some other related values, like + // standard deviation, etc. See https://en.wikipedia.org/wiki/Student%27s_t-distribution + void calcDelta(); + + int count; + bool complete; + Vector sum; + Vector sumSq; + Vector mean; + Vector median; + Vector sigma; + Vector delta; + std::vector xData; + std::vector yData; + std::vector zData; +}; diff --git a/applications/plugins/airmouse/tracking/imu/bmi160.c b/applications/plugins/airmouse/tracking/imu/bmi160.c new file mode 100644 index 000000000..968dddd4d --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/bmi160.c @@ -0,0 +1,5988 @@ +/** +* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved. +* +* BSD-3-Clause +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +* @file bmi160.c +* @date 2021-10-05 +* @version v3.9.2 +* +*/ + +#include "bmi160.h" + +/* Below look up table follows the enum bmi160_int_types. + * Hence any change should match to the enum bmi160_int_types + */ +const uint8_t int_mask_lookup_table[13] = { + BMI160_INT1_SLOPE_MASK, + BMI160_INT1_SLOPE_MASK, + BMI160_INT2_LOW_STEP_DETECT_MASK, + BMI160_INT1_DOUBLE_TAP_MASK, + BMI160_INT1_SINGLE_TAP_MASK, + BMI160_INT1_ORIENT_MASK, + BMI160_INT1_FLAT_MASK, + BMI160_INT1_HIGH_G_MASK, + BMI160_INT1_LOW_G_MASK, + BMI160_INT1_NO_MOTION_MASK, + BMI160_INT2_DATA_READY_MASK, + BMI160_INT2_FIFO_FULL_MASK, + BMI160_INT2_FIFO_WM_MASK}; + +/*********************************************************************/ +/* Static function declarations */ + +/*! + * @brief This API configures the pins to fire the + * interrupt signal when it occurs + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_intr_pin_config(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the any-motion interrupt of the sensor. + * This interrupt occurs when accel values exceeds preset threshold + * for a certain period of time. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_any_motion_int(struct bmi160_int_settg* int_config, struct bmi160_dev* dev); + +/*! + * @brief This API sets tap interrupts.Interrupt is fired when + * tap movements happen. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_accel_tap_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the data ready interrupt for both accel and gyro. + * This interrupt occurs when new accel and gyro data come. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_accel_gyro_data_ready_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_dev* dev); + +/*! + * @brief This API sets the significant motion interrupt of the sensor.This + * interrupt occurs when there is change in user location. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_sig_motion_int(struct bmi160_int_settg* int_config, struct bmi160_dev* dev); + +/*! + * @brief This API sets the no motion/slow motion interrupt of the sensor. + * Slow motion is similar to any motion interrupt.No motion interrupt + * occurs when slope bet. two accel values falls below preset threshold + * for preset duration. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_no_motion_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the step detection interrupt.This interrupt + * occurs when the single step causes accel values to go above + * preset threshold. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_step_detect_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the orientation interrupt of the sensor.This + * interrupt occurs when there is orientation change in the sensor + * with respect to gravitational field vector g. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_orientation_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the flat interrupt of the sensor.This interrupt + * occurs in case of flat orientation + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_flat_detect_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the low-g interrupt of the sensor.This interrupt + * occurs during free-fall. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_low_g_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the high-g interrupt of the sensor.The interrupt + * occurs if the absolute value of acceleration data of any enabled axis + * exceeds the programmed threshold and the sign of the value does not + * change for a preset duration. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_high_g_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the default configuration parameters of accel & gyro. + * Also maintain the previous state of configurations. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static void default_param_settg(struct bmi160_dev* dev); + +/*! + * @brief This API is used to validate the device structure pointer for + * null conditions. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t null_ptr_check(const struct bmi160_dev* dev); + +/*! + * @brief This API set the accel configuration. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_accel_conf(struct bmi160_dev* dev); + +/*! + * @brief This API gets the accel configuration. + * + * @param[out] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t get_accel_conf(struct bmi160_dev* dev); + +/*! + * @brief This API check the accel configuration. + * + * @param[in] data : Pointer to store the updated accel config. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t check_accel_config(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the accel odr. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_accel_odr(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the accel bandwidth. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_accel_bw(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the accel range. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_accel_range(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API checks the invalid settings for ODR & Bw for Accel and Gyro. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t check_invalid_settg(const struct bmi160_dev* dev); + +/*! + * @brief This API set the gyro configuration. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_gyro_conf(struct bmi160_dev* dev); + +/*! + * @brief This API get the gyro configuration. + * + * @param[out] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t get_gyro_conf(struct bmi160_dev* dev); + +/*! + * @brief This API check the gyro configuration. + * + * @param[in] data : Pointer to store the updated gyro config. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t check_gyro_config(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the gyro odr. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_gyro_odr(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the gyro bandwidth. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_gyro_bw(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the gyro range. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_gyro_range(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the accel power mode. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_accel_pwr(struct bmi160_dev* dev); + +/*! + * @brief This API process the undersampling setting of Accel. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_under_sampling(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the gyro power mode. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_gyro_pwr(struct bmi160_dev* dev); + +/*! + * @brief This API reads accel data along with sensor time if time is requested + * by user. Kindly refer the user guide(README.md) for more info. + * + * @param[in] len : len to read no of bytes + * @param[out] accel : Structure pointer to store accel data + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + get_accel_data(uint8_t len, struct bmi160_sensor_data* accel, const struct bmi160_dev* dev); + +/*! + * @brief This API reads accel data along with sensor time if time is requested + * by user. Kindly refer the user guide(README.md) for more info. + * + * @param[in] len : len to read no of bytes + * @param[out] gyro : Structure pointer to store accel data + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + get_gyro_data(uint8_t len, struct bmi160_sensor_data* gyro, const struct bmi160_dev* dev); + +/*! + * @brief This API reads accel and gyro data along with sensor time + * if time is requested by user. + * Kindly refer the user guide(README.md) for more info. + * + * @param[in] len : len to read no of bytes + * @param[out] accel : Structure pointer to store accel data + * @param[out] gyro : Structure pointer to store accel data + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t get_accel_gyro_data( + uint8_t len, + struct bmi160_sensor_data* accel, + struct bmi160_sensor_data* gyro, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the any-motion interrupt for accel. + * + * @param[in] any_motion_int_cfg : Structure instance of + * bmi160_acc_any_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_accel_any_motion_int( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + struct bmi160_dev* dev); + +/*! + * @brief This API disable the sig-motion interrupt. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t disable_sig_motion_int(const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for any-motion interrupt. + * + * @param[in] any_motion_int_cfg : Structure instance of + * bmi160_acc_any_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_any_motion_src( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the duration and threshold of + * any-motion interrupt. + * + * @param[in] any_motion_int_cfg : Structure instance of + * bmi160_acc_any_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_any_dur_threshold( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure necessary setting of any-motion interrupt. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] any_motion_int_cfg : Structure instance of + * bmi160_acc_any_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_any_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enable the data ready interrupt. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_data_ready_int(const struct bmi160_dev* dev); + +/*! + * @brief This API enables the no motion/slow motion interrupt. + * + * @param[in] no_mot_int_cfg : Structure instance of + * bmi160_acc_no_motion_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_no_motion_int( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the interrupt PIN setting for + * no motion/slow motion interrupt. + * + * @param[in] int_config : structure instance of bmi160_int_settg. + * @param[in] no_mot_int_cfg : Structure instance of + * bmi160_acc_no_motion_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_no_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of interrupt for no motion. + * + * @param[in] no_mot_int_cfg : Structure instance of + * bmi160_acc_no_motion_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_no_motion_data_src( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the duration and threshold of + * no motion/slow motion interrupt along with selection of no/slow motion. + * + * @param[in] no_mot_int_cfg : Structure instance of + * bmi160_acc_no_motion_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_no_motion_dur_thr( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the sig-motion motion interrupt. + * + * @param[in] sig_mot_int_cfg : Structure instance of + * bmi160_acc_sig_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_sig_motion_int( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + struct bmi160_dev* dev); + +/*! + * @brief This API configure the interrupt PIN setting for + * significant motion interrupt. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] sig_mot_int_cfg : Structure instance of + * bmi160_acc_sig_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_sig_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for sig motion interrupt. + * + * @param[in] sig_mot_int_cfg : Structure instance of + * bmi160_acc_sig_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_sig_motion_data_src( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the threshold, skip and proof time of + * sig motion interrupt. + * + * @param[in] sig_mot_int_cfg : Structure instance of + * bmi160_acc_sig_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_sig_dur_threshold( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the step detector interrupt. + * + * @param[in] step_detect_int_cfg : Structure instance of + * bmi160_acc_step_detect_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_step_detect_int( + const struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the step detector parameter. + * + * @param[in] step_detect_int_cfg : Structure instance of + * bmi160_acc_step_detect_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_step_detect( + const struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the single/double tap interrupt. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_tap_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the interrupt PIN setting for + * tap interrupt. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] tap_int_cfg : Structure instance of bmi160_acc_tap_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_tap_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for tap interrupt. + * + * @param[in] tap_int_cfg : Structure instance of bmi160_acc_tap_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_tap_data_src( + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the parameters of tap interrupt. + * Threshold, quite, shock, and duration. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] tap_int_cfg : Structure instance of bmi160_acc_tap_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_tap_param( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enable the external mode configuration. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_sec_if(const struct bmi160_dev* dev); + +/*! + * @brief This API configure the ODR of the auxiliary sensor. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_aux_odr(const struct bmi160_dev* dev); + +/*! + * @brief This API maps the actual burst read length set by user. + * + * @param[in] len : Pointer to store the read length. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t map_read_len(uint16_t* len, const struct bmi160_dev* dev); + +/*! + * @brief This API configure the settings of auxiliary sensor. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_aux_settg(const struct bmi160_dev* dev); + +/*! + * @brief This API extract the read data from auxiliary sensor. + * + * @param[in] map_len : burst read value. + * @param[in] reg_addr : Address of register to read. + * @param[in] aux_data : Pointer to store the read data. + * @param[in] len : length to read the data. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t extract_aux_read( + uint16_t map_len, + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the orient interrupt. + * + * @param[in] orient_int_cfg : Structure instance of bmi160_acc_orient_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_orient_int( + const struct bmi160_acc_orient_int_cfg* orient_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the necessary setting of orientation interrupt. + * + * @param[in] orient_int_cfg : Structure instance of bmi160_acc_orient_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_orient_int_settg( + const struct bmi160_acc_orient_int_cfg* orient_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the flat interrupt. + * + * @param[in] flat_int : Structure instance of bmi160_acc_flat_detect_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_flat_int( + const struct bmi160_acc_flat_detect_int_cfg* flat_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the necessary setting of flat interrupt. + * + * @param[in] flat_int : Structure instance of bmi160_acc_flat_detect_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_flat_int_settg( + const struct bmi160_acc_flat_detect_int_cfg* flat_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the Low-g interrupt. + * + * @param[in] low_g_int : Structure instance of bmi160_acc_low_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_low_g_int( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) for low-g interrupt. + * + * @param[in] low_g_int : Structure instance of bmi160_acc_low_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_low_g_data_src( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the necessary setting of low-g interrupt. + * + * @param[in] low_g_int : Structure instance of bmi160_acc_low_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_low_g_int_settg( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the high-g interrupt. + * + * @param[in] high_g_int_cfg : Structure instance of bmi160_acc_high_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_high_g_int( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for high-g interrupt. + * + * @param[in] high_g_int_cfg : Structure instance of bmi160_acc_high_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_high_g_data_src( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the necessary setting of high-g interrupt. + * + * @param[in] high_g_int_cfg : Structure instance of bmi160_acc_high_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_high_g_int_settg( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the behavioural setting of interrupt pin. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + config_int_out_ctrl(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API configure the mode(input enable, latch or non-latch) of interrupt pin. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + config_int_latch(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API performs the self test for accelerometer of BMI160 + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t perform_accel_self_test(struct bmi160_dev* dev); + +/*! + * @brief This API enables to perform the accel self test by setting proper + * configurations to facilitate accel self test + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_accel_self_test(struct bmi160_dev* dev); + +/*! + * @brief This API performs accel self test with positive excitation + * + * @param[in] accel_pos : Structure pointer to store accel data + * for positive excitation + * @param[in] dev : structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t accel_self_test_positive_excitation( + struct bmi160_sensor_data* accel_pos, + const struct bmi160_dev* dev); + +/*! + * @brief This API performs accel self test with negative excitation + * + * @param[in] accel_neg : Structure pointer to store accel data + * for negative excitation + * @param[in] dev : structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t accel_self_test_negative_excitation( + struct bmi160_sensor_data* accel_neg, + const struct bmi160_dev* dev); + +/*! + * @brief This API validates the accel self test results + * + * @param[in] accel_pos : Structure pointer to store accel data + * for positive excitation + * @param[in] accel_neg : Structure pointer to store accel data + * for negative excitation + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error / +ve value -> Self test fail + */ +static int8_t validate_accel_self_test( + const struct bmi160_sensor_data* accel_pos, + const struct bmi160_sensor_data* accel_neg); + +/*! + * @brief This API performs the self test for gyroscope of BMI160 + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t perform_gyro_self_test(const struct bmi160_dev* dev); + +/*! + * @brief This API enables the self test bit to trigger self test for gyro + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_gyro_self_test(const struct bmi160_dev* dev); + +/*! + * @brief This API validates the self test results of gyro + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t validate_gyro_self_test(const struct bmi160_dev* dev); + +/*! + * @brief This API sets FIFO full interrupt of the sensor.This interrupt + * occurs when the FIFO is full and the next full data sample would cause + * a FIFO overflow, which may delete the old samples. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + set_fifo_full_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This enable the FIFO full interrupt engine. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + enable_fifo_full_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets FIFO watermark interrupt of the sensor.The FIFO + * watermark interrupt is fired, when the FIFO fill level is above a fifo + * watermark. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + set_fifo_watermark_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This enable the FIFO watermark interrupt engine. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + enable_fifo_wtm_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to reset the FIFO related configurations + * in the fifo_frame structure. + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void reset_fifo_data_structure(const struct bmi160_dev* dev); + +/*! + * @brief This API is used to read number of bytes filled + * currently in FIFO buffer. + * + * @param[in] bytes_to_read : Number of bytes available in FIFO at the + * instant which is obtained from FIFO counter. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + * @retval Any non zero value -> Fail + * + */ +static int8_t get_fifo_byte_counter(uint16_t* bytes_to_read, struct bmi160_dev const* dev); + +/*! + * @brief This API is used to compute the number of bytes of accel FIFO data + * which is to be parsed in header-less mode + * + * @param[out] data_index : The start index for parsing data + * @param[out] data_read_length : Number of bytes to be parsed + * @param[in] acc_frame_count : Number of accelerometer frames to be read + * @param[in] dev : Structure instance of bmi160_dev. + * + */ +static void get_accel_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* acc_frame_count, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed. + * + * @param[in,out] acc : structure instance of sensor data + * @param[in,out] idx : Index value of number of bytes parsed + * @param[in,out] acc_idx : Index value of accelerometer data + * (x,y,z axes) frames parsed + * @param[in] frame_info : It consists of either fifo_data_enable + * parameter in header-less mode or + * frame header data in header mode + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_accel_frame( + struct bmi160_sensor_data* acc, + uint16_t* idx, + uint8_t* acc_idx, + uint8_t frame_info, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data and store it in the instance of the structure bmi160_sensor_data. + * + * @param[in,out] accel_data : structure instance of sensor data + * @param[in,out] data_start_index : Index value of number of bytes parsed + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_accel_data( + struct bmi160_sensor_data* accel_data, + uint16_t data_start_index, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data in header mode. + * + * @param[in,out] accel_data : Structure instance of sensor data + * @param[in,out] accel_length : Number of accelerometer frames + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void extract_accel_header_mode( + struct bmi160_sensor_data* accel_data, + uint8_t* accel_length, + const struct bmi160_dev* dev); + +/*! + * @brief This API computes the number of bytes of gyro FIFO data + * which is to be parsed in header-less mode + * + * @param[out] data_index : The start index for parsing data + * @param[out] data_read_length : No of bytes to be parsed from FIFO buffer + * @param[in] gyro_frame_count : Number of Gyro data frames to be read + * @param[in] dev : Structure instance of bmi160_dev. + */ +static void get_gyro_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* gyro_frame_count, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the gyroscope's data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed. + * + * @param[in,out] gyro : structure instance of sensor data + * @param[in,out] idx : Index value of number of bytes parsed + * @param[in,out] gyro_idx : Index value of gyro data + * (x,y,z axes) frames parsed + * @param[in] frame_info : It consists of either fifo_data_enable + * parameter in header-less mode or + * frame header data in header mode + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_gyro_frame( + struct bmi160_sensor_data* gyro, + uint16_t* idx, + uint8_t* gyro_idx, + uint8_t frame_info, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the gyro data from the + * FIFO data and store it in the instance of the structure bmi160_sensor_data. + * + * @param[in,out] gyro_data : structure instance of sensor data + * @param[in,out] data_start_index : Index value of number of bytes parsed + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_gyro_data( + struct bmi160_sensor_data* gyro_data, + uint16_t data_start_index, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the gyro data from the + * FIFO data in header mode. + * + * @param[in,out] gyro_data : Structure instance of sensor data + * @param[in,out] gyro_length : Number of gyro frames + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void extract_gyro_header_mode( + struct bmi160_sensor_data* gyro_data, + uint8_t* gyro_length, + const struct bmi160_dev* dev); + +/*! + * @brief This API computes the number of bytes of aux FIFO data + * which is to be parsed in header-less mode + * + * @param[out] data_index : The start index for parsing data + * @param[out] data_read_length : No of bytes to be parsed from FIFO buffer + * @param[in] aux_frame_count : Number of Aux data frames to be read + * @param[in] dev : Structure instance of bmi160_dev. + */ +static void get_aux_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* aux_frame_count, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the aux's data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed + * + * @param[in,out] aux_data : structure instance of sensor data + * @param[in,out] idx : Index value of number of bytes parsed + * @param[in,out] aux_index : Index value of gyro data + * (x,y,z axes) frames parsed + * @param[in] frame_info : It consists of either fifo_data_enable + * parameter in header-less mode or + * frame header data in header mode + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_aux_frame( + struct bmi160_aux_data* aux_data, + uint16_t* idx, + uint8_t* aux_index, + uint8_t frame_info, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the aux data from the + * FIFO data and store it in the instance of the structure bmi160_aux_data. + * + * @param[in,out] aux_data : structure instance of sensor data + * @param[in,out] data_start_index : Index value of number of bytes parsed + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_aux_data( + struct bmi160_aux_data* aux_data, + uint16_t data_start_index, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the aux data from the + * FIFO data in header mode. + * + * @param[in,out] aux_data : Structure instance of sensor data + * @param[in,out] aux_length : Number of aux frames + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void extract_aux_header_mode( + struct bmi160_aux_data* aux_data, + uint8_t* aux_length, + const struct bmi160_dev* dev); + +/*! + * @brief This API checks the presence of non-valid frames in the read fifo data. + * + * @param[in,out] data_index : The index of the current data to + * be parsed from fifo data + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void check_frame_validity(uint16_t* data_index, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to move the data index ahead of the + * current_frame_length parameter when unnecessary FIFO data appears while + * extracting the user specified data. + * + * @param[in,out] data_index : Index of the FIFO data which + * is to be moved ahead of the + * current_frame_length + * @param[in] current_frame_length : Number of bytes in a particular frame + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void move_next_frame( + uint16_t* data_index, + uint8_t current_frame_length, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse and store the sensor time from the + * FIFO data in the structure instance dev. + * + * @param[in,out] data_index : Index of the FIFO data which + * has the sensor time. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_sensortime_frame(uint16_t* data_index, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse and store the skipped_frame_count from + * the FIFO data in the structure instance dev. + * + * @param[in,out] data_index : Index of the FIFO data which + * has the skipped frame count. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_skipped_frame(uint16_t* data_index, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to get the FOC status from the sensor + * + * @param[in,out] foc_status : Result of FOC status. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t get_foc_status(uint8_t* foc_status, struct bmi160_dev const* dev); + +/*! + * @brief This API is used to configure the offset enable bits in the sensor + * + * @param[in,out] foc_conf : Structure instance of bmi160_foc_conf which + * has the FOC and offset configurations + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + configure_offset_enable(const struct bmi160_foc_conf* foc_conf, struct bmi160_dev const* dev); + +/*! + * @brief This API is used to trigger the FOC in the sensor + * + * @param[in,out] offset : Structure instance of bmi160_offsets which + * reads and stores the offset values after FOC + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t trigger_foc(struct bmi160_offsets* offset, struct bmi160_dev const* dev); + +/*! + * @brief This API is used to map/unmap the Dataready(Accel & Gyro), FIFO full + * and FIFO watermark interrupt + * + * @param[in] int_config : Structure instance of bmi160_int_settg which + * stores the interrupt type and interrupt channel + * configurations to map/unmap the interrupt pins + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + map_hardware_interrupt(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to map/unmap the Any/Sig motion, Step det/Low-g, + * Double tap, Single tap, Orientation, Flat, High-G, Nomotion interrupt pins. + * + * @param[in] int_config : Structure instance of bmi160_int_settg which + * stores the interrupt type and interrupt channel + * configurations to map/unmap the interrupt pins + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + map_feature_interrupt(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*********************** User function definitions ****************************/ + +/*! + * @brief This API reads the data from the given register address + * of sensor. + */ +int8_t + bmi160_get_regs(uint8_t reg_addr, uint8_t* data, uint16_t len, const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + /* Null-pointer check */ + if((dev == NULL) || (dev->read == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else if(len == 0) { + rslt = BMI160_E_READ_WRITE_LENGTH_INVALID; + } else { + /* Configuring reg_addr for SPI Interface */ + if(dev->intf == BMI160_SPI_INTF) { + reg_addr = (reg_addr | BMI160_SPI_RD_MASK); + } + + rslt = dev->read(dev->id, reg_addr, data, len); + } + + return rslt; +} + +/*! + * @brief This API writes the given data to the register address + * of sensor. + */ +int8_t + bmi160_set_regs(uint8_t reg_addr, uint8_t* data, uint16_t len, const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint8_t count = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->write == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else if(len == 0) { + rslt = BMI160_E_READ_WRITE_LENGTH_INVALID; + } else { + /* Configuring reg_addr for SPI Interface */ + if(dev->intf == BMI160_SPI_INTF) { + reg_addr = (reg_addr & BMI160_SPI_WR_MASK); + } + + if((dev->prev_accel_cfg.power == BMI160_ACCEL_NORMAL_MODE) || + (dev->prev_gyro_cfg.power == BMI160_GYRO_NORMAL_MODE)) { + rslt = dev->write(dev->id, reg_addr, data, len); + + /* Kindly refer bmi160 data sheet section 3.2.4 */ + dev->delay_ms(1); + + } else { + /*Burst write is not allowed in + * suspend & low power mode */ + for(; count < len; count++) { + rslt = dev->write(dev->id, reg_addr, &data[count], 1); + reg_addr++; + + /* Kindly refer bmi160 data sheet section 3.2.4 */ + dev->delay_ms(1); + } + } + + if(rslt != BMI160_OK) { + rslt = BMI160_E_COM_FAIL; + } + } + + return rslt; +} + +/*! + * @brief This API is the entry point for sensor.It performs + * the selection of I2C/SPI read mechanism according to the + * selected interface and reads the chip-id of bmi160 sensor. + */ +int8_t bmi160_init(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data; + uint8_t try = 3; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + + /* Dummy read of 0x7F register to enable SPI Interface + * if SPI is used */ + if((rslt == BMI160_OK) && (dev->intf == BMI160_SPI_INTF)) { + rslt = bmi160_get_regs(BMI160_SPI_COMM_TEST_ADDR, &data, 1, dev); + } + + if(rslt == BMI160_OK) { + /* Assign chip id as zero */ + dev->chip_id = 0; + + while((try--) && (dev->chip_id != BMI160_CHIP_ID)) { + /* Read chip_id */ + rslt = bmi160_get_regs(BMI160_CHIP_ID_ADDR, &dev->chip_id, 1, dev); + } + + if((rslt == BMI160_OK) && (dev->chip_id == BMI160_CHIP_ID)) { + dev->any_sig_sel = BMI160_BOTH_ANY_SIG_MOTION_DISABLED; + + /* Soft reset */ + rslt = bmi160_soft_reset(dev); + } else { + rslt = BMI160_E_DEV_NOT_FOUND; + } + } + + return rslt; +} + +/*! + * @brief This API resets and restarts the device. + * All register values are overwritten with default parameters. + */ +int8_t bmi160_soft_reset(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = BMI160_SOFT_RESET_CMD; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Reset the device */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &data, 1, dev); + dev->delay_ms(BMI160_SOFT_RESET_DELAY_MS); + if((rslt == BMI160_OK) && (dev->intf == BMI160_SPI_INTF)) { + /* Dummy read of 0x7F register to enable SPI Interface + * if SPI is used */ + rslt = bmi160_get_regs(BMI160_SPI_COMM_TEST_ADDR, &data, 1, dev); + } + + if(rslt == BMI160_OK) { + /* Update the default parameters */ + default_param_settg(dev); + } + } + + return rslt; +} + +/*! + * @brief This API configures the power mode, range and bandwidth + * of sensor. + */ +int8_t bmi160_set_sens_conf(struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = set_accel_conf(dev); + if(rslt == BMI160_OK) { + rslt = set_gyro_conf(dev); + if(rslt == BMI160_OK) { + /* write power mode for accel and gyro */ + rslt = bmi160_set_power_mode(dev); + if(rslt == BMI160_OK) { + rslt = check_invalid_settg(dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API gets accel and gyro configurations. + */ +int8_t bmi160_get_sens_conf(struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = get_accel_conf(dev); + if(rslt == BMI160_OK) { + rslt = get_gyro_conf(dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the power mode of the sensor. + */ +int8_t bmi160_set_power_mode(struct bmi160_dev* dev) { + int8_t rslt = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = set_accel_pwr(dev); + if(rslt == BMI160_OK) { + rslt = set_gyro_pwr(dev); + } + } + + return rslt; +} + +/*! + * @brief This API gets the power mode of the sensor. + */ +int8_t bmi160_get_power_mode(struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t power_mode = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(BMI160_PMU_STATUS_ADDR, &power_mode, 1, dev); + if(rslt == BMI160_OK) { + /* Power mode of the accel, gyro sensor is obtained */ + dev->gyro_cfg.power = BMI160_GET_BITS(power_mode, BMI160_GYRO_POWER_MODE); + dev->accel_cfg.power = BMI160_GET_BITS(power_mode, BMI160_ACCEL_POWER_MODE); + } + } + + return rslt; +} + +/*! + * @brief This API reads sensor data, stores it in + * the bmi160_sensor_data structure pointer passed by the user. + */ +int8_t bmi160_get_sensor_data( + uint8_t select_sensor, + struct bmi160_sensor_data* accel, + struct bmi160_sensor_data* gyro, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint8_t time_sel; + uint8_t sen_sel; + uint8_t len = 0; + + /*Extract the sensor and time select information*/ + sen_sel = select_sensor & BMI160_SEN_SEL_MASK; + time_sel = ((sen_sel & BMI160_TIME_SEL) >> 2); + sen_sel = sen_sel & (BMI160_ACCEL_SEL | BMI160_GYRO_SEL); + if(time_sel == 1) { + len = 3; + } + + /* Null-pointer check */ + if(dev != NULL) { + switch(sen_sel) { + case BMI160_ACCEL_ONLY: + + /* Null-pointer check */ + if(accel == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = get_accel_data(len, accel, dev); + } + + break; + case BMI160_GYRO_ONLY: + + /* Null-pointer check */ + if(gyro == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = get_gyro_data(len, gyro, dev); + } + + break; + case BMI160_BOTH_ACCEL_AND_GYRO: + + /* Null-pointer check */ + if((gyro == NULL) || (accel == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = get_accel_gyro_data(len, accel, gyro, dev); + } + + break; + default: + rslt = BMI160_E_INVALID_INPUT; + break; + } + } else { + rslt = BMI160_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API configures the necessary interrupt based on + * the user settings in the bmi160_int_settg structure instance. + */ +int8_t bmi160_set_int_config(struct bmi160_int_settg* int_config, struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + switch(int_config->int_type) { + case BMI160_ACC_ANY_MOTION_INT: + + /*Any-motion interrupt*/ + rslt = set_accel_any_motion_int(int_config, dev); + break; + case BMI160_ACC_SIG_MOTION_INT: + + /* Significant motion interrupt */ + rslt = set_accel_sig_motion_int(int_config, dev); + break; + case BMI160_ACC_SLOW_NO_MOTION_INT: + + /* Slow or no motion interrupt */ + rslt = set_accel_no_motion_int(int_config, dev); + break; + case BMI160_ACC_DOUBLE_TAP_INT: + case BMI160_ACC_SINGLE_TAP_INT: + + /* Double tap and single tap Interrupt */ + rslt = set_accel_tap_int(int_config, dev); + break; + case BMI160_STEP_DETECT_INT: + + /* Step detector interrupt */ + rslt = set_accel_step_detect_int(int_config, dev); + break; + case BMI160_ACC_ORIENT_INT: + + /* Orientation interrupt */ + rslt = set_accel_orientation_int(int_config, dev); + break; + case BMI160_ACC_FLAT_INT: + + /* Flat detection interrupt */ + rslt = set_accel_flat_detect_int(int_config, dev); + break; + case BMI160_ACC_LOW_G_INT: + + /* Low-g interrupt */ + rslt = set_accel_low_g_int(int_config, dev); + break; + case BMI160_ACC_HIGH_G_INT: + + /* High-g interrupt */ + rslt = set_accel_high_g_int(int_config, dev); + break; + case BMI160_ACC_GYRO_DATA_RDY_INT: + + /* Data ready interrupt */ + rslt = set_accel_gyro_data_ready_int(int_config, dev); + break; + case BMI160_ACC_GYRO_FIFO_FULL_INT: + + /* Fifo full interrupt */ + rslt = set_fifo_full_int(int_config, dev); + break; + case BMI160_ACC_GYRO_FIFO_WATERMARK_INT: + + /* Fifo water-mark interrupt */ + rslt = set_fifo_watermark_int(int_config, dev); + break; + case BMI160_FIFO_TAG_INT_PIN: + + /* Fifo tagging feature support */ + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + break; + default: + break; + } + + return rslt; +} + +/*! + * @brief This API enables or disable the step counter feature. + * 1 - enable step counter (0 - disable) + */ +int8_t bmi160_set_step_counter(uint8_t step_cnt_enable, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(BMI160_INT_STEP_CONFIG_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(step_cnt_enable == BMI160_ENABLE) { + data |= (uint8_t)(step_cnt_enable << 3); + } else { + data &= ~BMI160_STEP_COUNT_EN_BIT_MASK; + } + + rslt = bmi160_set_regs(BMI160_INT_STEP_CONFIG_1_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API reads the step counter value. + */ +int8_t bmi160_read_step_counter(uint16_t* step_val, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0, 0}; + uint16_t msb = 0; + uint8_t lsb = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(BMI160_INT_STEP_CNT_0_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + lsb = data[0]; + msb = data[1] << 8; + *step_val = msb | lsb; + } + } + + return rslt; +} + +/*! + * @brief This API reads the mention no of byte of data from the given + * register address of auxiliary sensor. + */ +int8_t bmi160_aux_read( + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint16_t map_len = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->read == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + if(dev->aux_cfg.aux_sensor_enable == BMI160_ENABLE) { + rslt = map_read_len(&map_len, dev); + if(rslt == BMI160_OK) { + rslt = extract_aux_read(map_len, reg_addr, aux_data, len, dev); + } + } else { + rslt = BMI160_E_INVALID_INPUT; + } + } + + return rslt; +} + +/*! + * @brief This API writes the mention no of byte of data to the given + * register address of auxiliary sensor. + */ +int8_t bmi160_aux_write( + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint8_t count = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->write == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + for(; count < len; count++) { + /* set data to write */ + rslt = bmi160_set_regs(BMI160_AUX_IF_4_ADDR, aux_data, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + if(rslt == BMI160_OK) { + /* set address to write */ + rslt = bmi160_set_regs(BMI160_AUX_IF_3_ADDR, ®_addr, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + if(rslt == BMI160_OK && (count < len - 1)) { + aux_data++; + reg_addr++; + } + } + } + } + + return rslt; +} + +/*! + * @brief This API initialize the auxiliary sensor + * in order to access it. + */ +int8_t bmi160_aux_init(const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + if(dev->aux_cfg.aux_sensor_enable == BMI160_ENABLE) { + /* Configures the auxiliary sensor interface settings */ + rslt = config_aux_settg(dev); + } else { + rslt = BMI160_E_INVALID_INPUT; + } + } + + return rslt; +} + +/*! + * @brief This API is used to setup the auxiliary sensor of bmi160 in auto mode + * Thus enabling the auto update of 8 bytes of data from auxiliary sensor + * to BMI160 register address 0x04 to 0x0B + */ +int8_t bmi160_set_aux_auto_mode(uint8_t* data_addr, struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + if(dev->aux_cfg.aux_sensor_enable == BMI160_ENABLE) { + /* Write the aux. address to read in 0x4D of BMI160*/ + rslt = bmi160_set_regs(BMI160_AUX_IF_2_ADDR, data_addr, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + if(rslt == BMI160_OK) { + /* Configure the polling ODR for + * auxiliary sensor */ + rslt = config_aux_odr(dev); + if(rslt == BMI160_OK) { + /* Disable the aux. manual mode, i.e aux. + * sensor is in auto-mode (data-mode) */ + dev->aux_cfg.manual_enable = BMI160_DISABLE; + rslt = bmi160_config_aux_mode(dev); + + /* Auxiliary sensor data is obtained + * in auto mode from this point */ + } + } + } else { + rslt = BMI160_E_INVALID_INPUT; + } + } + + return rslt; +} + +/*! + * @brief This API configures the 0x4C register and settings like + * Auxiliary sensor manual enable/ disable and aux burst read length. + */ +int8_t bmi160_config_aux_mode(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t aux_if[2] = {(uint8_t)(dev->aux_cfg.aux_i2c_addr * 2), 0}; + + rslt = bmi160_get_regs(BMI160_AUX_IF_1_ADDR, &aux_if[1], 1, dev); + if(rslt == BMI160_OK) { + /* update the Auxiliary interface to manual/auto mode */ + aux_if[1] = BMI160_SET_BITS(aux_if[1], BMI160_MANUAL_MODE_EN, dev->aux_cfg.manual_enable); + + /* update the burst read length defined by user */ + aux_if[1] = + BMI160_SET_BITS_POS_0(aux_if[1], BMI160_AUX_READ_BURST, dev->aux_cfg.aux_rd_burst_len); + + /* Set the secondary interface address and manual mode + * along with burst read length */ + rslt = bmi160_set_regs(BMI160_AUX_IF_0_ADDR, &aux_if[0], 2, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + } + + return rslt; +} + +/*! + * @brief This API is used to read the raw uncompensated auxiliary sensor + * data of 8 bytes from BMI160 register address 0x04 to 0x0B + */ +int8_t bmi160_read_aux_data_auto_mode(uint8_t* aux_data, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + if((dev->aux_cfg.aux_sensor_enable == BMI160_ENABLE) && + (dev->aux_cfg.manual_enable == BMI160_DISABLE)) { + /* Read the aux. sensor's raw data */ + rslt = bmi160_get_regs(BMI160_AUX_DATA_ADDR, aux_data, 8, dev); + } else { + rslt = BMI160_E_INVALID_INPUT; + } + } + + return rslt; +} + +/*! + * @brief This is used to perform self test of accel/gyro of the BMI160 sensor + */ +int8_t bmi160_perform_self_test(uint8_t select_sensor, struct bmi160_dev* dev) { + int8_t rslt; + int8_t self_test_rslt = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Proceed if null check is fine */ + switch(select_sensor) { + case BMI160_ACCEL_ONLY: + rslt = perform_accel_self_test(dev); + break; + case BMI160_GYRO_ONLY: + + /* Set the power mode as normal mode */ + dev->gyro_cfg.power = BMI160_GYRO_NORMAL_MODE; + rslt = bmi160_set_power_mode(dev); + + /* Perform gyro self test */ + if(rslt == BMI160_OK) { + /* Perform gyro self test */ + rslt = perform_gyro_self_test(dev); + } + + break; + default: + rslt = BMI160_E_INVALID_INPUT; + break; + } + + /* Check to ensure bus error does not occur */ + if(rslt >= BMI160_OK) { + /* Store the status of self test result */ + self_test_rslt = rslt; + + /* Perform soft reset */ + rslt = bmi160_soft_reset(dev); + } + + /* Check to ensure bus operations are success */ + if(rslt == BMI160_OK) { + /* Restore self_test_rslt as return value */ + rslt = self_test_rslt; + } + } + + return rslt; +} + +/*! + * @brief This API reads the data from fifo buffer. + */ +int8_t bmi160_get_fifo_data(struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint16_t bytes_to_read = 0; + uint16_t user_fifo_len = 0; + + /* check the bmi160 structure as NULL*/ + if((dev == NULL) || (dev->fifo->data == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + reset_fifo_data_structure(dev); + + /* get current FIFO fill-level*/ + rslt = get_fifo_byte_counter(&bytes_to_read, dev); + if(rslt == BMI160_OK) { + user_fifo_len = dev->fifo->length; + if((dev->fifo->length > bytes_to_read)) { + /* Handling the case where user requests + * more data than available in FIFO */ + dev->fifo->length = bytes_to_read; + } + + if((dev->fifo->fifo_time_enable == BMI160_FIFO_TIME_ENABLE) && + (bytes_to_read + BMI160_FIFO_BYTES_OVERREAD <= user_fifo_len)) { + /* Handling case of sensor time availability*/ + dev->fifo->length = dev->fifo->length + BMI160_FIFO_BYTES_OVERREAD; + } + + /* read only the filled bytes in the FIFO Buffer */ + rslt = bmi160_get_regs(BMI160_FIFO_DATA_ADDR, dev->fifo->data, dev->fifo->length, dev); + } + } + + return rslt; +} + +/*! + * @brief This API writes fifo_flush command to command register.This + * action clears all data in the Fifo without changing fifo configuration + * settings + */ +int8_t bmi160_set_fifo_flush(const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t data = BMI160_FIFO_FLUSH_VALUE; + uint8_t reg_addr = BMI160_COMMAND_REG_ADDR; + + /* Check the bmi160_dev structure for NULL address*/ + if(dev == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_set_regs(reg_addr, &data, BMI160_ONE, dev); + } + + return rslt; +} + +/*! + * @brief This API sets the FIFO configuration in the sensor. + */ +int8_t bmi160_set_fifo_config(uint8_t config, uint8_t enable, struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint8_t data = 0; + uint8_t reg_addr = BMI160_FIFO_CONFIG_1_ADDR; + uint8_t fifo_config = config & BMI160_FIFO_CONFIG_1_MASK; + + /* Check the bmi160_dev structure for NULL address*/ + if(dev == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(reg_addr, &data, BMI160_ONE, dev); + if(rslt == BMI160_OK) { + if(fifo_config > 0) { + if(enable == BMI160_ENABLE) { + data = data | fifo_config; + } else { + data = data & (~fifo_config); + } + } + + /* write fifo frame content configuration*/ + rslt = bmi160_set_regs(reg_addr, &data, BMI160_ONE, dev); + if(rslt == BMI160_OK) { + /* read fifo frame content configuration*/ + rslt = bmi160_get_regs(reg_addr, &data, BMI160_ONE, dev); + if(rslt == BMI160_OK) { + /* extract fifo header enabled status */ + dev->fifo->fifo_header_enable = data & BMI160_FIFO_HEAD_ENABLE; + + /* extract accel/gyr/aux. data enabled status */ + dev->fifo->fifo_data_enable = data & BMI160_FIFO_M_G_A_ENABLE; + + /* extract fifo sensor time enabled status */ + dev->fifo->fifo_time_enable = data & BMI160_FIFO_TIME_ENABLE; + } + } + } + } + + return rslt; +} + +/*! @brief This API is used to configure the down sampling ratios of + * the accel and gyro data for FIFO.Also, it configures filtered or + * pre-filtered data for accel and gyro. + * + */ +int8_t bmi160_set_fifo_down(uint8_t fifo_down, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t data = 0; + uint8_t reg_addr = BMI160_FIFO_DOWN_ADDR; + + /* Check the bmi160_dev structure for NULL address*/ + if(dev == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(reg_addr, &data, BMI160_ONE, dev); + if(rslt == BMI160_OK) { + data = data | fifo_down; + rslt = bmi160_set_regs(reg_addr, &data, BMI160_ONE, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the FIFO watermark level in the sensor. + * + */ +int8_t bmi160_set_fifo_wm(uint8_t fifo_wm, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t data = fifo_wm; + uint8_t reg_addr = BMI160_FIFO_CONFIG_0_ADDR; + + /* Check the bmi160_dev structure for NULL address*/ + if(dev == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_set_regs(reg_addr, &data, BMI160_ONE, dev); + } + + return rslt; +} + +/*! + * @brief This API parses and extracts the accelerometer frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "accel_data" structure instance. + */ +int8_t bmi160_extract_accel( + struct bmi160_sensor_data* accel_data, + uint8_t* accel_length, + struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint16_t data_index = 0; + uint16_t data_read_length = 0; + uint8_t accel_index = 0; + uint8_t fifo_data_enable = 0; + + if(dev == NULL || dev->fifo == NULL || dev->fifo->data == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Parsing the FIFO data in header-less mode */ + if(dev->fifo->fifo_header_enable == 0) { + /* Number of bytes to be parsed from FIFO */ + get_accel_len_to_parse(&data_index, &data_read_length, accel_length, dev); + for(; data_index < data_read_length;) { + /*Check for the availability of next two bytes of FIFO data */ + check_frame_validity(&data_index, dev); + fifo_data_enable = dev->fifo->fifo_data_enable; + unpack_accel_frame(accel_data, &data_index, &accel_index, fifo_data_enable, dev); + } + + /* update number of accel data read*/ + *accel_length = accel_index; + + /*update the accel byte index*/ + dev->fifo->accel_byte_start_idx = data_index; + } else { + /* Parsing the FIFO data in header mode */ + extract_accel_header_mode(accel_data, accel_length, dev); + } + } + + return rslt; +} + +/*! + * @brief This API parses and extracts the gyro frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "gyro_data" structure instance. + */ +int8_t bmi160_extract_gyro( + struct bmi160_sensor_data* gyro_data, + uint8_t* gyro_length, + struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint16_t data_index = 0; + uint16_t data_read_length = 0; + uint8_t gyro_index = 0; + uint8_t fifo_data_enable = 0; + + if(dev == NULL || dev->fifo->data == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Parsing the FIFO data in header-less mode */ + if(dev->fifo->fifo_header_enable == 0) { + /* Number of bytes to be parsed from FIFO */ + get_gyro_len_to_parse(&data_index, &data_read_length, gyro_length, dev); + for(; data_index < data_read_length;) { + /*Check for the availability of next two bytes of FIFO data */ + check_frame_validity(&data_index, dev); + fifo_data_enable = dev->fifo->fifo_data_enable; + unpack_gyro_frame(gyro_data, &data_index, &gyro_index, fifo_data_enable, dev); + } + + /* update number of gyro data read */ + *gyro_length = gyro_index; + + /* update the gyro byte index */ + dev->fifo->gyro_byte_start_idx = data_index; + } else { + /* Parsing the FIFO data in header mode */ + extract_gyro_header_mode(gyro_data, gyro_length, dev); + } + } + + return rslt; +} + +/*! + * @brief This API parses and extracts the aux frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "aux_data" structure instance. + */ +int8_t bmi160_extract_aux( + struct bmi160_aux_data* aux_data, + uint8_t* aux_len, + struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint16_t data_index = 0; + uint16_t data_read_length = 0; + uint8_t aux_index = 0; + uint8_t fifo_data_enable = 0; + + if((dev == NULL) || (dev->fifo->data == NULL) || (aux_data == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Parsing the FIFO data in header-less mode */ + if(dev->fifo->fifo_header_enable == 0) { + /* Number of bytes to be parsed from FIFO */ + get_aux_len_to_parse(&data_index, &data_read_length, aux_len, dev); + for(; data_index < data_read_length;) { + /* Check for the availability of next two + * bytes of FIFO data */ + check_frame_validity(&data_index, dev); + fifo_data_enable = dev->fifo->fifo_data_enable; + unpack_aux_frame(aux_data, &data_index, &aux_index, fifo_data_enable, dev); + } + + /* update number of aux data read */ + *aux_len = aux_index; + + /* update the aux byte index */ + dev->fifo->aux_byte_start_idx = data_index; + } else { + /* Parsing the FIFO data in header mode */ + extract_aux_header_mode(aux_data, aux_len, dev); + } + } + + return rslt; +} + +/*! + * @brief This API starts the FOC of accel and gyro + * + * @note FOC should not be used in low-power mode of sensor + * + * @note Accel FOC targets values of +1g , 0g , -1g + * Gyro FOC always targets value of 0 dps + */ +int8_t bmi160_start_foc( + const struct bmi160_foc_conf* foc_conf, + struct bmi160_offsets* offset, + struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Set the offset enable bits */ + rslt = configure_offset_enable(foc_conf, dev); + if(rslt == BMI160_OK) { + /* Read the FOC config from the sensor */ + rslt = bmi160_get_regs(BMI160_FOC_CONF_ADDR, &data, 1, dev); + + /* Set the FOC config for gyro */ + data = BMI160_SET_BITS(data, BMI160_GYRO_FOC_EN, foc_conf->foc_gyr_en); + + /* Set the FOC config for accel xyz axes */ + data = BMI160_SET_BITS(data, BMI160_ACCEL_FOC_X_CONF, foc_conf->foc_acc_x); + data = BMI160_SET_BITS(data, BMI160_ACCEL_FOC_Y_CONF, foc_conf->foc_acc_y); + data = BMI160_SET_BITS_POS_0(data, BMI160_ACCEL_FOC_Z_CONF, foc_conf->foc_acc_z); + if(rslt == BMI160_OK) { + /* Set the FOC config in the sensor */ + rslt = bmi160_set_regs(BMI160_FOC_CONF_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Procedure to trigger + * FOC and check status */ + rslt = trigger_foc(offset, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API reads and stores the offset values of accel and gyro + */ +int8_t bmi160_get_offsets(struct bmi160_offsets* offset, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[7]; + uint8_t lsb, msb; + int16_t offset_msb, offset_lsb; + int16_t offset_data; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Read the FOC config from the sensor */ + rslt = bmi160_get_regs(BMI160_OFFSET_ADDR, data, 7, dev); + + /* Accel offsets */ + offset->off_acc_x = (int8_t)data[0]; + offset->off_acc_y = (int8_t)data[1]; + offset->off_acc_z = (int8_t)data[2]; + + /* Gyro x-axis offset */ + lsb = data[3]; + msb = BMI160_GET_BITS_POS_0(data[6], BMI160_GYRO_OFFSET_X); + offset_msb = (int16_t)(msb << 14); + offset_lsb = lsb << 6; + offset_data = offset_msb | offset_lsb; + + /* Divide by 64 to get the Right shift by 6 value */ + offset->off_gyro_x = (int16_t)(offset_data / 64); + + /* Gyro y-axis offset */ + lsb = data[4]; + msb = BMI160_GET_BITS(data[6], BMI160_GYRO_OFFSET_Y); + offset_msb = (int16_t)(msb << 14); + offset_lsb = lsb << 6; + offset_data = offset_msb | offset_lsb; + + /* Divide by 64 to get the Right shift by 6 value */ + offset->off_gyro_y = (int16_t)(offset_data / 64); + + /* Gyro z-axis offset */ + lsb = data[5]; + msb = BMI160_GET_BITS(data[6], BMI160_GYRO_OFFSET_Z); + offset_msb = (int16_t)(msb << 14); + offset_lsb = lsb << 6; + offset_data = offset_msb | offset_lsb; + + /* Divide by 64 to get the Right shift by 6 value */ + offset->off_gyro_z = (int16_t)(offset_data / 64); + } + + return rslt; +} + +/*! + * @brief This API writes the offset values of accel and gyro to + * the sensor but these values will be reset on POR or soft reset. + */ +int8_t bmi160_set_offsets( + const struct bmi160_foc_conf* foc_conf, + const struct bmi160_offsets* offset, + struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data[7]; + uint8_t x_msb, y_msb, z_msb; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Update the accel offset */ + data[0] = (uint8_t)offset->off_acc_x; + data[1] = (uint8_t)offset->off_acc_y; + data[2] = (uint8_t)offset->off_acc_z; + + /* Update the LSB of gyro offset */ + data[3] = BMI160_GET_LSB(offset->off_gyro_x); + data[4] = BMI160_GET_LSB(offset->off_gyro_y); + data[5] = BMI160_GET_LSB(offset->off_gyro_z); + + /* Update the MSB of gyro offset */ + x_msb = BMI160_GET_BITS(offset->off_gyro_x, BMI160_GYRO_OFFSET); + y_msb = BMI160_GET_BITS(offset->off_gyro_y, BMI160_GYRO_OFFSET); + z_msb = BMI160_GET_BITS(offset->off_gyro_z, BMI160_GYRO_OFFSET); + data[6] = (uint8_t)(z_msb << 4 | y_msb << 2 | x_msb); + + /* Set the offset enable/disable for gyro and accel */ + data[6] = BMI160_SET_BITS(data[6], BMI160_GYRO_OFFSET_EN, foc_conf->gyro_off_en); + data[6] = BMI160_SET_BITS(data[6], BMI160_ACCEL_OFFSET_EN, foc_conf->acc_off_en); + + /* Set the offset config and values in the sensor */ + rslt = bmi160_set_regs(BMI160_OFFSET_ADDR, data, 7, dev); + } + + return rslt; +} + +/*! + * @brief This API writes the image registers values to NVM which is + * stored even after POR or soft reset + */ +int8_t bmi160_update_nvm(struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data; + uint8_t cmd = BMI160_NVM_BACKUP_EN; + + /* Read the nvm_prog_en configuration */ + rslt = bmi160_get_regs(BMI160_CONF_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + data = BMI160_SET_BITS(data, BMI160_NVM_UPDATE, 1); + + /* Set the nvm_prog_en bit in the sensor */ + rslt = bmi160_set_regs(BMI160_CONF_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Update NVM */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &cmd, 1, dev); + if(rslt == BMI160_OK) { + /* Check for NVM ready status */ + rslt = bmi160_get_regs(BMI160_STATUS_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + data = BMI160_GET_BITS(data, BMI160_NVM_STATUS); + if(data != BMI160_ENABLE) { + /* Delay to update NVM */ + dev->delay_ms(25); + } + } + } + } + } + + return rslt; +} + +/*! + * @brief This API gets the interrupt status from the sensor. + */ +int8_t bmi160_get_int_status( + enum bmi160_int_status_sel int_status_sel, + union bmi160_int_status* int_status, + struct bmi160_dev const* dev) { + int8_t rslt = 0; + + /* To get the status of all interrupts */ + if(int_status_sel == BMI160_INT_STATUS_ALL) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR, &int_status->data[0], 4, dev); + } else { + if(int_status_sel & BMI160_INT_STATUS_0) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR, &int_status->data[0], 1, dev); + } + + if(int_status_sel & BMI160_INT_STATUS_1) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR + 1, &int_status->data[1], 1, dev); + } + + if(int_status_sel & BMI160_INT_STATUS_2) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR + 2, &int_status->data[2], 1, dev); + } + + if(int_status_sel & BMI160_INT_STATUS_3) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR + 3, &int_status->data[3], 1, dev); + } + } + + return rslt; +} + +/*********************** Local function definitions ***************************/ + +/*! + * @brief This API sets the any-motion interrupt of the sensor. + * This interrupt occurs when accel values exceeds preset threshold + * for a certain period of time. + */ +static int8_t + set_accel_any_motion_int(struct bmi160_int_settg* int_config, struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg = + &(int_config->int_type_cfg.acc_any_motion_int); + rslt = enable_accel_any_motion_int(any_motion_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_any_motion_int_settg(int_config, any_motion_int_cfg, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets tap interrupts.Interrupt is fired when + * tap movements happen. + */ +static int8_t + set_accel_tap_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_tap_int_cfg* tap_int_cfg = &(int_config->int_type_cfg.acc_tap_int); + rslt = enable_tap_int(int_config, tap_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_tap_int_settg(int_config, tap_int_cfg, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the data ready interrupt for both accel and gyro. + * This interrupt occurs when new accel and gyro data comes. + */ +static int8_t set_accel_gyro_data_ready_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = enable_data_ready_int(dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_hardware_interrupt(int_config, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the significant motion interrupt of the sensor.This + * interrupt occurs when there is change in user location. + */ +static int8_t + set_accel_sig_motion_int(struct bmi160_int_settg* int_config, struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg = + &(int_config->int_type_cfg.acc_sig_motion_int); + rslt = enable_sig_motion_int(sig_mot_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_sig_motion_int_settg(int_config, sig_mot_int_cfg, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the no motion/slow motion interrupt of the sensor. + * Slow motion is similar to any motion interrupt.No motion interrupt + * occurs when slope bet. two accel values falls below preset threshold + * for preset duration. + */ +static int8_t + set_accel_no_motion_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg = + &(int_config->int_type_cfg.acc_no_motion_int); + rslt = enable_no_motion_int(no_mot_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure the INT PIN settings*/ + rslt = config_no_motion_int_settg(int_config, no_mot_int_cfg, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the step detection interrupt.This interrupt + * occurs when the single step causes accel values to go above + * preset threshold. + */ +static int8_t + set_accel_step_detect_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg = + &(int_config->int_type_cfg.acc_step_detect_int); + rslt = enable_step_detect_int(step_detect_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_step_detect(step_detect_int_cfg, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the orientation interrupt of the sensor.This + * interrupt occurs when there is orientation change in the sensor + * with respect to gravitational field vector g. + */ +static int8_t + set_accel_orientation_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_orient_int_cfg* orient_int_cfg = + &(int_config->int_type_cfg.acc_orient_int); + rslt = enable_orient_int(orient_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + /* map INT pin to orient interrupt */ + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + /* configure the + * orientation setting*/ + rslt = config_orient_int_settg(orient_int_cfg, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the flat interrupt of the sensor.This interrupt + * occurs in case of flat orientation + */ +static int8_t + set_accel_flat_detect_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_flat_detect_int_cfg* flat_detect_int = + &(int_config->int_type_cfg.acc_flat_int); + + /* enable the flat interrupt */ + rslt = enable_flat_int(flat_detect_int, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + /* map INT pin to flat interrupt */ + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + /* configure the flat setting*/ + rslt = config_flat_int_settg(flat_detect_int, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the low-g interrupt of the sensor.This interrupt + * occurs during free-fall. + */ +static int8_t + set_accel_low_g_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_low_g_int_cfg* low_g_int = &(int_config->int_type_cfg.acc_low_g_int); + + /* Enable the low-g interrupt*/ + rslt = enable_low_g_int(low_g_int, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + /* Map INT pin to low-g interrupt */ + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + /* configure the data source + * for low-g interrupt*/ + rslt = config_low_g_data_src(low_g_int, dev); + if(rslt == BMI160_OK) { + rslt = config_low_g_int_settg(low_g_int, dev); + } + } + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the high-g interrupt of the sensor.The interrupt + * occurs if the absolute value of acceleration data of any enabled axis + * exceeds the programmed threshold and the sign of the value does not + * change for a preset duration. + */ +static int8_t + set_accel_high_g_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_high_g_int_cfg* high_g_int_cfg = + &(int_config->int_type_cfg.acc_high_g_int); + + /* Enable the high-g interrupt */ + rslt = enable_high_g_int(high_g_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + /* Map INT pin to high-g interrupt */ + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + /* configure the data source + * for high-g interrupt*/ + rslt = config_high_g_data_src(high_g_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_high_g_int_settg(high_g_int_cfg, dev); + } + } + } + } + } + + return rslt; +} + +/*! + * @brief This API configures the pins to fire the + * interrupt signal when it occurs. + */ +static int8_t + set_intr_pin_config(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* configure the behavioural settings of interrupt pin */ + rslt = config_int_out_ctrl(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_int_latch(int_config, dev); + } + + return rslt; +} + +/*! + * @brief This internal API is used to validate the device structure pointer for + * null conditions. + */ +static int8_t null_ptr_check(const struct bmi160_dev* dev) { + int8_t rslt; + + if((dev == NULL) || (dev->read == NULL) || (dev->write == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Device structure is fine */ + rslt = BMI160_OK; + } + + return rslt; +} + +/*! + * @brief This API sets the default configuration parameters of accel & gyro. + * Also maintain the previous state of configurations. + */ +static void default_param_settg(struct bmi160_dev* dev) { + /* Initializing accel and gyro params with + * default values */ + dev->accel_cfg.bw = BMI160_ACCEL_BW_NORMAL_AVG4; + dev->accel_cfg.odr = BMI160_ACCEL_ODR_100HZ; + dev->accel_cfg.power = BMI160_ACCEL_SUSPEND_MODE; + dev->accel_cfg.range = BMI160_ACCEL_RANGE_2G; + dev->gyro_cfg.bw = BMI160_GYRO_BW_NORMAL_MODE; + dev->gyro_cfg.odr = BMI160_GYRO_ODR_100HZ; + dev->gyro_cfg.power = BMI160_GYRO_SUSPEND_MODE; + dev->gyro_cfg.range = BMI160_GYRO_RANGE_2000_DPS; + + /* To maintain the previous state of accel configuration */ + dev->prev_accel_cfg = dev->accel_cfg; + + /* To maintain the previous state of gyro configuration */ + dev->prev_gyro_cfg = dev->gyro_cfg; +} + +/*! + * @brief This API set the accel configuration. + */ +static int8_t set_accel_conf(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0}; + + rslt = check_accel_config(data, dev); + if(rslt == BMI160_OK) { + /* Write output data rate and bandwidth */ + rslt = bmi160_set_regs(BMI160_ACCEL_CONFIG_ADDR, &data[0], 1, dev); + if(rslt == BMI160_OK) { + dev->prev_accel_cfg.odr = dev->accel_cfg.odr; + dev->prev_accel_cfg.bw = dev->accel_cfg.bw; + + /* write accel range */ + rslt = bmi160_set_regs(BMI160_ACCEL_RANGE_ADDR, &data[1], 1, dev); + if(rslt == BMI160_OK) { + dev->prev_accel_cfg.range = dev->accel_cfg.range; + } + } + } + + return rslt; +} + +/*! + * @brief This API gets the accel configuration. + */ +static int8_t get_accel_conf(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0}; + + /* Get accel configurations */ + rslt = bmi160_get_regs(BMI160_ACCEL_CONFIG_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + dev->accel_cfg.odr = (data[0] & BMI160_ACCEL_ODR_MASK); + dev->accel_cfg.bw = (data[0] & BMI160_ACCEL_BW_MASK) >> BMI160_ACCEL_BW_POS; + dev->accel_cfg.range = (data[1] & BMI160_ACCEL_RANGE_MASK); + } + + return rslt; +} + +/*! + * @brief This API check the accel configuration. + */ +static int8_t check_accel_config(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt; + + /* read accel Output data rate and bandwidth */ + rslt = bmi160_get_regs(BMI160_ACCEL_CONFIG_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + rslt = process_accel_odr(&data[0], dev); + if(rslt == BMI160_OK) { + rslt = process_accel_bw(&data[0], dev); + if(rslt == BMI160_OK) { + rslt = process_accel_range(&data[1], dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API process the accel odr. + */ +static int8_t process_accel_odr(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t odr = 0; + + if(dev->accel_cfg.odr <= BMI160_ACCEL_ODR_1600HZ) { + if(dev->accel_cfg.odr != dev->prev_accel_cfg.odr) { + odr = (uint8_t)dev->accel_cfg.odr; + temp = *data & ~BMI160_ACCEL_ODR_MASK; + + /* Adding output data rate */ + *data = temp | (odr & BMI160_ACCEL_ODR_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API process the accel bandwidth. + */ +static int8_t process_accel_bw(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t bw = 0; + + if(dev->accel_cfg.bw <= BMI160_ACCEL_BW_RES_AVG128) { + if(dev->accel_cfg.bw != dev->prev_accel_cfg.bw) { + bw = (uint8_t)dev->accel_cfg.bw; + temp = *data & ~BMI160_ACCEL_BW_MASK; + + /* Adding bandwidth */ + *data = temp | ((bw << 4) & BMI160_ACCEL_BW_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API process the accel range. + */ +static int8_t process_accel_range(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t range = 0; + + if(dev->accel_cfg.range <= BMI160_ACCEL_RANGE_16G) { + if(dev->accel_cfg.range != dev->prev_accel_cfg.range) { + range = (uint8_t)dev->accel_cfg.range; + temp = *data & ~BMI160_ACCEL_RANGE_MASK; + + /* Adding range */ + *data = temp | (range & BMI160_ACCEL_RANGE_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API checks the invalid settings for ODR & Bw for + * Accel and Gyro. + */ +static int8_t check_invalid_settg(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + + /* read the error reg */ + rslt = bmi160_get_regs(BMI160_ERROR_REG_ADDR, &data, 1, dev); + data = data >> 1; + data = data & BMI160_ERR_REG_MASK; + if(data == 1) { + rslt = BMI160_E_ACCEL_ODR_BW_INVALID; + } else if(data == 2) { + rslt = BMI160_E_GYRO_ODR_BW_INVALID; + } else if(data == 3) { + rslt = BMI160_E_LWP_PRE_FLTR_INT_INVALID; + } else if(data == 7) { + rslt = BMI160_E_LWP_PRE_FLTR_INVALID; + } + + return rslt; +} +static int8_t set_gyro_conf(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0}; + + rslt = check_gyro_config(data, dev); + if(rslt == BMI160_OK) { + /* Write output data rate and bandwidth */ + rslt = bmi160_set_regs(BMI160_GYRO_CONFIG_ADDR, &data[0], 1, dev); + if(rslt == BMI160_OK) { + dev->prev_gyro_cfg.odr = dev->gyro_cfg.odr; + dev->prev_gyro_cfg.bw = dev->gyro_cfg.bw; + + /* Write gyro range */ + rslt = bmi160_set_regs(BMI160_GYRO_RANGE_ADDR, &data[1], 1, dev); + if(rslt == BMI160_OK) { + dev->prev_gyro_cfg.range = dev->gyro_cfg.range; + } + } + } + + return rslt; +} + +/*! + * @brief This API gets the gyro configuration. + */ +static int8_t get_gyro_conf(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0}; + + /* Get accel configurations */ + rslt = bmi160_get_regs(BMI160_GYRO_CONFIG_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + dev->gyro_cfg.odr = (data[0] & BMI160_GYRO_ODR_MASK); + dev->gyro_cfg.bw = (data[0] & BMI160_GYRO_BW_MASK) >> BMI160_GYRO_BW_POS; + dev->gyro_cfg.range = (data[1] & BMI160_GYRO_RANGE_MASK); + } + + return rslt; +} + +/*! + * @brief This API check the gyro configuration. + */ +static int8_t check_gyro_config(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt; + + /* read gyro Output data rate and bandwidth */ + rslt = bmi160_get_regs(BMI160_GYRO_CONFIG_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + rslt = process_gyro_odr(&data[0], dev); + if(rslt == BMI160_OK) { + rslt = process_gyro_bw(&data[0], dev); + if(rslt == BMI160_OK) { + rslt = process_gyro_range(&data[1], dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API process the gyro odr. + */ +static int8_t process_gyro_odr(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t odr = 0; + + if(dev->gyro_cfg.odr <= BMI160_GYRO_ODR_3200HZ) { + if(dev->gyro_cfg.odr != dev->prev_gyro_cfg.odr) { + odr = (uint8_t)dev->gyro_cfg.odr; + temp = (*data & ~BMI160_GYRO_ODR_MASK); + + /* Adding output data rate */ + *data = temp | (odr & BMI160_GYRO_ODR_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API process the gyro bandwidth. + */ +static int8_t process_gyro_bw(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t bw = 0; + + if(dev->gyro_cfg.bw <= BMI160_GYRO_BW_NORMAL_MODE) { + bw = (uint8_t)dev->gyro_cfg.bw; + temp = *data & ~BMI160_GYRO_BW_MASK; + + /* Adding bandwidth */ + *data = temp | ((bw << 4) & BMI160_GYRO_BW_MASK); + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API process the gyro range. + */ +static int8_t process_gyro_range(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t range = 0; + + if(dev->gyro_cfg.range <= BMI160_GYRO_RANGE_125_DPS) { + if(dev->gyro_cfg.range != dev->prev_gyro_cfg.range) { + range = (uint8_t)dev->gyro_cfg.range; + temp = *data & ~BMI160_GYRO_RANGE_MASK; + + /* Adding range */ + *data = temp | (range & BMI160_GYRO_RANGE_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API sets the accel power. + */ +static int8_t set_accel_pwr(struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t data = 0; + + if((dev->accel_cfg.power >= BMI160_ACCEL_SUSPEND_MODE) && + (dev->accel_cfg.power <= BMI160_ACCEL_LOWPOWER_MODE)) { + if(dev->accel_cfg.power != dev->prev_accel_cfg.power) { + rslt = process_under_sampling(&data, dev); + if(rslt == BMI160_OK) { + /* Write accel power */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &dev->accel_cfg.power, 1, dev); + + /* Add delay of 3.8 ms - refer data sheet table 24*/ + if(dev->prev_accel_cfg.power == BMI160_ACCEL_SUSPEND_MODE) { + dev->delay_ms(BMI160_ACCEL_DELAY_MS); + } + + dev->prev_accel_cfg.power = dev->accel_cfg.power; + } + } + } else { + rslt = BMI160_E_INVALID_CONFIG; + } + + return rslt; +} + +/*! + * @brief This API process the undersampling setting of Accel. + */ +static int8_t process_under_sampling(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t pre_filter[2] = {0}; + + rslt = bmi160_get_regs(BMI160_ACCEL_CONFIG_ADDR, data, 1, dev); + if(rslt == BMI160_OK) { + if(dev->accel_cfg.power == BMI160_ACCEL_LOWPOWER_MODE) { + temp = *data & ~BMI160_ACCEL_UNDERSAMPLING_MASK; + + /* Set under-sampling parameter */ + *data = temp | ((1 << 7) & BMI160_ACCEL_UNDERSAMPLING_MASK); + + /* Write data */ + rslt = bmi160_set_regs(BMI160_ACCEL_CONFIG_ADDR, data, 1, dev); + + /* Disable the pre-filter data in low power mode */ + if(rslt == BMI160_OK) { + /* Disable the Pre-filter data*/ + rslt = bmi160_set_regs(BMI160_INT_DATA_0_ADDR, pre_filter, 2, dev); + } + } else if(*data & BMI160_ACCEL_UNDERSAMPLING_MASK) { + temp = *data & ~BMI160_ACCEL_UNDERSAMPLING_MASK; + + /* Disable under-sampling parameter if already enabled */ + *data = temp; + + /* Write data */ + rslt = bmi160_set_regs(BMI160_ACCEL_CONFIG_ADDR, data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the gyro power mode. + */ +static int8_t set_gyro_pwr(struct bmi160_dev* dev) { + int8_t rslt = 0; + + if((dev->gyro_cfg.power == BMI160_GYRO_SUSPEND_MODE) || + (dev->gyro_cfg.power == BMI160_GYRO_NORMAL_MODE) || + (dev->gyro_cfg.power == BMI160_GYRO_FASTSTARTUP_MODE)) { + if(dev->gyro_cfg.power != dev->prev_gyro_cfg.power) { + /* Write gyro power */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &dev->gyro_cfg.power, 1, dev); + if(dev->prev_gyro_cfg.power == BMI160_GYRO_SUSPEND_MODE) { + /* Delay of 80 ms - datasheet Table 24 */ + dev->delay_ms(BMI160_GYRO_DELAY_MS); + } else if( + (dev->prev_gyro_cfg.power == BMI160_GYRO_FASTSTARTUP_MODE) && + (dev->gyro_cfg.power == BMI160_GYRO_NORMAL_MODE)) { + /* This delay is required for transition from + * fast-startup mode to normal mode - datasheet Table 3 */ + dev->delay_ms(10); + } else { + /* do nothing */ + } + + dev->prev_gyro_cfg.power = dev->gyro_cfg.power; + } + } else { + rslt = BMI160_E_INVALID_CONFIG; + } + + return rslt; +} + +/*! + * @brief This API reads accel data along with sensor time if time is requested + * by user. Kindly refer the user guide(README.md) for more info. + */ +static int8_t + get_accel_data(uint8_t len, struct bmi160_sensor_data* accel, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t idx = 0; + uint8_t data_array[9] = {0}; + uint8_t time_0 = 0; + uint16_t time_1 = 0; + uint32_t time_2 = 0; + uint8_t lsb; + uint8_t msb; + int16_t msblsb; + + /* read accel sensor data along with time if requested */ + rslt = bmi160_get_regs(BMI160_ACCEL_DATA_ADDR, data_array, 6 + len, dev); + if(rslt == BMI160_OK) { + /* Accel Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->x = msblsb; /* Data in X axis */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->y = msblsb; /* Data in Y axis */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->z = msblsb; /* Data in Z axis */ + if(len == 3) { + time_0 = data_array[idx++]; + time_1 = (uint16_t)(data_array[idx++] << 8); + time_2 = (uint32_t)(data_array[idx++] << 16); + accel->sensortime = (uint32_t)(time_2 | time_1 | time_0); + } else { + accel->sensortime = 0; + } + } else { + rslt = BMI160_E_COM_FAIL; + } + + return rslt; +} + +/*! + * @brief This API reads accel data along with sensor time if time is requested + * by user. Kindly refer the user guide(README.md) for more info. + */ +static int8_t + get_gyro_data(uint8_t len, struct bmi160_sensor_data* gyro, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t idx = 0; + uint8_t data_array[15] = {0}; + uint8_t time_0 = 0; + uint16_t time_1 = 0; + uint32_t time_2 = 0; + uint8_t lsb; + uint8_t msb; + int16_t msblsb; + + if(len == 0) { + /* read gyro data only */ + rslt = bmi160_get_regs(BMI160_GYRO_DATA_ADDR, data_array, 6, dev); + if(rslt == BMI160_OK) { + /* Gyro Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->x = msblsb; /* Data in X axis */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->y = msblsb; /* Data in Y axis */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->z = msblsb; /* Data in Z axis */ + gyro->sensortime = 0; + } else { + rslt = BMI160_E_COM_FAIL; + } + } else { + /* read gyro sensor data along with time */ + rslt = bmi160_get_regs(BMI160_GYRO_DATA_ADDR, data_array, 12 + len, dev); + if(rslt == BMI160_OK) { + /* Gyro Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->x = msblsb; /* gyro X axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->y = msblsb; /* gyro Y axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->z = msblsb; /* gyro Z axis data */ + idx = idx + 6; + time_0 = data_array[idx++]; + time_1 = (uint16_t)(data_array[idx++] << 8); + time_2 = (uint32_t)(data_array[idx++] << 16); + gyro->sensortime = (uint32_t)(time_2 | time_1 | time_0); + } else { + rslt = BMI160_E_COM_FAIL; + } + } + + return rslt; +} + +/*! + * @brief This API reads accel and gyro data along with sensor time + * if time is requested by user. + * Kindly refer the user guide(README.md) for more info. + */ +static int8_t get_accel_gyro_data( + uint8_t len, + struct bmi160_sensor_data* accel, + struct bmi160_sensor_data* gyro, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t idx = 0; + uint8_t data_array[15] = {0}; + uint8_t time_0 = 0; + uint16_t time_1 = 0; + uint32_t time_2 = 0; + uint8_t lsb; + uint8_t msb; + int16_t msblsb; + + /* read both accel and gyro sensor data + * along with time if requested */ + rslt = bmi160_get_regs(BMI160_GYRO_DATA_ADDR, data_array, 12 + len, dev); + if(rslt == BMI160_OK) { + /* Gyro Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->x = msblsb; /* gyro X axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->y = msblsb; /* gyro Y axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->z = msblsb; /* gyro Z axis data */ + /* Accel Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->x = (int16_t)msblsb; /* accel X axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->y = (int16_t)msblsb; /* accel Y axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->z = (int16_t)msblsb; /* accel Z axis data */ + if(len == 3) { + time_0 = data_array[idx++]; + time_1 = (uint16_t)(data_array[idx++] << 8); + time_2 = (uint32_t)(data_array[idx++] << 16); + accel->sensortime = (uint32_t)(time_2 | time_1 | time_0); + gyro->sensortime = (uint32_t)(time_2 | time_1 | time_0); + } else { + accel->sensortime = 0; + gyro->sensortime = 0; + } + } else { + rslt = BMI160_E_COM_FAIL; + } + + return rslt; +} + +/*! + * @brief This API enables the any-motion interrupt for accel. + */ +static int8_t enable_accel_any_motion_int( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable any motion x, any motion y, any motion z + * in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(any_motion_int_cfg->anymotion_en == BMI160_ENABLE) { + temp = data & ~BMI160_ANY_MOTION_X_INT_EN_MASK; + + /* Adding Any_motion x axis */ + data = temp | (any_motion_int_cfg->anymotion_x & BMI160_ANY_MOTION_X_INT_EN_MASK); + temp = data & ~BMI160_ANY_MOTION_Y_INT_EN_MASK; + + /* Adding Any_motion y axis */ + data = temp | + ((any_motion_int_cfg->anymotion_y << 1) & BMI160_ANY_MOTION_Y_INT_EN_MASK); + temp = data & ~BMI160_ANY_MOTION_Z_INT_EN_MASK; + + /* Adding Any_motion z axis */ + data = temp | + ((any_motion_int_cfg->anymotion_z << 2) & BMI160_ANY_MOTION_Z_INT_EN_MASK); + + /* any-motion feature selected*/ + dev->any_sig_sel = BMI160_ANY_MOTION_ENABLED; + } else { + data = data & ~BMI160_ANY_MOTION_ALL_INT_EN_MASK; + + /* neither any-motion feature nor sig-motion selected */ + dev->any_sig_sel = BMI160_BOTH_ANY_SIG_MOTION_DISABLED; + } + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API disable the sig-motion interrupt. + */ +static int8_t disable_sig_motion_int(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Disabling Significant motion interrupt if enabled */ + rslt = bmi160_get_regs(BMI160_INT_MOTION_3_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = (data & BMI160_SIG_MOTION_SEL_MASK); + if(temp) { + temp = data & ~BMI160_SIG_MOTION_SEL_MASK; + data = temp; + + /* Write data to register */ + rslt = bmi160_set_regs(BMI160_INT_MOTION_3_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API is used to map/unmap the Any/Sig motion, Step det/Low-g, + * Double tap, Single tap, Orientation, Flat, High-G, Nomotion interrupt pins. + */ +static int8_t + map_feature_interrupt(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[3] = {0, 0, 0}; + uint8_t temp[3] = {0, 0, 0}; + + rslt = bmi160_get_regs(BMI160_INT_MAP_0_ADDR, data, 3, dev); + if(rslt == BMI160_OK) { + temp[0] = data[0] & ~int_mask_lookup_table[int_config->int_type]; + temp[2] = data[2] & ~int_mask_lookup_table[int_config->int_type]; + switch(int_config->int_channel) { + case BMI160_INT_CHANNEL_NONE: + data[0] = temp[0]; + data[2] = temp[2]; + break; + case BMI160_INT_CHANNEL_1: + data[0] = temp[0] | int_mask_lookup_table[int_config->int_type]; + data[2] = temp[2]; + break; + case BMI160_INT_CHANNEL_2: + data[2] = temp[2] | int_mask_lookup_table[int_config->int_type]; + data[0] = temp[0]; + break; + case BMI160_INT_CHANNEL_BOTH: + data[0] = temp[0] | int_mask_lookup_table[int_config->int_type]; + data[2] = temp[2] | int_mask_lookup_table[int_config->int_type]; + break; + default: + rslt = BMI160_E_OUT_OF_RANGE; + } + if(rslt == BMI160_OK) { + rslt = bmi160_set_regs(BMI160_INT_MAP_0_ADDR, data, 3, dev); + } + } + + return rslt; +} + +/*! + * @brief This API is used to map/unmap the Dataready(Accel & Gyro), FIFO full + * and FIFO watermark interrupt. + */ +static int8_t map_hardware_interrupt( + const struct bmi160_int_settg* int_config, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + rslt = bmi160_get_regs(BMI160_INT_MAP_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~int_mask_lookup_table[int_config->int_type]; + temp = temp & ~((uint8_t)(int_mask_lookup_table[int_config->int_type] << 4)); + switch(int_config->int_channel) { + case BMI160_INT_CHANNEL_NONE: + data = temp; + break; + case BMI160_INT_CHANNEL_1: + data = temp | (uint8_t)((int_mask_lookup_table[int_config->int_type]) << 4); + break; + case BMI160_INT_CHANNEL_2: + data = temp | int_mask_lookup_table[int_config->int_type]; + break; + case BMI160_INT_CHANNEL_BOTH: + data = temp | int_mask_lookup_table[int_config->int_type]; + data = data | (uint8_t)((int_mask_lookup_table[int_config->int_type]) << 4); + break; + default: + rslt = BMI160_E_OUT_OF_RANGE; + } + if(rslt == BMI160_OK) { + rslt = bmi160_set_regs(BMI160_INT_MAP_1_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for any-motion interrupt. + */ +static int8_t config_any_motion_src( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 1 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_MOTION_SRC_INT_MASK; + data = temp | ((any_motion_int_cfg->anymotion_data_src << 7) & BMI160_MOTION_SRC_INT_MASK); + + /* Write data to DATA 1 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the duration and threshold of + * any-motion interrupt. + */ +static int8_t config_any_dur_threshold( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + uint8_t data_array[2] = {0}; + uint8_t dur; + + /* Configure Int Motion 0 register */ + rslt = bmi160_get_regs(BMI160_INT_MOTION_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* slope duration */ + dur = (uint8_t)any_motion_int_cfg->anymotion_dur; + temp = data & ~BMI160_SLOPE_INT_DUR_MASK; + data = temp | (dur & BMI160_MOTION_SRC_INT_MASK); + data_array[0] = data; + + /* add slope threshold */ + data_array[1] = any_motion_int_cfg->anymotion_thr; + + /* INT MOTION 0 and INT MOTION 1 address lie consecutively, + * hence writing data to respective registers at one go */ + + /* Writing to Int_motion 0 and + * Int_motion 1 Address simultaneously */ + rslt = bmi160_set_regs(BMI160_INT_MOTION_0_ADDR, data_array, 2, dev); + } + + return rslt; +} + +/*! + * @brief This API configure necessary setting of any-motion interrupt. + */ +static int8_t config_any_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = disable_sig_motion_int(dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_any_motion_src(any_motion_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_any_dur_threshold(any_motion_int_cfg, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API enable the data ready interrupt. + */ +static int8_t enable_data_ready_int(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable data ready interrupt in Int Enable 1 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_DATA_RDY_INT_EN_MASK; + data = temp | ((1 << 4) & BMI160_DATA_RDY_INT_EN_MASK); + + /* Writing data to INT ENABLE 1 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API enables the no motion/slow motion interrupt. + */ +static int8_t enable_no_motion_int( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable no motion x, no motion y, no motion z + * in Int Enable 2 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_2_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(no_mot_int_cfg->no_motion_x == 1) { + temp = data & ~BMI160_NO_MOTION_X_INT_EN_MASK; + + /* Adding No_motion x axis */ + data = temp | (1 & BMI160_NO_MOTION_X_INT_EN_MASK); + } + + if(no_mot_int_cfg->no_motion_y == 1) { + temp = data & ~BMI160_NO_MOTION_Y_INT_EN_MASK; + + /* Adding No_motion x axis */ + data = temp | ((1 << 1) & BMI160_NO_MOTION_Y_INT_EN_MASK); + } + + if(no_mot_int_cfg->no_motion_z == 1) { + temp = data & ~BMI160_NO_MOTION_Z_INT_EN_MASK; + + /* Adding No_motion x axis */ + data = temp | ((1 << 2) & BMI160_NO_MOTION_Z_INT_EN_MASK); + } + + /* write data to Int Enable 2 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_2_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the interrupt PIN setting for + * no motion/slow motion interrupt. + */ +static int8_t config_no_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_no_motion_data_src(no_mot_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_no_motion_dur_thr(no_mot_int_cfg, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API configure the source of interrupt for no motion. + */ +static int8_t config_no_motion_data_src( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 1 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_MOTION_SRC_INT_MASK; + data = temp | ((no_mot_int_cfg->no_motion_src << 7) & BMI160_MOTION_SRC_INT_MASK); + + /* Write data to DATA 1 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the duration and threshold of + * no motion/slow motion interrupt along with selection of no/slow motion. + */ +static int8_t config_no_motion_dur_thr( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + uint8_t temp_1 = 0; + uint8_t reg_addr; + uint8_t data_array[2] = {0}; + + /* Configuring INT_MOTION register */ + reg_addr = BMI160_INT_MOTION_0_ADDR; + rslt = bmi160_get_regs(reg_addr, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_NO_MOTION_INT_DUR_MASK; + + /* Adding no_motion duration */ + data = temp | ((no_mot_int_cfg->no_motion_dur << 2) & BMI160_NO_MOTION_INT_DUR_MASK); + + /* Write data to NO_MOTION 0 address */ + rslt = bmi160_set_regs(reg_addr, &data, 1, dev); + if(rslt == BMI160_OK) { + reg_addr = BMI160_INT_MOTION_3_ADDR; + rslt = bmi160_get_regs(reg_addr, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_NO_MOTION_SEL_BIT_MASK; + + /* Adding no_motion_sel bit */ + temp_1 = (no_mot_int_cfg->no_motion_sel & BMI160_NO_MOTION_SEL_BIT_MASK); + data = (temp | temp_1); + data_array[1] = data; + + /* Adding no motion threshold */ + data_array[0] = no_mot_int_cfg->no_motion_thres; + reg_addr = BMI160_INT_MOTION_2_ADDR; + + /* writing data to INT_MOTION 2 and INT_MOTION 3 + * address simultaneously */ + rslt = bmi160_set_regs(reg_addr, data_array, 2, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API enables the sig-motion motion interrupt. + */ +static int8_t enable_sig_motion_int( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* For significant motion,enable any motion x,any motion y, + * any motion z in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(sig_mot_int_cfg->sig_en == BMI160_ENABLE) { + temp = data & ~BMI160_SIG_MOTION_INT_EN_MASK; + data = temp | (7 & BMI160_SIG_MOTION_INT_EN_MASK); + + /* sig-motion feature selected*/ + dev->any_sig_sel = BMI160_SIG_MOTION_ENABLED; + } else { + data = data & ~BMI160_SIG_MOTION_INT_EN_MASK; + + /* neither any-motion feature nor sig-motion selected */ + dev->any_sig_sel = BMI160_BOTH_ANY_SIG_MOTION_DISABLED; + } + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the interrupt PIN setting for + * significant motion interrupt. + */ +static int8_t config_sig_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_sig_motion_data_src(sig_mot_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_sig_dur_threshold(sig_mot_int_cfg, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for sig motion interrupt. + */ +static int8_t config_sig_motion_data_src( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 1 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_MOTION_SRC_INT_MASK; + data = temp | ((sig_mot_int_cfg->sig_data_src << 7) & BMI160_MOTION_SRC_INT_MASK); + + /* Write data to DATA 1 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the threshold, skip and proof time of + * sig motion interrupt. + */ +static int8_t config_sig_dur_threshold( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data; + uint8_t temp = 0; + + /* Configuring INT_MOTION registers */ + + /* Write significant motion threshold. + * This threshold is same as any motion threshold */ + data = sig_mot_int_cfg->sig_mot_thres; + + /* Write data to INT_MOTION 1 address */ + rslt = bmi160_set_regs(BMI160_INT_MOTION_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + rslt = bmi160_get_regs(BMI160_INT_MOTION_3_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_SIG_MOTION_SKIP_MASK; + + /* adding skip time of sig_motion interrupt*/ + data = temp | ((sig_mot_int_cfg->sig_mot_skip << 2) & BMI160_SIG_MOTION_SKIP_MASK); + temp = data & ~BMI160_SIG_MOTION_PROOF_MASK; + + /* adding proof time of sig_motion interrupt */ + data = temp | ((sig_mot_int_cfg->sig_mot_proof << 4) & BMI160_SIG_MOTION_PROOF_MASK); + + /* configure the int_sig_mot_sel bit to select + * significant motion interrupt */ + temp = data & ~BMI160_SIG_MOTION_SEL_MASK; + data = temp | ((sig_mot_int_cfg->sig_en << 1) & BMI160_SIG_MOTION_SEL_MASK); + rslt = bmi160_set_regs(BMI160_INT_MOTION_3_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API enables the step detector interrupt. + */ +static int8_t enable_step_detect_int( + const struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable data ready interrupt in Int Enable 2 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_2_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_STEP_DETECT_INT_EN_MASK; + data = temp | + ((step_detect_int_cfg->step_detector_en << 3) & BMI160_STEP_DETECT_INT_EN_MASK); + + /* Writing data to INT ENABLE 2 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_2_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the step detector parameter. + */ +static int8_t config_step_detect( + const struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data_array[2] = {0}; + + if(step_detect_int_cfg->step_detector_mode == BMI160_STEP_DETECT_NORMAL) { + /* Normal mode setting */ + data_array[0] = 0x15; + data_array[1] = 0x03; + } else if(step_detect_int_cfg->step_detector_mode == BMI160_STEP_DETECT_SENSITIVE) { + /* Sensitive mode setting */ + data_array[0] = 0x2D; + data_array[1] = 0x00; + } else if(step_detect_int_cfg->step_detector_mode == BMI160_STEP_DETECT_ROBUST) { + /* Robust mode setting */ + data_array[0] = 0x1D; + data_array[1] = 0x07; + } else if(step_detect_int_cfg->step_detector_mode == BMI160_STEP_DETECT_USER_DEFINE) { + /* Non recommended User defined setting */ + /* Configuring STEP_CONFIG register */ + rslt = bmi160_get_regs(BMI160_INT_STEP_CONFIG_0_ADDR, &data_array[0], 2, dev); + if(rslt == BMI160_OK) { + temp = data_array[0] & ~BMI160_STEP_DETECT_MIN_THRES_MASK; + + /* Adding min_threshold */ + data_array[0] = temp | ((step_detect_int_cfg->min_threshold << 3) & + BMI160_STEP_DETECT_MIN_THRES_MASK); + temp = data_array[0] & ~BMI160_STEP_DETECT_STEPTIME_MIN_MASK; + + /* Adding steptime_min */ + data_array[0] = temp | ((step_detect_int_cfg->steptime_min) & + BMI160_STEP_DETECT_STEPTIME_MIN_MASK); + temp = data_array[1] & ~BMI160_STEP_MIN_BUF_MASK; + + /* Adding steptime_min */ + data_array[1] = temp | + ((step_detect_int_cfg->step_min_buf) & BMI160_STEP_MIN_BUF_MASK); + } + } + + /* Write data to STEP_CONFIG register */ + rslt = bmi160_set_regs(BMI160_INT_STEP_CONFIG_0_ADDR, data_array, 2, dev); + + return rslt; +} + +/*! + * @brief This API enables the single/double tap interrupt. + */ +static int8_t enable_tap_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable single tap or double tap interrupt in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(int_config->int_type == BMI160_ACC_SINGLE_TAP_INT) { + temp = data & ~BMI160_SINGLE_TAP_INT_EN_MASK; + data = temp | ((tap_int_cfg->tap_en << 5) & BMI160_SINGLE_TAP_INT_EN_MASK); + } else { + temp = data & ~BMI160_DOUBLE_TAP_INT_EN_MASK; + data = temp | ((tap_int_cfg->tap_en << 4) & BMI160_DOUBLE_TAP_INT_EN_MASK); + } + + /* Write to Enable 0 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the interrupt PIN setting for + * tap interrupt. + */ +static int8_t config_tap_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_tap_data_src(tap_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_tap_param(int_config, tap_int_cfg, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for tap interrupt. + */ +static int8_t config_tap_data_src( + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 0 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_TAP_SRC_INT_MASK; + data = temp | ((tap_int_cfg->tap_data_src << 3) & BMI160_TAP_SRC_INT_MASK); + + /* Write data to Data 0 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the parameters of tap interrupt. + * Threshold, quite, shock, and duration. + */ +static int8_t config_tap_param( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data = 0; + uint8_t data_array[2] = {0}; + uint8_t count = 0; + uint8_t dur, shock, quiet, thres; + + /* Configure tap 0 register for tap shock,tap quiet duration + * in case of single tap interrupt */ + rslt = bmi160_get_regs(BMI160_INT_TAP_0_ADDR, data_array, 2, dev); + if(rslt == BMI160_OK) { + data = data_array[count]; + if(int_config->int_type == BMI160_ACC_DOUBLE_TAP_INT) { + dur = (uint8_t)tap_int_cfg->tap_dur; + temp = (data & ~BMI160_TAP_DUR_MASK); + + /* Add tap duration data in case of + * double tap interrupt */ + data = temp | (dur & BMI160_TAP_DUR_MASK); + } + + shock = (uint8_t)tap_int_cfg->tap_shock; + temp = data & ~BMI160_TAP_SHOCK_DUR_MASK; + data = temp | ((shock << 6) & BMI160_TAP_SHOCK_DUR_MASK); + quiet = (uint8_t)tap_int_cfg->tap_quiet; + temp = data & ~BMI160_TAP_QUIET_DUR_MASK; + data = temp | ((quiet << 7) & BMI160_TAP_QUIET_DUR_MASK); + data_array[count++] = data; + data = data_array[count]; + thres = (uint8_t)tap_int_cfg->tap_thr; + temp = data & ~BMI160_TAP_THRES_MASK; + data = temp | (thres & BMI160_TAP_THRES_MASK); + data_array[count++] = data; + + /* TAP 0 and TAP 1 address lie consecutively, + * hence writing data to respective registers at one go */ + + /* Writing to Tap 0 and Tap 1 Address simultaneously */ + rslt = bmi160_set_regs(BMI160_INT_TAP_0_ADDR, data_array, count, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the secondary interface. + */ +static int8_t config_sec_if(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t if_conf = 0; + uint8_t cmd = BMI160_AUX_NORMAL_MODE; + + /* set the aux power mode to normal*/ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &cmd, 1, dev); + if(rslt == BMI160_OK) { + /* 0.5ms delay - refer datasheet table 24*/ + dev->delay_ms(1); + rslt = bmi160_get_regs(BMI160_IF_CONF_ADDR, &if_conf, 1, dev); + if_conf |= (uint8_t)(1 << 5); + if(rslt == BMI160_OK) { + /*enable the secondary interface also*/ + rslt = bmi160_set_regs(BMI160_IF_CONF_ADDR, &if_conf, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API configure the ODR of the auxiliary sensor. + */ +static int8_t config_aux_odr(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t aux_odr; + + rslt = bmi160_get_regs(BMI160_AUX_ODR_ADDR, &aux_odr, 1, dev); + if(rslt == BMI160_OK) { + aux_odr = (uint8_t)(dev->aux_cfg.aux_odr); + + /* Set the secondary interface ODR + * i.e polling rate of secondary sensor */ + rslt = bmi160_set_regs(BMI160_AUX_ODR_ADDR, &aux_odr, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + } + + return rslt; +} + +/*! + * @brief This API maps the actual burst read length set by user. + */ +static int8_t map_read_len(uint16_t* len, const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + switch(dev->aux_cfg.aux_rd_burst_len) { + case BMI160_AUX_READ_LEN_0: + *len = 1; + break; + case BMI160_AUX_READ_LEN_1: + *len = 2; + break; + case BMI160_AUX_READ_LEN_2: + *len = 6; + break; + case BMI160_AUX_READ_LEN_3: + *len = 8; + break; + default: + rslt = BMI160_E_INVALID_INPUT; + break; + } + + return rslt; +} + +/*! + * @brief This API configure the settings of auxiliary sensor. + */ +static int8_t config_aux_settg(const struct bmi160_dev* dev) { + int8_t rslt; + + rslt = config_sec_if(dev); + if(rslt == BMI160_OK) { + /* Configures the auxiliary interface settings */ + rslt = bmi160_config_aux_mode(dev); + } + + return rslt; +} + +/*! + * @brief This API extract the read data from auxiliary sensor. + */ +static int8_t extract_aux_read( + uint16_t map_len, + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint8_t data[8] = { + 0, + }; + uint8_t read_addr = BMI160_AUX_DATA_ADDR; + uint8_t count = 0; + uint8_t read_count; + uint8_t read_len = (uint8_t)map_len; + + for(; count < len;) { + /* set address to read */ + rslt = bmi160_set_regs(BMI160_AUX_IF_2_ADDR, ®_addr, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + if(rslt == BMI160_OK) { + rslt = bmi160_get_regs(read_addr, data, map_len, dev); + if(rslt == BMI160_OK) { + read_count = 0; + + /* if read len is less the burst read len + * mention by user*/ + if(len < map_len) { + read_len = (uint8_t)len; + } else if((len - count) < map_len) { + read_len = (uint8_t)(len - count); + } + + for(; read_count < read_len; read_count++) { + aux_data[count + read_count] = data[read_count]; + } + + reg_addr += (uint8_t)map_len; + count += (uint8_t)map_len; + } else { + rslt = BMI160_E_COM_FAIL; + break; + } + } + } + + return rslt; +} + +/*! + * @brief This API enables the orient interrupt. + */ +static int8_t enable_orient_int( + const struct bmi160_acc_orient_int_cfg* orient_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable data ready interrupt in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_ORIENT_INT_EN_MASK; + data = temp | ((orient_int_cfg->orient_en << 6) & BMI160_ORIENT_INT_EN_MASK); + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the necessary setting of orientation interrupt. + */ +static int8_t config_orient_int_settg( + const struct bmi160_acc_orient_int_cfg* orient_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + uint8_t data_array[2] = {0, 0}; + + /* Configuring INT_ORIENT registers */ + rslt = bmi160_get_regs(BMI160_INT_ORIENT_0_ADDR, data_array, 2, dev); + if(rslt == BMI160_OK) { + data = data_array[0]; + temp = data & ~BMI160_ORIENT_MODE_MASK; + + /* Adding Orientation mode */ + data = temp | ((orient_int_cfg->orient_mode) & BMI160_ORIENT_MODE_MASK); + temp = data & ~BMI160_ORIENT_BLOCK_MASK; + + /* Adding Orientation blocking */ + data = temp | ((orient_int_cfg->orient_blocking << 2) & BMI160_ORIENT_BLOCK_MASK); + temp = data & ~BMI160_ORIENT_HYST_MASK; + + /* Adding Orientation hysteresis */ + data = temp | ((orient_int_cfg->orient_hyst << 4) & BMI160_ORIENT_HYST_MASK); + data_array[0] = data; + data = data_array[1]; + temp = data & ~BMI160_ORIENT_THETA_MASK; + + /* Adding Orientation threshold */ + data = temp | ((orient_int_cfg->orient_theta) & BMI160_ORIENT_THETA_MASK); + temp = data & ~BMI160_ORIENT_UD_ENABLE; + + /* Adding Orient_ud_en */ + data = temp | ((orient_int_cfg->orient_ud_en << 6) & BMI160_ORIENT_UD_ENABLE); + temp = data & ~BMI160_AXES_EN_MASK; + + /* Adding axes_en */ + data = temp | ((orient_int_cfg->axes_ex << 7) & BMI160_AXES_EN_MASK); + data_array[1] = data; + + /* Writing data to INT_ORIENT 0 and INT_ORIENT 1 + * registers simultaneously */ + rslt = bmi160_set_regs(BMI160_INT_ORIENT_0_ADDR, data_array, 2, dev); + } + + return rslt; +} + +/*! + * @brief This API enables the flat interrupt. + */ +static int8_t enable_flat_int( + const struct bmi160_acc_flat_detect_int_cfg* flat_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable flat interrupt in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_FLAT_INT_EN_MASK; + data = temp | ((flat_int->flat_en << 7) & BMI160_FLAT_INT_EN_MASK); + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the necessary setting of flat interrupt. + */ +static int8_t config_flat_int_settg( + const struct bmi160_acc_flat_detect_int_cfg* flat_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + uint8_t data_array[2] = {0, 0}; + + /* Configuring INT_FLAT register */ + rslt = bmi160_get_regs(BMI160_INT_FLAT_0_ADDR, data_array, 2, dev); + if(rslt == BMI160_OK) { + data = data_array[0]; + temp = data & ~BMI160_FLAT_THRES_MASK; + + /* Adding flat theta */ + data = temp | ((flat_int->flat_theta) & BMI160_FLAT_THRES_MASK); + data_array[0] = data; + data = data_array[1]; + temp = data & ~BMI160_FLAT_HOLD_TIME_MASK; + + /* Adding flat hold time */ + data = temp | ((flat_int->flat_hold_time << 4) & BMI160_FLAT_HOLD_TIME_MASK); + temp = data & ~BMI160_FLAT_HYST_MASK; + + /* Adding flat hysteresis */ + data = temp | ((flat_int->flat_hy) & BMI160_FLAT_HYST_MASK); + data_array[1] = data; + + /* Writing data to INT_FLAT 0 and INT_FLAT 1 + * registers simultaneously */ + rslt = bmi160_set_regs(BMI160_INT_FLAT_0_ADDR, data_array, 2, dev); + } + + return rslt; +} + +/*! + * @brief This API enables the Low-g interrupt. + */ +static int8_t enable_low_g_int( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable low-g interrupt in Int Enable 1 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_LOW_G_INT_EN_MASK; + data = temp | ((low_g_int->low_en << 3) & BMI160_LOW_G_INT_EN_MASK); + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for low-g interrupt. + */ +static int8_t config_low_g_data_src( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 0 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_LOW_HIGH_SRC_INT_MASK; + data = temp | ((low_g_int->low_data_src << 7) & BMI160_LOW_HIGH_SRC_INT_MASK); + + /* Write data to Data 0 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the necessary setting of low-g interrupt. + */ +static int8_t config_low_g_int_settg( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data_array[3] = {0, 0, 0}; + + /* Configuring INT_LOWHIGH register for low-g interrupt */ + rslt = bmi160_get_regs(BMI160_INT_LOWHIGH_2_ADDR, &data_array[2], 1, dev); + if(rslt == BMI160_OK) { + temp = data_array[2] & ~BMI160_LOW_G_HYST_MASK; + + /* Adding low-g hysteresis */ + data_array[2] = temp | (low_g_int->low_hyst & BMI160_LOW_G_HYST_MASK); + temp = data_array[2] & ~BMI160_LOW_G_LOW_MODE_MASK; + + /* Adding low-mode */ + data_array[2] = temp | ((low_g_int->low_mode << 2) & BMI160_LOW_G_LOW_MODE_MASK); + + /* Adding low-g threshold */ + data_array[1] = low_g_int->low_thres; + + /* Adding low-g interrupt delay */ + data_array[0] = low_g_int->low_dur; + + /* Writing data to INT_LOWHIGH 0,1,2 registers simultaneously*/ + rslt = bmi160_set_regs(BMI160_INT_LOWHIGH_0_ADDR, data_array, 3, dev); + } + + return rslt; +} + +/*! + * @brief This API enables the high-g interrupt. + */ +static int8_t enable_high_g_int( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable low-g interrupt in Int Enable 1 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Adding high-g X-axis */ + temp = data & ~BMI160_HIGH_G_X_INT_EN_MASK; + data = temp | (high_g_int_cfg->high_g_x & BMI160_HIGH_G_X_INT_EN_MASK); + + /* Adding high-g Y-axis */ + temp = data & ~BMI160_HIGH_G_Y_INT_EN_MASK; + data = temp | ((high_g_int_cfg->high_g_y << 1) & BMI160_HIGH_G_Y_INT_EN_MASK); + + /* Adding high-g Z-axis */ + temp = data & ~BMI160_HIGH_G_Z_INT_EN_MASK; + data = temp | ((high_g_int_cfg->high_g_z << 2) & BMI160_HIGH_G_Z_INT_EN_MASK); + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for high-g interrupt. + */ +static int8_t config_high_g_data_src( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 0 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_LOW_HIGH_SRC_INT_MASK; + data = temp | ((high_g_int_cfg->high_data_src << 7) & BMI160_LOW_HIGH_SRC_INT_MASK); + + /* Write data to Data 0 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the necessary setting of high-g interrupt. + */ +static int8_t config_high_g_int_settg( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data_array[3] = {0, 0, 0}; + + rslt = bmi160_get_regs(BMI160_INT_LOWHIGH_2_ADDR, &data_array[0], 1, dev); + if(rslt == BMI160_OK) { + temp = data_array[0] & ~BMI160_HIGH_G_HYST_MASK; + + /* Adding high-g hysteresis */ + data_array[0] = temp | ((high_g_int_cfg->high_hy << 6) & BMI160_HIGH_G_HYST_MASK); + + /* Adding high-g duration */ + data_array[1] = high_g_int_cfg->high_dur; + + /* Adding high-g threshold */ + data_array[2] = high_g_int_cfg->high_thres; + rslt = bmi160_set_regs(BMI160_INT_LOWHIGH_2_ADDR, data_array, 3, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the behavioural setting of interrupt pin. + */ +static int8_t + config_int_out_ctrl(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data = 0; + + /* Configuration of output interrupt signals on pins INT1 and INT2 are + * done in BMI160_INT_OUT_CTRL_ADDR register*/ + rslt = bmi160_get_regs(BMI160_INT_OUT_CTRL_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* updating the interrupt pin structure to local structure */ + const struct bmi160_int_pin_settg* intr_pin_sett = &(int_config->int_pin_settg); + + /* Configuring channel 1 */ + if(int_config->int_channel == BMI160_INT_CHANNEL_1) { + /* Output enable */ + temp = data & ~BMI160_INT1_OUTPUT_EN_MASK; + data = temp | ((intr_pin_sett->output_en << 3) & BMI160_INT1_OUTPUT_EN_MASK); + + /* Output mode */ + temp = data & ~BMI160_INT1_OUTPUT_MODE_MASK; + data = temp | ((intr_pin_sett->output_mode << 2) & BMI160_INT1_OUTPUT_MODE_MASK); + + /* Output type */ + temp = data & ~BMI160_INT1_OUTPUT_TYPE_MASK; + data = temp | ((intr_pin_sett->output_type << 1) & BMI160_INT1_OUTPUT_TYPE_MASK); + + /* edge control */ + temp = data & ~BMI160_INT1_EDGE_CTRL_MASK; + data = temp | ((intr_pin_sett->edge_ctrl) & BMI160_INT1_EDGE_CTRL_MASK); + } else { + /* Configuring channel 2 */ + /* Output enable */ + temp = data & ~BMI160_INT2_OUTPUT_EN_MASK; + data = temp | ((intr_pin_sett->output_en << 7) & BMI160_INT2_OUTPUT_EN_MASK); + + /* Output mode */ + temp = data & ~BMI160_INT2_OUTPUT_MODE_MASK; + data = temp | ((intr_pin_sett->output_mode << 6) & BMI160_INT2_OUTPUT_MODE_MASK); + + /* Output type */ + temp = data & ~BMI160_INT2_OUTPUT_TYPE_MASK; + data = temp | ((intr_pin_sett->output_type << 5) & BMI160_INT2_OUTPUT_TYPE_MASK); + + /* edge control */ + temp = data & ~BMI160_INT2_EDGE_CTRL_MASK; + data = temp | ((intr_pin_sett->edge_ctrl << 4) & BMI160_INT2_EDGE_CTRL_MASK); + } + + rslt = bmi160_set_regs(BMI160_INT_OUT_CTRL_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the mode(input enable, latch or non-latch) of interrupt pin. + */ +static int8_t + config_int_latch(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data = 0; + + /* Configuration of latch on pins INT1 and INT2 are done in + * BMI160_INT_LATCH_ADDR register*/ + rslt = bmi160_get_regs(BMI160_INT_LATCH_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* updating the interrupt pin structure to local structure */ + const struct bmi160_int_pin_settg* intr_pin_sett = &(int_config->int_pin_settg); + if(int_config->int_channel == BMI160_INT_CHANNEL_1) { + /* Configuring channel 1 */ + /* Input enable */ + temp = data & ~BMI160_INT1_INPUT_EN_MASK; + data = temp | ((intr_pin_sett->input_en << 4) & BMI160_INT1_INPUT_EN_MASK); + } else { + /* Configuring channel 2 */ + /* Input enable */ + temp = data & ~BMI160_INT2_INPUT_EN_MASK; + data = temp | ((intr_pin_sett->input_en << 5) & BMI160_INT2_INPUT_EN_MASK); + } + + /* In case of latch interrupt,update the latch duration */ + + /* Latching holds the interrupt for the amount of latch + * duration time */ + temp = data & ~BMI160_INT_LATCH_MASK; + data = temp | (intr_pin_sett->latch_dur & BMI160_INT_LATCH_MASK); + + /* OUT_CTRL_INT and LATCH_INT address lie consecutively, + * hence writing data to respective registers at one go */ + rslt = bmi160_set_regs(BMI160_INT_LATCH_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API performs the self test for accelerometer of BMI160 + */ +static int8_t perform_accel_self_test(struct bmi160_dev* dev) { + int8_t rslt; + struct bmi160_sensor_data accel_pos, accel_neg; + + /* Enable Gyro self test bit */ + rslt = enable_accel_self_test(dev); + if(rslt == BMI160_OK) { + /* Perform accel self test with positive excitation */ + rslt = accel_self_test_positive_excitation(&accel_pos, dev); + if(rslt == BMI160_OK) { + /* Perform accel self test with negative excitation */ + rslt = accel_self_test_negative_excitation(&accel_neg, dev); + if(rslt == BMI160_OK) { + /* Validate the self test result */ + rslt = validate_accel_self_test(&accel_pos, &accel_neg); + } + } + } + + return rslt; +} + +/*! + * @brief This API enables to perform the accel self test by setting proper + * configurations to facilitate accel self test + */ +static int8_t enable_accel_self_test(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Set the Accel power mode as normal mode */ + dev->accel_cfg.power = BMI160_ACCEL_NORMAL_MODE; + + /* Set the sensor range configuration as 8G */ + dev->accel_cfg.range = BMI160_ACCEL_RANGE_8G; + rslt = bmi160_set_sens_conf(dev); + if(rslt == BMI160_OK) { + /* Accel configurations are set to facilitate self test + * acc_odr - 1600Hz ; acc_bwp = 2 ; acc_us = 0 */ + reg_data = BMI160_ACCEL_SELF_TEST_CONFIG; + rslt = bmi160_set_regs(BMI160_ACCEL_CONFIG_ADDR, ®_data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API performs accel self test with positive excitation + */ +static int8_t accel_self_test_positive_excitation( + struct bmi160_sensor_data* accel_pos, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Enable accel self test with positive self-test excitation + * and with amplitude of deflection set as high */ + reg_data = BMI160_ACCEL_SELF_TEST_POSITIVE_EN; + rslt = bmi160_set_regs(BMI160_SELF_TEST_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + /* Read the data after a delay of 50ms - refer datasheet 2.8.1 accel self test*/ + dev->delay_ms(BMI160_ACCEL_SELF_TEST_DELAY); + rslt = bmi160_get_sensor_data(BMI160_ACCEL_ONLY, accel_pos, NULL, dev); + } + + return rslt; +} + +/*! + * @brief This API performs accel self test with negative excitation + */ +static int8_t accel_self_test_negative_excitation( + struct bmi160_sensor_data* accel_neg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Enable accel self test with negative self-test excitation + * and with amplitude of deflection set as high */ + reg_data = BMI160_ACCEL_SELF_TEST_NEGATIVE_EN; + rslt = bmi160_set_regs(BMI160_SELF_TEST_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + /* Read the data after a delay of 50ms */ + dev->delay_ms(BMI160_ACCEL_SELF_TEST_DELAY); + rslt = bmi160_get_sensor_data(BMI160_ACCEL_ONLY, accel_neg, NULL, dev); + } + + return rslt; +} + +/*! + * @brief This API validates the accel self test results + */ +static int8_t validate_accel_self_test( + const struct bmi160_sensor_data* accel_pos, + const struct bmi160_sensor_data* accel_neg) { + int8_t rslt; + + /* Validate the results of self test */ + if(((accel_neg->x - accel_pos->x) > BMI160_ACCEL_SELF_TEST_LIMIT) && + ((accel_neg->y - accel_pos->y) > BMI160_ACCEL_SELF_TEST_LIMIT) && + ((accel_neg->z - accel_pos->z) > BMI160_ACCEL_SELF_TEST_LIMIT)) { + /* Self test pass condition */ + rslt = BMI160_OK; + } else { + rslt = BMI160_W_ACCEl_SELF_TEST_FAIL; + } + + return rslt; +} + +/*! + * @brief This API performs the self test for gyroscope of BMI160 + */ +static int8_t perform_gyro_self_test(const struct bmi160_dev* dev) { + int8_t rslt; + + /* Enable Gyro self test bit */ + rslt = enable_gyro_self_test(dev); + if(rslt == BMI160_OK) { + /* Validate the gyro self test a delay of 50ms */ + dev->delay_ms(50); + + /* Validate the gyro self test results */ + rslt = validate_gyro_self_test(dev); + } + + return rslt; +} + +/*! + * @brief This API enables the self test bit to trigger self test for Gyro + */ +static int8_t enable_gyro_self_test(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Enable the Gyro self test bit to trigger the self test */ + rslt = bmi160_get_regs(BMI160_SELF_TEST_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + reg_data = BMI160_SET_BITS(reg_data, BMI160_GYRO_SELF_TEST, 1); + rslt = bmi160_set_regs(BMI160_SELF_TEST_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + /* Delay to enable gyro self test */ + dev->delay_ms(15); + } + } + + return rslt; +} + +/*! + * @brief This API validates the self test results of Gyro + */ +static int8_t validate_gyro_self_test(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Validate the Gyro self test result */ + rslt = bmi160_get_regs(BMI160_STATUS_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + reg_data = BMI160_GET_BITS(reg_data, BMI160_GYRO_SELF_TEST_STATUS); + if(reg_data == BMI160_ENABLE) { + /* Gyro self test success case */ + rslt = BMI160_OK; + } else { + rslt = BMI160_W_GYRO_SELF_TEST_FAIL; + } + } + + return rslt; +} + +/*! + * @brief This API sets FIFO full interrupt of the sensor.This interrupt + * occurs when the FIFO is full and the next full data sample would cause + * a FIFO overflow, which may delete the old samples. + */ +static int8_t + set_fifo_full_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /*enable the fifo full interrupt */ + rslt = enable_fifo_full_int(int_config, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_hardware_interrupt(int_config, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This enable the FIFO full interrupt engine. + */ +static int8_t + enable_fifo_full_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + data = BMI160_SET_BITS(data, BMI160_FIFO_FULL_INT, int_config->fifo_full_int_en); + + /* Writing data to INT ENABLE 1 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API sets FIFO watermark interrupt of the sensor.The FIFO + * watermark interrupt is fired, when the FIFO fill level is above a fifo + * watermark. + */ +static int8_t set_fifo_watermark_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Enable fifo-watermark interrupt in Int Enable 1 register */ + rslt = enable_fifo_wtm_int(int_config, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_hardware_interrupt(int_config, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This enable the FIFO watermark interrupt engine. + */ +static int8_t + enable_fifo_wtm_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + data = BMI160_SET_BITS(data, BMI160_FIFO_WTM_INT, int_config->fifo_wtm_int_en); + + /* Writing data to INT ENABLE 1 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API is used to reset the FIFO related configurations + * in the fifo_frame structure. + */ +static void reset_fifo_data_structure(const struct bmi160_dev* dev) { + /*Prepare for next FIFO read by resetting FIFO's + * internal data structures*/ + dev->fifo->accel_byte_start_idx = 0; + dev->fifo->gyro_byte_start_idx = 0; + dev->fifo->aux_byte_start_idx = 0; + dev->fifo->sensor_time = 0; + dev->fifo->skipped_frame_count = 0; +} + +/*! + * @brief This API is used to read fifo_byte_counter value (i.e) + * current fill-level in Fifo buffer. + */ +static int8_t get_fifo_byte_counter(uint16_t* bytes_to_read, struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint8_t data[2]; + uint8_t addr = BMI160_FIFO_LENGTH_ADDR; + + rslt |= bmi160_get_regs(addr, data, 2, dev); + data[1] = data[1] & BMI160_FIFO_BYTE_COUNTER_MASK; + + /* Available data in FIFO is stored in bytes_to_read*/ + *bytes_to_read = (((uint16_t)data[1] << 8) | ((uint16_t)data[0])); + + return rslt; +} + +/*! + * @brief This API is used to compute the number of bytes of accel FIFO data + * which is to be parsed in header-less mode + */ +static void get_accel_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* acc_frame_count, + const struct bmi160_dev* dev) { + /* Data start index */ + *data_index = dev->fifo->accel_byte_start_idx; + if(dev->fifo->fifo_data_enable == BMI160_FIFO_A_ENABLE) { + *data_read_length = (*acc_frame_count) * BMI160_FIFO_A_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_G_A_ENABLE) { + *data_read_length = (*acc_frame_count) * BMI160_FIFO_GA_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_A_ENABLE) { + *data_read_length = (*acc_frame_count) * BMI160_FIFO_MA_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_A_ENABLE) { + *data_read_length = (*acc_frame_count) * BMI160_FIFO_MGA_LENGTH; + } else { + /* When accel is not enabled ,there will be no accel data. + * so we update the data index as complete */ + *data_index = dev->fifo->length; + } + + if(*data_read_length > dev->fifo->length) { + /* Handling the case where more data is requested + * than that is available*/ + *data_read_length = dev->fifo->length; + } +} + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed. + */ +static void unpack_accel_frame( + struct bmi160_sensor_data* acc, + uint16_t* idx, + uint8_t* acc_idx, + uint8_t frame_info, + const struct bmi160_dev* dev) { + switch(frame_info) { + case BMI160_FIFO_HEAD_A: + case BMI160_FIFO_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_A_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into the structure instance "acc" */ + unpack_accel_data(&acc[*acc_idx], *idx, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_A_LENGTH; + (*acc_idx)++; + break; + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_GA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "acc"*/ + unpack_accel_data(&acc[*acc_idx], *idx + BMI160_FIFO_G_LENGTH, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_GA_LENGTH; + (*acc_idx)++; + break; + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_M_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "acc"*/ + unpack_accel_data(&acc[*acc_idx], *idx + BMI160_FIFO_M_LENGTH, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_MA_LENGTH; + (*acc_idx)++; + break; + case BMI160_FIFO_HEAD_M_G_A: + case BMI160_FIFO_M_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MGA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "acc"*/ + unpack_accel_data(&acc[*acc_idx], *idx + BMI160_FIFO_MG_LENGTH, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_MGA_LENGTH; + (*acc_idx)++; + break; + case BMI160_FIFO_HEAD_M: + case BMI160_FIFO_M_ENABLE: + (*idx) = (*idx) + BMI160_FIFO_M_LENGTH; + break; + case BMI160_FIFO_HEAD_G: + case BMI160_FIFO_G_ENABLE: + (*idx) = (*idx) + BMI160_FIFO_G_LENGTH; + break; + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_M_G_ENABLE: + (*idx) = (*idx) + BMI160_FIFO_MG_LENGTH; + break; + default: + break; + } +} + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data and store it in the instance of the structure bmi160_sensor_data. + */ +static void unpack_accel_data( + struct bmi160_sensor_data* accel_data, + uint16_t data_start_index, + const struct bmi160_dev* dev) { + uint16_t data_lsb; + uint16_t data_msb; + + /* Accel raw x data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + accel_data->x = (int16_t)((data_msb << 8) | data_lsb); + + /* Accel raw y data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + accel_data->y = (int16_t)((data_msb << 8) | data_lsb); + + /* Accel raw z data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + accel_data->z = (int16_t)((data_msb << 8) | data_lsb); +} + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data in header mode. + */ +static void extract_accel_header_mode( + struct bmi160_sensor_data* accel_data, + uint8_t* accel_length, + const struct bmi160_dev* dev) { + uint8_t frame_header = 0; + uint16_t data_index; + uint8_t accel_index = 0; + + for(data_index = dev->fifo->accel_byte_start_idx; data_index < dev->fifo->length;) { + /* extracting Frame header */ + frame_header = (dev->fifo->data[data_index] & BMI160_FIFO_TAG_INTR_MASK); + + /*Index is moved to next byte where the data is starting*/ + data_index++; + switch(frame_header) { + /* Accel frame */ + case BMI160_FIFO_HEAD_A: + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_HEAD_M_G_A: + unpack_accel_frame(accel_data, &data_index, &accel_index, frame_header, dev); + break; + case BMI160_FIFO_HEAD_M: + move_next_frame(&data_index, BMI160_FIFO_M_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_G: + move_next_frame(&data_index, BMI160_FIFO_G_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_M_G: + move_next_frame(&data_index, BMI160_FIFO_MG_LENGTH, dev); + break; + + /* Sensor time frame */ + case BMI160_FIFO_HEAD_SENSOR_TIME: + unpack_sensortime_frame(&data_index, dev); + break; + + /* Skip frame */ + case BMI160_FIFO_HEAD_SKIP_FRAME: + unpack_skipped_frame(&data_index, dev); + break; + + /* Input config frame */ + case BMI160_FIFO_HEAD_INPUT_CONFIG: + move_next_frame(&data_index, 1, dev); + break; + case BMI160_FIFO_HEAD_OVER_READ: + + /* Update the data index as complete in case of Over read */ + data_index = dev->fifo->length; + break; + default: + break; + } + if(*accel_length == accel_index) { + /* Number of frames to read completed */ + break; + } + } + + /*Update number of accel data read*/ + *accel_length = accel_index; + + /*Update the accel frame index*/ + dev->fifo->accel_byte_start_idx = data_index; +} + +/*! + * @brief This API computes the number of bytes of gyro FIFO data + * which is to be parsed in header-less mode + */ +static void get_gyro_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* gyro_frame_count, + const struct bmi160_dev* dev) { + /* Data start index */ + *data_index = dev->fifo->gyro_byte_start_idx; + if(dev->fifo->fifo_data_enable == BMI160_FIFO_G_ENABLE) { + *data_read_length = (*gyro_frame_count) * BMI160_FIFO_G_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_G_A_ENABLE) { + *data_read_length = (*gyro_frame_count) * BMI160_FIFO_GA_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_ENABLE) { + *data_read_length = (*gyro_frame_count) * BMI160_FIFO_MG_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_A_ENABLE) { + *data_read_length = (*gyro_frame_count) * BMI160_FIFO_MGA_LENGTH; + } else { + /* When gyro is not enabled ,there will be no gyro data. + * so we update the data index as complete */ + *data_index = dev->fifo->length; + } + + if(*data_read_length > dev->fifo->length) { + /* Handling the case where more data is requested + * than that is available*/ + *data_read_length = dev->fifo->length; + } +} + +/*! + * @brief This API is used to parse the gyroscope's data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed. + */ +static void unpack_gyro_frame( + struct bmi160_sensor_data* gyro, + uint16_t* idx, + uint8_t* gyro_idx, + uint8_t frame_info, + const struct bmi160_dev* dev) { + switch(frame_info) { + case BMI160_FIFO_HEAD_G: + case BMI160_FIFO_G_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_G_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "gyro"*/ + unpack_gyro_data(&gyro[*gyro_idx], *idx, dev); + + /*Move the data index*/ + (*idx) = (*idx) + BMI160_FIFO_G_LENGTH; + (*gyro_idx)++; + break; + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_GA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance "gyro" */ + unpack_gyro_data(&gyro[*gyro_idx], *idx, dev); + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_GA_LENGTH; + (*gyro_idx)++; + break; + case BMI160_FIFO_HEAD_M_G_A: + case BMI160_FIFO_M_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MGA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "gyro"*/ + unpack_gyro_data(&gyro[*gyro_idx], *idx + BMI160_FIFO_M_LENGTH, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_MGA_LENGTH; + (*gyro_idx)++; + break; + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_M_A_ENABLE: + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_MA_LENGTH; + break; + case BMI160_FIFO_HEAD_M: + case BMI160_FIFO_M_ENABLE: + (*idx) = (*idx) + BMI160_FIFO_M_LENGTH; + break; + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_M_G_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MG_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "gyro"*/ + unpack_gyro_data(&gyro[*gyro_idx], *idx + BMI160_FIFO_M_LENGTH, dev); + + /*Move the data index*/ + (*idx) = (*idx) + BMI160_FIFO_MG_LENGTH; + (*gyro_idx)++; + break; + case BMI160_FIFO_HEAD_A: + case BMI160_FIFO_A_ENABLE: + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_A_LENGTH; + break; + default: + break; + } +} + +/*! + * @brief This API is used to parse the gyro data from the + * FIFO data and store it in the instance of the structure bmi160_sensor_data. + */ +static void unpack_gyro_data( + struct bmi160_sensor_data* gyro_data, + uint16_t data_start_index, + const struct bmi160_dev* dev) { + uint16_t data_lsb; + uint16_t data_msb; + + /* Gyro raw x data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + gyro_data->x = (int16_t)((data_msb << 8) | data_lsb); + + /* Gyro raw y data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + gyro_data->y = (int16_t)((data_msb << 8) | data_lsb); + + /* Gyro raw z data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + gyro_data->z = (int16_t)((data_msb << 8) | data_lsb); +} + +/*! + * @brief This API is used to parse the gyro data from the + * FIFO data in header mode. + */ +static void extract_gyro_header_mode( + struct bmi160_sensor_data* gyro_data, + uint8_t* gyro_length, + const struct bmi160_dev* dev) { + uint8_t frame_header = 0; + uint16_t data_index; + uint8_t gyro_index = 0; + + for(data_index = dev->fifo->gyro_byte_start_idx; data_index < dev->fifo->length;) { + /* extracting Frame header */ + frame_header = (dev->fifo->data[data_index] & BMI160_FIFO_TAG_INTR_MASK); + + /*Index is moved to next byte where the data is starting*/ + data_index++; + switch(frame_header) { + /* GYRO frame */ + case BMI160_FIFO_HEAD_G: + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_HEAD_M_G_A: + unpack_gyro_frame(gyro_data, &data_index, &gyro_index, frame_header, dev); + break; + case BMI160_FIFO_HEAD_A: + move_next_frame(&data_index, BMI160_FIFO_A_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_M: + move_next_frame(&data_index, BMI160_FIFO_M_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_M_A: + move_next_frame(&data_index, BMI160_FIFO_M_LENGTH, dev); + break; + + /* Sensor time frame */ + case BMI160_FIFO_HEAD_SENSOR_TIME: + unpack_sensortime_frame(&data_index, dev); + break; + + /* Skip frame */ + case BMI160_FIFO_HEAD_SKIP_FRAME: + unpack_skipped_frame(&data_index, dev); + break; + + /* Input config frame */ + case BMI160_FIFO_HEAD_INPUT_CONFIG: + move_next_frame(&data_index, 1, dev); + break; + case BMI160_FIFO_HEAD_OVER_READ: + + /* Update the data index as complete in case of over read */ + data_index = dev->fifo->length; + break; + default: + break; + } + if(*gyro_length == gyro_index) { + /*Number of frames to read completed*/ + break; + } + } + + /*Update number of gyro data read*/ + *gyro_length = gyro_index; + + /*Update the gyro frame index*/ + dev->fifo->gyro_byte_start_idx = data_index; +} + +/*! + * @brief This API computes the number of bytes of aux FIFO data + * which is to be parsed in header-less mode + */ +static void get_aux_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* aux_frame_count, + const struct bmi160_dev* dev) { + /* Data start index */ + *data_index = dev->fifo->gyro_byte_start_idx; + if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_ENABLE) { + *data_read_length = (*aux_frame_count) * BMI160_FIFO_M_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_A_ENABLE) { + *data_read_length = (*aux_frame_count) * BMI160_FIFO_MA_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_ENABLE) { + *data_read_length = (*aux_frame_count) * BMI160_FIFO_MG_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_A_ENABLE) { + *data_read_length = (*aux_frame_count) * BMI160_FIFO_MGA_LENGTH; + } else { + /* When aux is not enabled ,there will be no aux data. + * so we update the data index as complete */ + *data_index = dev->fifo->length; + } + + if(*data_read_length > dev->fifo->length) { + /* Handling the case where more data is requested + * than that is available */ + *data_read_length = dev->fifo->length; + } +} + +/*! + * @brief This API is used to parse the aux's data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed + */ +static void unpack_aux_frame( + struct bmi160_aux_data* aux_data, + uint16_t* idx, + uint8_t* aux_index, + uint8_t frame_info, + const struct bmi160_dev* dev) { + switch(frame_info) { + case BMI160_FIFO_HEAD_M: + case BMI160_FIFO_M_ENABLE: + + /* Partial read, then skip the data */ + if((*idx + BMI160_FIFO_M_LENGTH) > dev->fifo->length) { + /* Update the data index as complete */ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance */ + unpack_aux_data(&aux_data[*aux_index], *idx, dev); + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_M_LENGTH; + (*aux_index)++; + break; + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_M_A_ENABLE: + + /* Partial read, then skip the data */ + if((*idx + BMI160_FIFO_MA_LENGTH) > dev->fifo->length) { + /* Update the data index as complete */ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance */ + unpack_aux_data(&aux_data[*aux_index], *idx, dev); + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_MA_LENGTH; + (*aux_index)++; + break; + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_M_G_ENABLE: + + /* Partial read, then skip the data */ + if((*idx + BMI160_FIFO_MG_LENGTH) > dev->fifo->length) { + /* Update the data index as complete */ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance */ + unpack_aux_data(&aux_data[*aux_index], *idx, dev); + + /* Move the data index */ + (*idx) = (*idx) + BMI160_FIFO_MG_LENGTH; + (*aux_index)++; + break; + case BMI160_FIFO_HEAD_M_G_A: + case BMI160_FIFO_M_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MGA_LENGTH) > dev->fifo->length) { + /* Update the data index as complete */ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance */ + unpack_aux_data(&aux_data[*aux_index], *idx, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_MGA_LENGTH; + (*aux_index)++; + break; + case BMI160_FIFO_HEAD_G: + case BMI160_FIFO_G_ENABLE: + + /* Move the data index */ + (*idx) = (*idx) + BMI160_FIFO_G_LENGTH; + break; + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_G_A_ENABLE: + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_GA_LENGTH; + break; + case BMI160_FIFO_HEAD_A: + case BMI160_FIFO_A_ENABLE: + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_A_LENGTH; + break; + default: + break; + } +} + +/*! + * @brief This API is used to parse the aux data from the + * FIFO data and store it in the instance of the structure bmi160_aux_data. + */ +static void unpack_aux_data( + struct bmi160_aux_data* aux_data, + uint16_t data_start_index, + const struct bmi160_dev* dev) { + /* Aux data bytes */ + aux_data->data[0] = dev->fifo->data[data_start_index++]; + aux_data->data[1] = dev->fifo->data[data_start_index++]; + aux_data->data[2] = dev->fifo->data[data_start_index++]; + aux_data->data[3] = dev->fifo->data[data_start_index++]; + aux_data->data[4] = dev->fifo->data[data_start_index++]; + aux_data->data[5] = dev->fifo->data[data_start_index++]; + aux_data->data[6] = dev->fifo->data[data_start_index++]; + aux_data->data[7] = dev->fifo->data[data_start_index++]; +} + +/*! + * @brief This API is used to parse the aux data from the + * FIFO data in header mode. + */ +static void extract_aux_header_mode( + struct bmi160_aux_data* aux_data, + uint8_t* aux_length, + const struct bmi160_dev* dev) { + uint8_t frame_header = 0; + uint16_t data_index; + uint8_t aux_index = 0; + + for(data_index = dev->fifo->aux_byte_start_idx; data_index < dev->fifo->length;) { + /* extracting Frame header */ + frame_header = (dev->fifo->data[data_index] & BMI160_FIFO_TAG_INTR_MASK); + + /*Index is moved to next byte where the data is starting*/ + data_index++; + switch(frame_header) { + /* Aux frame */ + case BMI160_FIFO_HEAD_M: + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_HEAD_M_G_A: + unpack_aux_frame(aux_data, &data_index, &aux_index, frame_header, dev); + break; + case BMI160_FIFO_HEAD_G: + move_next_frame(&data_index, BMI160_FIFO_G_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_G_A: + move_next_frame(&data_index, BMI160_FIFO_GA_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_A: + move_next_frame(&data_index, BMI160_FIFO_A_LENGTH, dev); + break; + + /* Sensor time frame */ + case BMI160_FIFO_HEAD_SENSOR_TIME: + unpack_sensortime_frame(&data_index, dev); + break; + + /* Skip frame */ + case BMI160_FIFO_HEAD_SKIP_FRAME: + unpack_skipped_frame(&data_index, dev); + break; + + /* Input config frame */ + case BMI160_FIFO_HEAD_INPUT_CONFIG: + move_next_frame(&data_index, 1, dev); + break; + case BMI160_FIFO_HEAD_OVER_READ: + + /* Update the data index as complete in case + * of over read */ + data_index = dev->fifo->length; + break; + default: + + /* Update the data index as complete in case of + * getting other headers like 0x00 */ + data_index = dev->fifo->length; + break; + } + if(*aux_length == aux_index) { + /*Number of frames to read completed*/ + break; + } + } + + /* Update number of aux data read */ + *aux_length = aux_index; + + /* Update the aux frame index */ + dev->fifo->aux_byte_start_idx = data_index; +} + +/*! + * @brief This API checks the presence of non-valid frames in the read fifo data. + */ +static void check_frame_validity(uint16_t* data_index, const struct bmi160_dev* dev) { + if((*data_index + 2) < dev->fifo->length) { + /* Check if FIFO is empty */ + if((dev->fifo->data[*data_index] == FIFO_CONFIG_MSB_CHECK) && + (dev->fifo->data[*data_index + 1] == FIFO_CONFIG_LSB_CHECK)) { + /*Update the data index as complete*/ + *data_index = dev->fifo->length; + } + } +} + +/*! + * @brief This API is used to move the data index ahead of the + * current_frame_length parameter when unnecessary FIFO data appears while + * extracting the user specified data. + */ +static void move_next_frame( + uint16_t* data_index, + uint8_t current_frame_length, + const struct bmi160_dev* dev) { + /*Partial read, then move the data index to last data*/ + if((*data_index + current_frame_length) > dev->fifo->length) { + /*Update the data index as complete*/ + *data_index = dev->fifo->length; + } else { + /*Move the data index to next frame*/ + *data_index = *data_index + current_frame_length; + } +} + +/*! + * @brief This API is used to parse and store the sensor time from the + * FIFO data in the structure instance dev. + */ +static void unpack_sensortime_frame(uint16_t* data_index, const struct bmi160_dev* dev) { + uint32_t sensor_time_byte3 = 0; + uint16_t sensor_time_byte2 = 0; + uint8_t sensor_time_byte1 = 0; + + /*Partial read, then move the data index to last data*/ + if((*data_index + BMI160_SENSOR_TIME_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *data_index = dev->fifo->length; + } else { + sensor_time_byte3 = dev->fifo->data[(*data_index) + BMI160_SENSOR_TIME_MSB_BYTE] << 16; + sensor_time_byte2 = dev->fifo->data[(*data_index) + BMI160_SENSOR_TIME_XLSB_BYTE] << 8; + sensor_time_byte1 = dev->fifo->data[(*data_index)]; + + /* Sensor time */ + dev->fifo->sensor_time = + (uint32_t)(sensor_time_byte3 | sensor_time_byte2 | sensor_time_byte1); + *data_index = (*data_index) + BMI160_SENSOR_TIME_LENGTH; + } +} + +/*! + * @brief This API is used to parse and store the skipped_frame_count from + * the FIFO data in the structure instance dev. + */ +static void unpack_skipped_frame(uint16_t* data_index, const struct bmi160_dev* dev) { + /*Partial read, then move the data index to last data*/ + if(*data_index >= dev->fifo->length) { + /*Update the data index as complete*/ + *data_index = dev->fifo->length; + } else { + dev->fifo->skipped_frame_count = dev->fifo->data[*data_index]; + + /*Move the data index*/ + *data_index = (*data_index) + 1; + } +} + +/*! + * @brief This API is used to get the FOC status from the sensor + */ +static int8_t get_foc_status(uint8_t* foc_status, struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data; + + /* Read the FOC status from sensor */ + rslt = bmi160_get_regs(BMI160_STATUS_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Get the foc_status bit */ + *foc_status = BMI160_GET_BITS(data, BMI160_FOC_STATUS); + } + + return rslt; +} + +/*! + * @brief This API is used to configure the offset enable bits in the sensor + */ +static int8_t + configure_offset_enable(const struct bmi160_foc_conf* foc_conf, struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Read the FOC config from the sensor */ + rslt = bmi160_get_regs(BMI160_OFFSET_CONF_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Set the offset enable/disable for gyro */ + data = BMI160_SET_BITS(data, BMI160_GYRO_OFFSET_EN, foc_conf->gyro_off_en); + + /* Set the offset enable/disable for accel */ + data = BMI160_SET_BITS(data, BMI160_ACCEL_OFFSET_EN, foc_conf->acc_off_en); + + /* Set the offset config in the sensor */ + rslt = bmi160_set_regs(BMI160_OFFSET_CONF_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +static int8_t trigger_foc(struct bmi160_offsets* offset, struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t foc_status = BMI160_ENABLE; + uint8_t cmd = BMI160_START_FOC_CMD; + uint8_t timeout = 0; + uint8_t data_array[20]; + + /* Start the FOC process */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &cmd, 1, dev); + if(rslt == BMI160_OK) { + /* Check the FOC status*/ + rslt = get_foc_status(&foc_status, dev); + + if((rslt != BMI160_OK) || (foc_status != BMI160_ENABLE)) { + while((foc_status != BMI160_ENABLE) && (timeout < 11)) { + /* Maximum time of 250ms is given in 10 + * steps of 25ms each - 250ms refer datasheet 2.9.1 */ + dev->delay_ms(25); + + /* Check the FOC status*/ + rslt = get_foc_status(&foc_status, dev); + timeout++; + } + + if((rslt == BMI160_OK) && (foc_status == BMI160_ENABLE)) { + /* Get offset values from sensor */ + rslt = bmi160_get_offsets(offset, dev); + } else { + /* FOC failure case */ + rslt = BMI160_E_FOC_FAILURE; + } + } + + if(rslt == BMI160_OK) { + /* Read registers 0x04-0x17 */ + rslt = bmi160_get_regs(BMI160_GYRO_DATA_ADDR, data_array, 20, dev); + } + } + + return rslt; +} diff --git a/applications/plugins/airmouse/tracking/imu/bmi160.h b/applications/plugins/airmouse/tracking/imu/bmi160.h new file mode 100644 index 000000000..d4d98094c --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/bmi160.h @@ -0,0 +1,992 @@ +/** +* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved. +* +* BSD-3-Clause +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +* @file bmi160.h +* @date 2021-10-05 +* @version v3.9.2 +* +*/ + +/*! + * @defgroup bmi160 BMI160 + */ + +#ifndef BMI160_H_ +#define BMI160_H_ + +/*************************** C++ guard macro *****************************/ +#ifdef __cplusplus +extern "C" { +#endif + +#include "bmi160_defs.h" +#ifdef __KERNEL__ +#include +#else +#include +#include +#include +#endif + +/*********************** User function prototypes ************************/ + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiInit Initialization + * @brief Initialize the sensor and device structure + */ + +/*! + * \ingroup bmi160ApiInit + * \page bmi160_api_bmi160_init bmi160_init + * \code + * int8_t bmi160_init(struct bmi160_dev *dev); + * \endcode + * @details This API is the entry point for sensor.It performs + * the selection of I2C/SPI read mechanism according to the + * selected interface and reads the chip-id of bmi160 sensor. + * + * @param[in,out] dev : Structure instance of bmi160_dev + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_init(struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiRegs Registers + * @brief Read data from the given register address of sensor + */ + +/*! + * \ingroup bmi160ApiRegs + * \page bmi160_api_bmi160_get_regs bmi160_get_regs + * \code + * int8_t bmi160_get_regs(uint8_t reg_addr, uint8_t *data, uint16_t len, const struct bmi160_dev *dev); + * \endcode + * @details This API reads the data from the given register address of sensor. + * + * @param[in] reg_addr : Register address from where the data to be read + * @param[out] data : Pointer to data buffer to store the read data. + * @param[in] len : No of bytes of data to be read. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note For most of the registers auto address increment applies, with the + * exception of a few special registers, which trap the address. For e.g., + * Register address - 0x24(BMI160_FIFO_DATA_ADDR) + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t + bmi160_get_regs(uint8_t reg_addr, uint8_t* data, uint16_t len, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiRegs + * \page bmi160_api_bmi160_set_regs bmi160_set_regs + * \code + * int8_t bmi160_set_regs(uint8_t reg_addr, uint8_t *data, uint16_t len, const struct bmi160_dev *dev); + * \endcode + * @details This API writes the given data to the register address + * of sensor. + * + * @param[in] reg_addr : Register address from where the data to be written. + * @param[in] data : Pointer to data buffer which is to be written + * in the sensor. + * @param[in] len : No of bytes of data to write.. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t + bmi160_set_regs(uint8_t reg_addr, uint8_t* data, uint16_t len, const struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiSoftreset Soft reset + * @brief Perform soft reset of the sensor + */ + +/*! + * \ingroup bmi160ApiSoftreset + * \page bmi160_api_bmi160_soft_reset bmi160_soft_reset + * \code + * int8_t bmi160_soft_reset(struct bmi160_dev *dev); + * \endcode + * @details This API resets and restarts the device. + * All register values are overwritten with default parameters. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_soft_reset(struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiConfig Configuration + * @brief Configuration of the sensor + */ + +/*! + * \ingroup bmi160ApiConfig + * \page bmi160_api_bmi160_set_sens_conf bmi160_set_sens_conf + * \code + * int8_t bmi160_set_sens_conf(struct bmi160_dev *dev); + * \endcode + * @details This API configures the power mode, range and bandwidth + * of sensor. + * + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_sens_conf(struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiConfig + * \page bmi160_api_bmi160_get_sens_conf bmi160_get_sens_conf + * \code + * int8_t bmi160_get_sens_conf(struct bmi160_dev *dev); + * \endcode + * @details This API gets accel and gyro configurations. + * + * @param[out] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_get_sens_conf(struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiPowermode Power mode + * @brief Set / Get power mode of the sensor + */ + +/*! + * \ingroup bmi160ApiPowermode + * \page bmi160_api_bmi160_set_power_mode bmi160_set_power_mode + * \code + * int8_t bmi160_set_power_mode(struct bmi160_dev *dev); + * \endcode + * @details This API sets the power mode of the sensor. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_power_mode(struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiPowermode + * \page bmi160_api_bmi160_get_power_mode bmi160_get_power_mode + * \code + * int8_t bmi160_get_power_mode(struct bmi160_dev *dev); + * \endcode + * @details This API gets the power mode of the sensor. + * + * @param[in] dev : Structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_get_power_mode(struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiData Sensor Data + * @brief Read sensor data + */ + +/*! + * \ingroup bmi160ApiData + * \page bmi160_api_bmi160_get_sensor_data bmi160_get_sensor_data + * \code + * int8_t bmi160_get_sensor_data(uint8_t select_sensor, + * struct bmi160_sensor_data *accel, + * struct bmi160_sensor_data *gyro, + * const struct bmi160_dev *dev); + * + * \endcode + * @details This API reads sensor data, stores it in + * the bmi160_sensor_data structure pointer passed by the user. + * The user can ask for accel data ,gyro data or both sensor + * data using bmi160_select_sensor enum + * + * @param[in] select_sensor : enum to choose accel,gyro or both sensor data + * @param[out] accel : Structure pointer to store accel data + * @param[out] gyro : Structure pointer to store gyro data + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_get_sensor_data( + uint8_t select_sensor, + struct bmi160_sensor_data* accel, + struct bmi160_sensor_data* gyro, + const struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiInt Interrupt configuration + * @brief Set interrupt configuration of the sensor + */ + +/*! + * \ingroup bmi160ApiInt + * \page bmi160_api_bmi160_set_int_config bmi160_set_int_config + * \code + * int8_t bmi160_set_int_config(struct bmi160_int_settg *int_config, struct bmi160_dev *dev); + * \endcode + * @details This API configures the necessary interrupt based on + * the user settings in the bmi160_int_settg structure instance. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_int_config(struct bmi160_int_settg* int_config, struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiStepC Step counter + * @brief Step counter operations + */ + +/*! + * \ingroup bmi160ApiStepC + * \page bmi160_api_bmi160_set_step_counter bmi160_set_step_counter + * \code + * int8_t bmi160_set_step_counter(uint8_t step_cnt_enable, const struct bmi160_dev *dev); + * \endcode + * @details This API enables the step counter feature. + * + * @param[in] step_cnt_enable : value to enable or disable + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_step_counter(uint8_t step_cnt_enable, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiStepC + * \page bmi160_api_bmi160_read_step_counter bmi160_read_step_counter + * \code + * int8_t bmi160_read_step_counter(uint16_t *step_val, const struct bmi160_dev *dev); + * \endcode + * @details This API reads the step counter value. + * + * @param[in] step_val : Pointer to store the step counter value. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_read_step_counter(uint16_t* step_val, const struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiAux Auxiliary sensor + * @brief Auxiliary sensor operations + */ + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_aux_read bmi160_aux_read + * \code + * int8_t bmi160_aux_read(uint8_t reg_addr, uint8_t *aux_data, uint16_t len, const struct bmi160_dev *dev); + * \endcode + * @details This API reads the mention no of byte of data from the given + * register address of auxiliary sensor. + * + * @param[in] reg_addr : Address of register to read. + * @param[in] aux_data : Pointer to store the read data. + * @param[in] len : No of bytes to read. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_aux_read( + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_aux_write bmi160_aux_write + * \code + * int8_t bmi160_aux_write(uint8_t reg_addr, uint8_t *aux_data, uint16_t len, const struct bmi160_dev *dev); + * \endcode + * @details This API writes the mention no of byte of data to the given + * register address of auxiliary sensor. + * + * @param[in] reg_addr : Address of register to write. + * @param[in] aux_data : Pointer to write data. + * @param[in] len : No of bytes to write. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_aux_write( + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_aux_init bmi160_aux_init + * \code + * int8_t bmi160_aux_init(const struct bmi160_dev *dev); + * \endcode + * @details This API initialize the auxiliary sensor + * in order to access it. + * + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_aux_init(const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_set_aux_auto_mode bmi160_set_aux_auto_mode + * \code + * int8_t bmi160_set_aux_auto_mode(uint8_t *data_addr, struct bmi160_dev *dev); + * \endcode + * @details This API is used to setup the auxiliary sensor of bmi160 in auto mode + * Thus enabling the auto update of 8 bytes of data from auxiliary sensor + * to BMI160 register address 0x04 to 0x0B + * + * @param[in] data_addr : Starting address of aux. sensor's data register + * (BMI160 registers 0x04 to 0x0B will be updated + * with 8 bytes of data from auxiliary sensor + * starting from this register address.) + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note : Set the value of auxiliary polling rate by setting + * dev->aux_cfg.aux_odr to the required value from the table + * before calling this API + * + *@verbatim + * dev->aux_cfg.aux_odr | Auxiliary ODR (Hz) + * -----------------------|----------------------- + * BMI160_AUX_ODR_0_78HZ | 25/32 + * BMI160_AUX_ODR_1_56HZ | 25/16 + * BMI160_AUX_ODR_3_12HZ | 25/8 + * BMI160_AUX_ODR_6_25HZ | 25/4 + * BMI160_AUX_ODR_12_5HZ | 25/2 + * BMI160_AUX_ODR_25HZ | 25 + * BMI160_AUX_ODR_50HZ | 50 + * BMI160_AUX_ODR_100HZ | 100 + * BMI160_AUX_ODR_200HZ | 200 + * BMI160_AUX_ODR_400HZ | 400 + * BMI160_AUX_ODR_800HZ | 800 + *@endverbatim + * + * @note : Other values of dev->aux_cfg.aux_odr are reserved and not for use + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_aux_auto_mode(uint8_t* data_addr, struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_config_aux_mode bmi160_config_aux_mode + * \code + * int8_t bmi160_config_aux_mode(const struct bmi160_dev *dev); + * \endcode + * @details This API configures the 0x4C register and settings like + * Auxiliary sensor manual enable/ disable and aux burst read length. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_config_aux_mode(const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_read_aux_data_auto_mode bmi160_read_aux_data_auto_mode + * \code + * int8_t bmi160_read_aux_data_auto_mode(uint8_t *aux_data, const struct bmi160_dev *dev); + * \endcode + * @details This API is used to read the raw uncompensated auxiliary sensor + * data of 8 bytes from BMI160 register address 0x04 to 0x0B + * + * @param[in] aux_data : Pointer to user array of length 8 bytes + * Ensure that the aux_data array is of + * length 8 bytes + * @param[in] dev : Structure instance of bmi160_dev + * + * @retval zero -> Success / -ve value -> Error + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_read_aux_data_auto_mode(uint8_t* aux_data, const struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiSelfTest Self test + * @brief Perform self test of the sensor + */ + +/*! + * \ingroup bmi160ApiSelfTest + * \page bmi160_api_bmi160_perform_self_test bmi160_perform_self_test + * \code + * int8_t bmi160_perform_self_test(uint8_t select_sensor, struct bmi160_dev *dev); + * \endcode + * @details This is used to perform self test of accel/gyro of the BMI160 sensor + * + * @param[in] select_sensor : enum to choose accel or gyro for self test + * @param[in] dev : Structure instance of bmi160_dev + * + * @note self test can be performed either for accel/gyro at any instant. + * + *@verbatim + * value of select_sensor | Inference + *----------------------------------|-------------------------------- + * BMI160_ACCEL_ONLY | Accel self test enabled + * BMI160_GYRO_ONLY | Gyro self test enabled + * BMI160_BOTH_ACCEL_AND_GYRO | NOT TO BE USED + *@endverbatim + * + * @note The return value of this API gives us the result of self test. + * + * @note Performing self test does soft reset of the sensor, User can + * set the desired settings after performing the self test. + * + * @return Result of API execution status + * @retval BMI160_OK Self test success + * @retval BMI160_W_GYRO_SELF_TEST_FAIL Gyro self test fail + * @retval BMI160_W_ACCEl_SELF_TEST_FAIL Accel self test fail + */ +int8_t bmi160_perform_self_test(uint8_t select_sensor, struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiFIFO FIFO + * @brief FIFO operations of the sensor + */ + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_get_fifo_data bmi160_get_fifo_data + * \code + * int8_t bmi160_get_fifo_data(struct bmi160_dev const *dev); + * \endcode + * @details This API reads data from the fifo buffer. + * + * @note User has to allocate the FIFO buffer along with + * corresponding fifo length from his side before calling this API + * as mentioned in the readme.md + * + * @note User must specify the number of bytes to read from the FIFO in + * dev->fifo->length , It will be updated by the number of bytes actually + * read from FIFO after calling this API + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_get_fifo_data(struct bmi160_dev const* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_set_fifo_flush bmi160_set_fifo_flush + * \code + * int8_t bmi160_set_fifo_flush(const struct bmi160_dev *dev); + * \endcode + * @details This API writes fifo_flush command to command register.This + * action clears all data in the Fifo without changing fifo configuration + * settings. + * + * @param[in] dev : Structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_set_fifo_flush(const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_set_fifo_config bmi160_set_fifo_config + * \code + * int8_t bmi160_set_fifo_config(uint8_t config, uint8_t enable, struct bmi160_dev const *dev); + * \endcode + * @details This API sets the FIFO configuration in the sensor. + * + * @param[in] config : variable used to specify the FIFO + * configurations which are to be enabled or disabled in the sensor. + * + * @note : User can set either set one or more or all FIFO configurations + * by ORing the below mentioned macros. + * + *@verbatim + * config | Value + * ------------------------|--------------------------- + * BMI160_FIFO_TIME | 0x02 + * BMI160_FIFO_TAG_INT2 | 0x04 + * BMI160_FIFO_TAG_INT1 | 0x08 + * BMI160_FIFO_HEADER | 0x10 + * BMI160_FIFO_AUX | 0x20 + * BMI160_FIFO_ACCEL | 0x40 + * BMI160_FIFO_GYRO | 0x80 + *@endverbatim + * + * @param[in] enable : Parameter used to enable or disable the above + * FIFO configuration + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return status of bus communication result + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_set_fifo_config(uint8_t config, uint8_t enable, struct bmi160_dev const* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_set_fifo_down bmi160_set_fifo_down + * \code + * int8_t bmi160_set_fifo_down(uint8_t fifo_down, const struct bmi160_dev *dev); + * \endcode + * @details This API is used to configure the down sampling ratios of + * the accel and gyro data for FIFO.Also, it configures filtered or + * pre-filtered data for the fifo for accel and gyro. + * + * @param[in] fifo_down : variable used to specify the FIFO down + * configurations which are to be enabled or disabled in the sensor. + * + * @note The user must select one among the following macros to + * select down-sampling ratio for accel + * + *@verbatim + * config | Value + * -------------------------------------|--------------------------- + * BMI160_ACCEL_FIFO_DOWN_ZERO | 0x00 + * BMI160_ACCEL_FIFO_DOWN_ONE | 0x10 + * BMI160_ACCEL_FIFO_DOWN_TWO | 0x20 + * BMI160_ACCEL_FIFO_DOWN_THREE | 0x30 + * BMI160_ACCEL_FIFO_DOWN_FOUR | 0x40 + * BMI160_ACCEL_FIFO_DOWN_FIVE | 0x50 + * BMI160_ACCEL_FIFO_DOWN_SIX | 0x60 + * BMI160_ACCEL_FIFO_DOWN_SEVEN | 0x70 + *@endverbatim + * + * @note The user must select one among the following macros to + * select down-sampling ratio for gyro + * + *@verbatim + * config | Value + * -------------------------------------|--------------------------- + * BMI160_GYRO_FIFO_DOWN_ZERO | 0x00 + * BMI160_GYRO_FIFO_DOWN_ONE | 0x01 + * BMI160_GYRO_FIFO_DOWN_TWO | 0x02 + * BMI160_GYRO_FIFO_DOWN_THREE | 0x03 + * BMI160_GYRO_FIFO_DOWN_FOUR | 0x04 + * BMI160_GYRO_FIFO_DOWN_FIVE | 0x05 + * BMI160_GYRO_FIFO_DOWN_SIX | 0x06 + * BMI160_GYRO_FIFO_DOWN_SEVEN | 0x07 + *@endverbatim + * + * @note The user can enable filtered accel data by the following macro + * + *@verbatim + * config | Value + * -------------------------------------|--------------------------- + * BMI160_ACCEL_FIFO_FILT_EN | 0x80 + *@endverbatim + * + * @note The user can enable filtered gyro data by the following macro + * + *@verbatim + * config | Value + * -------------------------------------|--------------------------- + * BMI160_GYRO_FIFO_FILT_EN | 0x08 + *@endverbatim + * + * @note : By ORing the above mentioned macros, the user can select + * the required FIFO down config settings + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return status of bus communication result + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_set_fifo_down(uint8_t fifo_down, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_set_fifo_wm bmi160_set_fifo_wm + * \code + * int8_t bmi160_set_fifo_wm(uint8_t fifo_wm, const struct bmi160_dev *dev); + * \endcode + * @details This API sets the FIFO watermark level in the sensor. + * + * @note The FIFO watermark is issued when the FIFO fill level is + * equal or above the watermark level and units of watermark is 4 bytes. + * + * @param[in] fifo_wm : Variable used to set the FIFO water mark level + * @param[in] dev : Structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_set_fifo_wm(uint8_t fifo_wm, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_extract_accel bmi160_extract_accel + * \code + * int8_t bmi160_extract_accel(struct bmi160_sensor_data *accel_data, uint8_t *accel_length, struct bmi160_dev const + **dev); + * \endcode + * @details This API parses and extracts the accelerometer frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "accel_data" structure instance. + * + * @note The bmi160_extract_accel API should be called only after + * reading the FIFO data by calling the bmi160_get_fifo_data() API. + * + * @param[out] accel_data : Structure instance of bmi160_sensor_data + * where the accelerometer data in FIFO is stored. + * @param[in,out] accel_length : Number of valid accelerometer frames + * (x,y,z axes data) read out from fifo. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note accel_length is updated with the number of valid accelerometer + * frames extracted from fifo (1 accel frame = 6 bytes) at the end of + * execution of this API. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_extract_accel( + struct bmi160_sensor_data* accel_data, + uint8_t* accel_length, + struct bmi160_dev const* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_extract_gyro bmi160_extract_gyro + * \code + * int8_t bmi160_extract_gyro(struct bmi160_sensor_data *gyro_data, uint8_t *gyro_length, struct bmi160_dev const *dev); + * \endcode + * @details This API parses and extracts the gyro frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "gyro_data" structure instance. + * + * @note The bmi160_extract_gyro API should be called only after + * reading the FIFO data by calling the bmi160_get_fifo_data() API. + * + * @param[out] gyro_data : Structure instance of bmi160_sensor_data + * where the gyro data in FIFO is stored. + * @param[in,out] gyro_length : Number of valid gyro frames + * (x,y,z axes data) read out from fifo. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note gyro_length is updated with the number of valid gyro + * frames extracted from fifo (1 gyro frame = 6 bytes) at the end of + * execution of this API. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_extract_gyro( + struct bmi160_sensor_data* gyro_data, + uint8_t* gyro_length, + struct bmi160_dev const* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_extract_aux bmi160_extract_aux + * \code + * int8_t bmi160_extract_aux(struct bmi160_aux_data *aux_data, uint8_t *aux_len, struct bmi160_dev const *dev); + * \endcode + * @details This API parses and extracts the aux frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the bmi160_aux_data structure instance. + * + * @note The bmi160_extract_aux API should be called only after + * reading the FIFO data by calling the bmi160_get_fifo_data() API. + * + * @param[out] aux_data : Structure instance of bmi160_aux_data + * where the aux data in FIFO is stored. + * @param[in,out] aux_len : Number of valid aux frames (8bytes) + * read out from FIFO. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note aux_len is updated with the number of valid aux + * frames extracted from fifo (1 aux frame = 8 bytes) at the end of + * execution of this API. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_extract_aux( + struct bmi160_aux_data* aux_data, + uint8_t* aux_len, + struct bmi160_dev const* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiFOC FOC + * @brief Start FOC of accel and gyro sensors + */ + +/*! + * \ingroup bmi160ApiFOC + * \page bmi160_api_bmi160_start_foc bmi160_start_foc + * \code + * int8_t bmi160_start_foc(const struct bmi160_foc_conf *foc_conf, + * \endcode + * @details This API starts the FOC of accel and gyro + * + * @note FOC should not be used in low-power mode of sensor + * + * @note Accel FOC targets values of +1g , 0g , -1g + * Gyro FOC always targets value of 0 dps + * + * @param[in] foc_conf : Structure instance of bmi160_foc_conf which + * has the FOC configuration + * @param[in,out] offset : Structure instance to store Offset + * values read from sensor + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note Pre-requisites for triggering FOC in accel , Set the following, + * Enable the acc_off_en + * Ex : foc_conf.acc_off_en = BMI160_ENABLE; + * + * Set the desired target values of FOC to each axes (x,y,z) by using the + * following macros + * - BMI160_FOC_ACCEL_DISABLED + * - BMI160_FOC_ACCEL_POSITIVE_G + * - BMI160_FOC_ACCEL_NEGATIVE_G + * - BMI160_FOC_ACCEL_0G + * + * Ex : foc_conf.foc_acc_x = BMI160_FOC_ACCEL_0G; + * foc_conf.foc_acc_y = BMI160_FOC_ACCEL_0G; + * foc_conf.foc_acc_z = BMI160_FOC_ACCEL_POSITIVE_G; + * + * @note Pre-requisites for triggering FOC in gyro , + * Set the following parameters, + * + * Ex : foc_conf.foc_gyr_en = BMI160_ENABLE; + * foc_conf.gyro_off_en = BMI160_ENABLE; + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_start_foc( + const struct bmi160_foc_conf* foc_conf, + struct bmi160_offsets* offset, + struct bmi160_dev const* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiOffsets Offsets + * @brief Set / Get offset values of accel and gyro sensors + */ + +/*! + * \ingroup bmi160ApiOffsets + * \page bmi160_api_bmi160_get_offsets bmi160_get_offsets + * \code + * int8_t bmi160_get_offsets(struct bmi160_offsets *offset, const struct bmi160_dev *dev); + * \endcode + * @details This API reads and stores the offset values of accel and gyro + * + * @param[in,out] offset : Structure instance of bmi160_offsets in which + * the offset values are read and stored + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_get_offsets(struct bmi160_offsets* offset, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiOffsets + * \page bmi160_api_bmi160_set_offsets bmi160_set_offsets + * \code + * int8_t bmi160_set_offsets(const struct bmi160_foc_conf *foc_conf, + * const struct bmi160_offsets *offset, + * struct bmi160_dev const *dev); + * \endcode + * @details This API writes the offset values of accel and gyro to + * the sensor but these values will be reset on POR or soft reset. + * + * @param[in] foc_conf : Structure instance of bmi160_foc_conf which + * has the FOC configuration + * @param[in] offset : Structure instance in which user updates offset + * values which are to be written in the sensor + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note Offsets can be set by user like offset->off_acc_x = 10; + * where 1LSB = 3.9mg and for gyro 1LSB = 0.061degrees/second + * + * @note BMI160 offset values for xyz axes of accel should be within range of + * BMI160_ACCEL_MIN_OFFSET (-128) to BMI160_ACCEL_MAX_OFFSET (127) + * + * @note BMI160 offset values for xyz axes of gyro should be within range of + * BMI160_GYRO_MIN_OFFSET (-512) to BMI160_GYRO_MAX_OFFSET (511) + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_set_offsets( + const struct bmi160_foc_conf* foc_conf, + const struct bmi160_offsets* offset, + struct bmi160_dev const* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiNVM NVM + * @brief Write image registers values to NVM + */ + +/*! + * \ingroup bmi160ApiNVM + * \page bmi160_api_bmi160_update_nvm bmi160_update_nvm + * \code + * int8_t bmi160_update_nvm(struct bmi160_dev const *dev); + * \endcode + * @details This API writes the image registers values to NVM which is + * stored even after POR or soft reset + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_update_nvm(struct bmi160_dev const* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiInts Interrupt status + * @brief Read interrupt status from the sensor + */ + +/*! + * \ingroup bmi160ApiInts + * \page bmi160_api_bmi160_get_int_status bmi160_get_int_status + * \code + * int8_t bmi160_get_int_status(enum bmi160_int_status_sel int_status_sel, + * union bmi160_int_status *int_status, + * struct bmi160_dev const *dev); + * \endcode + * @details This API gets the interrupt status from the sensor. + * + * @param[in] int_status_sel : Enum variable to select either individual or all the + * interrupt status bits. + * @param[in] int_status : pointer variable to get the interrupt status + * from the sensor. + * param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_get_int_status( + enum bmi160_int_status_sel int_status_sel, + union bmi160_int_status* int_status, + struct bmi160_dev const* dev); + +/*************************** C++ guard macro *****************************/ +#ifdef __cplusplus +} +#endif + +#endif /* BMI160_H_ */ diff --git a/applications/plugins/airmouse/tracking/imu/bmi160_defs.h b/applications/plugins/airmouse/tracking/imu/bmi160_defs.h new file mode 100644 index 000000000..458ecaad5 --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/bmi160_defs.h @@ -0,0 +1,1619 @@ +/** +* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved. +* +* BSD-3-Clause +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +* @file bmi160_defs.h +* @date 2021-10-05 +* @version v3.9.2 +* +*/ + +#ifndef BMI160_DEFS_H_ +#define BMI160_DEFS_H_ + +/*************************** C types headers *****************************/ +#ifdef __KERNEL__ +#include +#include +#else +#include +#include +#endif + +/*************************** Common macros *****************************/ + +#if !defined(UINT8_C) && !defined(INT8_C) +#define INT8_C(x) S8_C(x) +#define UINT8_C(x) U8_C(x) +#endif + +#if !defined(UINT16_C) && !defined(INT16_C) +#define INT16_C(x) S16_C(x) +#define UINT16_C(x) U16_C(x) +#endif + +#if !defined(INT32_C) && !defined(UINT32_C) +#define INT32_C(x) S32_C(x) +#define UINT32_C(x) U32_C(x) +#endif + +#if !defined(INT64_C) && !defined(UINT64_C) +#define INT64_C(x) S64_C(x) +#define UINT64_C(x) U64_C(x) +#endif + +/**@}*/ +/**\name C standard macros */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void*)0) +#endif +#endif + +/*************************** Sensor macros *****************************/ +/* Test for an endian machine */ +#ifndef __ORDER_LITTLE_ENDIAN__ +#define __ORDER_LITTLE_ENDIAN__ 0 +#endif + +#ifndef __BYTE_ORDER__ +#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ +#endif + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1 +#endif +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 1 +#endif +#else +#error "Code does not support Endian format of the processor" +#endif + +/** Mask definitions */ +#define BMI160_ACCEL_BW_MASK UINT8_C(0x70) +#define BMI160_ACCEL_ODR_MASK UINT8_C(0x0F) +#define BMI160_ACCEL_UNDERSAMPLING_MASK UINT8_C(0x80) +#define BMI160_ACCEL_RANGE_MASK UINT8_C(0x0F) +#define BMI160_GYRO_BW_MASK UINT8_C(0x30) +#define BMI160_GYRO_ODR_MASK UINT8_C(0x0F) +#define BMI160_GYRO_RANGE_MASK UINT8_C(0x07) + +#define BMI160_ACCEL_BW_POS UINT8_C(4) +#define BMI160_GYRO_BW_POS UINT8_C(4) + +/** Mask definitions for INT_EN registers */ +#define BMI160_ANY_MOTION_X_INT_EN_MASK UINT8_C(0x01) +#define BMI160_HIGH_G_X_INT_EN_MASK UINT8_C(0x01) +#define BMI160_NO_MOTION_X_INT_EN_MASK UINT8_C(0x01) +#define BMI160_ANY_MOTION_Y_INT_EN_MASK UINT8_C(0x02) +#define BMI160_HIGH_G_Y_INT_EN_MASK UINT8_C(0x02) +#define BMI160_NO_MOTION_Y_INT_EN_MASK UINT8_C(0x02) +#define BMI160_ANY_MOTION_Z_INT_EN_MASK UINT8_C(0x04) +#define BMI160_HIGH_G_Z_INT_EN_MASK UINT8_C(0x04) +#define BMI160_NO_MOTION_Z_INT_EN_MASK UINT8_C(0x04) +#define BMI160_SIG_MOTION_INT_EN_MASK UINT8_C(0x07) +#define BMI160_ANY_MOTION_ALL_INT_EN_MASK UINT8_C(0x07) +#define BMI160_STEP_DETECT_INT_EN_MASK UINT8_C(0x08) +#define BMI160_DOUBLE_TAP_INT_EN_MASK UINT8_C(0x10) +#define BMI160_SINGLE_TAP_INT_EN_MASK UINT8_C(0x20) +#define BMI160_FIFO_FULL_INT_EN_MASK UINT8_C(0x20) +#define BMI160_ORIENT_INT_EN_MASK UINT8_C(0x40) +#define BMI160_FIFO_WATERMARK_INT_EN_MASK UINT8_C(0x40) +#define BMI160_LOW_G_INT_EN_MASK UINT8_C(0x08) +#define BMI160_STEP_DETECT_EN_MASK UINT8_C(0x08) +#define BMI160_FLAT_INT_EN_MASK UINT8_C(0x80) +#define BMI160_DATA_RDY_INT_EN_MASK UINT8_C(0x10) + +/** PMU status Macros */ +#define BMI160_AUX_PMU_SUSPEND UINT8_C(0x00) +#define BMI160_AUX_PMU_NORMAL UINT8_C(0x01) +#define BMI160_AUX_PMU_LOW_POWER UINT8_C(0x02) + +#define BMI160_GYRO_PMU_SUSPEND UINT8_C(0x00) +#define BMI160_GYRO_PMU_NORMAL UINT8_C(0x01) +#define BMI160_GYRO_PMU_FSU UINT8_C(0x03) + +#define BMI160_ACCEL_PMU_SUSPEND UINT8_C(0x00) +#define BMI160_ACCEL_PMU_NORMAL UINT8_C(0x01) +#define BMI160_ACCEL_PMU_LOW_POWER UINT8_C(0x02) + +/** Mask definitions for INT_OUT_CTRL register */ +#define BMI160_INT1_EDGE_CTRL_MASK UINT8_C(0x01) +#define BMI160_INT1_OUTPUT_MODE_MASK UINT8_C(0x04) +#define BMI160_INT1_OUTPUT_TYPE_MASK UINT8_C(0x02) +#define BMI160_INT1_OUTPUT_EN_MASK UINT8_C(0x08) +#define BMI160_INT2_EDGE_CTRL_MASK UINT8_C(0x10) +#define BMI160_INT2_OUTPUT_MODE_MASK UINT8_C(0x40) +#define BMI160_INT2_OUTPUT_TYPE_MASK UINT8_C(0x20) +#define BMI160_INT2_OUTPUT_EN_MASK UINT8_C(0x80) + +/** Mask definitions for INT_LATCH register */ +#define BMI160_INT1_INPUT_EN_MASK UINT8_C(0x10) +#define BMI160_INT2_INPUT_EN_MASK UINT8_C(0x20) +#define BMI160_INT_LATCH_MASK UINT8_C(0x0F) + +/** Mask definitions for INT_MAP register */ +#define BMI160_INT1_LOW_G_MASK UINT8_C(0x01) +#define BMI160_INT1_HIGH_G_MASK UINT8_C(0x02) +#define BMI160_INT1_SLOPE_MASK UINT8_C(0x04) +#define BMI160_INT1_NO_MOTION_MASK UINT8_C(0x08) +#define BMI160_INT1_DOUBLE_TAP_MASK UINT8_C(0x10) +#define BMI160_INT1_SINGLE_TAP_MASK UINT8_C(0x20) +#define BMI160_INT1_FIFO_FULL_MASK UINT8_C(0x20) +#define BMI160_INT1_FIFO_WM_MASK UINT8_C(0x40) +#define BMI160_INT1_ORIENT_MASK UINT8_C(0x40) +#define BMI160_INT1_FLAT_MASK UINT8_C(0x80) +#define BMI160_INT1_DATA_READY_MASK UINT8_C(0x80) +#define BMI160_INT2_LOW_G_MASK UINT8_C(0x01) +#define BMI160_INT1_LOW_STEP_DETECT_MASK UINT8_C(0x01) +#define BMI160_INT2_LOW_STEP_DETECT_MASK UINT8_C(0x01) +#define BMI160_INT2_HIGH_G_MASK UINT8_C(0x02) +#define BMI160_INT2_FIFO_FULL_MASK UINT8_C(0x02) +#define BMI160_INT2_FIFO_WM_MASK UINT8_C(0x04) +#define BMI160_INT2_SLOPE_MASK UINT8_C(0x04) +#define BMI160_INT2_DATA_READY_MASK UINT8_C(0x08) +#define BMI160_INT2_NO_MOTION_MASK UINT8_C(0x08) +#define BMI160_INT2_DOUBLE_TAP_MASK UINT8_C(0x10) +#define BMI160_INT2_SINGLE_TAP_MASK UINT8_C(0x20) +#define BMI160_INT2_ORIENT_MASK UINT8_C(0x40) +#define BMI160_INT2_FLAT_MASK UINT8_C(0x80) + +/** Mask definitions for INT_DATA register */ +#define BMI160_TAP_SRC_INT_MASK UINT8_C(0x08) +#define BMI160_LOW_HIGH_SRC_INT_MASK UINT8_C(0x80) +#define BMI160_MOTION_SRC_INT_MASK UINT8_C(0x80) + +/** Mask definitions for INT_MOTION register */ +#define BMI160_SLOPE_INT_DUR_MASK UINT8_C(0x03) +#define BMI160_NO_MOTION_INT_DUR_MASK UINT8_C(0xFC) +#define BMI160_NO_MOTION_SEL_BIT_MASK UINT8_C(0x01) + +/** Mask definitions for INT_TAP register */ +#define BMI160_TAP_DUR_MASK UINT8_C(0x07) +#define BMI160_TAP_SHOCK_DUR_MASK UINT8_C(0x40) +#define BMI160_TAP_QUIET_DUR_MASK UINT8_C(0x80) +#define BMI160_TAP_THRES_MASK UINT8_C(0x1F) + +/** Mask definitions for INT_FLAT register */ +#define BMI160_FLAT_THRES_MASK UINT8_C(0x3F) +#define BMI160_FLAT_HOLD_TIME_MASK UINT8_C(0x30) +#define BMI160_FLAT_HYST_MASK UINT8_C(0x07) + +/** Mask definitions for INT_LOWHIGH register */ +#define BMI160_LOW_G_HYST_MASK UINT8_C(0x03) +#define BMI160_LOW_G_LOW_MODE_MASK UINT8_C(0x04) +#define BMI160_HIGH_G_HYST_MASK UINT8_C(0xC0) + +/** Mask definitions for INT_SIG_MOTION register */ +#define BMI160_SIG_MOTION_SEL_MASK UINT8_C(0x02) +#define BMI160_SIG_MOTION_SKIP_MASK UINT8_C(0x0C) +#define BMI160_SIG_MOTION_PROOF_MASK UINT8_C(0x30) + +/** Mask definitions for INT_ORIENT register */ +#define BMI160_ORIENT_MODE_MASK UINT8_C(0x03) +#define BMI160_ORIENT_BLOCK_MASK UINT8_C(0x0C) +#define BMI160_ORIENT_HYST_MASK UINT8_C(0xF0) +#define BMI160_ORIENT_THETA_MASK UINT8_C(0x3F) +#define BMI160_ORIENT_UD_ENABLE UINT8_C(0x40) +#define BMI160_AXES_EN_MASK UINT8_C(0x80) + +/** Mask definitions for FIFO_CONFIG register */ +#define BMI160_FIFO_GYRO UINT8_C(0x80) +#define BMI160_FIFO_ACCEL UINT8_C(0x40) +#define BMI160_FIFO_AUX UINT8_C(0x20) +#define BMI160_FIFO_TAG_INT1 UINT8_C(0x08) +#define BMI160_FIFO_TAG_INT2 UINT8_C(0x04) +#define BMI160_FIFO_TIME UINT8_C(0x02) +#define BMI160_FIFO_HEADER UINT8_C(0x10) +#define BMI160_FIFO_CONFIG_1_MASK UINT8_C(0xFE) + +/** Mask definitions for STEP_CONF register */ +#define BMI160_STEP_COUNT_EN_BIT_MASK UINT8_C(0x08) +#define BMI160_STEP_DETECT_MIN_THRES_MASK UINT8_C(0x18) +#define BMI160_STEP_DETECT_STEPTIME_MIN_MASK UINT8_C(0x07) +#define BMI160_STEP_MIN_BUF_MASK UINT8_C(0x07) + +/** Mask definition for FIFO Header Data Tag */ +#define BMI160_FIFO_TAG_INTR_MASK UINT8_C(0xFC) + +/** Fifo byte counter mask definitions */ +#define BMI160_FIFO_BYTE_COUNTER_MASK UINT8_C(0x07) + +/** Enable/disable bit value */ +#define BMI160_ENABLE UINT8_C(0x01) +#define BMI160_DISABLE UINT8_C(0x00) + +/** Latch Duration */ +#define BMI160_LATCH_DUR_NONE UINT8_C(0x00) +#define BMI160_LATCH_DUR_312_5_MICRO_SEC UINT8_C(0x01) +#define BMI160_LATCH_DUR_625_MICRO_SEC UINT8_C(0x02) +#define BMI160_LATCH_DUR_1_25_MILLI_SEC UINT8_C(0x03) +#define BMI160_LATCH_DUR_2_5_MILLI_SEC UINT8_C(0x04) +#define BMI160_LATCH_DUR_5_MILLI_SEC UINT8_C(0x05) +#define BMI160_LATCH_DUR_10_MILLI_SEC UINT8_C(0x06) +#define BMI160_LATCH_DUR_20_MILLI_SEC UINT8_C(0x07) +#define BMI160_LATCH_DUR_40_MILLI_SEC UINT8_C(0x08) +#define BMI160_LATCH_DUR_80_MILLI_SEC UINT8_C(0x09) +#define BMI160_LATCH_DUR_160_MILLI_SEC UINT8_C(0x0A) +#define BMI160_LATCH_DUR_320_MILLI_SEC UINT8_C(0x0B) +#define BMI160_LATCH_DUR_640_MILLI_SEC UINT8_C(0x0C) +#define BMI160_LATCH_DUR_1_28_SEC UINT8_C(0x0D) +#define BMI160_LATCH_DUR_2_56_SEC UINT8_C(0x0E) +#define BMI160_LATCHED UINT8_C(0x0F) + +/** BMI160 Register map */ +#define BMI160_CHIP_ID_ADDR UINT8_C(0x00) +#define BMI160_ERROR_REG_ADDR UINT8_C(0x02) +#define BMI160_PMU_STATUS_ADDR UINT8_C(0x03) +#define BMI160_AUX_DATA_ADDR UINT8_C(0x04) +#define BMI160_GYRO_DATA_ADDR UINT8_C(0x0C) +#define BMI160_ACCEL_DATA_ADDR UINT8_C(0x12) +#define BMI160_STATUS_ADDR UINT8_C(0x1B) +#define BMI160_INT_STATUS_ADDR UINT8_C(0x1C) +#define BMI160_FIFO_LENGTH_ADDR UINT8_C(0x22) +#define BMI160_FIFO_DATA_ADDR UINT8_C(0x24) +#define BMI160_ACCEL_CONFIG_ADDR UINT8_C(0x40) +#define BMI160_ACCEL_RANGE_ADDR UINT8_C(0x41) +#define BMI160_GYRO_CONFIG_ADDR UINT8_C(0x42) +#define BMI160_GYRO_RANGE_ADDR UINT8_C(0x43) +#define BMI160_AUX_ODR_ADDR UINT8_C(0x44) +#define BMI160_FIFO_DOWN_ADDR UINT8_C(0x45) +#define BMI160_FIFO_CONFIG_0_ADDR UINT8_C(0x46) +#define BMI160_FIFO_CONFIG_1_ADDR UINT8_C(0x47) +#define BMI160_AUX_IF_0_ADDR UINT8_C(0x4B) +#define BMI160_AUX_IF_1_ADDR UINT8_C(0x4C) +#define BMI160_AUX_IF_2_ADDR UINT8_C(0x4D) +#define BMI160_AUX_IF_3_ADDR UINT8_C(0x4E) +#define BMI160_AUX_IF_4_ADDR UINT8_C(0x4F) +#define BMI160_INT_ENABLE_0_ADDR UINT8_C(0x50) +#define BMI160_INT_ENABLE_1_ADDR UINT8_C(0x51) +#define BMI160_INT_ENABLE_2_ADDR UINT8_C(0x52) +#define BMI160_INT_OUT_CTRL_ADDR UINT8_C(0x53) +#define BMI160_INT_LATCH_ADDR UINT8_C(0x54) +#define BMI160_INT_MAP_0_ADDR UINT8_C(0x55) +#define BMI160_INT_MAP_1_ADDR UINT8_C(0x56) +#define BMI160_INT_MAP_2_ADDR UINT8_C(0x57) +#define BMI160_INT_DATA_0_ADDR UINT8_C(0x58) +#define BMI160_INT_DATA_1_ADDR UINT8_C(0x59) +#define BMI160_INT_LOWHIGH_0_ADDR UINT8_C(0x5A) +#define BMI160_INT_LOWHIGH_1_ADDR UINT8_C(0x5B) +#define BMI160_INT_LOWHIGH_2_ADDR UINT8_C(0x5C) +#define BMI160_INT_LOWHIGH_3_ADDR UINT8_C(0x5D) +#define BMI160_INT_LOWHIGH_4_ADDR UINT8_C(0x5E) +#define BMI160_INT_MOTION_0_ADDR UINT8_C(0x5F) +#define BMI160_INT_MOTION_1_ADDR UINT8_C(0x60) +#define BMI160_INT_MOTION_2_ADDR UINT8_C(0x61) +#define BMI160_INT_MOTION_3_ADDR UINT8_C(0x62) +#define BMI160_INT_TAP_0_ADDR UINT8_C(0x63) +#define BMI160_INT_TAP_1_ADDR UINT8_C(0x64) +#define BMI160_INT_ORIENT_0_ADDR UINT8_C(0x65) +#define BMI160_INT_ORIENT_1_ADDR UINT8_C(0x66) +#define BMI160_INT_FLAT_0_ADDR UINT8_C(0x67) +#define BMI160_INT_FLAT_1_ADDR UINT8_C(0x68) +#define BMI160_FOC_CONF_ADDR UINT8_C(0x69) +#define BMI160_CONF_ADDR UINT8_C(0x6A) + +#define BMI160_IF_CONF_ADDR UINT8_C(0x6B) +#define BMI160_SELF_TEST_ADDR UINT8_C(0x6D) +#define BMI160_OFFSET_ADDR UINT8_C(0x71) +#define BMI160_OFFSET_CONF_ADDR UINT8_C(0x77) +#define BMI160_INT_STEP_CNT_0_ADDR UINT8_C(0x78) +#define BMI160_INT_STEP_CONFIG_0_ADDR UINT8_C(0x7A) +#define BMI160_INT_STEP_CONFIG_1_ADDR UINT8_C(0x7B) +#define BMI160_COMMAND_REG_ADDR UINT8_C(0x7E) +#define BMI160_SPI_COMM_TEST_ADDR UINT8_C(0x7F) +#define BMI160_INTL_PULLUP_CONF_ADDR UINT8_C(0x85) + +/** Error code definitions */ +#define BMI160_OK INT8_C(0) +#define BMI160_E_NULL_PTR INT8_C(-1) +#define BMI160_E_COM_FAIL INT8_C(-2) +#define BMI160_E_DEV_NOT_FOUND INT8_C(-3) +#define BMI160_E_OUT_OF_RANGE INT8_C(-4) +#define BMI160_E_INVALID_INPUT INT8_C(-5) +#define BMI160_E_ACCEL_ODR_BW_INVALID INT8_C(-6) +#define BMI160_E_GYRO_ODR_BW_INVALID INT8_C(-7) +#define BMI160_E_LWP_PRE_FLTR_INT_INVALID INT8_C(-8) +#define BMI160_E_LWP_PRE_FLTR_INVALID INT8_C(-9) +#define BMI160_E_AUX_NOT_FOUND INT8_C(-10) +#define BMI160_E_FOC_FAILURE INT8_C(-11) +#define BMI160_E_READ_WRITE_LENGTH_INVALID INT8_C(-12) +#define BMI160_E_INVALID_CONFIG INT8_C(-13) + +/**\name API warning codes */ +#define BMI160_W_GYRO_SELF_TEST_FAIL INT8_C(1) +#define BMI160_W_ACCEl_SELF_TEST_FAIL INT8_C(2) + +/** BMI160 unique chip identifier */ +#define BMI160_CHIP_ID UINT8_C(0xD1) + +/** Soft reset command */ +#define BMI160_SOFT_RESET_CMD UINT8_C(0xb6) +#define BMI160_SOFT_RESET_DELAY_MS UINT8_C(1) + +/** Start FOC command */ +#define BMI160_START_FOC_CMD UINT8_C(0x03) + +/** NVM backup enabling command */ +#define BMI160_NVM_BACKUP_EN UINT8_C(0xA0) + +/* Delay in ms settings */ +#define BMI160_ACCEL_DELAY_MS UINT8_C(5) +#define BMI160_GYRO_DELAY_MS UINT8_C(80) +#define BMI160_ONE_MS_DELAY UINT8_C(1) +#define BMI160_AUX_COM_DELAY UINT8_C(10) +#define BMI160_GYRO_SELF_TEST_DELAY UINT8_C(20) +#define BMI160_ACCEL_SELF_TEST_DELAY UINT8_C(50) + +/** Self test configurations */ +#define BMI160_ACCEL_SELF_TEST_CONFIG UINT8_C(0x2C) +#define BMI160_ACCEL_SELF_TEST_POSITIVE_EN UINT8_C(0x0D) +#define BMI160_ACCEL_SELF_TEST_NEGATIVE_EN UINT8_C(0x09) +#define BMI160_ACCEL_SELF_TEST_LIMIT UINT16_C(8192) + +/** Power mode settings */ +/* Accel power mode */ +#define BMI160_ACCEL_NORMAL_MODE UINT8_C(0x11) +#define BMI160_ACCEL_LOWPOWER_MODE UINT8_C(0x12) +#define BMI160_ACCEL_SUSPEND_MODE UINT8_C(0x10) + +/* Gyro power mode */ +#define BMI160_GYRO_SUSPEND_MODE UINT8_C(0x14) +#define BMI160_GYRO_NORMAL_MODE UINT8_C(0x15) +#define BMI160_GYRO_FASTSTARTUP_MODE UINT8_C(0x17) + +/* Aux power mode */ +#define BMI160_AUX_SUSPEND_MODE UINT8_C(0x18) +#define BMI160_AUX_NORMAL_MODE UINT8_C(0x19) +#define BMI160_AUX_LOWPOWER_MODE UINT8_C(0x1A) + +/** Range settings */ +/* Accel Range */ +#define BMI160_ACCEL_RANGE_2G UINT8_C(0x03) +#define BMI160_ACCEL_RANGE_4G UINT8_C(0x05) +#define BMI160_ACCEL_RANGE_8G UINT8_C(0x08) +#define BMI160_ACCEL_RANGE_16G UINT8_C(0x0C) + +/* Gyro Range */ +#define BMI160_GYRO_RANGE_2000_DPS UINT8_C(0x00) +#define BMI160_GYRO_RANGE_1000_DPS UINT8_C(0x01) +#define BMI160_GYRO_RANGE_500_DPS UINT8_C(0x02) +#define BMI160_GYRO_RANGE_250_DPS UINT8_C(0x03) +#define BMI160_GYRO_RANGE_125_DPS UINT8_C(0x04) + +/** Bandwidth settings */ +/* Accel Bandwidth */ +#define BMI160_ACCEL_BW_OSR4_AVG1 UINT8_C(0x00) +#define BMI160_ACCEL_BW_OSR2_AVG2 UINT8_C(0x01) +#define BMI160_ACCEL_BW_NORMAL_AVG4 UINT8_C(0x02) +#define BMI160_ACCEL_BW_RES_AVG8 UINT8_C(0x03) +#define BMI160_ACCEL_BW_RES_AVG16 UINT8_C(0x04) +#define BMI160_ACCEL_BW_RES_AVG32 UINT8_C(0x05) +#define BMI160_ACCEL_BW_RES_AVG64 UINT8_C(0x06) +#define BMI160_ACCEL_BW_RES_AVG128 UINT8_C(0x07) + +#define BMI160_GYRO_BW_OSR4_MODE UINT8_C(0x00) +#define BMI160_GYRO_BW_OSR2_MODE UINT8_C(0x01) +#define BMI160_GYRO_BW_NORMAL_MODE UINT8_C(0x02) + +/* Output Data Rate settings */ +/* Accel Output data rate */ +#define BMI160_ACCEL_ODR_RESERVED UINT8_C(0x00) +#define BMI160_ACCEL_ODR_0_78HZ UINT8_C(0x01) +#define BMI160_ACCEL_ODR_1_56HZ UINT8_C(0x02) +#define BMI160_ACCEL_ODR_3_12HZ UINT8_C(0x03) +#define BMI160_ACCEL_ODR_6_25HZ UINT8_C(0x04) +#define BMI160_ACCEL_ODR_12_5HZ UINT8_C(0x05) +#define BMI160_ACCEL_ODR_25HZ UINT8_C(0x06) +#define BMI160_ACCEL_ODR_50HZ UINT8_C(0x07) +#define BMI160_ACCEL_ODR_100HZ UINT8_C(0x08) +#define BMI160_ACCEL_ODR_200HZ UINT8_C(0x09) +#define BMI160_ACCEL_ODR_400HZ UINT8_C(0x0A) +#define BMI160_ACCEL_ODR_800HZ UINT8_C(0x0B) +#define BMI160_ACCEL_ODR_1600HZ UINT8_C(0x0C) +#define BMI160_ACCEL_ODR_RESERVED0 UINT8_C(0x0D) +#define BMI160_ACCEL_ODR_RESERVED1 UINT8_C(0x0E) +#define BMI160_ACCEL_ODR_RESERVED2 UINT8_C(0x0F) + +/* Gyro Output data rate */ +#define BMI160_GYRO_ODR_RESERVED UINT8_C(0x00) +#define BMI160_GYRO_ODR_25HZ UINT8_C(0x06) +#define BMI160_GYRO_ODR_50HZ UINT8_C(0x07) +#define BMI160_GYRO_ODR_100HZ UINT8_C(0x08) +#define BMI160_GYRO_ODR_200HZ UINT8_C(0x09) +#define BMI160_GYRO_ODR_400HZ UINT8_C(0x0A) +#define BMI160_GYRO_ODR_800HZ UINT8_C(0x0B) +#define BMI160_GYRO_ODR_1600HZ UINT8_C(0x0C) +#define BMI160_GYRO_ODR_3200HZ UINT8_C(0x0D) + +/* Auxiliary sensor Output data rate */ +#define BMI160_AUX_ODR_RESERVED UINT8_C(0x00) +#define BMI160_AUX_ODR_0_78HZ UINT8_C(0x01) +#define BMI160_AUX_ODR_1_56HZ UINT8_C(0x02) +#define BMI160_AUX_ODR_3_12HZ UINT8_C(0x03) +#define BMI160_AUX_ODR_6_25HZ UINT8_C(0x04) +#define BMI160_AUX_ODR_12_5HZ UINT8_C(0x05) +#define BMI160_AUX_ODR_25HZ UINT8_C(0x06) +#define BMI160_AUX_ODR_50HZ UINT8_C(0x07) +#define BMI160_AUX_ODR_100HZ UINT8_C(0x08) +#define BMI160_AUX_ODR_200HZ UINT8_C(0x09) +#define BMI160_AUX_ODR_400HZ UINT8_C(0x0A) +#define BMI160_AUX_ODR_800HZ UINT8_C(0x0B) + +/** FIFO_CONFIG Definitions */ +#define BMI160_FIFO_TIME_ENABLE UINT8_C(0x02) +#define BMI160_FIFO_TAG_INT2_ENABLE UINT8_C(0x04) +#define BMI160_FIFO_TAG_INT1_ENABLE UINT8_C(0x08) +#define BMI160_FIFO_HEAD_ENABLE UINT8_C(0x10) +#define BMI160_FIFO_M_ENABLE UINT8_C(0x20) +#define BMI160_FIFO_A_ENABLE UINT8_C(0x40) +#define BMI160_FIFO_M_A_ENABLE UINT8_C(0x60) +#define BMI160_FIFO_G_ENABLE UINT8_C(0x80) +#define BMI160_FIFO_M_G_ENABLE UINT8_C(0xA0) +#define BMI160_FIFO_G_A_ENABLE UINT8_C(0xC0) +#define BMI160_FIFO_M_G_A_ENABLE UINT8_C(0xE0) + +/* Macro to specify the number of bytes over-read from the + * FIFO in order to get the sensor time at the end of FIFO */ +#ifndef BMI160_FIFO_BYTES_OVERREAD +#define BMI160_FIFO_BYTES_OVERREAD UINT8_C(25) +#endif + +/* Accel, gyro and aux. sensor length and also their combined + * length definitions in FIFO */ +#define BMI160_FIFO_G_LENGTH UINT8_C(6) +#define BMI160_FIFO_A_LENGTH UINT8_C(6) +#define BMI160_FIFO_M_LENGTH UINT8_C(8) +#define BMI160_FIFO_GA_LENGTH UINT8_C(12) +#define BMI160_FIFO_MA_LENGTH UINT8_C(14) +#define BMI160_FIFO_MG_LENGTH UINT8_C(14) +#define BMI160_FIFO_MGA_LENGTH UINT8_C(20) + +/** FIFO Header Data definitions */ +#define BMI160_FIFO_HEAD_SKIP_FRAME UINT8_C(0x40) +#define BMI160_FIFO_HEAD_SENSOR_TIME UINT8_C(0x44) +#define BMI160_FIFO_HEAD_INPUT_CONFIG UINT8_C(0x48) +#define BMI160_FIFO_HEAD_OVER_READ UINT8_C(0x80) +#define BMI160_FIFO_HEAD_A UINT8_C(0x84) +#define BMI160_FIFO_HEAD_G UINT8_C(0x88) +#define BMI160_FIFO_HEAD_G_A UINT8_C(0x8C) +#define BMI160_FIFO_HEAD_M UINT8_C(0x90) +#define BMI160_FIFO_HEAD_M_A UINT8_C(0x94) +#define BMI160_FIFO_HEAD_M_G UINT8_C(0x98) +#define BMI160_FIFO_HEAD_M_G_A UINT8_C(0x9C) + +/** FIFO sensor time length definitions */ +#define BMI160_SENSOR_TIME_LENGTH UINT8_C(3) + +/** FIFO DOWN selection */ +/* Accel fifo down-sampling values*/ +#define BMI160_ACCEL_FIFO_DOWN_ZERO UINT8_C(0x00) +#define BMI160_ACCEL_FIFO_DOWN_ONE UINT8_C(0x10) +#define BMI160_ACCEL_FIFO_DOWN_TWO UINT8_C(0x20) +#define BMI160_ACCEL_FIFO_DOWN_THREE UINT8_C(0x30) +#define BMI160_ACCEL_FIFO_DOWN_FOUR UINT8_C(0x40) +#define BMI160_ACCEL_FIFO_DOWN_FIVE UINT8_C(0x50) +#define BMI160_ACCEL_FIFO_DOWN_SIX UINT8_C(0x60) +#define BMI160_ACCEL_FIFO_DOWN_SEVEN UINT8_C(0x70) + +/* Gyro fifo down-smapling values*/ +#define BMI160_GYRO_FIFO_DOWN_ZERO UINT8_C(0x00) +#define BMI160_GYRO_FIFO_DOWN_ONE UINT8_C(0x01) +#define BMI160_GYRO_FIFO_DOWN_TWO UINT8_C(0x02) +#define BMI160_GYRO_FIFO_DOWN_THREE UINT8_C(0x03) +#define BMI160_GYRO_FIFO_DOWN_FOUR UINT8_C(0x04) +#define BMI160_GYRO_FIFO_DOWN_FIVE UINT8_C(0x05) +#define BMI160_GYRO_FIFO_DOWN_SIX UINT8_C(0x06) +#define BMI160_GYRO_FIFO_DOWN_SEVEN UINT8_C(0x07) + +/* Accel Fifo filter enable*/ +#define BMI160_ACCEL_FIFO_FILT_EN UINT8_C(0x80) + +/* Gyro Fifo filter enable*/ +#define BMI160_GYRO_FIFO_FILT_EN UINT8_C(0x08) + +/** Definitions to check validity of FIFO frames */ +#define FIFO_CONFIG_MSB_CHECK UINT8_C(0x80) +#define FIFO_CONFIG_LSB_CHECK UINT8_C(0x00) + +/*! BMI160 accel FOC configurations */ +#define BMI160_FOC_ACCEL_DISABLED UINT8_C(0x00) +#define BMI160_FOC_ACCEL_POSITIVE_G UINT8_C(0x01) +#define BMI160_FOC_ACCEL_NEGATIVE_G UINT8_C(0x02) +#define BMI160_FOC_ACCEL_0G UINT8_C(0x03) + +/** Array Parameter DefinItions */ +#define BMI160_SENSOR_TIME_LSB_BYTE UINT8_C(0) +#define BMI160_SENSOR_TIME_XLSB_BYTE UINT8_C(1) +#define BMI160_SENSOR_TIME_MSB_BYTE UINT8_C(2) + +/** Interface settings */ +#define BMI160_SPI_INTF UINT8_C(1) +#define BMI160_I2C_INTF UINT8_C(0) +#define BMI160_SPI_RD_MASK UINT8_C(0x80) +#define BMI160_SPI_WR_MASK UINT8_C(0x7F) + +/* Sensor & time select definition*/ +#define BMI160_ACCEL_SEL UINT8_C(0x01) +#define BMI160_GYRO_SEL UINT8_C(0x02) +#define BMI160_TIME_SEL UINT8_C(0x04) + +/* Sensor select mask*/ +#define BMI160_SEN_SEL_MASK UINT8_C(0x07) + +/* Error code mask */ +#define BMI160_ERR_REG_MASK UINT8_C(0x0F) + +/* BMI160 I2C address */ +#define BMI160_I2C_ADDR UINT8_C(0x68) + +/* BMI160 secondary IF address */ +#define BMI160_AUX_BMM150_I2C_ADDR UINT8_C(0x10) + +/** BMI160 Length definitions */ +#define BMI160_ONE UINT8_C(1) +#define BMI160_TWO UINT8_C(2) +#define BMI160_THREE UINT8_C(3) +#define BMI160_FOUR UINT8_C(4) +#define BMI160_FIVE UINT8_C(5) + +/** BMI160 fifo level Margin */ +#define BMI160_FIFO_LEVEL_MARGIN UINT8_C(16) + +/** BMI160 fifo flush Command */ +#define BMI160_FIFO_FLUSH_VALUE UINT8_C(0xB0) + +/** BMI160 offset values for xyz axes of accel */ +#define BMI160_ACCEL_MIN_OFFSET INT8_C(-128) +#define BMI160_ACCEL_MAX_OFFSET INT8_C(127) + +/** BMI160 offset values for xyz axes of gyro */ +#define BMI160_GYRO_MIN_OFFSET INT16_C(-512) +#define BMI160_GYRO_MAX_OFFSET INT16_C(511) + +/** BMI160 fifo full interrupt position and mask */ +#define BMI160_FIFO_FULL_INT_POS UINT8_C(5) +#define BMI160_FIFO_FULL_INT_MSK UINT8_C(0x20) +#define BMI160_FIFO_WTM_INT_POS UINT8_C(6) +#define BMI160_FIFO_WTM_INT_MSK UINT8_C(0x40) + +#define BMI160_FIFO_FULL_INT_PIN1_POS UINT8_C(5) +#define BMI160_FIFO_FULL_INT_PIN1_MSK UINT8_C(0x20) +#define BMI160_FIFO_FULL_INT_PIN2_POS UINT8_C(1) +#define BMI160_FIFO_FULL_INT_PIN2_MSK UINT8_C(0x02) + +#define BMI160_FIFO_WTM_INT_PIN1_POS UINT8_C(6) +#define BMI160_FIFO_WTM_INT_PIN1_MSK UINT8_C(0x40) +#define BMI160_FIFO_WTM_INT_PIN2_POS UINT8_C(2) +#define BMI160_FIFO_WTM_INT_PIN2_MSK UINT8_C(0x04) + +#define BMI160_MANUAL_MODE_EN_POS UINT8_C(7) +#define BMI160_MANUAL_MODE_EN_MSK UINT8_C(0x80) +#define BMI160_AUX_READ_BURST_POS UINT8_C(0) +#define BMI160_AUX_READ_BURST_MSK UINT8_C(0x03) + +#define BMI160_GYRO_SELF_TEST_POS UINT8_C(4) +#define BMI160_GYRO_SELF_TEST_MSK UINT8_C(0x10) +#define BMI160_GYRO_SELF_TEST_STATUS_POS UINT8_C(1) +#define BMI160_GYRO_SELF_TEST_STATUS_MSK UINT8_C(0x02) + +#define BMI160_GYRO_FOC_EN_POS UINT8_C(6) +#define BMI160_GYRO_FOC_EN_MSK UINT8_C(0x40) + +#define BMI160_ACCEL_FOC_X_CONF_POS UINT8_C(4) +#define BMI160_ACCEL_FOC_X_CONF_MSK UINT8_C(0x30) + +#define BMI160_ACCEL_FOC_Y_CONF_POS UINT8_C(2) +#define BMI160_ACCEL_FOC_Y_CONF_MSK UINT8_C(0x0C) + +#define BMI160_ACCEL_FOC_Z_CONF_MSK UINT8_C(0x03) + +#define BMI160_FOC_STATUS_POS UINT8_C(3) +#define BMI160_FOC_STATUS_MSK UINT8_C(0x08) + +#define BMI160_GYRO_OFFSET_X_MSK UINT8_C(0x03) + +#define BMI160_GYRO_OFFSET_Y_POS UINT8_C(2) +#define BMI160_GYRO_OFFSET_Y_MSK UINT8_C(0x0C) + +#define BMI160_GYRO_OFFSET_Z_POS UINT8_C(4) +#define BMI160_GYRO_OFFSET_Z_MSK UINT8_C(0x30) + +#define BMI160_GYRO_OFFSET_EN_POS UINT8_C(7) +#define BMI160_GYRO_OFFSET_EN_MSK UINT8_C(0x80) + +#define BMI160_ACCEL_OFFSET_EN_POS UINT8_C(6) +#define BMI160_ACCEL_OFFSET_EN_MSK UINT8_C(0x40) + +#define BMI160_GYRO_OFFSET_POS UINT16_C(8) +#define BMI160_GYRO_OFFSET_MSK UINT16_C(0x0300) + +#define BMI160_NVM_UPDATE_POS UINT8_C(1) +#define BMI160_NVM_UPDATE_MSK UINT8_C(0x02) + +#define BMI160_NVM_STATUS_POS UINT8_C(4) +#define BMI160_NVM_STATUS_MSK UINT8_C(0x10) + +#define BMI160_MAG_POWER_MODE_MSK UINT8_C(0x03) + +#define BMI160_ACCEL_POWER_MODE_MSK UINT8_C(0x30) +#define BMI160_ACCEL_POWER_MODE_POS UINT8_C(4) + +#define BMI160_GYRO_POWER_MODE_MSK UINT8_C(0x0C) +#define BMI160_GYRO_POWER_MODE_POS UINT8_C(2) + +/* BIT SLICE GET AND SET FUNCTIONS */ +#define BMI160_GET_BITS(regvar, bitname) ((regvar & bitname##_MSK) >> bitname##_POS) +#define BMI160_SET_BITS(regvar, bitname, val) \ + ((regvar & ~bitname##_MSK) | ((val << bitname##_POS) & bitname##_MSK)) + +#define BMI160_SET_BITS_POS_0(reg_data, bitname, data) \ + ((reg_data & ~(bitname##_MSK)) | (data & bitname##_MSK)) + +#define BMI160_GET_BITS_POS_0(reg_data, bitname) (reg_data & (bitname##_MSK)) + +/**\name UTILITY MACROS */ +#define BMI160_SET_LOW_BYTE UINT16_C(0x00FF) +#define BMI160_SET_HIGH_BYTE UINT16_C(0xFF00) + +#define BMI160_GET_LSB(var) (uint8_t)(var & BMI160_SET_LOW_BYTE) +#define BMI160_GET_MSB(var) (uint8_t)((var & BMI160_SET_HIGH_BYTE) >> 8) + +/*****************************************************************************/ +/* type definitions */ + +/*! + * @brief Bus communication function pointer which should be mapped to + * the platform specific read functions of the user + */ +typedef int8_t ( + *bmi160_read_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t* data, uint16_t len); + +/*! + * @brief Bus communication function pointer which should be mapped to + * the platform specific write functions of the user + */ +typedef int8_t ( + *bmi160_write_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t* read_data, uint16_t len); +typedef void (*bmi160_delay_fptr_t)(uint32_t period); + +/*************************** Data structures *********************************/ + +/*! + * @brief bmi160 interrupt status selection enum. + */ +enum bmi160_int_status_sel { + BMI160_INT_STATUS_0 = 1, + BMI160_INT_STATUS_1 = 2, + BMI160_INT_STATUS_2 = 4, + BMI160_INT_STATUS_3 = 8, + BMI160_INT_STATUS_ALL = 15 +}; + +/*! + * @brief bmi160 interrupt status bits structure + */ +struct bmi160_int_status_bits { +#ifdef LITTLE_ENDIAN + + uint32_t step : 1; + uint32_t sigmot : 1; + uint32_t anym : 1; + + /* pmu trigger will be handled later */ + uint32_t pmu_trigger_reserved : 1; + uint32_t d_tap : 1; + uint32_t s_tap : 1; + uint32_t orient : 1; + uint32_t flat_int : 1; + uint32_t reserved : 2; + uint32_t high_g : 1; + uint32_t low_g : 1; + uint32_t drdy : 1; + uint32_t ffull : 1; + uint32_t fwm : 1; + uint32_t nomo : 1; + uint32_t anym_first_x : 1; + uint32_t anym_first_y : 1; + uint32_t anym_first_z : 1; + uint32_t anym_sign : 1; + uint32_t tap_first_x : 1; + uint32_t tap_first_y : 1; + uint32_t tap_first_z : 1; + uint32_t tap_sign : 1; + uint32_t high_first_x : 1; + uint32_t high_first_y : 1; + uint32_t high_first_z : 1; + uint32_t high_sign : 1; + uint32_t orient_1_0 : 2; + uint32_t orient_2 : 1; + uint32_t flat : 1; +#else + uint32_t high_first_x : 1; + uint32_t high_first_y : 1; + uint32_t high_first_z : 1; + uint32_t high_sign : 1; + uint32_t orient_1_0 : 2; + uint32_t orient_2 : 1; + uint32_t flat : 1; + uint32_t anym_first_x : 1; + uint32_t anym_first_y : 1; + uint32_t anym_first_z : 1; + uint32_t anym_sign : 1; + uint32_t tap_first_x : 1; + uint32_t tap_first_y : 1; + uint32_t tap_first_z : 1; + uint32_t tap_sign : 1; + uint32_t reserved : 2; + uint32_t high_g : 1; + uint32_t low_g : 1; + uint32_t drdy : 1; + uint32_t ffull : 1; + uint32_t fwm : 1; + uint32_t nomo : 1; + uint32_t step : 1; + uint32_t sigmot : 1; + uint32_t anym : 1; + + /* pmu trigger will be handled later */ + uint32_t pmu_trigger_reserved : 1; + uint32_t d_tap : 1; + uint32_t s_tap : 1; + uint32_t orient : 1; + uint32_t flat_int : 1; +#endif +}; + +/*! + * @brief bmi160 interrupt status structure + */ +union bmi160_int_status { + uint8_t data[4]; + struct bmi160_int_status_bits bit; +}; + +/*! + * @brief bmi160 sensor data structure which comprises of accel data + */ +struct bmi160_sensor_data { + /*! X-axis sensor data */ + int16_t x; + + /*! Y-axis sensor data */ + int16_t y; + + /*! Z-axis sensor data */ + int16_t z; + + /*! sensor time */ + uint32_t sensortime; +}; + +/*! + * @brief bmi160 aux data structure which comprises of 8 bytes of accel data + */ +struct bmi160_aux_data { + /*! Auxiliary data */ + uint8_t data[8]; +}; + +/*! + * @brief bmi160 FOC configuration structure + */ +struct bmi160_foc_conf { + /*! Enabling FOC in gyro + * Assignable macros : + * - BMI160_ENABLE + * - BMI160_DISABLE + */ + uint8_t foc_gyr_en; + + /*! Accel FOC configurations + * Assignable macros : + * - BMI160_FOC_ACCEL_DISABLED + * - BMI160_FOC_ACCEL_POSITIVE_G + * - BMI160_FOC_ACCEL_NEGATIVE_G + * - BMI160_FOC_ACCEL_0G + */ + uint8_t foc_acc_x; + uint8_t foc_acc_y; + uint8_t foc_acc_z; + + /*! Enabling offset compensation for accel in data registers + * Assignable macros : + * - BMI160_ENABLE + * - BMI160_DISABLE + */ + uint8_t acc_off_en; + + /*! Enabling offset compensation for gyro in data registers + * Assignable macros : + * - BMI160_ENABLE + * - BMI160_DISABLE + */ + uint8_t gyro_off_en; +}; + +/*! + * @brief bmi160 accel gyro offsets + */ +struct bmi160_offsets { + /*! Accel offset for x axis */ + int8_t off_acc_x; + + /*! Accel offset for y axis */ + int8_t off_acc_y; + + /*! Accel offset for z axis */ + int8_t off_acc_z; + + /*! Gyro offset for x axis */ + int16_t off_gyro_x; + + /*! Gyro offset for y axis */ + int16_t off_gyro_y; + + /*! Gyro offset for z axis */ + int16_t off_gyro_z; +}; + +/*! + * @brief FIFO aux. sensor data structure + */ +struct bmi160_aux_fifo_data { + /*! The value of aux. sensor x LSB data */ + uint8_t aux_x_lsb; + + /*! The value of aux. sensor x MSB data */ + uint8_t aux_x_msb; + + /*! The value of aux. sensor y LSB data */ + uint8_t aux_y_lsb; + + /*! The value of aux. sensor y MSB data */ + uint8_t aux_y_msb; + + /*! The value of aux. sensor z LSB data */ + uint8_t aux_z_lsb; + + /*! The value of aux. sensor z MSB data */ + uint8_t aux_z_msb; + + /*! The value of aux. sensor r for BMM150 LSB data */ + uint8_t aux_r_y2_lsb; + + /*! The value of aux. sensor r for BMM150 MSB data */ + uint8_t aux_r_y2_msb; +}; + +/*! + * @brief bmi160 sensor select structure + */ +enum bmi160_select_sensor { BMI160_ACCEL_ONLY = 1, BMI160_GYRO_ONLY, BMI160_BOTH_ACCEL_AND_GYRO }; + +/*! + * @brief bmi160 sensor step detector mode structure + */ +enum bmi160_step_detect_mode { + BMI160_STEP_DETECT_NORMAL, + BMI160_STEP_DETECT_SENSITIVE, + BMI160_STEP_DETECT_ROBUST, + + /*! Non recommended User defined setting */ + BMI160_STEP_DETECT_USER_DEFINE +}; + +/*! + * @brief enum for auxiliary burst read selection + */ +enum bmi160_aux_read_len { + BMI160_AUX_READ_LEN_0, + BMI160_AUX_READ_LEN_1, + BMI160_AUX_READ_LEN_2, + BMI160_AUX_READ_LEN_3 +}; + +/*! + * @brief bmi160 sensor configuration structure + */ +struct bmi160_cfg { + /*! power mode */ + uint8_t power; + + /*! output data rate */ + uint8_t odr; + + /*! range */ + uint8_t range; + + /*! bandwidth */ + uint8_t bw; +}; + +/*! + * @brief Aux sensor configuration structure + */ +struct bmi160_aux_cfg { + /*! Aux sensor, 1 - enable 0 - disable */ + uint8_t aux_sensor_enable : 1; + + /*! Aux manual/auto mode status */ + uint8_t manual_enable : 1; + + /*! Aux read burst length */ + uint8_t aux_rd_burst_len : 2; + + /*! output data rate */ + uint8_t aux_odr : 4; + + /*! i2c addr of auxiliary sensor */ + uint8_t aux_i2c_addr; +}; + +/*! + * @brief bmi160 interrupt channel selection structure + */ +enum bmi160_int_channel { + /*! Un-map both channels */ + BMI160_INT_CHANNEL_NONE, + + /*! interrupt Channel 1 */ + BMI160_INT_CHANNEL_1, + + /*! interrupt Channel 2 */ + BMI160_INT_CHANNEL_2, + + /*! Map both channels */ + BMI160_INT_CHANNEL_BOTH +}; +enum bmi160_int_types { + /*! Slope/Any-motion interrupt */ + BMI160_ACC_ANY_MOTION_INT, + + /*! Significant motion interrupt */ + BMI160_ACC_SIG_MOTION_INT, + + /*! Step detector interrupt */ + BMI160_STEP_DETECT_INT, + + /*! double tap interrupt */ + BMI160_ACC_DOUBLE_TAP_INT, + + /*! single tap interrupt */ + BMI160_ACC_SINGLE_TAP_INT, + + /*! orientation interrupt */ + BMI160_ACC_ORIENT_INT, + + /*! flat interrupt */ + BMI160_ACC_FLAT_INT, + + /*! high-g interrupt */ + BMI160_ACC_HIGH_G_INT, + + /*! low-g interrupt */ + BMI160_ACC_LOW_G_INT, + + /*! slow/no-motion interrupt */ + BMI160_ACC_SLOW_NO_MOTION_INT, + + /*! data ready interrupt */ + BMI160_ACC_GYRO_DATA_RDY_INT, + + /*! fifo full interrupt */ + BMI160_ACC_GYRO_FIFO_FULL_INT, + + /*! fifo watermark interrupt */ + BMI160_ACC_GYRO_FIFO_WATERMARK_INT, + + /*! fifo tagging feature support */ + BMI160_FIFO_TAG_INT_PIN +}; + +/*! + * @brief bmi160 active state of any & sig motion interrupt. + */ +enum bmi160_any_sig_motion_active_interrupt_state { + /*! Both any & sig motion are disabled */ + BMI160_BOTH_ANY_SIG_MOTION_DISABLED = -1, + + /*! Any-motion selected */ + BMI160_ANY_MOTION_ENABLED, + + /*! Sig-motion selected */ + BMI160_SIG_MOTION_ENABLED +}; +struct bmi160_acc_tap_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! tap threshold */ + uint16_t tap_thr : 5; + + /*! tap shock */ + uint16_t tap_shock : 1; + + /*! tap quiet */ + uint16_t tap_quiet : 1; + + /*! tap duration */ + uint16_t tap_dur : 3; + + /*! data source 0- filter & 1 pre-filter*/ + uint16_t tap_data_src : 1; + + /*! tap enable, 1 - enable, 0 - disable */ + uint16_t tap_en : 1; +#else + + /*! tap enable, 1 - enable, 0 - disable */ + uint16_t tap_en : 1; + + /*! data source 0- filter & 1 pre-filter*/ + uint16_t tap_data_src : 1; + + /*! tap duration */ + uint16_t tap_dur : 3; + + /*! tap quiet */ + uint16_t tap_quiet : 1; + + /*! tap shock */ + uint16_t tap_shock : 1; + + /*! tap threshold */ + uint16_t tap_thr : 5; +#endif +}; +struct bmi160_acc_any_mot_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! 1 any-motion enable, 0 - any-motion disable */ + uint8_t anymotion_en : 1; + + /*! slope interrupt x, 1 - enable, 0 - disable */ + uint8_t anymotion_x : 1; + + /*! slope interrupt y, 1 - enable, 0 - disable */ + uint8_t anymotion_y : 1; + + /*! slope interrupt z, 1 - enable, 0 - disable */ + uint8_t anymotion_z : 1; + + /*! slope duration */ + uint8_t anymotion_dur : 2; + + /*! data source 0- filter & 1 pre-filter*/ + uint8_t anymotion_data_src : 1; + + /*! slope threshold */ + uint8_t anymotion_thr; +#else + + /*! slope threshold */ + uint8_t anymotion_thr; + + /*! data source 0- filter & 1 pre-filter*/ + uint8_t anymotion_data_src : 1; + + /*! slope duration */ + uint8_t anymotion_dur : 2; + + /*! slope interrupt z, 1 - enable, 0 - disable */ + uint8_t anymotion_z : 1; + + /*! slope interrupt y, 1 - enable, 0 - disable */ + uint8_t anymotion_y : 1; + + /*! slope interrupt x, 1 - enable, 0 - disable */ + uint8_t anymotion_x : 1; + + /*! 1 any-motion enable, 0 - any-motion disable */ + uint8_t anymotion_en : 1; +#endif +}; +struct bmi160_acc_sig_mot_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! skip time of sig-motion interrupt */ + uint8_t sig_mot_skip : 2; + + /*! proof time of sig-motion interrupt */ + uint8_t sig_mot_proof : 2; + + /*! data source 0- filter & 1 pre-filter*/ + uint8_t sig_data_src : 1; + + /*! 1 - enable sig, 0 - disable sig & enable anymotion */ + uint8_t sig_en : 1; + + /*! sig-motion threshold */ + uint8_t sig_mot_thres; +#else + + /*! sig-motion threshold */ + uint8_t sig_mot_thres; + + /*! 1 - enable sig, 0 - disable sig & enable anymotion */ + uint8_t sig_en : 1; + + /*! data source 0- filter & 1 pre-filter*/ + uint8_t sig_data_src : 1; + + /*! proof time of sig-motion interrupt */ + uint8_t sig_mot_proof : 2; + + /*! skip time of sig-motion interrupt */ + uint8_t sig_mot_skip : 2; +#endif +}; +struct bmi160_acc_step_detect_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! 1- step detector enable, 0- step detector disable */ + uint16_t step_detector_en : 1; + + /*! minimum threshold */ + uint16_t min_threshold : 2; + + /*! minimal detectable step time */ + uint16_t steptime_min : 3; + + /*! enable step counter mode setting */ + uint16_t step_detector_mode : 2; + + /*! minimum step buffer size*/ + uint16_t step_min_buf : 3; +#else + + /*! minimum step buffer size*/ + uint16_t step_min_buf : 3; + + /*! enable step counter mode setting */ + uint16_t step_detector_mode : 2; + + /*! minimal detectable step time */ + uint16_t steptime_min : 3; + + /*! minimum threshold */ + uint16_t min_threshold : 2; + + /*! 1- step detector enable, 0- step detector disable */ + uint16_t step_detector_en : 1; +#endif +}; +struct bmi160_acc_no_motion_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! no motion interrupt x */ + uint16_t no_motion_x : 1; + + /*! no motion interrupt y */ + uint16_t no_motion_y : 1; + + /*! no motion interrupt z */ + uint16_t no_motion_z : 1; + + /*! no motion duration */ + uint16_t no_motion_dur : 6; + + /*! no motion sel , 1 - enable no-motion ,0- enable slow-motion */ + uint16_t no_motion_sel : 1; + + /*! data source 0- filter & 1 pre-filter*/ + uint16_t no_motion_src : 1; + + /*! no motion threshold */ + uint8_t no_motion_thres; +#else + + /*! no motion threshold */ + uint8_t no_motion_thres; + + /*! data source 0- filter & 1 pre-filter*/ + uint16_t no_motion_src : 1; + + /*! no motion sel , 1 - enable no-motion ,0- enable slow-motion */ + uint16_t no_motion_sel : 1; + + /*! no motion duration */ + uint16_t no_motion_dur : 6; + + /* no motion interrupt z */ + uint16_t no_motion_z : 1; + + /*! no motion interrupt y */ + uint16_t no_motion_y : 1; + + /*! no motion interrupt x */ + uint16_t no_motion_x : 1; +#endif +}; +struct bmi160_acc_orient_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! thresholds for switching between the different orientations */ + uint16_t orient_mode : 2; + + /*! blocking_mode */ + uint16_t orient_blocking : 2; + + /*! Orientation interrupt hysteresis */ + uint16_t orient_hyst : 4; + + /*! Orientation interrupt theta */ + uint16_t orient_theta : 6; + + /*! Enable/disable Orientation interrupt */ + uint16_t orient_ud_en : 1; + + /*! exchange x- and z-axis in algorithm ,0 - z, 1 - x */ + uint16_t axes_ex : 1; + + /*! 1 - orient enable, 0 - orient disable */ + uint8_t orient_en : 1; +#else + + /*! 1 - orient enable, 0 - orient disable */ + uint8_t orient_en : 1; + + /*! exchange x- and z-axis in algorithm ,0 - z, 1 - x */ + uint16_t axes_ex : 1; + + /*! Enable/disable Orientation interrupt */ + uint16_t orient_ud_en : 1; + + /*! Orientation interrupt theta */ + uint16_t orient_theta : 6; + + /*! Orientation interrupt hysteresis */ + uint16_t orient_hyst : 4; + + /*! blocking_mode */ + uint16_t orient_blocking : 2; + + /*! thresholds for switching between the different orientations */ + uint16_t orient_mode : 2; +#endif +}; +struct bmi160_acc_flat_detect_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! flat threshold */ + uint16_t flat_theta : 6; + + /*! flat interrupt hysteresis */ + uint16_t flat_hy : 3; + + /*! delay time for which the flat value must remain stable for the + * flat interrupt to be generated */ + uint16_t flat_hold_time : 2; + + /*! 1 - flat enable, 0 - flat disable */ + uint16_t flat_en : 1; +#else + + /*! 1 - flat enable, 0 - flat disable */ + uint16_t flat_en : 1; + + /*! delay time for which the flat value must remain stable for the + * flat interrupt to be generated */ + uint16_t flat_hold_time : 2; + + /*! flat interrupt hysteresis */ + uint16_t flat_hy : 3; + + /*! flat threshold */ + uint16_t flat_theta : 6; +#endif +}; +struct bmi160_acc_low_g_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! low-g interrupt trigger delay */ + uint8_t low_dur; + + /*! low-g interrupt trigger threshold */ + uint8_t low_thres; + + /*! hysteresis of low-g interrupt */ + uint8_t low_hyst : 2; + + /*! 0 - single-axis mode ,1 - axis-summing mode */ + uint8_t low_mode : 1; + + /*! data source 0- filter & 1 pre-filter */ + uint8_t low_data_src : 1; + + /*! 1 - enable low-g, 0 - disable low-g */ + uint8_t low_en : 1; +#else + + /*! 1 - enable low-g, 0 - disable low-g */ + uint8_t low_en : 1; + + /*! data source 0- filter & 1 pre-filter */ + uint8_t low_data_src : 1; + + /*! 0 - single-axis mode ,1 - axis-summing mode */ + uint8_t low_mode : 1; + + /*! hysteresis of low-g interrupt */ + uint8_t low_hyst : 2; + + /*! low-g interrupt trigger threshold */ + uint8_t low_thres; + + /*! low-g interrupt trigger delay */ + uint8_t low_dur; +#endif +}; +struct bmi160_acc_high_g_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! High-g interrupt x, 1 - enable, 0 - disable */ + uint8_t high_g_x : 1; + + /*! High-g interrupt y, 1 - enable, 0 - disable */ + uint8_t high_g_y : 1; + + /*! High-g interrupt z, 1 - enable, 0 - disable */ + uint8_t high_g_z : 1; + + /*! High-g hysteresis */ + uint8_t high_hy : 2; + + /*! data source 0- filter & 1 pre-filter */ + uint8_t high_data_src : 1; + + /*! High-g threshold */ + uint8_t high_thres; + + /*! High-g duration */ + uint8_t high_dur; +#else + + /*! High-g duration */ + uint8_t high_dur; + + /*! High-g threshold */ + uint8_t high_thres; + + /*! data source 0- filter & 1 pre-filter */ + uint8_t high_data_src : 1; + + /*! High-g hysteresis */ + uint8_t high_hy : 2; + + /*! High-g interrupt z, 1 - enable, 0 - disable */ + uint8_t high_g_z : 1; + + /*! High-g interrupt y, 1 - enable, 0 - disable */ + uint8_t high_g_y : 1; + + /*! High-g interrupt x, 1 - enable, 0 - disable */ + uint8_t high_g_x : 1; +#endif +}; +struct bmi160_int_pin_settg { +#ifdef LITTLE_ENDIAN + + /*! To enable either INT1 or INT2 pin as output. + * 0- output disabled ,1- output enabled */ + uint16_t output_en : 1; + + /*! 0 - push-pull 1- open drain,only valid if output_en is set 1 */ + uint16_t output_mode : 1; + + /*! 0 - active low , 1 - active high level. + * if output_en is 1,this applies to interrupts,else PMU_trigger */ + uint16_t output_type : 1; + + /*! 0 - level trigger , 1 - edge trigger */ + uint16_t edge_ctrl : 1; + + /*! To enable either INT1 or INT2 pin as input. + * 0 - input disabled ,1 - input enabled */ + uint16_t input_en : 1; + + /*! latch duration*/ + uint16_t latch_dur : 4; +#else + + /*! latch duration*/ + uint16_t latch_dur : 4; + + /*! Latched,non-latched or temporary interrupt modes */ + uint16_t input_en : 1; + + /*! 1 - edge trigger, 0 - level trigger */ + uint16_t edge_ctrl : 1; + + /*! 0 - active low , 1 - active high level. + * if output_en is 1,this applies to interrupts,else PMU_trigger */ + uint16_t output_type : 1; + + /*! 0 - push-pull , 1 - open drain,only valid if output_en is set 1 */ + uint16_t output_mode : 1; + + /*! To enable either INT1 or INT2 pin as output. + * 0 - output disabled , 1 - output enabled */ + uint16_t output_en : 1; +#endif +}; +union bmi160_int_type_cfg { + /*! Tap interrupt structure */ + struct bmi160_acc_tap_int_cfg acc_tap_int; + + /*! Slope interrupt structure */ + struct bmi160_acc_any_mot_int_cfg acc_any_motion_int; + + /*! Significant motion interrupt structure */ + struct bmi160_acc_sig_mot_int_cfg acc_sig_motion_int; + + /*! Step detector interrupt structure */ + struct bmi160_acc_step_detect_int_cfg acc_step_detect_int; + + /*! No motion interrupt structure */ + struct bmi160_acc_no_motion_int_cfg acc_no_motion_int; + + /*! Orientation interrupt structure */ + struct bmi160_acc_orient_int_cfg acc_orient_int; + + /*! Flat interrupt structure */ + struct bmi160_acc_flat_detect_int_cfg acc_flat_int; + + /*! Low-g interrupt structure */ + struct bmi160_acc_low_g_int_cfg acc_low_g_int; + + /*! High-g interrupt structure */ + struct bmi160_acc_high_g_int_cfg acc_high_g_int; +}; +struct bmi160_int_settg { + /*! Interrupt channel */ + enum bmi160_int_channel int_channel; + + /*! Select Interrupt */ + enum bmi160_int_types int_type; + + /*! Structure configuring Interrupt pins */ + struct bmi160_int_pin_settg int_pin_settg; + + /*! Union configures required interrupt */ + union bmi160_int_type_cfg int_type_cfg; + + /*! FIFO FULL INT 1-enable, 0-disable */ + uint8_t fifo_full_int_en : 1; + + /*! FIFO WTM INT 1-enable, 0-disable */ + uint8_t fifo_wtm_int_en : 1; +}; + +/*! + * @brief This structure holds the information for usage of + * FIFO by the user. + */ +struct bmi160_fifo_frame { + /*! Data buffer of user defined length is to be mapped here */ + uint8_t* data; + + /*! While calling the API "bmi160_get_fifo_data" , length stores + * number of bytes in FIFO to be read (specified by user as input) + * and after execution of the API ,number of FIFO data bytes + * available is provided as an output to user + */ + uint16_t length; + + /*! FIFO time enable */ + uint8_t fifo_time_enable; + + /*! Enabling of the FIFO header to stream in header mode */ + uint8_t fifo_header_enable; + + /*! Streaming of the Accelerometer, Gyroscope + * sensor data or both in FIFO */ + uint8_t fifo_data_enable; + + /*! Will be equal to length when no more frames are there to parse */ + uint16_t accel_byte_start_idx; + + /*! Will be equal to length when no more frames are there to parse */ + uint16_t gyro_byte_start_idx; + + /*! Will be equal to length when no more frames are there to parse */ + uint16_t aux_byte_start_idx; + + /*! Value of FIFO sensor time time */ + uint32_t sensor_time; + + /*! Value of Skipped frame counts */ + uint8_t skipped_frame_count; +}; +struct bmi160_dev { + /*! Chip Id */ + uint8_t chip_id; + + /*! Device Id */ + uint8_t id; + + /*! 0 - I2C , 1 - SPI Interface */ + uint8_t intf; + + /*! Hold active interrupts status for any and sig motion + * 0 - Any-motion enable, 1 - Sig-motion enable, + * -1 neither any-motion nor sig-motion selected */ + enum bmi160_any_sig_motion_active_interrupt_state any_sig_sel; + + /*! Structure to configure Accel sensor */ + struct bmi160_cfg accel_cfg; + + /*! Structure to hold previous/old accel config parameters. + * This is used at driver level to prevent overwriting of same + * data, hence user does not change it in the code */ + struct bmi160_cfg prev_accel_cfg; + + /*! Structure to configure Gyro sensor */ + struct bmi160_cfg gyro_cfg; + + /*! Structure to hold previous/old gyro config parameters. + * This is used at driver level to prevent overwriting of same + * data, hence user does not change it in the code */ + struct bmi160_cfg prev_gyro_cfg; + + /*! Structure to configure the auxiliary sensor */ + struct bmi160_aux_cfg aux_cfg; + + /*! Structure to hold previous/old aux config parameters. + * This is used at driver level to prevent overwriting of same + * data, hence user does not change it in the code */ + struct bmi160_aux_cfg prev_aux_cfg; + + /*! FIFO related configurations */ + struct bmi160_fifo_frame* fifo; + + /*! Read function pointer */ + bmi160_read_fptr_t read; + + /*! Write function pointer */ + bmi160_write_fptr_t write; + + /*! Delay function pointer */ + bmi160_delay_fptr_t delay_ms; + + /*! User set read/write length */ + uint16_t read_write_len; +}; + +#endif /* BMI160_DEFS_H_ */ diff --git a/applications/plugins/airmouse/tracking/imu/imu.c b/applications/plugins/airmouse/tracking/imu/imu.c new file mode 100644 index 000000000..5e89c9504 --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/imu.c @@ -0,0 +1,29 @@ +#include "imu.h" +#include + +bool bmi160_begin(); +int bmi160_read(double* vec); + +bool lsm6ds3trc_begin(); +void lsm6ds3trc_end(); +int lsm6ds3trc_read(double* vec); + +bool imu_begin() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + bool ret = bmi160_begin(); // lsm6ds3trc_begin(); + furi_hal_i2c_release(&furi_hal_i2c_handle_external); + return ret; +} + +void imu_end() { + // furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + // lsm6ds3trc_end(); + // furi_hal_i2c_release(&furi_hal_i2c_handle_external); +} + +int imu_read(double* vec) { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + int ret = bmi160_read(vec); // lsm6ds3trc_read(vec); + furi_hal_i2c_release(&furi_hal_i2c_handle_external); + return ret; +} diff --git a/applications/plugins/airmouse/tracking/imu/imu.h b/applications/plugins/airmouse/tracking/imu/imu.h new file mode 100644 index 000000000..f4c5e4b1d --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/imu.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ACC_DATA_READY (1 << 0) +#define GYR_DATA_READY (1 << 1) + +bool imu_begin(); +void imu_end(); +int imu_read(double* vec); + +#ifdef __cplusplus +} +#endif diff --git a/applications/plugins/airmouse/tracking/imu/imu_bmi160.c b/applications/plugins/airmouse/tracking/imu/imu_bmi160.c new file mode 100644 index 000000000..af771302f --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/imu_bmi160.c @@ -0,0 +1,88 @@ +#include "bmi160.h" + +#include + +#include "imu.h" + +#define TAG "BMI160" + +#define BMI160_DEV_ADDR (0x69 << 1) + +static const double DEG_TO_RAD = 0.017453292519943295769236907684886; +static const double G = 9.81; + +struct bmi160_dev bmi160dev; +struct bmi160_sensor_data bmi160_accel; +struct bmi160_sensor_data bmi160_gyro; + +int8_t bmi160_write_i2c(uint8_t dev_addr, uint8_t reg_addr, uint8_t* data, uint16_t len) { + if(furi_hal_i2c_write_mem(&furi_hal_i2c_handle_external, dev_addr, reg_addr, data, len, 50)) + return BMI160_OK; + return BMI160_E_COM_FAIL; +} + +int8_t bmi160_read_i2c(uint8_t dev_addr, uint8_t reg_addr, uint8_t* read_data, uint16_t len) { + if(furi_hal_i2c_read_mem(&furi_hal_i2c_handle_external, dev_addr, reg_addr, read_data, len, 50)) + return BMI160_OK; + return BMI160_E_COM_FAIL; +} + +bool bmi160_begin() { + FURI_LOG_I(TAG, "Init BMI160"); + + if(!furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, BMI160_DEV_ADDR, 50)) { + FURI_LOG_E(TAG, "Device not ready!"); + return false; + } + + FURI_LOG_I(TAG, "Device ready!"); + + bmi160dev.id = BMI160_DEV_ADDR; + bmi160dev.intf = BMI160_I2C_INTF; + bmi160dev.read = bmi160_read_i2c; + bmi160dev.write = bmi160_write_i2c; + bmi160dev.delay_ms = furi_delay_ms; + + if(bmi160_init(&bmi160dev) != BMI160_OK) { + FURI_LOG_E(TAG, "Initialization failure!"); + FURI_LOG_E(TAG, "Chip ID 0x%X", bmi160dev.chip_id); + return false; + } + + bmi160dev.accel_cfg.odr = BMI160_ACCEL_ODR_400HZ; + bmi160dev.accel_cfg.range = BMI160_ACCEL_RANGE_4G; + bmi160dev.accel_cfg.bw = BMI160_ACCEL_BW_NORMAL_AVG4; + bmi160dev.accel_cfg.power = BMI160_ACCEL_NORMAL_MODE; + bmi160dev.gyro_cfg.odr = BMI160_GYRO_ODR_400HZ; + bmi160dev.gyro_cfg.range = BMI160_GYRO_RANGE_2000_DPS; + bmi160dev.gyro_cfg.bw = BMI160_GYRO_BW_NORMAL_MODE; + bmi160dev.gyro_cfg.power = BMI160_GYRO_NORMAL_MODE; + + if(bmi160_set_sens_conf(&bmi160dev) != BMI160_OK) { + FURI_LOG_E(TAG, "Initialization failure!"); + FURI_LOG_E(TAG, "Chip ID 0x%X", bmi160dev.chip_id); + return false; + } + + FURI_LOG_I(TAG, "Initialization success!"); + FURI_LOG_I(TAG, "Chip ID 0x%X", bmi160dev.chip_id); + + return true; +} + +int bmi160_read(double* vec) { + if(bmi160_get_sensor_data( + (BMI160_ACCEL_SEL | BMI160_GYRO_SEL), &bmi160_accel, &bmi160_gyro, &bmi160dev) != + BMI160_OK) { + return 0; + } + + vec[0] = ((double)bmi160_accel.x * 4 / 32768) * G; + vec[1] = ((double)bmi160_accel.y * 4 / 32768) * G; + vec[2] = ((double)bmi160_accel.z * 4 / 32768) * G; + vec[3] = ((double)bmi160_gyro.x * 2000 / 32768) * DEG_TO_RAD; + vec[4] = ((double)bmi160_gyro.y * 2000 / 32768) * DEG_TO_RAD; + vec[5] = ((double)bmi160_gyro.z * 2000 / 32768) * DEG_TO_RAD; + + return ACC_DATA_READY | GYR_DATA_READY; +} diff --git a/applications/plugins/airmouse/tracking/imu/imu_lsm6ds3trc.c b/applications/plugins/airmouse/tracking/imu/imu_lsm6ds3trc.c new file mode 100644 index 000000000..c013fc6e6 --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/imu_lsm6ds3trc.c @@ -0,0 +1,94 @@ +#include "lsm6ds3tr_c_reg.h" + +#include + +#include "imu.h" + +#define TAG "LSM6DS3TR-C" + +#define LSM6DS3_ADDRESS (0x6A << 1) + +static const double DEG_TO_RAD = 0.017453292519943295769236907684886; + +stmdev_ctx_t lsm6ds3trc_ctx; + +int32_t lsm6ds3trc_write_i2c(void* handle, uint8_t reg_addr, const uint8_t* data, uint16_t len) { + if(furi_hal_i2c_write_mem(handle, LSM6DS3_ADDRESS, reg_addr, (uint8_t*)data, len, 50)) + return 0; + return -1; +} + +int32_t lsm6ds3trc_read_i2c(void* handle, uint8_t reg_addr, uint8_t* read_data, uint16_t len) { + if(furi_hal_i2c_read_mem(handle, LSM6DS3_ADDRESS, reg_addr, read_data, len, 50)) return 0; + return -1; +} + +bool lsm6ds3trc_begin() { + FURI_LOG_I(TAG, "Init LSM6DS3TR-C"); + + if(!furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, LSM6DS3_ADDRESS, 50)) { + FURI_LOG_E(TAG, "Not ready"); + return false; + } + + lsm6ds3trc_ctx.write_reg = lsm6ds3trc_write_i2c; + lsm6ds3trc_ctx.read_reg = lsm6ds3trc_read_i2c; + lsm6ds3trc_ctx.mdelay = furi_delay_ms; + lsm6ds3trc_ctx.handle = &furi_hal_i2c_handle_external; + + uint8_t whoami; + lsm6ds3tr_c_device_id_get(&lsm6ds3trc_ctx, &whoami); + if(whoami != LSM6DS3TR_C_ID) { + FURI_LOG_I(TAG, "Unknown model: %x", (int)whoami); + return false; + } + + lsm6ds3tr_c_reset_set(&lsm6ds3trc_ctx, PROPERTY_ENABLE); + uint8_t rst = PROPERTY_ENABLE; + while(rst) lsm6ds3tr_c_reset_get(&lsm6ds3trc_ctx, &rst); + + lsm6ds3tr_c_block_data_update_set(&lsm6ds3trc_ctx, PROPERTY_ENABLE); + lsm6ds3tr_c_fifo_mode_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_BYPASS_MODE); + + lsm6ds3tr_c_xl_data_rate_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_XL_ODR_104Hz); + lsm6ds3tr_c_xl_full_scale_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_4g); + lsm6ds3tr_c_xl_lp1_bandwidth_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_XL_LP1_ODR_DIV_4); + + lsm6ds3tr_c_gy_data_rate_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_GY_ODR_104Hz); + lsm6ds3tr_c_gy_full_scale_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_2000dps); + lsm6ds3tr_c_gy_power_mode_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_GY_HIGH_PERFORMANCE); + lsm6ds3tr_c_gy_band_pass_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_LP2_ONLY); + + FURI_LOG_I(TAG, "Init OK"); + return true; +} + +void lsm6ds3trc_end() { + lsm6ds3tr_c_xl_data_rate_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_XL_ODR_OFF); + lsm6ds3tr_c_gy_data_rate_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_GY_ODR_OFF); +} + +int lsm6ds3trc_read(double* vec) { + int ret = 0; + int16_t data[3]; + lsm6ds3tr_c_reg_t reg; + lsm6ds3tr_c_status_reg_get(&lsm6ds3trc_ctx, ®.status_reg); + + if(reg.status_reg.xlda) { + lsm6ds3tr_c_acceleration_raw_get(&lsm6ds3trc_ctx, data); + vec[2] = (double)lsm6ds3tr_c_from_fs2g_to_mg(data[0]) / 1000; + vec[0] = (double)lsm6ds3tr_c_from_fs2g_to_mg(data[1]) / 1000; + vec[1] = (double)lsm6ds3tr_c_from_fs2g_to_mg(data[2]) / 1000; + ret |= ACC_DATA_READY; + } + + if(reg.status_reg.gda) { + lsm6ds3tr_c_angular_rate_raw_get(&lsm6ds3trc_ctx, data); + vec[5] = (double)lsm6ds3tr_c_from_fs2000dps_to_mdps(data[0]) * DEG_TO_RAD / 1000; + vec[3] = (double)lsm6ds3tr_c_from_fs2000dps_to_mdps(data[1]) * DEG_TO_RAD / 1000; + vec[4] = (double)lsm6ds3tr_c_from_fs2000dps_to_mdps(data[2]) * DEG_TO_RAD / 1000; + ret |= GYR_DATA_READY; + } + + return ret; +} diff --git a/applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.c b/applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.c new file mode 100644 index 000000000..9f1890d2c --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.c @@ -0,0 +1,7105 @@ +/** + ****************************************************************************** + * @file lsm6ds3tr_c_reg.c + * @author Sensors Software Solution Team + * @brief LSM6DS3TR_C driver file + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2021 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +#include "lsm6ds3tr_c_reg.h" + +/** + * @defgroup LSM6DS3TR_C + * @brief This file provides a set of functions needed to drive the + * lsm6ds3tr_c enanced inertial module. + * @{ + * + */ + +/** + * @defgroup LSM6DS3TR_C_interfaces_functions + * @brief This section provide a set of functions used to read and + * write a generic register of the device. + * MANDATORY: return 0 -> no Error. + * @{ + * + */ + +/** + * @brief Read generic device register + * + * @param ctx read / write interface definitions(ptr) + * @param reg register to read + * @param data pointer to buffer that store the data read(ptr) + * @param len number of consecutive register to read + * @retval interface status (MANDATORY: return 0 -> no Error) + * + */ +int32_t lsm6ds3tr_c_read_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, uint16_t len) { + int32_t ret; + + ret = ctx->read_reg(ctx->handle, reg, data, len); + + return ret; +} + +/** + * @brief Write generic device register + * + * @param ctx read / write interface definitions(ptr) + * @param reg register to write + * @param data pointer to data to write in register reg(ptr) + * @param len number of consecutive register to write + * @retval interface status (MANDATORY: return 0 -> no Error) + * + */ +int32_t lsm6ds3tr_c_write_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, uint16_t len) { + int32_t ret; + + ret = ctx->write_reg(ctx->handle, reg, data, len); + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Sensitivity + * @brief These functions convert raw-data into engineering units. + * @{ + * + */ + +float_t lsm6ds3tr_c_from_fs2g_to_mg(int16_t lsb) { + return ((float_t)lsb * 0.061f); +} + +float_t lsm6ds3tr_c_from_fs4g_to_mg(int16_t lsb) { + return ((float_t)lsb * 0.122f); +} + +float_t lsm6ds3tr_c_from_fs8g_to_mg(int16_t lsb) { + return ((float_t)lsb * 0.244f); +} + +float_t lsm6ds3tr_c_from_fs16g_to_mg(int16_t lsb) { + return ((float_t)lsb * 0.488f); +} + +float_t lsm6ds3tr_c_from_fs125dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 4.375f); +} + +float_t lsm6ds3tr_c_from_fs250dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 8.750f); +} + +float_t lsm6ds3tr_c_from_fs500dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 17.50f); +} + +float_t lsm6ds3tr_c_from_fs1000dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 35.0f); +} + +float_t lsm6ds3tr_c_from_fs2000dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 70.0f); +} + +float_t lsm6ds3tr_c_from_lsb_to_celsius(int16_t lsb) { + return (((float_t)lsb / 256.0f) + 25.0f); +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_data_generation + * @brief This section groups all the functions concerning data + * generation + * @{ + * + */ + +/** + * @brief Accelerometer full-scale selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fs_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_xl_t val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ctrl1_xl.fs_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + } + + return ret; +} + +/** + * @brief Accelerometer full-scale selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of fs_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_xl_t* val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + switch(ctrl1_xl.fs_xl) { + case LSM6DS3TR_C_2g: + *val = LSM6DS3TR_C_2g; + break; + + case LSM6DS3TR_C_16g: + *val = LSM6DS3TR_C_16g; + break; + + case LSM6DS3TR_C_4g: + *val = LSM6DS3TR_C_4g; + break; + + case LSM6DS3TR_C_8g: + *val = LSM6DS3TR_C_8g; + break; + + default: + *val = LSM6DS3TR_C_XL_FS_ND; + break; + } + + return ret; +} + +/** + * @brief Accelerometer data rate selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of odr_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_xl_t val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ctrl1_xl.odr_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + } + + return ret; +} + +/** + * @brief Accelerometer data rate selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of odr_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_xl_t* val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + switch(ctrl1_xl.odr_xl) { + case LSM6DS3TR_C_XL_ODR_OFF: + *val = LSM6DS3TR_C_XL_ODR_OFF; + break; + + case LSM6DS3TR_C_XL_ODR_12Hz5: + *val = LSM6DS3TR_C_XL_ODR_12Hz5; + break; + + case LSM6DS3TR_C_XL_ODR_26Hz: + *val = LSM6DS3TR_C_XL_ODR_26Hz; + break; + + case LSM6DS3TR_C_XL_ODR_52Hz: + *val = LSM6DS3TR_C_XL_ODR_52Hz; + break; + + case LSM6DS3TR_C_XL_ODR_104Hz: + *val = LSM6DS3TR_C_XL_ODR_104Hz; + break; + + case LSM6DS3TR_C_XL_ODR_208Hz: + *val = LSM6DS3TR_C_XL_ODR_208Hz; + break; + + case LSM6DS3TR_C_XL_ODR_416Hz: + *val = LSM6DS3TR_C_XL_ODR_416Hz; + break; + + case LSM6DS3TR_C_XL_ODR_833Hz: + *val = LSM6DS3TR_C_XL_ODR_833Hz; + break; + + case LSM6DS3TR_C_XL_ODR_1k66Hz: + *val = LSM6DS3TR_C_XL_ODR_1k66Hz; + break; + + case LSM6DS3TR_C_XL_ODR_3k33Hz: + *val = LSM6DS3TR_C_XL_ODR_3k33Hz; + break; + + case LSM6DS3TR_C_XL_ODR_6k66Hz: + *val = LSM6DS3TR_C_XL_ODR_6k66Hz; + break; + + case LSM6DS3TR_C_XL_ODR_1Hz6: + *val = LSM6DS3TR_C_XL_ODR_1Hz6; + break; + + default: + *val = LSM6DS3TR_C_XL_ODR_ND; + break; + } + + return ret; +} + +/** + * @brief Gyroscope chain full-scale selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fs_g in reg CTRL2_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_g_t val) { + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + + if(ret == 0) { + ctrl2_g.fs_g = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + } + + return ret; +} + +/** + * @brief Gyroscope chain full-scale selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of fs_g in reg CTRL2_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_g_t* val) { + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + + switch(ctrl2_g.fs_g) { + case LSM6DS3TR_C_250dps: + *val = LSM6DS3TR_C_250dps; + break; + + case LSM6DS3TR_C_125dps: + *val = LSM6DS3TR_C_125dps; + break; + + case LSM6DS3TR_C_500dps: + *val = LSM6DS3TR_C_500dps; + break; + + case LSM6DS3TR_C_1000dps: + *val = LSM6DS3TR_C_1000dps; + break; + + case LSM6DS3TR_C_2000dps: + *val = LSM6DS3TR_C_2000dps; + break; + + default: + *val = LSM6DS3TR_C_GY_FS_ND; + break; + } + + return ret; +} + +/** + * @brief Gyroscope data rate selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of odr_g in reg CTRL2_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_g_t val) { + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + + if(ret == 0) { + ctrl2_g.odr_g = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + } + + return ret; +} + +/** + * @brief Gyroscope data rate selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of odr_g in reg CTRL2_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_g_t* val) { + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + + switch(ctrl2_g.odr_g) { + case LSM6DS3TR_C_GY_ODR_OFF: + *val = LSM6DS3TR_C_GY_ODR_OFF; + break; + + case LSM6DS3TR_C_GY_ODR_12Hz5: + *val = LSM6DS3TR_C_GY_ODR_12Hz5; + break; + + case LSM6DS3TR_C_GY_ODR_26Hz: + *val = LSM6DS3TR_C_GY_ODR_26Hz; + break; + + case LSM6DS3TR_C_GY_ODR_52Hz: + *val = LSM6DS3TR_C_GY_ODR_52Hz; + break; + + case LSM6DS3TR_C_GY_ODR_104Hz: + *val = LSM6DS3TR_C_GY_ODR_104Hz; + break; + + case LSM6DS3TR_C_GY_ODR_208Hz: + *val = LSM6DS3TR_C_GY_ODR_208Hz; + break; + + case LSM6DS3TR_C_GY_ODR_416Hz: + *val = LSM6DS3TR_C_GY_ODR_416Hz; + break; + + case LSM6DS3TR_C_GY_ODR_833Hz: + *val = LSM6DS3TR_C_GY_ODR_833Hz; + break; + + case LSM6DS3TR_C_GY_ODR_1k66Hz: + *val = LSM6DS3TR_C_GY_ODR_1k66Hz; + break; + + case LSM6DS3TR_C_GY_ODR_3k33Hz: + *val = LSM6DS3TR_C_GY_ODR_3k33Hz; + break; + + case LSM6DS3TR_C_GY_ODR_6k66Hz: + *val = LSM6DS3TR_C_GY_ODR_6k66Hz; + break; + + default: + *val = LSM6DS3TR_C_GY_ODR_ND; + break; + } + + return ret; +} + +/** + * @brief Block data update.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of bdu in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_block_data_update_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.bdu = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Block data update.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of bdu in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_block_data_update_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + *val = ctrl3_c.bdu; + + return ret; +} + +/** + * @brief Weight of XL user offset bits of registers + * X_OFS_USR(73h), Y_OFS_USR(74h), Z_OFS_USR(75h).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of usr_off_w in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_offset_weight_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_usr_off_w_t val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ctrl6_c.usr_off_w = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + } + + return ret; +} + +/** + * @brief Weight of XL user offset bits of registers + * X_OFS_USR(73h), Y_OFS_USR(74h), Z_OFS_USR(75h).[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of usr_off_w in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_offset_weight_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_usr_off_w_t* val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + switch(ctrl6_c.usr_off_w) { + case LSM6DS3TR_C_LSb_1mg: + *val = LSM6DS3TR_C_LSb_1mg; + break; + + case LSM6DS3TR_C_LSb_16mg: + *val = LSM6DS3TR_C_LSb_16mg; + break; + + default: + *val = LSM6DS3TR_C_WEIGHT_ND; + break; + } + + return ret; +} + +/** + * @brief High-performance operating mode for accelerometer[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of xl_hm_mode in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_power_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_xl_hm_mode_t val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ctrl6_c.xl_hm_mode = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + } + + return ret; +} + +/** + * @brief High-performance operating mode for accelerometer.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of xl_hm_mode in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_power_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_xl_hm_mode_t* val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + switch(ctrl6_c.xl_hm_mode) { + case LSM6DS3TR_C_XL_HIGH_PERFORMANCE: + *val = LSM6DS3TR_C_XL_HIGH_PERFORMANCE; + break; + + case LSM6DS3TR_C_XL_NORMAL: + *val = LSM6DS3TR_C_XL_NORMAL; + break; + + default: + *val = LSM6DS3TR_C_XL_PW_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Source register rounding function on WAKE_UP_SRC (1Bh), + * TAP_SRC (1Ch), D6D_SRC (1Dh), STATUS_REG (1Eh) and + * FUNC_SRC1 (53h) registers in the primary interface.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of rounding_status in reg CTRL7_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_rounding_on_status_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_status_t val) { + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + if(ret == 0) { + ctrl7_g.rounding_status = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + } + + return ret; +} + +/** + * @brief Source register rounding function on WAKE_UP_SRC (1Bh), + * TAP_SRC (1Ch), D6D_SRC (1Dh), STATUS_REG (1Eh) and + * FUNC_SRC1 (53h) registers in the primary interface.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of rounding_status in reg CTRL7_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_rounding_on_status_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_status_t* val) { + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + switch(ctrl7_g.rounding_status) { + case LSM6DS3TR_C_STAT_RND_DISABLE: + *val = LSM6DS3TR_C_STAT_RND_DISABLE; + break; + + case LSM6DS3TR_C_STAT_RND_ENABLE: + *val = LSM6DS3TR_C_STAT_RND_ENABLE; + break; + + default: + *val = LSM6DS3TR_C_STAT_RND_ND; + break; + } + + return ret; +} + +/** + * @brief High-performance operating mode disable for gyroscope.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of g_hm_mode in reg CTRL7_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_power_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_g_hm_mode_t val) { + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + if(ret == 0) { + ctrl7_g.g_hm_mode = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + } + + return ret; +} + +/** + * @brief High-performance operating mode disable for gyroscope.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of g_hm_mode in reg CTRL7_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_power_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_g_hm_mode_t* val) { + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + switch(ctrl7_g.g_hm_mode) { + case LSM6DS3TR_C_GY_HIGH_PERFORMANCE: + *val = LSM6DS3TR_C_GY_HIGH_PERFORMANCE; + break; + + case LSM6DS3TR_C_GY_NORMAL: + *val = LSM6DS3TR_C_GY_NORMAL; + break; + + default: + *val = LSM6DS3TR_C_GY_PW_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Read all the interrupt/status flag of the device.[get] + * + * @param ctx Read / write interface definitions + * @param val WAKE_UP_SRC, TAP_SRC, D6D_SRC, STATUS_REG, + * FUNC_SRC1, FUNC_SRC2, WRIST_TILT_IA, A_WRIST_TILT_Mask + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_all_sources_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_all_sources_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_SRC, (uint8_t*)&(val->wake_up_src), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_SRC, (uint8_t*)&(val->tap_src), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_D6D_SRC, (uint8_t*)&(val->d6d_src), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)&(val->status_reg), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FUNC_SRC1, (uint8_t*)&(val->func_src1), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FUNC_SRC2, (uint8_t*)&(val->func_src2), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_WRIST_TILT_IA, (uint8_t*)&(val->wrist_tilt_ia), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_A_WRIST_TILT_MASK, (uint8_t*)&(val->a_wrist_tilt_mask), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + + return ret; +} +/** + * @brief The STATUS_REG register is read by the primary interface[get] + * + * @param ctx Read / write interface definitions + * @param val Registers STATUS_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_status_reg_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_status_reg_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)val, 1); + + return ret; +} + +/** + * @brief Accelerometer new data available.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of xlda in reg STATUS_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_status_reg_t status_reg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)&status_reg, 1); + *val = status_reg.xlda; + + return ret; +} + +/** + * @brief Gyroscope new data available.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of gda in reg STATUS_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_status_reg_t status_reg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)&status_reg, 1); + *val = status_reg.gda; + + return ret; +} + +/** + * @brief Temperature new data available.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tda in reg STATUS_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_temp_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_status_reg_t status_reg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)&status_reg, 1); + *val = status_reg.tda; + + return ret; +} + +/** + * @brief Accelerometer axis user offset correction expressed in two’s + * complement, weight depends on USR_OFF_W in CTRL6_C. + * The value must be in the range [-127 127].[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_usr_offset_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_X_OFS_USR, buff, 3); + + return ret; +} + +/** + * @brief Accelerometer axis user offset correction xpressed in two’s + * complement, weight depends on USR_OFF_W in CTRL6_C. + * The value must be in the range [-127 127].[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_usr_offset_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_X_OFS_USR, buff, 3); + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Timestamp + * @brief This section groups all the functions that manage the + * timestamp generation. + * @{ + * + */ + +/** + * @brief Enable timestamp count. The count is saved in TIMESTAMP0_REG (40h), + * TIMESTAMP1_REG (41h) and TIMESTAMP2_REG (42h).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_timestamp_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.timer_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + } + + return ret; +} + +/** + * @brief Enable timestamp count. The count is saved in TIMESTAMP0_REG (40h), + * TIMESTAMP1_REG (41h) and TIMESTAMP2_REG (42h).[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_timestamp_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.timer_en; + + return ret; +} + +/** + * @brief Timestamp register resolution setting. + * Configuration of this bit affects + * TIMESTAMP0_REG(40h), TIMESTAMP1_REG(41h), + * TIMESTAMP2_REG(42h), STEP_TIMESTAMP_L(49h), + * STEP_TIMESTAMP_H(4Ah) and + * STEP_COUNT_DELTA(15h) registers.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_hr in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_timestamp_res_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_timer_hr_t val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + wake_up_dur.timer_hr = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + } + + return ret; +} + +/** + * @brief Timestamp register resolution setting. + * Configuration of this bit affects + * TIMESTAMP0_REG(40h), TIMESTAMP1_REG(41h), + * TIMESTAMP2_REG(42h), STEP_TIMESTAMP_L(49h), + * STEP_TIMESTAMP_H(4Ah) and + * STEP_COUNT_DELTA(15h) registers.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of timer_hr in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_timestamp_res_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_timer_hr_t* val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + switch(wake_up_dur.timer_hr) { + case LSM6DS3TR_C_LSB_6ms4: + *val = LSM6DS3TR_C_LSB_6ms4; + break; + + case LSM6DS3TR_C_LSB_25us: + *val = LSM6DS3TR_C_LSB_25us; + break; + + default: + *val = LSM6DS3TR_C_TS_RES_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Dataoutput + * @brief This section groups all the data output functions. + * @{ + * + */ + +/** + * @brief Circular burst-mode (rounding) read from output registers + * through the primary interface.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of rounding in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_rounding_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_t val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + if(ret == 0) { + ctrl5_c.rounding = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + } + + return ret; +} + +/** + * @brief Circular burst-mode (rounding) read from output registers + * through the primary interface.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of rounding in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_rounding_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_t* val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + switch(ctrl5_c.rounding) { + case LSM6DS3TR_C_ROUND_DISABLE: + *val = LSM6DS3TR_C_ROUND_DISABLE; + break; + + case LSM6DS3TR_C_ROUND_XL: + *val = LSM6DS3TR_C_ROUND_XL; + break; + + case LSM6DS3TR_C_ROUND_GY: + *val = LSM6DS3TR_C_ROUND_GY; + break; + + case LSM6DS3TR_C_ROUND_GY_XL: + *val = LSM6DS3TR_C_ROUND_GY_XL; + break; + + case LSM6DS3TR_C_ROUND_SH1_TO_SH6: + *val = LSM6DS3TR_C_ROUND_SH1_TO_SH6; + break; + + case LSM6DS3TR_C_ROUND_XL_SH1_TO_SH6: + *val = LSM6DS3TR_C_ROUND_XL_SH1_TO_SH6; + break; + + case LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH12: + *val = LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH12; + break; + + case LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH6: + *val = LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH6; + break; + + default: + *val = LSM6DS3TR_C_ROUND_OUT_ND; + break; + } + + return ret; +} + +/** + * @brief Temperature data output register (r). L and H registers together + * express a 16-bit word in two’s complement.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_temperature_raw_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[2]; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_OUT_TEMP_L, buff, 2); + *val = (int16_t)buff[1]; + *val = (*val * 256) + (int16_t)buff[0]; + + return ret; +} + +/** + * @brief Angular rate sensor. The value is expressed as a 16-bit word in + * two’s complement.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_angular_rate_raw_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_OUTX_L_G, buff, 6); + val[0] = (int16_t)buff[1]; + val[0] = (val[0] * 256) + (int16_t)buff[0]; + val[1] = (int16_t)buff[3]; + val[1] = (val[1] * 256) + (int16_t)buff[2]; + val[2] = (int16_t)buff[5]; + val[2] = (val[2] * 256) + (int16_t)buff[4]; + + return ret; +} + +/** + * @brief Linear acceleration output register. The value is expressed + * as a 16-bit word in two’s complement.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_acceleration_raw_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_OUTX_L_XL, buff, 6); + val[0] = (int16_t)buff[1]; + val[0] = (val[0] * 256) + (int16_t)buff[0]; + val[1] = (int16_t)buff[3]; + val[1] = (val[1] * 256) + (int16_t)buff[2]; + val[2] = (int16_t)buff[5]; + val[2] = (val[2] * 256) + (int16_t)buff[4]; + + return ret; +} + +/** + * @brief External magnetometer raw data.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_calibrated_raw_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_OUT_MAG_RAW_X_L, buff, 6); + val[0] = (int16_t)buff[1]; + val[0] = (val[0] * 256) + (int16_t)buff[0]; + val[1] = (int16_t)buff[3]; + val[1] = (val[1] * 256) + (int16_t)buff[2]; + val[2] = (int16_t)buff[5]; + val[2] = (val[2] * 256) + (int16_t)buff[4]; + + return ret; +} + +/** + * @brief Read data in FIFO.[get] + * + * @param ctx Read / write interface definitions + * @param buffer Data buffer to store FIFO data. + * @param len Number of data to read from FIFO. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_raw_data_get(stmdev_ctx_t* ctx, uint8_t* buffer, uint8_t len) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_DATA_OUT_L, buffer, len); + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_common + * @brief This section groups common useful functions. + * @{ + * + */ + +/** + * @brief Enable access to the embedded functions/sensor hub + * configuration registers[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of func_cfg_en in reg FUNC_CFG_ACCESS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mem_bank_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_func_cfg_en_t val) { + lsm6ds3tr_c_func_cfg_access_t func_cfg_access; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FUNC_CFG_ACCESS, (uint8_t*)&func_cfg_access, 1); + + if(ret == 0) { + func_cfg_access.func_cfg_en = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FUNC_CFG_ACCESS, (uint8_t*)&func_cfg_access, 1); + } + + return ret; +} + +/** + * @brief Enable access to the embedded functions/sensor hub configuration + * registers[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of func_cfg_en in reg FUNC_CFG_ACCESS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mem_bank_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_func_cfg_en_t* val) { + lsm6ds3tr_c_func_cfg_access_t func_cfg_access; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FUNC_CFG_ACCESS, (uint8_t*)&func_cfg_access, 1); + + switch(func_cfg_access.func_cfg_en) { + case LSM6DS3TR_C_USER_BANK: + *val = LSM6DS3TR_C_USER_BANK; + break; + + case LSM6DS3TR_C_BANK_B: + *val = LSM6DS3TR_C_BANK_B; + break; + + default: + *val = LSM6DS3TR_C_BANK_ND; + break; + } + + return ret; +} + +/** + * @brief Data-ready pulsed / letched mode[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_pulsed in reg DRDY_PULSE_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_data_ready_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_drdy_pulsed_g_t val) { + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + + if(ret == 0) { + drdy_pulse_cfg_g.drdy_pulsed = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + } + + return ret; +} + +/** + * @brief Data-ready pulsed / letched mode[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of drdy_pulsed in reg DRDY_PULSE_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_data_ready_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_drdy_pulsed_g_t* val) { + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + + switch(drdy_pulse_cfg_g.drdy_pulsed) { + case LSM6DS3TR_C_DRDY_LATCHED: + *val = LSM6DS3TR_C_DRDY_LATCHED; + break; + + case LSM6DS3TR_C_DRDY_PULSED: + *val = LSM6DS3TR_C_DRDY_PULSED; + break; + + default: + *val = LSM6DS3TR_C_DRDY_ND; + break; + } + + return ret; +} + +/** + * @brief DeviceWhoamI.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_device_id_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WHO_AM_I, buff, 1); + + return ret; +} + +/** + * @brief Software reset. Restore the default values in user registers[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sw_reset in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_reset_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.sw_reset = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Software reset. Restore the default values in user registers[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sw_reset in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_reset_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + *val = ctrl3_c.sw_reset; + + return ret; +} + +/** + * @brief Big/Little Endian Data selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ble in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_data_format_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_ble_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.ble = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Big/Little Endian Data selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of ble in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_data_format_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_ble_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + switch(ctrl3_c.ble) { + case LSM6DS3TR_C_LSB_AT_LOW_ADD: + *val = LSM6DS3TR_C_LSB_AT_LOW_ADD; + break; + + case LSM6DS3TR_C_MSB_AT_LOW_ADD: + *val = LSM6DS3TR_C_MSB_AT_LOW_ADD; + break; + + default: + *val = LSM6DS3TR_C_DATA_FMT_ND; + break; + } + + return ret; +} + +/** + * @brief Register address automatically incremented during a multiple byte + * access with a serial interface.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of if_inc in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_auto_increment_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.if_inc = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Register address automatically incremented during a multiple byte + * access with a serial interface.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of if_inc in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_auto_increment_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + *val = ctrl3_c.if_inc; + + return ret; +} + +/** + * @brief Reboot memory content. Reload the calibration parameters.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of boot in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_boot_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.boot = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Reboot memory content. Reload the calibration parameters.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of boot in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_boot_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + *val = ctrl3_c.boot; + + return ret; +} + +/** + * @brief Linear acceleration sensor self-test enable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of st_xl in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_self_test_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_xl_t val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + if(ret == 0) { + ctrl5_c.st_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + } + + return ret; +} + +/** + * @brief Linear acceleration sensor self-test enable.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of st_xl in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_self_test_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_xl_t* val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + switch(ctrl5_c.st_xl) { + case LSM6DS3TR_C_XL_ST_DISABLE: + *val = LSM6DS3TR_C_XL_ST_DISABLE; + break; + + case LSM6DS3TR_C_XL_ST_POSITIVE: + *val = LSM6DS3TR_C_XL_ST_POSITIVE; + break; + + case LSM6DS3TR_C_XL_ST_NEGATIVE: + *val = LSM6DS3TR_C_XL_ST_NEGATIVE; + break; + + default: + *val = LSM6DS3TR_C_XL_ST_ND; + break; + } + + return ret; +} + +/** + * @brief Angular rate sensor self-test enable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of st_g in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_self_test_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_g_t val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + if(ret == 0) { + ctrl5_c.st_g = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + } + + return ret; +} + +/** + * @brief Angular rate sensor self-test enable.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of st_g in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_self_test_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_g_t* val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + switch(ctrl5_c.st_g) { + case LSM6DS3TR_C_GY_ST_DISABLE: + *val = LSM6DS3TR_C_GY_ST_DISABLE; + break; + + case LSM6DS3TR_C_GY_ST_POSITIVE: + *val = LSM6DS3TR_C_GY_ST_POSITIVE; + break; + + case LSM6DS3TR_C_GY_ST_NEGATIVE: + *val = LSM6DS3TR_C_GY_ST_NEGATIVE; + break; + + default: + *val = LSM6DS3TR_C_GY_ST_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_filters + * @brief This section group all the functions concerning the filters + * configuration that impact both accelerometer and gyro. + * @{ + * + */ + +/** + * @brief Mask DRDY on pin (both XL & Gyro) until filter settling ends + * (XL and Gyro independently masked).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_mask in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_filter_settling_mask_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.drdy_mask = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + return ret; +} + +/** + * @brief Mask DRDY on pin (both XL & Gyro) until filter settling ends + * (XL and Gyro independently masked).[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_mask in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_filter_settling_mask_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + *val = ctrl4_c.drdy_mask; + + return ret; +} + +/** + * @brief HPF or SLOPE filter selection on wake-up and Activity/Inactivity + * functions.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slope_fds in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_hp_path_internal_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slope_fds_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.slope_fds = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief HPF or SLOPE filter selection on wake-up and Activity/Inactivity + * functions.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of slope_fds in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_hp_path_internal_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slope_fds_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + switch(tap_cfg.slope_fds) { + case LSM6DS3TR_C_USE_SLOPE: + *val = LSM6DS3TR_C_USE_SLOPE; + break; + + case LSM6DS3TR_C_USE_HPF: + *val = LSM6DS3TR_C_USE_HPF; + break; + + default: + *val = LSM6DS3TR_C_HP_PATH_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_accelerometer_filters + * @brief This section group all the functions concerning the filters + * configuration that impact accelerometer in every mode. + * @{ + * + */ + +/** + * @brief Accelerometer analog chain bandwidth selection (only for + * accelerometer ODR ≥ 1.67 kHz).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of bw0_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_filter_analog_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_bw0_xl_t val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ctrl1_xl.bw0_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + } + + return ret; +} + +/** + * @brief Accelerometer analog chain bandwidth selection (only for + * accelerometer ODR ≥ 1.67 kHz).[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of bw0_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_filter_analog_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_bw0_xl_t* val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + switch(ctrl1_xl.bw0_xl) { + case LSM6DS3TR_C_XL_ANA_BW_1k5Hz: + *val = LSM6DS3TR_C_XL_ANA_BW_1k5Hz; + break; + + case LSM6DS3TR_C_XL_ANA_BW_400Hz: + *val = LSM6DS3TR_C_XL_ANA_BW_400Hz; + break; + + default: + *val = LSM6DS3TR_C_XL_ANA_BW_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_accelerometer_filters + * @brief This section group all the functions concerning the filters + * configuration that impact accelerometer. + * @{ + * + */ + +/** + * @brief Accelerometer digital LPF (LPF1) bandwidth selection LPF2 is + * not used.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of lpf1_bw_sel in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_lp1_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_bw_sel_t val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ctrl1_xl.lpf1_bw_sel = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.lpf2_xl_en = 0; + ctrl8_xl.hp_slope_xl_en = 0; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + } + } + + return ret; +} + +/** + * @brief Accelerometer digital LPF (LPF1) bandwidth selection LPF2 + * is not used.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of lpf1_bw_sel in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_lp1_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_bw_sel_t* val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + if((ctrl8_xl.lpf2_xl_en != 0x00U) || (ctrl8_xl.hp_slope_xl_en != 0x00U)) { + *val = LSM6DS3TR_C_XL_LP1_NA; + } + + else { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + switch(ctrl1_xl.lpf1_bw_sel) { + case LSM6DS3TR_C_XL_LP1_ODR_DIV_2: + *val = LSM6DS3TR_C_XL_LP1_ODR_DIV_2; + break; + + case LSM6DS3TR_C_XL_LP1_ODR_DIV_4: + *val = LSM6DS3TR_C_XL_LP1_ODR_DIV_4; + break; + + default: + *val = LSM6DS3TR_C_XL_LP1_NA; + break; + } + } + } + + return ret; +} + +/** + * @brief LPF2 on outputs[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of input_composite in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_lp2_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_input_composite_t val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.input_composite = ((uint8_t)val & 0x10U) >> 4; + ctrl8_xl.hpcf_xl = (uint8_t)val & 0x03U; + ctrl8_xl.lpf2_xl_en = 1; + ctrl8_xl.hp_slope_xl_en = 0; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + + return ret; +} + +/** + * @brief LPF2 on outputs[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of input_composite in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_lp2_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_input_composite_t* val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + if((ctrl8_xl.lpf2_xl_en == 0x00U) || (ctrl8_xl.hp_slope_xl_en != 0x00U)) { + *val = LSM6DS3TR_C_XL_LP_NA; + } + + else { + switch((ctrl8_xl.input_composite << 4) + ctrl8_xl.hpcf_xl) { + case LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_50: + *val = LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_50; + break; + + case LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_100: + *val = LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_100; + break; + + case LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_9: + *val = LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_9; + break; + + case LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_400: + *val = LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_400; + break; + + case LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_50: + *val = LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_50; + break; + + case LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_100: + *val = LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_100; + break; + + case LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_9: + *val = LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_9; + break; + + case LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_400: + *val = LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_400; + break; + + default: + *val = LSM6DS3TR_C_XL_LP_NA; + break; + } + } + } + + return ret; +} + +/** + * @brief Enable HP filter reference mode.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of hp_ref_mode in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_reference_mode_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.hp_ref_mode = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + + return ret; +} + +/** + * @brief Enable HP filter reference mode.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of hp_ref_mode in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_reference_mode_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + *val = ctrl8_xl.hp_ref_mode; + + return ret; +} + +/** + * @brief High pass/Slope on outputs.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of hpcf_xl in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_hp_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_hpcf_xl_t val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.input_composite = 0; + ctrl8_xl.hpcf_xl = (uint8_t)val & 0x03U; + ctrl8_xl.hp_slope_xl_en = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + + return ret; +} + +/** + * @brief High pass/Slope on outputs.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of hpcf_xl in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_hp_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_hpcf_xl_t* val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ctrl8_xl.hp_slope_xl_en == 0x00U) { + *val = LSM6DS3TR_C_XL_HP_NA; + } + + switch(ctrl8_xl.hpcf_xl) { + case LSM6DS3TR_C_XL_HP_ODR_DIV_4: + *val = LSM6DS3TR_C_XL_HP_ODR_DIV_4; + break; + + case LSM6DS3TR_C_XL_HP_ODR_DIV_100: + *val = LSM6DS3TR_C_XL_HP_ODR_DIV_100; + break; + + case LSM6DS3TR_C_XL_HP_ODR_DIV_9: + *val = LSM6DS3TR_C_XL_HP_ODR_DIV_9; + break; + + case LSM6DS3TR_C_XL_HP_ODR_DIV_400: + *val = LSM6DS3TR_C_XL_HP_ODR_DIV_400; + break; + + default: + *val = LSM6DS3TR_C_XL_HP_NA; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_gyroscope_filters + * @brief This section group all the functions concerning the filters + * configuration that impact gyroscope. + * @{ + * + */ + +/** + * @brief Gyroscope low pass path bandwidth.[set] + * + * @param ctx Read / write interface definitions + * @param val gyroscope filtering chain configuration. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_band_pass_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_sel_g_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + if(ret == 0) { + ctrl7_g.hpm_g = ((uint8_t)val & 0x30U) >> 4; + ctrl7_g.hp_en_g = ((uint8_t)val & 0x80U) >> 7; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ctrl6_c.ftype = (uint8_t)val & 0x03U; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.lpf1_sel_g = ((uint8_t)val & 0x08U) >> 3; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + } + } + } + } + + return ret; +} + +/** + * @brief Gyroscope low pass path bandwidth.[get] + * + * @param ctx Read / write interface definitions + * @param val gyroscope filtering chain + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_band_pass_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_sel_g_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + switch((ctrl7_g.hp_en_g << 7) + (ctrl7_g.hpm_g << 4) + (ctrl4_c.lpf1_sel_g << 3) + + ctrl6_c.ftype) { + case LSM6DS3TR_C_HP_16mHz_LP2: + *val = LSM6DS3TR_C_HP_16mHz_LP2; + break; + + case LSM6DS3TR_C_HP_65mHz_LP2: + *val = LSM6DS3TR_C_HP_65mHz_LP2; + break; + + case LSM6DS3TR_C_HP_260mHz_LP2: + *val = LSM6DS3TR_C_HP_260mHz_LP2; + break; + + case LSM6DS3TR_C_HP_1Hz04_LP2: + *val = LSM6DS3TR_C_HP_1Hz04_LP2; + break; + + case LSM6DS3TR_C_HP_DISABLE_LP1_LIGHT: + *val = LSM6DS3TR_C_HP_DISABLE_LP1_LIGHT; + break; + + case LSM6DS3TR_C_HP_DISABLE_LP1_NORMAL: + *val = LSM6DS3TR_C_HP_DISABLE_LP1_NORMAL; + break; + + case LSM6DS3TR_C_HP_DISABLE_LP_STRONG: + *val = LSM6DS3TR_C_HP_DISABLE_LP_STRONG; + break; + + case LSM6DS3TR_C_HP_DISABLE_LP1_AGGRESSIVE: + *val = LSM6DS3TR_C_HP_DISABLE_LP1_AGGRESSIVE; + break; + + case LSM6DS3TR_C_HP_16mHz_LP1_LIGHT: + *val = LSM6DS3TR_C_HP_16mHz_LP1_LIGHT; + break; + + case LSM6DS3TR_C_HP_65mHz_LP1_NORMAL: + *val = LSM6DS3TR_C_HP_65mHz_LP1_NORMAL; + break; + + case LSM6DS3TR_C_HP_260mHz_LP1_STRONG: + *val = LSM6DS3TR_C_HP_260mHz_LP1_STRONG; + break; + + case LSM6DS3TR_C_HP_1Hz04_LP1_AGGRESSIVE: + *val = LSM6DS3TR_C_HP_1Hz04_LP1_AGGRESSIVE; + break; + + default: + *val = LSM6DS3TR_C_HP_GY_BAND_NA; + break; + } + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_serial_interface + * @brief This section groups all the functions concerning serial + * interface management + * @{ + * + */ + +/** + * @brief SPI Serial Interface Mode selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sim in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_spi_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_sim_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.sim = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief SPI Serial Interface Mode selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of sim in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_spi_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_sim_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + switch(ctrl3_c.sim) { + case LSM6DS3TR_C_SPI_4_WIRE: + *val = LSM6DS3TR_C_SPI_4_WIRE; + break; + + case LSM6DS3TR_C_SPI_3_WIRE: + *val = LSM6DS3TR_C_SPI_3_WIRE; + break; + + default: + *val = LSM6DS3TR_C_SPI_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Disable / Enable I2C interface.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of i2c_disable in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_i2c_interface_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_i2c_disable_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.i2c_disable = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + return ret; +} + +/** + * @brief Disable / Enable I2C interface.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of i2c_disable in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_i2c_interface_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_i2c_disable_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + switch(ctrl4_c.i2c_disable) { + case LSM6DS3TR_C_I2C_ENABLE: + *val = LSM6DS3TR_C_I2C_ENABLE; + break; + + case LSM6DS3TR_C_I2C_DISABLE: + *val = LSM6DS3TR_C_I2C_DISABLE; + break; + + default: + *val = LSM6DS3TR_C_I2C_MODE_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_interrupt_pins + * @brief This section groups all the functions that manage + * interrupt pins + * @{ + * + */ + +/** + * @brief Select the signal that need to route on int1 pad[set] + * + * @param ctx Read / write interface definitions + * @param val configure INT1_CTRL, MD1_CFG, CTRL4_C(den_drdy_int1), + * MASTER_CONFIG(drdy_on_int1) + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_int1_route_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_int1_route_t val) { + lsm6ds3tr_c_master_config_t master_config; + lsm6ds3tr_c_int1_ctrl_t int1_ctrl; + lsm6ds3tr_c_md1_cfg_t md1_cfg; + lsm6ds3tr_c_md2_cfg_t md2_cfg; + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT1_CTRL, (uint8_t*)&int1_ctrl, 1); + + if(ret == 0) { + int1_ctrl.int1_drdy_xl = val.int1_drdy_xl; + int1_ctrl.int1_drdy_g = val.int1_drdy_g; + int1_ctrl.int1_boot = val.int1_boot; + int1_ctrl.int1_fth = val.int1_fth; + int1_ctrl.int1_fifo_ovr = val.int1_fifo_ovr; + int1_ctrl.int1_full_flag = val.int1_full_flag; + int1_ctrl.int1_sign_mot = val.int1_sign_mot; + int1_ctrl.int1_step_detector = val.int1_step_detector; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT1_CTRL, (uint8_t*)&int1_ctrl, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD1_CFG, (uint8_t*)&md1_cfg, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD2_CFG, (uint8_t*)&md2_cfg, 1); + } + + if(ret == 0) { + md1_cfg.int1_timer = val.int1_timer; + md1_cfg.int1_tilt = val.int1_tilt; + md1_cfg.int1_6d = val.int1_6d; + md1_cfg.int1_double_tap = val.int1_double_tap; + md1_cfg.int1_ff = val.int1_ff; + md1_cfg.int1_wu = val.int1_wu; + md1_cfg.int1_single_tap = val.int1_single_tap; + md1_cfg.int1_inact_state = val.int1_inact_state; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MD1_CFG, (uint8_t*)&md1_cfg, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + if(ret == 0) { + ctrl4_c.den_drdy_int1 = val.den_drdy_int1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + if(ret == 0) { + master_config.drdy_on_int1 = val.den_drdy_int1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if((val.int1_6d != 0x00U) || (val.int1_ff != 0x00U) || (val.int1_wu != 0x00U) || + (val.int1_single_tap != 0x00U) || (val.int1_double_tap != 0x00U) || + (val.int1_inact_state != 0x00U) || (md2_cfg.int2_6d != 0x00U) || + (md2_cfg.int2_ff != 0x00U) || (md2_cfg.int2_wu != 0x00U) || + (md2_cfg.int2_single_tap != 0x00U) || (md2_cfg.int2_double_tap != 0x00U) || + (md2_cfg.int2_inact_state != 0x00U)) { + tap_cfg.interrupts_enable = PROPERTY_ENABLE; + } + + else { + tap_cfg.interrupts_enable = PROPERTY_DISABLE; + } + } + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Select the signal that need to route on int1 pad[get] + * + * @param ctx Read / write interface definitions + * @param val read INT1_CTRL, MD1_CFG, CTRL4_C(den_drdy_int1), + * MASTER_CONFIG(drdy_on_int1) + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_int1_route_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_int1_route_t* val) { + lsm6ds3tr_c_master_config_t master_config; + lsm6ds3tr_c_int1_ctrl_t int1_ctrl; + lsm6ds3tr_c_md1_cfg_t md1_cfg; + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT1_CTRL, (uint8_t*)&int1_ctrl, 1); + + if(ret == 0) { + val->int1_drdy_xl = int1_ctrl.int1_drdy_xl; + val->int1_drdy_g = int1_ctrl.int1_drdy_g; + val->int1_boot = int1_ctrl.int1_boot; + val->int1_fth = int1_ctrl.int1_fth; + val->int1_fifo_ovr = int1_ctrl.int1_fifo_ovr; + val->int1_full_flag = int1_ctrl.int1_full_flag; + val->int1_sign_mot = int1_ctrl.int1_sign_mot; + val->int1_step_detector = int1_ctrl.int1_step_detector; + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD1_CFG, (uint8_t*)&md1_cfg, 1); + + if(ret == 0) { + val->int1_timer = md1_cfg.int1_timer; + val->int1_tilt = md1_cfg.int1_tilt; + val->int1_6d = md1_cfg.int1_6d; + val->int1_double_tap = md1_cfg.int1_double_tap; + val->int1_ff = md1_cfg.int1_ff; + val->int1_wu = md1_cfg.int1_wu; + val->int1_single_tap = md1_cfg.int1_single_tap; + val->int1_inact_state = md1_cfg.int1_inact_state; + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + val->den_drdy_int1 = ctrl4_c.den_drdy_int1; + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + val->den_drdy_int1 = master_config.drdy_on_int1; + } + } + } + + return ret; +} + +/** + * @brief Select the signal that need to route on int2 pad[set] + * + * @param ctx Read / write interface definitions + * @param val INT2_CTRL, DRDY_PULSE_CFG(int2_wrist_tilt), MD2_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_int2_route_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_int2_route_t val) { + lsm6ds3tr_c_int2_ctrl_t int2_ctrl; + lsm6ds3tr_c_md1_cfg_t md1_cfg; + lsm6ds3tr_c_md2_cfg_t md2_cfg; + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT2_CTRL, (uint8_t*)&int2_ctrl, 1); + + if(ret == 0) { + int2_ctrl.int2_drdy_xl = val.int2_drdy_xl; + int2_ctrl.int2_drdy_g = val.int2_drdy_g; + int2_ctrl.int2_drdy_temp = val.int2_drdy_temp; + int2_ctrl.int2_fth = val.int2_fth; + int2_ctrl.int2_fifo_ovr = val.int2_fifo_ovr; + int2_ctrl.int2_full_flag = val.int2_full_flag; + int2_ctrl.int2_step_count_ov = val.int2_step_count_ov; + int2_ctrl.int2_step_delta = val.int2_step_delta; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT2_CTRL, (uint8_t*)&int2_ctrl, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD1_CFG, (uint8_t*)&md1_cfg, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD2_CFG, (uint8_t*)&md2_cfg, 1); + } + + if(ret == 0) { + md2_cfg.int2_iron = val.int2_iron; + md2_cfg.int2_tilt = val.int2_tilt; + md2_cfg.int2_6d = val.int2_6d; + md2_cfg.int2_double_tap = val.int2_double_tap; + md2_cfg.int2_ff = val.int2_ff; + md2_cfg.int2_wu = val.int2_wu; + md2_cfg.int2_single_tap = val.int2_single_tap; + md2_cfg.int2_inact_state = val.int2_inact_state; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MD2_CFG, (uint8_t*)&md2_cfg, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + } + + if(ret == 0) { + drdy_pulse_cfg_g.int2_wrist_tilt = val.int2_wrist_tilt; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if((md1_cfg.int1_6d != 0x00U) || (md1_cfg.int1_ff != 0x00U) || + (md1_cfg.int1_wu != 0x00U) || (md1_cfg.int1_single_tap != 0x00U) || + (md1_cfg.int1_double_tap != 0x00U) || (md1_cfg.int1_inact_state != 0x00U) || + (val.int2_6d != 0x00U) || (val.int2_ff != 0x00U) || (val.int2_wu != 0x00U) || + (val.int2_single_tap != 0x00U) || (val.int2_double_tap != 0x00U) || + (val.int2_inact_state != 0x00U)) { + tap_cfg.interrupts_enable = PROPERTY_ENABLE; + } + + else { + tap_cfg.interrupts_enable = PROPERTY_DISABLE; + } + } + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Select the signal that need to route on int2 pad[get] + * + * @param ctx Read / write interface definitions + * @param val INT2_CTRL, DRDY_PULSE_CFG(int2_wrist_tilt), MD2_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_int2_route_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_int2_route_t* val) { + lsm6ds3tr_c_int2_ctrl_t int2_ctrl; + lsm6ds3tr_c_md2_cfg_t md2_cfg; + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT2_CTRL, (uint8_t*)&int2_ctrl, 1); + + if(ret == 0) { + val->int2_drdy_xl = int2_ctrl.int2_drdy_xl; + val->int2_drdy_g = int2_ctrl.int2_drdy_g; + val->int2_drdy_temp = int2_ctrl.int2_drdy_temp; + val->int2_fth = int2_ctrl.int2_fth; + val->int2_fifo_ovr = int2_ctrl.int2_fifo_ovr; + val->int2_full_flag = int2_ctrl.int2_full_flag; + val->int2_step_count_ov = int2_ctrl.int2_step_count_ov; + val->int2_step_delta = int2_ctrl.int2_step_delta; + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD2_CFG, (uint8_t*)&md2_cfg, 1); + + if(ret == 0) { + val->int2_iron = md2_cfg.int2_iron; + val->int2_tilt = md2_cfg.int2_tilt; + val->int2_6d = md2_cfg.int2_6d; + val->int2_double_tap = md2_cfg.int2_double_tap; + val->int2_ff = md2_cfg.int2_ff; + val->int2_wu = md2_cfg.int2_wu; + val->int2_single_tap = md2_cfg.int2_single_tap; + val->int2_inact_state = md2_cfg.int2_inact_state; + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + val->int2_wrist_tilt = drdy_pulse_cfg_g.int2_wrist_tilt; + } + } + + return ret; +} + +/** + * @brief Push-pull/open drain selection on interrupt pads.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pp_od in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pp_od_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.pp_od = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Push-pull/open drain selection on interrupt pads.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of pp_od in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pp_od_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + switch(ctrl3_c.pp_od) { + case LSM6DS3TR_C_PUSH_PULL: + *val = LSM6DS3TR_C_PUSH_PULL; + break; + + case LSM6DS3TR_C_OPEN_DRAIN: + *val = LSM6DS3TR_C_OPEN_DRAIN; + break; + + default: + *val = LSM6DS3TR_C_PIN_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Interrupt active-high/low.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of h_lactive in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_polarity_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_h_lactive_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.h_lactive = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Interrupt active-high/low.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of h_lactive in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_polarity_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_h_lactive_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + switch(ctrl3_c.h_lactive) { + case LSM6DS3TR_C_ACTIVE_HIGH: + *val = LSM6DS3TR_C_ACTIVE_HIGH; + break; + + case LSM6DS3TR_C_ACTIVE_LOW: + *val = LSM6DS3TR_C_ACTIVE_LOW; + break; + + default: + *val = LSM6DS3TR_C_POLARITY_ND; + break; + } + + return ret; +} + +/** + * @brief All interrupt signals become available on INT1 pin.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of int2_on_int1 in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_all_on_int1_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.int2_on_int1 = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + return ret; +} + +/** + * @brief All interrupt signals become available on INT1 pin.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of int2_on_int1 in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_all_on_int1_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + *val = ctrl4_c.int2_on_int1; + + return ret; +} + +/** + * @brief Latched/pulsed interrupt.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of lir in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_int_notification_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lir_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.lir = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Latched/pulsed interrupt.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of lir in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_int_notification_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lir_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + switch(tap_cfg.lir) { + case LSM6DS3TR_C_INT_PULSED: + *val = LSM6DS3TR_C_INT_PULSED; + break; + + case LSM6DS3TR_C_INT_LATCHED: + *val = LSM6DS3TR_C_INT_LATCHED; + break; + + default: + *val = LSM6DS3TR_C_INT_MODE; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Wake_Up_event + * @brief This section groups all the functions that manage the + * Wake Up event generation. + * @{ + * + */ + +/** + * @brief Threshold for wakeup.1 LSB = FS_XL / 64.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of wk_ths in reg WAKE_UP_THS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wkup_threshold_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + + if(ret == 0) { + wake_up_ths.wk_ths = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + } + + return ret; +} + +/** + * @brief Threshold for wakeup.1 LSB = FS_XL / 64.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of wk_ths in reg WAKE_UP_THS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wkup_threshold_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + *val = wake_up_ths.wk_ths; + + return ret; +} + +/** + * @brief Wake up duration event.1LSb = 1 / ODR[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of wake_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wkup_dur_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + wake_up_dur.wake_dur = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + } + + return ret; +} + +/** + * @brief Wake up duration event.1LSb = 1 / ODR[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of wake_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wkup_dur_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + *val = wake_up_dur.wake_dur; + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Activity/Inactivity_detection + * @brief This section groups all the functions concerning + * activity/inactivity detection. + * @{ + * + */ + +/** + * @brief Enables gyroscope Sleep mode.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sleep in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_sleep_mode_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.sleep = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + return ret; +} + +/** + * @brief Enables gyroscope Sleep mode.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sleep in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_sleep_mode_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + *val = ctrl4_c.sleep; + + return ret; +} + +/** + * @brief Enable inactivity function.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of inact_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_act_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_inact_en_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.inact_en = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Enable inactivity function.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of inact_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_act_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_inact_en_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + switch(tap_cfg.inact_en) { + case LSM6DS3TR_C_PROPERTY_DISABLE: + *val = LSM6DS3TR_C_PROPERTY_DISABLE; + break; + + case LSM6DS3TR_C_XL_12Hz5_GY_NOT_AFFECTED: + *val = LSM6DS3TR_C_XL_12Hz5_GY_NOT_AFFECTED; + break; + + case LSM6DS3TR_C_XL_12Hz5_GY_SLEEP: + *val = LSM6DS3TR_C_XL_12Hz5_GY_SLEEP; + break; + + case LSM6DS3TR_C_XL_12Hz5_GY_PD: + *val = LSM6DS3TR_C_XL_12Hz5_GY_PD; + break; + + default: + *val = LSM6DS3TR_C_ACT_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Duration to go in sleep mode.1 LSb = 512 / ODR[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sleep_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_act_sleep_dur_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + wake_up_dur.sleep_dur = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + } + + return ret; +} + +/** + * @brief Duration to go in sleep mode. 1 LSb = 512 / ODR[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sleep_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_act_sleep_dur_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + *val = wake_up_dur.sleep_dur; + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_tap_generator + * @brief This section groups all the functions that manage the + * tap and double tap event generation. + * @{ + * + */ + +/** + * @brief Read the tap / double tap source register.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure of registers from TAP_SRC + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_src_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_tap_src_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_SRC, (uint8_t*)val, 1); + + return ret; +} + +/** + * @brief Enable Z direction in tap recognition.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_z_en in reg TAP_CFG + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_z_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.tap_z_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Enable Z direction in tap recognition.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_z_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_z_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + *val = tap_cfg.tap_z_en; + + return ret; +} + +/** + * @brief Enable Y direction in tap recognition.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_y_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_y_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.tap_y_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Enable Y direction in tap recognition.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_y_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_y_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + *val = tap_cfg.tap_y_en; + + return ret; +} + +/** + * @brief Enable X direction in tap recognition.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_x_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_x_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.tap_x_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Enable X direction in tap recognition.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_x_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_x_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + *val = tap_cfg.tap_x_en; + + return ret; +} + +/** + * @brief Threshold for tap recognition.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_ths in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_threshold_x_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + + if(ret == 0) { + tap_ths_6d.tap_ths = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + } + + return ret; +} + +/** + * @brief Threshold for tap recognition.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_ths in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_threshold_x_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + *val = tap_ths_6d.tap_ths; + + return ret; +} + +/** + * @brief Maximum duration is the maximum time of an overthreshold signal + * detection to be recognized as a tap event. + * The default value of these bits is 00b which corresponds to + * 4*ODR_XL time. + * If the SHOCK[1:0] bits are set to a different + * value, 1LSB corresponds to 8*ODR_XL time.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of shock in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_shock_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + + if(ret == 0) { + int_dur2.shock = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + } + + return ret; +} + +/** + * @brief Maximum duration is the maximum time of an overthreshold signal + * detection to be recognized as a tap event. + * The default value of these bits is 00b which corresponds to + * 4*ODR_XL time. + * If the SHOCK[1:0] bits are set to a different value, 1LSB + * corresponds to 8*ODR_XL time.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of shock in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_shock_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + *val = int_dur2.shock; + + return ret; +} + +/** + * @brief Quiet time is the time after the first detected tap in which there + * must not be any overthreshold event. + * The default value of these bits is 00b which corresponds to + * 2*ODR_XL time. + * If the QUIET[1:0] bits are set to a different value, 1LSB + * corresponds to 4*ODR_XL time.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of quiet in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_quiet_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + + if(ret == 0) { + int_dur2.quiet = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + } + + return ret; +} + +/** + * @brief Quiet time is the time after the first detected tap in which there + * must not be any overthreshold event. + * The default value of these bits is 00b which corresponds to + * 2*ODR_XL time. + * If the QUIET[1:0] bits are set to a different value, 1LSB + * corresponds to 4*ODR_XL time.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of quiet in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_quiet_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + *val = int_dur2.quiet; + + return ret; +} + +/** + * @brief When double tap recognition is enabled, this register expresses the + * maximum time between two consecutive detected taps to determine a + * double tap event. + * The default value of these bits is 0000b which corresponds to + * 16*ODR_XL time. + * If the DUR[3:0] bits are set to a different value,1LSB corresponds + * to 32*ODR_XL time.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dur in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_dur_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + + if(ret == 0) { + int_dur2.dur = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + } + + return ret; +} + +/** + * @brief When double tap recognition is enabled, this register expresses the + * maximum time between two consecutive detected taps to determine a + * double tap event. + * The default value of these bits is 0000b which corresponds to + * 16*ODR_XL time. + * If the DUR[3:0] bits are set to a different value,1LSB corresponds + * to 32*ODR_XL time.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dur in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_dur_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + *val = int_dur2.dur; + + return ret; +} + +/** + * @brief Single/double-tap event enable/disable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of + * single_double_tap in reg WAKE_UP_THS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_single_double_tap_t val) { + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + + if(ret == 0) { + wake_up_ths.single_double_tap = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + } + + return ret; +} + +/** + * @brief Single/double-tap event enable/disable.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of single_double_tap + * in reg WAKE_UP_THS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_single_double_tap_t* val) { + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + + switch(wake_up_ths.single_double_tap) { + case LSM6DS3TR_C_ONLY_SINGLE: + *val = LSM6DS3TR_C_ONLY_SINGLE; + break; + + case LSM6DS3TR_C_BOTH_SINGLE_DOUBLE: + *val = LSM6DS3TR_C_BOTH_SINGLE_DOUBLE; + break; + + default: + *val = LSM6DS3TR_C_TAP_MODE_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_ Six_position_detection(6D/4D) + * @brief This section groups all the functions concerning six + * position detection (6D). + * @{ + * + */ + +/** + * @brief LPF2 feed 6D function selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of low_pass_on_6d in + * reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_6d_feed_data_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_low_pass_on_6d_t val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.low_pass_on_6d = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + + return ret; +} + +/** + * @brief LPF2 feed 6D function selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of low_pass_on_6d in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_6d_feed_data_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_low_pass_on_6d_t* val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + switch(ctrl8_xl.low_pass_on_6d) { + case LSM6DS3TR_C_ODR_DIV_2_FEED: + *val = LSM6DS3TR_C_ODR_DIV_2_FEED; + break; + + case LSM6DS3TR_C_LPF2_FEED: + *val = LSM6DS3TR_C_LPF2_FEED; + break; + + default: + *val = LSM6DS3TR_C_6D_FEED_ND; + break; + } + + return ret; +} + +/** + * @brief Threshold for 4D/6D function.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sixd_ths in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_6d_threshold_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_sixd_ths_t val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + + if(ret == 0) { + tap_ths_6d.sixd_ths = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + } + + return ret; +} + +/** + * @brief Threshold for 4D/6D function.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of sixd_ths in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_6d_threshold_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_sixd_ths_t* val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + + switch(tap_ths_6d.sixd_ths) { + case LSM6DS3TR_C_DEG_80: + *val = LSM6DS3TR_C_DEG_80; + break; + + case LSM6DS3TR_C_DEG_70: + *val = LSM6DS3TR_C_DEG_70; + break; + + case LSM6DS3TR_C_DEG_60: + *val = LSM6DS3TR_C_DEG_60; + break; + + case LSM6DS3TR_C_DEG_50: + *val = LSM6DS3TR_C_DEG_50; + break; + + default: + *val = LSM6DS3TR_C_6D_TH_ND; + break; + } + + return ret; +} + +/** + * @brief 4D orientation detection enable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of d4d_en in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_4d_mode_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + + if(ret == 0) { + tap_ths_6d.d4d_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + } + + return ret; +} + +/** + * @brief 4D orientation detection enable.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of d4d_en in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_4d_mode_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + *val = tap_ths_6d.d4d_en; + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_free_fall + * @brief This section group all the functions concerning the free + * fall detection. + * @{ + * + */ + +/** + * @brief Free-fall duration event. 1LSb = 1 / ODR[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ff_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_ff_dur_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + lsm6ds3tr_c_free_fall_t free_fall; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + + if(ret == 0) { + free_fall.ff_dur = (val & 0x1FU); + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + wake_up_dur.ff_dur = (val & 0x20U) >> 5; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + } + } + } + + return ret; +} + +/** + * @brief Free-fall duration event. 1LSb = 1 / ODR[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ff_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_ff_dur_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + lsm6ds3tr_c_free_fall_t free_fall; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + } + + *val = (wake_up_dur.ff_dur << 5) + free_fall.ff_dur; + + return ret; +} + +/** + * @brief Free fall threshold setting.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ff_ths in reg FREE_FALL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_ff_threshold_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_ff_ths_t val) { + lsm6ds3tr_c_free_fall_t free_fall; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + + if(ret == 0) { + free_fall.ff_ths = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + } + + return ret; +} + +/** + * @brief Free fall threshold setting.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of ff_ths in reg FREE_FALL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_ff_threshold_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_ff_ths_t* val) { + lsm6ds3tr_c_free_fall_t free_fall; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + + switch(free_fall.ff_ths) { + case LSM6DS3TR_C_FF_TSH_156mg: + *val = LSM6DS3TR_C_FF_TSH_156mg; + break; + + case LSM6DS3TR_C_FF_TSH_219mg: + *val = LSM6DS3TR_C_FF_TSH_219mg; + break; + + case LSM6DS3TR_C_FF_TSH_250mg: + *val = LSM6DS3TR_C_FF_TSH_250mg; + break; + + case LSM6DS3TR_C_FF_TSH_312mg: + *val = LSM6DS3TR_C_FF_TSH_312mg; + break; + + case LSM6DS3TR_C_FF_TSH_344mg: + *val = LSM6DS3TR_C_FF_TSH_344mg; + break; + + case LSM6DS3TR_C_FF_TSH_406mg: + *val = LSM6DS3TR_C_FF_TSH_406mg; + break; + + case LSM6DS3TR_C_FF_TSH_469mg: + *val = LSM6DS3TR_C_FF_TSH_469mg; + break; + + case LSM6DS3TR_C_FF_TSH_500mg: + *val = LSM6DS3TR_C_FF_TSH_500mg; + break; + + default: + *val = LSM6DS3TR_C_FF_TSH_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_fifo + * @brief This section group all the functions concerning the + * fifo usage + * @{ + * + */ + +/** + * @brief FIFO watermark level selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fth in reg FIFO_CTRL1 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_watermark_set(stmdev_ctx_t* ctx, uint16_t val) { + lsm6ds3tr_c_fifo_ctrl1_t fifo_ctrl1; + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + fifo_ctrl1.fth = (uint8_t)(0x00FFU & val); + fifo_ctrl2.fth = (uint8_t)((0x0700U & val) >> 8); + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL1, (uint8_t*)&fifo_ctrl1, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + } + } + + return ret; +} + +/** + * @brief FIFO watermark level selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fth in reg FIFO_CTRL1 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_watermark_get(stmdev_ctx_t* ctx, uint16_t* val) { + lsm6ds3tr_c_fifo_ctrl1_t fifo_ctrl1; + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL1, (uint8_t*)&fifo_ctrl1, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + } + + *val = ((uint16_t)fifo_ctrl2.fth << 8) + (uint16_t)fifo_ctrl1.fth; + + return ret; +} + +/** + * @brief FIFO data level.[get] + * + * @param ctx Read / write interface definitions + * @param val get the values of diff_fifo in reg FIFO_STATUS1 and + * FIFO_STATUS2(diff_fifo), it is recommended to set the + * BDU bit. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_data_level_get(stmdev_ctx_t* ctx, uint16_t* val) { + lsm6ds3tr_c_fifo_status1_t fifo_status1; + lsm6ds3tr_c_fifo_status2_t fifo_status2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS1, (uint8_t*)&fifo_status1, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS2, (uint8_t*)&fifo_status2, 1); + *val = ((uint16_t)fifo_status2.diff_fifo << 8) + (uint16_t)fifo_status1.diff_fifo; + } + + return ret; +} + +/** + * @brief FIFO watermark.[get] + * + * @param ctx Read / write interface definitions + * @param val get the values of watermark in reg FIFO_STATUS2 and + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_wtm_flag_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_status2_t fifo_status2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS2, (uint8_t*)&fifo_status2, 1); + *val = fifo_status2.waterm; + + return ret; +} + +/** + * @brief FIFO pattern.[get] + * + * @param ctx Read / write interface definitions + * @param val get the values of fifo_pattern in reg FIFO_STATUS3 and + * FIFO_STATUS4, it is recommended to set the BDU bit + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_pattern_get(stmdev_ctx_t* ctx, uint16_t* val) { + lsm6ds3tr_c_fifo_status3_t fifo_status3; + lsm6ds3tr_c_fifo_status4_t fifo_status4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS3, (uint8_t*)&fifo_status3, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS4, (uint8_t*)&fifo_status4, 1); + *val = ((uint16_t)fifo_status4.fifo_pattern << 8) + fifo_status3.fifo_pattern; + } + + return ret; +} + +/** + * @brief Batching of temperature data[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fifo_temp_en in reg FIFO_CTRL2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_temp_batch_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + fifo_ctrl2.fifo_temp_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + } + + return ret; +} + +/** + * @brief Batching of temperature data[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fifo_temp_en in reg FIFO_CTRL2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_temp_batch_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + *val = fifo_ctrl2.fifo_temp_en; + + return ret; +} + +/** + * @brief Trigger signal for FIFO write operation.[set] + * + * @param ctx Read / write interface definitions + * @param val act on FIFO_CTRL2(timer_pedo_fifo_drdy) + * and MASTER_CONFIG(data_valid_sel_fifo) + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_write_trigger_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_trigger_fifo_t val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + fifo_ctrl2.timer_pedo_fifo_drdy = (uint8_t)val & 0x01U; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + ret = + lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.data_valid_sel_fifo = (((uint8_t)val & 0x02U) >> 1); + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + } + } + + return ret; +} + +/** + * @brief Trigger signal for FIFO write operation.[get] + * + * @param ctx Read / write interface definitions + * @param val act on FIFO_CTRL2(timer_pedo_fifo_drdy) + * and MASTER_CONFIG(data_valid_sel_fifo) + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_write_trigger_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_trigger_fifo_t* val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + switch((fifo_ctrl2.timer_pedo_fifo_drdy << 1) + fifo_ctrl2.timer_pedo_fifo_drdy) { + case LSM6DS3TR_C_TRG_XL_GY_DRDY: + *val = LSM6DS3TR_C_TRG_XL_GY_DRDY; + break; + + case LSM6DS3TR_C_TRG_STEP_DETECT: + *val = LSM6DS3TR_C_TRG_STEP_DETECT; + break; + + case LSM6DS3TR_C_TRG_SH_DRDY: + *val = LSM6DS3TR_C_TRG_SH_DRDY; + break; + + default: + *val = LSM6DS3TR_C_TRG_SH_ND; + break; + } + } + + return ret; +} + +/** + * @brief Enable pedometer step counter and timestamp as 4th + * FIFO data set.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_pedo_fifo_en in reg FIFO_CTRL2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_pedo_and_timestamp_batch_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + fifo_ctrl2.timer_pedo_fifo_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + } + + return ret; +} + +/** + * @brief Enable pedometer step counter and timestamp as 4th + * FIFO data set.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_pedo_fifo_en in reg FIFO_CTRL2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_pedo_and_timestamp_batch_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + *val = fifo_ctrl2.timer_pedo_fifo_en; + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) for + * accelerometer data.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dec_fifo_xl in reg FIFO_CTRL3 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_xl_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_xl_t val) { + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + + if(ret == 0) { + fifo_ctrl3.dec_fifo_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) for + * accelerometer data.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of dec_fifo_xl in reg FIFO_CTRL3 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_xl_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_xl_t* val) { + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + + switch(fifo_ctrl3.dec_fifo_xl) { + case LSM6DS3TR_C_FIFO_XL_DISABLE: + *val = LSM6DS3TR_C_FIFO_XL_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_XL_NO_DEC: + *val = LSM6DS3TR_C_FIFO_XL_NO_DEC; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_2: + *val = LSM6DS3TR_C_FIFO_XL_DEC_2; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_3: + *val = LSM6DS3TR_C_FIFO_XL_DEC_3; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_4: + *val = LSM6DS3TR_C_FIFO_XL_DEC_4; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_8: + *val = LSM6DS3TR_C_FIFO_XL_DEC_8; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_16: + *val = LSM6DS3TR_C_FIFO_XL_DEC_16; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_32: + *val = LSM6DS3TR_C_FIFO_XL_DEC_32; + break; + + default: + *val = LSM6DS3TR_C_FIFO_XL_DEC_ND; + break; + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for gyroscope data.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dec_fifo_gyro in reg FIFO_CTRL3 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_gy_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_gyro_t val) { + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + + if(ret == 0) { + fifo_ctrl3.dec_fifo_gyro = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for gyroscope data.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of dec_fifo_gyro in reg FIFO_CTRL3 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_gy_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_gyro_t* val) { + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + + switch(fifo_ctrl3.dec_fifo_gyro) { + case LSM6DS3TR_C_FIFO_GY_DISABLE: + *val = LSM6DS3TR_C_FIFO_GY_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_GY_NO_DEC: + *val = LSM6DS3TR_C_FIFO_GY_NO_DEC; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_2: + *val = LSM6DS3TR_C_FIFO_GY_DEC_2; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_3: + *val = LSM6DS3TR_C_FIFO_GY_DEC_3; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_4: + *val = LSM6DS3TR_C_FIFO_GY_DEC_4; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_8: + *val = LSM6DS3TR_C_FIFO_GY_DEC_8; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_16: + *val = LSM6DS3TR_C_FIFO_GY_DEC_16; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_32: + *val = LSM6DS3TR_C_FIFO_GY_DEC_32; + break; + + default: + *val = LSM6DS3TR_C_FIFO_GY_DEC_ND; + break; + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for third data set.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dec_ds3_fifo in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_dataset_3_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds3_fifo_t val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + if(ret == 0) { + fifo_ctrl4.dec_ds3_fifo = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for third data set.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of dec_ds3_fifo in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_dataset_3_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds3_fifo_t* val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + switch(fifo_ctrl4.dec_ds3_fifo) { + case LSM6DS3TR_C_FIFO_DS3_DISABLE: + *val = LSM6DS3TR_C_FIFO_DS3_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_DS3_NO_DEC: + *val = LSM6DS3TR_C_FIFO_DS3_NO_DEC; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_2: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_2; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_3: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_3; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_4: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_4; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_8: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_8; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_16: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_16; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_32: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_32; + break; + + default: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_ND; + break; + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for fourth data set.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dec_ds4_fifo in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_dataset_4_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds4_fifo_t val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + if(ret == 0) { + fifo_ctrl4.dec_ds4_fifo = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) for + * fourth data set.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of dec_ds4_fifo in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_dataset_4_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds4_fifo_t* val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + switch(fifo_ctrl4.dec_ds4_fifo) { + case LSM6DS3TR_C_FIFO_DS4_DISABLE: + *val = LSM6DS3TR_C_FIFO_DS4_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_DS4_NO_DEC: + *val = LSM6DS3TR_C_FIFO_DS4_NO_DEC; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_2: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_2; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_3: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_3; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_4: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_4; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_8: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_8; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_16: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_16; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_32: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_32; + break; + + default: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_ND; + break; + } + + return ret; +} + +/** + * @brief 8-bit data storage in FIFO.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of only_high_data in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_xl_gy_8bit_format_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + if(ret == 0) { + fifo_ctrl4.only_high_data = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + } + + return ret; +} + +/** + * @brief 8-bit data storage in FIFO.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of only_high_data in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_xl_gy_8bit_format_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + *val = fifo_ctrl4.only_high_data; + + return ret; +} + +/** + * @brief Sensing chain FIFO stop values memorization at threshold + * level.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of stop_on_fth in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_stop_on_wtm_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + if(ret == 0) { + fifo_ctrl4.stop_on_fth = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + } + + return ret; +} + +/** + * @brief Sensing chain FIFO stop values memorization at threshold + * level.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of stop_on_fth in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_stop_on_wtm_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + *val = fifo_ctrl4.stop_on_fth; + + return ret; +} + +/** + * @brief FIFO mode selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fifo_mode in reg FIFO_CTRL5 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fifo_mode_t val) { + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + + if(ret == 0) { + fifo_ctrl5.fifo_mode = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + } + + return ret; +} + +/** + * @brief FIFO mode selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of fifo_mode in reg FIFO_CTRL5 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fifo_mode_t* val) { + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + + switch(fifo_ctrl5.fifo_mode) { + case LSM6DS3TR_C_BYPASS_MODE: + *val = LSM6DS3TR_C_BYPASS_MODE; + break; + + case LSM6DS3TR_C_FIFO_MODE: + *val = LSM6DS3TR_C_FIFO_MODE; + break; + + case LSM6DS3TR_C_STREAM_TO_FIFO_MODE: + *val = LSM6DS3TR_C_STREAM_TO_FIFO_MODE; + break; + + case LSM6DS3TR_C_BYPASS_TO_STREAM_MODE: + *val = LSM6DS3TR_C_BYPASS_TO_STREAM_MODE; + break; + + case LSM6DS3TR_C_STREAM_MODE: + *val = LSM6DS3TR_C_STREAM_MODE; + break; + + default: + *val = LSM6DS3TR_C_FIFO_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief FIFO ODR selection, setting FIFO_MODE also.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of odr_fifo in reg FIFO_CTRL5 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_fifo_t val) { + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + + if(ret == 0) { + fifo_ctrl5.odr_fifo = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + } + + return ret; +} + +/** + * @brief FIFO ODR selection, setting FIFO_MODE also.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of odr_fifo in reg FIFO_CTRL5 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_fifo_t* val) { + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + + switch(fifo_ctrl5.odr_fifo) { + case LSM6DS3TR_C_FIFO_DISABLE: + *val = LSM6DS3TR_C_FIFO_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_12Hz5: + *val = LSM6DS3TR_C_FIFO_12Hz5; + break; + + case LSM6DS3TR_C_FIFO_26Hz: + *val = LSM6DS3TR_C_FIFO_26Hz; + break; + + case LSM6DS3TR_C_FIFO_52Hz: + *val = LSM6DS3TR_C_FIFO_52Hz; + break; + + case LSM6DS3TR_C_FIFO_104Hz: + *val = LSM6DS3TR_C_FIFO_104Hz; + break; + + case LSM6DS3TR_C_FIFO_208Hz: + *val = LSM6DS3TR_C_FIFO_208Hz; + break; + + case LSM6DS3TR_C_FIFO_416Hz: + *val = LSM6DS3TR_C_FIFO_416Hz; + break; + + case LSM6DS3TR_C_FIFO_833Hz: + *val = LSM6DS3TR_C_FIFO_833Hz; + break; + + case LSM6DS3TR_C_FIFO_1k66Hz: + *val = LSM6DS3TR_C_FIFO_1k66Hz; + break; + + case LSM6DS3TR_C_FIFO_3k33Hz: + *val = LSM6DS3TR_C_FIFO_3k33Hz; + break; + + case LSM6DS3TR_C_FIFO_6k66Hz: + *val = LSM6DS3TR_C_FIFO_6k66Hz; + break; + + default: + *val = LSM6DS3TR_C_FIFO_RATE_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_DEN_functionality + * @brief This section groups all the functions concerning DEN + * functionality. + * @{ + * + */ + +/** + * @brief DEN active level configuration.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_lh in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_polarity_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_lh_t val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + if(ret == 0) { + ctrl5_c.den_lh = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + } + + return ret; +} + +/** + * @brief DEN active level configuration.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of den_lh in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_polarity_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_lh_t* val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + switch(ctrl5_c.den_lh) { + case LSM6DS3TR_C_DEN_ACT_LOW: + *val = LSM6DS3TR_C_DEN_ACT_LOW; + break; + + case LSM6DS3TR_C_DEN_ACT_HIGH: + *val = LSM6DS3TR_C_DEN_ACT_HIGH; + break; + + default: + *val = LSM6DS3TR_C_DEN_POL_ND; + break; + } + + return ret; +} + +/** + * @brief DEN functionality marking mode[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_mode in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_mode_t val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ctrl6_c.den_mode = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + } + + return ret; +} + +/** + * @brief DEN functionality marking mode[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_mode in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_mode_t* val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + switch(ctrl6_c.den_mode) { + case LSM6DS3TR_C_DEN_DISABLE: + *val = LSM6DS3TR_C_DEN_DISABLE; + break; + + case LSM6DS3TR_C_LEVEL_LETCHED: + *val = LSM6DS3TR_C_LEVEL_LETCHED; + break; + + case LSM6DS3TR_C_LEVEL_TRIGGER: + *val = LSM6DS3TR_C_LEVEL_TRIGGER; + break; + + case LSM6DS3TR_C_EDGE_TRIGGER: + *val = LSM6DS3TR_C_EDGE_TRIGGER; + break; + + default: + *val = LSM6DS3TR_C_DEN_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Extend DEN functionality to accelerometer sensor.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_xl_g in reg CTRL9_XL + * and den_xl_en in CTRL4_C. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_enable_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_xl_en_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.den_xl_g = (uint8_t)val & 0x01U; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.den_xl_en = (uint8_t)val & 0x02U; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + } + } + + return ret; +} + +/** + * @brief Extend DEN functionality to accelerometer sensor. [get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of den_xl_g in reg CTRL9_XL + * and den_xl_en in CTRL4_C. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_enable_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_xl_en_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + switch((ctrl4_c.den_xl_en << 1) + ctrl9_xl.den_xl_g) { + case LSM6DS3TR_C_STAMP_IN_GY_DATA: + *val = LSM6DS3TR_C_STAMP_IN_GY_DATA; + break; + + case LSM6DS3TR_C_STAMP_IN_XL_DATA: + *val = LSM6DS3TR_C_STAMP_IN_XL_DATA; + break; + + case LSM6DS3TR_C_STAMP_IN_GY_XL_DATA: + *val = LSM6DS3TR_C_STAMP_IN_GY_XL_DATA; + break; + + default: + *val = LSM6DS3TR_C_DEN_STAMP_ND; + break; + } + } + + return ret; +} + +/** + * @brief DEN value stored in LSB of Z-axis.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_z in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_z_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.den_z = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + } + + return ret; +} + +/** + * @brief DEN value stored in LSB of Z-axis.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_z in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_z_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + *val = ctrl9_xl.den_z; + + return ret; +} + +/** + * @brief DEN value stored in LSB of Y-axis.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_y in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_y_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.den_y = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + } + + return ret; +} + +/** + * @brief DEN value stored in LSB of Y-axis.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_y in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_y_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + *val = ctrl9_xl.den_y; + + return ret; +} + +/** + * @brief DEN value stored in LSB of X-axis.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_x in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_x_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.den_x = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + } + + return ret; +} + +/** + * @brief DEN value stored in LSB of X-axis.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_x in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_x_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + *val = ctrl9_xl.den_x; + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Pedometer + * @brief This section groups all the functions that manage pedometer. + * @{ + * + */ + +/** + * @brief Reset pedometer step counter.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_rst_step in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_step_reset_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.pedo_rst_step = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief Reset pedometer step counter.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_rst_step in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_step_reset_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.pedo_rst_step; + + return ret; +} + +/** + * @brief Enable pedometer algorithm.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_sens_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.pedo_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + } + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief pedo_sens: Enable pedometer algorithm.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_sens_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.pedo_en; + + return ret; +} + +/** + * @brief Minimum threshold to detect a peak. Default is 10h.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ths_min in reg + * CONFIG_PEDO_THS_MIN + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_threshold_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + config_pedo_ths_min.ths_min = val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Minimum threshold to detect a peak. Default is 10h.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ths_min in reg CONFIG_PEDO_THS_MIN + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_threshold_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + *val = config_pedo_ths_min.ths_min; + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief pedo_full_scale: Pedometer data range.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_fs in + * reg CONFIG_PEDO_THS_MIN + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pedo_fs_t val) { + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + config_pedo_ths_min.pedo_fs = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Pedometer data range.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of pedo_fs in + * reg CONFIG_PEDO_THS_MIN + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pedo_fs_t* val) { + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + switch(config_pedo_ths_min.pedo_fs) { + case LSM6DS3TR_C_PEDO_AT_2g: + *val = LSM6DS3TR_C_PEDO_AT_2g; + break; + + case LSM6DS3TR_C_PEDO_AT_4g: + *val = LSM6DS3TR_C_PEDO_AT_4g; + break; + + default: + *val = LSM6DS3TR_C_PEDO_FS_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Pedometer debounce configuration register (r/w).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of deb_step in reg PEDO_DEB_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_debounce_steps_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + pedo_deb_reg.deb_step = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Pedometer debounce configuration register (r/w).[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of deb_step in reg PEDO_DEB_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_debounce_steps_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + *val = pedo_deb_reg.deb_step; + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Debounce time. If the time between two consecutive steps is + * greater than DEB_TIME*80ms, the debouncer is reactivated. + * Default value: 01101[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of deb_time in reg PEDO_DEB_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_timeout_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + pedo_deb_reg.deb_time = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Debounce time. If the time between two consecutive steps is + * greater than DEB_TIME*80ms, the debouncer is reactivated. + * Default value: 01101[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of deb_time in reg PEDO_DEB_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_timeout_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + *val = pedo_deb_reg.deb_time; + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Time period register for step detection on delta time (r/w).[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_steps_period_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_STEP_COUNT_DELTA, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Time period register for step detection on delta time (r/w).[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_steps_period_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STEP_COUNT_DELTA, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_significant_motion + * @brief This section groups all the functions that manage the + * significant motion detection. + * @{ + * + */ + +/** + * @brief Enable significant motion detection function.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sign_motion_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_motion_sens_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.sign_motion_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + } + + return ret; +} + +/** + * @brief Enable significant motion detection function.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sign_motion_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_motion_sens_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.sign_motion_en; + + return ret; +} + +/** + * @brief Significant motion threshold.[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that store significant motion threshold. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_motion_threshold_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SM_THS, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Significant motion threshold.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that store significant motion threshold. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_motion_threshold_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SM_THS, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_tilt_detection + * @brief This section groups all the functions that manage the tilt + * event detection. + * @{ + * + */ + +/** + * @brief Enable tilt calculation.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tilt_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_sens_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.tilt_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + } + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief Enable tilt calculation.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tilt_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_sens_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.tilt_en; + + return ret; +} + +/** + * @brief Enable tilt calculation.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tilt_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wrist_tilt_sens_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.wrist_tilt_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + } + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief Enable tilt calculation.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tilt_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wrist_tilt_sens_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.wrist_tilt_en; + + return ret; +} + +/** + * @brief Absolute Wrist Tilt latency register (r/w). + * Absolute wrist tilt latency parameters. + * 1 LSB = 40 ms. Default value: 0Fh (600 ms).[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_latency_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_LAT, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt latency register (r/w). + * Absolute wrist tilt latency parameters. + * 1 LSB = 40 ms. Default value: 0Fh (600 ms).[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_latency_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_LAT, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt threshold register(r/w). + * Absolute wrist tilt threshold parameters. + * 1 LSB = 15.625 mg.Default value: 20h (500 mg).[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_threshold_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_THS, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt threshold register(r/w). + * Absolute wrist tilt threshold parameters. + * 1 LSB = 15.625 mg.Default value: 20h (500 mg).[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_threshold_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_THS, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt mask register (r/w).[set] + * + * @param ctx Read / write interface definitions + * @param val Registers A_WRIST_TILT_MASK + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_src_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_a_wrist_tilt_mask_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_MASK, (uint8_t*)val, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt mask register (r/w).[get] + * + * @param ctx Read / write interface definitions + * @param val Registers A_WRIST_TILT_MASK + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_src_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_a_wrist_tilt_mask_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_MASK, (uint8_t*)val, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_ magnetometer_sensor + * @brief This section groups all the functions that manage additional + * magnetometer sensor. + * @{ + * + */ + +/** + * @brief Enable soft-iron correction algorithm for magnetometer.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of soft_en in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_soft_iron_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.soft_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + } + + return ret; +} + +/** + * @brief Enable soft-iron correction algorithm for magnetometer.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of soft_en in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_soft_iron_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + *val = ctrl9_xl.soft_en; + + return ret; +} + +/** + * @brief Enable hard-iron correction algorithm for magnetometer.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of iron_en in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_hard_iron_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_config_t master_config; + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.iron_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + if(val != 0x00U) { + ctrl10_c.func_en = val; + } + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + } + } + + return ret; +} + +/** + * @brief Enable hard-iron correction algorithm for magnetometer.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of iron_en in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_hard_iron_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + *val = master_config.iron_en; + + return ret; +} + +/** + * @brief Soft iron 3x3 matrix. Value are expressed in sign-module format. + * (Es. SVVVVVVVb where S is the sign 0/+1/- and V is the value).[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_soft_iron_mat_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MAG_SI_XX, buff, 9); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Soft iron 3x3 matrix. Value are expressed in sign-module format. + * (Es. SVVVVVVVb where S is the sign 0/+1/- and V is the value).[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_soft_iron_mat_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MAG_SI_XX, buff, 9); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Offset for hard-iron compensation register (r/w). The value is + * expressed as a 16-bit word in two’s complement.[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_offset_set(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + buff[1] = (uint8_t)((uint16_t)val[0] / 256U); + buff[0] = (uint8_t)((uint16_t)val[0] - (buff[1] * 256U)); + buff[3] = (uint8_t)((uint16_t)val[1] / 256U); + buff[2] = (uint8_t)((uint16_t)val[1] - (buff[3] * 256U)); + buff[5] = (uint8_t)((uint16_t)val[2] / 256U); + buff[4] = (uint8_t)((uint16_t)val[2] - (buff[5] * 256U)); + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MAG_OFFX_L, buff, 6); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Offset for hard-iron compensation register(r/w). + * The value is expressed as a 16-bit word in two’s complement.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_offset_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MAG_OFFX_L, buff, 6); + + if(ret == 0) { + val[0] = (int16_t)buff[1]; + val[0] = (val[0] * 256) + (int16_t)buff[0]; + val[1] = (int16_t)buff[3]; + val[1] = (val[1] * 256) + (int16_t)buff[2]; + val[2] = (int16_t)buff[5]; + val[2] = (val[2] * 256) + (int16_t)buff[4]; + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Sensor_hub + * @brief This section groups all the functions that manage the sensor + * hub functionality. + * @{ + * + */ + +/** + * @brief Enable function.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values func_en + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_func_en_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.func_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief Sensor synchronization time frame with the step of 500 ms and + * full range of 5s. Unsigned 8-bit.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tph in reg SENSOR_SYNC_TIME_FRAME + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_sync_sens_frame_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_sensor_sync_time_frame_t sensor_sync_time_frame; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_TIME_FRAME, (uint8_t*)&sensor_sync_time_frame, 1); + + if(ret == 0) { + sensor_sync_time_frame.tph = val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_TIME_FRAME, (uint8_t*)&sensor_sync_time_frame, 1); + } + + return ret; +} + +/** + * @brief Sensor synchronization time frame with the step of 500 ms and + * full range of 5s. Unsigned 8-bit.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tph in reg SENSOR_SYNC_TIME_FRAME + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_sync_sens_frame_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_sensor_sync_time_frame_t sensor_sync_time_frame; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_TIME_FRAME, (uint8_t*)&sensor_sync_time_frame, 1); + *val = sensor_sync_time_frame.tph; + + return ret; +} + +/** + * @brief Resolution ratio of error code for sensor synchronization.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of rr in reg SENSOR_SYNC_RES_RATIO + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_sync_sens_ratio_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rr_t val) { + lsm6ds3tr_c_sensor_sync_res_ratio_t sensor_sync_res_ratio; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_RES_RATIO, (uint8_t*)&sensor_sync_res_ratio, 1); + + if(ret == 0) { + sensor_sync_res_ratio.rr = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_RES_RATIO, (uint8_t*)&sensor_sync_res_ratio, 1); + } + + return ret; +} + +/** + * @brief Resolution ratio of error code for sensor synchronization.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of rr in reg SENSOR_SYNC_RES_RATIO + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_sync_sens_ratio_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rr_t* val) { + lsm6ds3tr_c_sensor_sync_res_ratio_t sensor_sync_res_ratio; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_RES_RATIO, (uint8_t*)&sensor_sync_res_ratio, 1); + + switch(sensor_sync_res_ratio.rr) { + case LSM6DS3TR_C_RES_RATIO_2_11: + *val = LSM6DS3TR_C_RES_RATIO_2_11; + break; + + case LSM6DS3TR_C_RES_RATIO_2_12: + *val = LSM6DS3TR_C_RES_RATIO_2_12; + break; + + case LSM6DS3TR_C_RES_RATIO_2_13: + *val = LSM6DS3TR_C_RES_RATIO_2_13; + break; + + case LSM6DS3TR_C_RES_RATIO_2_14: + *val = LSM6DS3TR_C_RES_RATIO_2_14; + break; + + default: + *val = LSM6DS3TR_C_RES_RATIO_ND; + break; + } + + return ret; +} + +/** + * @brief Sensor hub I2C master enable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of master_on in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_master_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.master_on = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief Sensor hub I2C master enable.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of master_on in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_master_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + *val = master_config.master_on; + + return ret; +} + +/** + * @brief I2C interface pass-through.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pass_through_mode in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_pass_through_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.pass_through_mode = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief I2C interface pass-through.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pass_through_mode in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_pass_through_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + *val = master_config.pass_through_mode; + + return ret; +} + +/** + * @brief Master I2C pull-up enable/disable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pull_up_en in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_pin_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pull_up_en_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.pull_up_en = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief Master I2C pull-up enable/disable.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of pull_up_en in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_pin_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pull_up_en_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + switch(master_config.pull_up_en) { + case LSM6DS3TR_C_EXT_PULL_UP: + *val = LSM6DS3TR_C_EXT_PULL_UP; + break; + + case LSM6DS3TR_C_INTERNAL_PULL_UP: + *val = LSM6DS3TR_C_INTERNAL_PULL_UP; + break; + + default: + *val = LSM6DS3TR_C_SH_PIN_MODE; + break; + } + + return ret; +} + +/** + * @brief Sensor hub trigger signal selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of start_config in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_syncro_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_start_config_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.start_config = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief Sensor hub trigger signal selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of start_config in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_syncro_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_start_config_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + switch(master_config.start_config) { + case LSM6DS3TR_C_XL_GY_DRDY: + *val = LSM6DS3TR_C_XL_GY_DRDY; + break; + + case LSM6DS3TR_C_EXT_ON_INT2_PIN: + *val = LSM6DS3TR_C_EXT_ON_INT2_PIN; + break; + + default: + *val = LSM6DS3TR_C_SH_SYNCRO_ND; + break; + } + + return ret; +} + +/** + * @brief Manage the Master DRDY signal on INT1 pad.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_on_int1 in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_drdy_on_int1_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.drdy_on_int1 = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief Manage the Master DRDY signal on INT1 pad.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_on_int1 in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_drdy_on_int1_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + *val = master_config.drdy_on_int1; + + return ret; +} + +/** + * @brief Sensor hub output registers.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure of registers from SENSORHUB1_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_read_data_raw_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_emb_sh_read_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SENSORHUB1_REG, (uint8_t*)&(val->sh_byte_1), 12); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSORHUB13_REG, (uint8_t*)&(val->sh_byte_13), 6); + } + + return ret; +} + +/** + * @brief Master command code used for stamping for sensor sync.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of master_cmd_code in + * reg MASTER_CMD_CODE + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_cmd_sens_sync_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_cmd_code_t master_cmd_code; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CMD_CODE, (uint8_t*)&master_cmd_code, 1); + + if(ret == 0) { + master_cmd_code.master_cmd_code = val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CMD_CODE, (uint8_t*)&master_cmd_code, 1); + } + + return ret; +} + +/** + * @brief Master command code used for stamping for sensor sync.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of master_cmd_code in + * reg MASTER_CMD_CODE + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_cmd_sens_sync_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_cmd_code_t master_cmd_code; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CMD_CODE, (uint8_t*)&master_cmd_code, 1); + *val = master_cmd_code.master_cmd_code; + + return ret; +} + +/** + * @brief Error code used for sensor synchronization.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of error_code in + * reg SENS_SYNC_SPI_ERROR_CODE. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_spi_sync_error_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_sens_sync_spi_error_code_t sens_sync_spi_error_code; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENS_SYNC_SPI_ERROR_CODE, (uint8_t*)&sens_sync_spi_error_code, 1); + + if(ret == 0) { + sens_sync_spi_error_code.error_code = val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SENS_SYNC_SPI_ERROR_CODE, (uint8_t*)&sens_sync_spi_error_code, 1); + } + + return ret; +} + +/** + * @brief Error code used for sensor synchronization.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of error_code in + * reg SENS_SYNC_SPI_ERROR_CODE. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_spi_sync_error_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_sens_sync_spi_error_code_t sens_sync_spi_error_code; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENS_SYNC_SPI_ERROR_CODE, (uint8_t*)&sens_sync_spi_error_code, 1); + *val = sens_sync_spi_error_code.error_code; + + return ret; +} + +/** + * @brief Number of external sensors to be read by the sensor hub.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of aux_sens_on in reg SLAVE0_CONFIG. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_num_of_dev_connected_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_aux_sens_on_t val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + slave0_config.aux_sens_on = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Number of external sensors to be read by the sensor hub.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of aux_sens_on in reg SLAVE0_CONFIG. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t + lsm6ds3tr_c_sh_num_of_dev_connected_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_aux_sens_on_t* val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + switch(slave0_config.aux_sens_on) { + case LSM6DS3TR_C_SLV_0: + *val = LSM6DS3TR_C_SLV_0; + break; + + case LSM6DS3TR_C_SLV_0_1: + *val = LSM6DS3TR_C_SLV_0_1; + break; + + case LSM6DS3TR_C_SLV_0_1_2: + *val = LSM6DS3TR_C_SLV_0_1_2; + break; + + case LSM6DS3TR_C_SLV_0_1_2_3: + *val = LSM6DS3TR_C_SLV_0_1_2_3; + break; + + default: + *val = LSM6DS3TR_C_SLV_EN_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Configure slave 0 for perform a write.[set] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_data; 8 bit data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_cfg_write(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_write_t* val) { + lsm6ds3tr_c_slv0_add_t slv0_add; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv0_add.slave0_add = val->slv0_add; + slv0_add.rw_0 = 0; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV0_ADD, (uint8_t*)&slv0_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV0_SUBADD, &(val->slv0_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_DATAWRITE_SRC_MODE_SUB_SLV0, &(val->slv0_data), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + + return ret; +} + +/** + * @brief Configure slave 0 for perform a read.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_len; num of bit to read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slv0_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + lsm6ds3tr_c_slv0_add_t slv0_add; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv0_add.slave0_add = val->slv_add; + slv0_add.rw_0 = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV0_ADD, (uint8_t*)&slv0_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV0_SUBADD, &(val->slv_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + slave0_config.slave0_numop = val->slv_len; + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + } + + return ret; +} + +/** + * @brief Configure slave 1 for perform a read.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_len; num of bit to read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slv1_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + lsm6ds3tr_c_slv1_add_t slv1_add; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv1_add.slave1_add = val->slv_add; + slv1_add.r_1 = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV1_ADD, (uint8_t*)&slv1_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV1_SUBADD, &(val->slv_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + slave1_config.slave1_numop = val->slv_len; + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + } + + return ret; +} + +/** + * @brief Configure slave 2 for perform a read.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_len; num of bit to read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slv2_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val) { + lsm6ds3tr_c_slv2_add_t slv2_add; + lsm6ds3tr_c_slave2_config_t slave2_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv2_add.slave2_add = val->slv_add; + slv2_add.r_2 = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV2_ADD, (uint8_t*)&slv2_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV2_SUBADD, &(val->slv_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + slave2_config.slave2_numop = val->slv_len; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + } + + return ret; +} + +/** + * @brief Configure slave 3 for perform a read.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_len; num of bit to read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slv3_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val) { + lsm6ds3tr_c_slave3_config_t slave3_config; + lsm6ds3tr_c_slv3_add_t slv3_add; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv3_add.slave3_add = val->slv_add; + slv3_add.r_3 = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV3_ADD, (uint8_t*)&slv3_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLV3_SUBADD, (uint8_t*)&(val->slv_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + + if(ret == 0) { + slave3_config.slave3_numop = val->slv_len; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 0 starting from the + * sensor hub trigger.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slave0_rate in reg SLAVE0_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_0_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave0_rate_t val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + slave0_config.slave0_rate = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 0 starting from the + * sensor hub trigger.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of slave0_rate in reg SLAVE0_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_0_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave0_rate_t* val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + switch(slave0_config.slave0_rate) { + case LSM6DS3TR_C_SL0_NO_DEC: + *val = LSM6DS3TR_C_SL0_NO_DEC; + break; + + case LSM6DS3TR_C_SL0_DEC_2: + *val = LSM6DS3TR_C_SL0_DEC_2; + break; + + case LSM6DS3TR_C_SL0_DEC_4: + *val = LSM6DS3TR_C_SL0_DEC_4; + break; + + case LSM6DS3TR_C_SL0_DEC_8: + *val = LSM6DS3TR_C_SL0_DEC_8; + break; + + default: + *val = LSM6DS3TR_C_SL0_DEC_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Slave 0 write operation is performed only at the first sensor + * hub cycle. + * This is effective if the Aux_sens_on[1:0] field in + * SLAVE0_CONFIG(04h) is set to a value other than 00.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of write_once in reg SLAVE1_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_write_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_write_once_t val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + slave1_config.write_once = (uint8_t)val; + + if(ret == 0) { + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Slave 0 write operation is performed only at the first sensor + * hub cycle. + * This is effective if the Aux_sens_on[1:0] field in + * SLAVE0_CONFIG(04h) is set to a value other than 00.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of write_once in reg SLAVE1_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_write_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_write_once_t* val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + switch(slave1_config.write_once) { + case LSM6DS3TR_C_EACH_SH_CYCLE: + *val = LSM6DS3TR_C_EACH_SH_CYCLE; + break; + + case LSM6DS3TR_C_ONLY_FIRST_CYCLE: + *val = LSM6DS3TR_C_ONLY_FIRST_CYCLE; + break; + + default: + *val = LSM6DS3TR_C_SH_WR_MODE_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 1 starting from the + * sensor hub trigger.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slave1_rate in reg SLAVE1_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_1_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave1_rate_t val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + slave1_config.slave1_rate = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 1 starting from the + * sensor hub trigger.[get] + * + * @param ctx Read / write interface definitions reg SLAVE1_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_1_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave1_rate_t* val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + switch(slave1_config.slave1_rate) { + case LSM6DS3TR_C_SL1_NO_DEC: + *val = LSM6DS3TR_C_SL1_NO_DEC; + break; + + case LSM6DS3TR_C_SL1_DEC_2: + *val = LSM6DS3TR_C_SL1_DEC_2; + break; + + case LSM6DS3TR_C_SL1_DEC_4: + *val = LSM6DS3TR_C_SL1_DEC_4; + break; + + case LSM6DS3TR_C_SL1_DEC_8: + *val = LSM6DS3TR_C_SL1_DEC_8; + break; + + default: + *val = LSM6DS3TR_C_SL1_DEC_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 2 starting from the + * sensor hub trigger.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slave2_rate in reg SLAVE2_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_2_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave2_rate_t val) { + lsm6ds3tr_c_slave2_config_t slave2_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + slave2_config.slave2_rate = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 2 starting from the + * sensor hub trigger.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of slave2_rate in reg SLAVE2_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_2_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave2_rate_t* val) { + lsm6ds3tr_c_slave2_config_t slave2_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + switch(slave2_config.slave2_rate) { + case LSM6DS3TR_C_SL2_NO_DEC: + *val = LSM6DS3TR_C_SL2_NO_DEC; + break; + + case LSM6DS3TR_C_SL2_DEC_2: + *val = LSM6DS3TR_C_SL2_DEC_2; + break; + + case LSM6DS3TR_C_SL2_DEC_4: + *val = LSM6DS3TR_C_SL2_DEC_4; + break; + + case LSM6DS3TR_C_SL2_DEC_8: + *val = LSM6DS3TR_C_SL2_DEC_8; + break; + + default: + *val = LSM6DS3TR_C_SL2_DEC_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 3 starting from the + * sensor hub trigger.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slave3_rate in reg SLAVE3_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_3_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave3_rate_t val) { + lsm6ds3tr_c_slave3_config_t slave3_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + slave3_config.slave3_rate = (uint8_t)val; + + if(ret == 0) { + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 3 starting from the + * sensor hub trigger.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of slave3_rate in reg SLAVE3_CONFIG. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_3_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave3_rate_t* val) { + lsm6ds3tr_c_slave3_config_t slave3_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + + if(ret == 0) { + switch(slave3_config.slave3_rate) { + case LSM6DS3TR_C_SL3_NO_DEC: + *val = LSM6DS3TR_C_SL3_NO_DEC; + break; + + case LSM6DS3TR_C_SL3_DEC_2: + *val = LSM6DS3TR_C_SL3_DEC_2; + break; + + case LSM6DS3TR_C_SL3_DEC_4: + *val = LSM6DS3TR_C_SL3_DEC_4; + break; + + case LSM6DS3TR_C_SL3_DEC_8: + *val = LSM6DS3TR_C_SL3_DEC_8; + break; + + default: + *val = LSM6DS3TR_C_SL3_DEC_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @} + * + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.h b/applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.h new file mode 100644 index 000000000..8cb592c0d --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.h @@ -0,0 +1,2448 @@ +/** + ****************************************************************************** + * @file lsm6ds3tr_c_reg.h + * @author Sensors Software Solution Team + * @brief This file contains all the functions prototypes for the + * lsm6ds3tr_c_reg.c driver. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2021 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef LSM6DS3TR_C_DRIVER_H +#define LSM6DS3TR_C_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +#include +#include + +/** @addtogroup LSM6DS3TR_C + * @{ + * + */ + +/** @defgroup Endianness definitions + * @{ + * + */ + +#ifndef DRV_BYTE_ORDER +#ifndef __BYTE_ORDER__ + +#define DRV_LITTLE_ENDIAN 1234 +#define DRV_BIG_ENDIAN 4321 + +/** if _BYTE_ORDER is not defined, choose the endianness of your architecture + * by uncommenting the define which fits your platform endianness + */ +//#define DRV_BYTE_ORDER DRV_BIG_ENDIAN +#define DRV_BYTE_ORDER DRV_LITTLE_ENDIAN + +#else /* defined __BYTE_ORDER__ */ + +#define DRV_LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ +#define DRV_BIG_ENDIAN __ORDER_BIG_ENDIAN__ +#define DRV_BYTE_ORDER __BYTE_ORDER__ + +#endif /* __BYTE_ORDER__*/ +#endif /* DRV_BYTE_ORDER */ + +/** + * @} + * + */ + +/** @defgroup STMicroelectronics sensors common types + * @{ + * + */ + +#ifndef MEMS_SHARED_TYPES +#define MEMS_SHARED_TYPES + +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} bitwise_t; + +#define PROPERTY_DISABLE (0U) +#define PROPERTY_ENABLE (1U) + +/** @addtogroup Interfaces_Functions + * @brief This section provide a set of functions used to read and + * write a generic register of the device. + * MANDATORY: return 0 -> no Error. + * @{ + * + */ + +typedef int32_t (*stmdev_write_ptr)(void*, uint8_t, const uint8_t*, uint16_t); +typedef int32_t (*stmdev_read_ptr)(void*, uint8_t, uint8_t*, uint16_t); +typedef void (*stmdev_mdelay_ptr)(uint32_t millisec); + +typedef struct { + /** Component mandatory fields **/ + stmdev_write_ptr write_reg; + stmdev_read_ptr read_reg; + /** Component optional fields **/ + stmdev_mdelay_ptr mdelay; + /** Customizable optional pointer **/ + void* handle; +} stmdev_ctx_t; + +/** + * @} + * + */ + +#endif /* MEMS_SHARED_TYPES */ + +#ifndef MEMS_UCF_SHARED_TYPES +#define MEMS_UCF_SHARED_TYPES + +/** @defgroup Generic address-data structure definition + * @brief This structure is useful to load a predefined configuration + * of a sensor. + * You can create a sensor configuration by your own or using + * Unico / Unicleo tools available on STMicroelectronics + * web site. + * + * @{ + * + */ + +typedef struct { + uint8_t address; + uint8_t data; +} ucf_line_t; + +/** + * @} + * + */ + +#endif /* MEMS_UCF_SHARED_TYPES */ + +/** + * @} + * + */ + +/** @defgroup LSM6DS3TR_C_Infos + * @{ + * + */ + +/** I2C Device Address 8 bit format if SA0=0 -> D5 if SA0=1 -> D7 **/ +#define LSM6DS3TR_C_I2C_ADD_L 0xD5U +#define LSM6DS3TR_C_I2C_ADD_H 0xD7U + +/** Device Identification (Who am I) **/ +#define LSM6DS3TR_C_ID 0x6AU + +/** + * @} + * + */ + +#define LSM6DS3TR_C_FUNC_CFG_ACCESS 0x01U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 5; + uint8_t func_cfg_en : 3; /* func_cfg_en + func_cfg_en_b */ +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t func_cfg_en : 3; /* func_cfg_en + func_cfg_en_b */ + uint8_t not_used_01 : 5; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_func_cfg_access_t; + +#define LSM6DS3TR_C_SENSOR_SYNC_TIME_FRAME 0x04U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t tph : 4; + uint8_t not_used_01 : 4; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 4; + uint8_t tph : 4; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensor_sync_time_frame_t; + +#define LSM6DS3TR_C_SENSOR_SYNC_RES_RATIO 0x05U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t rr : 2; + uint8_t not_used_01 : 6; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 6; + uint8_t rr : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensor_sync_res_ratio_t; + +#define LSM6DS3TR_C_FIFO_CTRL1 0x06U +typedef struct { + uint8_t fth : 8; /* + FIFO_CTRL2(fth) */ +} lsm6ds3tr_c_fifo_ctrl1_t; + +#define LSM6DS3TR_C_FIFO_CTRL2 0x07U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t fth : 3; /* + FIFO_CTRL1(fth) */ + uint8_t fifo_temp_en : 1; + uint8_t not_used_01 : 2; + uint8_t timer_pedo_fifo_drdy : 1; + uint8_t timer_pedo_fifo_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t timer_pedo_fifo_en : 1; + uint8_t timer_pedo_fifo_drdy : 1; + uint8_t not_used_01 : 2; + uint8_t fifo_temp_en : 1; + uint8_t fth : 3; /* + FIFO_CTRL1(fth) */ +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_ctrl2_t; + +#define LSM6DS3TR_C_FIFO_CTRL3 0x08U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t dec_fifo_xl : 3; + uint8_t dec_fifo_gyro : 3; + uint8_t not_used_01 : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 2; + uint8_t dec_fifo_gyro : 3; + uint8_t dec_fifo_xl : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_ctrl3_t; + +#define LSM6DS3TR_C_FIFO_CTRL4 0x09U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t dec_ds3_fifo : 3; + uint8_t dec_ds4_fifo : 3; + uint8_t only_high_data : 1; + uint8_t stop_on_fth : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t stop_on_fth : 1; + uint8_t only_high_data : 1; + uint8_t dec_ds4_fifo : 3; + uint8_t dec_ds3_fifo : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_ctrl4_t; + +#define LSM6DS3TR_C_FIFO_CTRL5 0x0AU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t fifo_mode : 3; + uint8_t odr_fifo : 4; + uint8_t not_used_01 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 1; + uint8_t odr_fifo : 4; + uint8_t fifo_mode : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_ctrl5_t; + +#define LSM6DS3TR_C_DRDY_PULSE_CFG_G 0x0BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int2_wrist_tilt : 1; + uint8_t not_used_01 : 6; + uint8_t drdy_pulsed : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t drdy_pulsed : 1; + uint8_t not_used_01 : 6; + uint8_t int2_wrist_tilt : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_drdy_pulse_cfg_g_t; + +#define LSM6DS3TR_C_INT1_CTRL 0x0DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int1_drdy_xl : 1; + uint8_t int1_drdy_g : 1; + uint8_t int1_boot : 1; + uint8_t int1_fth : 1; + uint8_t int1_fifo_ovr : 1; + uint8_t int1_full_flag : 1; + uint8_t int1_sign_mot : 1; + uint8_t int1_step_detector : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t int1_step_detector : 1; + uint8_t int1_sign_mot : 1; + uint8_t int1_full_flag : 1; + uint8_t int1_fifo_ovr : 1; + uint8_t int1_fth : 1; + uint8_t int1_boot : 1; + uint8_t int1_drdy_g : 1; + uint8_t int1_drdy_xl : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_int1_ctrl_t; + +#define LSM6DS3TR_C_INT2_CTRL 0x0EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int2_drdy_xl : 1; + uint8_t int2_drdy_g : 1; + uint8_t int2_drdy_temp : 1; + uint8_t int2_fth : 1; + uint8_t int2_fifo_ovr : 1; + uint8_t int2_full_flag : 1; + uint8_t int2_step_count_ov : 1; + uint8_t int2_step_delta : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t int2_step_delta : 1; + uint8_t int2_step_count_ov : 1; + uint8_t int2_full_flag : 1; + uint8_t int2_fifo_ovr : 1; + uint8_t int2_fth : 1; + uint8_t int2_drdy_temp : 1; + uint8_t int2_drdy_g : 1; + uint8_t int2_drdy_xl : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_int2_ctrl_t; + +#define LSM6DS3TR_C_WHO_AM_I 0x0FU +#define LSM6DS3TR_C_CTRL1_XL 0x10U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bw0_xl : 1; + uint8_t lpf1_bw_sel : 1; + uint8_t fs_xl : 2; + uint8_t odr_xl : 4; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t odr_xl : 4; + uint8_t fs_xl : 2; + uint8_t lpf1_bw_sel : 1; + uint8_t bw0_xl : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl1_xl_t; + +#define LSM6DS3TR_C_CTRL2_G 0x11U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 1; + uint8_t fs_g : 3; /* fs_g + fs_125 */ + uint8_t odr_g : 4; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t odr_g : 4; + uint8_t fs_g : 3; /* fs_g + fs_125 */ + uint8_t not_used_01 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl2_g_t; + +#define LSM6DS3TR_C_CTRL3_C 0x12U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t sw_reset : 1; + uint8_t ble : 1; + uint8_t if_inc : 1; + uint8_t sim : 1; + uint8_t pp_od : 1; + uint8_t h_lactive : 1; + uint8_t bdu : 1; + uint8_t boot : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t boot : 1; + uint8_t bdu : 1; + uint8_t h_lactive : 1; + uint8_t pp_od : 1; + uint8_t sim : 1; + uint8_t if_inc : 1; + uint8_t ble : 1; + uint8_t sw_reset : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl3_c_t; + +#define LSM6DS3TR_C_CTRL4_C 0x13U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 1; + uint8_t lpf1_sel_g : 1; + uint8_t i2c_disable : 1; + uint8_t drdy_mask : 1; + uint8_t den_drdy_int1 : 1; + uint8_t int2_on_int1 : 1; + uint8_t sleep : 1; + uint8_t den_xl_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t den_xl_en : 1; + uint8_t sleep : 1; + uint8_t int2_on_int1 : 1; + uint8_t den_drdy_int1 : 1; + uint8_t drdy_mask : 1; + uint8_t i2c_disable : 1; + uint8_t lpf1_sel_g : 1; + uint8_t not_used_01 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl4_c_t; + +#define LSM6DS3TR_C_CTRL5_C 0x14U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t st_xl : 2; + uint8_t st_g : 2; + uint8_t den_lh : 1; + uint8_t rounding : 3; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t rounding : 3; + uint8_t den_lh : 1; + uint8_t st_g : 2; + uint8_t st_xl : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl5_c_t; + +#define LSM6DS3TR_C_CTRL6_C 0x15U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t ftype : 2; + uint8_t not_used_01 : 1; + uint8_t usr_off_w : 1; + uint8_t xl_hm_mode : 1; + uint8_t den_mode : 3; /* trig_en + lvl_en + lvl2_en */ +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t den_mode : 3; /* trig_en + lvl_en + lvl2_en */ + uint8_t xl_hm_mode : 1; + uint8_t usr_off_w : 1; + uint8_t not_used_01 : 1; + uint8_t ftype : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl6_c_t; + +#define LSM6DS3TR_C_CTRL7_G 0x16U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 2; + uint8_t rounding_status : 1; + uint8_t not_used_02 : 1; + uint8_t hpm_g : 2; + uint8_t hp_en_g : 1; + uint8_t g_hm_mode : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t g_hm_mode : 1; + uint8_t hp_en_g : 1; + uint8_t hpm_g : 2; + uint8_t not_used_02 : 1; + uint8_t rounding_status : 1; + uint8_t not_used_01 : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl7_g_t; + +#define LSM6DS3TR_C_CTRL8_XL 0x17U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t low_pass_on_6d : 1; + uint8_t not_used_01 : 1; + uint8_t hp_slope_xl_en : 1; + uint8_t input_composite : 1; + uint8_t hp_ref_mode : 1; + uint8_t hpcf_xl : 2; + uint8_t lpf2_xl_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t lpf2_xl_en : 1; + uint8_t hpcf_xl : 2; + uint8_t hp_ref_mode : 1; + uint8_t input_composite : 1; + uint8_t hp_slope_xl_en : 1; + uint8_t not_used_01 : 1; + uint8_t low_pass_on_6d : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl8_xl_t; + +#define LSM6DS3TR_C_CTRL9_XL 0x18U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 2; + uint8_t soft_en : 1; + uint8_t not_used_02 : 1; + uint8_t den_xl_g : 1; + uint8_t den_z : 1; + uint8_t den_y : 1; + uint8_t den_x : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t den_x : 1; + uint8_t den_y : 1; + uint8_t den_z : 1; + uint8_t den_xl_g : 1; + uint8_t not_used_02 : 1; + uint8_t soft_en : 1; + uint8_t not_used_01 : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl9_xl_t; + +#define LSM6DS3TR_C_CTRL10_C 0x19U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t sign_motion_en : 1; + uint8_t pedo_rst_step : 1; + uint8_t func_en : 1; + uint8_t tilt_en : 1; + uint8_t pedo_en : 1; + uint8_t timer_en : 1; + uint8_t not_used_01 : 1; + uint8_t wrist_tilt_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t wrist_tilt_en : 1; + uint8_t not_used_01 : 1; + uint8_t timer_en : 1; + uint8_t pedo_en : 1; + uint8_t tilt_en : 1; + uint8_t func_en : 1; + uint8_t pedo_rst_step : 1; + uint8_t sign_motion_en : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl10_c_t; + +#define LSM6DS3TR_C_MASTER_CONFIG 0x1AU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t master_on : 1; + uint8_t iron_en : 1; + uint8_t pass_through_mode : 1; + uint8_t pull_up_en : 1; + uint8_t start_config : 1; + uint8_t not_used_01 : 1; + uint8_t data_valid_sel_fifo : 1; + uint8_t drdy_on_int1 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t drdy_on_int1 : 1; + uint8_t data_valid_sel_fifo : 1; + uint8_t not_used_01 : 1; + uint8_t start_config : 1; + uint8_t pull_up_en : 1; + uint8_t pass_through_mode : 1; + uint8_t iron_en : 1; + uint8_t master_on : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_master_config_t; + +#define LSM6DS3TR_C_WAKE_UP_SRC 0x1BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t z_wu : 1; + uint8_t y_wu : 1; + uint8_t x_wu : 1; + uint8_t wu_ia : 1; + uint8_t sleep_state_ia : 1; + uint8_t ff_ia : 1; + uint8_t not_used_01 : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 2; + uint8_t ff_ia : 1; + uint8_t sleep_state_ia : 1; + uint8_t wu_ia : 1; + uint8_t x_wu : 1; + uint8_t y_wu : 1; + uint8_t z_wu : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_wake_up_src_t; + +#define LSM6DS3TR_C_TAP_SRC 0x1CU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t z_tap : 1; + uint8_t y_tap : 1; + uint8_t x_tap : 1; + uint8_t tap_sign : 1; + uint8_t double_tap : 1; + uint8_t single_tap : 1; + uint8_t tap_ia : 1; + uint8_t not_used_01 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 1; + uint8_t tap_ia : 1; + uint8_t single_tap : 1; + uint8_t double_tap : 1; + uint8_t tap_sign : 1; + uint8_t x_tap : 1; + uint8_t y_tap : 1; + uint8_t z_tap : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_tap_src_t; + +#define LSM6DS3TR_C_D6D_SRC 0x1DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t xl : 1; + uint8_t xh : 1; + uint8_t yl : 1; + uint8_t yh : 1; + uint8_t zl : 1; + uint8_t zh : 1; + uint8_t d6d_ia : 1; + uint8_t den_drdy : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t den_drdy : 1; + uint8_t d6d_ia : 1; + uint8_t zh : 1; + uint8_t zl : 1; + uint8_t yh : 1; + uint8_t yl : 1; + uint8_t xh : 1; + uint8_t xl : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_d6d_src_t; + +#define LSM6DS3TR_C_STATUS_REG 0x1EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t xlda : 1; + uint8_t gda : 1; + uint8_t tda : 1; + uint8_t not_used_01 : 5; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 5; + uint8_t tda : 1; + uint8_t gda : 1; + uint8_t xlda : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_status_reg_t; + +#define LSM6DS3TR_C_OUT_TEMP_L 0x20U +#define LSM6DS3TR_C_OUT_TEMP_H 0x21U +#define LSM6DS3TR_C_OUTX_L_G 0x22U +#define LSM6DS3TR_C_OUTX_H_G 0x23U +#define LSM6DS3TR_C_OUTY_L_G 0x24U +#define LSM6DS3TR_C_OUTY_H_G 0x25U +#define LSM6DS3TR_C_OUTZ_L_G 0x26U +#define LSM6DS3TR_C_OUTZ_H_G 0x27U +#define LSM6DS3TR_C_OUTX_L_XL 0x28U +#define LSM6DS3TR_C_OUTX_H_XL 0x29U +#define LSM6DS3TR_C_OUTY_L_XL 0x2AU +#define LSM6DS3TR_C_OUTY_H_XL 0x2BU +#define LSM6DS3TR_C_OUTZ_L_XL 0x2CU +#define LSM6DS3TR_C_OUTZ_H_XL 0x2DU +#define LSM6DS3TR_C_SENSORHUB1_REG 0x2EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub1_reg_t; + +#define LSM6DS3TR_C_SENSORHUB2_REG 0x2FU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub2_reg_t; + +#define LSM6DS3TR_C_SENSORHUB3_REG 0x30U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub3_reg_t; + +#define LSM6DS3TR_C_SENSORHUB4_REG 0x31U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub4_reg_t; + +#define LSM6DS3TR_C_SENSORHUB5_REG 0x32U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub5_reg_t; + +#define LSM6DS3TR_C_SENSORHUB6_REG 0x33U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub6_reg_t; + +#define LSM6DS3TR_C_SENSORHUB7_REG 0x34U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub7_reg_t; + +#define LSM6DS3TR_C_SENSORHUB8_REG 0x35U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub8_reg_t; + +#define LSM6DS3TR_C_SENSORHUB9_REG 0x36U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub9_reg_t; + +#define LSM6DS3TR_C_SENSORHUB10_REG 0x37U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub10_reg_t; + +#define LSM6DS3TR_C_SENSORHUB11_REG 0x38U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub11_reg_t; + +#define LSM6DS3TR_C_SENSORHUB12_REG 0x39U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub12_reg_t; + +#define LSM6DS3TR_C_FIFO_STATUS1 0x3AU +typedef struct { + uint8_t diff_fifo : 8; /* + FIFO_STATUS2(diff_fifo) */ +} lsm6ds3tr_c_fifo_status1_t; + +#define LSM6DS3TR_C_FIFO_STATUS2 0x3BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t diff_fifo : 3; /* + FIFO_STATUS1(diff_fifo) */ + uint8_t not_used_01 : 1; + uint8_t fifo_empty : 1; + uint8_t fifo_full_smart : 1; + uint8_t over_run : 1; + uint8_t waterm : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t waterm : 1; + uint8_t over_run : 1; + uint8_t fifo_full_smart : 1; + uint8_t fifo_empty : 1; + uint8_t not_used_01 : 1; + uint8_t diff_fifo : 3; /* + FIFO_STATUS1(diff_fifo) */ +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_status2_t; + +#define LSM6DS3TR_C_FIFO_STATUS3 0x3CU +typedef struct { + uint8_t fifo_pattern : 8; /* + FIFO_STATUS4(fifo_pattern) */ +} lsm6ds3tr_c_fifo_status3_t; + +#define LSM6DS3TR_C_FIFO_STATUS4 0x3DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t fifo_pattern : 2; /* + FIFO_STATUS3(fifo_pattern) */ + uint8_t not_used_01 : 6; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 6; + uint8_t fifo_pattern : 2; /* + FIFO_STATUS3(fifo_pattern) */ +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_status4_t; + +#define LSM6DS3TR_C_FIFO_DATA_OUT_L 0x3EU +#define LSM6DS3TR_C_FIFO_DATA_OUT_H 0x3FU +#define LSM6DS3TR_C_TIMESTAMP0_REG 0x40U +#define LSM6DS3TR_C_TIMESTAMP1_REG 0x41U +#define LSM6DS3TR_C_TIMESTAMP2_REG 0x42U +#define LSM6DS3TR_C_STEP_TIMESTAMP_L 0x49U +#define LSM6DS3TR_C_STEP_TIMESTAMP_H 0x4AU +#define LSM6DS3TR_C_STEP_COUNTER_L 0x4BU +#define LSM6DS3TR_C_STEP_COUNTER_H 0x4CU + +#define LSM6DS3TR_C_SENSORHUB13_REG 0x4DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub13_reg_t; + +#define LSM6DS3TR_C_SENSORHUB14_REG 0x4EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub14_reg_t; + +#define LSM6DS3TR_C_SENSORHUB15_REG 0x4FU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub15_reg_t; + +#define LSM6DS3TR_C_SENSORHUB16_REG 0x50U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub16_reg_t; + +#define LSM6DS3TR_C_SENSORHUB17_REG 0x51U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub17_reg_t; + +#define LSM6DS3TR_C_SENSORHUB18_REG 0x52U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub18_reg_t; + +#define LSM6DS3TR_C_FUNC_SRC1 0x53U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t sensorhub_end_op : 1; + uint8_t si_end_op : 1; + uint8_t hi_fail : 1; + uint8_t step_overflow : 1; + uint8_t step_detected : 1; + uint8_t tilt_ia : 1; + uint8_t sign_motion_ia : 1; + uint8_t step_count_delta_ia : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t step_count_delta_ia : 1; + uint8_t sign_motion_ia : 1; + uint8_t tilt_ia : 1; + uint8_t step_detected : 1; + uint8_t step_overflow : 1; + uint8_t hi_fail : 1; + uint8_t si_end_op : 1; + uint8_t sensorhub_end_op : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_func_src1_t; + +#define LSM6DS3TR_C_FUNC_SRC2 0x54U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t wrist_tilt_ia : 1; + uint8_t not_used_01 : 2; + uint8_t slave0_nack : 1; + uint8_t slave1_nack : 1; + uint8_t slave2_nack : 1; + uint8_t slave3_nack : 1; + uint8_t not_used_02 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_02 : 1; + uint8_t slave3_nack : 1; + uint8_t slave2_nack : 1; + uint8_t slave1_nack : 1; + uint8_t slave0_nack : 1; + uint8_t not_used_01 : 2; + uint8_t wrist_tilt_ia : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_func_src2_t; + +#define LSM6DS3TR_C_WRIST_TILT_IA 0x55U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 2; + uint8_t wrist_tilt_ia_zneg : 1; + uint8_t wrist_tilt_ia_zpos : 1; + uint8_t wrist_tilt_ia_yneg : 1; + uint8_t wrist_tilt_ia_ypos : 1; + uint8_t wrist_tilt_ia_xneg : 1; + uint8_t wrist_tilt_ia_xpos : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t wrist_tilt_ia_xpos : 1; + uint8_t wrist_tilt_ia_xneg : 1; + uint8_t wrist_tilt_ia_ypos : 1; + uint8_t wrist_tilt_ia_yneg : 1; + uint8_t wrist_tilt_ia_zpos : 1; + uint8_t wrist_tilt_ia_zneg : 1; + uint8_t not_used_01 : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_wrist_tilt_ia_t; + +#define LSM6DS3TR_C_TAP_CFG 0x58U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t lir : 1; + uint8_t tap_z_en : 1; + uint8_t tap_y_en : 1; + uint8_t tap_x_en : 1; + uint8_t slope_fds : 1; + uint8_t inact_en : 2; + uint8_t interrupts_enable : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t interrupts_enable : 1; + uint8_t inact_en : 2; + uint8_t slope_fds : 1; + uint8_t tap_x_en : 1; + uint8_t tap_y_en : 1; + uint8_t tap_z_en : 1; + uint8_t lir : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_tap_cfg_t; + +#define LSM6DS3TR_C_TAP_THS_6D 0x59U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t tap_ths : 5; + uint8_t sixd_ths : 2; + uint8_t d4d_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t d4d_en : 1; + uint8_t sixd_ths : 2; + uint8_t tap_ths : 5; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_tap_ths_6d_t; + +#define LSM6DS3TR_C_INT_DUR2 0x5AU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t shock : 2; + uint8_t quiet : 2; + uint8_t dur : 4; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t dur : 4; + uint8_t quiet : 2; + uint8_t shock : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_int_dur2_t; + +#define LSM6DS3TR_C_WAKE_UP_THS 0x5BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t wk_ths : 6; + uint8_t not_used_01 : 1; + uint8_t single_double_tap : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t single_double_tap : 1; + uint8_t not_used_01 : 1; + uint8_t wk_ths : 6; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_wake_up_ths_t; + +#define LSM6DS3TR_C_WAKE_UP_DUR 0x5CU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t sleep_dur : 4; + uint8_t timer_hr : 1; + uint8_t wake_dur : 2; + uint8_t ff_dur : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t ff_dur : 1; + uint8_t wake_dur : 2; + uint8_t timer_hr : 1; + uint8_t sleep_dur : 4; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_wake_up_dur_t; + +#define LSM6DS3TR_C_FREE_FALL 0x5DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t ff_ths : 3; + uint8_t ff_dur : 5; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t ff_dur : 5; + uint8_t ff_ths : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_free_fall_t; + +#define LSM6DS3TR_C_MD1_CFG 0x5EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int1_timer : 1; + uint8_t int1_tilt : 1; + uint8_t int1_6d : 1; + uint8_t int1_double_tap : 1; + uint8_t int1_ff : 1; + uint8_t int1_wu : 1; + uint8_t int1_single_tap : 1; + uint8_t int1_inact_state : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t int1_inact_state : 1; + uint8_t int1_single_tap : 1; + uint8_t int1_wu : 1; + uint8_t int1_ff : 1; + uint8_t int1_double_tap : 1; + uint8_t int1_6d : 1; + uint8_t int1_tilt : 1; + uint8_t int1_timer : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_md1_cfg_t; + +#define LSM6DS3TR_C_MD2_CFG 0x5FU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int2_iron : 1; + uint8_t int2_tilt : 1; + uint8_t int2_6d : 1; + uint8_t int2_double_tap : 1; + uint8_t int2_ff : 1; + uint8_t int2_wu : 1; + uint8_t int2_single_tap : 1; + uint8_t int2_inact_state : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t int2_inact_state : 1; + uint8_t int2_single_tap : 1; + uint8_t int2_wu : 1; + uint8_t int2_ff : 1; + uint8_t int2_double_tap : 1; + uint8_t int2_6d : 1; + uint8_t int2_tilt : 1; + uint8_t int2_iron : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_md2_cfg_t; + +#define LSM6DS3TR_C_MASTER_CMD_CODE 0x60U +typedef struct { + uint8_t master_cmd_code : 8; +} lsm6ds3tr_c_master_cmd_code_t; + +#define LSM6DS3TR_C_SENS_SYNC_SPI_ERROR_CODE 0x61U +typedef struct { + uint8_t error_code : 8; +} lsm6ds3tr_c_sens_sync_spi_error_code_t; + +#define LSM6DS3TR_C_OUT_MAG_RAW_X_L 0x66U +#define LSM6DS3TR_C_OUT_MAG_RAW_X_H 0x67U +#define LSM6DS3TR_C_OUT_MAG_RAW_Y_L 0x68U +#define LSM6DS3TR_C_OUT_MAG_RAW_Y_H 0x69U +#define LSM6DS3TR_C_OUT_MAG_RAW_Z_L 0x6AU +#define LSM6DS3TR_C_OUT_MAG_RAW_Z_H 0x6BU +#define LSM6DS3TR_C_X_OFS_USR 0x73U +#define LSM6DS3TR_C_Y_OFS_USR 0x74U +#define LSM6DS3TR_C_Z_OFS_USR 0x75U +#define LSM6DS3TR_C_SLV0_ADD 0x02U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t rw_0 : 1; + uint8_t slave0_add : 7; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave0_add : 7; + uint8_t rw_0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slv0_add_t; + +#define LSM6DS3TR_C_SLV0_SUBADD 0x03U +typedef struct { + uint8_t slave0_reg : 8; +} lsm6ds3tr_c_slv0_subadd_t; + +#define LSM6DS3TR_C_SLAVE0_CONFIG 0x04U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t slave0_numop : 3; + uint8_t src_mode : 1; + uint8_t aux_sens_on : 2; + uint8_t slave0_rate : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave0_rate : 2; + uint8_t aux_sens_on : 2; + uint8_t src_mode : 1; + uint8_t slave0_numop : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slave0_config_t; + +#define LSM6DS3TR_C_SLV1_ADD 0x05U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t r_1 : 1; + uint8_t slave1_add : 7; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave1_add : 7; + uint8_t r_1 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slv1_add_t; + +#define LSM6DS3TR_C_SLV1_SUBADD 0x06U +typedef struct { + uint8_t slave1_reg : 8; +} lsm6ds3tr_c_slv1_subadd_t; + +#define LSM6DS3TR_C_SLAVE1_CONFIG 0x07U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t slave1_numop : 3; + uint8_t not_used_01 : 2; + uint8_t write_once : 1; + uint8_t slave1_rate : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave1_rate : 2; + uint8_t write_once : 1; + uint8_t not_used_01 : 2; + uint8_t slave1_numop : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slave1_config_t; + +#define LSM6DS3TR_C_SLV2_ADD 0x08U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t r_2 : 1; + uint8_t slave2_add : 7; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave2_add : 7; + uint8_t r_2 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slv2_add_t; + +#define LSM6DS3TR_C_SLV2_SUBADD 0x09U +typedef struct { + uint8_t slave2_reg : 8; +} lsm6ds3tr_c_slv2_subadd_t; + +#define LSM6DS3TR_C_SLAVE2_CONFIG 0x0AU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t slave2_numop : 3; + uint8_t not_used_01 : 3; + uint8_t slave2_rate : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave2_rate : 2; + uint8_t not_used_01 : 3; + uint8_t slave2_numop : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slave2_config_t; + +#define LSM6DS3TR_C_SLV3_ADD 0x0BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t r_3 : 1; + uint8_t slave3_add : 7; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave3_add : 7; + uint8_t r_3 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slv3_add_t; + +#define LSM6DS3TR_C_SLV3_SUBADD 0x0CU +typedef struct { + uint8_t slave3_reg : 8; +} lsm6ds3tr_c_slv3_subadd_t; + +#define LSM6DS3TR_C_SLAVE3_CONFIG 0x0DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t slave3_numop : 3; + uint8_t not_used_01 : 3; + uint8_t slave3_rate : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave3_rate : 2; + uint8_t not_used_01 : 3; + uint8_t slave3_numop : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slave3_config_t; + +#define LSM6DS3TR_C_DATAWRITE_SRC_MODE_SUB_SLV0 0x0EU +typedef struct { + uint8_t slave_dataw : 8; +} lsm6ds3tr_c_datawrite_src_mode_sub_slv0_t; + +#define LSM6DS3TR_C_CONFIG_PEDO_THS_MIN 0x0FU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t ths_min : 5; + uint8_t not_used_01 : 2; + uint8_t pedo_fs : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t pedo_fs : 1; + uint8_t not_used_01 : 2; + uint8_t ths_min : 5; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_config_pedo_ths_min_t; + +#define LSM6DS3TR_C_SM_THS 0x13U +#define LSM6DS3TR_C_PEDO_DEB_REG 0x14U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t deb_step : 3; + uint8_t deb_time : 5; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t deb_time : 5; + uint8_t deb_step : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_pedo_deb_reg_t; + +#define LSM6DS3TR_C_STEP_COUNT_DELTA 0x15U +#define LSM6DS3TR_C_MAG_SI_XX 0x24U +#define LSM6DS3TR_C_MAG_SI_XY 0x25U +#define LSM6DS3TR_C_MAG_SI_XZ 0x26U +#define LSM6DS3TR_C_MAG_SI_YX 0x27U +#define LSM6DS3TR_C_MAG_SI_YY 0x28U +#define LSM6DS3TR_C_MAG_SI_YZ 0x29U +#define LSM6DS3TR_C_MAG_SI_ZX 0x2AU +#define LSM6DS3TR_C_MAG_SI_ZY 0x2BU +#define LSM6DS3TR_C_MAG_SI_ZZ 0x2CU +#define LSM6DS3TR_C_MAG_OFFX_L 0x2DU +#define LSM6DS3TR_C_MAG_OFFX_H 0x2EU +#define LSM6DS3TR_C_MAG_OFFY_L 0x2FU +#define LSM6DS3TR_C_MAG_OFFY_H 0x30U +#define LSM6DS3TR_C_MAG_OFFZ_L 0x31U +#define LSM6DS3TR_C_MAG_OFFZ_H 0x32U +#define LSM6DS3TR_C_A_WRIST_TILT_LAT 0x50U +#define LSM6DS3TR_C_A_WRIST_TILT_THS 0x54U +#define LSM6DS3TR_C_A_WRIST_TILT_MASK 0x59U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 2; + uint8_t wrist_tilt_mask_zneg : 1; + uint8_t wrist_tilt_mask_zpos : 1; + uint8_t wrist_tilt_mask_yneg : 1; + uint8_t wrist_tilt_mask_ypos : 1; + uint8_t wrist_tilt_mask_xneg : 1; + uint8_t wrist_tilt_mask_xpos : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t wrist_tilt_mask_xpos : 1; + uint8_t wrist_tilt_mask_xneg : 1; + uint8_t wrist_tilt_mask_ypos : 1; + uint8_t wrist_tilt_mask_yneg : 1; + uint8_t wrist_tilt_mask_zpos : 1; + uint8_t wrist_tilt_mask_zneg : 1; + uint8_t not_used_01 : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_a_wrist_tilt_mask_t; + +/** + * @defgroup LSM6DS3TR_C_Register_Union + * @brief This union group all the registers having a bit-field + * description. + * This union is useful but it's not needed by the driver. + * + * REMOVING this union you are compliant with: + * MISRA-C 2012 [Rule 19.2] -> " Union are not allowed " + * + * @{ + * + */ +typedef union { + lsm6ds3tr_c_func_cfg_access_t func_cfg_access; + lsm6ds3tr_c_sensor_sync_time_frame_t sensor_sync_time_frame; + lsm6ds3tr_c_sensor_sync_res_ratio_t sensor_sync_res_ratio; + lsm6ds3tr_c_fifo_ctrl1_t fifo_ctrl1; + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + lsm6ds3tr_c_int1_ctrl_t int1_ctrl; + lsm6ds3tr_c_int2_ctrl_t int2_ctrl; + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + lsm6ds3tr_c_master_config_t master_config; + lsm6ds3tr_c_wake_up_src_t wake_up_src; + lsm6ds3tr_c_tap_src_t tap_src; + lsm6ds3tr_c_d6d_src_t d6d_src; + lsm6ds3tr_c_status_reg_t status_reg; + lsm6ds3tr_c_sensorhub1_reg_t sensorhub1_reg; + lsm6ds3tr_c_sensorhub2_reg_t sensorhub2_reg; + lsm6ds3tr_c_sensorhub3_reg_t sensorhub3_reg; + lsm6ds3tr_c_sensorhub4_reg_t sensorhub4_reg; + lsm6ds3tr_c_sensorhub5_reg_t sensorhub5_reg; + lsm6ds3tr_c_sensorhub6_reg_t sensorhub6_reg; + lsm6ds3tr_c_sensorhub7_reg_t sensorhub7_reg; + lsm6ds3tr_c_sensorhub8_reg_t sensorhub8_reg; + lsm6ds3tr_c_sensorhub9_reg_t sensorhub9_reg; + lsm6ds3tr_c_sensorhub10_reg_t sensorhub10_reg; + lsm6ds3tr_c_sensorhub11_reg_t sensorhub11_reg; + lsm6ds3tr_c_sensorhub12_reg_t sensorhub12_reg; + lsm6ds3tr_c_fifo_status1_t fifo_status1; + lsm6ds3tr_c_fifo_status2_t fifo_status2; + lsm6ds3tr_c_fifo_status3_t fifo_status3; + lsm6ds3tr_c_fifo_status4_t fifo_status4; + lsm6ds3tr_c_sensorhub13_reg_t sensorhub13_reg; + lsm6ds3tr_c_sensorhub14_reg_t sensorhub14_reg; + lsm6ds3tr_c_sensorhub15_reg_t sensorhub15_reg; + lsm6ds3tr_c_sensorhub16_reg_t sensorhub16_reg; + lsm6ds3tr_c_sensorhub17_reg_t sensorhub17_reg; + lsm6ds3tr_c_sensorhub18_reg_t sensorhub18_reg; + lsm6ds3tr_c_func_src1_t func_src1; + lsm6ds3tr_c_func_src2_t func_src2; + lsm6ds3tr_c_wrist_tilt_ia_t wrist_tilt_ia; + lsm6ds3tr_c_tap_cfg_t tap_cfg; + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + lsm6ds3tr_c_int_dur2_t int_dur2; + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + lsm6ds3tr_c_free_fall_t free_fall; + lsm6ds3tr_c_md1_cfg_t md1_cfg; + lsm6ds3tr_c_md2_cfg_t md2_cfg; + lsm6ds3tr_c_master_cmd_code_t master_cmd_code; + lsm6ds3tr_c_sens_sync_spi_error_code_t sens_sync_spi_error_code; + lsm6ds3tr_c_slv0_add_t slv0_add; + lsm6ds3tr_c_slv0_subadd_t slv0_subadd; + lsm6ds3tr_c_slave0_config_t slave0_config; + lsm6ds3tr_c_slv1_add_t slv1_add; + lsm6ds3tr_c_slv1_subadd_t slv1_subadd; + lsm6ds3tr_c_slave1_config_t slave1_config; + lsm6ds3tr_c_slv2_add_t slv2_add; + lsm6ds3tr_c_slv2_subadd_t slv2_subadd; + lsm6ds3tr_c_slave2_config_t slave2_config; + lsm6ds3tr_c_slv3_add_t slv3_add; + lsm6ds3tr_c_slv3_subadd_t slv3_subadd; + lsm6ds3tr_c_slave3_config_t slave3_config; + lsm6ds3tr_c_datawrite_src_mode_sub_slv0_t datawrite_src_mode_sub_slv0; + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + lsm6ds3tr_c_a_wrist_tilt_mask_t a_wrist_tilt_mask; + bitwise_t bitwise; + uint8_t byte; +} lsm6ds3tr_c_reg_t; + +/** + * @} + * + */ + +int32_t lsm6ds3tr_c_read_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, uint16_t len); +int32_t lsm6ds3tr_c_write_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, uint16_t len); + +float_t lsm6ds3tr_c_from_fs2g_to_mg(int16_t lsb); +float_t lsm6ds3tr_c_from_fs4g_to_mg(int16_t lsb); +float_t lsm6ds3tr_c_from_fs8g_to_mg(int16_t lsb); +float_t lsm6ds3tr_c_from_fs16g_to_mg(int16_t lsb); + +float_t lsm6ds3tr_c_from_fs125dps_to_mdps(int16_t lsb); +float_t lsm6ds3tr_c_from_fs250dps_to_mdps(int16_t lsb); +float_t lsm6ds3tr_c_from_fs500dps_to_mdps(int16_t lsb); +float_t lsm6ds3tr_c_from_fs1000dps_to_mdps(int16_t lsb); +float_t lsm6ds3tr_c_from_fs2000dps_to_mdps(int16_t lsb); + +float_t lsm6ds3tr_c_from_lsb_to_celsius(int16_t lsb); + +typedef enum { + LSM6DS3TR_C_2g = 0, + LSM6DS3TR_C_16g = 1, + LSM6DS3TR_C_4g = 2, + LSM6DS3TR_C_8g = 3, + LSM6DS3TR_C_XL_FS_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_fs_xl_t; +int32_t lsm6ds3tr_c_xl_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_xl_t val); +int32_t lsm6ds3tr_c_xl_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_xl_t* val); + +typedef enum { + LSM6DS3TR_C_XL_ODR_OFF = 0, + LSM6DS3TR_C_XL_ODR_12Hz5 = 1, + LSM6DS3TR_C_XL_ODR_26Hz = 2, + LSM6DS3TR_C_XL_ODR_52Hz = 3, + LSM6DS3TR_C_XL_ODR_104Hz = 4, + LSM6DS3TR_C_XL_ODR_208Hz = 5, + LSM6DS3TR_C_XL_ODR_416Hz = 6, + LSM6DS3TR_C_XL_ODR_833Hz = 7, + LSM6DS3TR_C_XL_ODR_1k66Hz = 8, + LSM6DS3TR_C_XL_ODR_3k33Hz = 9, + LSM6DS3TR_C_XL_ODR_6k66Hz = 10, + LSM6DS3TR_C_XL_ODR_1Hz6 = 11, + LSM6DS3TR_C_XL_ODR_ND = 12, /* ERROR CODE */ +} lsm6ds3tr_c_odr_xl_t; +int32_t lsm6ds3tr_c_xl_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_xl_t val); +int32_t lsm6ds3tr_c_xl_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_xl_t* val); + +typedef enum { + LSM6DS3TR_C_250dps = 0, + LSM6DS3TR_C_125dps = 1, + LSM6DS3TR_C_500dps = 2, + LSM6DS3TR_C_1000dps = 4, + LSM6DS3TR_C_2000dps = 6, + LSM6DS3TR_C_GY_FS_ND = 7, /* ERROR CODE */ +} lsm6ds3tr_c_fs_g_t; +int32_t lsm6ds3tr_c_gy_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_g_t val); +int32_t lsm6ds3tr_c_gy_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_g_t* val); + +typedef enum { + LSM6DS3TR_C_GY_ODR_OFF = 0, + LSM6DS3TR_C_GY_ODR_12Hz5 = 1, + LSM6DS3TR_C_GY_ODR_26Hz = 2, + LSM6DS3TR_C_GY_ODR_52Hz = 3, + LSM6DS3TR_C_GY_ODR_104Hz = 4, + LSM6DS3TR_C_GY_ODR_208Hz = 5, + LSM6DS3TR_C_GY_ODR_416Hz = 6, + LSM6DS3TR_C_GY_ODR_833Hz = 7, + LSM6DS3TR_C_GY_ODR_1k66Hz = 8, + LSM6DS3TR_C_GY_ODR_3k33Hz = 9, + LSM6DS3TR_C_GY_ODR_6k66Hz = 10, + LSM6DS3TR_C_GY_ODR_ND = 11, /* ERROR CODE */ +} lsm6ds3tr_c_odr_g_t; +int32_t lsm6ds3tr_c_gy_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_g_t val); +int32_t lsm6ds3tr_c_gy_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_g_t* val); + +int32_t lsm6ds3tr_c_block_data_update_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_block_data_update_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_LSb_1mg = 0, + LSM6DS3TR_C_LSb_16mg = 1, + LSM6DS3TR_C_WEIGHT_ND = 2, +} lsm6ds3tr_c_usr_off_w_t; +int32_t lsm6ds3tr_c_xl_offset_weight_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_usr_off_w_t val); +int32_t lsm6ds3tr_c_xl_offset_weight_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_usr_off_w_t* val); + +typedef enum { + LSM6DS3TR_C_XL_HIGH_PERFORMANCE = 0, + LSM6DS3TR_C_XL_NORMAL = 1, + LSM6DS3TR_C_XL_PW_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_xl_hm_mode_t; +int32_t lsm6ds3tr_c_xl_power_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_xl_hm_mode_t val); +int32_t lsm6ds3tr_c_xl_power_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_xl_hm_mode_t* val); + +typedef enum { + LSM6DS3TR_C_STAT_RND_DISABLE = 0, + LSM6DS3TR_C_STAT_RND_ENABLE = 1, + LSM6DS3TR_C_STAT_RND_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_rounding_status_t; +int32_t lsm6ds3tr_c_rounding_on_status_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_status_t val); +int32_t lsm6ds3tr_c_rounding_on_status_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_status_t* val); + +typedef enum { + LSM6DS3TR_C_GY_HIGH_PERFORMANCE = 0, + LSM6DS3TR_C_GY_NORMAL = 1, + LSM6DS3TR_C_GY_PW_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_g_hm_mode_t; +int32_t lsm6ds3tr_c_gy_power_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_g_hm_mode_t val); +int32_t lsm6ds3tr_c_gy_power_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_g_hm_mode_t* val); + +typedef struct { + lsm6ds3tr_c_wake_up_src_t wake_up_src; + lsm6ds3tr_c_tap_src_t tap_src; + lsm6ds3tr_c_d6d_src_t d6d_src; + lsm6ds3tr_c_status_reg_t status_reg; + lsm6ds3tr_c_func_src1_t func_src1; + lsm6ds3tr_c_func_src2_t func_src2; + lsm6ds3tr_c_wrist_tilt_ia_t wrist_tilt_ia; + lsm6ds3tr_c_a_wrist_tilt_mask_t a_wrist_tilt_mask; +} lsm6ds3tr_c_all_sources_t; +int32_t lsm6ds3tr_c_all_sources_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_all_sources_t* val); + +int32_t lsm6ds3tr_c_status_reg_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_status_reg_t* val); + +int32_t lsm6ds3tr_c_xl_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_gy_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_temp_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_xl_usr_offset_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_xl_usr_offset_get(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_timestamp_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_timestamp_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_LSB_6ms4 = 0, + LSM6DS3TR_C_LSB_25us = 1, + LSM6DS3TR_C_TS_RES_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_timer_hr_t; +int32_t lsm6ds3tr_c_timestamp_res_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_timer_hr_t val); +int32_t lsm6ds3tr_c_timestamp_res_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_timer_hr_t* val); + +typedef enum { + LSM6DS3TR_C_ROUND_DISABLE = 0, + LSM6DS3TR_C_ROUND_XL = 1, + LSM6DS3TR_C_ROUND_GY = 2, + LSM6DS3TR_C_ROUND_GY_XL = 3, + LSM6DS3TR_C_ROUND_SH1_TO_SH6 = 4, + LSM6DS3TR_C_ROUND_XL_SH1_TO_SH6 = 5, + LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH12 = 6, + LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH6 = 7, + LSM6DS3TR_C_ROUND_OUT_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_rounding_t; +int32_t lsm6ds3tr_c_rounding_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_t val); +int32_t lsm6ds3tr_c_rounding_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_t* val); + +int32_t lsm6ds3tr_c_temperature_raw_get(stmdev_ctx_t* ctx, int16_t* val); +int32_t lsm6ds3tr_c_angular_rate_raw_get(stmdev_ctx_t* ctx, int16_t* val); +int32_t lsm6ds3tr_c_acceleration_raw_get(stmdev_ctx_t* ctx, int16_t* val); + +int32_t lsm6ds3tr_c_mag_calibrated_raw_get(stmdev_ctx_t* ctx, int16_t* val); + +int32_t lsm6ds3tr_c_fifo_raw_data_get(stmdev_ctx_t* ctx, uint8_t* buffer, uint8_t len); + +typedef enum { + LSM6DS3TR_C_USER_BANK = 0, + LSM6DS3TR_C_BANK_A = 4, + LSM6DS3TR_C_BANK_B = 5, + LSM6DS3TR_C_BANK_ND = 6, /* ERROR CODE */ +} lsm6ds3tr_c_func_cfg_en_t; +int32_t lsm6ds3tr_c_mem_bank_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_func_cfg_en_t val); +int32_t lsm6ds3tr_c_mem_bank_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_func_cfg_en_t* val); + +typedef enum { + LSM6DS3TR_C_DRDY_LATCHED = 0, + LSM6DS3TR_C_DRDY_PULSED = 1, + LSM6DS3TR_C_DRDY_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_drdy_pulsed_g_t; +int32_t lsm6ds3tr_c_data_ready_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_drdy_pulsed_g_t val); +int32_t lsm6ds3tr_c_data_ready_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_drdy_pulsed_g_t* val); + +int32_t lsm6ds3tr_c_device_id_get(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_reset_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_reset_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_LSB_AT_LOW_ADD = 0, + LSM6DS3TR_C_MSB_AT_LOW_ADD = 1, + LSM6DS3TR_C_DATA_FMT_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_ble_t; +int32_t lsm6ds3tr_c_data_format_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_ble_t val); +int32_t lsm6ds3tr_c_data_format_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_ble_t* val); + +int32_t lsm6ds3tr_c_auto_increment_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_auto_increment_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_boot_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_boot_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_XL_ST_DISABLE = 0, + LSM6DS3TR_C_XL_ST_POSITIVE = 1, + LSM6DS3TR_C_XL_ST_NEGATIVE = 2, + LSM6DS3TR_C_XL_ST_ND = 3, /* ERROR CODE */ +} lsm6ds3tr_c_st_xl_t; +int32_t lsm6ds3tr_c_xl_self_test_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_xl_t val); +int32_t lsm6ds3tr_c_xl_self_test_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_xl_t* val); + +typedef enum { + LSM6DS3TR_C_GY_ST_DISABLE = 0, + LSM6DS3TR_C_GY_ST_POSITIVE = 1, + LSM6DS3TR_C_GY_ST_NEGATIVE = 3, + LSM6DS3TR_C_GY_ST_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_st_g_t; +int32_t lsm6ds3tr_c_gy_self_test_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_g_t val); +int32_t lsm6ds3tr_c_gy_self_test_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_g_t* val); + +int32_t lsm6ds3tr_c_filter_settling_mask_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_filter_settling_mask_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_USE_SLOPE = 0, + LSM6DS3TR_C_USE_HPF = 1, + LSM6DS3TR_C_HP_PATH_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_slope_fds_t; +int32_t lsm6ds3tr_c_xl_hp_path_internal_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slope_fds_t val); +int32_t lsm6ds3tr_c_xl_hp_path_internal_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slope_fds_t* val); + +typedef enum { + LSM6DS3TR_C_XL_ANA_BW_1k5Hz = 0, + LSM6DS3TR_C_XL_ANA_BW_400Hz = 1, + LSM6DS3TR_C_XL_ANA_BW_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_bw0_xl_t; +int32_t lsm6ds3tr_c_xl_filter_analog_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_bw0_xl_t val); +int32_t lsm6ds3tr_c_xl_filter_analog_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_bw0_xl_t* val); + +typedef enum { + LSM6DS3TR_C_XL_LP1_ODR_DIV_2 = 0, + LSM6DS3TR_C_XL_LP1_ODR_DIV_4 = 1, + LSM6DS3TR_C_XL_LP1_NA = 2, /* ERROR CODE */ +} lsm6ds3tr_c_lpf1_bw_sel_t; +int32_t lsm6ds3tr_c_xl_lp1_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_bw_sel_t val); +int32_t lsm6ds3tr_c_xl_lp1_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_bw_sel_t* val); + +typedef enum { + LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_50 = 0x00, + LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_100 = 0x01, + LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_9 = 0x02, + LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_400 = 0x03, + LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_50 = 0x10, + LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_100 = 0x11, + LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_9 = 0x12, + LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_400 = 0x13, + LSM6DS3TR_C_XL_LP_NA = 0x20, /* ERROR CODE */ +} lsm6ds3tr_c_input_composite_t; +int32_t lsm6ds3tr_c_xl_lp2_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_input_composite_t val); +int32_t lsm6ds3tr_c_xl_lp2_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_input_composite_t* val); + +int32_t lsm6ds3tr_c_xl_reference_mode_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_xl_reference_mode_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_XL_HP_ODR_DIV_4 = 0x00, /* Slope filter */ + LSM6DS3TR_C_XL_HP_ODR_DIV_100 = 0x01, + LSM6DS3TR_C_XL_HP_ODR_DIV_9 = 0x02, + LSM6DS3TR_C_XL_HP_ODR_DIV_400 = 0x03, + LSM6DS3TR_C_XL_HP_NA = 0x10, /* ERROR CODE */ +} lsm6ds3tr_c_hpcf_xl_t; +int32_t lsm6ds3tr_c_xl_hp_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_hpcf_xl_t val); +int32_t lsm6ds3tr_c_xl_hp_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_hpcf_xl_t* val); + +typedef enum { + LSM6DS3TR_C_LP2_ONLY = 0x00, + + LSM6DS3TR_C_HP_16mHz_LP2 = 0x80, + LSM6DS3TR_C_HP_65mHz_LP2 = 0x90, + LSM6DS3TR_C_HP_260mHz_LP2 = 0xA0, + LSM6DS3TR_C_HP_1Hz04_LP2 = 0xB0, + + LSM6DS3TR_C_HP_DISABLE_LP1_LIGHT = 0x0A, + LSM6DS3TR_C_HP_DISABLE_LP1_NORMAL = 0x09, + LSM6DS3TR_C_HP_DISABLE_LP_STRONG = 0x08, + LSM6DS3TR_C_HP_DISABLE_LP1_AGGRESSIVE = 0x0B, + + LSM6DS3TR_C_HP_16mHz_LP1_LIGHT = 0x8A, + LSM6DS3TR_C_HP_65mHz_LP1_NORMAL = 0x99, + LSM6DS3TR_C_HP_260mHz_LP1_STRONG = 0xA8, + LSM6DS3TR_C_HP_1Hz04_LP1_AGGRESSIVE = 0xBB, + + LSM6DS3TR_C_HP_GY_BAND_NA = 0xFF, /* ERROR CODE */ +} lsm6ds3tr_c_lpf1_sel_g_t; +int32_t lsm6ds3tr_c_gy_band_pass_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_sel_g_t val); +int32_t lsm6ds3tr_c_gy_band_pass_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_sel_g_t* val); + +typedef enum { + LSM6DS3TR_C_SPI_4_WIRE = 0, + LSM6DS3TR_C_SPI_3_WIRE = 1, + LSM6DS3TR_C_SPI_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_sim_t; +int32_t lsm6ds3tr_c_spi_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_sim_t val); +int32_t lsm6ds3tr_c_spi_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_sim_t* val); + +typedef enum { + LSM6DS3TR_C_I2C_ENABLE = 0, + LSM6DS3TR_C_I2C_DISABLE = 1, + LSM6DS3TR_C_I2C_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_i2c_disable_t; +int32_t lsm6ds3tr_c_i2c_interface_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_i2c_disable_t val); +int32_t lsm6ds3tr_c_i2c_interface_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_i2c_disable_t* val); + +typedef struct { + uint8_t int1_drdy_xl : 1; + uint8_t int1_drdy_g : 1; + uint8_t int1_boot : 1; + uint8_t int1_fth : 1; + uint8_t int1_fifo_ovr : 1; + uint8_t int1_full_flag : 1; + uint8_t int1_sign_mot : 1; + uint8_t int1_step_detector : 1; + uint8_t int1_timer : 1; + uint8_t int1_tilt : 1; + uint8_t int1_6d : 1; + uint8_t int1_double_tap : 1; + uint8_t int1_ff : 1; + uint8_t int1_wu : 1; + uint8_t int1_single_tap : 1; + uint8_t int1_inact_state : 1; + uint8_t den_drdy_int1 : 1; + uint8_t drdy_on_int1 : 1; +} lsm6ds3tr_c_int1_route_t; +int32_t lsm6ds3tr_c_pin_int1_route_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_int1_route_t val); +int32_t lsm6ds3tr_c_pin_int1_route_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_int1_route_t* val); + +typedef struct { + uint8_t int2_drdy_xl : 1; + uint8_t int2_drdy_g : 1; + uint8_t int2_drdy_temp : 1; + uint8_t int2_fth : 1; + uint8_t int2_fifo_ovr : 1; + uint8_t int2_full_flag : 1; + uint8_t int2_step_count_ov : 1; + uint8_t int2_step_delta : 1; + uint8_t int2_iron : 1; + uint8_t int2_tilt : 1; + uint8_t int2_6d : 1; + uint8_t int2_double_tap : 1; + uint8_t int2_ff : 1; + uint8_t int2_wu : 1; + uint8_t int2_single_tap : 1; + uint8_t int2_inact_state : 1; + uint8_t int2_wrist_tilt : 1; +} lsm6ds3tr_c_int2_route_t; +int32_t lsm6ds3tr_c_pin_int2_route_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_int2_route_t val); +int32_t lsm6ds3tr_c_pin_int2_route_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_int2_route_t* val); + +typedef enum { + LSM6DS3TR_C_PUSH_PULL = 0, + LSM6DS3TR_C_OPEN_DRAIN = 1, + LSM6DS3TR_C_PIN_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_pp_od_t; +int32_t lsm6ds3tr_c_pin_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pp_od_t val); +int32_t lsm6ds3tr_c_pin_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pp_od_t* val); + +typedef enum { + LSM6DS3TR_C_ACTIVE_HIGH = 0, + LSM6DS3TR_C_ACTIVE_LOW = 1, + LSM6DS3TR_C_POLARITY_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_h_lactive_t; +int32_t lsm6ds3tr_c_pin_polarity_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_h_lactive_t val); +int32_t lsm6ds3tr_c_pin_polarity_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_h_lactive_t* val); + +int32_t lsm6ds3tr_c_all_on_int1_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_all_on_int1_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_INT_PULSED = 0, + LSM6DS3TR_C_INT_LATCHED = 1, + LSM6DS3TR_C_INT_MODE = 2, /* ERROR CODE */ +} lsm6ds3tr_c_lir_t; +int32_t lsm6ds3tr_c_int_notification_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lir_t val); +int32_t lsm6ds3tr_c_int_notification_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lir_t* val); + +int32_t lsm6ds3tr_c_wkup_threshold_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_wkup_threshold_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_wkup_dur_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_wkup_dur_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_gy_sleep_mode_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_gy_sleep_mode_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_PROPERTY_DISABLE = 0, + LSM6DS3TR_C_XL_12Hz5_GY_NOT_AFFECTED = 1, + LSM6DS3TR_C_XL_12Hz5_GY_SLEEP = 2, + LSM6DS3TR_C_XL_12Hz5_GY_PD = 3, + LSM6DS3TR_C_ACT_MODE_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_inact_en_t; +int32_t lsm6ds3tr_c_act_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_inact_en_t val); +int32_t lsm6ds3tr_c_act_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_inact_en_t* val); + +int32_t lsm6ds3tr_c_act_sleep_dur_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_act_sleep_dur_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_src_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_tap_src_t* val); + +int32_t lsm6ds3tr_c_tap_detection_on_z_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_detection_on_z_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_detection_on_y_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_detection_on_y_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_detection_on_x_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_detection_on_x_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_threshold_x_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_threshold_x_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_shock_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_shock_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_quiet_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_quiet_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_dur_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_dur_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_ONLY_SINGLE = 0, + LSM6DS3TR_C_BOTH_SINGLE_DOUBLE = 1, + LSM6DS3TR_C_TAP_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_single_double_tap_t; +int32_t lsm6ds3tr_c_tap_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_single_double_tap_t val); +int32_t lsm6ds3tr_c_tap_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_single_double_tap_t* val); + +typedef enum { + LSM6DS3TR_C_ODR_DIV_2_FEED = 0, + LSM6DS3TR_C_LPF2_FEED = 1, + LSM6DS3TR_C_6D_FEED_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_low_pass_on_6d_t; +int32_t lsm6ds3tr_c_6d_feed_data_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_low_pass_on_6d_t val); +int32_t lsm6ds3tr_c_6d_feed_data_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_low_pass_on_6d_t* val); + +typedef enum { + LSM6DS3TR_C_DEG_80 = 0, + LSM6DS3TR_C_DEG_70 = 1, + LSM6DS3TR_C_DEG_60 = 2, + LSM6DS3TR_C_DEG_50 = 3, + LSM6DS3TR_C_6D_TH_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_sixd_ths_t; +int32_t lsm6ds3tr_c_6d_threshold_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_sixd_ths_t val); +int32_t lsm6ds3tr_c_6d_threshold_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_sixd_ths_t* val); + +int32_t lsm6ds3tr_c_4d_mode_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_4d_mode_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_ff_dur_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_ff_dur_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_FF_TSH_156mg = 0, + LSM6DS3TR_C_FF_TSH_219mg = 1, + LSM6DS3TR_C_FF_TSH_250mg = 2, + LSM6DS3TR_C_FF_TSH_312mg = 3, + LSM6DS3TR_C_FF_TSH_344mg = 4, + LSM6DS3TR_C_FF_TSH_406mg = 5, + LSM6DS3TR_C_FF_TSH_469mg = 6, + LSM6DS3TR_C_FF_TSH_500mg = 7, + LSM6DS3TR_C_FF_TSH_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_ff_ths_t; +int32_t lsm6ds3tr_c_ff_threshold_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_ff_ths_t val); +int32_t lsm6ds3tr_c_ff_threshold_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_ff_ths_t* val); + +int32_t lsm6ds3tr_c_fifo_watermark_set(stmdev_ctx_t* ctx, uint16_t val); +int32_t lsm6ds3tr_c_fifo_watermark_get(stmdev_ctx_t* ctx, uint16_t* val); + +int32_t lsm6ds3tr_c_fifo_data_level_get(stmdev_ctx_t* ctx, uint16_t* val); + +int32_t lsm6ds3tr_c_fifo_wtm_flag_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_fifo_pattern_get(stmdev_ctx_t* ctx, uint16_t* val); + +int32_t lsm6ds3tr_c_fifo_temp_batch_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_fifo_temp_batch_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_TRG_XL_GY_DRDY = 0, + LSM6DS3TR_C_TRG_STEP_DETECT = 1, + LSM6DS3TR_C_TRG_SH_DRDY = 2, + LSM6DS3TR_C_TRG_SH_ND = 3, /* ERROR CODE */ +} lsm6ds3tr_c_trigger_fifo_t; +int32_t lsm6ds3tr_c_fifo_write_trigger_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_trigger_fifo_t val); +int32_t lsm6ds3tr_c_fifo_write_trigger_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_trigger_fifo_t* val); + +int32_t lsm6ds3tr_c_fifo_pedo_and_timestamp_batch_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_fifo_pedo_and_timestamp_batch_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_XL_DISABLE = 0, + LSM6DS3TR_C_FIFO_XL_NO_DEC = 1, + LSM6DS3TR_C_FIFO_XL_DEC_2 = 2, + LSM6DS3TR_C_FIFO_XL_DEC_3 = 3, + LSM6DS3TR_C_FIFO_XL_DEC_4 = 4, + LSM6DS3TR_C_FIFO_XL_DEC_8 = 5, + LSM6DS3TR_C_FIFO_XL_DEC_16 = 6, + LSM6DS3TR_C_FIFO_XL_DEC_32 = 7, + LSM6DS3TR_C_FIFO_XL_DEC_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_dec_fifo_xl_t; +int32_t lsm6ds3tr_c_fifo_xl_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_xl_t val); +int32_t lsm6ds3tr_c_fifo_xl_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_xl_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_GY_DISABLE = 0, + LSM6DS3TR_C_FIFO_GY_NO_DEC = 1, + LSM6DS3TR_C_FIFO_GY_DEC_2 = 2, + LSM6DS3TR_C_FIFO_GY_DEC_3 = 3, + LSM6DS3TR_C_FIFO_GY_DEC_4 = 4, + LSM6DS3TR_C_FIFO_GY_DEC_8 = 5, + LSM6DS3TR_C_FIFO_GY_DEC_16 = 6, + LSM6DS3TR_C_FIFO_GY_DEC_32 = 7, + LSM6DS3TR_C_FIFO_GY_DEC_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_dec_fifo_gyro_t; +int32_t lsm6ds3tr_c_fifo_gy_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_gyro_t val); +int32_t lsm6ds3tr_c_fifo_gy_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_gyro_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_DS3_DISABLE = 0, + LSM6DS3TR_C_FIFO_DS3_NO_DEC = 1, + LSM6DS3TR_C_FIFO_DS3_DEC_2 = 2, + LSM6DS3TR_C_FIFO_DS3_DEC_3 = 3, + LSM6DS3TR_C_FIFO_DS3_DEC_4 = 4, + LSM6DS3TR_C_FIFO_DS3_DEC_8 = 5, + LSM6DS3TR_C_FIFO_DS3_DEC_16 = 6, + LSM6DS3TR_C_FIFO_DS3_DEC_32 = 7, + LSM6DS3TR_C_FIFO_DS3_DEC_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_dec_ds3_fifo_t; +int32_t lsm6ds3tr_c_fifo_dataset_3_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds3_fifo_t val); +int32_t lsm6ds3tr_c_fifo_dataset_3_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds3_fifo_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_DS4_DISABLE = 0, + LSM6DS3TR_C_FIFO_DS4_NO_DEC = 1, + LSM6DS3TR_C_FIFO_DS4_DEC_2 = 2, + LSM6DS3TR_C_FIFO_DS4_DEC_3 = 3, + LSM6DS3TR_C_FIFO_DS4_DEC_4 = 4, + LSM6DS3TR_C_FIFO_DS4_DEC_8 = 5, + LSM6DS3TR_C_FIFO_DS4_DEC_16 = 6, + LSM6DS3TR_C_FIFO_DS4_DEC_32 = 7, + LSM6DS3TR_C_FIFO_DS4_DEC_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_dec_ds4_fifo_t; +int32_t lsm6ds3tr_c_fifo_dataset_4_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds4_fifo_t val); +int32_t lsm6ds3tr_c_fifo_dataset_4_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds4_fifo_t* val); + +int32_t lsm6ds3tr_c_fifo_xl_gy_8bit_format_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_fifo_xl_gy_8bit_format_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_fifo_stop_on_wtm_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_fifo_stop_on_wtm_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_BYPASS_MODE = 0, + LSM6DS3TR_C_FIFO_MODE = 1, + LSM6DS3TR_C_STREAM_TO_FIFO_MODE = 3, + LSM6DS3TR_C_BYPASS_TO_STREAM_MODE = 4, + LSM6DS3TR_C_STREAM_MODE = 6, + LSM6DS3TR_C_FIFO_MODE_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_fifo_mode_t; +int32_t lsm6ds3tr_c_fifo_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fifo_mode_t val); +int32_t lsm6ds3tr_c_fifo_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fifo_mode_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_DISABLE = 0, + LSM6DS3TR_C_FIFO_12Hz5 = 1, + LSM6DS3TR_C_FIFO_26Hz = 2, + LSM6DS3TR_C_FIFO_52Hz = 3, + LSM6DS3TR_C_FIFO_104Hz = 4, + LSM6DS3TR_C_FIFO_208Hz = 5, + LSM6DS3TR_C_FIFO_416Hz = 6, + LSM6DS3TR_C_FIFO_833Hz = 7, + LSM6DS3TR_C_FIFO_1k66Hz = 8, + LSM6DS3TR_C_FIFO_3k33Hz = 9, + LSM6DS3TR_C_FIFO_6k66Hz = 10, + LSM6DS3TR_C_FIFO_RATE_ND = 11, /* ERROR CODE */ +} lsm6ds3tr_c_odr_fifo_t; +int32_t lsm6ds3tr_c_fifo_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_fifo_t val); +int32_t lsm6ds3tr_c_fifo_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_fifo_t* val); + +typedef enum { + LSM6DS3TR_C_DEN_ACT_LOW = 0, + LSM6DS3TR_C_DEN_ACT_HIGH = 1, + LSM6DS3TR_C_DEN_POL_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_den_lh_t; +int32_t lsm6ds3tr_c_den_polarity_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_lh_t val); +int32_t lsm6ds3tr_c_den_polarity_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_lh_t* val); + +typedef enum { + LSM6DS3TR_C_DEN_DISABLE = 0, + LSM6DS3TR_C_LEVEL_FIFO = 6, + LSM6DS3TR_C_LEVEL_LETCHED = 3, + LSM6DS3TR_C_LEVEL_TRIGGER = 2, + LSM6DS3TR_C_EDGE_TRIGGER = 4, + LSM6DS3TR_C_DEN_MODE_ND = 5, /* ERROR CODE */ +} lsm6ds3tr_c_den_mode_t; +int32_t lsm6ds3tr_c_den_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_mode_t val); +int32_t lsm6ds3tr_c_den_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_mode_t* val); + +typedef enum { + LSM6DS3TR_C_STAMP_IN_GY_DATA = 0, + LSM6DS3TR_C_STAMP_IN_XL_DATA = 1, + LSM6DS3TR_C_STAMP_IN_GY_XL_DATA = 2, + LSM6DS3TR_C_DEN_STAMP_ND = 3, /* ERROR CODE */ +} lsm6ds3tr_c_den_xl_en_t; +int32_t lsm6ds3tr_c_den_enable_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_xl_en_t val); +int32_t lsm6ds3tr_c_den_enable_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_xl_en_t* val); + +int32_t lsm6ds3tr_c_den_mark_axis_z_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_den_mark_axis_z_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_den_mark_axis_y_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_den_mark_axis_y_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_den_mark_axis_x_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_den_mark_axis_x_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_step_reset_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_step_reset_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_sens_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_sens_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_threshold_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_threshold_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_PEDO_AT_2g = 0, + LSM6DS3TR_C_PEDO_AT_4g = 1, + LSM6DS3TR_C_PEDO_FS_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_pedo_fs_t; +int32_t lsm6ds3tr_c_pedo_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pedo_fs_t val); +int32_t lsm6ds3tr_c_pedo_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pedo_fs_t* val); + +int32_t lsm6ds3tr_c_pedo_debounce_steps_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_debounce_steps_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_timeout_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_timeout_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_steps_period_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_pedo_steps_period_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_motion_sens_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_motion_sens_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_motion_threshold_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_motion_threshold_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_tilt_sens_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tilt_sens_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_wrist_tilt_sens_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_wrist_tilt_sens_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tilt_latency_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_tilt_latency_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_tilt_threshold_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_tilt_threshold_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_tilt_src_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_a_wrist_tilt_mask_t* val); +int32_t lsm6ds3tr_c_tilt_src_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_a_wrist_tilt_mask_t* val); + +int32_t lsm6ds3tr_c_mag_soft_iron_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_mag_soft_iron_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_mag_hard_iron_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_mag_hard_iron_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_mag_soft_iron_mat_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_mag_soft_iron_mat_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_mag_offset_set(stmdev_ctx_t* ctx, int16_t* val); +int32_t lsm6ds3tr_c_mag_offset_get(stmdev_ctx_t* ctx, int16_t* val); + +int32_t lsm6ds3tr_c_func_en_set(stmdev_ctx_t* ctx, uint8_t val); + +int32_t lsm6ds3tr_c_sh_sync_sens_frame_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_sync_sens_frame_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_RES_RATIO_2_11 = 0, + LSM6DS3TR_C_RES_RATIO_2_12 = 1, + LSM6DS3TR_C_RES_RATIO_2_13 = 2, + LSM6DS3TR_C_RES_RATIO_2_14 = 3, + LSM6DS3TR_C_RES_RATIO_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_rr_t; +int32_t lsm6ds3tr_c_sh_sync_sens_ratio_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rr_t val); +int32_t lsm6ds3tr_c_sh_sync_sens_ratio_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rr_t* val); + +int32_t lsm6ds3tr_c_sh_master_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_master_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_sh_pass_through_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_pass_through_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_EXT_PULL_UP = 0, + LSM6DS3TR_C_INTERNAL_PULL_UP = 1, + LSM6DS3TR_C_SH_PIN_MODE = 2, /* ERROR CODE */ +} lsm6ds3tr_c_pull_up_en_t; +int32_t lsm6ds3tr_c_sh_pin_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pull_up_en_t val); +int32_t lsm6ds3tr_c_sh_pin_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pull_up_en_t* val); + +typedef enum { + LSM6DS3TR_C_XL_GY_DRDY = 0, + LSM6DS3TR_C_EXT_ON_INT2_PIN = 1, + LSM6DS3TR_C_SH_SYNCRO_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_start_config_t; +int32_t lsm6ds3tr_c_sh_syncro_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_start_config_t val); +int32_t lsm6ds3tr_c_sh_syncro_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_start_config_t* val); + +int32_t lsm6ds3tr_c_sh_drdy_on_int1_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_drdy_on_int1_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef struct { + lsm6ds3tr_c_sensorhub1_reg_t sh_byte_1; + lsm6ds3tr_c_sensorhub2_reg_t sh_byte_2; + lsm6ds3tr_c_sensorhub3_reg_t sh_byte_3; + lsm6ds3tr_c_sensorhub4_reg_t sh_byte_4; + lsm6ds3tr_c_sensorhub5_reg_t sh_byte_5; + lsm6ds3tr_c_sensorhub6_reg_t sh_byte_6; + lsm6ds3tr_c_sensorhub7_reg_t sh_byte_7; + lsm6ds3tr_c_sensorhub8_reg_t sh_byte_8; + lsm6ds3tr_c_sensorhub9_reg_t sh_byte_9; + lsm6ds3tr_c_sensorhub10_reg_t sh_byte_10; + lsm6ds3tr_c_sensorhub11_reg_t sh_byte_11; + lsm6ds3tr_c_sensorhub12_reg_t sh_byte_12; + lsm6ds3tr_c_sensorhub13_reg_t sh_byte_13; + lsm6ds3tr_c_sensorhub14_reg_t sh_byte_14; + lsm6ds3tr_c_sensorhub15_reg_t sh_byte_15; + lsm6ds3tr_c_sensorhub16_reg_t sh_byte_16; + lsm6ds3tr_c_sensorhub17_reg_t sh_byte_17; + lsm6ds3tr_c_sensorhub18_reg_t sh_byte_18; +} lsm6ds3tr_c_emb_sh_read_t; +int32_t lsm6ds3tr_c_sh_read_data_raw_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_emb_sh_read_t* val); + +int32_t lsm6ds3tr_c_sh_cmd_sens_sync_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_cmd_sens_sync_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_sh_spi_sync_error_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_spi_sync_error_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_SLV_0 = 0, + LSM6DS3TR_C_SLV_0_1 = 1, + LSM6DS3TR_C_SLV_0_1_2 = 2, + LSM6DS3TR_C_SLV_0_1_2_3 = 3, + LSM6DS3TR_C_SLV_EN_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_aux_sens_on_t; +int32_t lsm6ds3tr_c_sh_num_of_dev_connected_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_aux_sens_on_t val); +int32_t lsm6ds3tr_c_sh_num_of_dev_connected_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_aux_sens_on_t* val); + +typedef struct { + uint8_t slv0_add; + uint8_t slv0_subadd; + uint8_t slv0_data; +} lsm6ds3tr_c_sh_cfg_write_t; +int32_t lsm6ds3tr_c_sh_cfg_write(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_write_t* val); + +typedef struct { + uint8_t slv_add; + uint8_t slv_subadd; + uint8_t slv_len; +} lsm6ds3tr_c_sh_cfg_read_t; +int32_t lsm6ds3tr_c_sh_slv0_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val); +int32_t lsm6ds3tr_c_sh_slv1_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val); +int32_t lsm6ds3tr_c_sh_slv2_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val); +int32_t lsm6ds3tr_c_sh_slv3_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val); + +typedef enum { + LSM6DS3TR_C_SL0_NO_DEC = 0, + LSM6DS3TR_C_SL0_DEC_2 = 1, + LSM6DS3TR_C_SL0_DEC_4 = 2, + LSM6DS3TR_C_SL0_DEC_8 = 3, + LSM6DS3TR_C_SL0_DEC_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_slave0_rate_t; +int32_t lsm6ds3tr_c_sh_slave_0_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave0_rate_t val); +int32_t lsm6ds3tr_c_sh_slave_0_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave0_rate_t* val); + +typedef enum { + LSM6DS3TR_C_EACH_SH_CYCLE = 0, + LSM6DS3TR_C_ONLY_FIRST_CYCLE = 1, + LSM6DS3TR_C_SH_WR_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_write_once_t; +int32_t lsm6ds3tr_c_sh_write_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_write_once_t val); +int32_t lsm6ds3tr_c_sh_write_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_write_once_t* val); + +typedef enum { + LSM6DS3TR_C_SL1_NO_DEC = 0, + LSM6DS3TR_C_SL1_DEC_2 = 1, + LSM6DS3TR_C_SL1_DEC_4 = 2, + LSM6DS3TR_C_SL1_DEC_8 = 3, + LSM6DS3TR_C_SL1_DEC_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_slave1_rate_t; +int32_t lsm6ds3tr_c_sh_slave_1_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave1_rate_t val); +int32_t lsm6ds3tr_c_sh_slave_1_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave1_rate_t* val); + +typedef enum { + LSM6DS3TR_C_SL2_NO_DEC = 0, + LSM6DS3TR_C_SL2_DEC_2 = 1, + LSM6DS3TR_C_SL2_DEC_4 = 2, + LSM6DS3TR_C_SL2_DEC_8 = 3, + LSM6DS3TR_C_SL2_DEC_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_slave2_rate_t; +int32_t lsm6ds3tr_c_sh_slave_2_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave2_rate_t val); +int32_t lsm6ds3tr_c_sh_slave_2_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave2_rate_t* val); + +typedef enum { + LSM6DS3TR_C_SL3_NO_DEC = 0, + LSM6DS3TR_C_SL3_DEC_2 = 1, + LSM6DS3TR_C_SL3_DEC_4 = 2, + LSM6DS3TR_C_SL3_DEC_8 = 3, + LSM6DS3TR_C_SL3_DEC_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_slave3_rate_t; +int32_t lsm6ds3tr_c_sh_slave_3_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave3_rate_t val); +int32_t lsm6ds3tr_c_sh_slave_3_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave3_rate_t* val); + +/** + * @} + * + */ + +#ifdef __cplusplus +} +#endif + +#endif /* LSM6DS3TR_C_DRIVER_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/applications/plugins/airmouse/tracking/main_loop.cc b/applications/plugins/airmouse/tracking/main_loop.cc new file mode 100644 index 000000000..492157a4d --- /dev/null +++ b/applications/plugins/airmouse/tracking/main_loop.cc @@ -0,0 +1,189 @@ +#include "main_loop.h" + +#include +#include + +#include "imu/imu.h" +#include "orientation_tracker.h" +#include "calibration_data.h" + +#define TAG "tracker" + +static const float CURSOR_SPEED = 1024.0 / (M_PI / 4); +static const float STABILIZE_BIAS = 16.0; + +float g_yaw = 0; +float g_pitch = 0; +float g_dYaw = 0; +float g_dPitch = 0; +bool firstRead = true; +bool stabilize = true; +CalibrationData calibration; +cardboard::OrientationTracker tracker(10000000l); // 10 ms / 100 Hz +uint64_t ippms, ippms2; + +static inline float clamp(float val) +{ + while (val <= -M_PI) { + val += 2 * M_PI; + } + while (val >= M_PI) { + val -= 2 * M_PI; + } + return val; +} + +static inline float highpass(float oldVal, float newVal) +{ + if (!stabilize) { + return newVal; + } + float delta = clamp(oldVal - newVal); + float alpha = (float) std::max(0.0, 1 - std::pow(std::fabs(delta) * CURSOR_SPEED / STABILIZE_BIAS, 3.0)); + return newVal + alpha * delta; +} + +void sendCurrentState(MouseMoveCallback mouse_move) +{ + float dX = g_dYaw * CURSOR_SPEED; + float dY = g_dPitch * CURSOR_SPEED; + + // Scale the shift down to fit the protocol. + if (dX > 127) { + dY *= 127.0 / dX; + dX = 127; + } + if (dX < -127) { + dY *= -127.0 / dX; + dX = -127; + } + if (dY > 127) { + dX *= 127.0 / dY; + dY = 127; + } + if (dY < -127) { + dX *= -127.0 / dY; + dY = -127; + } + + const int8_t x = (int8_t)std::floor(dX + 0.5); + const int8_t y = (int8_t)std::floor(dY + 0.5); + + mouse_move(x, y); + + // Only subtract the part of the error that was already sent. + if (x != 0) { + g_dYaw -= x / CURSOR_SPEED; + } + if (y != 0) { + g_dPitch -= y / CURSOR_SPEED; + } +} + +void onOrientation(cardboard::Vector4& quaternion) +{ + float q1 = quaternion[0]; // X * sin(T/2) + float q2 = quaternion[1]; // Y * sin(T/2) + float q3 = quaternion[2]; // Z * sin(T/2) + float q0 = quaternion[3]; // cos(T/2) + + float yaw = std::atan2(2 * (q0 * q3 - q1 * q2), (1 - 2 * (q1 * q1 + q3 * q3))); + float pitch = std::asin(2 * (q0 * q1 + q2 * q3)); + // float roll = std::atan2(2 * (q0 * q2 - q1 * q3), (1 - 2 * (q1 * q1 + q2 * q2))); + + if (yaw == NAN || pitch == NAN) { + // NaN case, skip it + return; + } + + if (firstRead) { + g_yaw = yaw; + g_pitch = pitch; + firstRead = false; + } else { + const float newYaw = highpass(g_yaw, yaw); + const float newPitch = highpass(g_pitch, pitch); + + float dYaw = clamp(g_yaw - newYaw); + float dPitch = g_pitch - newPitch; + g_yaw = newYaw; + g_pitch = newPitch; + + // Accumulate the error locally. + g_dYaw += dYaw; + g_dPitch += dPitch; + } +} + +extern "C" { + +void calibration_begin() { + calibration.reset(); + FURI_LOG_I(TAG, "Calibrating"); +} + +bool calibration_step() { + if (calibration.isComplete()) + return true; + + double vec[6]; + if (imu_read(vec) & GYR_DATA_READY) { + cardboard::Vector3 data(vec[3], vec[4], vec[5]); + furi_delay_ms(9); // Artificially limit to ~100Hz + return calibration.add(data); + } + + return false; +} + +void calibration_end() { + CalibrationMedian store; + cardboard::Vector3 median = calibration.getMedian(); + store.x = median[0]; + store.y = median[1]; + store.z = median[2]; + CALIBRATION_DATA_SAVE(&store); +} + +void tracking_begin() { + CalibrationMedian store; + cardboard::Vector3 median = calibration.getMedian(); + if (CALIBRATION_DATA_LOAD(&store)) { + median[0] = store.x; + median[1] = store.y; + median[2] = store.z; + } + + ippms = furi_hal_cortex_instructions_per_microsecond(); + ippms2 = ippms / 2; + tracker.SetCalibration(median); + tracker.Resume(); +} + +void tracking_step(MouseMoveCallback mouse_move) { + double vec[6]; + int ret = imu_read(vec); + if (ret != 0) { + uint64_t t = (DWT->CYCCNT * 1000llu + ippms2) / ippms; + if (ret & ACC_DATA_READY) { + cardboard::AccelerometerData adata + = { .system_timestamp = t, .sensor_timestamp_ns = t, + .data = cardboard::Vector3(vec[0], vec[1], vec[2]) }; + tracker.OnAccelerometerData(adata); + } + if (ret & GYR_DATA_READY) { + cardboard::GyroscopeData gdata + = { .system_timestamp = t, .sensor_timestamp_ns = t, + .data = cardboard::Vector3(vec[3], vec[4], vec[5]) }; + cardboard::Vector4 pose = tracker.OnGyroscopeData(gdata); + onOrientation(pose); + sendCurrentState(mouse_move); + } + } +} + +void tracking_end() { + tracker.Pause(); +} + +} diff --git a/applications/plugins/airmouse/tracking/main_loop.h b/applications/plugins/airmouse/tracking/main_loop.h new file mode 100644 index 000000000..ce0d96568 --- /dev/null +++ b/applications/plugins/airmouse/tracking/main_loop.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef bool (*MouseMoveCallback)(int8_t x, int8_t y); + +void calibration_begin(); +bool calibration_step(); +void calibration_end(); + +void tracking_begin(); +void tracking_step(MouseMoveCallback mouse_move); +void tracking_end(); + +#ifdef __cplusplus +} +#endif diff --git a/applications/plugins/airmouse/tracking/orientation_tracker.cc b/applications/plugins/airmouse/tracking/orientation_tracker.cc new file mode 100644 index 000000000..ac20e9672 --- /dev/null +++ b/applications/plugins/airmouse/tracking/orientation_tracker.cc @@ -0,0 +1,95 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "orientation_tracker.h" + +#include "sensors/pose_prediction.h" +#include "util/logging.h" +#include "util/vector.h" +#include "util/vectorutils.h" + +namespace cardboard { + +OrientationTracker::OrientationTracker(const long sampling_period_ns) + : sampling_period_ns_(sampling_period_ns) + , calibration_(Vector3::Zero()) + , is_tracking_(false) + , sensor_fusion_(new SensorFusionEkf()) + , latest_gyroscope_data_({ 0, 0, Vector3::Zero() }) +{ + sensor_fusion_->SetBiasEstimationEnabled(/*kGyroBiasEstimationEnabled*/ true); +} + +void OrientationTracker::SetCalibration(const Vector3& calibration) { + calibration_ = calibration; +} + +void OrientationTracker::Pause() +{ + if (!is_tracking_) { + return; + } + + // Create a gyro event with zero velocity. This effectively stops the prediction. + GyroscopeData event = latest_gyroscope_data_; + event.data = Vector3::Zero(); + + OnGyroscopeData(event); + + is_tracking_ = false; +} + +void OrientationTracker::Resume() { is_tracking_ = true; } + +Vector4 OrientationTracker::GetPose(int64_t timestamp_ns) const +{ + Rotation predicted_rotation; + const PoseState pose_state = sensor_fusion_->GetLatestPoseState(); + if (sensor_fusion_->IsFullyInitialized()) { + predicted_rotation = pose_state.sensor_from_start_rotation; + } else { + CARDBOARD_LOGI("Tracker not fully initialized yet. Using pose prediction only."); + predicted_rotation = pose_prediction::PredictPose(timestamp_ns, pose_state); + } + + return (-predicted_rotation).GetQuaternion(); +} + +void OrientationTracker::OnAccelerometerData(const AccelerometerData& event) +{ + if (!is_tracking_) { + return; + } + sensor_fusion_->ProcessAccelerometerSample(event); +} + +Vector4 OrientationTracker::OnGyroscopeData(const GyroscopeData& event) +{ + if (!is_tracking_) { + return Vector4(); + } + + const GyroscopeData data = { .system_timestamp = event.system_timestamp, + .sensor_timestamp_ns = event.sensor_timestamp_ns, + .data = event.data - calibration_ }; + + latest_gyroscope_data_ = data; + + sensor_fusion_->ProcessGyroscopeSample(data); + + return OrientationTracker::GetPose(data.sensor_timestamp_ns + sampling_period_ns_); +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/orientation_tracker.h b/applications/plugins/airmouse/tracking/orientation_tracker.h new file mode 100644 index 000000000..fb49c9859 --- /dev/null +++ b/applications/plugins/airmouse/tracking/orientation_tracker.h @@ -0,0 +1,68 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include // NOLINT + +#include "sensors/accelerometer_data.h" +#include "sensors/gyroscope_data.h" +#include "sensors/sensor_fusion_ekf.h" +#include "util/rotation.h" + +namespace cardboard { + +// OrientationTracker encapsulates pose tracking by connecting sensors +// to SensorFusion. +// This pose tracker reports poses in display space. +class OrientationTracker { +public: + OrientationTracker(const long sampling_period_ns); + + void SetCalibration(const Vector3& calibration); + + // Pauses tracking and sensors. + void Pause(); + + // Resumes tracking ans sensors. + void Resume(); + + // Gets the predicted pose for a given timestamp. + Vector4 GetPose(int64_t timestamp_ns) const; + + // Function called when receiving AccelerometerData. + // + // @param event sensor event. + void OnAccelerometerData(const AccelerometerData& event); + + // Function called when receiving GyroscopeData. + // + // @param event sensor event. + Vector4 OnGyroscopeData(const GyroscopeData& event); + +private: + long sampling_period_ns_; + Vector3 calibration_; + + std::atomic is_tracking_; + // Sensor Fusion object that stores the internal state of the filter. + std::unique_ptr sensor_fusion_; + // Latest gyroscope data. + GyroscopeData latest_gyroscope_data_; +}; + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/sensors/accelerometer_data.h b/applications/plugins/airmouse/tracking/sensors/accelerometer_data.h new file mode 100644 index 000000000..bdf3289af --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/accelerometer_data.h @@ -0,0 +1,38 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_ACCELEROMETER_DATA_H_ +#define CARDBOARD_SDK_SENSORS_ACCELEROMETER_DATA_H_ + +#include "../util/vector.h" + +namespace cardboard { + +struct AccelerometerData { + // System wall time. + uint64_t system_timestamp; + + // Sensor clock time in nanoseconds. + uint64_t sensor_timestamp_ns; + + // Acceleration force along the x,y,z axes in m/s^2. This follows android + // specification + // (https://developer.android.com/guide/topics/sensors/sensors_overview.html#sensors-coords). + Vector3 data; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_ACCELEROMETER_DATA_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.cc b/applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.cc new file mode 100644 index 000000000..96f2f7346 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.cc @@ -0,0 +1,313 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "gyroscope_bias_estimator.h" + +#include +#include // NOLINT + +#include "../util/rotation.h" +#include "../util/vector.h" + +namespace { + +// Cutoff frequencies in Hertz applied to our various signals, and their +// corresponding filters. +const float kAccelerometerLowPassCutOffFrequencyHz = 1.0f; +const float kRotationVelocityBasedAccelerometerLowPassCutOffFrequencyHz = 0.15f; +const float kGyroscopeLowPassCutOffFrequencyHz = 1.0f; +const float kGyroscopeBiasLowPassCutOffFrequencyHz = 0.15f; + +// Note that MEMS IMU are not that precise. +const double kEpsilon = 1.0e-8; + +// Size of the filtering window for the mean and median filter. The larger the +// windows the larger the filter delay. +const int kFilterWindowSize = 5; + +// Threshold used to compare rotation computed from the accelerometer and the +// gyroscope bias. +const double kRatioBetweenGyroBiasAndAccel = 1.5; + +// The minimum sum of weights we need to acquire before returning a bias +// estimation. +const float kMinSumOfWeightsGyroBiasThreshold = 25.0f; + +// Amount of change in m/s^3 we allow on the smoothed accelerometer values to +// consider the phone static. +const double kAccelerometerDeltaStaticThreshold = 0.5; + +// Amount of change in radians/s^2 we allow on the smoothed gyroscope values to +// consider the phone static. +const double kGyroscopeDeltaStaticThreshold = 0.03; + +// If the gyroscope value is above this threshold, don't update the gyroscope +// bias estimation. This threshold is applied to the magnitude of gyroscope +// vectors in radians/s. +const float kGyroscopeForBiasThreshold = 0.30f; + +// Used to monitor if accelerometer and gyroscope have been static for a few +// frames. +const int kStaticFrameDetectionThreshold = 50; + +// Minimum time step between sensor updates. +const double kMinTimestep = 1; // std::chrono::nanoseconds(1); +} // namespace + +namespace cardboard { + +// A helper class to keep track of whether some signal can be considered static +// over specified number of frames. +class GyroscopeBiasEstimator::IsStaticCounter { +public: + // Initializes a counter with the number of consecutive frames we require + // the signal to be static before IsRecentlyStatic returns true. + // + // @param min_static_frames_threshold number of consecutive frames we + // require the signal to be static before IsRecentlyStatic returns true. + explicit IsStaticCounter(int min_static_frames_threshold) + : min_static_frames_threshold_(min_static_frames_threshold) + , consecutive_static_frames_(0) + { + } + + // Specifies whether the current frame is considered static. + // + // @param is_static static flag for current frame. + void AppendFrame(bool is_static) + { + if (is_static) { + ++consecutive_static_frames_; + } else { + consecutive_static_frames_ = 0; + } + } + + // Returns if static movement is assumed. + bool IsRecentlyStatic() const + { + return consecutive_static_frames_ >= min_static_frames_threshold_; + } + // Resets counter. + void Reset() { consecutive_static_frames_ = 0; } + +private: + const int min_static_frames_threshold_; + int consecutive_static_frames_; +}; + +GyroscopeBiasEstimator::GyroscopeBiasEstimator() + : accelerometer_lowpass_filter_(kAccelerometerLowPassCutOffFrequencyHz) + , simulated_gyroscope_from_accelerometer_lowpass_filter_( + kRotationVelocityBasedAccelerometerLowPassCutOffFrequencyHz) + , gyroscope_lowpass_filter_(kGyroscopeLowPassCutOffFrequencyHz) + , gyroscope_bias_lowpass_filter_(kGyroscopeBiasLowPassCutOffFrequencyHz) + , accelerometer_static_counter_(new IsStaticCounter(kStaticFrameDetectionThreshold)) + , gyroscope_static_counter_(new IsStaticCounter(kStaticFrameDetectionThreshold)) + , current_accumulated_weights_gyroscope_bias_(0.f) + , mean_filter_(kFilterWindowSize) + , median_filter_(kFilterWindowSize) + , last_mean_filtered_accelerometer_value_({ 0, 0, 0 }) +{ + Reset(); +} + +GyroscopeBiasEstimator::~GyroscopeBiasEstimator() { } + +void GyroscopeBiasEstimator::Reset() +{ + accelerometer_lowpass_filter_.Reset(); + gyroscope_lowpass_filter_.Reset(); + gyroscope_bias_lowpass_filter_.Reset(); + accelerometer_static_counter_->Reset(); + gyroscope_static_counter_->Reset(); +} + +void GyroscopeBiasEstimator::ProcessGyroscope( + const Vector3& gyroscope_sample, uint64_t timestamp_ns) +{ + // Update gyroscope and gyroscope delta low-pass filters. + gyroscope_lowpass_filter_.AddSample(gyroscope_sample, timestamp_ns); + + const auto smoothed_gyroscope_delta + = gyroscope_sample - gyroscope_lowpass_filter_.GetFilteredData(); + + gyroscope_static_counter_->AppendFrame( + Length(smoothed_gyroscope_delta) < kGyroscopeDeltaStaticThreshold); + + // Only update the bias if the gyroscope and accelerometer signals have been + // relatively static recently. + if (gyroscope_static_counter_->IsRecentlyStatic() + && accelerometer_static_counter_->IsRecentlyStatic()) { + // Reset static counter when updating the bias fails. + if (!UpdateGyroscopeBias(gyroscope_sample, timestamp_ns)) { + // Bias update fails because of large motion, thus reset the static + // counter. + gyroscope_static_counter_->AppendFrame(false); + } + } else { + // Reset weights, if not static. + current_accumulated_weights_gyroscope_bias_ = 0; + } +} + +void GyroscopeBiasEstimator::ProcessAccelerometer( + const Vector3& accelerometer_sample, uint64_t timestamp_ns) +{ + // Get current state of the filter. + const uint64_t previous_accel_timestamp_ns + = accelerometer_lowpass_filter_.GetMostRecentTimestampNs(); + const bool is_low_pass_filter_init = accelerometer_lowpass_filter_.IsInitialized(); + + // Update accel and accel delta low-pass filters. + accelerometer_lowpass_filter_.AddSample(accelerometer_sample, timestamp_ns); + + const auto smoothed_accelerometer_delta + = accelerometer_sample - accelerometer_lowpass_filter_.GetFilteredData(); + + accelerometer_static_counter_->AppendFrame( + Length(smoothed_accelerometer_delta) < kAccelerometerDeltaStaticThreshold); + + // Rotation from accel cannot be differentiated with only one sample. + if (!is_low_pass_filter_init) { + simulated_gyroscope_from_accelerometer_lowpass_filter_.AddSample({ 0, 0, 0 }, timestamp_ns); + return; + } + + // No need to update the simulated gyroscope at this point because the motion + // is too large. + if (!accelerometer_static_counter_->IsRecentlyStatic()) { + return; + } + + median_filter_.AddSample(accelerometer_lowpass_filter_.GetFilteredData()); + + // This processing can only be started if the buffer is fully initialized. + if (!median_filter_.IsValid()) { + mean_filter_.AddSample(accelerometer_lowpass_filter_.GetFilteredData()); + + // Update the last filtered accelerometer value. + last_mean_filtered_accelerometer_value_ = accelerometer_lowpass_filter_.GetFilteredData(); + return; + } + + mean_filter_.AddSample(median_filter_.GetFilteredData()); + + // Compute a mock gyroscope value from accelerometer. + const int64_t diff = timestamp_ns - previous_accel_timestamp_ns; + const double timestep = static_cast(diff); + + simulated_gyroscope_from_accelerometer_lowpass_filter_.AddSample( + ComputeAngularVelocityFromLatestAccelerometer(timestep), timestamp_ns); + last_mean_filtered_accelerometer_value_ = mean_filter_.GetFilteredData(); +} + +Vector3 GyroscopeBiasEstimator::ComputeAngularVelocityFromLatestAccelerometer(double timestep) const +{ + if (timestep < kMinTimestep) { + return { 0, 0, 0 }; + } + + const auto mean_of_median = mean_filter_.GetFilteredData(); + + // Compute an incremental rotation between the last state and the current + // state. + // + // Note that we switch to double precision here because of precision problem + // with small rotation. + const auto incremental_rotation = Rotation::RotateInto( + Vector3(last_mean_filtered_accelerometer_value_[0], + last_mean_filtered_accelerometer_value_[1], last_mean_filtered_accelerometer_value_[2]), + Vector3(mean_of_median[0], mean_of_median[1], mean_of_median[2])); + + // We use axis angle here because this is how gyroscope values are stored. + Vector3 incremental_rotation_axis; + double incremental_rotation_angle; + incremental_rotation.GetAxisAndAngle(&incremental_rotation_axis, &incremental_rotation_angle); + + incremental_rotation_axis *= incremental_rotation_angle / timestep; + + return { static_cast(incremental_rotation_axis[0]), + static_cast(incremental_rotation_axis[1]), + static_cast(incremental_rotation_axis[2]) }; +} + +bool GyroscopeBiasEstimator::UpdateGyroscopeBias( + const Vector3& gyroscope_sample, uint64_t timestamp_ns) +{ + // Gyroscope values that are too big are potentially dangerous as they could + // originate from slow and steady head rotations. + // + // Therefore we compute an update weight which: + // * favors gyroscope values that are closer to 0 + // * is set to zero if gyroscope values are greater than a threshold. + // + // This way, the gyroscope bias estimation converges faster if the phone is + // flat on a table, as opposed to held up somewhat stationary in the user's + // hands. + + // If magnitude is too big, don't update the filter at all so that we don't + // artificially increase the number of samples accumulated by the filter. + const float gyroscope_sample_norm2 = Length(gyroscope_sample); + if (gyroscope_sample_norm2 >= kGyroscopeForBiasThreshold) { + return false; + } + + float update_weight + = std::max(0.0f, 1 - gyroscope_sample_norm2 / kGyroscopeForBiasThreshold); + update_weight *= update_weight; + gyroscope_bias_lowpass_filter_.AddWeightedSample( + gyroscope_lowpass_filter_.GetFilteredData(), timestamp_ns, update_weight); + + // This counter is only partially valid as the low pass filter drops large + // samples. + current_accumulated_weights_gyroscope_bias_ += update_weight; + + return true; +} + +Vector3 GyroscopeBiasEstimator::GetGyroscopeBias() const +{ + return gyroscope_bias_lowpass_filter_.GetFilteredData(); +} + +bool GyroscopeBiasEstimator::IsCurrentEstimateValid() const +{ + // Remove any bias component along the gravity because they cannot be + // evaluated from accelerometer. + const auto current_gravity_dir = Normalized(last_mean_filtered_accelerometer_value_); + const auto gyro_bias_lowpass = gyroscope_bias_lowpass_filter_.GetFilteredData(); + + const auto off_gravity_gyro_bias + = gyro_bias_lowpass - current_gravity_dir * Dot(gyro_bias_lowpass, current_gravity_dir); + + // Checks that the current bias estimate is not correlated with the + // rotation computed from accelerometer. + const auto gyro_from_accel + = simulated_gyroscope_from_accelerometer_lowpass_filter_.GetFilteredData(); + const bool isGyroscopeBiasCorrelatedWithSimulatedGyro + = (Length(gyro_from_accel) * kRatioBetweenGyroBiasAndAccel + > (Length(off_gravity_gyro_bias) + kEpsilon)); + const bool hasEnoughSamples + = current_accumulated_weights_gyroscope_bias_ > kMinSumOfWeightsGyroBiasThreshold; + const bool areCountersStatic = gyroscope_static_counter_->IsRecentlyStatic() + && accelerometer_static_counter_->IsRecentlyStatic(); + + const bool isStatic + = hasEnoughSamples && areCountersStatic && !isGyroscopeBiasCorrelatedWithSimulatedGyro; + return isStatic; +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.h b/applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.h new file mode 100644 index 000000000..1a46f96be --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.h @@ -0,0 +1,134 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_GYROSCOPE_BIAS_ESTIMATOR_H_ +#define CARDBOARD_SDK_SENSORS_GYROSCOPE_BIAS_ESTIMATOR_H_ + +#include // NOLINT +#include +#include +#include +#include + +#include "lowpass_filter.h" +#include "mean_filter.h" +#include "median_filter.h" +#include "../util/vector.h" + +namespace cardboard { + +// Class that attempts to estimate the gyroscope's bias. +// Its main idea is that it averages the gyroscope values when the phone is +// considered stationary. +// Usage: A client should call the ProcessGyroscope and ProcessAccelerometer +// methods for every accelerometer and gyroscope sensor sample. This class +// expects these calls to be frequent, i.e., at least at 10 Hz. The client can +// then call GetGyroBias to retrieve the current estimate of the gyroscope bias. +// For best results, the fastest available delay option should be used when +// registering to sensors. Note that this class is not thread-safe. +// +// The filtering applied to the accelerometer to estimate a rotation +// from it follows : +// Baptiste Delporte, Laurent Perroton, Thierry Grandpierre, Jacques Trichet. +// Accelerometer and Magnetometer Based Gyroscope Emulation on Smart Sensor +// for a Virtual Reality Application. Sensor and Transducers Journal, 2012. +// +// which is a combination of a IIR filter, a median and a mean filter. +class GyroscopeBiasEstimator { +public: + GyroscopeBiasEstimator(); + virtual ~GyroscopeBiasEstimator(); + + // Updates the estimator with a gyroscope event. + // + // @param gyroscope_sample the angular speed around the x, y, z axis in + // radians/sec. + // @param timestamp_ns the nanosecond at which the event occurred. Only + // guaranteed to be comparable with timestamps from other PocessGyroscope + // invocations. + virtual void ProcessGyroscope(const Vector3& gyroscope_sample, uint64_t timestamp_ns); + + // Processes accelerometer samples to estimate if device is + // stable or not. + // + // First we filter the accelerometer. This is done with 3 filters. + // - A IIR low-pass filter + // - A median filter + // - A mean filter. + // Then a rotation is computed between consecutive filtered accelerometer + // samples. + // Finally this is converted to a velocity to emulate a gyroscope. + // + // @param accelerometer_sample the acceleration (including gravity) on the x, + // y, z axis in meters/s^2. + // @param timestamp_ns the nanosecond at which the event occurred. Only + // guaranteed to be comparable with timestamps from other + // ProcessAccelerometer invocations. + virtual void ProcessAccelerometer(const Vector3& accelerometer_sample, uint64_t timestamp_ns); + + // Returns the estimated gyroscope bias. + // + // @return Estimated gyroscope bias. A vector with zeros is returned if no + // estimate has been computed. + virtual Vector3 GetGyroscopeBias() const; + + // Resets the estimator state. + void Reset(); + + // Returns true if the current estimate returned by GetGyroscopeBias is + // correct. The device (measured using the sensors) has to be static for this + // function to return true. + virtual bool IsCurrentEstimateValid() const; + +private: + // A helper class to keep track of whether some signal can be considered + // static over specified number of frames. + class IsStaticCounter; + + // Updates gyroscope bias estimation. + // + // @return false if the current sample is too large. + bool UpdateGyroscopeBias(const Vector3& gyroscope_sample, uint64_t timestamp_ns); + + // Returns device angular velocity (rad/s) from the latest accelerometer data. + // + // @param timestep in seconds between the last two samples. + // @return rotation velocity from latest accelerometer. This can be + // interpreted as an gyroscope. + Vector3 ComputeAngularVelocityFromLatestAccelerometer(double timestep) const; + + LowpassFilter accelerometer_lowpass_filter_; + LowpassFilter simulated_gyroscope_from_accelerometer_lowpass_filter_; + LowpassFilter gyroscope_lowpass_filter_; + LowpassFilter gyroscope_bias_lowpass_filter_; + + std::unique_ptr accelerometer_static_counter_; + std::unique_ptr gyroscope_static_counter_; + + // Sum of the weight of sample used for gyroscope filtering. + float current_accumulated_weights_gyroscope_bias_; + + // Set of filters for accelerometer data to estimate a rotation + // based only on accelerometer. + MeanFilter mean_filter_; + MedianFilter median_filter_; + + // Last computed filter accelerometer value used for finite differences. + Vector3 last_mean_filtered_accelerometer_value_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_GYROSCOPE_BIAS_ESTIMATOR_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/gyroscope_data.h b/applications/plugins/airmouse/tracking/sensors/gyroscope_data.h new file mode 100644 index 000000000..085e85209 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/gyroscope_data.h @@ -0,0 +1,38 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_GYROSCOPE_DATA_H_ +#define CARDBOARD_SDK_SENSORS_GYROSCOPE_DATA_H_ + +#include "../util/vector.h" + +namespace cardboard { + +struct GyroscopeData { + // System wall time. + uint64_t system_timestamp; + + // Sensor clock time in nanoseconds. + uint64_t sensor_timestamp_ns; + + // Rate of rotation around the x,y,z axes in rad/s. This follows android + // specification + // (https://developer.android.com/guide/topics/sensors/sensors_overview.html#sensors-coords). + Vector3 data; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_GYROSCOPE_DATA_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/lowpass_filter.cc b/applications/plugins/airmouse/tracking/sensors/lowpass_filter.cc new file mode 100644 index 000000000..efadfbf4e --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/lowpass_filter.cc @@ -0,0 +1,84 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "lowpass_filter.h" + +#include + +namespace { + +const double kSecondsFromNanoseconds = 1.0e-9; + +// Minimum time step between sensor updates. This corresponds to 1000 Hz. +const double kMinTimestepS = 0.001f; + +// Maximum time step between sensor updates. This corresponds to 1 Hz. +const double kMaxTimestepS = 1.00f; + +} // namespace + +namespace cardboard { + +LowpassFilter::LowpassFilter(double cutoff_freq_hz) + : cutoff_time_constant_(1 / (2 * (double)M_PI * cutoff_freq_hz)) + , initialized_(false) +{ + Reset(); +} + +void LowpassFilter::AddSample(const Vector3& sample, uint64_t timestamp_ns) +{ + AddWeightedSample(sample, timestamp_ns, 1.0); +} + +void LowpassFilter::AddWeightedSample(const Vector3& sample, uint64_t timestamp_ns, double weight) +{ + if (!initialized_) { + // Initialize filter state + filtered_data_ = { sample[0], sample[1], sample[2] }; + timestamp_most_recent_update_ns_ = timestamp_ns; + initialized_ = true; + return; + } + + if (timestamp_ns < timestamp_most_recent_update_ns_) { + timestamp_most_recent_update_ns_ = timestamp_ns; + return; + } + + const double delta_s = static_cast(timestamp_ns - timestamp_most_recent_update_ns_) + * kSecondsFromNanoseconds; + if (delta_s <= kMinTimestepS || delta_s > kMaxTimestepS) { + timestamp_most_recent_update_ns_ = timestamp_ns; + return; + } + + const double weighted_delta_secs = weight * delta_s; + + const double alpha = weighted_delta_secs / (cutoff_time_constant_ + weighted_delta_secs); + + for (int i = 0; i < 3; ++i) { + filtered_data_[i] = (1 - alpha) * filtered_data_[i] + alpha * sample[i]; + } + timestamp_most_recent_update_ns_ = timestamp_ns; +} + +void LowpassFilter::Reset() +{ + initialized_ = false; + filtered_data_ = { 0, 0, 0 }; +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/sensors/lowpass_filter.h b/applications/plugins/airmouse/tracking/sensors/lowpass_filter.h new file mode 100644 index 000000000..c4994c425 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/lowpass_filter.h @@ -0,0 +1,81 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_LOWPASS_FILTER_H_ +#define CARDBOARD_SDK_SENSORS_LOWPASS_FILTER_H_ + +#include +#include + +#include "../util/vector.h" + +namespace cardboard { + +// Implements an IIR, first order, low pass filter over vectors of the given +// dimension = 3. +// See http://en.wikipedia.org/wiki/Low-pass_filter +class LowpassFilter { +public: + // Initializes a filter with the given cutoff frequency in Hz. + explicit LowpassFilter(double cutoff_freq_hz); + + // Updates the filter with the given sample. Note that samples with + // non-monotonic timestamps and successive samples with a time steps below 1 + // ms or above 1 s are ignored. + // + // @param sample current sample data. + // @param timestamp_ns timestamp associated to this sample in nanoseconds. + void AddSample(const Vector3& sample, uint64_t timestamp_ns); + + // Updates the filter with the given weighted sample. + // + // @param sample current sample data. + // @param timestamp_ns timestamp associated to this sample in nanoseconds. + // @param weight typically a [0, 1] weight factor used when applying a new + // sample. A weight of 1 corresponds to calling AddSample. A weight of 0 + // makes the update no-op. The first initial sample is not affected by + // this. + void AddWeightedSample(const Vector3& sample, uint64_t timestamp_ns, double weight); + + // Returns the filtered value. A vector with zeros is returned if no samples + // have been added. + Vector3 GetFilteredData() const { + return filtered_data_; + } + + // Returns the most recent update timestamp in ns. + uint64_t GetMostRecentTimestampNs() const { + return timestamp_most_recent_update_ns_; + } + + // Returns true when the filter is initialized. + bool IsInitialized() const { + return initialized_; + } + + // Resets filter state. + void Reset(); + +private: + const double cutoff_time_constant_; + uint64_t timestamp_most_recent_update_ns_; + bool initialized_; + + Vector3 filtered_data_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_LOWPASS_FILTER_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/mean_filter.cc b/applications/plugins/airmouse/tracking/sensors/mean_filter.cc new file mode 100644 index 000000000..02fb8034c --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/mean_filter.cc @@ -0,0 +1,46 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mean_filter.h" + +namespace cardboard { + +MeanFilter::MeanFilter(size_t filter_size) + : filter_size_(filter_size) +{ +} + +void MeanFilter::AddSample(const Vector3& sample) +{ + buffer_.push_back(sample); + if (buffer_.size() > filter_size_) { + buffer_.pop_front(); + } +} + +bool MeanFilter::IsValid() const { return buffer_.size() == filter_size_; } + +Vector3 MeanFilter::GetFilteredData() const +{ + // Compute mean of the samples stored in buffer_. + Vector3 mean = Vector3::Zero(); + for (auto sample : buffer_) { + mean += sample; + } + + return mean / static_cast(filter_size_); +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/sensors/mean_filter.h b/applications/plugins/airmouse/tracking/sensors/mean_filter.h new file mode 100644 index 000000000..6b4956fef --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/mean_filter.h @@ -0,0 +1,48 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_MEAN_FILTER_H_ +#define CARDBOARD_SDK_SENSORS_MEAN_FILTER_H_ + +#include + +#include "../util/vector.h" + +namespace cardboard { + +// Fixed window FIFO mean filter for vectors of the given dimension. +class MeanFilter { +public: + // Create a mean filter of size filter_size. + // @param filter_size size of the internal filter. + explicit MeanFilter(size_t filter_size); + + // Add sample to buffer_ if buffer_ is full it drop the oldest sample. + void AddSample(const Vector3& sample); + + // Returns true if buffer has filter_size_ sample, false otherwise. + bool IsValid() const; + + // Returns the mean of values stored in the internal buffer. + Vector3 GetFilteredData() const; + +private: + const size_t filter_size_; + std::deque buffer_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_MEAN_FILTER_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/median_filter.cc b/applications/plugins/airmouse/tracking/sensors/median_filter.cc new file mode 100644 index 000000000..d27c19f47 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/median_filter.cc @@ -0,0 +1,69 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "median_filter.h" + +#include +#include + +#include "../util/vector.h" +#include "../util/vectorutils.h" + +namespace cardboard { + +MedianFilter::MedianFilter(size_t filter_size) + : filter_size_(filter_size) +{ +} + +void MedianFilter::AddSample(const Vector3& sample) +{ + buffer_.push_back(sample); + norms_.push_back(Length(sample)); + if (buffer_.size() > filter_size_) { + buffer_.pop_front(); + norms_.pop_front(); + } +} + +bool MedianFilter::IsValid() const { return buffer_.size() == filter_size_; } + +Vector3 MedianFilter::GetFilteredData() const +{ + std::vector norms(norms_.begin(), norms_.end()); + + // Get median of value of the norms. + std::nth_element(norms.begin(), norms.begin() + filter_size_ / 2, norms.end()); + const float median_norm = norms[filter_size_ / 2]; + + // Get median value based on their norm. + auto median_it = buffer_.begin(); + for (const auto norm : norms_) { + if (norm == median_norm) { + break; + } + ++median_it; + } + + return *median_it; +} + +void MedianFilter::Reset() +{ + buffer_.clear(); + norms_.clear(); +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/sensors/median_filter.h b/applications/plugins/airmouse/tracking/sensors/median_filter.h new file mode 100644 index 000000000..9a8e7cfc7 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/median_filter.h @@ -0,0 +1,53 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_MEDIAN_FILTER_H_ +#define CARDBOARD_SDK_SENSORS_MEDIAN_FILTER_H_ + +#include + +#include "../util/vector.h" + +namespace cardboard { + +// Fixed window FIFO median filter for vectors of the given dimension = 3. +class MedianFilter { +public: + // Creates a median filter of size filter_size. + // @param filter_size size of the internal filter. + explicit MedianFilter(size_t filter_size); + + // Adds sample to buffer_ if buffer_ is full it drops the oldest sample. + void AddSample(const Vector3& sample); + + // Returns true if buffer has filter_size_ sample, false otherwise. + bool IsValid() const; + + // Returns the median of values store in the internal buffer. + Vector3 GetFilteredData() const; + + // Resets the filter, removing all samples that have been added. + void Reset(); + +private: + const size_t filter_size_; + std::deque buffer_; + // Contains norms of the elements stored in buffer_. + std::deque norms_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_MEDIAN_FILTER_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/pose_prediction.cc b/applications/plugins/airmouse/tracking/sensors/pose_prediction.cc new file mode 100644 index 000000000..baaf844dd --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/pose_prediction.cc @@ -0,0 +1,71 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "pose_prediction.h" + +#include // NOLINT + +#include "../util/logging.h" +#include "../util/vectorutils.h" + +namespace cardboard { + +namespace { + const double kEpsilon = 1.0e-15; +} // namespace + +namespace pose_prediction { + + Rotation GetRotationFromGyroscope(const Vector3& gyroscope_value, double timestep_s) + { + const double velocity = Length(gyroscope_value); + + // When there is no rotation data return an identity rotation. + if (velocity < kEpsilon) { + CARDBOARD_LOGI("PosePrediction::GetRotationFromGyroscope: Velocity really small, " + "returning identity rotation."); + return Rotation::Identity(); + } + // Since the gyroscope_value is a start from sensor transformation we need to + // invert it to have a sensor from start transformation, hence the minus sign. + // For more info: + // http://developer.android.com/guide/topics/sensors/sensors_motion.html#sensors-motion-gyro + return Rotation::FromAxisAndAngle(gyroscope_value / velocity, -timestep_s * velocity); + } + + Rotation PredictPose(int64_t requested_pose_timestamp, const PoseState& current_state) + { + // Subtracting unsigned numbers is bad when the result is negative. + const int64_t diff = requested_pose_timestamp - current_state.timestamp; + const double timestep_s = diff * 1.0e-9; + + const Rotation update = GetRotationFromGyroscope( + current_state.sensor_from_start_rotation_velocity, timestep_s); + return update * current_state.sensor_from_start_rotation; + } + + Rotation PredictPoseInv(int64_t requested_pose_timestamp, const PoseState& current_state) + { + // Subtracting unsigned numbers is bad when the result is negative. + const int64_t diff = requested_pose_timestamp - current_state.timestamp; + const double timestep_s = diff * 1.0e-9; + + const Rotation update = GetRotationFromGyroscope( + current_state.sensor_from_start_rotation_velocity, timestep_s); + return current_state.sensor_from_start_rotation * (-update); + } + +} // namespace pose_prediction +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/sensors/pose_prediction.h b/applications/plugins/airmouse/tracking/sensors/pose_prediction.h new file mode 100644 index 000000000..9ab311b33 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/pose_prediction.h @@ -0,0 +1,55 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_POSE_PREDICTION_H_ +#define CARDBOARD_SDK_SENSORS_POSE_PREDICTION_H_ + +#include + +#include "pose_state.h" +#include "../util/rotation.h" + +namespace cardboard { +namespace pose_prediction { + +// Returns a rotation matrix based on the integration of the gyroscope_value +// over the timestep_s in seconds. +// TODO(pfg): Document the space better here. +// +// @param gyroscope_value gyroscope sensor values. +// @param timestep_s integration period in seconds. +// @return Integration of the gyroscope value the rotation is from Start to +// Sensor Space. +Rotation GetRotationFromGyroscope(const Vector3& gyroscope_value, double timestep_s); + +// Gets a predicted pose for a given time in the future (e.g. rendering time) +// based on a linear prediction model. This uses the system current state +// (position, velocity, etc) from the past to extrapolate a position in the +// future. +// +// @param requested_pose_timestamp time at which you want the pose. +// @param current_state current state that stores the pose and linear model at a +// given time prior to requested_pose_timestamp_ns. +// @return pose from Start to Sensor Space. +Rotation PredictPose(int64_t requested_pose_timestamp, const PoseState& current_state); + +// Equivalent to PredictPose, but for use with poses relative to Start Space +// rather than sensor space. +Rotation PredictPoseInv(int64_t requested_pose_timestamp, const PoseState& current_state); + +} // namespace pose_prediction +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_POSE_PREDICTION_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/pose_state.h b/applications/plugins/airmouse/tracking/sensors/pose_state.h new file mode 100644 index 000000000..f7801c9f3 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/pose_state.h @@ -0,0 +1,56 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_POSE_STATE_H_ +#define CARDBOARD_SDK_SENSORS_POSE_STATE_H_ + +#include "../util/rotation.h" +#include "../util/vector.h" + +namespace cardboard { + +enum { + kPoseStateFlagInvalid = 1U << 0, + kPoseStateFlagInitializing = 1U << 1, + kPoseStateFlagHas6DoF = 1U << 2, +}; + +// Stores a head pose pose plus derivatives. This can be used for prediction. +struct PoseState { + // System wall time. + int64_t timestamp; + + // Rotation from Sensor Space to Start Space. + Rotation sensor_from_start_rotation; + + // First derivative of the rotation. + Vector3 sensor_from_start_rotation_velocity; + + // Current gyroscope bias in rad/s. + Vector3 bias; + + // The position of the headset. + Vector3 position = Vector3(0, 0, 0); + + // In the same coordinate frame as the position. + Vector3 velocity = Vector3(0, 0, 0); + + // Flags indicating the status of the pose. + uint64_t flags = 0U; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_POSE_STATE_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.cc b/applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.cc new file mode 100644 index 000000000..575dde6f0 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.cc @@ -0,0 +1,333 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "sensor_fusion_ekf.h" + +#include +#include + +#include "accelerometer_data.h" +#include "gyroscope_data.h" +#include "pose_prediction.h" +#include "../util/matrixutils.h" + +namespace cardboard { + +namespace { + + const double kFiniteDifferencingEpsilon = 1.0e-7; + const double kEpsilon = 1.0e-15; + // Default gyroscope frequency. This corresponds to 100 Hz. + const double kDefaultGyroscopeTimestep_s = 0.01f; + // Maximum time between gyroscope before we start limiting the integration. + const double kMaximumGyroscopeSampleDelay_s = 0.04f; + // Compute a first-order exponential moving average of changes in accel norm per + // frame. + const double kSmoothingFactor = 0.5; + // Minimum and maximum values used for accelerometer noise covariance matrix. + // The smaller the sigma value, the more weight is given to the accelerometer + // signal. + const double kMinAccelNoiseSigma = 0.75; + const double kMaxAccelNoiseSigma = 7.0; + // Initial value for the diagonal elements of the different covariance matrices. + const double kInitialStateCovarianceValue = 25.0; + const double kInitialProcessCovarianceValue = 1.0; + // Maximum accelerometer norm change allowed before capping it covariance to a + // large value. + const double kMaxAccelNormChange = 0.15; + // Timestep IIR filtering coefficient. + const double kTimestepFilterCoeff = 0.95; + // Minimum number of sample for timestep filtering. + const int kTimestepFilterMinSamples = 10; + + // Z direction in start space. + const Vector3 kCanonicalZDirection(0.0, 0.0, 1.0); + + // Computes an axis-angle rotation from the input vector. + // angle = norm(a) + // axis = a.normalized() + // If norm(a) == 0, it returns an identity rotation. + static inline Rotation RotationFromVector(const Vector3& a) + { + const double norm_a = Length(a); + if (norm_a < kEpsilon) { + return Rotation::Identity(); + } + return Rotation::FromAxisAndAngle(a / norm_a, norm_a); + } + +} // namespace + +SensorFusionEkf::SensorFusionEkf() + : execute_reset_with_next_accelerometer_sample_(false) + , bias_estimation_enabled_(true) + , gyroscope_bias_estimate_({ 0, 0, 0 }) +{ + ResetState(); +} + +void SensorFusionEkf::Reset() { execute_reset_with_next_accelerometer_sample_ = true; } + +void SensorFusionEkf::ResetState() +{ + current_state_.sensor_from_start_rotation = Rotation::Identity(); + current_state_.sensor_from_start_rotation_velocity = Vector3::Zero(); + + current_gyroscope_sensor_timestamp_ns_ = 0; + current_accelerometer_sensor_timestamp_ns_ = 0; + + state_covariance_ = Matrix3x3::Identity() * kInitialStateCovarianceValue; + process_covariance_ = Matrix3x3::Identity() * kInitialProcessCovarianceValue; + accelerometer_measurement_covariance_ + = Matrix3x3::Identity() * kMinAccelNoiseSigma * kMinAccelNoiseSigma; + innovation_covariance_ = Matrix3x3::Identity(); + + accelerometer_measurement_jacobian_ = Matrix3x3::Zero(); + kalman_gain_ = Matrix3x3::Zero(); + innovation_ = Vector3::Zero(); + accelerometer_measurement_ = Vector3::Zero(); + prediction_ = Vector3::Zero(); + control_input_ = Vector3::Zero(); + state_update_ = Vector3::Zero(); + + moving_average_accelerometer_norm_change_ = 0.0; + + is_timestep_filter_initialized_ = false; + is_gyroscope_filter_valid_ = false; + is_aligned_with_gravity_ = false; + + // Reset biases. + gyroscope_bias_estimator_.Reset(); + gyroscope_bias_estimate_ = { 0, 0, 0 }; +} + +// Here I am doing something wrong relative to time stamps. The state timestamps +// always correspond to the gyrostamps because it would require additional +// extrapolation if I wanted to do otherwise. +PoseState SensorFusionEkf::GetLatestPoseState() const { return current_state_; } + +void SensorFusionEkf::ProcessGyroscopeSample(const GyroscopeData& sample) +{ + // Don't accept gyroscope sample when waiting for a reset. + if (execute_reset_with_next_accelerometer_sample_) { + return; + } + + // Discard outdated samples. + if (current_gyroscope_sensor_timestamp_ns_ >= sample.sensor_timestamp_ns) { + current_gyroscope_sensor_timestamp_ns_ = sample.sensor_timestamp_ns; + return; + } + + // Checks that we received at least one gyroscope sample in the past. + if (current_gyroscope_sensor_timestamp_ns_ != 0) { + double current_timestep_s = std::chrono::duration_cast>( + std::chrono::nanoseconds( + sample.sensor_timestamp_ns - current_gyroscope_sensor_timestamp_ns_)) + .count(); + if (current_timestep_s > kMaximumGyroscopeSampleDelay_s) { + if (is_gyroscope_filter_valid_) { + // Replaces the delta timestamp by the filtered estimates of the delta time. + current_timestep_s = filtered_gyroscope_timestep_s_; + } else { + current_timestep_s = kDefaultGyroscopeTimestep_s; + } + } else { + FilterGyroscopeTimestep(current_timestep_s); + } + + if (bias_estimation_enabled_) { + gyroscope_bias_estimator_.ProcessGyroscope(sample.data, sample.sensor_timestamp_ns); + + if (gyroscope_bias_estimator_.IsCurrentEstimateValid()) { + // As soon as the device is considered to be static, the bias estimator + // should have a precise estimate of the gyroscope bias. + gyroscope_bias_estimate_ = gyroscope_bias_estimator_.GetGyroscopeBias(); + } + } + + // Only integrate after receiving an accelerometer sample. + if (is_aligned_with_gravity_) { + const Rotation rotation_from_gyroscope = pose_prediction::GetRotationFromGyroscope( + { sample.data[0] - gyroscope_bias_estimate_[0], + sample.data[1] - gyroscope_bias_estimate_[1], + sample.data[2] - gyroscope_bias_estimate_[2] }, + current_timestep_s); + current_state_.sensor_from_start_rotation + = rotation_from_gyroscope * current_state_.sensor_from_start_rotation; + UpdateStateCovariance(RotationMatrixNH(rotation_from_gyroscope)); + state_covariance_ = state_covariance_ + + ((current_timestep_s * current_timestep_s) * process_covariance_); + } + } + + // Saves gyroscope event for future prediction. + current_state_.timestamp = sample.system_timestamp; + current_gyroscope_sensor_timestamp_ns_ = sample.sensor_timestamp_ns; + current_state_.sensor_from_start_rotation_velocity.Set( + sample.data[0] - gyroscope_bias_estimate_[0], sample.data[1] - gyroscope_bias_estimate_[1], + sample.data[2] - gyroscope_bias_estimate_[2]); +} + +Vector3 SensorFusionEkf::ComputeInnovation(const Rotation& pose) +{ + const Vector3 predicted_down_direction = pose * kCanonicalZDirection; + + const Rotation rotation + = Rotation::RotateInto(predicted_down_direction, accelerometer_measurement_); + Vector3 axis; + double angle; + rotation.GetAxisAndAngle(&axis, &angle); + return axis * angle; +} + +void SensorFusionEkf::ComputeMeasurementJacobian() +{ + for (int dof = 0; dof < 3; dof++) { + Vector3 delta = Vector3::Zero(); + delta[dof] = kFiniteDifferencingEpsilon; + + const Rotation epsilon_rotation = RotationFromVector(delta); + const Vector3 delta_rotation + = ComputeInnovation(epsilon_rotation * current_state_.sensor_from_start_rotation); + + const Vector3 col = (innovation_ - delta_rotation) / kFiniteDifferencingEpsilon; + accelerometer_measurement_jacobian_(0, dof) = col[0]; + accelerometer_measurement_jacobian_(1, dof) = col[1]; + accelerometer_measurement_jacobian_(2, dof) = col[2]; + } +} + +void SensorFusionEkf::ProcessAccelerometerSample(const AccelerometerData& sample) +{ + // Discard outdated samples. + if (current_accelerometer_sensor_timestamp_ns_ >= sample.sensor_timestamp_ns) { + current_accelerometer_sensor_timestamp_ns_ = sample.sensor_timestamp_ns; + return; + } + + // Call reset state if required. + if (execute_reset_with_next_accelerometer_sample_.exchange(false)) { + ResetState(); + } + + accelerometer_measurement_.Set(sample.data[0], sample.data[1], sample.data[2]); + current_accelerometer_sensor_timestamp_ns_ = sample.sensor_timestamp_ns; + + if (bias_estimation_enabled_) { + gyroscope_bias_estimator_.ProcessAccelerometer(sample.data, sample.sensor_timestamp_ns); + } + + if (!is_aligned_with_gravity_) { + // This is the first accelerometer measurement so it initializes the + // orientation estimate. + current_state_.sensor_from_start_rotation + = Rotation::RotateInto(kCanonicalZDirection, accelerometer_measurement_); + is_aligned_with_gravity_ = true; + + previous_accelerometer_norm_ = Length(accelerometer_measurement_); + return; + } + + UpdateMeasurementCovariance(); + + innovation_ = ComputeInnovation(current_state_.sensor_from_start_rotation); + ComputeMeasurementJacobian(); + + // S = H * P * H' + R + innovation_covariance_ = accelerometer_measurement_jacobian_ * state_covariance_ + * Transpose(accelerometer_measurement_jacobian_) + + accelerometer_measurement_covariance_; + + // K = P * H' * S^-1 + kalman_gain_ = state_covariance_ * Transpose(accelerometer_measurement_jacobian_) + * Inverse(innovation_covariance_); + + // x_update = K*nu + state_update_ = kalman_gain_ * innovation_; + + // P = (I - K * H) * P; + state_covariance_ = (Matrix3x3::Identity() - kalman_gain_ * accelerometer_measurement_jacobian_) + * state_covariance_; + + // Updates pose and associate covariance matrix. + const Rotation rotation_from_state_update = RotationFromVector(state_update_); + + current_state_.sensor_from_start_rotation + = rotation_from_state_update * current_state_.sensor_from_start_rotation; + UpdateStateCovariance(RotationMatrixNH(rotation_from_state_update)); +} + +void SensorFusionEkf::UpdateStateCovariance(const Matrix3x3& motion_update) +{ + state_covariance_ = motion_update * state_covariance_ * Transpose(motion_update); +} + +void SensorFusionEkf::FilterGyroscopeTimestep(double gyroscope_timestep_s) +{ + if (!is_timestep_filter_initialized_) { + // Initializes the filter. + filtered_gyroscope_timestep_s_ = gyroscope_timestep_s; + num_gyroscope_timestep_samples_ = 1; + is_timestep_filter_initialized_ = true; + return; + } + + // Computes the IIR filter response. + filtered_gyroscope_timestep_s_ = kTimestepFilterCoeff * filtered_gyroscope_timestep_s_ + + (1 - kTimestepFilterCoeff) * gyroscope_timestep_s; + ++num_gyroscope_timestep_samples_; + + if (num_gyroscope_timestep_samples_ > kTimestepFilterMinSamples) { + is_gyroscope_filter_valid_ = true; + } +} + +void SensorFusionEkf::UpdateMeasurementCovariance() +{ + const double current_accelerometer_norm = Length(accelerometer_measurement_); + // Norm change between current and previous accel readings. + const double current_accelerometer_norm_change + = std::abs(current_accelerometer_norm - previous_accelerometer_norm_); + previous_accelerometer_norm_ = current_accelerometer_norm; + + moving_average_accelerometer_norm_change_ = kSmoothingFactor * current_accelerometer_norm_change + + (1 - kSmoothingFactor) * moving_average_accelerometer_norm_change_; + + // If we hit the accel norm change threshold, we use the maximum noise sigma + // for the accel covariance. For anything below that, we use a linear + // combination between min and max sigma values. + const double norm_change_ratio + = moving_average_accelerometer_norm_change_ / kMaxAccelNormChange; + const double accelerometer_noise_sigma = std::min(kMaxAccelNoiseSigma, + kMinAccelNoiseSigma + norm_change_ratio * (kMaxAccelNoiseSigma - kMinAccelNoiseSigma)); + + // Updates the accel covariance matrix with the new sigma value. + accelerometer_measurement_covariance_ + = Matrix3x3::Identity() * accelerometer_noise_sigma * accelerometer_noise_sigma; +} + +bool SensorFusionEkf::IsBiasEstimationEnabled() const { return bias_estimation_enabled_; } + +void SensorFusionEkf::SetBiasEstimationEnabled(bool enable) +{ + if (bias_estimation_enabled_ != enable) { + bias_estimation_enabled_ = enable; + gyroscope_bias_estimate_ = { 0, 0, 0 }; + gyroscope_bias_estimator_.Reset(); + } +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.h b/applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.h new file mode 100644 index 000000000..a66fe33f4 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.h @@ -0,0 +1,188 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_SENSOR_FUSION_EKF_H_ +#define CARDBOARD_SDK_SENSORS_SENSOR_FUSION_EKF_H_ + +#include +#include +#include + +#include "accelerometer_data.h" +#include "gyroscope_bias_estimator.h" +#include "gyroscope_data.h" +#include "pose_state.h" +#include "../util/matrix_3x3.h" +#include "../util/rotation.h" +#include "../util/vector.h" + +namespace cardboard { + +// Sensor fusion class that implements an Extended Kalman Filter (EKF) to +// estimate a 3D rotation from a gyroscope and an accelerometer. +// This system only has one state, the pose. It does not estimate any velocity +// or acceleration. +// +// To learn more about Kalman filtering one can read this article which is a +// good introduction: https://en.wikipedia.org/wiki/Kalman_filter +class SensorFusionEkf { +public: + SensorFusionEkf(); + + // Resets the state of the sensor fusion. It sets the velocity for + // prediction to zero. The reset will happen with the next + // accelerometer sample. Gyroscope sample will be discarded until a new + // accelerometer sample arrives. + void Reset(); + + // Gets the PoseState representing the latest pose and derivatives at a + // particular timestamp as estimated by SensorFusion. + PoseState GetLatestPoseState() const; + + // Processes one gyroscope sample event. This updates the pose of the system + // and the prediction model. The gyroscope data is assumed to be in axis angle + // form. Angle = ||v|| and Axis = v / ||v||, with v = [v_x, v_y, v_z]^T. + // + // @param sample gyroscope sample data. + void ProcessGyroscopeSample(const GyroscopeData& sample); + + // Processes one accelerometer sample event. This updates the pose of the + // system. If the Accelerometer norm changes too much between sample it is not + // trusted as much. + // + // @param sample accelerometer sample data. + void ProcessAccelerometerSample(const AccelerometerData& sample); + + // Enables or disables the drift correction by estimating the gyroscope bias. + // + // @param enable Enable drift correction. + void SetBiasEstimationEnabled(bool enable); + + // Returns a boolean that indicates if bias estimation is enabled or disabled. + // + // @return true if bias estimation is enabled, false otherwise. + bool IsBiasEstimationEnabled() const; + + // Returns the current gyroscope bias estimate from GyroscopeBiasEstimator. + Vector3 GetGyroscopeBias() const { + return { + gyroscope_bias_estimate_[0], gyroscope_bias_estimate_[1], gyroscope_bias_estimate_[2]}; + } + + // Returns true after receiving the first accelerometer measurement. + bool IsFullyInitialized() const { + return is_aligned_with_gravity_; + } + +private: + // Estimates the average timestep between gyroscope event. + void FilterGyroscopeTimestep(double gyroscope_timestep); + + // Updates the state covariance with an incremental motion. It changes the + // space of the quadric. + void UpdateStateCovariance(const Matrix3x3& motion_update); + + // Computes the innovation vector of the Kalman based on the input pose. + // It uses the latest measurement vector (i.e. accelerometer data), which must + // be set prior to calling this function. + Vector3 ComputeInnovation(const Rotation& pose); + + // This computes the measurement_jacobian_ via numerical differentiation based + // on the current value of sensor_from_start_rotation_. + void ComputeMeasurementJacobian(); + + // Updates the accelerometer covariance matrix. + // + // This looks at the norm of recent accelerometer readings. If it has changed + // significantly, it means the phone receives additional acceleration than + // just gravity, and so the down vector information gravity signal is noisier. + void UpdateMeasurementCovariance(); + + // Reset all internal states. This is not thread safe. Lock should be acquired + // outside of it. This function is called in ProcessAccelerometerSample. + void ResetState(); + + // Current transformation from Sensor Space to Start Space. + // x_sensor = sensor_from_start_rotation_ * x_start; + PoseState current_state_; + + // Filtering of the gyroscope timestep started? + bool is_timestep_filter_initialized_; + // Filtered gyroscope timestep valid? + bool is_gyroscope_filter_valid_; + // Sensor fusion currently aligned with gravity? After initialization + // it will requires a couple of accelerometer data for the system to get + // aligned. + std::atomic is_aligned_with_gravity_; + + // Covariance of Kalman filter state (P in common formulation). + Matrix3x3 state_covariance_; + // Covariance of the process noise (Q in common formulation). + Matrix3x3 process_covariance_; + // Covariance of the accelerometer measurement (R in common formulation). + Matrix3x3 accelerometer_measurement_covariance_; + // Covariance of innovation (S in common formulation). + Matrix3x3 innovation_covariance_; + // Jacobian of the measurements (H in common formulation). + Matrix3x3 accelerometer_measurement_jacobian_; + // Gain of the Kalman filter (K in common formulation). + Matrix3x3 kalman_gain_; + // Parameter update a.k.a. innovation vector. (\nu in common formulation). + Vector3 innovation_; + // Measurement vector (z in common formulation). + Vector3 accelerometer_measurement_; + // Current prediction vector (g in common formulation). + Vector3 prediction_; + // Control input, currently this is only the gyroscope data (\mu in common + // formulation). + Vector3 control_input_; + // Update of the state vector. (x in common formulation). + Vector3 state_update_; + + // Sensor time of the last gyroscope processed event. + uint64_t current_gyroscope_sensor_timestamp_ns_; + // Sensor time of the last accelerometer processed event. + uint64_t current_accelerometer_sensor_timestamp_ns_; + + // Estimates of the timestep between gyroscope event in seconds. + double filtered_gyroscope_timestep_s_; + // Number of timestep samples processed so far by the filter. + uint32_t num_gyroscope_timestep_samples_; + // Norm of the accelerometer for the previous measurement. + double previous_accelerometer_norm_; + // Moving average of the accelerometer norm changes. It is computed for every + // sensor datum. + double moving_average_accelerometer_norm_change_; + + // Flag indicating if a state reset should be executed with the next + // accelerometer sample. + std::atomic execute_reset_with_next_accelerometer_sample_; + + // Flag indicating if bias estimation is enabled (enabled by default). + std::atomic bias_estimation_enabled_; + + // Bias estimator and static device detector. + GyroscopeBiasEstimator gyroscope_bias_estimator_; + + // Current bias estimate_; + Vector3 gyroscope_bias_estimate_; + + SensorFusionEkf(const SensorFusionEkf&) = delete; + SensorFusionEkf& operator=(const SensorFusionEkf&) = delete; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_SENSOR_FUSION_EKF_H_ diff --git a/applications/plugins/airmouse/tracking/util/logging.h b/applications/plugins/airmouse/tracking/util/logging.h new file mode 100644 index 000000000..dee224b1c --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/logging.h @@ -0,0 +1,38 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_LOGGING_H_ +#define CARDBOARD_SDK_UTIL_LOGGING_H_ + +#include +#include + +#if defined(__ANDROID__) + +#include + +// Uncomment these to enable debug logging from native code + +#define CARDBOARD_LOGI(...) // __android_log_print(ANDROID_LOG_INFO, "CardboardSDK", __VA_ARGS__) +#define CARDBOARD_LOGE(...) // __android_log_print(ANDROID_LOG_ERROR, "CardboardSDK", __VA_ARGS__) + +#else + +#define CARDBOARD_LOGI(...) // FURI_LOG_I("CardboardSDK", __VA_ARGS__) +#define CARDBOARD_LOGE(...) // FURI_LOG_E("CardboardSDK", __VA_ARGS__) + +#endif + +#endif // CARDBOARD_SDK_UTIL_LOGGING_H_ diff --git a/applications/plugins/airmouse/tracking/util/matrix_3x3.cc b/applications/plugins/airmouse/tracking/util/matrix_3x3.cc new file mode 100644 index 000000000..9ddd847b0 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/matrix_3x3.cc @@ -0,0 +1,121 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "matrix_3x3.h" + +namespace cardboard { + +Matrix3x3::Matrix3x3(double m00, double m01, double m02, double m10, double m11, double m12, + double m20, double m21, double m22) + : elem_ { { { m00, m01, m02 }, { m10, m11, m12 }, { m20, m21, m22 } } } +{ +} + +Matrix3x3::Matrix3x3() +{ + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + elem_[row][col] = 0; + } +} + +Matrix3x3 Matrix3x3::Zero() +{ + Matrix3x3 result; + return result; +} + +Matrix3x3 Matrix3x3::Identity() +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + result.elem_[row][row] = 1; + } + return result; +} + +void Matrix3x3::MultiplyScalar(double s) +{ + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + elem_[row][col] *= s; + } +} + +Matrix3x3 Matrix3x3::Negation() const +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result.elem_[row][col] = -elem_[row][col]; + } + return result; +} + +Matrix3x3 Matrix3x3::Scale(const Matrix3x3& m, double s) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result.elem_[row][col] = m.elem_[row][col] * s; + } + return result; +} + +Matrix3x3 Matrix3x3::Addition(const Matrix3x3& lhs, const Matrix3x3& rhs) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result.elem_[row][col] = lhs.elem_[row][col] + rhs.elem_[row][col]; + } + return result; +} + +Matrix3x3 Matrix3x3::Subtraction(const Matrix3x3& lhs, const Matrix3x3& rhs) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result.elem_[row][col] = lhs.elem_[row][col] - rhs.elem_[row][col]; + } + return result; +} + +Matrix3x3 Matrix3x3::Product(const Matrix3x3& m0, const Matrix3x3& m1) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + result.elem_[row][col] = 0; + for (int i = 0; i < 3; ++i) + result.elem_[row][col] += m0.elem_[row][i] * m1.elem_[i][col]; + } + } + return result; +} + +bool Matrix3x3::AreEqual(const Matrix3x3& m0, const Matrix3x3& m1) +{ + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + if (m0.elem_[row][col] != m1.elem_[row][col]) + return false; + } + } + return true; +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/util/matrix_3x3.h b/applications/plugins/airmouse/tracking/util/matrix_3x3.h new file mode 100644 index 000000000..81e4f2158 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/matrix_3x3.h @@ -0,0 +1,138 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_MATRIX_3X3_H_ +#define CARDBOARD_SDK_UTIL_MATRIX_3X3_H_ + +#include +#include // For memcpy(). +#include // NOLINT +#include // NOLINT + +namespace cardboard { + +// The Matrix3x3 class defines a square 3-dimensional matrix. Elements are +// stored in row-major order. +// TODO(b/135461889): Make this class consistent with Matrix4x4. +class Matrix3x3 { +public: + // The default constructor zero-initializes all elements. + Matrix3x3(); + + // Dimension-specific constructors that are passed individual element values. + Matrix3x3( + double m00, + double m01, + double m02, + double m10, + double m11, + double m12, + double m20, + double m21, + double m22); + + // Constructor that reads elements from a linear array of the correct size. + explicit Matrix3x3(const double array[3 * 3]); + + // Returns a Matrix3x3 containing all zeroes. + static Matrix3x3 Zero(); + + // Returns an identity Matrix3x3. + static Matrix3x3 Identity(); + + // Mutable element accessors. + double& operator()(int row, int col) { + return elem_[row][col]; + } + std::array& operator[](int row) { + return elem_[row]; + } + + // Read-only element accessors. + const double& operator()(int row, int col) const { + return elem_[row][col]; + } + const std::array& operator[](int row) const { + return elem_[row]; + } + + // Return a pointer to the data for interfacing with libraries. + double* Data() { + return &elem_[0][0]; + } + const double* Data() const { + return &elem_[0][0]; + } + + // Self-modifying multiplication operators. + void operator*=(double s) { + MultiplyScalar(s); + } + void operator*=(const Matrix3x3& m) { + *this = Product(*this, m); + } + + // Unary operators. + Matrix3x3 operator-() const { + return Negation(); + } + + // Binary scale operators. + friend Matrix3x3 operator*(const Matrix3x3& m, double s) { + return Scale(m, s); + } + friend Matrix3x3 operator*(double s, const Matrix3x3& m) { + return Scale(m, s); + } + + // Binary matrix addition. + friend Matrix3x3 operator+(const Matrix3x3& lhs, const Matrix3x3& rhs) { + return Addition(lhs, rhs); + } + + // Binary matrix subtraction. + friend Matrix3x3 operator-(const Matrix3x3& lhs, const Matrix3x3& rhs) { + return Subtraction(lhs, rhs); + } + + // Binary multiplication operator. + friend Matrix3x3 operator*(const Matrix3x3& m0, const Matrix3x3& m1) { + return Product(m0, m1); + } + + // Exact equality and inequality comparisons. + friend bool operator==(const Matrix3x3& m0, const Matrix3x3& m1) { + return AreEqual(m0, m1); + } + friend bool operator!=(const Matrix3x3& m0, const Matrix3x3& m1) { + return !AreEqual(m0, m1); + } + +private: + // These private functions implement most of the operators. + void MultiplyScalar(double s); + Matrix3x3 Negation() const; + static Matrix3x3 Addition(const Matrix3x3& lhs, const Matrix3x3& rhs); + static Matrix3x3 Subtraction(const Matrix3x3& lhs, const Matrix3x3& rhs); + static Matrix3x3 Scale(const Matrix3x3& m, double s); + static Matrix3x3 Product(const Matrix3x3& m0, const Matrix3x3& m1); + static bool AreEqual(const Matrix3x3& m0, const Matrix3x3& m1); + + std::array, 3> elem_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_MATRIX_3X3_H_ diff --git a/applications/plugins/airmouse/tracking/util/matrix_4x4.cc b/applications/plugins/airmouse/tracking/util/matrix_4x4.cc new file mode 100644 index 000000000..8db3cbc5b --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/matrix_4x4.cc @@ -0,0 +1,87 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "matrix_4x4.h" + +#include +#include +#include + +namespace cardboard { + +Matrix4x4 Matrix4x4::Identity() +{ + Matrix4x4 ret; + for (int j = 0; j < 4; ++j) { + for (int i = 0; i < 4; ++i) { + ret.m[j][i] = (i == j) ? 1 : 0; + } + } + + return ret; +} + +Matrix4x4 Matrix4x4::Zeros() +{ + Matrix4x4 ret; + for (int j = 0; j < 4; ++j) { + for (int i = 0; i < 4; ++i) { + ret.m[j][i] = 0; + } + } + + return ret; +} + +Matrix4x4 Matrix4x4::Translation(float x, float y, float z) +{ + Matrix4x4 ret = Matrix4x4::Identity(); + ret.m[3][0] = x; + ret.m[3][1] = y; + ret.m[3][2] = z; + + return ret; +} + +Matrix4x4 Matrix4x4::Perspective(const std::array& fov, float zNear, float zFar) +{ + Matrix4x4 ret = Matrix4x4::Zeros(); + + const float xLeft = -std::tan(fov[0] * M_PI / 180.0f) * zNear; + const float xRight = std::tan(fov[1] * M_PI / 180.0f) * zNear; + const float yBottom = -std::tan(fov[2] * M_PI / 180.0f) * zNear; + const float yTop = std::tan(fov[3] * M_PI / 180.0f) * zNear; + + const float X = (2 * zNear) / (xRight - xLeft); + const float Y = (2 * zNear) / (yTop - yBottom); + const float A = (xRight + xLeft) / (xRight - xLeft); + const float B = (yTop + yBottom) / (yTop - yBottom); + const float C = (zNear + zFar) / (zNear - zFar); + const float D = (2 * zNear * zFar) / (zNear - zFar); + + ret.m[0][0] = X; + ret.m[2][0] = A; + ret.m[1][1] = Y; + ret.m[2][1] = B; + ret.m[2][2] = C; + ret.m[3][2] = D; + ret.m[2][3] = -1; + + return ret; +} + +void Matrix4x4::ToArray(float* array) const { std::memcpy(array, &m[0][0], 16 * sizeof(float)); } + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/util/matrix_4x4.h b/applications/plugins/airmouse/tracking/util/matrix_4x4.h new file mode 100644 index 000000000..9934f6be0 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/matrix_4x4.h @@ -0,0 +1,37 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_MATRIX_4X4_H_ +#define CARDBOARD_SDK_UTIL_MATRIX_4X4_H_ + +#include + +namespace cardboard { + +class Matrix4x4 { +public: + static Matrix4x4 Identity(); + static Matrix4x4 Zeros(); + static Matrix4x4 Translation(float x, float y, float z); + static Matrix4x4 Perspective(const std::array& fov, float zNear, float zFar); + void ToArray(float* array) const; + +private: + std::array, 4> m; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_MATRIX4X4_H_ diff --git a/applications/plugins/airmouse/tracking/util/matrixutils.cc b/applications/plugins/airmouse/tracking/util/matrixutils.cc new file mode 100644 index 000000000..12470beae --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/matrixutils.cc @@ -0,0 +1,148 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "matrixutils.h" + +#include "vectorutils.h" + +namespace cardboard { + +namespace { + + // Returns true if the cofactor for a given row and column should be negated. + static bool IsCofactorNegated(int row, int col) + { + // Negated iff (row + col) is odd. + return ((row + col) & 1) != 0; + } + + static double CofactorElement3(const Matrix3x3& m, int row, int col) + { + static const int index[3][2] = { { 1, 2 }, { 0, 2 }, { 0, 1 } }; + const int i0 = index[row][0]; + const int i1 = index[row][1]; + const int j0 = index[col][0]; + const int j1 = index[col][1]; + const double cofactor = m(i0, j0) * m(i1, j1) - m(i0, j1) * m(i1, j0); + return IsCofactorNegated(row, col) ? -cofactor : cofactor; + } + + // Multiplies a matrix and some type of column vector to + // produce another column vector of the same type. + Vector3 MultiplyMatrixAndVector(const Matrix3x3& m, const Vector3& v) + { + Vector3 result = Vector3::Zero(); + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result[row] += m(row, col) * v[col]; + } + return result; + } + + // Sets the upper 3x3 of a Matrix to represent a 3D rotation. + void RotationMatrix3x3(const Rotation& r, Matrix3x3* matrix) + { + // + // Given a quaternion (a,b,c,d) where d is the scalar part, the 3x3 rotation + // matrix is: + // + // a^2 - b^2 - c^2 + d^2 2ab - 2cd 2ac + 2bd + // 2ab + 2cd -a^2 + b^2 - c^2 + d^2 2bc - 2ad + // 2ac - 2bd 2bc + 2ad -a^2 - b^2 + c^2 + d^2 + // + const Vector<4>& quat = r.GetQuaternion(); + const double aa = quat[0] * quat[0]; + const double bb = quat[1] * quat[1]; + const double cc = quat[2] * quat[2]; + const double dd = quat[3] * quat[3]; + + const double ab = quat[0] * quat[1]; + const double ac = quat[0] * quat[2]; + const double bc = quat[1] * quat[2]; + + const double ad = quat[0] * quat[3]; + const double bd = quat[1] * quat[3]; + const double cd = quat[2] * quat[3]; + + Matrix3x3& m = *matrix; + m[0][0] = aa - bb - cc + dd; + m[0][1] = 2 * ab - 2 * cd; + m[0][2] = 2 * ac + 2 * bd; + m[1][0] = 2 * ab + 2 * cd; + m[1][1] = -aa + bb - cc + dd; + m[1][2] = 2 * bc - 2 * ad; + m[2][0] = 2 * ac - 2 * bd; + m[2][1] = 2 * bc + 2 * ad; + m[2][2] = -aa - bb + cc + dd; + } + +} // anonymous namespace + +Vector3 operator*(const Matrix3x3& m, const Vector3& v) { return MultiplyMatrixAndVector(m, v); } + +Matrix3x3 CofactorMatrix(const Matrix3x3& m) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result(row, col) = CofactorElement3(m, row, col); + } + return result; +} + +Matrix3x3 AdjugateWithDeterminant(const Matrix3x3& m, double* determinant) +{ + const Matrix3x3 cofactor_matrix = CofactorMatrix(m); + if (determinant) { + *determinant = m(0, 0) * cofactor_matrix(0, 0) + m(0, 1) * cofactor_matrix(0, 1) + + m(0, 2) * cofactor_matrix(0, 2); + } + return Transpose(cofactor_matrix); +} + +// Returns the transpose of a matrix. +Matrix3x3 Transpose(const Matrix3x3& m) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result(row, col) = m(col, row); + } + return result; +} + +Matrix3x3 InverseWithDeterminant(const Matrix3x3& m, double* determinant) +{ + // The inverse is the adjugate divided by the determinant. + double det; + Matrix3x3 adjugate = AdjugateWithDeterminant(m, &det); + if (determinant) + *determinant = det; + if (det == 0) + return Matrix3x3::Zero(); + else + return adjugate * (1 / det); +} + +Matrix3x3 Inverse(const Matrix3x3& m) { return InverseWithDeterminant(m, nullptr); } + +Matrix3x3 RotationMatrixNH(const Rotation& r) +{ + Matrix3x3 m; + RotationMatrix3x3(r, &m); + return m; +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/util/matrixutils.h b/applications/plugins/airmouse/tracking/util/matrixutils.h new file mode 100644 index 000000000..80f9b2168 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/matrixutils.h @@ -0,0 +1,65 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_MATRIXUTILS_H_ +#define CARDBOARD_SDK_UTIL_MATRIXUTILS_H_ + +// +// This file contains operators and free functions that define generic Matrix +// operations. +// + +#include "matrix_3x3.h" +#include "rotation.h" +#include "vector.h" + +namespace cardboard { + +// Returns the transpose of a matrix. +Matrix3x3 Transpose(const Matrix3x3& m); + +// Multiplies a Matrix and a column Vector of the same Dimension to produce +// another column Vector. +Vector3 operator*(const Matrix3x3& m, const Vector3& v); + +// Returns the determinant of the matrix. This function is defined for all the +// typedef'ed Matrix types. +double Determinant(const Matrix3x3& m); + +// Returns the adjugate of the matrix, which is defined as the transpose of the +// cofactor matrix. This function is defined for all the typedef'ed Matrix +// types. The determinant of the matrix is computed as a side effect, so it is +// returned in the determinant parameter if it is not null. +Matrix3x3 AdjugateWithDeterminant(const Matrix3x3& m, double* determinant); + +// Returns the inverse of the matrix. This function is defined for all the +// typedef'ed Matrix types. The determinant of the matrix is computed as a +// side effect, so it is returned in the determinant parameter if it is not +// null. If the determinant is 0, the returned matrix has all zeroes. +Matrix3x3 InverseWithDeterminant(const Matrix3x3& m, double* determinant); + +// Returns the inverse of the matrix. This function is defined for all the +// typedef'ed Matrix types. If the determinant of the matrix is 0, the returned +// matrix has all zeroes. +Matrix3x3 Inverse(const Matrix3x3& m); + +// Returns a 3x3 Matrix representing a 3D rotation. This creates a Matrix that +// does not work with homogeneous coordinates, so the function name ends in +// "NH". +Matrix3x3 RotationMatrixNH(const Rotation& r); + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_MATRIXUTILS_H_ diff --git a/applications/plugins/airmouse/tracking/util/rotation.cc b/applications/plugins/airmouse/tracking/util/rotation.cc new file mode 100644 index 000000000..5c3d09a2b --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/rotation.cc @@ -0,0 +1,117 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rotation.h" + +#include +#include + +#include "vectorutils.h" + +namespace cardboard { + +void Rotation::SetAxisAndAngle(const VectorType& axis, double angle) +{ + VectorType unit_axis = axis; + if (!Normalize(&unit_axis)) { + *this = Identity(); + } else { + double a = angle / 2; + const double s = sin(a); + const VectorType v(unit_axis * s); + SetQuaternion(QuaternionType(v[0], v[1], v[2], cos(a))); + } +} + +Rotation Rotation::FromRotationMatrix(const Matrix3x3& mat) +{ + static const double kOne = 1.0; + static const double kFour = 4.0; + + const double d0 = mat(0, 0), d1 = mat(1, 1), d2 = mat(2, 2); + const double ww = kOne + d0 + d1 + d2; + const double xx = kOne + d0 - d1 - d2; + const double yy = kOne - d0 + d1 - d2; + const double zz = kOne - d0 - d1 + d2; + + const double max = std::max(ww, std::max(xx, std::max(yy, zz))); + if (ww == max) { + const double w4 = sqrt(ww * kFour); + return Rotation::FromQuaternion(QuaternionType((mat(2, 1) - mat(1, 2)) / w4, + (mat(0, 2) - mat(2, 0)) / w4, (mat(1, 0) - mat(0, 1)) / w4, w4 / kFour)); + } + + if (xx == max) { + const double x4 = sqrt(xx * kFour); + return Rotation::FromQuaternion(QuaternionType(x4 / kFour, (mat(0, 1) + mat(1, 0)) / x4, + (mat(0, 2) + mat(2, 0)) / x4, (mat(2, 1) - mat(1, 2)) / x4)); + } + + if (yy == max) { + const double y4 = sqrt(yy * kFour); + return Rotation::FromQuaternion(QuaternionType((mat(0, 1) + mat(1, 0)) / y4, y4 / kFour, + (mat(1, 2) + mat(2, 1)) / y4, (mat(0, 2) - mat(2, 0)) / y4)); + } + + // zz is the largest component. + const double z4 = sqrt(zz * kFour); + return Rotation::FromQuaternion(QuaternionType((mat(0, 2) + mat(2, 0)) / z4, + (mat(1, 2) + mat(2, 1)) / z4, z4 / kFour, (mat(1, 0) - mat(0, 1)) / z4)); +} + +void Rotation::GetAxisAndAngle(VectorType* axis, double* angle) const +{ + VectorType vec(quat_[0], quat_[1], quat_[2]); + if (Normalize(&vec)) { + *angle = 2 * acos(quat_[3]); + *axis = vec; + } else { + *axis = VectorType(1, 0, 0); + *angle = 0.0; + } +} + +Rotation Rotation::RotateInto(const VectorType& from, const VectorType& to) +{ + static const double kTolerance = std::numeric_limits::epsilon() * 100; + + // Directly build the quaternion using the following technique: + // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final + const double norm_u_norm_v = sqrt(LengthSquared(from) * LengthSquared(to)); + double real_part = norm_u_norm_v + Dot(from, to); + VectorType w; + if (real_part < kTolerance * norm_u_norm_v) { + // If |from| and |to| are exactly opposite, rotate 180 degrees around an + // arbitrary orthogonal axis. Axis normalization can happen later, when we + // normalize the quaternion. + real_part = 0.0; + w = (abs(from[0]) > abs(from[2])) ? VectorType(-from[1], from[0], 0) + : VectorType(0, -from[2], from[1]); + } else { + // Otherwise, build the quaternion the standard way. + w = Cross(from, to); + } + + // Build and return a normalized quaternion. + // Note that Rotation::FromQuaternion automatically performs normalization. + return Rotation::FromQuaternion(QuaternionType(w[0], w[1], w[2], real_part)); +} + +Rotation::VectorType Rotation::operator*(const Rotation::VectorType& v) const +{ + return ApplyToVector(v); +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/util/rotation.h b/applications/plugins/airmouse/tracking/util/rotation.h new file mode 100644 index 000000000..8730cb3b0 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/rotation.h @@ -0,0 +1,156 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_ROTATION_H_ +#define CARDBOARD_SDK_UTIL_ROTATION_H_ + +#include "matrix_3x3.h" +#include "vector.h" +#include "vectorutils.h" + +namespace cardboard { + +// The Rotation class represents a rotation around a 3-dimensional axis. It +// uses normalized quaternions internally to make the math robust. +class Rotation { +public: + // Convenience typedefs for vector of the correct type. + typedef Vector<3> VectorType; + typedef Vector<4> QuaternionType; + + // The default constructor creates an identity Rotation, which has no effect. + Rotation() { + quat_.Set(0, 0, 0, 1); + } + + // Returns an identity Rotation, which has no effect. + static Rotation Identity() { + return Rotation(); + } + + // Sets the Rotation from a quaternion (4D vector), which is first normalized. + void SetQuaternion(const QuaternionType& quaternion) { + quat_ = Normalized(quaternion); + } + + // Returns the Rotation as a normalized quaternion (4D vector). + const QuaternionType& GetQuaternion() const { + return quat_; + } + + // Sets the Rotation to rotate by the given angle around the given axis, + // following the right-hand rule. The axis does not need to be unit + // length. If it is zero length, this results in an identity Rotation. + void SetAxisAndAngle(const VectorType& axis, double angle); + + // Returns the right-hand rule axis and angle corresponding to the + // Rotation. If the Rotation is the identity rotation, this returns the +X + // axis and an angle of 0. + void GetAxisAndAngle(VectorType* axis, double* angle) const; + + // Convenience function that constructs and returns a Rotation given an axis + // and angle. + static Rotation FromAxisAndAngle(const VectorType& axis, double angle) { + Rotation r; + r.SetAxisAndAngle(axis, angle); + return r; + } + + // Convenience function that constructs and returns a Rotation given a + // quaternion. + static Rotation FromQuaternion(const QuaternionType& quat) { + Rotation r; + r.SetQuaternion(quat); + return r; + } + + // Convenience function that constructs and returns a Rotation given a + // rotation matrix R with $R^\top R = I && det(R) = 1$. + static Rotation FromRotationMatrix(const Matrix3x3& mat); + + // Convenience function that constructs and returns a Rotation given Euler + // angles that are applied in the order of rotate-Z by roll, rotate-X by + // pitch, rotate-Y by yaw (same as GetRollPitchYaw). + static Rotation FromRollPitchYaw(double roll, double pitch, double yaw) { + VectorType x(1, 0, 0), y(0, 1, 0), z(0, 0, 1); + return FromAxisAndAngle(z, roll) * (FromAxisAndAngle(x, pitch) * FromAxisAndAngle(y, yaw)); + } + + // Convenience function that constructs and returns a Rotation given Euler + // angles that are applied in the order of rotate-Y by yaw, rotate-X by + // pitch, rotate-Z by roll (same as GetYawPitchRoll). + static Rotation FromYawPitchRoll(double yaw, double pitch, double roll) { + VectorType x(1, 0, 0), y(0, 1, 0), z(0, 0, 1); + return FromAxisAndAngle(y, yaw) * (FromAxisAndAngle(x, pitch) * FromAxisAndAngle(z, roll)); + } + + // Constructs and returns a Rotation that rotates one vector to another along + // the shortest arc. This returns an identity rotation if either vector has + // zero length. + static Rotation RotateInto(const VectorType& from, const VectorType& to); + + // The negation operator returns the inverse rotation. + friend Rotation operator-(const Rotation& r) { + // Because we store normalized quaternions, the inverse is found by + // negating the vector part. + return Rotation(-r.quat_[0], -r.quat_[1], -r.quat_[2], r.quat_[3]); + } + + // Appends a rotation to this one. + Rotation& operator*=(const Rotation& r) { + const QuaternionType& qr = r.quat_; + QuaternionType& qt = quat_; + SetQuaternion(QuaternionType( + qr[3] * qt[0] + qr[0] * qt[3] + qr[2] * qt[1] - qr[1] * qt[2], + qr[3] * qt[1] + qr[1] * qt[3] + qr[0] * qt[2] - qr[2] * qt[0], + qr[3] * qt[2] + qr[2] * qt[3] + qr[1] * qt[0] - qr[0] * qt[1], + qr[3] * qt[3] - qr[0] * qt[0] - qr[1] * qt[1] - qr[2] * qt[2])); + return *this; + } + + // Binary multiplication operator - returns a composite Rotation. + friend const Rotation operator*(const Rotation& r0, const Rotation& r1) { + Rotation r = r0; + r *= r1; + return r; + } + + // Multiply a Rotation and a Vector to get a Vector. + VectorType operator*(const VectorType& v) const; + +private: + // Private constructor that builds a Rotation from quaternion components. + Rotation(double q0, double q1, double q2, double q3) + : quat_(q0, q1, q2, q3) { + } + + // Applies a Rotation to a Vector to rotate the Vector. Method borrowed from: + // http://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/ + VectorType ApplyToVector(const VectorType& v) const { + VectorType im(quat_[0], quat_[1], quat_[2]); + VectorType temp = 2.0 * Cross(im, v); + return v + quat_[3] * temp + Cross(im, temp); + } + + // The rotation represented as a normalized quaternion. (Unit quaternions are + // required for constructing rotation matrices, so it makes sense to always + // store them that way.) The vector part is in the first 3 elements, and the + // scalar part is in the last element. + QuaternionType quat_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_ROTATION_H_ diff --git a/applications/plugins/airmouse/tracking/util/vector.h b/applications/plugins/airmouse/tracking/util/vector.h new file mode 100644 index 000000000..64c4f2546 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/vector.h @@ -0,0 +1,251 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_VECTOR_H_ +#define CARDBOARD_SDK_UTIL_VECTOR_H_ + +#include + +namespace cardboard { + +// Geometric N-dimensional Vector class. +template +class Vector { +public: + // The default constructor zero-initializes all elements. + Vector(); + + // Dimension-specific constructors that are passed individual element values. + constexpr Vector(double e0, double e1, double e2); + constexpr Vector(double e0, double e1, double e2, double e3); + + // Constructor for a Vector of dimension N from a Vector of dimension N-1 and + // a scalar of the correct type, assuming N is at least 2. + // constexpr Vector(const Vector& v, double s); + + void Set(double e0, double e1, double e2); // Only when Dimension == 3. + void Set(double e0, double e1, double e2, + double e3); // Only when Dimension == 4. + + // Mutable element accessor. + double& operator[](int index) { + return elem_[index]; + } + + // Element accessor. + double operator[](int index) const { + return elem_[index]; + } + + // Returns a Vector containing all zeroes. + static Vector Zero(); + + // Self-modifying operators. + void operator+=(const Vector& v) { + Add(v); + } + void operator-=(const Vector& v) { + Subtract(v); + } + void operator*=(double s) { + Multiply(s); + } + void operator/=(double s) { + Divide(s); + } + + // Unary negation operator. + Vector operator-() const { + return Negation(); + } + + // Binary operators. + friend Vector operator+(const Vector& v0, const Vector& v1) { + return Sum(v0, v1); + } + friend Vector operator-(const Vector& v0, const Vector& v1) { + return Difference(v0, v1); + } + friend Vector operator*(const Vector& v, double s) { + return Scale(v, s); + } + friend Vector operator*(double s, const Vector& v) { + return Scale(v, s); + } + friend Vector operator*(const Vector& v, const Vector& s) { + return Product(v, s); + } + friend Vector operator/(const Vector& v, double s) { + return Divide(v, s); + } + + // Self-modifying addition. + void Add(const Vector& v); + // Self-modifying subtraction. + void Subtract(const Vector& v); + // Self-modifying multiplication by a scalar. + void Multiply(double s); + // Self-modifying division by a scalar. + void Divide(double s); + + // Unary negation. + Vector Negation() const; + + // Binary component-wise multiplication. + static Vector Product(const Vector& v0, const Vector& v1); + // Binary component-wise addition. + static Vector Sum(const Vector& v0, const Vector& v1); + // Binary component-wise subtraction. + static Vector Difference(const Vector& v0, const Vector& v1); + // Binary multiplication by a scalar. + static Vector Scale(const Vector& v, double s); + // Binary division by a scalar. + static Vector Divide(const Vector& v, double s); + +private: + std::array elem_; +}; +//------------------------------------------------------------------------------ + +template +Vector::Vector() { + for(int i = 0; i < Dimension; i++) { + elem_[i] = 0; + } +} + +template +constexpr Vector::Vector(double e0, double e1, double e2) + : elem_{e0, e1, e2} { +} + +template +constexpr Vector::Vector(double e0, double e1, double e2, double e3) + : elem_{e0, e1, e2, e3} { +} +/* +template <> +constexpr Vector<4>::Vector(const Vector<3>& v, double s) + : elem_{v[0], v[1], v[2], s} {} +*/ +template +void Vector::Set(double e0, double e1, double e2) { + elem_[0] = e0; + elem_[1] = e1; + elem_[2] = e2; +} + +template +void Vector::Set(double e0, double e1, double e2, double e3) { + elem_[0] = e0; + elem_[1] = e1; + elem_[2] = e2; + elem_[3] = e3; +} + +template +Vector Vector::Zero() { + Vector v; + return v; +} + +template +void Vector::Add(const Vector& v) { + for(int i = 0; i < Dimension; i++) { + elem_[i] += v[i]; + } +} + +template +void Vector::Subtract(const Vector& v) { + for(int i = 0; i < Dimension; i++) { + elem_[i] -= v[i]; + } +} + +template +void Vector::Multiply(double s) { + for(int i = 0; i < Dimension; i++) { + elem_[i] *= s; + } +} + +template +void Vector::Divide(double s) { + for(int i = 0; i < Dimension; i++) { + elem_[i] /= s; + } +} + +template +Vector Vector::Negation() const { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = -elem_[i]; + } + return ret; +} + +template +Vector Vector::Product(const Vector& v0, const Vector& v1) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v0[i] * v1[i]; + } + return ret; +} + +template +Vector Vector::Sum(const Vector& v0, const Vector& v1) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v0[i] + v1[i]; + } + return ret; +} + +template +Vector Vector::Difference(const Vector& v0, const Vector& v1) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v0[i] - v1[i]; + } + return ret; +} + +template +Vector Vector::Scale(const Vector& v, double s) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v[i] * s; + } + return ret; +} + +template +Vector Vector::Divide(const Vector& v, double s) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v[i] / s; + } + return ret; +} + +typedef Vector<3> Vector3; +typedef Vector<4> Vector4; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_VECTOR_H_ diff --git a/applications/plugins/airmouse/tracking/util/vectorutils.cc b/applications/plugins/airmouse/tracking/util/vectorutils.cc new file mode 100644 index 000000000..b8f419c04 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/vectorutils.cc @@ -0,0 +1,40 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "vectorutils.h" + +namespace cardboard { + +// Returns the dot (inner) product of two Vectors. +double Dot(const Vector<3>& v0, const Vector<3>& v1) +{ + return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2]; +} + +// Returns the dot (inner) product of two Vectors. +double Dot(const Vector<4>& v0, const Vector<4>& v1) +{ + return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2] + v0[3] * v1[3]; +} + +// Returns the 3-dimensional cross product of 2 Vectors. Note that this is +// defined only for 3-dimensional Vectors. +Vector<3> Cross(const Vector<3>& v0, const Vector<3>& v1) +{ + return Vector<3>(v0[1] * v1[2] - v0[2] * v1[1], v0[2] * v1[0] - v0[0] * v1[2], + v0[0] * v1[1] - v0[1] * v1[0]); +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/util/vectorutils.h b/applications/plugins/airmouse/tracking/util/vectorutils.h new file mode 100644 index 000000000..054236713 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/vectorutils.h @@ -0,0 +1,76 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_VECTORUTILS_H_ +#define CARDBOARD_SDK_UTIL_VECTORUTILS_H_ + +// +// This file contains free functions that operate on Vector instances. +// + +#include + +#include "vector.h" + +namespace cardboard { + +// Returns the dot (inner) product of two Vectors. +double Dot(const Vector<3>& v0, const Vector<3>& v1); + +// Returns the dot (inner) product of two Vectors. +double Dot(const Vector<4>& v0, const Vector<4>& v1); + +// Returns the 3-dimensional cross product of 2 Vectors. Note that this is +// defined only for 3-dimensional Vectors. +Vector<3> Cross(const Vector<3>& v0, const Vector<3>& v1); + +// Returns the square of the length of a Vector. +template +double LengthSquared(const Vector& v) { + return Dot(v, v); +} + +// Returns the geometric length of a Vector. +template +double Length(const Vector& v) { + return sqrt(LengthSquared(v)); +} + +// the Vector untouched and returns false. +template +bool Normalize(Vector* v) { + const double len = Length(*v); + if(len == 0) { + return false; + } else { + (*v) /= len; + return true; + } +} + +// Returns a unit-length version of a Vector. If the given Vector has no +// length, this returns a Zero() Vector. +template +Vector Normalized(const Vector& v) { + Vector result = v; + if(Normalize(&result)) + return result; + else + return Vector::Zero(); +} + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_VECTORUTILS_H_ diff --git a/applications/plugins/airmouse/views/bt_mouse.c b/applications/plugins/airmouse/views/bt_mouse.c new file mode 100644 index 000000000..1a6ea3020 --- /dev/null +++ b/applications/plugins/airmouse/views/bt_mouse.c @@ -0,0 +1,157 @@ +#include "bt_mouse.h" +#include "../tracking/main_loop.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +struct BtMouse { + View* view; + ViewDispatcher* view_dispatcher; + Bt* bt; + NotificationApp* notifications; +}; + +#define MOUSE_MOVE_SHORT 5 +#define MOUSE_MOVE_LONG 20 + +static void bt_mouse_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "Bluetooth Mouse mode"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 63, "Hold [back] to exit"); +} + +static void bt_mouse_process(BtMouse* bt_mouse, InputEvent* event) { + with_view_model( + bt_mouse->view, + void* model, + { + UNUSED(model); + if(event->key == InputKeyUp) { + if(event->type == InputTypePress) { + furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); + } else if(event->type == InputTypeRelease) { + furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); + } + } else if(event->key == InputKeyDown) { + if(event->type == InputTypePress) { + furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_RIGHT); + } else if(event->type == InputTypeRelease) { + furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_RIGHT); + } + } else if(event->key == InputKeyOk) { + if(event->type == InputTypePress) { + furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_WHEEL); + } else if(event->type == InputTypeRelease) { + furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_WHEEL); + } + } + }, + true); +} + +static bool bt_mouse_input_callback(InputEvent* event, void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + furi_hal_bt_hid_mouse_release_all(); + } else { + bt_mouse_process(bt_mouse, event); + consumed = true; + } + + return consumed; +} + +void bt_mouse_connection_status_changed_callback(BtStatus status, void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + + bool connected = (status == BtStatusConnected); + if(connected) { + notification_internal_message(bt_mouse->notifications, &sequence_set_blue_255); + } else { + notification_internal_message(bt_mouse->notifications, &sequence_reset_blue); + } + + //with_view_model( + // bt_mouse->view, void * model, { model->connected = connected; }, true); +} + +void bt_mouse_enter_callback(void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + + bt_mouse->bt = furi_record_open(RECORD_BT); + bt_mouse->notifications = furi_record_open(RECORD_NOTIFICATION); + bt_set_status_changed_callback( + bt_mouse->bt, bt_mouse_connection_status_changed_callback, bt_mouse); + furi_assert(bt_set_profile(bt_mouse->bt, BtProfileHidKeyboard)); + furi_hal_bt_start_advertising(); + + tracking_begin(); + + view_dispatcher_send_custom_event(bt_mouse->view_dispatcher, 0); +} + +bool bt_mouse_custom_callback(uint32_t event, void* context) { + UNUSED(event); + furi_assert(context); + BtMouse* bt_mouse = context; + + tracking_step(furi_hal_bt_hid_mouse_move); + + view_dispatcher_send_custom_event(bt_mouse->view_dispatcher, 0); + return true; +} + +void bt_mouse_exit_callback(void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + + tracking_end(); + + notification_internal_message(bt_mouse->notifications, &sequence_reset_blue); + + furi_hal_bt_stop_advertising(); + bt_set_profile(bt_mouse->bt, BtProfileSerial); + + furi_record_close(RECORD_NOTIFICATION); + bt_mouse->notifications = NULL; + furi_record_close(RECORD_BT); + bt_mouse->bt = NULL; +} + +BtMouse* bt_mouse_alloc(ViewDispatcher* view_dispatcher) { + BtMouse* bt_mouse = malloc(sizeof(BtMouse)); + bt_mouse->view = view_alloc(); + bt_mouse->view_dispatcher = view_dispatcher; + view_set_context(bt_mouse->view, bt_mouse); + view_set_draw_callback(bt_mouse->view, bt_mouse_draw_callback); + view_set_input_callback(bt_mouse->view, bt_mouse_input_callback); + view_set_enter_callback(bt_mouse->view, bt_mouse_enter_callback); + view_set_custom_callback(bt_mouse->view, bt_mouse_custom_callback); + view_set_exit_callback(bt_mouse->view, bt_mouse_exit_callback); + return bt_mouse; +} + +void bt_mouse_free(BtMouse* bt_mouse) { + furi_assert(bt_mouse); + view_free(bt_mouse->view); + free(bt_mouse); +} + +View* bt_mouse_get_view(BtMouse* bt_mouse) { + furi_assert(bt_mouse); + return bt_mouse->view; +} diff --git a/applications/plugins/airmouse/views/bt_mouse.h b/applications/plugins/airmouse/views/bt_mouse.h new file mode 100644 index 000000000..09153d8fa --- /dev/null +++ b/applications/plugins/airmouse/views/bt_mouse.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +typedef struct BtMouse BtMouse; + +BtMouse* bt_mouse_alloc(ViewDispatcher* view_dispatcher); + +void bt_mouse_free(BtMouse* bt_mouse); + +View* bt_mouse_get_view(BtMouse* bt_mouse); + +void bt_mouse_set_connected_status(BtMouse* bt_mouse, bool connected); diff --git a/applications/plugins/airmouse/views/calibration.c b/applications/plugins/airmouse/views/calibration.c new file mode 100644 index 000000000..a92f68be4 --- /dev/null +++ b/applications/plugins/airmouse/views/calibration.c @@ -0,0 +1,69 @@ +#include "calibration.h" +#include "../tracking/main_loop.h" +#include "../air_mouse.h" + +#include +#include + +struct Calibration { + View* view; + ViewDispatcher* view_dispatcher; +}; + +static void calibration_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "Calibrating..."); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 63, "Please wait"); +} + +void calibration_enter_callback(void* context) { + furi_assert(context); + Calibration* calibration = context; + calibration_begin(); + view_dispatcher_send_custom_event(calibration->view_dispatcher, 0); +} + +bool calibration_custom_callback(uint32_t event, void* context) { + UNUSED(event); + furi_assert(context); + Calibration* calibration = context; + + if(calibration_step()) { + view_dispatcher_switch_to_view(calibration->view_dispatcher, AirMouseViewSubmenu); + } else { + view_dispatcher_send_custom_event(calibration->view_dispatcher, 0); + } + + return true; +} + +void calibration_exit_callback(void* context) { + furi_assert(context); + calibration_end(); +} + +Calibration* calibration_alloc(ViewDispatcher* view_dispatcher) { + Calibration* calibration = malloc(sizeof(Calibration)); + calibration->view = view_alloc(); + calibration->view_dispatcher = view_dispatcher; + view_set_context(calibration->view, calibration); + view_set_draw_callback(calibration->view, calibration_draw_callback); + view_set_enter_callback(calibration->view, calibration_enter_callback); + view_set_custom_callback(calibration->view, calibration_custom_callback); + view_set_exit_callback(calibration->view, calibration_exit_callback); + return calibration; +} + +void calibration_free(Calibration* calibration) { + furi_assert(calibration); + view_free(calibration->view); + free(calibration); +} + +View* calibration_get_view(Calibration* calibration) { + furi_assert(calibration); + return calibration->view; +} diff --git a/applications/plugins/airmouse/views/calibration.h b/applications/plugins/airmouse/views/calibration.h new file mode 100644 index 000000000..da44ce0cd --- /dev/null +++ b/applications/plugins/airmouse/views/calibration.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +typedef struct Calibration Calibration; + +Calibration* calibration_alloc(ViewDispatcher* view_dispatcher); + +void calibration_free(Calibration* calibration); + +View* calibration_get_view(Calibration* calibration); diff --git a/applications/plugins/airmouse/views/usb_mouse.c b/applications/plugins/airmouse/views/usb_mouse.c new file mode 100644 index 000000000..2d5f1b0a9 --- /dev/null +++ b/applications/plugins/airmouse/views/usb_mouse.c @@ -0,0 +1,124 @@ +#include "usb_mouse.h" +#include "../tracking/main_loop.h" + +#include +#include +#include +#include + +struct UsbMouse { + View* view; + ViewDispatcher* view_dispatcher; + FuriHalUsbInterface* usb_mode_prev; +}; + +static void usb_mouse_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "USB Mouse mode"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 63, "Hold [back] to exit"); +} + +static void usb_mouse_process(UsbMouse* usb_mouse, InputEvent* event) { + with_view_model( + usb_mouse->view, + void* model, + { + UNUSED(model); + if(event->key == InputKeyUp) { + if(event->type == InputTypePress) { + furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT); + } else if(event->type == InputTypeRelease) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); + } + } else if(event->key == InputKeyDown) { + if(event->type == InputTypePress) { + furi_hal_hid_mouse_press(HID_MOUSE_BTN_RIGHT); + } else if(event->type == InputTypeRelease) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT); + } + } else if(event->key == InputKeyOk) { + if(event->type == InputTypePress) { + furi_hal_hid_mouse_press(HID_MOUSE_BTN_WHEEL); + } else if(event->type == InputTypeRelease) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_WHEEL); + } + } + }, + true); +} + +static bool usb_mouse_input_callback(InputEvent* event, void* context) { + furi_assert(context); + UsbMouse* usb_mouse = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + // furi_hal_hid_mouse_release_all(); + } else { + usb_mouse_process(usb_mouse, event); + consumed = true; + } + + return consumed; +} + +void usb_mouse_enter_callback(void* context) { + furi_assert(context); + UsbMouse* usb_mouse = context; + + usb_mouse->usb_mode_prev = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); + + tracking_begin(); + + view_dispatcher_send_custom_event(usb_mouse->view_dispatcher, 0); +} + +bool usb_mouse_custom_callback(uint32_t event, void* context) { + UNUSED(event); + furi_assert(context); + UsbMouse* usb_mouse = context; + + tracking_step(furi_hal_hid_mouse_move); + furi_delay_ms(1); // Magic! Removing this will break the buttons + + view_dispatcher_send_custom_event(usb_mouse->view_dispatcher, 0); + return true; +} + +void usb_mouse_exit_callback(void* context) { + furi_assert(context); + UsbMouse* usb_mouse = context; + + tracking_end(); + + furi_hal_usb_set_config(usb_mouse->usb_mode_prev, NULL); +} + +UsbMouse* usb_mouse_alloc(ViewDispatcher* view_dispatcher) { + UsbMouse* usb_mouse = malloc(sizeof(UsbMouse)); + usb_mouse->view = view_alloc(); + usb_mouse->view_dispatcher = view_dispatcher; + view_set_context(usb_mouse->view, usb_mouse); + view_set_draw_callback(usb_mouse->view, usb_mouse_draw_callback); + view_set_input_callback(usb_mouse->view, usb_mouse_input_callback); + view_set_enter_callback(usb_mouse->view, usb_mouse_enter_callback); + view_set_custom_callback(usb_mouse->view, usb_mouse_custom_callback); + view_set_exit_callback(usb_mouse->view, usb_mouse_exit_callback); + return usb_mouse; +} + +void usb_mouse_free(UsbMouse* usb_mouse) { + furi_assert(usb_mouse); + view_free(usb_mouse->view); + free(usb_mouse); +} + +View* usb_mouse_get_view(UsbMouse* usb_mouse) { + furi_assert(usb_mouse); + return usb_mouse->view; +} diff --git a/applications/plugins/airmouse/views/usb_mouse.h b/applications/plugins/airmouse/views/usb_mouse.h new file mode 100644 index 000000000..5ce589a69 --- /dev/null +++ b/applications/plugins/airmouse/views/usb_mouse.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +typedef struct UsbMouse UsbMouse; + +UsbMouse* usb_mouse_alloc(ViewDispatcher* view_dispatcher); + +void usb_mouse_free(UsbMouse* usb_mouse); + +View* usb_mouse_get_view(UsbMouse* usb_mouse);