From 2efea841188eb836611eabb5cc2df4aa7e3e93ea Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Thu, 28 May 2026 10:03:06 +0800 Subject: [PATCH 01/11] fix(APPLaunch): add missing pkg_config helpers and HAL_PLATFORM_SDL for x86 SDL2 --- projects/APPLaunch/main/SConstruct | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/projects/APPLaunch/main/SConstruct b/projects/APPLaunch/main/SConstruct index 55659ce3..149bbf37 100644 --- a/projects/APPLaunch/main/SConstruct +++ b/projects/APPLaunch/main/SConstruct @@ -9,6 +9,18 @@ Import('env') with open(env['PROJECT_TOOL_S']) as f: exec(f.read()) +def pkg_config_cflags(pkg): + try: + return subprocess.check_output(['pkg-config', '--cflags', pkg], text=True).split() + except Exception: + return [] + +def pkg_config_ldflags(pkg): + try: + return subprocess.check_output(['pkg-config', '--libs', pkg], text=True).split() + except Exception: + return [] + rootfs_path=os.environ.get("CONFIG_TOOLCHAIN_SYSROOT", "") def wget_github(url): @@ -107,6 +119,8 @@ lvgl_component['SRCS'] = list(filter( SRCS += Glob('hal/*.c*') # x86 if 'linux_x86_sdl2_config_defaults.mk' in os.environ.get("CONFIG_DEFAULT_FILE", ''): + DEFINITIONS += ['-DHAL_PLATFORM_SDL=1'] + lvgl_component['DEFINITIONS'] += pkg_config_cflags("freetype2") lvgl_component['REQUIREMENTS'] += pkg_config_ldflags("freetype2") From be589a7b82786cce57c68e7fab6459774699e24a Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Thu, 28 May 2026 14:06:28 +0800 Subject: [PATCH 02/11] Add Recorder app with audio record/playback support Features: - Record audio via miniaudio (PulseAudio backend) - Playback recorded WAV files - LVGL-based UI with keyboard controls - File browser for recordings in ~/Music/Recorder Architecture: - AudioEngine: PIMPL wrapper around miniaudio - RecorderApp: application state and file management - UiRecorder: LVGL UI layer --- projects/Recorder/SConstruct | 71 ++++ .../Recorder/applications/Recorder.desktop | 6 + projects/Recorder/config_defaults.mk | 12 + projects/Recorder/main/SConstruct | 77 +++++ projects/Recorder/main/include/audio_engine.h | 58 ++++ .../Recorder/main/include/compat/input_keys.h | 82 +++++ .../Recorder/main/include/keyboard_input.h | 46 +++ projects/Recorder/main/include/recorder_app.h | 62 ++++ projects/Recorder/main/include/ui_recorder.h | 30 ++ projects/Recorder/main/include/wav_file.h | 13 + projects/Recorder/main/src/audio_engine.cpp | 318 ++++++++++++++++++ projects/Recorder/main/src/main.cpp | 227 +++++++++++++ projects/Recorder/main/src/recorder_app.cpp | 265 +++++++++++++++ projects/Recorder/main/src/ui_recorder.cpp | 131 ++++++++ projects/Recorder/main/src/wav_file.cpp | 72 ++++ projects/Recorder/share/images/Recorder.png | Bin 0 -> 4215 bytes 16 files changed, 1470 insertions(+) create mode 100644 projects/Recorder/SConstruct create mode 100644 projects/Recorder/applications/Recorder.desktop create mode 100644 projects/Recorder/config_defaults.mk create mode 100644 projects/Recorder/main/SConstruct create mode 100644 projects/Recorder/main/include/audio_engine.h create mode 100644 projects/Recorder/main/include/compat/input_keys.h create mode 100644 projects/Recorder/main/include/keyboard_input.h create mode 100644 projects/Recorder/main/include/recorder_app.h create mode 100644 projects/Recorder/main/include/ui_recorder.h create mode 100644 projects/Recorder/main/include/wav_file.h create mode 100644 projects/Recorder/main/src/audio_engine.cpp create mode 100644 projects/Recorder/main/src/main.cpp create mode 100644 projects/Recorder/main/src/recorder_app.cpp create mode 100644 projects/Recorder/main/src/ui_recorder.cpp create mode 100644 projects/Recorder/main/src/wav_file.cpp create mode 100644 projects/Recorder/share/images/Recorder.png diff --git a/projects/Recorder/SConstruct b/projects/Recorder/SConstruct new file mode 100644 index 00000000..1e5c1ff5 --- /dev/null +++ b/projects/Recorder/SConstruct @@ -0,0 +1,71 @@ +from pathlib import Path +import os +import platform +import shutil +import sys + +arch = platform.machine() +homebrew_toolchain = "/opt/homebrew/bin" +has_homebrew_aarch64 = os.path.exists(os.path.join(homebrew_toolchain, "aarch64-linux-gnu-gcc")) + +version = "v0.0.3" +static_lib = "static_lib" +update = False + +if "CardputerZero" in os.environ: + if not os.path.exists("build/config/config_tmp.mk"): + os.makedirs("build/config", exist_ok=True) + with open("build/config/config_tmp.mk", "w") as f: + f.write("CONFIG_V9_5_LV_USE_LINUX_FBDEV=y\n") + f.write("CONFIG_V9_5_LV_DRAW_SW_ASM_NEON=y\n") + f.write("CONFIG_V9_5_LV_USE_DRAW_SW_ASM=1\n") + f.write("CONFIG_V9_5_LV_USE_EVDEV=y\n") + if has_homebrew_aarch64: + f.write(f'CONFIG_TOOLCHAIN_PATH="{homebrew_toolchain}"\n') + f.write('CONFIG_TOOLCHAIN_PREFIX="aarch64-linux-gnu-"\n') + f.write(f'''CONFIG_TOOLCHAIN_SYSROOT="{os.path.join(sys.path[0], "static_lib")}"\n''') +elif arch != "aarch64": + if not os.path.exists("build/config/config_tmp.mk"): + os.makedirs("build/config", exist_ok=True) + with open("build/config/config_tmp.mk", "w") as f: + f.write("CONFIG_V9_5_LV_USE_SDL=y\n") +else: + if not os.path.exists("build/config/config_tmp.mk"): + os.makedirs("build/config", exist_ok=True) + with open("build/config/config_tmp.mk", "w") as f: + f.write("CONFIG_V9_5_LV_USE_LINUX_FBDEV=y\n") + f.write("CONFIG_V9_5_LV_DRAW_SW_ASM_NEON=y\n") + f.write("CONFIG_V9_5_LV_USE_DRAW_SW_ASM=1\n") + f.write("CONFIG_V9_5_LV_USE_EVDEV=y\n") + +local_path = Path(os.getcwd()) +sdk_path = local_path.parent.parent / "SDK" +os.environ["SDK_PATH"] = str(sdk_path) +os.environ["EXT_COMPONENTS_PATH"] = str(sdk_path.parent / "ext_components") + +env = SConscript( + str(sdk_path / "tools" / "scons" / "project.py"), + variant_dir=os.getcwd(), + duplicate=0, +) + +if not os.path.exists(static_lib): + update = True +else: + try: + with open(str(Path(static_lib) / "version"), "r") as f: + if version != f.read().strip(): + update = True + except Exception: + update = True + +if update and "CardputerZero" in os.environ: + with open(env["PROJECT_TOOL_S"]) as f: + exec(f.read()) + down_url = "https://github.com/dianjixz/M5CardputerZero-UserDemo/releases/download/{}/sdk_bsp.tar.gz".format( + version + ) + down_path = check_wget_down(down_url, "static_lib_{}.tar.gz".format(version)) + if os.path.exists(static_lib): + shutil.rmtree(static_lib) + shutil.move(down_path, static_lib) diff --git a/projects/Recorder/applications/Recorder.desktop b/projects/Recorder/applications/Recorder.desktop new file mode 100644 index 00000000..4244ceea --- /dev/null +++ b/projects/Recorder/applications/Recorder.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=Recorder +Exec=/usr/share/APPLaunch/bin/M5CardputerZero-Recorder +Terminal=false +Icon=share/images/Recorder.png +Type=Application diff --git a/projects/Recorder/config_defaults.mk b/projects/Recorder/config_defaults.mk new file mode 100644 index 00000000..743de6c3 --- /dev/null +++ b/projects/Recorder/config_defaults.mk @@ -0,0 +1,12 @@ +CONFIG_LVGL_COMPONENT_ENABLED=y +CONFIG_LVGL_9_5_SRC=y +CONFIG_V9_5_LV_USE_CLIB_MALLOC=y +CONFIG_V9_5_LV_USE_CLIB_STRING=y +CONFIG_V9_5_LV_USE_CLIB_SPRINTF=y + +CONFIG_V9_5_LV_FONT_MONTSERRAT_10=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_12=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_14=y +CONFIG_V9_5_LV_FONT_DEFAULT_MONTSERRAT_14=y + +CONFIG_MINIAUDIO_COMPONENT_ENABLED=y diff --git a/projects/Recorder/main/SConstruct b/projects/Recorder/main/SConstruct new file mode 100644 index 00000000..bd4c4dda --- /dev/null +++ b/projects/Recorder/main/SConstruct @@ -0,0 +1,77 @@ +import os + +Import("env") +with open(env["PROJECT_TOOL_S"]) as f: + exec(f.read()) + +if "CONFIG_TOOLCHAIN_PREFIX" in os.environ: + env["RANLIB"] = '${_concat(GCCPREFIX, "ranlib", GCCSUFFIX, __env__)}' + env["RANLIBCOM"] = "$RANLIB $TARGET" + env["ARFLAGS"] = ["rcs"] + +SRCS = Glob("src/*.c*") +INCLUDE = [ADir("."), ADir("include")] +PRIVATE_INCLUDE = [] +REQUIREMENTS = ["lvgl_component", "pthread", "Miniaudio"] +if "CONFIG_V9_5_LV_USE_SDL" not in os.environ: + REQUIREMENTS += ["input", "xkbcommon", "udev", "tinyalsa_component"] +STATIC_LIB = [] +DYNAMIC_LIB = [] +DEFINITIONS = [] +DEFINITIONS_PRIVATE = [] +LDFLAGS = [] +LINK_SEARCH_PATH = [] +STATIC_FILES = [] +MULTIARCH_INCLUDE = None + +if "CONFIG_TOOLCHAIN_SYSROOT" in os.environ: + sysroot = os.environ["CONFIG_TOOLCHAIN_SYSROOT"].strip('"') + multiarch_include = os.path.join(sysroot, "usr", "include", "aarch64-linux-gnu") + if os.path.isdir(multiarch_include): + INCLUDE += [multiarch_include] + MULTIARCH_INCLUDE = multiarch_include + multiarch_lib = os.path.join(sysroot, "usr", "lib", "aarch64-linux-gnu") + if os.path.isdir(multiarch_lib): + LDFLAGS += [f"-B{multiarch_lib}", f"-Wl,-rpath-link,{multiarch_lib}"] + LINK_SEARCH_PATH += [multiarch_lib] + +lvgl_component = list(filter(lambda x: x["target"] == "lvgl_component", env["COMPONENTS"]))[0] +if MULTIARCH_INCLUDE: + for component in env["COMPONENTS"]: + component["INCLUDE"] += [MULTIARCH_INCLUDE] +if "CONFIG_V9_5_LV_USE_SDL" in os.environ: + lvgl_component["REQUIREMENTS"] += ["SDL2"] + lvgl_component["INCLUDE"] += ["/usr/include/SDL2"] + for sdl_include in [ + "/opt/homebrew/include", + "/opt/homebrew/include/SDL2", + "/usr/local/include", + "/usr/local/include/SDL2", + ]: + if os.path.exists(sdl_include): + lvgl_component["INCLUDE"] += [sdl_include] + for sdl_lib in ["/opt/homebrew/lib", "/usr/local/lib"]: + if os.path.exists(sdl_lib): + lvgl_component["LINK_SEARCH_PATH"] += [sdl_lib] + lvgl_component["DEFINITIONS"] += ["-D_REENTRANT"] + for src in list(lvgl_component["SRCS"]): + if "lv_sdl_keyboard.c" in str(src): + lvgl_component["SRCS"].remove(src) + +env["COMPONENTS"].append( + { + "target": "M5CardputerZero-Recorder", + "SRCS": SRCS, + "INCLUDE": INCLUDE, + "PRIVATE_INCLUDE": PRIVATE_INCLUDE, + "REQUIREMENTS": REQUIREMENTS, + "STATIC_LIB": STATIC_LIB, + "DYNAMIC_LIB": DYNAMIC_LIB, + "DEFINITIONS": DEFINITIONS, + "DEFINITIONS_PRIVATE": DEFINITIONS_PRIVATE, + "LDFLAGS": LDFLAGS, + "LINK_SEARCH_PATH": LINK_SEARCH_PATH, + "STATIC_FILES": STATIC_FILES, + "REGISTER": "project", + } +) diff --git a/projects/Recorder/main/include/audio_engine.h b/projects/Recorder/main/include/audio_engine.h new file mode 100644 index 00000000..8bafd1a8 --- /dev/null +++ b/projects/Recorder/main/include/audio_engine.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include +#include + +// miniaudio forward-declares + types (header-only, declarations are lightweight) +#include "miniaudio.h" + +enum class AudioState { + Idle, + Recording, + RecPaused, + Playing, + PlayPaused, +}; + +class AudioEngineImpl; + +class AudioEngine { +public: + AudioEngine(); + ~AudioEngine(); + + bool initialize(); + void shutdown(); + + // Recording + bool startRecording(const std::string& filepath); + void pauseRecording(); + void resumeRecording(); + void stopRecording(); // finalizes WAV file + + // Playback + bool startPlayback(const std::string& filepath); + void pausePlayback(); + void resumePlayback(); + void stopPlayback(); + + AudioState state() const; + + // Poll from main thread to handle async events (e.g. playback finished) + void poll(); + + // Duration in seconds + float recordingDuration() const; + float playbackPosition() const; + float playbackDuration() const; + + // UI update callback (called from audio thread, must be non-blocking) + using UpdateCallback = std::function; + void setUpdateCallback(UpdateCallback cb); + + // PIMPL - exposed for C callbacks only (AudioEngineImpl is opaque outside audio_engine.cpp) + std::unique_ptr impl_; +}; diff --git a/projects/Recorder/main/include/compat/input_keys.h b/projects/Recorder/main/include/compat/input_keys.h new file mode 100644 index 00000000..da5eefe3 --- /dev/null +++ b/projects/Recorder/main/include/compat/input_keys.h @@ -0,0 +1,82 @@ +#pragma once +// Cross-platform KEY_* constants +// On Linux: use the real header. On macOS/Windows: define ourselves. + +#ifdef __linux__ +#include +#else + +#ifndef KEY_ESC +#define KEY_ESC 1 +#define KEY_1 2 +#define KEY_2 3 +#define KEY_3 4 +#define KEY_4 5 +#define KEY_5 6 +#define KEY_6 7 +#define KEY_7 8 +#define KEY_8 9 +#define KEY_9 10 +#define KEY_0 11 +#define KEY_BACKSPACE 14 +#define KEY_TAB 15 +#define KEY_Q 16 +#define KEY_W 17 +#define KEY_E 18 +#define KEY_R 19 +#define KEY_T 20 +#define KEY_Y 21 +#define KEY_U 22 +#define KEY_I 23 +#define KEY_O 24 +#define KEY_P 25 +#define KEY_ENTER 28 +#define KEY_LEFTCTRL 29 +#define KEY_A 30 +#define KEY_S 31 +#define KEY_D 32 +#define KEY_F 33 +#define KEY_G 34 +#define KEY_H 35 +#define KEY_J 36 +#define KEY_K 37 +#define KEY_L 38 +#define KEY_LEFTSHIFT 42 +#define KEY_Z 44 +#define KEY_X 45 +#define KEY_C 46 +#define KEY_V 47 +#define KEY_B 48 +#define KEY_N 49 +#define KEY_M 50 +#define KEY_SPACE 57 +#define KEY_LEFTALT 56 +#define KEY_CAPSLOCK 58 +#define KEY_F1 59 +#define KEY_F2 60 +#define KEY_F3 61 +#define KEY_F4 62 +#define KEY_F5 63 +#define KEY_F6 64 +#define KEY_F7 65 +#define KEY_F8 66 +#define KEY_F9 67 +#define KEY_F10 68 +#define KEY_F11 87 +#define KEY_F12 88 +#define KEY_KPENTER 96 +#define KEY_HOME 102 +#define KEY_UP 103 +#define KEY_PAGEUP 104 +#define KEY_LEFT 105 +#define KEY_RIGHT 106 +#define KEY_END 107 +#define KEY_DOWN 108 +#define KEY_PAGEDOWN 109 +#define KEY_INSERT 110 +#define KEY_DELETE 111 +#define KEY_NEXT 407 +#define KEY_PREVIOUS 412 +#endif + +#endif // __linux__ diff --git a/projects/Recorder/main/include/keyboard_input.h b/projects/Recorder/main/include/keyboard_input.h new file mode 100644 index 00000000..c26a5393 --- /dev/null +++ b/projects/Recorder/main/include/keyboard_input.h @@ -0,0 +1,46 @@ +#ifndef __MAIN__H__ +#define __MAIN__H__ + +#include +#include +#include +#ifdef __cplusplus +extern "C" { +#endif + +// Modifier key bitmap +#define KBD_MOD_SHIFT (1u << 0) +#define KBD_MOD_CTRL (1u << 1) +#define KBD_MOD_ALT (1u << 2) +#define KBD_MOD_LOGO (1u << 3) +#define KBD_MOD_CAPS (1u << 4) +#define KBD_MOD_NUM (1u << 5) + +// Key state +#define KBD_KEY_RELEASED 0 +#define KBD_KEY_PRESSED 1 +#define KBD_KEY_REPEATED 2 + +struct key_item { + uint32_t key_code; // Linux evdev key code + uint32_t keysym; // Primary XKB keysym + uint32_t codepoint; // Unicode codepoint, 0 if none + uint32_t mods; // Modifier bitmap (KBD_MOD_*) + int key_state; // 0=released, 1=pressed, 2=repeated + char sym_name[65]; // XKB keysym name + char utf8[16]; // UTF-8 character + char flage; // Flag: needs free + STAILQ_ENTRY(key_item) entries; +}; + +STAILQ_HEAD(keyboard_queue_t, key_item); +extern struct keyboard_queue_t keyboard_queue; +extern pthread_mutex_t keyboard_mutex; +extern volatile int LVGL_HOME_KEY_FLAGE; +extern volatile int LVGL_RUN_FLAGE; +extern volatile uint32_t LV_EVENT_KEYBOARD; +void *keyboard_read_thread(void *argv); +#ifdef __cplusplus +} +#endif +#endif diff --git a/projects/Recorder/main/include/recorder_app.h b/projects/Recorder/main/include/recorder_app.h new file mode 100644 index 00000000..1f7539e2 --- /dev/null +++ b/projects/Recorder/main/include/recorder_app.h @@ -0,0 +1,62 @@ +#pragma once + +#include "audio_engine.h" +#include +#include + +enum class AppState { + Idle, + Recording, + RecPaused, + Playing, + PlayPaused, +}; + +struct RecordingInfo { + std::string filename; + std::string filepath; + float duration; +}; + +class RecorderApp { +public: + static RecorderApp& instance(); + + bool init(); + void deinit(); + + // Actions + void toggleRecord(); // Enter: start/stop recording + void pauseResumeRecord(); // P: pause/resume recording + void togglePlay(); // Space: play/pause playback + void stop(); // S: stop current operation + void prevFile(); + void nextFile(); + + // State queries + AppState state() const; + std::string statusText() const; + std::string timerText() const; + std::string currentFileName() const; + std::vector fileList() const; + + // Audio engine access for UI callbacks + AudioEngine& engine() { return engine_; } + + // Scan recordings directory + void scanFiles(); + +private: + RecorderApp() = default; + ~RecorderApp() = default; + RecorderApp(const RecorderApp&) = delete; + RecorderApp& operator=(const RecorderApp&) = delete; + + std::string generateFilename(); + std::string recordingsDir(); + + AudioEngine engine_; + std::vector files_; + int currentFileIndex_ = -1; + std::string lastRecordingPath_; +}; diff --git a/projects/Recorder/main/include/ui_recorder.h b/projects/Recorder/main/include/ui_recorder.h new file mode 100644 index 00000000..f60d9e9f --- /dev/null +++ b/projects/Recorder/main/include/ui_recorder.h @@ -0,0 +1,30 @@ +#pragma once + +#include "lvgl/lvgl.h" + +class UiRecorder { +public: + static UiRecorder& instance(); + + void init(lv_obj_t* parent); + void update(); // call from main loop periodically + + // Keyboard handling + void onKeyPressed(uint32_t key_code); + +private: + UiRecorder() = default; + + void buildUi(lv_obj_t* parent); + void refreshDisplay(); + + lv_obj_t* parent_ = nullptr; + lv_obj_t* lblStatus_ = nullptr; + lv_obj_t* lblTimer_ = nullptr; + lv_obj_t* lblFile_ = nullptr; + lv_obj_t* lblHint_ = nullptr; + + uint32_t lastUpdate_ = 0; + uint32_t lastKeyTime_ = 0; + uint32_t lastKeyCode_ = 0; +}; diff --git a/projects/Recorder/main/include/wav_file.h b/projects/Recorder/main/include/wav_file.h new file mode 100644 index 00000000..e3b78d8f --- /dev/null +++ b/projects/Recorder/main/include/wav_file.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +// Write a placeholder WAV header. Call wav_file_finalize() after writing PCM data. +bool wav_file_write_header(FILE* file, uint16_t channels, uint32_t sampleRate, uint32_t dataSize); + +// Finalize WAV header by updating chunk sizes. file must be open for read/write. +bool wav_file_finalize(FILE* file); + +// Read WAV header and return parameters. File pointer is left at start of PCM data. +bool wav_file_read_header(FILE* file, uint16_t& channels, uint32_t& sampleRate, uint32_t& dataSize); diff --git a/projects/Recorder/main/src/audio_engine.cpp b/projects/Recorder/main/src/audio_engine.cpp new file mode 100644 index 00000000..583724b2 --- /dev/null +++ b/projects/Recorder/main/src/audio_engine.cpp @@ -0,0 +1,318 @@ +#include "audio_engine.h" +#include "wav_file.h" + +#define MINIAUDIO_IMPLEMENTATION +#include "miniaudio.h" + +#include +#include + +struct AudioEngineImpl { + ma_context context{}; + bool contextInitialized = false; + + ma_device* recDevice = nullptr; + ma_device* playDevice = nullptr; + + FILE* recFile = nullptr; + uint32_t recSampleRate = 48000; + uint8_t recChannels = 1; + uint32_t recTotalFrames = 0; + + FILE* playFile = nullptr; + uint32_t playSampleRate = 48000; + uint8_t playChannels = 1; + uint32_t playTotalFrames = 0; + uint32_t playDataOffset = 0; + uint32_t playCurrentFrame = 0; + + std::atomic state{AudioState::Idle}; + std::atomic playbackFinished{false}; + AudioEngine::UpdateCallback updateCb; + + ~AudioEngineImpl() { + if (recDevice) { + ma_device_stop(recDevice); + ma_device_uninit(recDevice); + delete recDevice; + } + if (playDevice) { + ma_device_stop(playDevice); + ma_device_uninit(playDevice); + delete playDevice; + } + if (contextInitialized) { + ma_context_uninit(&context); + } + } +}; + +extern "C" { + static void recordingCallback(ma_device* pDevice, void* pOutput, + const void* pInput, ma_uint32 frameCount) + { + (void)pOutput; + AudioEngine* self = static_cast(pDevice->pUserData); + if (!self->impl_ || !self->impl_->recFile) return; + + size_t bytesToWrite = frameCount * self->impl_->recChannels * sizeof(int16_t); + size_t written = fwrite(pInput, 1, bytesToWrite, self->impl_->recFile); + if (written == bytesToWrite) { + self->impl_->recTotalFrames += frameCount; + if (self->impl_->updateCb) self->impl_->updateCb(); + } + } + + static void playbackCallback(ma_device* pDevice, void* pOutput, + const void* pInput, ma_uint32 frameCount) + { + (void)pInput; + AudioEngine* self = static_cast(pDevice->pUserData); + if (!self->impl_ || !self->impl_->playFile) { + memset(pOutput, 0, frameCount * pDevice->playback.channels * sizeof(int16_t)); + return; + } + + size_t bytesPerFrame = self->impl_->playChannels * sizeof(int16_t); + size_t bytesToRead = frameCount * bytesPerFrame; + size_t read = fread(pOutput, 1, bytesToRead, self->impl_->playFile); + + if (read < bytesToRead) { + memset(static_cast(pOutput) + read, 0, bytesToRead - read); + self->impl_->playCurrentFrame += static_cast(read / bytesPerFrame); + if (read == 0) { + self->impl_->playbackFinished.store(true); + } + } else { + self->impl_->playCurrentFrame += frameCount; + if (self->impl_->updateCb) self->impl_->updateCb(); + } + } +} + +AudioEngine::AudioEngine() : impl_(std::make_unique()) {} +AudioEngine::~AudioEngine() = default; + +bool AudioEngine::initialize() +{ + if (impl_->contextInitialized) return true; + if (ma_context_init(nullptr, 0, nullptr, &impl_->context) != MA_SUCCESS) { + return false; + } + impl_->contextInitialized = true; + return true; +} + +void AudioEngine::shutdown() +{ + stopRecording(); + stopPlayback(); +} + +bool AudioEngine::startRecording(const std::string& filepath) +{ + if (impl_->state.load() != AudioState::Idle) return false; + if (!impl_->contextInitialized && !initialize()) return false; + + stopRecording(); + + impl_->recFile = fopen(filepath.c_str(), "wb"); + if (!impl_->recFile) return false; + impl_->recTotalFrames = 0; + wav_file_write_header(impl_->recFile, impl_->recChannels, impl_->recSampleRate, 0); + + ma_device_config config = ma_device_config_init(ma_device_type_capture); + config.capture.format = ma_format_s16; + config.capture.channels = impl_->recChannels; + config.sampleRate = impl_->recSampleRate; + config.dataCallback = recordingCallback; + config.pUserData = this; + + impl_->recDevice = new ma_device(); + memset(impl_->recDevice, 0, sizeof(ma_device)); + ma_result result = ma_device_init(&impl_->context, &config, impl_->recDevice); + if (result != MA_SUCCESS) { + delete impl_->recDevice; + impl_->recDevice = nullptr; + fclose(impl_->recFile); + impl_->recFile = nullptr; + return false; + } + + result = ma_device_start(impl_->recDevice); + if (result != MA_SUCCESS) { + ma_device_uninit(impl_->recDevice); + delete impl_->recDevice; + impl_->recDevice = nullptr; + fclose(impl_->recFile); + impl_->recFile = nullptr; + return false; + } + + impl_->state.store(AudioState::Recording); + return true; +} + +void AudioEngine::pauseRecording() +{ + if (impl_->state.load() == AudioState::Recording && impl_->recDevice) { + ma_device_stop(impl_->recDevice); + impl_->state.store(AudioState::RecPaused); + } +} + +void AudioEngine::resumeRecording() +{ + if (impl_->state.load() == AudioState::RecPaused && impl_->recDevice) { + ma_device_start(impl_->recDevice); + impl_->state.store(AudioState::Recording); + } +} + +void AudioEngine::stopRecording() +{ + AudioState s = impl_->state.load(); + if (s != AudioState::Recording && s != AudioState::RecPaused) return; + + if (impl_->recDevice) { + ma_device_stop(impl_->recDevice); + ma_device_uninit(impl_->recDevice); + delete impl_->recDevice; + impl_->recDevice = nullptr; + } + + if (impl_->recFile) { + wav_file_finalize(impl_->recFile); + fclose(impl_->recFile); + impl_->recFile = nullptr; + } + impl_->recTotalFrames = 0; + impl_->state.store(AudioState::Idle); +} + +bool AudioEngine::startPlayback(const std::string& filepath) +{ + if (impl_->state.load() != AudioState::Idle) return false; + if (!impl_->contextInitialized && !initialize()) return false; + + stopPlayback(); + + impl_->playFile = fopen(filepath.c_str(), "rb"); + if (!impl_->playFile) return false; + + uint16_t channels = 0; + uint32_t sampleRate = 0; + uint32_t dataSize = 0; + if (!wav_file_read_header(impl_->playFile, channels, sampleRate, dataSize)) { + fclose(impl_->playFile); + impl_->playFile = nullptr; + return false; + } + + impl_->playChannels = static_cast(channels); + impl_->playSampleRate = sampleRate; + impl_->playTotalFrames = dataSize / (channels * sizeof(int16_t)); + impl_->playCurrentFrame = 0; + impl_->playDataOffset = ftell(impl_->playFile); + + ma_device_config config = ma_device_config_init(ma_device_type_playback); + config.playback.format = ma_format_s16; + config.playback.channels = impl_->playChannels; + config.sampleRate = impl_->playSampleRate; + config.dataCallback = playbackCallback; + config.pUserData = this; + + impl_->playDevice = new ma_device(); + memset(impl_->playDevice, 0, sizeof(ma_device)); + ma_result result = ma_device_init(&impl_->context, &config, impl_->playDevice); + if (result != MA_SUCCESS) { + delete impl_->playDevice; + impl_->playDevice = nullptr; + fclose(impl_->playFile); + impl_->playFile = nullptr; + return false; + } + + result = ma_device_start(impl_->playDevice); + if (result != MA_SUCCESS) { + ma_device_uninit(impl_->playDevice); + delete impl_->playDevice; + impl_->playDevice = nullptr; + fclose(impl_->playFile); + impl_->playFile = nullptr; + return false; + } + + impl_->state.store(AudioState::Playing); + return true; +} + +void AudioEngine::pausePlayback() +{ + if (impl_->state.load() == AudioState::Playing && impl_->playDevice) { + ma_device_stop(impl_->playDevice); + impl_->state.store(AudioState::PlayPaused); + } +} + +void AudioEngine::resumePlayback() +{ + if (impl_->state.load() == AudioState::PlayPaused && impl_->playDevice) { + ma_device_start(impl_->playDevice); + impl_->state.store(AudioState::Playing); + } +} + +void AudioEngine::stopPlayback() +{ + AudioState s = impl_->state.load(); + if (s != AudioState::Playing && s != AudioState::PlayPaused) return; + + if (impl_->playDevice) { + ma_device_stop(impl_->playDevice); + ma_device_uninit(impl_->playDevice); + delete impl_->playDevice; + impl_->playDevice = nullptr; + } + + if (impl_->playFile) { + fclose(impl_->playFile); + impl_->playFile = nullptr; + } + impl_->playTotalFrames = 0; + impl_->playCurrentFrame = 0; + impl_->playbackFinished.store(false); + impl_->state.store(AudioState::Idle); +} + +AudioState AudioEngine::state() const +{ + return impl_->state.load(); +} + +void AudioEngine::poll() +{ + if (impl_->playbackFinished.exchange(false)) { + stopPlayback(); + } +} + +float AudioEngine::recordingDuration() const +{ + return static_cast(impl_->recTotalFrames) / static_cast(impl_->recSampleRate); +} + +float AudioEngine::playbackPosition() const +{ + return static_cast(impl_->playCurrentFrame) / static_cast(impl_->playSampleRate); +} + +float AudioEngine::playbackDuration() const +{ + return static_cast(impl_->playTotalFrames) / static_cast(impl_->playSampleRate); +} + +void AudioEngine::setUpdateCallback(UpdateCallback cb) +{ + impl_->updateCb = cb; +} diff --git a/projects/Recorder/main/src/main.cpp b/projects/Recorder/main/src/main.cpp new file mode 100644 index 00000000..8297acbe --- /dev/null +++ b/projects/Recorder/main/src/main.cpp @@ -0,0 +1,227 @@ +#include "keyboard_input.h" +#include "compat/input_keys.h" +#include "global_config.h" +#include "lvgl/lvgl.h" +#include "recorder_app.h" +#include "ui_recorder.h" + +#include +#include +#include +#include +#include +#include + +#if LV_USE_SDL +#include "lvgl/src/drivers/sdl/lv_sdl_mouse.h" +#include "lvgl/src/drivers/sdl/lv_sdl_window.h" +#include "lvgl/src/drivers/sdl/lv_sdl_keyboard.h" +#endif + +#if LV_USE_EVDEV +#include +#endif + +namespace { + +constexpr int kScreenWidth = 320; +constexpr int kScreenHeight = 170; + +volatile sig_atomic_t g_quit_requested = 0; +lv_obj_t *g_root = nullptr; +lv_indev_t *g_keyboard_indev = nullptr; +lv_group_t *g_group = nullptr; + +void request_quit() +{ + g_quit_requested = 1; +} + +void handle_signal(int) +{ + request_quit(); +} + +int get_st7789v_fbdev(char *dev_path, size_t buf_size) +{ + if (!dev_path || buf_size == 0) return -1; + FILE *fp = std::fopen("/proc/fb", "r"); + if (!fp) return -1; + char line[256]; + int fb_num = -1; + while (std::fgets(line, sizeof(line), fp)) { + if (std::strstr(line, "fb_st7789v") && std::sscanf(line, "%d", &fb_num) == 1) break; + } + std::fclose(fp); + if (fb_num < 0) return -1; + std::snprintf(dev_path, buf_size, "/dev/fb%d", fb_num); + return 0; +} + +void handle_keyboard_event(lv_event_t *event) +{ + uint32_t key_code = 0; + bool pressed = false; + + if (lv_event_get_code(event) == static_cast(LV_EVENT_KEYBOARD)) { + auto *key = static_cast(lv_event_get_param(event)); + if (!key || key->key_state != KBD_KEY_PRESSED) return; // Ignore release and repeat + key_code = key->key_code; + pressed = true; + } +#if LV_USE_SDL + else if (lv_event_get_code(event) == LV_EVENT_KEY) { + lv_indev_t *indev = lv_indev_active(); + if (lv_indev_get_state(indev) != LV_INDEV_STATE_PRESSED) return; + uint32_t lv_key = lv_indev_get_key(indev); + switch (lv_key) { + case LV_KEY_ENTER: key_code = KEY_ENTER; break; + case LV_KEY_ESC: key_code = KEY_ESC; break; + case LV_KEY_LEFT: key_code = KEY_LEFT; break; + case LV_KEY_RIGHT: key_code = KEY_RIGHT; break; + case 'p': case 'P': key_code = KEY_P; break; + case 's': case 'S': key_code = KEY_S; break; + case ' ': key_code = KEY_SPACE; break; + default: key_code = lv_key; break; + } + pressed = true; + } +#endif + + if (!pressed) return; + if (key_code == KEY_ESC) { + request_quit(); + return; + } + UiRecorder::instance().onKeyPressed(key_code); +} + +void build_ui() +{ + g_root = lv_screen_active(); + lv_obj_set_size(g_root, kScreenWidth, kScreenHeight); + lv_obj_clear_flag(g_root, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_bg_color(g_root, lv_color_hex(0x1A1A2E), 0); + lv_obj_set_style_bg_opa(g_root, LV_OPA_COVER, 0); + + UiRecorder::instance().init(g_root); + + lv_obj_add_event_cb(g_root, handle_keyboard_event, + static_cast(LV_EVENT_KEYBOARD), nullptr); +#if LV_USE_SDL + lv_obj_add_event_cb(g_root, handle_keyboard_event, LV_EVENT_KEY, nullptr); +#endif + + g_group = lv_group_create(); + lv_group_add_obj(g_group, g_root); + lv_group_focus_obj(g_root); + if (g_keyboard_indev) lv_indev_set_group(g_keyboard_indev, g_group); +} + +#if LV_USE_EVDEV +int evdev_to_lv_key(uint16_t code) +{ + switch (code) { + case KEY_UP: return LV_KEY_UP; + case KEY_DOWN: return LV_KEY_DOWN; + case KEY_RIGHT: return LV_KEY_RIGHT; + case KEY_LEFT: return LV_KEY_LEFT; + case KEY_ESC: return LV_KEY_ESC; + case KEY_DELETE: return LV_KEY_DEL; + case KEY_BACKSPACE: return LV_KEY_BACKSPACE; + case KEY_ENTER: return LV_KEY_ENTER; + case KEY_TAB: return KEY_TAB; + case KEY_HOME: return LV_KEY_HOME; + case KEY_END: return LV_KEY_END; + default: return code; + } +} + +void keypad_read_cb(lv_indev_t *, lv_indev_data_t *data) +{ + data->state = LV_INDEV_STATE_RELEASED; + data->continue_reading = false; + pthread_mutex_lock(&keyboard_mutex); + if (!STAILQ_EMPTY(&keyboard_queue)) { + key_item *elm = STAILQ_FIRST(&keyboard_queue); + STAILQ_REMOVE_HEAD(&keyboard_queue, entries); + if (g_root) { + lv_obj_send_event(g_root, static_cast(LV_EVENT_KEYBOARD), elm); + } + data->key = evdev_to_lv_key(elm->key_code); + data->state = static_cast(elm->key_state); + data->continue_reading = !STAILQ_EMPTY(&keyboard_queue); + std::free(elm); + } + pthread_mutex_unlock(&keyboard_mutex); +} + +void lv_linux_indev_init() +{ + const char *keyboard_device = getenv("LV_LINUX_KEYBOARD_DEVICE"); + if (!keyboard_device) keyboard_device = "/dev/input/by-path/platform-3f804000.i2c-event"; + pthread_t thread_id; + pthread_create(&thread_id, nullptr, keyboard_read_thread, const_cast(keyboard_device)); + pthread_detach(thread_id); + g_keyboard_indev = lv_indev_create(); + lv_indev_set_type(g_keyboard_indev, LV_INDEV_TYPE_KEYPAD); + lv_indev_set_read_cb(g_keyboard_indev, keypad_read_cb); +} +#endif + +#if LV_USE_LINUX_FBDEV +void lv_linux_disp_init() +{ + char fbdev[64] = {}; + const char *device = getenv("LV_LINUX_FBDEV_DEVICE"); + if (!device && get_st7789v_fbdev(fbdev, sizeof(fbdev)) == 0) device = fbdev; + if (!device) device = "/dev/fb0"; + lv_display_t *disp = lv_linux_fbdev_create(); + if (disp) lv_linux_fbdev_set_file(disp, device); +} + +#if !LV_USE_EVDEV && !LV_USE_LIBINPUT +void lv_linux_indev_init() {} +#endif + +#elif LV_USE_SDL +void lv_linux_disp_init() +{ + lv_display_t *disp = lv_sdl_window_create(kScreenWidth, kScreenHeight); + lv_sdl_window_set_title(disp, "Recorder"); +} + +void lv_linux_indev_init() +{ + lv_sdl_mouse_create(); + g_keyboard_indev = lv_sdl_keyboard_create(); +} +#else +#error Unsupported display configuration +#endif + +} // namespace + +int main() +{ + std::signal(SIGINT, handle_signal); + std::signal(SIGTERM, handle_signal); + std::signal(SIGPIPE, SIG_IGN); + + RecorderApp::instance().init(); + + lv_init(); + lv_linux_disp_init(); + LV_EVENT_KEYBOARD = lv_event_register_id(); + lv_linux_indev_init(); + build_ui(); + + while (!g_quit_requested) { + UiRecorder::instance().update(); + lv_timer_handler(); + usleep(5000); + } + + RecorderApp::instance().deinit(); + return 0; +} diff --git a/projects/Recorder/main/src/recorder_app.cpp b/projects/Recorder/main/src/recorder_app.cpp new file mode 100644 index 00000000..1e96eace --- /dev/null +++ b/projects/Recorder/main/src/recorder_app.cpp @@ -0,0 +1,265 @@ +#include "recorder_app.h" +#include "wav_file.h" +#include "compat/input_keys.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +RecorderApp& RecorderApp::instance() +{ + static RecorderApp app; + return app; +} + +bool RecorderApp::init() +{ + engine_.initialize(); + scanFiles(); + return true; +} + +void RecorderApp::deinit() +{ + engine_.shutdown(); +} + +void RecorderApp::toggleRecord() +{ + AppState s = state(); + if (s == AppState::Idle) { + lastRecordingPath_ = generateFilename(); + if (engine_.startRecording(lastRecordingPath_)) { + scanFiles(); + } + } else if (s == AppState::Recording || s == AppState::RecPaused) { + engine_.stopRecording(); + scanFiles(); + } else if (s == AppState::Playing || s == AppState::PlayPaused) { + engine_.stopPlayback(); + lastRecordingPath_ = generateFilename(); + engine_.startRecording(lastRecordingPath_); + } +} + +void RecorderApp::pauseResumeRecord() +{ + AppState s = state(); + if (s == AppState::Recording) { + engine_.pauseRecording(); + } else if (s == AppState::RecPaused) { + engine_.resumeRecording(); + } +} + +void RecorderApp::togglePlay() +{ + AppState s = state(); + if (s == AppState::Idle) { + if (files_.empty()) return; + if (currentFileIndex_ < 0 || currentFileIndex_ >= static_cast(files_.size())) { + currentFileIndex_ = static_cast(files_.size()) - 1; + } + engine_.startPlayback(files_[currentFileIndex_].filepath); + } else if (s == AppState::Playing) { + engine_.pausePlayback(); + } else if (s == AppState::PlayPaused) { + engine_.resumePlayback(); + } else if (s == AppState::Recording || s == AppState::RecPaused) { + engine_.stopRecording(); + scanFiles(); + if (files_.empty()) return; + if (currentFileIndex_ < 0 || currentFileIndex_ >= static_cast(files_.size())) { + currentFileIndex_ = static_cast(files_.size()) - 1; + } + engine_.startPlayback(files_[currentFileIndex_].filepath); + } +} + +void RecorderApp::stop() +{ + AppState s = state(); + if (s == AppState::Recording || s == AppState::RecPaused) { + engine_.stopRecording(); + scanFiles(); + } else if (s == AppState::Playing || s == AppState::PlayPaused) { + engine_.stopPlayback(); + } +} + +void RecorderApp::prevFile() +{ + if (files_.empty()) return; + if (currentFileIndex_ > 0) currentFileIndex_--; + else currentFileIndex_ = static_cast(files_.size()) - 1; +} + +void RecorderApp::nextFile() +{ + if (files_.empty()) return; + if (currentFileIndex_ < static_cast(files_.size()) - 1) currentFileIndex_++; + else currentFileIndex_ = 0; +} + +AppState RecorderApp::state() const +{ + switch (engine_.state()) { + case AudioState::Idle: return AppState::Idle; + case AudioState::Recording: return AppState::Recording; + case AudioState::RecPaused: return AppState::RecPaused; + case AudioState::Playing: return AppState::Playing; + case AudioState::PlayPaused: return AppState::PlayPaused; + } + return AppState::Idle; +} + +std::string RecorderApp::statusText() const +{ + switch (state()) { + case AppState::Idle: return "IDLE"; + case AppState::Recording: return "RECORDING"; + case AppState::RecPaused: return "REC PAUSED"; + case AppState::Playing: return "PLAYING"; + case AppState::PlayPaused: return "PLAY PAUSED"; + } + return "UNKNOWN"; +} + +std::string RecorderApp::timerText() const +{ + std::ostringstream oss; + switch (state()) { + case AppState::Recording: + case AppState::RecPaused: { + float d = engine_.recordingDuration(); + int mins = static_cast(d) / 60; + int secs = static_cast(d) % 60; + oss << std::setfill('0') << std::setw(2) << mins << ":" + << std::setfill('0') << std::setw(2) << secs; + break; + } + case AppState::Playing: + case AppState::PlayPaused: { + float pos = engine_.playbackPosition(); + float dur = engine_.playbackDuration(); + int pmin = static_cast(pos) / 60; + int psec = static_cast(pos) % 60; + int dmin = static_cast(dur) / 60; + int dsec = static_cast(dur) % 60; + oss << std::setfill('0') << std::setw(2) << pmin << ":" + << std::setfill('0') << std::setw(2) << psec << " / " + << std::setfill('0') << std::setw(2) << dmin << ":" + << std::setfill('0') << std::setw(2) << dsec; + break; + } + default: + break; + } + return oss.str(); +} + +std::string RecorderApp::currentFileName() const +{ + if (state() == AppState::Recording || state() == AppState::RecPaused) { + size_t pos = lastRecordingPath_.find_last_of('/'); + return lastRecordingPath_.substr(pos + 1); + } + if (state() == AppState::Playing || state() == AppState::PlayPaused) { + if (currentFileIndex_ >= 0 && currentFileIndex_ < static_cast(files_.size())) { + return files_[currentFileIndex_].filename; + } + } + if (!files_.empty() && currentFileIndex_ >= 0 && currentFileIndex_ < static_cast(files_.size())) { + return files_[currentFileIndex_].filename; + } + return "No recordings"; +} + +std::vector RecorderApp::fileList() const +{ + return files_; +} + +std::string RecorderApp::recordingsDir() +{ + const char* home = getenv("HOME"); + if (!home) return "/tmp/recordings"; + + std::string musicDir = std::string(home) + "/Music"; + std::string dir = musicDir + "/Recorder"; + + struct stat st; + if (stat(musicDir.c_str(), &st) != 0) { + mkdir(musicDir.c_str(), 0755); + } + if (stat(dir.c_str(), &st) != 0) { + mkdir(dir.c_str(), 0755); + } + return dir; +} + +std::string RecorderApp::generateFilename() +{ + auto now = std::chrono::system_clock::now(); + auto t = std::chrono::system_clock::to_time_t(now); + std::tm tm; + localtime_r(&t, &tm); + + std::ostringstream oss; + oss << recordingsDir() << "/rec_" + << (tm.tm_year + 1900) + << std::setfill('0') << std::setw(2) << (tm.tm_mon + 1) + << std::setfill('0') << std::setw(2) << tm.tm_mday << "_" + << std::setfill('0') << std::setw(2) << tm.tm_hour + << std::setfill('0') << std::setw(2) << tm.tm_min + << std::setfill('0') << std::setw(2) << tm.tm_sec + << ".wav"; + return oss.str(); +} + +void RecorderApp::scanFiles() +{ + files_.clear(); + std::string dir = recordingsDir(); + DIR* d = opendir(dir.c_str()); + if (!d) return; + + struct dirent* entry; + while ((entry = readdir(d)) != nullptr) { + if (entry->d_type != DT_REG && entry->d_type != DT_UNKNOWN) continue; + size_t len = strlen(entry->d_name); + if (len < 5 || strcmp(entry->d_name + len - 4, ".wav") != 0) continue; + + std::string path = dir + "/" + entry->d_name; + FILE* f = fopen(path.c_str(), "rb"); + if (!f) continue; + + uint16_t channels = 0; + uint32_t sampleRate = 0; + uint32_t dataSize = 0; + if (wav_file_read_header(f, channels, sampleRate, dataSize)) { + RecordingInfo info; + info.filename = entry->d_name; + info.filepath = path; + info.duration = static_cast(dataSize) / (sampleRate * channels * sizeof(int16_t)); + files_.push_back(info); + } + fclose(f); + } + closedir(d); + + std::sort(files_.begin(), files_.end(), + [](const RecordingInfo& a, const RecordingInfo& b) { return a.filename > b.filename; }); + + if (!files_.empty() && currentFileIndex_ < 0) { + currentFileIndex_ = 0; + } +} diff --git a/projects/Recorder/main/src/ui_recorder.cpp b/projects/Recorder/main/src/ui_recorder.cpp new file mode 100644 index 00000000..d3509a57 --- /dev/null +++ b/projects/Recorder/main/src/ui_recorder.cpp @@ -0,0 +1,131 @@ +#include "ui_recorder.h" +#include "recorder_app.h" +#include "compat/input_keys.h" + +#include + +UiRecorder& UiRecorder::instance() +{ + static UiRecorder ui; + return ui; +} + +void UiRecorder::init(lv_obj_t* parent) +{ + parent_ = parent; + buildUi(parent); +} + +void UiRecorder::buildUi(lv_obj_t* parent) +{ + lv_obj_set_size(parent, 320, 170); + lv_obj_clear_flag(parent, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_bg_color(parent, lv_color_hex(0x1A1A2E), 0); + lv_obj_set_style_bg_opa(parent, LV_OPA_COVER, 0); + + // Status label (top left) + lblStatus_ = lv_label_create(parent); + lv_obj_set_pos(lblStatus_, 10, 10); + lv_obj_set_style_text_font(lblStatus_, &lv_font_montserrat_14, 0); + lv_obj_set_style_text_color(lblStatus_, lv_color_hex(0xE0E0E0), 0); + lv_label_set_text(lblStatus_, "IDLE"); + + // Timer label (top right) + lblTimer_ = lv_label_create(parent); + lv_obj_set_pos(lblTimer_, 220, 10); + lv_obj_set_style_text_font(lblTimer_, &lv_font_montserrat_14, 0); + lv_obj_set_style_text_color(lblTimer_, lv_color_hex(0xE0E0E0), 0); + lv_label_set_text(lblTimer_, ""); + + // File name label (center) + lblFile_ = lv_label_create(parent); + lv_obj_set_pos(lblFile_, 10, 55); + lv_obj_set_width(lblFile_, 300); + lv_obj_set_style_text_font(lblFile_, &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblFile_, lv_color_hex(0xA0A0A0), 0); + lv_label_set_long_mode(lblFile_, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_label_set_text(lblFile_, "No recordings"); + + // Hint label (bottom) + lblHint_ = lv_label_create(parent); + lv_obj_set_pos(lblHint_, 10, 142); + lv_obj_set_width(lblHint_, 300); + lv_obj_set_style_text_font(lblHint_, &lv_font_montserrat_10, 0); + lv_obj_set_style_text_color(lblHint_, lv_color_hex(0x606060), 0); + lv_label_set_text(lblHint_, "[Enter]Rec [P]Play [S]Stop [<- ->]File [Esc]Quit"); +} + +void UiRecorder::update() +{ + uint32_t now = lv_tick_get(); + if (now - lastUpdate_ < 200) return; + lastUpdate_ = now; + + RecorderApp::instance().engine().poll(); + refreshDisplay(); +} + +void UiRecorder::refreshDisplay() +{ + auto& app = RecorderApp::instance(); + lv_label_set_text(lblStatus_, app.statusText().c_str()); + lv_label_set_text(lblTimer_, app.timerText().c_str()); + lv_label_set_text(lblFile_, app.currentFileName().c_str()); + + auto s = app.state(); + switch (s) { + case AppState::Idle: + lv_label_set_text(lblHint_, "[Enter]Rec [Space]Play [<- ->]File [Esc]Quit"); + break; + case AppState::Recording: + lv_label_set_text(lblHint_, "[P]Pause [Enter]Stop"); + break; + case AppState::RecPaused: + lv_label_set_text(lblHint_, "[P]Resume [Enter]Stop"); + break; + case AppState::Playing: + lv_label_set_text(lblHint_, "[Space]Pause [S]Stop [<- ->]File"); + break; + case AppState::PlayPaused: + lv_label_set_text(lblHint_, "[Space]Resume [S]Stop"); + break; + } +} + +void UiRecorder::onKeyPressed(uint32_t key_code) +{ + uint32_t now = lv_tick_get(); + if (key_code == lastKeyCode_ && now - lastKeyTime_ < 300) return; + lastKeyCode_ = key_code; + lastKeyTime_ = now; + + auto& app = RecorderApp::instance(); + + switch (key_code) { + case KEY_ENTER: + case KEY_KPENTER: + app.toggleRecord(); // Enter: start/stop recording + break; + case KEY_P: + app.pauseResumeRecord(); // P: pause/resume recording + break; + case KEY_SPACE: + app.togglePlay(); // Space: play/pause playback + break; + case KEY_S: + app.stop(); // S: stop current operation + break; + case KEY_ESC: + app.stop(); // Esc: stop and quit + break; + case KEY_LEFT: + app.prevFile(); + break; + case KEY_RIGHT: + app.nextFile(); + break; + default: + break; + } + refreshDisplay(); +} diff --git a/projects/Recorder/main/src/wav_file.cpp b/projects/Recorder/main/src/wav_file.cpp new file mode 100644 index 00000000..138bd760 --- /dev/null +++ b/projects/Recorder/main/src/wav_file.cpp @@ -0,0 +1,72 @@ +#include "wav_file.h" +#include + +struct WavHeader { + char riff[4]; + uint32_t fileSize; + char wave[4]; + char fmt[4]; + uint32_t fmtSize; + uint16_t audioFormat; + uint16_t channels; + uint32_t sampleRate; + uint32_t byteRate; + uint16_t blockAlign; + uint16_t bitsPerSample; + char data[4]; + uint32_t dataSize; +}; + +static_assert(sizeof(WavHeader) == 44, "WAV header must be 44 bytes"); + +bool wav_file_write_header(FILE* file, uint16_t channels, uint32_t sampleRate, uint32_t dataSize) +{ + WavHeader h = {}; + h.riff[0] = 'R'; h.riff[1] = 'I'; h.riff[2] = 'F'; h.riff[3] = 'F'; + h.fileSize = dataSize + 36; + h.wave[0] = 'W'; h.wave[1] = 'A'; h.wave[2] = 'V'; h.wave[3] = 'E'; + h.fmt[0] = 'f'; h.fmt[1] = 'm'; h.fmt[2] = 't'; h.fmt[3] = ' '; + h.fmtSize = 16; + h.audioFormat = 1; // PCM + h.channels = channels; + h.sampleRate = sampleRate; + h.bitsPerSample = 16; + h.blockAlign = channels * (h.bitsPerSample / 8); + h.byteRate = sampleRate * h.blockAlign; + h.data[0] = 'd'; h.data[1] = 'a'; h.data[2] = 't'; h.data[3] = 'a'; + h.dataSize = dataSize; + + return fwrite(&h, sizeof(h), 1, file) == 1; +} + +bool wav_file_finalize(FILE* file) +{ + long fileSize = ftell(file); + if (fileSize < static_cast(sizeof(WavHeader))) return false; + + uint32_t dataSize = static_cast(fileSize - sizeof(WavHeader)); + uint32_t riffSize = static_cast(fileSize - 8); + + if (fseek(file, 4, SEEK_SET) != 0) return false; + if (fwrite(&riffSize, 4, 1, file) != 1) return false; + + if (fseek(file, 40, SEEK_SET) != 0) return false; + if (fwrite(&dataSize, 4, 1, file) != 1) return false; + + fseek(file, 0, SEEK_END); + return true; +} + +bool wav_file_read_header(FILE* file, uint16_t& channels, uint32_t& sampleRate, uint32_t& dataSize) +{ + WavHeader h; + if (fread(&h, sizeof(h), 1, file) != 1) return false; + if (std::memcmp(h.riff, "RIFF", 4) != 0 || std::memcmp(h.wave, "WAVE", 4) != 0) return false; + if (h.audioFormat != 1) return false; // PCM only + if (h.bitsPerSample != 16) return false; // s16 only + + channels = h.channels; + sampleRate = h.sampleRate; + dataSize = h.dataSize; + return true; +} diff --git a/projects/Recorder/share/images/Recorder.png b/projects/Recorder/share/images/Recorder.png new file mode 100644 index 0000000000000000000000000000000000000000..cbb65550e0e05bcf3afcdafa5c45b44d51a41407 GIT binary patch literal 4215 zcmV--5Qy)IP)^ulgAfyx`N)bYcFHzO@1FcF_ zS|Pp^ifBq9lu*=`R#b68fl^v2QlwH9RUq+!gbzq@B|h*0P3J$mv-{t@nepy<<8aQc z^wY)OwJ)CE?SE$eGdpwDRjs3Sw2s!%I$B5TXdSJi8IBbz0@qx-YO<}}cdWzLH^=e! zgo&I8>F!;KC?n7(BW4inmk~GU8IX}M=p8ii2San5j;`RbYuh>}SFE@?@P+lTvaMsb zy{qSxFE~t0R1%s;hLxlRe_-&`s&?P(7fM8D_rM2M5m$zczK}_>v^zX`8WSFE#pf`N>nw4#}CBG{~BH$ApieyPFSrQa(bIQ;d(J{XVzdsx= zkp7{RAp^6jV~OsTwvO)OLJ{GtUWnyoFT)PpIsHRvLk36!y8OMzms|$K)@Y)1#meB< zz<}yqveaDw9OohT3RESru3{EmTLhG6$5|U9#21k>Uw%}<+Z*NVt zWXNUk@9}zt)6*Y0dr3kS)ENuOfL2zR)+k@K?6J?F0m~gV9krES0 zRZY_MMAIWCnR;TGQN#!%t|ODdfhID@faX8oN$A3zK4?v;DsT`10tRCRMkuzNWKgnJ z;*^9$2CwJ`X*A(NdI#bkczSoJnzkS#q>H#RWYlyq z$*{{XrNkBWiguT1boUIMYB+0axjQ0RX`NvYQ;EI%Nae+6$m;*TBLvPR_wEZnk}!I>|SGtcfIpP!u-0_X0S`La;P9XD+xXAU1BpFi@5kT>bM zJ8$|%y^Nd=x>K+W?Jg-9LV$*{#E?SAxhYvX1io*Pv1czC|IgnX0phTX(aC$r?XP|2 zh}zV>78$eCH0GCbl)p)_Z`ED zLfPE`ttlL)%Fq3RjQ#73Bj8#broQ?38|1*BK6M0Ki-nwidOP{_@3%Q(j$Vs}%pJOu zeEQHsj+i@iSS%wqMl1NyIvJc$B*$bx`0Dca9CP3I@b%5{6gpH$G9l2k#dSvQ-0QBu zb;kZb%)0{D8S`)Ma|ISM|KNkJ5XxXeYC;(R^h-bn5bO)jH6(6>`(F9P6}STb4?N%sp$v-9C4Cf zi+kWFXZDi!?!Ma*CujD_W#q@DGCUR{Podk>A+gCFr2N`RN5r@9u%tsOn-7wyXWn&0 zb;}`(km+sf$h)VuJ7Q*Xoh21Avu-_k_s9`P?73lsTm~SK;h~U-ay&znn~F_Nk?c<& zC8bw?BP93uqa;qR5#^@Jn`g<_jy+E%pE@T5&Q-P?6y~OOZ@r$J_|YbE?!|3F;M}xL z2G31Rulp)Fv1^8$JA7Ekuj#p&8`i63)Cn0C3V}3KgF>{qQL4eEWtS+Fb^26cN+@Jh zCcn7Lr?>5Y-s?a!H|QZ=WS2 zFFh+AGJtb%jZ%h02*@yn%=Kkowsn z_Au3qDfG(oPdOeks6PL+R7R?>S}Nlb2oX=!vDX#YW|Ap@BVEW|FuVbND#S?&9S;Ze zZNAw+He?``0c7PuxQvE{@W;kfb8YQHnlXj$cE~^~qo@#)!$R`zh2ZD-R1=@=LYgs! zUU>2`$3q6iCy&cz$b{suki3^d>Sv+b(;>~6LQn6Sc06Q|*}X?9BVC-3%Fu-5y%ZvT zBw)Fu(9=7oN&aU)mJS&d=(!C2d6kShAvsLVOCj>-GnRFtP}Q0e$gl{p$?#A}O!=@( zOU6XK5P=MjglG@stDanH$*>8L$iPBiT2%u=Vh)5pPBz4AxL_G8yU8gj5E=Wemp(jS1mv zg{U5|vs}@!Q(7aHp$RE?Eu{X@V|xXDxn`leopp~ZaJRGWWil*6Y%7aOs6frY?2TMY;?m&H@9 z9b)f&H)9HY(+^&DJY=xxhc8QIm_lk&As}N>Lf{Ep)j}?IAr0Jscz0JI??e1S;SsyfRhg+8|RFxmLMlhPprI5$qksbpl!Yvn@nHA6;2Lhy>6 zswIw=bD~hyni9#73dwUJMUR9i)|+a{Fl7jZJZO_$X8-p zE-CaQx2-2{o!BWIGI;C6E_%%dr3{M@n+y+yB$UhI*=00i3jMDA>m3gn+(pI7WEesO zGCUHJFoj6gAGC+4W=x^~^_NqQhYVhSW`|S;6C#n}fe>>obk$;zb|L@1``hH`jW>}U zwFwdp#YuDslVHR+!wJ&fJ4`xsIx_`IQ6q~fF}i>pP-b-YM>PgwB$%mD#!Rx9aT3g# z^iaA&3DPx`WF(3iovcQQle3U419{S$D3F0viG@9jkjh%A3@ijznQK@GEPAP0nG?$x zh~zX0A*%p`VNHTYlElJEDk5y4i6~jg0cAr%SKb6fFb3l!KntFO3*R8W+Cmq7e~u{v z6@E!8d<&HV!xzmML}<^P%g9x(lgi*iKn5&m){qdsD}ZV#RW1W2JG2Lf%)7x-_z70{ zbwpC6qc38>1$0@ki>MfYh|Z8kz<~rtVS>>WP8l!>w7{(tNTOn=$neFo8u1+QQ&O}w z1~QlsMNgf}Xh;a&ra`r?EpOTehiKvU3^Nf?TG(~a!ncy9Legf~p>*LpA!jXoMxkop zgI6k|Ybe2RB7xIWTtj^Hy*Y*e3=+Vo7D-aauZ!>}@{B=R4hsp%lrB-FcHauL0_h#6aq3bG?aNJ1b%^R#b+*rhn@O6ff7wKq(Z0+ z2s?5iAYcRu8PUGxLa6j$K_vvsSj)g+r(QtGgh?n{U?NgQ(v6S_;WA*HWKlw3ucdk+ zRjH89{s45zEvQ&oVCX0@cF~D`4Xeun8!kuS=uEM?)AF0%0dWo6mLu#@D(8 z2CaaDvf<)`6_)%a-4!Z`L^w zWuNo_w5BWqvQ}szaB#>0Od%r{A$}?V-~|l-_9gSC5E^#;$&ztCSkl6hA*8%k=Q>7r zx}wMb0;89(205ZD@y3xVZLWqpyj zE~F=#HA0U8YU>+!fB@dw!Jl{l7dRmMd4D|5kkvuF@UZ|<*b#m5?S&u6R;}c!Bvhzr zLa1<&3?P;)8$!B+{qC2Eg?;+)Ze%bONVv0%E+mp3u?j%#e89)=03-mnbSwg>T)-&s zSk6Q*d?w%`DsYM+Krej$wNQYg!pCTZUq^m~q-m!xU%g%^BBOT{xeQpHw7t{sej#O8 z4jXR`2cuoG{WIcv8hwzynIVA|I7btJ3OvFkWG&bQ7zK_b7~#eypc@O&3*Gp-uJBC) z413{Mq=nB30tMQjDNisNSxD83WaMkcPH!M1O7D~vS2wsJC2X3=g}@d*vdv6j%LXnb zLciBbh-IPJ}*U~Dgum2EY= zgC&>JlHp7SaBbTvZ}-QA9mcG?Sf-Vgd`re92vzn2_IAZPSOSn%fmnIP%b@RLuQPx^ zZ|KtQhYNeV+IO*pRsVUtawW>pnzrSomthTGY0IXGfF`D-hp%NWBd7Jiai?!d!j!6_ zhwVhM5WMjfEY<~e4E$$d?+l<>W$@n(A1=t+;zXfc2DGL;ufW~jEwFI)@8lqH6J z`e17~*!)T(GS}=fU{Hwz;~9`bZ#Zk)I$B5TXdSJib+nGw(K?p+_&;X)hv|XWeINh; N002ovPDHLkV1jxQ*xmpD literal 0 HcmV?d00001 From 53af3ab058f226de9295d82a17ccf28eecb7d64a Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Thu, 28 May 2026 14:35:02 +0800 Subject: [PATCH 03/11] Refactor Recorder architecture: MVP pattern, async scan, private PIMPL - Introduce IRecorderView interface and RecorderState to decouple UI from logic - Remove singletons from RecorderApp and UiRecorder; wire via dependency injection - Add unified onAction() entry point with explicit state-machine handlers - Move AudioEngine::impl_ to private; expose onRecordingData/onPlaybackData callbacks - Run file scanning in background thread with dirty-flag sync in main loop - Fix UI initialization order: setView() after init() to avoid nullptr LVGL objects --- projects/Recorder/main/include/audio_engine.h | 15 +- projects/Recorder/main/include/recorder_app.h | 76 ++-- projects/Recorder/main/include/ui_recorder.h | 16 +- projects/Recorder/main/src/audio_engine.cpp | 63 ++-- projects/Recorder/main/src/main.cpp | 60 ++-- projects/Recorder/main/src/recorder_app.cpp | 324 ++++++++++++------ projects/Recorder/main/src/ui_recorder.cpp | 69 +--- 7 files changed, 363 insertions(+), 260 deletions(-) diff --git a/projects/Recorder/main/include/audio_engine.h b/projects/Recorder/main/include/audio_engine.h index 8bafd1a8..8c21ad67 100644 --- a/projects/Recorder/main/include/audio_engine.h +++ b/projects/Recorder/main/include/audio_engine.h @@ -6,7 +6,6 @@ #include #include -// miniaudio forward-declares + types (header-only, declarations are lightweight) #include "miniaudio.h" enum class AudioState { @@ -27,32 +26,30 @@ class AudioEngine { bool initialize(); void shutdown(); - // Recording bool startRecording(const std::string& filepath); void pauseRecording(); void resumeRecording(); - void stopRecording(); // finalizes WAV file + void stopRecording(); - // Playback bool startPlayback(const std::string& filepath); void pausePlayback(); void resumePlayback(); void stopPlayback(); AudioState state() const; - - // Poll from main thread to handle async events (e.g. playback finished) void poll(); - // Duration in seconds float recordingDuration() const; float playbackPosition() const; float playbackDuration() const; - // UI update callback (called from audio thread, must be non-blocking) using UpdateCallback = std::function; void setUpdateCallback(UpdateCallback cb); - // PIMPL - exposed for C callbacks only (AudioEngineImpl is opaque outside audio_engine.cpp) + // Internal callbacks invoked from miniaudio's audio thread + void onRecordingData(const void* input, ma_uint32 frameCount); + void onPlaybackData(void* output, ma_uint32 frameCount); + +private: std::unique_ptr impl_; }; diff --git a/projects/Recorder/main/include/recorder_app.h b/projects/Recorder/main/include/recorder_app.h index 1f7539e2..953e453d 100644 --- a/projects/Recorder/main/include/recorder_app.h +++ b/projects/Recorder/main/include/recorder_app.h @@ -1,6 +1,10 @@ #pragma once #include "audio_engine.h" +#include +#include +#include +#include #include #include @@ -18,45 +22,67 @@ struct RecordingInfo { float duration; }; +struct RecorderState { + AppState state; + std::string statusText; + std::string timerText; + std::string currentFileName; + std::string hintText; + std::vector fileList; + int selectedFileIndex; +}; + +class IRecorderView { +public: + virtual ~IRecorderView() = default; + virtual void update(const RecorderState& state) = 0; + virtual void setActionHandler(std::function handler) = 0; +}; + class RecorderApp { public: - static RecorderApp& instance(); + RecorderApp(); + ~RecorderApp(); bool init(); void deinit(); - // Actions - void toggleRecord(); // Enter: start/stop recording - void pauseResumeRecord(); // P: pause/resume recording - void togglePlay(); // Space: play/pause playback - void stop(); // S: stop current operation - void prevFile(); - void nextFile(); - - // State queries - AppState state() const; - std::string statusText() const; - std::string timerText() const; - std::string currentFileName() const; - std::vector fileList() const; - - // Audio engine access for UI callbacks - AudioEngine& engine() { return engine_; } - - // Scan recordings directory - void scanFiles(); + void setView(IRecorderView* view); + + // Unified action entry point + void onAction(const std::string& action); + + // Call from main loop + void poll(); + + // Build current state snapshot + RecorderState getState() const; private: - RecorderApp() = default; - ~RecorderApp() = default; - RecorderApp(const RecorderApp&) = delete; - RecorderApp& operator=(const RecorderApp&) = delete; + void notifyView(); + void handleToggleRecord(); + void handleTogglePlay(); + void handleTogglePause(); + void handleStop(); + void handlePrevFile(); + void handleNextFile(); + void scanFiles(); + void asyncScanFiles(); std::string generateFilename(); std::string recordingsDir(); + void clampFileIndex(); AudioEngine engine_; + IRecorderView* view_ = nullptr; + + mutable std::mutex filesMutex_; std::vector files_; int currentFileIndex_ = -1; std::string lastRecordingPath_; + + std::future scanFuture_; + std::atomic filesDirty_{false}; + AudioState lastAudioState_ = AudioState::Idle; + uint32_t lastNotifyTime_ = 0; }; diff --git a/projects/Recorder/main/include/ui_recorder.h b/projects/Recorder/main/include/ui_recorder.h index f60d9e9f..9c3d24ca 100644 --- a/projects/Recorder/main/include/ui_recorder.h +++ b/projects/Recorder/main/include/ui_recorder.h @@ -1,22 +1,21 @@ #pragma once +#include "recorder_app.h" #include "lvgl/lvgl.h" -class UiRecorder { +class UiRecorder : public IRecorderView { public: - static UiRecorder& instance(); + UiRecorder() = default; void init(lv_obj_t* parent); - void update(); // call from main loop periodically + void update(const RecorderState& state) override; + void setActionHandler(std::function handler) override; - // Keyboard handling + // Keyboard event from input system void onKeyPressed(uint32_t key_code); private: - UiRecorder() = default; - void buildUi(lv_obj_t* parent); - void refreshDisplay(); lv_obj_t* parent_ = nullptr; lv_obj_t* lblStatus_ = nullptr; @@ -24,7 +23,8 @@ class UiRecorder { lv_obj_t* lblFile_ = nullptr; lv_obj_t* lblHint_ = nullptr; - uint32_t lastUpdate_ = 0; uint32_t lastKeyTime_ = 0; uint32_t lastKeyCode_ = 0; + + std::function actionHandler_; }; diff --git a/projects/Recorder/main/src/audio_engine.cpp b/projects/Recorder/main/src/audio_engine.cpp index 583724b2..27492ed0 100644 --- a/projects/Recorder/main/src/audio_engine.cpp +++ b/projects/Recorder/main/src/audio_engine.cpp @@ -4,7 +4,6 @@ #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" -#include #include struct AudioEngineImpl { @@ -53,14 +52,7 @@ extern "C" { { (void)pOutput; AudioEngine* self = static_cast(pDevice->pUserData); - if (!self->impl_ || !self->impl_->recFile) return; - - size_t bytesToWrite = frameCount * self->impl_->recChannels * sizeof(int16_t); - size_t written = fwrite(pInput, 1, bytesToWrite, self->impl_->recFile); - if (written == bytesToWrite) { - self->impl_->recTotalFrames += frameCount; - if (self->impl_->updateCb) self->impl_->updateCb(); - } + self->onRecordingData(pInput, frameCount); } static void playbackCallback(ma_device* pDevice, void* pOutput, @@ -68,25 +60,7 @@ extern "C" { { (void)pInput; AudioEngine* self = static_cast(pDevice->pUserData); - if (!self->impl_ || !self->impl_->playFile) { - memset(pOutput, 0, frameCount * pDevice->playback.channels * sizeof(int16_t)); - return; - } - - size_t bytesPerFrame = self->impl_->playChannels * sizeof(int16_t); - size_t bytesToRead = frameCount * bytesPerFrame; - size_t read = fread(pOutput, 1, bytesToRead, self->impl_->playFile); - - if (read < bytesToRead) { - memset(static_cast(pOutput) + read, 0, bytesToRead - read); - self->impl_->playCurrentFrame += static_cast(read / bytesPerFrame); - if (read == 0) { - self->impl_->playbackFinished.store(true); - } - } else { - self->impl_->playCurrentFrame += frameCount; - if (self->impl_->updateCb) self->impl_->updateCb(); - } + self->onPlaybackData(pOutput, frameCount); } } @@ -316,3 +290,36 @@ void AudioEngine::setUpdateCallback(UpdateCallback cb) { impl_->updateCb = cb; } + +void AudioEngine::onRecordingData(const void* input, ma_uint32 frameCount) +{ + if (!impl_->recFile) return; + size_t bytesToWrite = frameCount * impl_->recChannels * sizeof(int16_t); + size_t written = fwrite(input, 1, bytesToWrite, impl_->recFile); + if (written == bytesToWrite) { + impl_->recTotalFrames += frameCount; + if (impl_->updateCb) impl_->updateCb(); + } +} + +void AudioEngine::onPlaybackData(void* output, ma_uint32 frameCount) +{ + if (!impl_->playFile) { + memset(output, 0, frameCount * impl_->playChannels * sizeof(int16_t)); + return; + } + size_t bytesPerFrame = impl_->playChannels * sizeof(int16_t); + size_t bytesToRead = frameCount * bytesPerFrame; + size_t read = fread(output, 1, bytesToRead, impl_->playFile); + + if (read < bytesToRead) { + memset(static_cast(output) + read, 0, bytesToRead - read); + impl_->playCurrentFrame += static_cast(read / bytesPerFrame); + if (read == 0) { + impl_->playbackFinished.store(true); + } + } else { + impl_->playCurrentFrame += frameCount; + if (impl_->updateCb) impl_->updateCb(); + } +} diff --git a/projects/Recorder/main/src/main.cpp b/projects/Recorder/main/src/main.cpp index 8297acbe..87f19e0c 100644 --- a/projects/Recorder/main/src/main.cpp +++ b/projects/Recorder/main/src/main.cpp @@ -31,6 +31,7 @@ volatile sig_atomic_t g_quit_requested = 0; lv_obj_t *g_root = nullptr; lv_indev_t *g_keyboard_indev = nullptr; lv_group_t *g_group = nullptr; +UiRecorder *g_ui = nullptr; void request_quit() { @@ -93,29 +94,7 @@ void handle_keyboard_event(lv_event_t *event) request_quit(); return; } - UiRecorder::instance().onKeyPressed(key_code); -} - -void build_ui() -{ - g_root = lv_screen_active(); - lv_obj_set_size(g_root, kScreenWidth, kScreenHeight); - lv_obj_clear_flag(g_root, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_set_style_bg_color(g_root, lv_color_hex(0x1A1A2E), 0); - lv_obj_set_style_bg_opa(g_root, LV_OPA_COVER, 0); - - UiRecorder::instance().init(g_root); - - lv_obj_add_event_cb(g_root, handle_keyboard_event, - static_cast(LV_EVENT_KEYBOARD), nullptr); -#if LV_USE_SDL - lv_obj_add_event_cb(g_root, handle_keyboard_event, LV_EVENT_KEY, nullptr); -#endif - - g_group = lv_group_create(); - lv_group_add_obj(g_group, g_root); - lv_group_focus_obj(g_root); - if (g_keyboard_indev) lv_indev_set_group(g_keyboard_indev, g_group); + if (g_ui) g_ui->onKeyPressed(key_code); } #if LV_USE_EVDEV @@ -208,20 +187,47 @@ int main() std::signal(SIGTERM, handle_signal); std::signal(SIGPIPE, SIG_IGN); - RecorderApp::instance().init(); + RecorderApp app; + UiRecorder ui; + + ui.setActionHandler([&](const std::string& action) { + app.onAction(action); + }); + + app.init(); lv_init(); lv_linux_disp_init(); LV_EVENT_KEYBOARD = lv_event_register_id(); lv_linux_indev_init(); - build_ui(); + + g_root = lv_screen_active(); + lv_obj_set_size(g_root, kScreenWidth, kScreenHeight); + lv_obj_clear_flag(g_root, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_bg_color(g_root, lv_color_hex(0x1A1A2E), 0); + lv_obj_set_style_bg_opa(g_root, LV_OPA_COVER, 0); + + g_ui = &ui; + ui.init(g_root); + app.setView(&ui); + + lv_obj_add_event_cb(g_root, handle_keyboard_event, + static_cast(LV_EVENT_KEYBOARD), nullptr); +#if LV_USE_SDL + lv_obj_add_event_cb(g_root, handle_keyboard_event, LV_EVENT_KEY, nullptr); +#endif + + g_group = lv_group_create(); + lv_group_add_obj(g_group, g_root); + lv_group_focus_obj(g_root); + if (g_keyboard_indev) lv_indev_set_group(g_keyboard_indev, g_group); while (!g_quit_requested) { - UiRecorder::instance().update(); + app.poll(); lv_timer_handler(); usleep(5000); } - RecorderApp::instance().deinit(); + app.deinit(); return 0; } diff --git a/projects/Recorder/main/src/recorder_app.cpp b/projects/Recorder/main/src/recorder_app.cpp index 1e96eace..bd21ef96 100644 --- a/projects/Recorder/main/src/recorder_app.cpp +++ b/projects/Recorder/main/src/recorder_app.cpp @@ -1,10 +1,10 @@ #include "recorder_app.h" #include "wav_file.h" #include "compat/input_keys.h" +#include "lvgl/lvgl.h" #include #include -#include #include #include #include @@ -14,129 +14,100 @@ #include #include -RecorderApp& RecorderApp::instance() -{ - static RecorderApp app; - return app; -} +RecorderApp::RecorderApp() = default; +RecorderApp::~RecorderApp() = default; bool RecorderApp::init() { engine_.initialize(); scanFiles(); + lastAudioState_ = engine_.state(); return true; } void RecorderApp::deinit() { + if (scanFuture_.valid()) { + scanFuture_.wait(); + } engine_.shutdown(); } -void RecorderApp::toggleRecord() +void RecorderApp::setView(IRecorderView* view) { - AppState s = state(); - if (s == AppState::Idle) { - lastRecordingPath_ = generateFilename(); - if (engine_.startRecording(lastRecordingPath_)) { - scanFiles(); - } - } else if (s == AppState::Recording || s == AppState::RecPaused) { - engine_.stopRecording(); - scanFiles(); - } else if (s == AppState::Playing || s == AppState::PlayPaused) { - engine_.stopPlayback(); - lastRecordingPath_ = generateFilename(); - engine_.startRecording(lastRecordingPath_); - } + view_ = view; + notifyView(); } -void RecorderApp::pauseResumeRecord() +void RecorderApp::onAction(const std::string& action) { - AppState s = state(); - if (s == AppState::Recording) { - engine_.pauseRecording(); - } else if (s == AppState::RecPaused) { - engine_.resumeRecording(); + if (action == "toggle_record") { + handleToggleRecord(); + } else if (action == "toggle_play") { + handleTogglePlay(); + } else if (action == "toggle_pause") { + handleTogglePause(); + } else if (action == "stop") { + handleStop(); + } else if (action == "prev_file") { + handlePrevFile(); + } else if (action == "next_file") { + handleNextFile(); } + notifyView(); } -void RecorderApp::togglePlay() +void RecorderApp::poll() { - AppState s = state(); - if (s == AppState::Idle) { - if (files_.empty()) return; - if (currentFileIndex_ < 0 || currentFileIndex_ >= static_cast(files_.size())) { - currentFileIndex_ = static_cast(files_.size()) - 1; - } - engine_.startPlayback(files_[currentFileIndex_].filepath); - } else if (s == AppState::Playing) { - engine_.pausePlayback(); - } else if (s == AppState::PlayPaused) { - engine_.resumePlayback(); - } else if (s == AppState::Recording || s == AppState::RecPaused) { - engine_.stopRecording(); - scanFiles(); - if (files_.empty()) return; - if (currentFileIndex_ < 0 || currentFileIndex_ >= static_cast(files_.size())) { - currentFileIndex_ = static_cast(files_.size()) - 1; - } - engine_.startPlayback(files_[currentFileIndex_].filepath); - } -} + engine_.poll(); -void RecorderApp::stop() -{ - AppState s = state(); - if (s == AppState::Recording || s == AppState::RecPaused) { - engine_.stopRecording(); - scanFiles(); - } else if (s == AppState::Playing || s == AppState::PlayPaused) { - engine_.stopPlayback(); + uint32_t now = lv_tick_get(); + bool needNotify = false; + + if (now - lastNotifyTime_ >= 200) { + lastNotifyTime_ = now; + needNotify = true; } -} -void RecorderApp::prevFile() -{ - if (files_.empty()) return; - if (currentFileIndex_ > 0) currentFileIndex_--; - else currentFileIndex_ = static_cast(files_.size()) - 1; -} + if (filesDirty_.exchange(false)) { + needNotify = true; + } -void RecorderApp::nextFile() -{ - if (files_.empty()) return; - if (currentFileIndex_ < static_cast(files_.size()) - 1) currentFileIndex_++; - else currentFileIndex_ = 0; -} + AudioState current = engine_.state(); + if (current != lastAudioState_) { + lastAudioState_ = current; + needNotify = true; + } -AppState RecorderApp::state() const -{ - switch (engine_.state()) { - case AudioState::Idle: return AppState::Idle; - case AudioState::Recording: return AppState::Recording; - case AudioState::RecPaused: return AppState::RecPaused; - case AudioState::Playing: return AppState::Playing; - case AudioState::PlayPaused: return AppState::PlayPaused; + if (needNotify) { + notifyView(); } - return AppState::Idle; } -std::string RecorderApp::statusText() const +RecorderState RecorderApp::getState() const { - switch (state()) { - case AppState::Idle: return "IDLE"; - case AppState::Recording: return "RECORDING"; - case AppState::RecPaused: return "REC PAUSED"; - case AppState::Playing: return "PLAYING"; - case AppState::PlayPaused: return "PLAY PAUSED"; + RecorderState rs; + rs.state = [this]() { + switch (engine_.state()) { + case AudioState::Idle: return AppState::Idle; + case AudioState::Recording: return AppState::Recording; + case AudioState::RecPaused: return AppState::RecPaused; + case AudioState::Playing: return AppState::Playing; + case AudioState::PlayPaused: return AppState::PlayPaused; + } + return AppState::Idle; + }(); + + switch (rs.state) { + case AppState::Idle: rs.statusText = "IDLE"; break; + case AppState::Recording: rs.statusText = "RECORDING"; break; + case AppState::RecPaused: rs.statusText = "REC PAUSED"; break; + case AppState::Playing: rs.statusText = "PLAYING"; break; + case AppState::PlayPaused: rs.statusText = "PLAY PAUSED"; break; } - return "UNKNOWN"; -} -std::string RecorderApp::timerText() const -{ std::ostringstream oss; - switch (state()) { + switch (rs.state) { case AppState::Recording: case AppState::RecPaused: { float d = engine_.recordingDuration(); @@ -163,29 +134,147 @@ std::string RecorderApp::timerText() const default: break; } - return oss.str(); + rs.timerText = oss.str(); + + { + std::lock_guard lock(filesMutex_); + rs.fileList = files_; + rs.selectedFileIndex = currentFileIndex_; + + if (rs.state == AppState::Recording || rs.state == AppState::RecPaused) { + size_t pos = lastRecordingPath_.find_last_of('/'); + rs.currentFileName = lastRecordingPath_.substr(pos + 1); + } else if (!files_.empty() && currentFileIndex_ >= 0 && currentFileIndex_ < static_cast(files_.size())) { + rs.currentFileName = files_[currentFileIndex_].filename; + } else { + rs.currentFileName = "No recordings"; + } + } + + switch (rs.state) { + case AppState::Idle: rs.hintText = "[Enter]Rec [Space]Play [<- ->]File [Esc]Quit"; break; + case AppState::Recording: rs.hintText = "[P]Pause [Enter]Stop"; break; + case AppState::RecPaused: rs.hintText = "[P]Resume [Enter]Stop"; break; + case AppState::Playing: rs.hintText = "[Space]Pause [S]Stop [<- ->]File"; break; + case AppState::PlayPaused: rs.hintText = "[Space]Resume [S]Stop"; break; + } + + return rs; } -std::string RecorderApp::currentFileName() const +void RecorderApp::notifyView() { - if (state() == AppState::Recording || state() == AppState::RecPaused) { - size_t pos = lastRecordingPath_.find_last_of('/'); - return lastRecordingPath_.substr(pos + 1); + if (view_) { + view_->update(getState()); } - if (state() == AppState::Playing || state() == AppState::PlayPaused) { - if (currentFileIndex_ >= 0 && currentFileIndex_ < static_cast(files_.size())) { - return files_[currentFileIndex_].filename; - } +} + +void RecorderApp::handleToggleRecord() +{ + switch (engine_.state()) { + case AudioState::Idle: + lastRecordingPath_ = generateFilename(); + engine_.startRecording(lastRecordingPath_); + asyncScanFiles(); + break; + case AudioState::Recording: + case AudioState::RecPaused: + engine_.stopRecording(); + asyncScanFiles(); + break; + case AudioState::Playing: + case AudioState::PlayPaused: + engine_.stopPlayback(); + lastRecordingPath_ = generateFilename(); + engine_.startRecording(lastRecordingPath_); + break; + } +} + +void RecorderApp::handleTogglePlay() +{ + switch (engine_.state()) { + case AudioState::Idle: + if (files_.empty()) return; + clampFileIndex(); + engine_.startPlayback(files_[currentFileIndex_].filepath); + break; + case AudioState::Playing: + engine_.pausePlayback(); + break; + case AudioState::PlayPaused: + engine_.resumePlayback(); + break; + case AudioState::Recording: + case AudioState::RecPaused: + engine_.stopRecording(); + asyncScanFiles(); + if (files_.empty()) return; + clampFileIndex(); + engine_.startPlayback(files_[currentFileIndex_].filepath); + break; + } +} + +void RecorderApp::handleTogglePause() +{ + switch (engine_.state()) { + case AudioState::Recording: + engine_.pauseRecording(); + break; + case AudioState::RecPaused: + engine_.resumeRecording(); + break; + case AudioState::Playing: + engine_.pausePlayback(); + break; + case AudioState::PlayPaused: + engine_.resumePlayback(); + break; + default: + break; } - if (!files_.empty() && currentFileIndex_ >= 0 && currentFileIndex_ < static_cast(files_.size())) { - return files_[currentFileIndex_].filename; +} + +void RecorderApp::handleStop() +{ + switch (engine_.state()) { + case AudioState::Recording: + case AudioState::RecPaused: + engine_.stopRecording(); + asyncScanFiles(); + break; + case AudioState::Playing: + case AudioState::PlayPaused: + engine_.stopPlayback(); + break; + default: + break; } - return "No recordings"; } -std::vector RecorderApp::fileList() const +void RecorderApp::handlePrevFile() +{ + std::lock_guard lock(filesMutex_); + if (files_.empty()) return; + if (currentFileIndex_ > 0) currentFileIndex_--; + else currentFileIndex_ = static_cast(files_.size()) - 1; +} + +void RecorderApp::handleNextFile() +{ + std::lock_guard lock(filesMutex_); + if (files_.empty()) return; + if (currentFileIndex_ < static_cast(files_.size()) - 1) currentFileIndex_++; + else currentFileIndex_ = 0; +} + +void RecorderApp::clampFileIndex() { - return files_; + std::lock_guard lock(filesMutex_); + if (currentFileIndex_ < 0 || currentFileIndex_ >= static_cast(files_.size())) { + currentFileIndex_ = files_.empty() ? -1 : static_cast(files_.size()) - 1; + } } std::string RecorderApp::recordingsDir() @@ -227,7 +316,7 @@ std::string RecorderApp::generateFilename() void RecorderApp::scanFiles() { - files_.clear(); + std::vector result; std::string dir = recordingsDir(); DIR* d = opendir(dir.c_str()); if (!d) return; @@ -250,16 +339,31 @@ void RecorderApp::scanFiles() info.filename = entry->d_name; info.filepath = path; info.duration = static_cast(dataSize) / (sampleRate * channels * sizeof(int16_t)); - files_.push_back(info); + result.push_back(info); } fclose(f); } closedir(d); - std::sort(files_.begin(), files_.end(), + std::sort(result.begin(), result.end(), [](const RecordingInfo& a, const RecordingInfo& b) { return a.filename > b.filename; }); - if (!files_.empty() && currentFileIndex_ < 0) { - currentFileIndex_ = 0; + { + std::lock_guard lock(filesMutex_); + files_ = std::move(result); + if (!files_.empty() && currentFileIndex_ < 0) { + currentFileIndex_ = 0; + } + } +} + +void RecorderApp::asyncScanFiles() +{ + if (scanFuture_.valid()) { + scanFuture_.wait(); } + scanFuture_ = std::async(std::launch::async, [this]() { + scanFiles(); + filesDirty_.store(true); + }); } diff --git a/projects/Recorder/main/src/ui_recorder.cpp b/projects/Recorder/main/src/ui_recorder.cpp index d3509a57..d1d07be7 100644 --- a/projects/Recorder/main/src/ui_recorder.cpp +++ b/projects/Recorder/main/src/ui_recorder.cpp @@ -1,15 +1,6 @@ #include "ui_recorder.h" -#include "recorder_app.h" #include "compat/input_keys.h" -#include - -UiRecorder& UiRecorder::instance() -{ - static UiRecorder ui; - return ui; -} - void UiRecorder::init(lv_obj_t* parent) { parent_ = parent; @@ -23,21 +14,18 @@ void UiRecorder::buildUi(lv_obj_t* parent) lv_obj_set_style_bg_color(parent, lv_color_hex(0x1A1A2E), 0); lv_obj_set_style_bg_opa(parent, LV_OPA_COVER, 0); - // Status label (top left) lblStatus_ = lv_label_create(parent); lv_obj_set_pos(lblStatus_, 10, 10); lv_obj_set_style_text_font(lblStatus_, &lv_font_montserrat_14, 0); lv_obj_set_style_text_color(lblStatus_, lv_color_hex(0xE0E0E0), 0); lv_label_set_text(lblStatus_, "IDLE"); - // Timer label (top right) lblTimer_ = lv_label_create(parent); lv_obj_set_pos(lblTimer_, 220, 10); lv_obj_set_style_text_font(lblTimer_, &lv_font_montserrat_14, 0); lv_obj_set_style_text_color(lblTimer_, lv_color_hex(0xE0E0E0), 0); lv_label_set_text(lblTimer_, ""); - // File name label (center) lblFile_ = lv_label_create(parent); lv_obj_set_pos(lblFile_, 10, 55); lv_obj_set_width(lblFile_, 300); @@ -46,7 +34,6 @@ void UiRecorder::buildUi(lv_obj_t* parent) lv_label_set_long_mode(lblFile_, LV_LABEL_LONG_SCROLL_CIRCULAR); lv_label_set_text(lblFile_, "No recordings"); - // Hint label (bottom) lblHint_ = lv_label_create(parent); lv_obj_set_pos(lblHint_, 10, 142); lv_obj_set_width(lblHint_, 300); @@ -55,41 +42,18 @@ void UiRecorder::buildUi(lv_obj_t* parent) lv_label_set_text(lblHint_, "[Enter]Rec [P]Play [S]Stop [<- ->]File [Esc]Quit"); } -void UiRecorder::update() +void UiRecorder::update(const RecorderState& state) { - uint32_t now = lv_tick_get(); - if (now - lastUpdate_ < 200) return; - lastUpdate_ = now; - - RecorderApp::instance().engine().poll(); - refreshDisplay(); + if (!lblStatus_) return; + lv_label_set_text(lblStatus_, state.statusText.c_str()); + lv_label_set_text(lblTimer_, state.timerText.c_str()); + lv_label_set_text(lblFile_, state.currentFileName.c_str()); + lv_label_set_text(lblHint_, state.hintText.c_str()); } -void UiRecorder::refreshDisplay() +void UiRecorder::setActionHandler(std::function handler) { - auto& app = RecorderApp::instance(); - lv_label_set_text(lblStatus_, app.statusText().c_str()); - lv_label_set_text(lblTimer_, app.timerText().c_str()); - lv_label_set_text(lblFile_, app.currentFileName().c_str()); - - auto s = app.state(); - switch (s) { - case AppState::Idle: - lv_label_set_text(lblHint_, "[Enter]Rec [Space]Play [<- ->]File [Esc]Quit"); - break; - case AppState::Recording: - lv_label_set_text(lblHint_, "[P]Pause [Enter]Stop"); - break; - case AppState::RecPaused: - lv_label_set_text(lblHint_, "[P]Resume [Enter]Stop"); - break; - case AppState::Playing: - lv_label_set_text(lblHint_, "[Space]Pause [S]Stop [<- ->]File"); - break; - case AppState::PlayPaused: - lv_label_set_text(lblHint_, "[Space]Resume [S]Stop"); - break; - } + actionHandler_ = handler; } void UiRecorder::onKeyPressed(uint32_t key_code) @@ -99,33 +63,32 @@ void UiRecorder::onKeyPressed(uint32_t key_code) lastKeyCode_ = key_code; lastKeyTime_ = now; - auto& app = RecorderApp::instance(); + if (!actionHandler_) return; switch (key_code) { case KEY_ENTER: case KEY_KPENTER: - app.toggleRecord(); // Enter: start/stop recording + actionHandler_("toggle_record"); break; case KEY_P: - app.pauseResumeRecord(); // P: pause/resume recording + actionHandler_("toggle_pause"); break; case KEY_SPACE: - app.togglePlay(); // Space: play/pause playback + actionHandler_("toggle_play"); break; case KEY_S: - app.stop(); // S: stop current operation + actionHandler_("stop"); break; case KEY_ESC: - app.stop(); // Esc: stop and quit + actionHandler_("stop"); break; case KEY_LEFT: - app.prevFile(); + actionHandler_("prev_file"); break; case KEY_RIGHT: - app.nextFile(); + actionHandler_("next_file"); break; default: break; } - refreshDisplay(); } From d3b3cb2dc166b787509e51e50c1843e82169734f Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Thu, 28 May 2026 16:48:49 +0800 Subject: [PATCH 04/11] refactor(Recorder): redesign UI with 6-page layout and light theme - Add Home, FileList, Recording, RecPaused, SaveConfirm, Playback pages - Implement app-level state machine for page navigation - Support F4-F8 physical keys (1-5 fallback for SDL) - Add file delete logic bound to Del key - Switch to light theme matching reference design --- projects/Recorder/main/include/recorder_app.h | 21 +- projects/Recorder/main/include/ui_recorder.h | 73 ++- projects/Recorder/main/src/main.cpp | 16 +- projects/Recorder/main/src/recorder_app.cpp | 141 ++++- projects/Recorder/main/src/ui_recorder.cpp | 496 ++++++++++++++++-- 5 files changed, 663 insertions(+), 84 deletions(-) diff --git a/projects/Recorder/main/include/recorder_app.h b/projects/Recorder/main/include/recorder_app.h index 953e453d..2c6b0cf2 100644 --- a/projects/Recorder/main/include/recorder_app.h +++ b/projects/Recorder/main/include/recorder_app.h @@ -9,11 +9,13 @@ #include enum class AppState { - Idle, - Recording, - RecPaused, - Playing, - PlayPaused, + Idle, // Home entry page + Recording, // Recording in progress + RecPaused, // Recording paused + SaveConfirm, // Save recording dialog + FileList, // Saved recordings list + Playing, // Playback in progress + PlayPaused, // Playback paused }; struct RecordingInfo { @@ -30,6 +32,8 @@ struct RecorderState { std::string hintText; std::vector fileList; int selectedFileIndex; + std::string sampleRate; // e.g. "48k" + std::string playbackSpeed; // e.g. "1.0X" }; class IRecorderView { @@ -60,12 +64,15 @@ class RecorderApp { private: void notifyView(); + void syncStateFromEngine(); + void handleToggleRecord(); void handleTogglePlay(); void handleTogglePause(); void handleStop(); void handlePrevFile(); void handleNextFile(); + void handleDelete(); void scanFiles(); void asyncScanFiles(); @@ -85,4 +92,8 @@ class RecorderApp { std::atomic filesDirty_{false}; AudioState lastAudioState_ = AudioState::Idle; uint32_t lastNotifyTime_ = 0; + + AppState appState_ = AppState::Idle; + std::string sampleRate_ = "48k"; + std::string playbackSpeed_ = "1.0X"; }; diff --git a/projects/Recorder/main/include/ui_recorder.h b/projects/Recorder/main/include/ui_recorder.h index 9c3d24ca..ef3ef582 100644 --- a/projects/Recorder/main/include/ui_recorder.h +++ b/projects/Recorder/main/include/ui_recorder.h @@ -2,6 +2,17 @@ #include "recorder_app.h" #include "lvgl/lvgl.h" +#include +#include + +enum class UiPage { + Home, + FileList, + Recording, + RecPaused, + SaveConfirm, + Playback, +}; class UiRecorder : public IRecorderView { public: @@ -16,15 +27,67 @@ class UiRecorder : public IRecorderView { private: void buildUi(lv_obj_t* parent); + void createStatusBar(lv_obj_t* parent); + void createBottomBar(lv_obj_t* parent); + + lv_obj_t* createPageContainer(lv_obj_t* parent); + void createPageHome(lv_obj_t* page); + void createPageFileList(lv_obj_t* page); + void createPageRecording(lv_obj_t* page); + void createPageRecPaused(lv_obj_t* page); + void createPageSaveConfirm(lv_obj_t* page); + void createPagePlayback(lv_obj_t* page); + + void switchPage(UiPage page); + void updatePageContent(const RecorderState& state); + void updateBottomLabels(UiPage page, bool isPaused); + void handleActionForPage(UiPage page, int btnIndex); lv_obj_t* parent_ = nullptr; - lv_obj_t* lblStatus_ = nullptr; - lv_obj_t* lblTimer_ = nullptr; - lv_obj_t* lblFile_ = nullptr; - lv_obj_t* lblHint_ = nullptr; + UiPage currentPage_ = UiPage::Home; + + // Status bar + lv_obj_t* statusBar_ = nullptr; + lv_obj_t* lblStatusText_ = nullptr; + + // Bottom bar + lv_obj_t* bottomBar_ = nullptr; + std::array lblBottomBtns_{}; + + // Pages + std::array pages_{}; + + // Home + lv_obj_t* lblHomeReady_ = nullptr; + + // FileList + lv_obj_t* lblFileListEmpty_ = nullptr; + std::array lblFileListItems_{}; + int fileListOffset_ = 0; + + // Recording + lv_obj_t* lblRecFilename_ = nullptr; + lv_obj_t* recWaveContainer_ = nullptr; + lv_obj_t* lblRecTimer_ = nullptr; + + // RecPaused + lv_obj_t* lblPauseFilename_ = nullptr; + lv_obj_t* pauseLine_ = nullptr; + lv_obj_t* lblPauseTimer_ = nullptr; + + // SaveConfirm + lv_obj_t* lblSaveFilename_ = nullptr; + + // Playback + lv_obj_t* lblPlayFilename_ = nullptr; + lv_obj_t* playWaveContainer_ = nullptr; + lv_obj_t* lblPlayTimer_ = nullptr; + + // State cache + RecorderState lastState_{}; + bool isPlaybackPaused_ = false; uint32_t lastKeyTime_ = 0; uint32_t lastKeyCode_ = 0; - std::function actionHandler_; }; diff --git a/projects/Recorder/main/src/main.cpp b/projects/Recorder/main/src/main.cpp index 87f19e0c..9987dc9c 100644 --- a/projects/Recorder/main/src/main.cpp +++ b/projects/Recorder/main/src/main.cpp @@ -32,6 +32,7 @@ lv_obj_t *g_root = nullptr; lv_indev_t *g_keyboard_indev = nullptr; lv_group_t *g_group = nullptr; UiRecorder *g_ui = nullptr; +RecorderApp *g_app = nullptr; void request_quit() { @@ -83,6 +84,12 @@ void handle_keyboard_event(lv_event_t *event) case 'p': case 'P': key_code = KEY_P; break; case 's': case 'S': key_code = KEY_S; break; case ' ': key_code = KEY_SPACE; break; + // Map digits to F4-F8 for SDL testing convenience + case '1': key_code = KEY_F4; break; + case '2': key_code = KEY_F5; break; + case '3': key_code = KEY_F6; break; + case '4': key_code = KEY_F7; break; + case '5': key_code = KEY_F8; break; default: key_code = lv_key; break; } pressed = true; @@ -189,9 +196,14 @@ int main() RecorderApp app; UiRecorder ui; + g_app = &app; ui.setActionHandler([&](const std::string& action) { - app.onAction(action); + if (action == "quit") { + request_quit(); + } else { + app.onAction(action); + } }); app.init(); @@ -204,7 +216,7 @@ int main() g_root = lv_screen_active(); lv_obj_set_size(g_root, kScreenWidth, kScreenHeight); lv_obj_clear_flag(g_root, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_set_style_bg_color(g_root, lv_color_hex(0x1A1A2E), 0); + lv_obj_set_style_bg_color(g_root, lv_color_hex(0xE8E8EC), 0); lv_obj_set_style_bg_opa(g_root, LV_OPA_COVER, 0); g_ui = &ui; diff --git a/projects/Recorder/main/src/recorder_app.cpp b/projects/Recorder/main/src/recorder_app.cpp index bd21ef96..d55e0fc2 100644 --- a/projects/Recorder/main/src/recorder_app.cpp +++ b/projects/Recorder/main/src/recorder_app.cpp @@ -22,6 +22,7 @@ bool RecorderApp::init() engine_.initialize(); scanFiles(); lastAudioState_ = engine_.state(); + appState_ = AppState::Idle; return true; } @@ -53,6 +54,62 @@ void RecorderApp::onAction(const std::string& action) handlePrevFile(); } else if (action == "next_file") { handleNextFile(); + } else if (action == "list") { + appState_ = AppState::FileList; + } else if (action == "home") { + appState_ = AppState::Idle; + } else if (action == "done") { + if (engine_.state() == AudioState::Recording || engine_.state() == AudioState::RecPaused) { + engine_.stopRecording(); + asyncScanFiles(); + } + appState_ = AppState::SaveConfirm; + } else if (action == "save") { + appState_ = AppState::Idle; + } else if (action == "exit") { + switch (appState_) { + case AppState::Recording: + case AppState::RecPaused: + engine_.stopRecording(); + asyncScanFiles(); + appState_ = AppState::Idle; + break; + case AppState::SaveConfirm: + appState_ = AppState::Idle; + break; + case AppState::FileList: + appState_ = AppState::Idle; + break; + case AppState::Playing: + case AppState::PlayPaused: + engine_.stopPlayback(); + appState_ = AppState::FileList; + break; + default: + break; + } + } else if (action == "quit") { + // Handled in main.cpp + } else if (action == "cycle_rate") { + if (sampleRate_ == "16k") sampleRate_ = "44.1k"; + else if (sampleRate_ == "44.1k") sampleRate_ = "48k"; + else sampleRate_ = "16k"; + } else if (action == "speed") { + if (playbackSpeed_ == "0.5X") playbackSpeed_ = "0.75X"; + else if (playbackSpeed_ == "0.75X") playbackSpeed_ = "1.0X"; + else if (playbackSpeed_ == "1.0X") playbackSpeed_ = "1.25X"; + else if (playbackSpeed_ == "1.25X") playbackSpeed_ = "1.5X"; + else if (playbackSpeed_ == "1.5X") playbackSpeed_ = "1.75X"; + else if (playbackSpeed_ == "1.75X") playbackSpeed_ = "2.0X"; + else playbackSpeed_ = "0.5X"; + } else if (action == "seek_back") { + // TODO: implement seek + } else if (action == "seek_fwd") { + // TODO: implement seek + } else if (action == "rename") { + // TODO: implement rename + } else if (action == "delete") { + handleDelete(); } notifyView(); } @@ -76,6 +133,7 @@ void RecorderApp::poll() AudioState current = engine_.state(); if (current != lastAudioState_) { lastAudioState_ = current; + syncStateFromEngine(); needNotify = true; } @@ -84,26 +142,48 @@ void RecorderApp::poll() } } +void RecorderApp::syncStateFromEngine() +{ + AudioState audio = engine_.state(); + switch (audio) { + case AudioState::Idle: + if (appState_ == AppState::Recording || appState_ == AppState::RecPaused || + appState_ == AppState::Playing || appState_ == AppState::PlayPaused) { + appState_ = AppState::Idle; + } + break; + case AudioState::Recording: + appState_ = AppState::Recording; + break; + case AudioState::RecPaused: + appState_ = AppState::RecPaused; + break; + case AudioState::Playing: + if (appState_ != AppState::Playing && appState_ != AppState::PlayPaused) { + appState_ = AppState::Playing; + } + break; + case AudioState::PlayPaused: + if (appState_ != AppState::Playing && appState_ != AppState::PlayPaused) { + appState_ = AppState::PlayPaused; + } + break; + } +} + RecorderState RecorderApp::getState() const { RecorderState rs; - rs.state = [this]() { - switch (engine_.state()) { - case AudioState::Idle: return AppState::Idle; - case AudioState::Recording: return AppState::Recording; - case AudioState::RecPaused: return AppState::RecPaused; - case AudioState::Playing: return AppState::Playing; - case AudioState::PlayPaused: return AppState::PlayPaused; - } - return AppState::Idle; - }(); + rs.state = appState_; switch (rs.state) { - case AppState::Idle: rs.statusText = "IDLE"; break; - case AppState::Recording: rs.statusText = "RECORDING"; break; - case AppState::RecPaused: rs.statusText = "REC PAUSED"; break; - case AppState::Playing: rs.statusText = "PLAYING"; break; - case AppState::PlayPaused: rs.statusText = "PLAY PAUSED"; break; + case AppState::Idle: rs.statusText = "Recorder"; break; + case AppState::Recording: rs.statusText = "Recording"; break; + case AppState::RecPaused: rs.statusText = "Paused"; break; + case AppState::SaveConfirm: rs.statusText = "Save"; break; + case AppState::FileList: rs.statusText = "Files"; break; + case AppState::Playing: rs.statusText = "Playing"; break; + case AppState::PlayPaused: rs.statusText = "Paused"; break; } std::ostringstream oss; @@ -141,23 +221,19 @@ RecorderState RecorderApp::getState() const rs.fileList = files_; rs.selectedFileIndex = currentFileIndex_; - if (rs.state == AppState::Recording || rs.state == AppState::RecPaused) { + if (rs.state == AppState::Recording || rs.state == AppState::RecPaused || rs.state == AppState::SaveConfirm) { size_t pos = lastRecordingPath_.find_last_of('/'); rs.currentFileName = lastRecordingPath_.substr(pos + 1); } else if (!files_.empty() && currentFileIndex_ >= 0 && currentFileIndex_ < static_cast(files_.size())) { rs.currentFileName = files_[currentFileIndex_].filename; } else { - rs.currentFileName = "No recordings"; + rs.currentFileName = ""; } } - switch (rs.state) { - case AppState::Idle: rs.hintText = "[Enter]Rec [Space]Play [<- ->]File [Esc]Quit"; break; - case AppState::Recording: rs.hintText = "[P]Pause [Enter]Stop"; break; - case AppState::RecPaused: rs.hintText = "[P]Resume [Enter]Stop"; break; - case AppState::Playing: rs.hintText = "[Space]Pause [S]Stop [<- ->]File"; break; - case AppState::PlayPaused: rs.hintText = "[Space]Resume [S]Stop"; break; - } + rs.sampleRate = sampleRate_; + rs.playbackSpeed = playbackSpeed_; + rs.hintText = ""; return rs; } @@ -269,6 +345,23 @@ void RecorderApp::handleNextFile() else currentFileIndex_ = 0; } +void RecorderApp::handleDelete() +{ + std::lock_guard lock(filesMutex_); + if (files_.empty() || currentFileIndex_ < 0 || currentFileIndex_ >= static_cast(files_.size())) { + return; + } + std::string path = files_[currentFileIndex_].filepath; + files_.erase(files_.begin() + currentFileIndex_); + if (currentFileIndex_ >= static_cast(files_.size())) { + currentFileIndex_ = files_.empty() ? -1 : static_cast(files_.size()) - 1; + } + // Delete file in background + std::async(std::launch::async, [path]() { + unlink(path.c_str()); + }); +} + void RecorderApp::clampFileIndex() { std::lock_guard lock(filesMutex_); diff --git a/projects/Recorder/main/src/ui_recorder.cpp b/projects/Recorder/main/src/ui_recorder.cpp index d1d07be7..7b9213a2 100644 --- a/projects/Recorder/main/src/ui_recorder.cpp +++ b/projects/Recorder/main/src/ui_recorder.cpp @@ -1,6 +1,27 @@ #include "ui_recorder.h" #include "compat/input_keys.h" +LV_FONT_DECLARE(lv_font_montserrat_16) +LV_FONT_DECLARE(lv_font_montserrat_26) + +// Layout constants +constexpr int kScreenW = 320; +constexpr int kScreenH = 170; +constexpr int kStatusH = 30; +constexpr int kBottomH = 30; +constexpr int kContentH = kScreenH - kStatusH - kBottomH; // 110 +constexpr int kBtnW = kScreenW / 5; // 64 + +// Colors (light theme matching reference image) +const lv_color_t kColorBg = lv_color_hex(0xE8E8EC); +const lv_color_t kColorStatusBar = lv_color_hex(0x555555); +const lv_color_t kColorStatusText = lv_color_hex(0xFFFFFF); +const lv_color_t kColorText = lv_color_hex(0x222222); +const lv_color_t kColorTextGray = lv_color_hex(0x888888); +const lv_color_t kColorBottomBar = lv_color_hex(0xD8D8E0); +const lv_color_t kColorWaveBg = lv_color_hex(0xC8C8D0); +const lv_color_t kColorHighlight = lv_color_hex(0x0066CC); + void UiRecorder::init(lv_obj_t* parent) { parent_ = parent; @@ -9,46 +30,357 @@ void UiRecorder::init(lv_obj_t* parent) void UiRecorder::buildUi(lv_obj_t* parent) { - lv_obj_set_size(parent, 320, 170); + lv_obj_set_size(parent, kScreenW, kScreenH); lv_obj_clear_flag(parent, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_set_style_bg_color(parent, lv_color_hex(0x1A1A2E), 0); + lv_obj_set_style_bg_color(parent, kColorBg, 0); lv_obj_set_style_bg_opa(parent, LV_OPA_COVER, 0); - lblStatus_ = lv_label_create(parent); - lv_obj_set_pos(lblStatus_, 10, 10); - lv_obj_set_style_text_font(lblStatus_, &lv_font_montserrat_14, 0); - lv_obj_set_style_text_color(lblStatus_, lv_color_hex(0xE0E0E0), 0); - lv_label_set_text(lblStatus_, "IDLE"); - - lblTimer_ = lv_label_create(parent); - lv_obj_set_pos(lblTimer_, 220, 10); - lv_obj_set_style_text_font(lblTimer_, &lv_font_montserrat_14, 0); - lv_obj_set_style_text_color(lblTimer_, lv_color_hex(0xE0E0E0), 0); - lv_label_set_text(lblTimer_, ""); - - lblFile_ = lv_label_create(parent); - lv_obj_set_pos(lblFile_, 10, 55); - lv_obj_set_width(lblFile_, 300); - lv_obj_set_style_text_font(lblFile_, &lv_font_montserrat_12, 0); - lv_obj_set_style_text_color(lblFile_, lv_color_hex(0xA0A0A0), 0); - lv_label_set_long_mode(lblFile_, LV_LABEL_LONG_SCROLL_CIRCULAR); - lv_label_set_text(lblFile_, "No recordings"); - - lblHint_ = lv_label_create(parent); - lv_obj_set_pos(lblHint_, 10, 142); - lv_obj_set_width(lblHint_, 300); - lv_obj_set_style_text_font(lblHint_, &lv_font_montserrat_10, 0); - lv_obj_set_style_text_color(lblHint_, lv_color_hex(0x606060), 0); - lv_label_set_text(lblHint_, "[Enter]Rec [P]Play [S]Stop [<- ->]File [Esc]Quit"); + createStatusBar(parent); + createBottomBar(parent); + + pages_[static_cast(UiPage::Home)] = createPageContainer(parent); + pages_[static_cast(UiPage::FileList)] = createPageContainer(parent); + pages_[static_cast(UiPage::Recording)] = createPageContainer(parent); + pages_[static_cast(UiPage::RecPaused)] = createPageContainer(parent); + pages_[static_cast(UiPage::SaveConfirm)] = createPageContainer(parent); + pages_[static_cast(UiPage::Playback)] = createPageContainer(parent); + + createPageHome(pages_[static_cast(UiPage::Home)]); + createPageFileList(pages_[static_cast(UiPage::FileList)]); + createPageRecording(pages_[static_cast(UiPage::Recording)]); + createPageRecPaused(pages_[static_cast(UiPage::RecPaused)]); + createPageSaveConfirm(pages_[static_cast(UiPage::SaveConfirm)]); + createPagePlayback(pages_[static_cast(UiPage::Playback)]); + + switchPage(UiPage::Home); +} + +void UiRecorder::createStatusBar(lv_obj_t* parent) +{ + statusBar_ = lv_obj_create(parent); + lv_obj_set_size(statusBar_, kScreenW, kStatusH); + lv_obj_set_pos(statusBar_, 0, 0); + lv_obj_clear_flag(statusBar_, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_bg_color(statusBar_, kColorStatusBar, 0); + lv_obj_set_style_bg_opa(statusBar_, LV_OPA_COVER, 0); + lv_obj_set_style_border_width(statusBar_, 0, 0); + lv_obj_set_style_pad_all(statusBar_, 0, 0); + lv_obj_set_style_radius(statusBar_, 0, 0); + + lblStatusText_ = lv_label_create(statusBar_); + lv_obj_set_pos(lblStatusText_, 8, 6); + lv_obj_set_style_text_font(lblStatusText_, &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblStatusText_, kColorStatusText, 0); + lv_label_set_text(lblStatusText_, "Recorder"); +} + +void UiRecorder::createBottomBar(lv_obj_t* parent) +{ + bottomBar_ = lv_obj_create(parent); + lv_obj_set_size(bottomBar_, kScreenW, kBottomH); + lv_obj_set_pos(bottomBar_, 0, kScreenH - kBottomH); + lv_obj_clear_flag(bottomBar_, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_bg_color(bottomBar_, kColorBottomBar, 0); + lv_obj_set_style_bg_opa(bottomBar_, LV_OPA_COVER, 0); + lv_obj_set_style_border_width(bottomBar_, 0, 0); + lv_obj_set_style_pad_all(bottomBar_, 0, 0); + lv_obj_set_style_radius(bottomBar_, 0, 0); + + for (int i = 0; i < 5; i++) { + lblBottomBtns_[i] = lv_label_create(bottomBar_); + lv_obj_set_pos(lblBottomBtns_[i], i * kBtnW, 0); + lv_obj_set_size(lblBottomBtns_[i], kBtnW, kBottomH); + lv_obj_set_style_text_font(lblBottomBtns_[i], &lv_font_montserrat_10, 0); + lv_obj_set_style_text_color(lblBottomBtns_[i], kColorText, 0); + lv_obj_set_style_text_align(lblBottomBtns_[i], LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_text(lblBottomBtns_[i], "--"); + lv_obj_set_style_pad_top(lblBottomBtns_[i], 8, 0); + } +} + +lv_obj_t* UiRecorder::createPageContainer(lv_obj_t* parent) +{ + lv_obj_t* page = lv_obj_create(parent); + lv_obj_set_size(page, kScreenW, kContentH); + lv_obj_set_pos(page, 0, kStatusH); + lv_obj_clear_flag(page, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_bg_color(page, kColorBg, 0); + lv_obj_set_style_bg_opa(page, LV_OPA_COVER, 0); + lv_obj_set_style_border_width(page, 0, 0); + lv_obj_set_style_pad_all(page, 0, 0); + lv_obj_set_style_radius(page, 0, 0); + lv_obj_add_flag(page, LV_OBJ_FLAG_HIDDEN); + return page; +} + +// ---------- Home Page ---------- +void UiRecorder::createPageHome(lv_obj_t* page) +{ + lblHomeReady_ = lv_label_create(page); + lv_obj_set_style_text_font(lblHomeReady_, &lv_font_montserrat_26, 0); + lv_obj_set_style_text_color(lblHomeReady_, kColorText, 0); + lv_label_set_text(lblHomeReady_, "Ready"); + lv_obj_center(lblHomeReady_); +} + +// ---------- File List Page ---------- +void UiRecorder::createPageFileList(lv_obj_t* page) +{ + for (int i = 0; i < 5; i++) { + lblFileListItems_[i] = lv_label_create(page); + lv_obj_set_pos(lblFileListItems_[i], 10, 5 + i * 20); + lv_obj_set_width(lblFileListItems_[i], 300); + lv_obj_set_style_text_font(lblFileListItems_[i], &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblFileListItems_[i], kColorText, 0); + lv_label_set_text(lblFileListItems_[i], ""); + lv_obj_add_flag(lblFileListItems_[i], LV_OBJ_FLAG_HIDDEN); + } + + lblFileListEmpty_ = lv_label_create(page); + lv_obj_set_style_text_font(lblFileListEmpty_, &lv_font_montserrat_16, 0); + lv_obj_set_style_text_color(lblFileListEmpty_, kColorTextGray, 0); + lv_label_set_text(lblFileListEmpty_, "Empty"); + lv_obj_center(lblFileListEmpty_); +} + +// ---------- Recording Page ---------- +void UiRecorder::createPageRecording(lv_obj_t* page) +{ + lblRecFilename_ = lv_label_create(page); + lv_obj_set_pos(lblRecFilename_, 10, 5); + lv_obj_set_width(lblRecFilename_, 300); + lv_obj_set_style_text_font(lblRecFilename_, &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblRecFilename_, kColorText, 0); + lv_label_set_long_mode(lblRecFilename_, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_style_text_align(lblRecFilename_, LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_text(lblRecFilename_, ""); + + recWaveContainer_ = lv_obj_create(page); + lv_obj_set_size(recWaveContainer_, 280, 50); + lv_obj_set_pos(recWaveContainer_, 20, 30); + lv_obj_set_style_bg_color(recWaveContainer_, kColorWaveBg, 0); + lv_obj_set_style_bg_opa(recWaveContainer_, LV_OPA_COVER, 0); + lv_obj_set_style_border_color(recWaveContainer_, lv_color_hex(0xAAAAAA), 0); + lv_obj_set_style_border_width(recWaveContainer_, 1, 0); + lv_obj_clear_flag(recWaveContainer_, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_radius(recWaveContainer_, 4, 0); + + lblRecTimer_ = lv_label_create(page); + lv_obj_set_pos(lblRecTimer_, 0, 88); + lv_obj_set_width(lblRecTimer_, kScreenW); + lv_obj_set_style_text_font(lblRecTimer_, &lv_font_montserrat_14, 0); + lv_obj_set_style_text_color(lblRecTimer_, kColorText, 0); + lv_obj_set_style_text_align(lblRecTimer_, LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_text(lblRecTimer_, "00:00"); +} + +// ---------- Rec Paused Page ---------- +void UiRecorder::createPageRecPaused(lv_obj_t* page) +{ + lblPauseFilename_ = lv_label_create(page); + lv_obj_set_pos(lblPauseFilename_, 10, 5); + lv_obj_set_width(lblPauseFilename_, 300); + lv_obj_set_style_text_font(lblPauseFilename_, &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblPauseFilename_, kColorText, 0); + lv_label_set_long_mode(lblPauseFilename_, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_style_text_align(lblPauseFilename_, LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_text(lblPauseFilename_, ""); + + // Horizontal pause line in the center + pauseLine_ = lv_line_create(page); + static lv_point_precise_t line_points[2]; + line_points[0].x = 20; + line_points[0].y = 55; + line_points[1].x = 300; + line_points[1].y = 55; + lv_line_set_points(pauseLine_, line_points, 2); + lv_obj_set_style_line_width(pauseLine_, 2, 0); + lv_obj_set_style_line_color(pauseLine_, kColorText, 0); + + lblPauseTimer_ = lv_label_create(page); + lv_obj_set_pos(lblPauseTimer_, 0, 88); + lv_obj_set_width(lblPauseTimer_, kScreenW); + lv_obj_set_style_text_font(lblPauseTimer_, &lv_font_montserrat_14, 0); + lv_obj_set_style_text_color(lblPauseTimer_, kColorText, 0); + lv_obj_set_style_text_align(lblPauseTimer_, LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_text(lblPauseTimer_, "00:00"); +} + +// ---------- Save Confirm Page ---------- +void UiRecorder::createPageSaveConfirm(lv_obj_t* page) +{ + lblSaveFilename_ = lv_label_create(page); + lv_obj_set_pos(lblSaveFilename_, 10, 40); + lv_obj_set_width(lblSaveFilename_, 300); + lv_obj_set_style_text_font(lblSaveFilename_, &lv_font_montserrat_14, 0); + lv_obj_set_style_text_color(lblSaveFilename_, kColorText, 0); + lv_obj_set_style_text_align(lblSaveFilename_, LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_long_mode(lblSaveFilename_, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_label_set_text(lblSaveFilename_, ""); +} + +// ---------- Playback Page ---------- +void UiRecorder::createPagePlayback(lv_obj_t* page) +{ + lblPlayFilename_ = lv_label_create(page); + lv_obj_set_pos(lblPlayFilename_, 10, 5); + lv_obj_set_width(lblPlayFilename_, 300); + lv_obj_set_style_text_font(lblPlayFilename_, &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblPlayFilename_, kColorText, 0); + lv_label_set_long_mode(lblPlayFilename_, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_style_text_align(lblPlayFilename_, LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_text(lblPlayFilename_, ""); + + playWaveContainer_ = lv_obj_create(page); + lv_obj_set_size(playWaveContainer_, 280, 50); + lv_obj_set_pos(playWaveContainer_, 20, 30); + lv_obj_set_style_bg_color(playWaveContainer_, kColorWaveBg, 0); + lv_obj_set_style_bg_opa(playWaveContainer_, LV_OPA_COVER, 0); + lv_obj_set_style_border_color(playWaveContainer_, lv_color_hex(0xAAAAAA), 0); + lv_obj_set_style_border_width(playWaveContainer_, 1, 0); + lv_obj_clear_flag(playWaveContainer_, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_radius(playWaveContainer_, 4, 0); + + lblPlayTimer_ = lv_label_create(page); + lv_obj_set_pos(lblPlayTimer_, 0, 88); + lv_obj_set_width(lblPlayTimer_, kScreenW); + lv_obj_set_style_text_font(lblPlayTimer_, &lv_font_montserrat_14, 0); + lv_obj_set_style_text_color(lblPlayTimer_, kColorText, 0); + lv_obj_set_style_text_align(lblPlayTimer_, LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_text(lblPlayTimer_, "00:00 / 00:00"); +} + +// ---------- Page switching ---------- +void UiRecorder::switchPage(UiPage page) +{ + for (size_t i = 0; i < pages_.size(); i++) { + if (i == static_cast(page)) { + lv_obj_clear_flag(pages_[i], LV_OBJ_FLAG_HIDDEN); + } else { + lv_obj_add_flag(pages_[i], LV_OBJ_FLAG_HIDDEN); + } + } + currentPage_ = page; + updateBottomLabels(page, isPlaybackPaused_); +} + +void UiRecorder::updateBottomLabels(UiPage page, bool isPaused) +{ + struct LabelSet { + const char* b0; + const char* b1; + const char* b2; + const char* b3; + const char* b4; + }; + + LabelSet labels = {"--", "--", "--", "--", "--"}; + switch (page) { + case UiPage::Home: + labels = {"List", "48k", "Rec", "--", "Exit"}; + break; + case UiPage::FileList: + labels = {"Up", "Down", "Play", "Rename", "Exit"}; + break; + case UiPage::Recording: + labels = {"--", "--", "Pause", "Done", "Exit"}; + break; + case UiPage::RecPaused: + labels = {"--", "--", "Continue", "Done", "Exit"}; + break; + case UiPage::SaveConfirm: + labels = {"--", "--", "--", "Save", "Exit"}; + break; + case UiPage::Playback: + labels = {lastState_.playbackSpeed.c_str(), "-10s", isPaused ? "Play" : "Pause", "+10s", "Exit"}; + break; + } + + lv_label_set_text(lblBottomBtns_[0], labels.b0); + lv_label_set_text(lblBottomBtns_[1], labels.b1); + lv_label_set_text(lblBottomBtns_[2], labels.b2); + lv_label_set_text(lblBottomBtns_[3], labels.b3); + lv_label_set_text(lblBottomBtns_[4], labels.b4); +} + +// ---------- Content update ---------- +void UiRecorder::updatePageContent(const RecorderState& state) +{ + lastState_ = state; + + // Status bar + lv_label_set_text(lblStatusText_, state.statusText.c_str()); + + // FileList + if (currentPage_ == UiPage::FileList) { + bool hasFiles = !state.fileList.empty(); + if (hasFiles) { + lv_obj_add_flag(lblFileListEmpty_, LV_OBJ_FLAG_HIDDEN); + + int sel = state.selectedFileIndex; + if (sel < 0) sel = 0; + if (sel >= static_cast(state.fileList.size())) sel = static_cast(state.fileList.size()) - 1; + + if (sel < fileListOffset_) fileListOffset_ = sel; + if (sel >= fileListOffset_ + 5) fileListOffset_ = sel - 4; + if (fileListOffset_ < 0) fileListOffset_ = 0; + int maxOffset = static_cast(state.fileList.size()) - 1; + if (fileListOffset_ > maxOffset) fileListOffset_ = maxOffset; + + for (int i = 0; i < 5; i++) { + int idx = fileListOffset_ + i; + if (idx < static_cast(state.fileList.size())) { + lv_label_set_text(lblFileListItems_[i], state.fileList[idx].filename.c_str()); + lv_obj_clear_flag(lblFileListItems_[i], LV_OBJ_FLAG_HIDDEN); + if (idx == sel) { + lv_obj_set_style_text_color(lblFileListItems_[i], kColorHighlight, 0); + } else { + lv_obj_set_style_text_color(lblFileListItems_[i], kColorText, 0); + } + } else { + lv_label_set_text(lblFileListItems_[i], ""); + lv_obj_add_flag(lblFileListItems_[i], LV_OBJ_FLAG_HIDDEN); + } + } + } else { + fileListOffset_ = 0; + lv_obj_clear_flag(lblFileListEmpty_, LV_OBJ_FLAG_HIDDEN); + for (int i = 0; i < 5; i++) { + lv_obj_add_flag(lblFileListItems_[i], LV_OBJ_FLAG_HIDDEN); + } + } + } + + // Recording / RecPaused / SaveConfirm filename & timer + const char* fname = state.currentFileName.empty() ? "" : state.currentFileName.c_str(); + lv_label_set_text(lblRecFilename_, fname); + lv_label_set_text(lblPauseFilename_, fname); + lv_label_set_text(lblSaveFilename_, fname); + lv_label_set_text(lblRecTimer_, state.timerText.c_str()); + lv_label_set_text(lblPauseTimer_, state.timerText.c_str()); + + // Playback + lv_label_set_text(lblPlayFilename_, fname); + lv_label_set_text(lblPlayTimer_, state.timerText.c_str()); + + // Update bottom labels in case sample rate or speed changed + updateBottomLabels(currentPage_, isPlaybackPaused_); } void UiRecorder::update(const RecorderState& state) { - if (!lblStatus_) return; - lv_label_set_text(lblStatus_, state.statusText.c_str()); - lv_label_set_text(lblTimer_, state.timerText.c_str()); - lv_label_set_text(lblFile_, state.currentFileName.c_str()); - lv_label_set_text(lblHint_, state.hintText.c_str()); + switch (state.state) { + case AppState::Idle: currentPage_ = UiPage::Home; break; + case AppState::Recording: currentPage_ = UiPage::Recording; break; + case AppState::RecPaused: currentPage_ = UiPage::RecPaused; break; + case AppState::SaveConfirm: currentPage_ = UiPage::SaveConfirm; break; + case AppState::FileList: currentPage_ = UiPage::FileList; break; + case AppState::Playing: currentPage_ = UiPage::Playback; isPlaybackPaused_ = false; break; + case AppState::PlayPaused: currentPage_ = UiPage::Playback; isPlaybackPaused_ = true; break; + } + + updatePageContent(state); + switchPage(currentPage_); } void UiRecorder::setActionHandler(std::function handler) @@ -56,39 +388,107 @@ void UiRecorder::setActionHandler(std::function handle actionHandler_ = handler; } +// ---------- Key handling ---------- void UiRecorder::onKeyPressed(uint32_t key_code) { uint32_t now = lv_tick_get(); - if (key_code == lastKeyCode_ && now - lastKeyTime_ < 300) return; + if (key_code == lastKeyCode_ && now - lastKeyTime_ < 200) return; lastKeyCode_ = key_code; lastKeyTime_ = now; - if (!actionHandler_) return; - + int btnIndex = -1; switch (key_code) { + case KEY_F4: btnIndex = 0; break; + case KEY_F5: btnIndex = 1; break; + case KEY_F6: btnIndex = 2; break; + case KEY_F7: btnIndex = 3; break; + case KEY_F8: btnIndex = 4; break; case KEY_ENTER: case KEY_KPENTER: - actionHandler_("toggle_record"); + if (currentPage_ == UiPage::Home) btnIndex = 2; // Rec + else if (currentPage_ == UiPage::Recording) btnIndex = 3; // Done + else if (currentPage_ == UiPage::RecPaused) btnIndex = 3; // Done + else if (currentPage_ == UiPage::FileList) btnIndex = 2; // Play + else if (currentPage_ == UiPage::Playback) btnIndex = 2; // Pause/Play + else if (currentPage_ == UiPage::SaveConfirm) btnIndex = 3; // Save + break; + case KEY_ESC: + if (currentPage_ == UiPage::Home) { + if (actionHandler_) actionHandler_("quit"); + return; + } + btnIndex = 4; // Exit break; + case KEY_LEFT: + if (currentPage_ == UiPage::FileList) { btnIndex = 0; break; } + return; + case KEY_RIGHT: + if (currentPage_ == UiPage::FileList) { btnIndex = 1; break; } + return; case KEY_P: - actionHandler_("toggle_pause"); + if (currentPage_ == UiPage::Recording || currentPage_ == UiPage::RecPaused) btnIndex = 2; + else if (currentPage_ == UiPage::Playback) btnIndex = 2; break; case KEY_SPACE: - actionHandler_("toggle_play"); + if (currentPage_ == UiPage::Playback) btnIndex = 2; + break; + case KEY_DELETE: + if (currentPage_ == UiPage::FileList && actionHandler_) { + actionHandler_("delete"); + } + return; + default: + return; + } + + if (btnIndex < 0 || btnIndex > 4) return; + handleActionForPage(currentPage_, btnIndex); +} + +void UiRecorder::handleActionForPage(UiPage page, int btn) +{ + if (!actionHandler_) return; + + struct ActionMap { + const char* a0; + const char* a1; + const char* a2; + const char* a3; + const char* a4; + }; + + ActionMap map = {"", "", "", "", ""}; + switch (page) { + case UiPage::Home: + map = {"list", "cycle_rate", "toggle_record", "", "quit"}; break; - case KEY_S: - actionHandler_("stop"); + case UiPage::FileList: + map = {"prev_file", "next_file", "toggle_play", "rename", "exit"}; break; - case KEY_ESC: - actionHandler_("stop"); + case UiPage::Recording: + map = {"", "", "toggle_pause", "done", "exit"}; break; - case KEY_LEFT: - actionHandler_("prev_file"); + case UiPage::RecPaused: + map = {"", "", "toggle_pause", "done", "exit"}; break; - case KEY_RIGHT: - actionHandler_("next_file"); + case UiPage::SaveConfirm: + map = {"", "", "", "save", "exit"}; break; - default: + case UiPage::Playback: + map = {"speed", "seek_back", "toggle_pause", "seek_fwd", "exit"}; break; } + + const char* action = nullptr; + switch (btn) { + case 0: action = map.a0; break; + case 1: action = map.a1; break; + case 2: action = map.a2; break; + case 3: action = map.a3; break; + case 4: action = map.a4; break; + } + + if (action && action[0] != '\0') { + actionHandler_(action); + } } From e953c9082257d4d4715a9a686820394549132439 Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Thu, 28 May 2026 18:21:08 +0800 Subject: [PATCH 05/11] Enhance Recorder with playback controls and waveform support --- projects/Recorder/config_defaults.mk | 2 + projects/Recorder/main/include/audio_engine.h | 7 + projects/Recorder/main/include/recorder_app.h | 6 + projects/Recorder/main/include/ui_recorder.h | 6 + projects/Recorder/main/src/audio_engine.cpp | 192 +++++++++++++++++- projects/Recorder/main/src/recorder_app.cpp | 24 ++- projects/Recorder/main/src/ui_recorder.cpp | 75 ++++++- projects/Recorder/main/src/wav_file.cpp | 56 ++++- 8 files changed, 354 insertions(+), 14 deletions(-) diff --git a/projects/Recorder/config_defaults.mk b/projects/Recorder/config_defaults.mk index 743de6c3..e6143732 100644 --- a/projects/Recorder/config_defaults.mk +++ b/projects/Recorder/config_defaults.mk @@ -7,6 +7,8 @@ CONFIG_V9_5_LV_USE_CLIB_SPRINTF=y CONFIG_V9_5_LV_FONT_MONTSERRAT_10=y CONFIG_V9_5_LV_FONT_MONTSERRAT_12=y CONFIG_V9_5_LV_FONT_MONTSERRAT_14=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_16=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_26=y CONFIG_V9_5_LV_FONT_DEFAULT_MONTSERRAT_14=y CONFIG_MINIAUDIO_COMPONENT_ENABLED=y diff --git a/projects/Recorder/main/include/audio_engine.h b/projects/Recorder/main/include/audio_engine.h index 8c21ad67..5345b2aa 100644 --- a/projects/Recorder/main/include/audio_engine.h +++ b/projects/Recorder/main/include/audio_engine.h @@ -43,6 +43,13 @@ class AudioEngine { float playbackPosition() const; float playbackDuration() const; + // Extended APIs + void seekPlayback(float seconds); + void setPlaybackSpeed(float speed); + void getRecordingWaveform(float* out, int count) const; + void getPlaybackWaveform(float* out, int count) const; + bool hasPlaybackWaveform() const; + using UpdateCallback = std::function; void setUpdateCallback(UpdateCallback cb); diff --git a/projects/Recorder/main/include/recorder_app.h b/projects/Recorder/main/include/recorder_app.h index 2c6b0cf2..9fff1f95 100644 --- a/projects/Recorder/main/include/recorder_app.h +++ b/projects/Recorder/main/include/recorder_app.h @@ -1,6 +1,7 @@ #pragma once #include "audio_engine.h" +#include #include #include #include @@ -34,6 +35,11 @@ struct RecorderState { int selectedFileIndex; std::string sampleRate; // e.g. "48k" std::string playbackSpeed; // e.g. "1.0X" + float playbackPosition = 0; // seconds + float playbackDuration = 0; // seconds + std::array recWaveform{}; + std::array playWaveform{}; + bool hasPlayWaveform = false; }; class IRecorderView { diff --git a/projects/Recorder/main/include/ui_recorder.h b/projects/Recorder/main/include/ui_recorder.h index ef3ef582..9345d09e 100644 --- a/projects/Recorder/main/include/ui_recorder.h +++ b/projects/Recorder/main/include/ui_recorder.h @@ -68,6 +68,8 @@ class UiRecorder : public IRecorderView { // Recording lv_obj_t* lblRecFilename_ = nullptr; lv_obj_t* recWaveContainer_ = nullptr; + lv_obj_t* recWaveLine_ = nullptr; + std::array recWavePoints_{}; lv_obj_t* lblRecTimer_ = nullptr; // RecPaused @@ -81,6 +83,10 @@ class UiRecorder : public IRecorderView { // Playback lv_obj_t* lblPlayFilename_ = nullptr; lv_obj_t* playWaveContainer_ = nullptr; + lv_obj_t* playWaveLine_ = nullptr; + lv_obj_t* playProgressLine_ = nullptr; + std::array playWavePoints_{}; + lv_point_precise_t playProgressPoints_[2]{}; lv_obj_t* lblPlayTimer_ = nullptr; // State cache diff --git a/projects/Recorder/main/src/audio_engine.cpp b/projects/Recorder/main/src/audio_engine.cpp index 27492ed0..efa3f38b 100644 --- a/projects/Recorder/main/src/audio_engine.cpp +++ b/projects/Recorder/main/src/audio_engine.cpp @@ -4,7 +4,12 @@ #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" +#include +#include +#include #include +#include +#include struct AudioEngineImpl { ma_context context{}; @@ -20,15 +25,28 @@ struct AudioEngineImpl { FILE* playFile = nullptr; uint32_t playSampleRate = 48000; + uint32_t deviceSampleRate = 48000; uint8_t playChannels = 1; uint32_t playTotalFrames = 0; uint32_t playDataOffset = 0; uint32_t playCurrentFrame = 0; + float playbackSpeed = 1.0f; std::atomic state{AudioState::Idle}; std::atomic playbackFinished{false}; AudioEngine::UpdateCallback updateCb; + // Recording waveform ring buffer + static constexpr int kRecWaveformSize = 128; + std::array recWaveform{}; + mutable std::mutex recWaveformMutex; + size_t recWaveformIndex = 0; + + // Playback waveform + static constexpr int kPlayWaveformSize = 256; + std::array playWaveform{}; + bool hasPlayWaveform = false; + ~AudioEngineImpl() { if (recDevice) { ma_device_stop(recDevice); @@ -185,14 +203,57 @@ bool AudioEngine::startPlayback(const std::string& filepath) impl_->playChannels = static_cast(channels); impl_->playSampleRate = sampleRate; + impl_->deviceSampleRate = sampleRate; + impl_->playbackSpeed = 1.0f; impl_->playTotalFrames = dataSize / (channels * sizeof(int16_t)); impl_->playCurrentFrame = 0; impl_->playDataOffset = ftell(impl_->playFile); + // Generate playback waveform + { + long savedPos = ftell(impl_->playFile); + size_t bytesPerFrame = impl_->playChannels * sizeof(int16_t); + uint32_t totalFrames = impl_->playTotalFrames; + int outCount = AudioEngineImpl::kPlayWaveformSize; + uint32_t framesPerBin = totalFrames / outCount; + if (framesPerBin < 1) framesPerBin = 1; + + fseek(impl_->playFile, impl_->playDataOffset, SEEK_SET); + std::vector buffer(framesPerBin * impl_->playChannels); + + for (int i = 0; i < outCount; i++) { + uint32_t toRead = framesPerBin; + long curPos = ftell(impl_->playFile); + fseek(impl_->playFile, 0, SEEK_END); + long endPos = ftell(impl_->playFile); + fseek(impl_->playFile, curPos, SEEK_SET); + uint32_t remaining = static_cast((endPos - curPos) / static_cast(bytesPerFrame)); + if (toRead > remaining) toRead = remaining; + + if (toRead > 0) { + if (toRead > buffer.size() / impl_->playChannels) { + buffer.resize(toRead * impl_->playChannels); + } + size_t read = fread(buffer.data(), bytesPerFrame, toRead, impl_->playFile); + int16_t peak = 0; + for (size_t j = 0; j < read * impl_->playChannels; j++) { + if (std::abs(buffer[j]) > std::abs(peak)) { + peak = buffer[j]; + } + } + impl_->playWaveform[i] = std::abs(peak) / 32768.0f; + } else { + impl_->playWaveform[i] = 0.0f; + } + } + impl_->hasPlayWaveform = true; + fseek(impl_->playFile, savedPos, SEEK_SET); + } + ma_device_config config = ma_device_config_init(ma_device_type_playback); config.playback.format = ma_format_s16; config.playback.channels = impl_->playChannels; - config.sampleRate = impl_->playSampleRate; + config.sampleRate = impl_->deviceSampleRate; config.dataCallback = playbackCallback; config.pUserData = this; @@ -255,7 +316,10 @@ void AudioEngine::stopPlayback() } impl_->playTotalFrames = 0; impl_->playCurrentFrame = 0; + impl_->playbackSpeed = 1.0f; + impl_->deviceSampleRate = 48000; impl_->playbackFinished.store(false); + impl_->hasPlayWaveform = false; impl_->state.store(AudioState::Idle); } @@ -278,14 +342,121 @@ float AudioEngine::recordingDuration() const float AudioEngine::playbackPosition() const { - return static_cast(impl_->playCurrentFrame) / static_cast(impl_->playSampleRate); + if (impl_->deviceSampleRate == 0) return 0; + return static_cast(impl_->playCurrentFrame) / static_cast(impl_->deviceSampleRate); } float AudioEngine::playbackDuration() const { + if (impl_->playSampleRate == 0) return 0; return static_cast(impl_->playTotalFrames) / static_cast(impl_->playSampleRate); } +void AudioEngine::seekPlayback(float seconds) +{ + if (!impl_->playFile) return; + float dur = playbackDuration(); + if (seconds < 0) seconds = 0; + if (seconds > dur) seconds = dur; + + uint32_t fileFrame = static_cast(seconds * impl_->playSampleRate); + if (fileFrame > impl_->playTotalFrames) fileFrame = impl_->playTotalFrames; + + size_t bytesPerFrame = impl_->playChannels * sizeof(int16_t); + fseek(impl_->playFile, static_cast(impl_->playDataOffset + fileFrame * bytesPerFrame), SEEK_SET); + impl_->playCurrentFrame = static_cast(seconds * impl_->deviceSampleRate); +} + +void AudioEngine::setPlaybackSpeed(float speed) +{ + if (!impl_->playFile || speed <= 0.0f) return; + if (speed == impl_->playbackSpeed) return; + + AudioState currentState = impl_->state.load(); + if (currentState != AudioState::Playing && currentState != AudioState::PlayPaused) return; + + uint32_t oldDeviceSampleRate = impl_->deviceSampleRate; + uint32_t currentFrame = impl_->playCurrentFrame; + + if (impl_->playDevice) { + ma_device_stop(impl_->playDevice); + ma_device_uninit(impl_->playDevice); + delete impl_->playDevice; + impl_->playDevice = nullptr; + } + + impl_->playbackSpeed = speed; + impl_->deviceSampleRate = static_cast(impl_->playSampleRate * speed); + + ma_device_config config = ma_device_config_init(ma_device_type_playback); + config.playback.format = ma_format_s16; + config.playback.channels = impl_->playChannels; + config.sampleRate = impl_->deviceSampleRate; + config.dataCallback = playbackCallback; + config.pUserData = this; + + impl_->playDevice = new ma_device(); + memset(impl_->playDevice, 0, sizeof(ma_device)); + ma_result result = ma_device_init(&impl_->context, &config, impl_->playDevice); + if (result != MA_SUCCESS) { + impl_->deviceSampleRate = impl_->playSampleRate; + impl_->playbackSpeed = 1.0f; + config.sampleRate = impl_->deviceSampleRate; + result = ma_device_init(&impl_->context, &config, impl_->playDevice); + if (result != MA_SUCCESS) { + delete impl_->playDevice; + impl_->playDevice = nullptr; + fclose(impl_->playFile); + impl_->playFile = nullptr; + impl_->state.store(AudioState::Idle); + return; + } + } + + float posSec = static_cast(currentFrame) / static_cast(oldDeviceSampleRate); + uint32_t fileFrame = static_cast(posSec * impl_->playSampleRate); + if (fileFrame > impl_->playTotalFrames) fileFrame = impl_->playTotalFrames; + + size_t bytesPerFrame = impl_->playChannels * sizeof(int16_t); + fseek(impl_->playFile, static_cast(impl_->playDataOffset + fileFrame * bytesPerFrame), SEEK_SET); + impl_->playCurrentFrame = static_cast(posSec * impl_->deviceSampleRate); + + if (currentState == AudioState::Playing) { + ma_device_start(impl_->playDevice); + } else { + impl_->state.store(AudioState::PlayPaused); + } +} + +void AudioEngine::getRecordingWaveform(float* out, int count) const +{ + if (count > AudioEngineImpl::kRecWaveformSize) count = AudioEngineImpl::kRecWaveformSize; + if (count <= 0) return; + std::lock_guard lock(impl_->recWaveformMutex); + for (int i = 0; i < count; i++) { + size_t idx = (impl_->recWaveformIndex + AudioEngineImpl::kRecWaveformSize - count + i) % AudioEngineImpl::kRecWaveformSize; + out[i] = impl_->recWaveform[idx]; + } +} + +void AudioEngine::getPlaybackWaveform(float* out, int count) const +{ + if (count > AudioEngineImpl::kPlayWaveformSize) count = AudioEngineImpl::kPlayWaveformSize; + if (count <= 0) return; + if (!impl_->hasPlayWaveform) { + for (int i = 0; i < count; i++) out[i] = 0; + return; + } + for (int i = 0; i < count; i++) { + out[i] = impl_->playWaveform[i]; + } +} + +bool AudioEngine::hasPlaybackWaveform() const +{ + return impl_->hasPlayWaveform; +} + void AudioEngine::setUpdateCallback(UpdateCallback cb) { impl_->updateCb = cb; @@ -298,6 +469,21 @@ void AudioEngine::onRecordingData(const void* input, ma_uint32 frameCount) size_t written = fwrite(input, 1, bytesToWrite, impl_->recFile); if (written == bytesToWrite) { impl_->recTotalFrames += frameCount; + + const int16_t* samples = static_cast(input); + int16_t peak = 0; + for (ma_uint32 i = 0; i < frameCount * impl_->recChannels; i++) { + if (std::abs(samples[i]) > std::abs(peak)) { + peak = samples[i]; + } + } + float normalized = peak / 32768.0f; + { + std::lock_guard lock(impl_->recWaveformMutex); + impl_->recWaveform[impl_->recWaveformIndex] = normalized; + impl_->recWaveformIndex = (impl_->recWaveformIndex + 1) % AudioEngineImpl::kRecWaveformSize; + } + if (impl_->updateCb) impl_->updateCb(); } } @@ -310,7 +496,9 @@ void AudioEngine::onPlaybackData(void* output, ma_uint32 frameCount) } size_t bytesPerFrame = impl_->playChannels * sizeof(int16_t); size_t bytesToRead = frameCount * bytesPerFrame; + long beforePos = ftell(impl_->playFile); size_t read = fread(output, 1, bytesToRead, impl_->playFile); + long afterPos = ftell(impl_->playFile); if (read < bytesToRead) { memset(static_cast(output) + read, 0, bytesToRead - read); diff --git a/projects/Recorder/main/src/recorder_app.cpp b/projects/Recorder/main/src/recorder_app.cpp index d55e0fc2..85c19722 100644 --- a/projects/Recorder/main/src/recorder_app.cpp +++ b/projects/Recorder/main/src/recorder_app.cpp @@ -102,10 +102,20 @@ void RecorderApp::onAction(const std::string& action) else if (playbackSpeed_ == "1.5X") playbackSpeed_ = "1.75X"; else if (playbackSpeed_ == "1.75X") playbackSpeed_ = "2.0X"; else playbackSpeed_ = "0.5X"; + + float speed = 1.0f; + if (playbackSpeed_ == "0.5X") speed = 0.5f; + else if (playbackSpeed_ == "0.75X") speed = 0.75f; + else if (playbackSpeed_ == "1.0X") speed = 1.0f; + else if (playbackSpeed_ == "1.25X") speed = 1.25f; + else if (playbackSpeed_ == "1.5X") speed = 1.5f; + else if (playbackSpeed_ == "1.75X") speed = 1.75f; + else if (playbackSpeed_ == "2.0X") speed = 2.0f; + engine_.setPlaybackSpeed(speed); } else if (action == "seek_back") { - // TODO: implement seek + engine_.seekPlayback(engine_.playbackPosition() - 10.0f); } else if (action == "seek_fwd") { - // TODO: implement seek + engine_.seekPlayback(engine_.playbackPosition() + 10.0f); } else if (action == "rename") { // TODO: implement rename } else if (action == "delete") { @@ -201,6 +211,8 @@ RecorderState RecorderApp::getState() const case AppState::PlayPaused: { float pos = engine_.playbackPosition(); float dur = engine_.playbackDuration(); + rs.playbackPosition = pos; + rs.playbackDuration = dur; int pmin = static_cast(pos) / 60; int psec = static_cast(pos) % 60; int dmin = static_cast(dur) / 60; @@ -235,6 +247,14 @@ RecorderState RecorderApp::getState() const rs.playbackSpeed = playbackSpeed_; rs.hintText = ""; + engine_.getRecordingWaveform(rs.recWaveform.data(), static_cast(rs.recWaveform.size())); + if (engine_.hasPlaybackWaveform()) { + engine_.getPlaybackWaveform(rs.playWaveform.data(), static_cast(rs.playWaveform.size())); + rs.hasPlayWaveform = true; + } else { + rs.hasPlayWaveform = false; + } + return rs; } diff --git a/projects/Recorder/main/src/ui_recorder.cpp b/projects/Recorder/main/src/ui_recorder.cpp index 7b9213a2..c1a7e627 100644 --- a/projects/Recorder/main/src/ui_recorder.cpp +++ b/projects/Recorder/main/src/ui_recorder.cpp @@ -165,6 +165,14 @@ void UiRecorder::createPageRecording(lv_obj_t* page) lv_obj_clear_flag(recWaveContainer_, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_style_radius(recWaveContainer_, 4, 0); + recWaveLine_ = lv_line_create(recWaveContainer_); + lv_obj_set_size(recWaveLine_, 280, 50); + lv_obj_set_pos(recWaveLine_, 0, 0); + lv_obj_set_style_line_color(recWaveLine_, kColorHighlight, 0); + lv_obj_set_style_line_width(recWaveLine_, 2, 0); + lv_line_set_y_invert(recWaveLine_, true); + lv_line_set_points_mutable(recWaveLine_, recWavePoints_.data(), recWavePoints_.size()); + lblRecTimer_ = lv_label_create(page); lv_obj_set_pos(lblRecTimer_, 0, 88); lv_obj_set_width(lblRecTimer_, kScreenW); @@ -241,6 +249,22 @@ void UiRecorder::createPagePlayback(lv_obj_t* page) lv_obj_clear_flag(playWaveContainer_, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_style_radius(playWaveContainer_, 4, 0); + playWaveLine_ = lv_line_create(playWaveContainer_); + lv_obj_set_size(playWaveLine_, 280, 50); + lv_obj_set_pos(playWaveLine_, 0, 0); + lv_obj_set_style_line_color(playWaveLine_, kColorHighlight, 0); + lv_obj_set_style_line_width(playWaveLine_, 2, 0); + lv_line_set_y_invert(playWaveLine_, true); + lv_line_set_points_mutable(playWaveLine_, playWavePoints_.data(), playWavePoints_.size()); + + playProgressLine_ = lv_line_create(playWaveContainer_); + lv_obj_set_size(playProgressLine_, 280, 50); + lv_obj_set_pos(playProgressLine_, 0, 0); + lv_obj_set_style_line_color(playProgressLine_, lv_color_hex(0xFF0000), 0); + lv_obj_set_style_line_width(playProgressLine_, 2, 0); + lv_line_set_y_invert(playProgressLine_, true); + lv_line_set_points_mutable(playProgressLine_, playProgressPoints_, 2); + lblPlayTimer_ = lv_label_create(page); lv_obj_set_pos(lblPlayTimer_, 0, 88); lv_obj_set_width(lblPlayTimer_, kScreenW); @@ -277,7 +301,7 @@ void UiRecorder::updateBottomLabels(UiPage page, bool isPaused) LabelSet labels = {"--", "--", "--", "--", "--"}; switch (page) { case UiPage::Home: - labels = {"List", "48k", "Rec", "--", "Exit"}; + labels = {"List", lastState_.sampleRate.c_str(), "Rec", "--", "Exit"}; break; case UiPage::FileList: labels = {"Up", "Down", "Play", "Rename", "Exit"}; @@ -363,6 +387,55 @@ void UiRecorder::updatePageContent(const RecorderState& state) lv_label_set_text(lblPlayFilename_, fname); lv_label_set_text(lblPlayTimer_, state.timerText.c_str()); + // Update recording waveform + if (currentPage_ == UiPage::Recording) { + constexpr int count = 128; + float stepX = 280.0f / (count - 1); + for (int i = 0; i < count; i++) { + recWavePoints_[i].x = i * stepX; + float v = state.recWaveform[i]; + // Clamp to [-1, 1] + if (v < -1.0f) v = -1.0f; + if (v > 1.0f) v = 1.0f; + // y_invert enabled: y=0 at bottom, y=50 at top. + // Center line is y=25. Positive values go up, negative go down. + recWavePoints_[i].y = 25.0f + v * 25.0f; + } + lv_obj_invalidate(recWaveLine_); + } + + // Update playback waveform and progress + if (currentPage_ == UiPage::Playback) { + if (state.hasPlayWaveform) { + constexpr int count = 256; + float stepX = 280.0f / (count - 1); + for (int i = 0; i < count; i++) { + playWavePoints_[i].x = i * stepX; + float v = state.playWaveform[i]; + if (v < 0.0f) v = 0.0f; + if (v > 1.0f) v = 1.0f; + // y_invert enabled: draw envelope from bottom to top + playWavePoints_[i].y = v * 50.0f; + } + lv_obj_invalidate(playWaveLine_); + } + + // Progress line + float dur = state.playbackDuration; + float pos = state.playbackPosition; + if (dur > 0.0f) { + float ratio = pos / dur; + if (ratio < 0.0f) ratio = 0.0f; + if (ratio > 1.0f) ratio = 1.0f; + int px = static_cast(ratio * 280.0f); + playProgressPoints_[0].x = px; + playProgressPoints_[0].y = 0; + playProgressPoints_[1].x = px; + playProgressPoints_[1].y = 50; + lv_obj_invalidate(playProgressLine_); + } + } + // Update bottom labels in case sample rate or speed changed updateBottomLabels(currentPage_, isPlaybackPaused_); } diff --git a/projects/Recorder/main/src/wav_file.cpp b/projects/Recorder/main/src/wav_file.cpp index 138bd760..f313ff7c 100644 --- a/projects/Recorder/main/src/wav_file.cpp +++ b/projects/Recorder/main/src/wav_file.cpp @@ -59,14 +59,52 @@ bool wav_file_finalize(FILE* file) bool wav_file_read_header(FILE* file, uint16_t& channels, uint32_t& sampleRate, uint32_t& dataSize) { - WavHeader h; - if (fread(&h, sizeof(h), 1, file) != 1) return false; - if (std::memcmp(h.riff, "RIFF", 4) != 0 || std::memcmp(h.wave, "WAVE", 4) != 0) return false; - if (h.audioFormat != 1) return false; // PCM only - if (h.bitsPerSample != 16) return false; // s16 only + // RIFF header + char riff[4]; + uint32_t fileSize; + char wave[4]; + if (fread(riff, 1, 4, file) != 4) return false; + if (fread(&fileSize, 4, 1, file) != 1) return false; + if (fread(wave, 1, 4, file) != 4) return false; + if (std::memcmp(riff, "RIFF", 4) != 0 || std::memcmp(wave, "WAVE", 4) != 0) return false; - channels = h.channels; - sampleRate = h.sampleRate; - dataSize = h.dataSize; - return true; + channels = 0; + sampleRate = 0; + dataSize = 0; + uint16_t bitsPerSample = 0; + + while (!feof(file)) { + char chunkId[4]; + uint32_t chunkSize; + if (fread(chunkId, 1, 4, file) != 4) break; + if (fread(&chunkSize, 4, 1, file) != 1) break; + + if (std::memcmp(chunkId, "fmt ", 4) == 0) { + uint16_t audioFormat; + if (fread(&audioFormat, 2, 1, file) != 1) return false; + if (audioFormat != 1) return false; // PCM only + if (fread(&channels, 2, 1, file) != 1) return false; + if (fread(&sampleRate, 4, 1, file) != 1) return false; + // Skip byteRate (4) + blockAlign (2) + if (fseek(file, 6, SEEK_CUR) != 0) return false; + if (fread(&bitsPerSample, 2, 1, file) != 1) return false; + // Skip any extra fmt bytes (pad to even boundary) + uint32_t skip = chunkSize > 16 ? chunkSize - 16 : 0; + if (skip > 0) { + if (fseek(file, skip, SEEK_CUR) != 0) return false; + } + } + else if (std::memcmp(chunkId, "data", 4) == 0) { + dataSize = chunkSize; + // File pointer is now at start of PCM data + return (channels != 0 && sampleRate != 0 && dataSize > 0 && bitsPerSample == 16); + } + else { + // Skip unknown chunk (pad to even size as per RIFF spec) + uint32_t skip = (chunkSize + 1) & ~1U; + if (fseek(file, skip, SEEK_CUR) != 0) return false; + } + } + + return false; } From c99464de42f6116f0d00ccba22fb2d4534303247 Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Fri, 29 May 2026 16:04:19 +0800 Subject: [PATCH 06/11] feat(Recorder): add Linux build configs, debian packager and improve audio waveform - Add linux_x86_sdl2 and cross_cp0 default build configs - Add llm_pack.py tool for creating .deb packages - Switch audio waveform from peak to logarithmic RMS/dB scale (-36dB~0dB) - Fix Recorder.desktop exec path and remove tinyalsa dependency - Increase bottom bar font from 10 to 12 --- .../Recorder/applications/Recorder.desktop | 2 +- .../linux_x86_cross_cp0_config_defaults.mk | 26 +++ .../linux_x86_sdl2_config_defaults.mk | 23 +++ projects/Recorder/main/SConstruct | 2 +- projects/Recorder/main/src/audio_engine.cpp | 31 +++- projects/Recorder/main/src/ui_recorder.cpp | 2 +- projects/Recorder/tools/llm_pack.py | 174 ++++++++++++++++++ 7 files changed, 248 insertions(+), 12 deletions(-) create mode 100644 projects/Recorder/linux_x86_cross_cp0_config_defaults.mk create mode 100644 projects/Recorder/linux_x86_sdl2_config_defaults.mk create mode 100755 projects/Recorder/tools/llm_pack.py diff --git a/projects/Recorder/applications/Recorder.desktop b/projects/Recorder/applications/Recorder.desktop index 4244ceea..5df7a6b0 100644 --- a/projects/Recorder/applications/Recorder.desktop +++ b/projects/Recorder/applications/Recorder.desktop @@ -1,6 +1,6 @@ [Desktop Entry] Name=Recorder -Exec=/usr/share/APPLaunch/bin/M5CardputerZero-Recorder +Exec=/usr/share/Recorder/bin/M5CardputerZero-Recorder Terminal=false Icon=share/images/Recorder.png Type=Application diff --git a/projects/Recorder/linux_x86_cross_cp0_config_defaults.mk b/projects/Recorder/linux_x86_cross_cp0_config_defaults.mk new file mode 100644 index 00000000..3b9a4a2e --- /dev/null +++ b/projects/Recorder/linux_x86_cross_cp0_config_defaults.mk @@ -0,0 +1,26 @@ + +CONFIG_TOOLCHAIN_PREFIX="aarch64-linux-gnu-" + +CONFIG_LVGL_COMPONENT_ENABLED=y +CONFIG_LVGL_9_5_SRC=y + +CONFIG_V9_5_LV_USE_CLIB_MALLOC=y +CONFIG_V9_5_LV_USE_CLIB_STRING=y +CONFIG_V9_5_LV_USE_CLIB_SPRINTF=y + +CONFIG_V9_5_LV_FONT_MONTSERRAT_10=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_12=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_14=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_16=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_26=y +CONFIG_V9_5_LV_FONT_DEFAULT_MONTSERRAT_14=y + +CONFIG_MINIAUDIO_COMPONENT_ENABLED=y + +CONFIG_V9_5_LV_USE_LINUX_FBDEV=y +CONFIG_V9_5_LV_DRAW_SW_ASM_NEON=y +CONFIG_V9_5_LV_USE_DRAW_SW_ASM=1 +CONFIG_V9_5_LV_USE_EVDEV=y +CONFIG_V9_5_LV_OS_PTHREAD=y +CONFIG_V9_5_LV_DRAW_THREAD_STACK_SIZE=65536 +CONFIG_V9_5_LV_DRAW_THREAD_PRIO=3 diff --git a/projects/Recorder/linux_x86_sdl2_config_defaults.mk b/projects/Recorder/linux_x86_sdl2_config_defaults.mk new file mode 100644 index 00000000..726b3c5b --- /dev/null +++ b/projects/Recorder/linux_x86_sdl2_config_defaults.mk @@ -0,0 +1,23 @@ + +# CONFIG_TOOLCHAIN_PREFIX="aarch64-linux-gnu-" + +CONFIG_LVGL_COMPONENT_ENABLED=y +CONFIG_LVGL_9_5_SRC=y + +CONFIG_V9_5_LV_USE_CLIB_MALLOC=y +CONFIG_V9_5_LV_USE_CLIB_STRING=y +CONFIG_V9_5_LV_USE_CLIB_SPRINTF=y + +CONFIG_V9_5_LV_FONT_MONTSERRAT_10=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_12=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_14=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_16=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_26=y +CONFIG_V9_5_LV_FONT_DEFAULT_MONTSERRAT_14=y + +CONFIG_MINIAUDIO_COMPONENT_ENABLED=y + +CONFIG_V9_5_LV_USE_SDL=y +CONFIG_V9_5_LV_OS_PTHREAD=y +CONFIG_V9_5_LV_DRAW_THREAD_STACK_SIZE=65536 +CONFIG_V9_5_LV_DRAW_THREAD_PRIO=3 diff --git a/projects/Recorder/main/SConstruct b/projects/Recorder/main/SConstruct index bd4c4dda..9e135fbb 100644 --- a/projects/Recorder/main/SConstruct +++ b/projects/Recorder/main/SConstruct @@ -14,7 +14,7 @@ INCLUDE = [ADir("."), ADir("include")] PRIVATE_INCLUDE = [] REQUIREMENTS = ["lvgl_component", "pthread", "Miniaudio"] if "CONFIG_V9_5_LV_USE_SDL" not in os.environ: - REQUIREMENTS += ["input", "xkbcommon", "udev", "tinyalsa_component"] + REQUIREMENTS += ["input", "xkbcommon", "udev"] STATIC_LIB = [] DYNAMIC_LIB = [] DEFINITIONS = [] diff --git a/projects/Recorder/main/src/audio_engine.cpp b/projects/Recorder/main/src/audio_engine.cpp index efa3f38b..949f6055 100644 --- a/projects/Recorder/main/src/audio_engine.cpp +++ b/projects/Recorder/main/src/audio_engine.cpp @@ -235,13 +235,17 @@ bool AudioEngine::startPlayback(const std::string& filepath) buffer.resize(toRead * impl_->playChannels); } size_t read = fread(buffer.data(), bytesPerFrame, toRead, impl_->playFile); - int16_t peak = 0; - for (size_t j = 0; j < read * impl_->playChannels; j++) { - if (std::abs(buffer[j]) > std::abs(peak)) { - peak = buffer[j]; - } + double sumSq = 0.0; + size_t sampleCount = read * impl_->playChannels; + for (size_t j = 0; j < sampleCount; j++) { + double s = static_cast(buffer[j]) / 32768.0; + sumSq += s * s; } - impl_->playWaveform[i] = std::abs(peak) / 32768.0f; + float rms = (sampleCount > 0) ? static_cast(std::sqrt(sumSq / sampleCount)) : 0.0f; + // Map RMS to logarithmic/dB scale: -36dB ~ 0dB mapped to 0~1 + float db = 20.0f * std::log10(rms + 1e-6f); + if (db < -36.0f) db = -36.0f; + impl_->playWaveform[i] = (db + 36.0f) / 36.0f; } else { impl_->playWaveform[i] = 0.0f; } @@ -472,15 +476,24 @@ void AudioEngine::onRecordingData(const void* input, ma_uint32 frameCount) const int16_t* samples = static_cast(input); int16_t peak = 0; - for (ma_uint32 i = 0; i < frameCount * impl_->recChannels; i++) { + double sumSq = 0.0; + uint32_t sampleCount = frameCount * impl_->recChannels; + for (ma_uint32 i = 0; i < sampleCount; i++) { if (std::abs(samples[i]) > std::abs(peak)) { peak = samples[i]; } + double s = static_cast(samples[i]) / 32768.0; + sumSq += s * s; } - float normalized = peak / 32768.0f; + float rms = (sampleCount > 0) ? static_cast(std::sqrt(sumSq / sampleCount)) : 0.0f; + // Map RMS to logarithmic/dB scale: -36dB ~ 0dB mapped to 0~1, keep sign from peak + float db = 20.0f * std::log10(rms + 1e-6f); + if (db < -36.0f) db = -36.0f; + float dbNorm = (db + 36.0f) / 36.0f; + if (peak < 0) dbNorm = -dbNorm; { std::lock_guard lock(impl_->recWaveformMutex); - impl_->recWaveform[impl_->recWaveformIndex] = normalized; + impl_->recWaveform[impl_->recWaveformIndex] = dbNorm; impl_->recWaveformIndex = (impl_->recWaveformIndex + 1) % AudioEngineImpl::kRecWaveformSize; } diff --git a/projects/Recorder/main/src/ui_recorder.cpp b/projects/Recorder/main/src/ui_recorder.cpp index c1a7e627..db561f10 100644 --- a/projects/Recorder/main/src/ui_recorder.cpp +++ b/projects/Recorder/main/src/ui_recorder.cpp @@ -90,7 +90,7 @@ void UiRecorder::createBottomBar(lv_obj_t* parent) lblBottomBtns_[i] = lv_label_create(bottomBar_); lv_obj_set_pos(lblBottomBtns_[i], i * kBtnW, 0); lv_obj_set_size(lblBottomBtns_[i], kBtnW, kBottomH); - lv_obj_set_style_text_font(lblBottomBtns_[i], &lv_font_montserrat_10, 0); + lv_obj_set_style_text_font(lblBottomBtns_[i], &lv_font_montserrat_12, 0); lv_obj_set_style_text_color(lblBottomBtns_[i], kColorText, 0); lv_obj_set_style_text_align(lblBottomBtns_[i], LV_TEXT_ALIGN_CENTER, 0); lv_label_set_text(lblBottomBtns_[i], "--"); diff --git a/projects/Recorder/tools/llm_pack.py b/projects/Recorder/tools/llm_pack.py new file mode 100755 index 00000000..8d645ae0 --- /dev/null +++ b/projects/Recorder/tools/llm_pack.py @@ -0,0 +1,174 @@ +#!/bin/env python3 +import os +import sys +import shutil +import subprocess +import glob +from datetime import datetime + +''' +{package_name}_{version}-{revision}_{architecture}.deb +recorder_0.1.0-m5stack1_arm64.deb + +debian-Recorder directory structure: + DEBIAN/ + control + postinst + prerm + usr/ + share/ + applications/ + Recorder.desktop + Recorder/ + bin/ + M5CardputerZero-Recorder + lib/ + lvgl.so + share/ + images/ + *.png +''' + +PACKAGE_NAME = 'recorder' +APP_NAME = 'Recorder' +BIN_NAME = 'M5CardputerZero-Recorder' +INSTALL_PPREFIX = 'usr/share' +INSTALL_PREFIX = f'{INSTALL_PPREFIX}/Recorder' +BIN_PATH = f'{INSTALL_PREFIX}/bin' +LIB_PATH = f'{INSTALL_PREFIX}/lib' +SHARE_PATH = f'{INSTALL_PREFIX}/share' +APP_PATH = f'{INSTALL_PREFIX}/applications' + + +def create_recorder_deb(version='0.1.0', src_folder='../dist', revision='m5stack1'): + """ + Build the Recorder Debian package from src_folder. + + Expected files inside src_folder: + bin/M5CardputerZero-Recorder (or directly M5CardputerZero-Recorder) + lib/lvgl.so (optional) + share/images/*.png (optional, also searched in project dir) + applications/*.desktop (optional, also searched in project dir) + """ + tools_dir = os.path.dirname(os.path.abspath(__file__)) + deb_folder = os.path.join(tools_dir, f'debian-{APP_NAME}') + deb_file = os.path.join(tools_dir, f'{PACKAGE_NAME}_{version}-{revision}_arm64.deb') + + # ------------------------------------------------------------------ cleanup + if os.path.exists(deb_folder): + shutil.rmtree(deb_folder) + + print(f'Creating Debian package {PACKAGE_NAME} {version} ...') + + # --------------------------------------------------------- create directories + for d in [ + os.path.join(deb_folder, 'DEBIAN'), + os.path.join(deb_folder, BIN_PATH), + os.path.join(deb_folder, LIB_PATH), + os.path.join(deb_folder, SHARE_PATH, 'images'), + os.path.join(deb_folder, APP_PATH), + ]: + os.makedirs(d, exist_ok=True) + + # ------------------------------------------------------- copy binary + # Search in src_folder directly, or inside src_folder/bin/ + bin_src = os.path.join(src_folder, BIN_NAME) + if not os.path.exists(bin_src): + bin_src = os.path.join(src_folder, 'bin', BIN_NAME) + if not os.path.exists(bin_src): + raise FileNotFoundError(f'Binary {BIN_NAME} not found in {src_folder}') + shutil.copy2(bin_src, os.path.join(deb_folder, BIN_PATH, BIN_NAME)) + + # ------------------------------------------------------- copy lib/lvgl.so (optional) + lib_src = os.path.join(src_folder, 'lib', 'lvgl.so') + if os.path.exists(lib_src): + shutil.copy2(lib_src, os.path.join(deb_folder, LIB_PATH, 'lvgl.so')) + print(f' Included: lib/lvgl.so') + + # ------------------------------------------------------- copy project resources + project_dir = os.path.abspath(os.path.join(tools_dir, '..')) + + # share/images/*.png + src_images = os.path.join(project_dir, 'share', 'images') + if os.path.isdir(src_images): + dst_images = os.path.join(deb_folder, SHARE_PATH, 'images') + os.makedirs(dst_images, exist_ok=True) + for img in glob.glob(os.path.join(src_images, '*.png')): + shutil.copy2(img, dst_images) + print(f' Included: share/images/{os.path.basename(img)}') + + # applications/*.desktop -> standard system path + src_apps = os.path.join(project_dir, 'applications') + if os.path.isdir(src_apps): + std_apps_dir = os.path.join(deb_folder, 'usr', 'share', 'applications') + os.makedirs(std_apps_dir, exist_ok=True) + for desktop in glob.glob(os.path.join(src_apps, '*.desktop')): + basename = os.path.basename(desktop) + with open(desktop, 'r') as f: + content = f.read() + # Use absolute icon path for standard location + content = content.replace( + 'Icon=share/images/Recorder.png', + 'Icon=/usr/share/Recorder/share/images/Recorder.png' + ) + std_desktop = os.path.join(std_apps_dir, basename) + with open(std_desktop, 'w') as f: + f.write(content) + print(f' Included: usr/share/applications/{basename}') + + # ------------------------------------------------------- DEBIAN/control + with open(os.path.join(deb_folder, 'DEBIAN', 'control'), 'w') as f: + f.write(f'Package: {PACKAGE_NAME}\n') + f.write(f'Version: {version}\n') + f.write(f'Architecture: arm64\n') + f.write(f'Maintainer: dianjixz \n') + f.write(f'Original-Maintainer: m5stack \n') + f.write(f'Section: {APP_NAME}\n') + f.write(f'Priority: optional\n') + f.write(f'Homepage: https://www.m5stack.com\n') + f.write(f'Packaged-Date: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n') + f.write(f'Description: M5CardputerZero {APP_NAME}\n') + + # ------------------------------------------------------- DEBIAN/postinst + with open(os.path.join(deb_folder, 'DEBIAN', 'postinst'), 'w') as f: + f.write('#!/bin/sh\n') + f.write(f'mkdir -p /var/cache/{APP_NAME}\n') + f.write(f'ln -s /var/cache/{APP_NAME} /usr/share/{APP_NAME}/cache\n') + f.write('exit 0\n') + + # ------------------------------------------------------- DEBIAN/prerm + with open(os.path.join(deb_folder, 'DEBIAN', 'prerm'), 'w') as f: + f.write('#!/bin/sh\n') + f.write(f'rm -rf /var/cache/{APP_NAME}\n') + f.write('exit 0\n') + + # ------------------------------------------------------- fix permissions + os.chmod(os.path.join(deb_folder, 'DEBIAN', 'postinst'), 0o755) + os.chmod(os.path.join(deb_folder, 'DEBIAN', 'prerm'), 0o755) + os.chmod(os.path.join(deb_folder, BIN_PATH, BIN_NAME), 0o755) + + # ------------------------------------------------------- build .deb + subprocess.run(['dpkg-deb', '-b', deb_folder, deb_file], check=True) + print(f'Debian package created: {deb_file}') + + # shutil.rmtree(deb_folder) + return f'{PACKAGE_NAME} create success!' + + +if __name__ == '__main__': + + if 'clean' in sys.argv: + os.system('rm -f ./*.deb') + os.system('find . -maxdepth 1 -type d ! -name "." -exec rm -rf {} +') + sys.exit(0) + + if 'distclean' in sys.argv: + os.system('rm -rf ./*.deb m5stack_*') + sys.exit(0) + + version = '0.2.0' + src_folder = '../dist' + revision = 'm5stack1' + + result = create_recorder_deb(version, src_folder, revision) + print(result) From 2c400671267091a0c01e0b786043ec5898ba2251 Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Mon, 1 Jun 2026 14:08:09 +0800 Subject: [PATCH 07/11] Add filename editing and rename support to Recorder --- projects/Recorder/main/include/recorder_app.h | 5 + projects/Recorder/main/include/ui_recorder.h | 7 +- projects/Recorder/main/src/main.cpp | 12 +- projects/Recorder/main/src/recorder_app.cpp | 72 +++++++++-- projects/Recorder/main/src/ui_recorder.cpp | 116 +++++++++++++++--- 5 files changed, 187 insertions(+), 25 deletions(-) diff --git a/projects/Recorder/main/include/recorder_app.h b/projects/Recorder/main/include/recorder_app.h index 9fff1f95..338d150f 100644 --- a/projects/Recorder/main/include/recorder_app.h +++ b/projects/Recorder/main/include/recorder_app.h @@ -102,4 +102,9 @@ class RecorderApp { AppState appState_ = AppState::Idle; std::string sampleRate_ = "48k"; std::string playbackSpeed_ = "1.0X"; + + // Filename editing state (used by SaveConfirm and rename) + std::string editingName_; + bool isRenaming_ = false; + std::string renameOriginalPath_; }; diff --git a/projects/Recorder/main/include/ui_recorder.h b/projects/Recorder/main/include/ui_recorder.h index 9345d09e..9b27c546 100644 --- a/projects/Recorder/main/include/ui_recorder.h +++ b/projects/Recorder/main/include/ui_recorder.h @@ -23,7 +23,8 @@ class UiRecorder : public IRecorderView { void setActionHandler(std::function handler) override; // Keyboard event from input system - void onKeyPressed(uint32_t key_code); + void onKeyPressed(uint32_t key_code, bool isRepeat = false); + void onCharTyped(uint32_t codepoint); private: void buildUi(lv_obj_t* parent); @@ -78,7 +79,8 @@ class UiRecorder : public IRecorderView { lv_obj_t* lblPauseTimer_ = nullptr; // SaveConfirm - lv_obj_t* lblSaveFilename_ = nullptr; + lv_obj_t* taEdit_ = nullptr; + std::string editOriginalName_; // Playback lv_obj_t* lblPlayFilename_ = nullptr; @@ -95,5 +97,6 @@ class UiRecorder : public IRecorderView { uint32_t lastKeyTime_ = 0; uint32_t lastKeyCode_ = 0; + uint32_t backspaceRepeatCount_ = 0; std::function actionHandler_; }; diff --git a/projects/Recorder/main/src/main.cpp b/projects/Recorder/main/src/main.cpp index 9987dc9c..d72cce7b 100644 --- a/projects/Recorder/main/src/main.cpp +++ b/projects/Recorder/main/src/main.cpp @@ -67,9 +67,14 @@ void handle_keyboard_event(lv_event_t *event) if (lv_event_get_code(event) == static_cast(LV_EVENT_KEYBOARD)) { auto *key = static_cast(lv_event_get_param(event)); - if (!key || key->key_state != KBD_KEY_PRESSED) return; // Ignore release and repeat + if (!key || key->key_state == KBD_KEY_RELEASED) return; // Ignore release, allow repeat key_code = key->key_code; - pressed = true; + bool isRepeat = (key->key_state == KBD_KEY_REPEATED); + if (g_ui && key->codepoint >= 32 && key->codepoint < 127 && !isRepeat) { + g_ui->onCharTyped(key->codepoint); + } + if (g_ui) g_ui->onKeyPressed(key_code, isRepeat); + return; } #if LV_USE_SDL else if (lv_event_get_code(event) == LV_EVENT_KEY) { @@ -92,6 +97,9 @@ void handle_keyboard_event(lv_event_t *event) case '5': key_code = KEY_F8; break; default: key_code = lv_key; break; } + if (g_ui && lv_key >= 32 && lv_key < 127) { + g_ui->onCharTyped(lv_key); + } pressed = true; } #endif diff --git a/projects/Recorder/main/src/recorder_app.cpp b/projects/Recorder/main/src/recorder_app.cpp index 85c19722..7bdf2153 100644 --- a/projects/Recorder/main/src/recorder_app.cpp +++ b/projects/Recorder/main/src/recorder_app.cpp @@ -63,9 +63,44 @@ void RecorderApp::onAction(const std::string& action) engine_.stopRecording(); asyncScanFiles(); } + { + size_t pos = lastRecordingPath_.find_last_of('/'); + std::string fname = lastRecordingPath_.substr(pos + 1); + size_t dot = fname.find_last_of('.'); + if (dot != std::string::npos) editingName_ = fname.substr(0, dot); + else editingName_ = fname; + } + isRenaming_ = false; + renameOriginalPath_.clear(); appState_ = AppState::SaveConfirm; - } else if (action == "save") { - appState_ = AppState::Idle; + } else if (action.rfind("save", 0) == 0) { + std::string newName; + if (action.length() > 5 && action[4] == ':') { + newName = action.substr(5); + } else { + newName = editingName_; + } + if (newName.empty()) newName = "recording"; + std::string newFilename = newName + ".wav"; + std::string dir = recordingsDir(); + std::string newPath = dir + "/" + newFilename; + if (isRenaming_) { + if (renameOriginalPath_ != newPath) { + ::rename(renameOriginalPath_.c_str(), newPath.c_str()); + asyncScanFiles(); + } + } else { + if (lastRecordingPath_ != newPath) { + ::rename(lastRecordingPath_.c_str(), newPath.c_str()); + lastRecordingPath_ = newPath; + asyncScanFiles(); + } + } + editingName_.clear(); + bool wasRenaming = isRenaming_; + isRenaming_ = false; + renameOriginalPath_.clear(); + appState_ = wasRenaming ? AppState::FileList : AppState::Idle; } else if (action == "exit") { switch (appState_) { case AppState::Recording: @@ -75,7 +110,18 @@ void RecorderApp::onAction(const std::string& action) appState_ = AppState::Idle; break; case AppState::SaveConfirm: - appState_ = AppState::Idle; + if (isRenaming_) { + appState_ = AppState::FileList; + } else { + if (!lastRecordingPath_.empty()) { + unlink(lastRecordingPath_.c_str()); + asyncScanFiles(); + } + appState_ = AppState::Idle; + } + editingName_.clear(); + isRenaming_ = false; + renameOriginalPath_.clear(); break; case AppState::FileList: appState_ = AppState::Idle; @@ -117,7 +163,16 @@ void RecorderApp::onAction(const std::string& action) } else if (action == "seek_fwd") { engine_.seekPlayback(engine_.playbackPosition() + 10.0f); } else if (action == "rename") { - // TODO: implement rename + std::lock_guard lock(filesMutex_); + if (!files_.empty() && currentFileIndex_ >= 0 && currentFileIndex_ < static_cast(files_.size())) { + renameOriginalPath_ = files_[currentFileIndex_].filepath; + std::string fname = files_[currentFileIndex_].filename; + size_t dot = fname.find_last_of('.'); + if (dot != std::string::npos) editingName_ = fname.substr(0, dot); + else editingName_ = fname; + isRenaming_ = true; + appState_ = AppState::SaveConfirm; + } } else if (action == "delete") { handleDelete(); } @@ -157,9 +212,10 @@ void RecorderApp::syncStateFromEngine() AudioState audio = engine_.state(); switch (audio) { case AudioState::Idle: - if (appState_ == AppState::Recording || appState_ == AppState::RecPaused || - appState_ == AppState::Playing || appState_ == AppState::PlayPaused) { + if (appState_ == AppState::Recording || appState_ == AppState::RecPaused) { appState_ = AppState::Idle; + } else if (appState_ == AppState::Playing || appState_ == AppState::PlayPaused) { + appState_ = AppState::FileList; } break; case AudioState::Recording: @@ -233,7 +289,9 @@ RecorderState RecorderApp::getState() const rs.fileList = files_; rs.selectedFileIndex = currentFileIndex_; - if (rs.state == AppState::Recording || rs.state == AppState::RecPaused || rs.state == AppState::SaveConfirm) { + if (rs.state == AppState::SaveConfirm) { + rs.currentFileName = editingName_ + ".wav"; + } else if (rs.state == AppState::Recording || rs.state == AppState::RecPaused) { size_t pos = lastRecordingPath_.find_last_of('/'); rs.currentFileName = lastRecordingPath_.substr(pos + 1); } else if (!files_.empty() && currentFileIndex_ >= 0 && currentFileIndex_ < static_cast(files_.size())) { diff --git a/projects/Recorder/main/src/ui_recorder.cpp b/projects/Recorder/main/src/ui_recorder.cpp index db561f10..b3c7d797 100644 --- a/projects/Recorder/main/src/ui_recorder.cpp +++ b/projects/Recorder/main/src/ui_recorder.cpp @@ -217,14 +217,23 @@ void UiRecorder::createPageRecPaused(lv_obj_t* page) // ---------- Save Confirm Page ---------- void UiRecorder::createPageSaveConfirm(lv_obj_t* page) { - lblSaveFilename_ = lv_label_create(page); - lv_obj_set_pos(lblSaveFilename_, 10, 40); - lv_obj_set_width(lblSaveFilename_, 300); - lv_obj_set_style_text_font(lblSaveFilename_, &lv_font_montserrat_14, 0); - lv_obj_set_style_text_color(lblSaveFilename_, kColorText, 0); - lv_obj_set_style_text_align(lblSaveFilename_, LV_TEXT_ALIGN_CENTER, 0); - lv_label_set_long_mode(lblSaveFilename_, LV_LABEL_LONG_SCROLL_CIRCULAR); - lv_label_set_text(lblSaveFilename_, ""); + taEdit_ = lv_textarea_create(page); + lv_obj_set_pos(taEdit_, 10, 35); + lv_obj_set_size(taEdit_, 300, 30); + lv_obj_set_style_text_font(taEdit_, &lv_font_montserrat_14, 0); + lv_obj_set_style_text_color(taEdit_, kColorText, 0); + lv_obj_set_style_bg_opa(taEdit_, LV_OPA_TRANSP, 0); + lv_obj_set_style_border_width(taEdit_, 0, 0); + lv_obj_set_style_pad_all(taEdit_, 0, 0); + lv_textarea_set_one_line(taEdit_, true); + lv_textarea_set_text(taEdit_, ""); + lv_textarea_set_text_selection(taEdit_, true); + + // Cursor styling + lv_obj_set_style_border_width(taEdit_, 2, LV_PART_CURSOR); + lv_obj_set_style_border_color(taEdit_, kColorHighlight, LV_PART_CURSOR); + lv_obj_set_style_bg_opa(taEdit_, LV_OPA_50, LV_PART_CURSOR); + lv_obj_set_style_bg_color(taEdit_, kColorHighlight, LV_PART_CURSOR); } // ---------- Playback Page ---------- @@ -313,7 +322,7 @@ void UiRecorder::updateBottomLabels(UiPage page, bool isPaused) labels = {"--", "--", "Continue", "Done", "Exit"}; break; case UiPage::SaveConfirm: - labels = {"--", "--", "--", "Save", "Exit"}; + labels = {"Undo", "<-", "->", "Save", "Exit"}; break; case UiPage::Playback: labels = {lastState_.playbackSpeed.c_str(), "-10s", isPaused ? "Play" : "Pause", "+10s", "Exit"}; @@ -330,6 +339,8 @@ void UiRecorder::updateBottomLabels(UiPage page, bool isPaused) // ---------- Content update ---------- void UiRecorder::updatePageContent(const RecorderState& state) { + // Detect page transitions before updating lastState_ + bool enteringSaveConfirm = (lastState_.state != AppState::SaveConfirm && state.state == AppState::SaveConfirm); lastState_ = state; // Status bar @@ -379,10 +390,23 @@ void UiRecorder::updatePageContent(const RecorderState& state) const char* fname = state.currentFileName.empty() ? "" : state.currentFileName.c_str(); lv_label_set_text(lblRecFilename_, fname); lv_label_set_text(lblPauseFilename_, fname); - lv_label_set_text(lblSaveFilename_, fname); lv_label_set_text(lblRecTimer_, state.timerText.c_str()); lv_label_set_text(lblPauseTimer_, state.timerText.c_str()); + if (enteringSaveConfirm && taEdit_) { + std::string nameOnly = state.currentFileName; + size_t dot = nameOnly.find_last_of('.'); + if (dot != std::string::npos) nameOnly = nameOnly.substr(0, dot); + lv_textarea_set_text(taEdit_, nameOnly.c_str()); + lv_textarea_set_cursor_pos(taEdit_, LV_TEXTAREA_CURSOR_LAST); + lv_obj_t* label = lv_textarea_get_label(taEdit_); + if (label) { + lv_label_set_text_selection_start(label, 0); + lv_label_set_text_selection_end(label, nameOnly.length()); + } + editOriginalName_ = nameOnly; + } + // Playback lv_label_set_text(lblPlayFilename_, fname); lv_label_set_text(lblPlayTimer_, state.timerText.c_str()); @@ -462,13 +486,60 @@ void UiRecorder::setActionHandler(std::function handle } // ---------- Key handling ---------- -void UiRecorder::onKeyPressed(uint32_t key_code) +void UiRecorder::onKeyPressed(uint32_t key_code, bool isRepeat) { uint32_t now = lv_tick_get(); - if (key_code == lastKeyCode_ && now - lastKeyTime_ < 200) return; + if (!isRepeat && key_code == lastKeyCode_ && now - lastKeyTime_ < 200) return; lastKeyCode_ = key_code; lastKeyTime_ = now; + // Direct textarea editing in SaveConfirm + if (currentPage_ == UiPage::SaveConfirm) { + switch (key_code) { + case KEY_F4: + if (taEdit_) { + lv_textarea_set_text(taEdit_, editOriginalName_.c_str()); + lv_obj_t* label = lv_textarea_get_label(taEdit_); + if (label) { + lv_label_set_text_selection_start(label, 0); + lv_label_set_text_selection_end(label, editOriginalName_.length()); + } + } + return; + case KEY_F5: + if (taEdit_) { + lv_textarea_cursor_left(taEdit_); + lv_textarea_clear_selection(taEdit_); + } + return; + case KEY_F6: + if (taEdit_) { + lv_textarea_cursor_right(taEdit_); + lv_textarea_clear_selection(taEdit_); + } + return; + case KEY_BACKSPACE: + case KEY_DELETE: + if (taEdit_) { + if (isRepeat) { + backspaceRepeatCount_++; + if (backspaceRepeatCount_ >= 15) { + lv_textarea_set_text(taEdit_, ""); + backspaceRepeatCount_ = 0; + } else { + lv_textarea_delete_char(taEdit_); + } + } else { + backspaceRepeatCount_ = 1; + lv_textarea_delete_char(taEdit_); + } + } + return; + default: + break; + } + } + int btnIndex = -1; switch (key_code) { case KEY_F4: btnIndex = 0; break; @@ -518,6 +589,14 @@ void UiRecorder::onKeyPressed(uint32_t key_code) handleActionForPage(currentPage_, btnIndex); } +void UiRecorder::onCharTyped(uint32_t codepoint) +{ + if (currentPage_ != UiPage::SaveConfirm || !taEdit_) return; + if (codepoint >= 32 && codepoint < 127) { + lv_textarea_add_char(taEdit_, codepoint); + } +} + void UiRecorder::handleActionForPage(UiPage page, int btn) { if (!actionHandler_) return; @@ -545,7 +624,7 @@ void UiRecorder::handleActionForPage(UiPage page, int btn) map = {"", "", "toggle_pause", "done", "exit"}; break; case UiPage::SaveConfirm: - map = {"", "", "", "save", "exit"}; + map = {"undo", "left", "right", "save", "exit"}; break; case UiPage::Playback: map = {"speed", "seek_back", "toggle_pause", "seek_fwd", "exit"}; @@ -562,6 +641,15 @@ void UiRecorder::handleActionForPage(UiPage page, int btn) } if (action && action[0] != '\0') { - actionHandler_(action); + if (page == UiPage::SaveConfirm && btn == 3) { + // Save: get edited name from textarea + if (taEdit_) { + const char* text = lv_textarea_get_text(taEdit_); + std::string name = text ? text : ""; + actionHandler_(std::string("save:") + name); + } + } else { + actionHandler_(action); + } } } From a6019a304ab1519a53f2a2f6df98e27022f43039 Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Tue, 2 Jun 2026 11:42:16 +0800 Subject: [PATCH 08/11] Add Compass app with BMI270 + BMM150 sensor support --- projects/Compass/SConstruct | 71 + projects/Compass/applications/Compass.desktop | 6 + projects/Compass/config_defaults.mk | 13 + .../linux_x86_cross_cp0_config_defaults.mk | 25 + .../Compass/linux_x86_sdl2_config_defaults.mk | 22 + projects/Compass/main/SConstruct | 77 + projects/Compass/main/include/bmi2.h | 2071 +++ projects/Compass/main/include/bmi270.h | 420 + projects/Compass/main/include/bmi270_port.h | 28 + projects/Compass/main/include/bmi2_defs.h | 3164 +++++ projects/Compass/main/include/bmm150.h | 479 + .../Compass/main/include/bmm150_aux_port.h | 39 + projects/Compass/main/include/bmm150_defs.h | 656 + projects/Compass/main/include/compass_app.h | 67 + .../Compass/main/include/compat/input_keys.h | 82 + .../Compass/main/include/keyboard_input.h | 46 + projects/Compass/main/include/ui_compass.h | 44 + projects/Compass/main/src/bmi2.c | 11702 ++++++++++++++++ projects/Compass/main/src/bmi270.c | 4488 ++++++ projects/Compass/main/src/bmi270_port.c | 131 + projects/Compass/main/src/bmm150.c | 2187 +++ projects/Compass/main/src/bmm150_aux_port.c | 70 + projects/Compass/main/src/compass_app.cpp | 383 + projects/Compass/main/src/main.cpp | 254 + projects/Compass/main/src/ui_compass.cpp | 165 + projects/Compass/share/images/Compass.png | Bin 0 -> 4215 bytes projects/Recorder/tools/llm_pack.py | 2 +- 27 files changed, 26691 insertions(+), 1 deletion(-) create mode 100644 projects/Compass/SConstruct create mode 100644 projects/Compass/applications/Compass.desktop create mode 100644 projects/Compass/config_defaults.mk create mode 100644 projects/Compass/linux_x86_cross_cp0_config_defaults.mk create mode 100644 projects/Compass/linux_x86_sdl2_config_defaults.mk create mode 100644 projects/Compass/main/SConstruct create mode 100644 projects/Compass/main/include/bmi2.h create mode 100644 projects/Compass/main/include/bmi270.h create mode 100644 projects/Compass/main/include/bmi270_port.h create mode 100644 projects/Compass/main/include/bmi2_defs.h create mode 100644 projects/Compass/main/include/bmm150.h create mode 100644 projects/Compass/main/include/bmm150_aux_port.h create mode 100644 projects/Compass/main/include/bmm150_defs.h create mode 100644 projects/Compass/main/include/compass_app.h create mode 100644 projects/Compass/main/include/compat/input_keys.h create mode 100644 projects/Compass/main/include/keyboard_input.h create mode 100644 projects/Compass/main/include/ui_compass.h create mode 100644 projects/Compass/main/src/bmi2.c create mode 100644 projects/Compass/main/src/bmi270.c create mode 100644 projects/Compass/main/src/bmi270_port.c create mode 100644 projects/Compass/main/src/bmm150.c create mode 100644 projects/Compass/main/src/bmm150_aux_port.c create mode 100644 projects/Compass/main/src/compass_app.cpp create mode 100644 projects/Compass/main/src/main.cpp create mode 100644 projects/Compass/main/src/ui_compass.cpp create mode 100644 projects/Compass/share/images/Compass.png diff --git a/projects/Compass/SConstruct b/projects/Compass/SConstruct new file mode 100644 index 00000000..1e5c1ff5 --- /dev/null +++ b/projects/Compass/SConstruct @@ -0,0 +1,71 @@ +from pathlib import Path +import os +import platform +import shutil +import sys + +arch = platform.machine() +homebrew_toolchain = "/opt/homebrew/bin" +has_homebrew_aarch64 = os.path.exists(os.path.join(homebrew_toolchain, "aarch64-linux-gnu-gcc")) + +version = "v0.0.3" +static_lib = "static_lib" +update = False + +if "CardputerZero" in os.environ: + if not os.path.exists("build/config/config_tmp.mk"): + os.makedirs("build/config", exist_ok=True) + with open("build/config/config_tmp.mk", "w") as f: + f.write("CONFIG_V9_5_LV_USE_LINUX_FBDEV=y\n") + f.write("CONFIG_V9_5_LV_DRAW_SW_ASM_NEON=y\n") + f.write("CONFIG_V9_5_LV_USE_DRAW_SW_ASM=1\n") + f.write("CONFIG_V9_5_LV_USE_EVDEV=y\n") + if has_homebrew_aarch64: + f.write(f'CONFIG_TOOLCHAIN_PATH="{homebrew_toolchain}"\n') + f.write('CONFIG_TOOLCHAIN_PREFIX="aarch64-linux-gnu-"\n') + f.write(f'''CONFIG_TOOLCHAIN_SYSROOT="{os.path.join(sys.path[0], "static_lib")}"\n''') +elif arch != "aarch64": + if not os.path.exists("build/config/config_tmp.mk"): + os.makedirs("build/config", exist_ok=True) + with open("build/config/config_tmp.mk", "w") as f: + f.write("CONFIG_V9_5_LV_USE_SDL=y\n") +else: + if not os.path.exists("build/config/config_tmp.mk"): + os.makedirs("build/config", exist_ok=True) + with open("build/config/config_tmp.mk", "w") as f: + f.write("CONFIG_V9_5_LV_USE_LINUX_FBDEV=y\n") + f.write("CONFIG_V9_5_LV_DRAW_SW_ASM_NEON=y\n") + f.write("CONFIG_V9_5_LV_USE_DRAW_SW_ASM=1\n") + f.write("CONFIG_V9_5_LV_USE_EVDEV=y\n") + +local_path = Path(os.getcwd()) +sdk_path = local_path.parent.parent / "SDK" +os.environ["SDK_PATH"] = str(sdk_path) +os.environ["EXT_COMPONENTS_PATH"] = str(sdk_path.parent / "ext_components") + +env = SConscript( + str(sdk_path / "tools" / "scons" / "project.py"), + variant_dir=os.getcwd(), + duplicate=0, +) + +if not os.path.exists(static_lib): + update = True +else: + try: + with open(str(Path(static_lib) / "version"), "r") as f: + if version != f.read().strip(): + update = True + except Exception: + update = True + +if update and "CardputerZero" in os.environ: + with open(env["PROJECT_TOOL_S"]) as f: + exec(f.read()) + down_url = "https://github.com/dianjixz/M5CardputerZero-UserDemo/releases/download/{}/sdk_bsp.tar.gz".format( + version + ) + down_path = check_wget_down(down_url, "static_lib_{}.tar.gz".format(version)) + if os.path.exists(static_lib): + shutil.rmtree(static_lib) + shutil.move(down_path, static_lib) diff --git a/projects/Compass/applications/Compass.desktop b/projects/Compass/applications/Compass.desktop new file mode 100644 index 00000000..fe8733a4 --- /dev/null +++ b/projects/Compass/applications/Compass.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=Compass +Exec=/usr/share/Compass/bin/M5CardputerZero-Compass +Terminal=false +Icon=share/images/Compass.png +Type=Application diff --git a/projects/Compass/config_defaults.mk b/projects/Compass/config_defaults.mk new file mode 100644 index 00000000..b1de7ee1 --- /dev/null +++ b/projects/Compass/config_defaults.mk @@ -0,0 +1,13 @@ +CONFIG_LVGL_COMPONENT_ENABLED=y +CONFIG_LVGL_9_5_SRC=y +CONFIG_V9_5_LV_USE_CLIB_MALLOC=y +CONFIG_V9_5_LV_USE_CLIB_STRING=y +CONFIG_V9_5_LV_USE_CLIB_SPRINTF=y + +CONFIG_V9_5_LV_FONT_MONTSERRAT_10=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_12=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_14=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_16=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_26=y +CONFIG_V9_5_LV_FONT_DEFAULT_MONTSERRAT_14=y + diff --git a/projects/Compass/linux_x86_cross_cp0_config_defaults.mk b/projects/Compass/linux_x86_cross_cp0_config_defaults.mk new file mode 100644 index 00000000..a0864e35 --- /dev/null +++ b/projects/Compass/linux_x86_cross_cp0_config_defaults.mk @@ -0,0 +1,25 @@ + +CONFIG_TOOLCHAIN_PREFIX="aarch64-linux-gnu-" + +CONFIG_LVGL_COMPONENT_ENABLED=y +CONFIG_LVGL_9_5_SRC=y + +CONFIG_V9_5_LV_USE_CLIB_MALLOC=y +CONFIG_V9_5_LV_USE_CLIB_STRING=y +CONFIG_V9_5_LV_USE_CLIB_SPRINTF=y + +CONFIG_V9_5_LV_FONT_MONTSERRAT_10=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_12=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_14=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_16=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_26=y +CONFIG_V9_5_LV_FONT_DEFAULT_MONTSERRAT_14=y + + +CONFIG_V9_5_LV_USE_LINUX_FBDEV=y +CONFIG_V9_5_LV_DRAW_SW_ASM_NEON=y +CONFIG_V9_5_LV_USE_DRAW_SW_ASM=1 +CONFIG_V9_5_LV_USE_EVDEV=y +CONFIG_V9_5_LV_OS_PTHREAD=y +CONFIG_V9_5_LV_DRAW_THREAD_STACK_SIZE=65536 +CONFIG_V9_5_LV_DRAW_THREAD_PRIO=3 diff --git a/projects/Compass/linux_x86_sdl2_config_defaults.mk b/projects/Compass/linux_x86_sdl2_config_defaults.mk new file mode 100644 index 00000000..a015cb52 --- /dev/null +++ b/projects/Compass/linux_x86_sdl2_config_defaults.mk @@ -0,0 +1,22 @@ + +# CONFIG_TOOLCHAIN_PREFIX="aarch64-linux-gnu-" + +CONFIG_LVGL_COMPONENT_ENABLED=y +CONFIG_LVGL_9_5_SRC=y + +CONFIG_V9_5_LV_USE_CLIB_MALLOC=y +CONFIG_V9_5_LV_USE_CLIB_STRING=y +CONFIG_V9_5_LV_USE_CLIB_SPRINTF=y + +CONFIG_V9_5_LV_FONT_MONTSERRAT_10=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_12=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_14=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_16=y +CONFIG_V9_5_LV_FONT_MONTSERRAT_26=y +CONFIG_V9_5_LV_FONT_DEFAULT_MONTSERRAT_14=y + + +CONFIG_V9_5_LV_USE_SDL=y +CONFIG_V9_5_LV_OS_PTHREAD=y +CONFIG_V9_5_LV_DRAW_THREAD_STACK_SIZE=65536 +CONFIG_V9_5_LV_DRAW_THREAD_PRIO=3 diff --git a/projects/Compass/main/SConstruct b/projects/Compass/main/SConstruct new file mode 100644 index 00000000..c1a3215c --- /dev/null +++ b/projects/Compass/main/SConstruct @@ -0,0 +1,77 @@ +import os + +Import("env") +with open(env["PROJECT_TOOL_S"]) as f: + exec(f.read()) + +if "CONFIG_TOOLCHAIN_PREFIX" in os.environ: + env["RANLIB"] = '${_concat(GCCPREFIX, "ranlib", GCCSUFFIX, __env__)}' + env["RANLIBCOM"] = "$RANLIB $TARGET" + env["ARFLAGS"] = ["rcs"] + +SRCS = Glob("src/*.c*") +INCLUDE = [ADir("."), ADir("include")] +PRIVATE_INCLUDE = [] +REQUIREMENTS = ["lvgl_component", "pthread"] +if "CONFIG_V9_5_LV_USE_SDL" not in os.environ: + REQUIREMENTS += ["input", "xkbcommon", "udev"] +STATIC_LIB = [] +DYNAMIC_LIB = [] +DEFINITIONS = [] +DEFINITIONS_PRIVATE = [] +LDFLAGS = [] +LINK_SEARCH_PATH = [] +STATIC_FILES = [] +MULTIARCH_INCLUDE = None + +if "CONFIG_TOOLCHAIN_SYSROOT" in os.environ: + sysroot = os.environ["CONFIG_TOOLCHAIN_SYSROOT"].strip('"') + multiarch_include = os.path.join(sysroot, "usr", "include", "aarch64-linux-gnu") + if os.path.isdir(multiarch_include): + INCLUDE += [multiarch_include] + MULTIARCH_INCLUDE = multiarch_include + multiarch_lib = os.path.join(sysroot, "usr", "lib", "aarch64-linux-gnu") + if os.path.isdir(multiarch_lib): + LDFLAGS += [f"-B{multiarch_lib}", f"-Wl,-rpath-link,{multiarch_lib}"] + LINK_SEARCH_PATH += [multiarch_lib] + +lvgl_component = list(filter(lambda x: x["target"] == "lvgl_component", env["COMPONENTS"]))[0] +if MULTIARCH_INCLUDE: + for component in env["COMPONENTS"]: + component["INCLUDE"] += [MULTIARCH_INCLUDE] +if "CONFIG_V9_5_LV_USE_SDL" in os.environ: + lvgl_component["REQUIREMENTS"] += ["SDL2"] + lvgl_component["INCLUDE"] += ["/usr/include/SDL2"] + for sdl_include in [ + "/opt/homebrew/include", + "/opt/homebrew/include/SDL2", + "/usr/local/include", + "/usr/local/include/SDL2", + ]: + if os.path.exists(sdl_include): + lvgl_component["INCLUDE"] += [sdl_include] + for sdl_lib in ["/opt/homebrew/lib", "/usr/local/lib"]: + if os.path.exists(sdl_lib): + lvgl_component["LINK_SEARCH_PATH"] += [sdl_lib] + lvgl_component["DEFINITIONS"] += ["-D_REENTRANT"] + for src in list(lvgl_component["SRCS"]): + if "lv_sdl_keyboard.c" in str(src): + lvgl_component["SRCS"].remove(src) + +env["COMPONENTS"].append( + { + "target": "M5CardputerZero-Compass", + "SRCS": SRCS, + "INCLUDE": INCLUDE, + "PRIVATE_INCLUDE": PRIVATE_INCLUDE, + "REQUIREMENTS": REQUIREMENTS, + "STATIC_LIB": STATIC_LIB, + "DYNAMIC_LIB": DYNAMIC_LIB, + "DEFINITIONS": DEFINITIONS, + "DEFINITIONS_PRIVATE": DEFINITIONS_PRIVATE, + "LDFLAGS": LDFLAGS, + "LINK_SEARCH_PATH": LINK_SEARCH_PATH, + "STATIC_FILES": STATIC_FILES, + "REGISTER": "project", + } +) diff --git a/projects/Compass/main/include/bmi2.h b/projects/Compass/main/include/bmi2.h new file mode 100644 index 00000000..c5c1fc1e --- /dev/null +++ b/projects/Compass/main/include/bmi2.h @@ -0,0 +1,2071 @@ +/** +* Copyright (c) 2025 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 bmi2.h +* @date 2025-04-22 +* @version v2.113.0 +* +*/ + +/*! + * @defgroup bmi2xy BMI2XY + */ + +/** + * \ingroup bmi2xy + * \defgroup bmi2 BMI2 + * @brief Sensor driver for BMI2 sensor + */ + +#ifndef BMI2_H_ +#define BMI2_H_ + +/*! CPP guard */ +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************/ + +/*! Header files + ****************************************************************************/ +#include "bmi2_defs.h" + +/***************************************************************************/ + +/*! BMI2XY User Interface function prototypes + ****************************************************************************/ + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiInit Initialization + * @brief Initialize the sensor and device structure + */ + +/*! + * \ingroup bmi2ApiInit + * \page bmi2_api_bmi2_sec_init bmi2_sec_init + * \code + * int8_t bmi2_sec_init(struct bmi2_dev *dev); + * \endcode + * @details This API is the entry point for bmi2 sensor. It selects between + * I2C/SPI interface, based on user selection. It also reads the chip-id of + * the sensor. + * + * @param[in,out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_sec_init(struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiInit + * \page bmi2_api_bmi2_set_spi_en bmi2_set_spi_en + * \code + * int8_t bmi2_set_spi_en(uint8_t enable, struct bmi2_dev *dev); + * \endcode + * @details This API sets the status of SPI enable . + * + * @param[in] enable : To enable/disable SPI interface. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Enable I2C + * BMI2_ENABLE | Enable SPI + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_spi_en(uint8_t enable, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiInit + * \page bmi2_api_bmi2_get_spi_en bmi2_get_spi_en + * \code + * int8_t bmi2_get_spi_en(uint8_t *enable, struct bmi2_dev *dev); + * \endcode + * @details This API gets the status of SPI enable . + * + * @param[in] enable : Pointer to get enable/disable SPI interface. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Enable I2C + * BMI2_ENABLE | Enable SPI + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_spi_en(uint8_t *enable, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiInit + * \page bmi2_api_bmi2_set_spi3_interface_mode bmi2_set_spi3_interface_mode + * \code + * int8_t bmi2_set_spi3_interface_mode(uint8_t enable, struct bmi2_dev *dev); + * \endcode + * @details This API sets the status of SPI interface. + * + * @param[in] enable : To enable/disable SPI 3/4 wire interface. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Enable SPI 4 wire mode + * BMI2_ENABLE | Enable SPI 3 wire mode + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_spi3_interface_mode(uint8_t enable, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiInit + * \page bmi2_api_bmi2_get_spi3_interface_mode bmi2_get_spi3_interface_mode + * \code + * int8_t bmi2_get_spi3_interface_mode(uint8_t *enable, struct bmi2_dev *dev); + * \endcode + * @details This API gets the status of SPI interface. + * + * @param[in] enable : Pointer to get enable/disable SPI 3/4 wire interface. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Enable SPI 4 wire mode + * BMI2_ENABLE | Enable SPI 3 wire mode + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_spi3_interface_mode(uint8_t *enable, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiI2C I2c watchdog init + * @brief Initialize the watchdog for i2c + */ + +/*! + * \ingroup bmi2ApiI2C + * \page bmi2_api_bmi2_set_i2c_wdt_en bmi2_set_i2c_wdt_en + * \code + * int8_t bmi2_set_i2c_wdt_en(uint8_t enable, struct bmi2_dev *dev); + * \endcode + * @details This API enables/disables the i2c watchdog timer . + * + * @param[in] enable : To enable/disable i2c watchdog timer. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Disable I2C watchdog timer + * BMI2_ENABLE | Enable I2C watchdog timer + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_i2c_wdt_en(uint8_t enable, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiI2C + * \page bmi2_api_bmi2_get_i2c_wdt_en bmi2_get_i2c_wdt_en + * \code + * int8_t bmi2_get_i2c_wdt_en(uint8_t *enable, struct bmi2_dev *dev); + * \endcode + * @details This API gets the enable/disable status of the i2c watchdog timer . + * + * @param[in] enable : Pointer to get status of enable/disable i2c watchdog timer. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Disable I2C watchdog timer + * BMI2_ENABLE | Enable I2C watchdog timer + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_i2c_wdt_en(uint8_t *enable, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiI2C + * \page bmi2_api_bmi2_set_i2c_wdt_sel bmi2_set_i2c_wdt_sel + * \code + * int8_t bmi2_set_i2c_wdt_sel(uint8_t watchdog_select, struct bmi2_dev *dev); + * \endcode + * @details This API sets i2c watchdog timer . + * + * @param[in] watchdog_select : To set watchdog timer for i2c. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Watchdog_select | Description + * ----------------------|--------------- + * BMI2_DISABLE | I2C watchdog timeout after 1.25ms + * BMI2_ENABLE | I2c watchdog timeout after 40ms + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_i2c_wdt_sel(uint8_t watchdog_select, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiI2C + * \page bmi2_api_bmi2_get_i2c_wdt_sel bmi2_get_i2c_wdt_sel + * \code + * int8_t bmi2_get_i2c_wdt_sel(uint8_t *watchdog_select, struct bmi2_dev *dev); + * \endcode + * @details This API sets i2c watchdog timer . + * + * @param[in] watchdog_select : Pointer to get watchdog timer for i2c. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Watchdog_select | Description + * --------------------- |--------------- + * BMI2_DISABLE | I2C watchdog timeout after 1.25ms + * BMI2_ENABLE | I2c watchdog timeout after 40ms + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_i2c_wdt_sel(uint8_t *watchdog_select, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiRegs Registers + * @brief Set / Get data from the given register address of the sensor + */ + +/*! + * \ingroup bmi2ApiRegs + * \page bmi2_api_bmi2_get_regs bmi2_get_regs + * \code + * int8_t bmi2_get_regs(uint8_t reg_addr, uint8_t *data, uint16_t len, const struct bmi2_dev *dev); + * \endcode + * @details This API reads the data from the given register address of bmi2 + * sensor. + * + * @param[in] reg_addr : Register address from which data is read. + * @param[out] data : Pointer to data buffer where read data is stored. + * @param[in] len : No. of bytes of data to be read. + * @param[in] dev : Structure instance of bmi2_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 - 0x26, 0x5E. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_regs(uint8_t reg_addr, uint8_t *data, uint16_t len, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiRegs + * \page bmi2_api_bmi2_set_regs bmi2_set_regs + * \code + * int8_t bmi2_set_regs(uint8_t reg_addr, const uint8_t *data, uint16_t len, struct bmi2_dev *dev); + * \endcode + * @details This API writes data to the given register address of bmi2 sensor. + * + * @param[in] reg_addr : Register address to which the data is written. + * @param[in] data : Pointer to data buffer in which data to be written + * is stored. + * @param[in] len : No. of bytes of data to be written. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_regs(uint8_t reg_addr, const uint8_t *data, uint16_t len, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiSR Soft reset + * @brief Set / Get data from the given register address of the sensor + */ + +/*! + * \ingroup bmi2ApiSR + * \page bmi2_api_bmi2_soft_reset bmi2_soft_reset + * \code + * int8_t bmi2_soft_reset(struct bmi2_dev *dev); + * \endcode + * @details This API resets bmi2 sensor. All registers are overwritten with + * their default values. + * + * @note If selected interface is SPI, an extra dummy byte is read to bring the + * interface back to SPI from default, after the soft reset command. + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_soft_reset(struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiConfig Configuration + * @brief Functions related to configuration of the sensor + */ + +/*! + * \ingroup bmi2ApiConfig + * \page bmi2_api_bmi2_get_config_file_version bmi2_get_config_file_version + * \code + * int8_t bmi2_get_config_file_version(uint8_t *config_major, uint8_t *config_minor, struct bmi2_dev *dev); + * \endcode + * @details This API is used to get the config file major and minor information. + * + * @param[in] dev : Structure instance of bmi2_dev. + * @param[out] config_major : pointer to data buffer to store the config major. + * @param[out] config_minor : pointer to data buffer to store the config minor. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_config_file_version(uint8_t *config_major, uint8_t *config_minor, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiPowersave Advanced power save mode + * @brief Set / Get Advanced power save mode of the sensor + */ + +/*! + * \ingroup bmi2ApiPowersave + * \page bmi2_api_bmi2_set_adv_power_save bmi2_set_adv_power_save + * \code + * int8_t bmi2_set_adv_power_save(uint8_t enable, struct bmi2_dev *dev); + * \endcode + * @details This API enables/disables the advance power save mode in the sensor. + * + * @param[in] enable : To enable/disable advance power mode. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Disables advance power save. + * BMI2_ENABLE | Enables advance power save. + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_adv_power_save(uint8_t enable, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiPowersave + * \page bmi2_api_bmi2_get_adv_power_save bmi2_get_adv_power_save + * \code + * int8_t bmi2_get_adv_power_save(uint8_t *aps_status, struct bmi2_dev *dev); + * \endcode + * @details This API gets the status of advance power save mode in the sensor. + * + * @param[out] aps_status : Pointer to get the status of APS mode. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * aps_status | Description + * -------------|--------------- + * BMI2_DISABLE | Advance power save disabled. + * BMI2_ENABLE | Advance power save enabled. + *@endverbatim + * + * @return Result of API execution status + * + * @retval BMI2_OK - Success. + * @retval BMI2_E_NULL_PTR - Error: Null pointer error + * @retval BMI2_E_COM_FAIL - Error: Communication fail + * @retval BMI2_E_SET_APS_FAIL - Error: Set Advance Power Save Fail + */ +int8_t bmi2_get_adv_power_save(uint8_t *aps_status, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiPowersave + * \page bmi2_api_bmi2_set_fast_power_up bmi2_set_fast_power_up + * \code + * int8_t bmi2_set_fast_power_up(uint8_t fast_power_up, struct bmi2_dev *dev); + * \endcode + * @details This API enables/disables the fast power up mode in the sensor. + * + * @param[in] fast_power_up : To enable/disable advance power mode. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Fast_power_up | Description + * --------------------|--------------- + * BMI2_DISABLE | Disables fast power up. + * BMI2_ENABLE | Enables fast power up. + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_fast_power_up(uint8_t fast_power_up, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiPowersave + * \page bmi2_api_bmi2_get_fast_power_up bmi2_get_fast_power_up + * \code + * int8_t bmi2_get_fast_power_up(uint8_t *fast_power_up, struct bmi2_dev *dev); + * \endcode + * @details This API gets the enable/disable status of the fast power up mode in the sensor. + * + * @param[in] fast_power_up : Pointer to get the enable/disable advance power mode. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Fast power up | Description + * --------------------|--------------- + * BMI2_DISABLE | Disables fast power up. + * BMI2_ENABLE | Enables fast power up. + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_fast_power_up(uint8_t *fast_power_up, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiConfig + * \page bmi2_api_bmi2_write_config_file bmi2_write_config_file + * \code + * int8_t bmi2_write_config_file(struct bmi2_dev *dev); + * \endcode + * @details This API loads the configuration file to the bmi2 sensor. + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_write_config_file(struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiInt Interrupt + * @brief Interrupt operations of the sensor + */ + +/*! + * \ingroup bmi2ApiInt + * \page bmi2_api_bmi2_set_int_pin_config bmi2_set_int_pin_config + * \code + * int8_t bmi2_set_int_pin_config(const struct bmi2_int_pin_config *int_cfg, struct bmi2_dev *dev); + * \endcode + * @details This API sets: + * 1) The input output configuration of the selected interrupt pin: + * INT1 or INT2. + * 2) The interrupt mode: permanently latched or non-latched. + * + * @param[in] int_cfg : Structure instance of bmi2_int_pin_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_int_pin_config(const struct bmi2_int_pin_config *int_cfg, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiInt + * \page bmi2_api_bmi2_get_int_pin_config bmi2_get_int_pin_config + * \code + * int8_t bmi2_get_int_pin_config(struct bmi2_int_pin_config *int_cfg, const struct bmi2_dev *dev); + * \endcode + * @details This API gets: + * 1) The input output configuration of the selected interrupt pin: + * INT1 or INT2. + * 2) The interrupt mode: permanently latched or non-latched. + * + * @param[in,out] int_cfg : Structure instance of bmi2_int_pin_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_int_pin_config(struct bmi2_int_pin_config *int_cfg, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiInt + * \page bmi2_api_bmi2_get_int_status bmi2_get_int_status + * \code + * int8_t bmi2_get_int_status(uint16_t *int_status, const struct bmi2_dev *dev); + * \endcode + * @details This API gets the interrupt status of both feature and data + * interrupts. + * + * @param[out] int_status : Pointer to get the status of the interrupts. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * int_status | Status + * -----------|------------ + * 0x00 | BIT0 + * 0x01 | BIT1 + * 0x02 | BIT2 + * 0x03 | BIT3 + * 0x04 | BIT4 + * 0x05 | BIT5 + * 0x06 | BIT6 + * 0x07 | BIT7 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_int_status(uint16_t *int_status, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiSensorC Sensor Configuration + * @brief Enable / Disable feature configuration of the sensor + */ + +/*! + * \ingroup bmi2ApiSensorC + * \page bmi2_api_bmi2_set_sensor_config bmi2_set_sensor_config + * \code + * int8_t bmi2_set_sensor_config(struct bmi2_sens_config *sens_cfg, uint8_t n_sens, struct bmi2_dev *dev); + * \endcode + * @details This API sets the sensor/feature configuration. + * + * @param[in] sens_cfg : Structure instance of bmi2_sens_config. + * @param[in] n_sens : Number of sensors selected. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @note Sensors/features that can be configured + * + *@verbatim + * sens_list | Values + * ----------------------------|----------- + * BMI2_ACCEL | 0 + * BMI2_GYRO | 1 + * BMI2_AUX | 2 + * BMI2_GYRO_GAIN_UPDATE | 9 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_sensor_config(struct bmi2_sens_config *sens_cfg, uint8_t n_sens, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiSensorC + * \page bmi2_api_bmi2_get_sensor_config bmi2_get_sensor_config + * \code + * int8_t bmi2_get_sensor_config(struct bmi2_sens_config *sens_cfg, uint8_t n_sens, struct bmi2_dev *dev); + * \endcode + * @details This API gets the sensor/feature configuration. + * + * @param[in] sens_cfg : Structure instance of bmi2_sens_config. + * @param[in] n_sens : Number of sensors selected. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @note Sensors/features whose configurations can be read. + * + *@verbatim + * sens_list | Values + * -------------------------|----------- + * BMI2_ACCEL | 0 + * BMI2_GYRO | 1 + * BMI2_AUX | 2 + * BMI2_GYRO_GAIN_UPDATE | 9 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_sensor_config(struct bmi2_sens_config *sens_cfg, uint8_t n_sens, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiSensor Feature Set + * @brief Enable / Disable features of the sensor + */ + +/*! + * \ingroup bmi2ApiSensor + * \page bmi2_api_bmi2_sensor_enable bmi2_sensor_enable + * \code + * int8_t bmi2_sensor_enable(const uint8_t *sens_list, uint8_t n_sens, struct bmi2_dev *dev); + * \endcode + * @details This API selects the sensors/features to be enabled. + * + * @param[in] sens_list : Pointer to select the sensor/feature. + * @param[in] n_sens : Number of sensors selected. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @note Sensors/features that can be enabled. + * + *@verbatim + * sens_list | Values + * -------------------------|----------- + * BMI2_ACCEL | 0 + * BMI2_GYRO | 1 + * BMI2_AUX | 2 + * BMI2_TEMP | 31 + *@endverbatim + * + * @note : + * example uint8_t sens_list[2] = {BMI2_ACCEL, BMI2_GYRO}; + * uint8_t n_sens = 2; + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_sensor_enable(const uint8_t *sens_list, uint8_t n_sens, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiSensor + * \page bmi2_api_bmi2_sensor_disable bmi2_sensor_disable + * \code + * int8_t bmi2_sensor_disable(const uint8_t *sens_list, uint8_t n_sens, struct bmi2_dev *dev); + * \endcode + * @details This API selects the sensors/features to be disabled. + * + * @param[in] sens_list : Pointer to select the sensor/feature. + * @param[in] n_sens : Number of sensors selected. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @note Sensors/features that can be disabled. + * + *@verbatim + * sens_list | Values + * ----------------------------|----------- + * BMI2_ACCEL | 0 + * BMI2_GYRO | 1 + * BMI2_AUX | 2 + * BMI2_TEMP | 31 + *@endverbatim + * + * @note : + * example uint8_t sens_list[2] = {BMI2_ACCEL, BMI2_GYRO}; + * uint8_t n_sens = 2; + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_sensor_disable(const uint8_t *sens_list, uint8_t n_sens, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiSensorD Sensor Data + * @brief Get sensor data + */ + +/*! + * \ingroup bmi2ApiSensorD + * \page bmi2_api_bmi2_get_feature_data bmi2_get_feature_data + * \code + * int8_t bmi2_get_feature_data(struct bmi2_feat_sensor_data *feat_sensor_data, uint8_t n_sens, struct bmi2_dev *dev); + * \endcode + * @details This API gets the feature data for gyroscope user-gain update and gyroscope cross sensitivity + * + * @param[out] feat_sensor_data : Structure instance of bmi2_feat_sensor_data. + * @param[in] n_sens : Number of sensors selected. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @note Sensors/features whose data can be read + * + *@verbatim + * sens_list | Values + * ---------------------|----------- + * BMI2_GYRO_GAIN_UPDATE| 12 + * BMI2_GYRO_CROSS_SENSE| 28 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_feature_data(struct bmi2_feat_sensor_data *feat_sensor_data, uint8_t n_sens, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiSensorD + * \page bmi2_api_bmi2_get_sensor_data bmi2_get_sensor_data + * \code + * int8_t bmi2_get_sensor_data(struct bmi2_sens_data *data, struct bmi2_dev *dev); + * \endcode + * @details This API gets the sensor data for accelerometer, gyroscope and auxiliary sensor + * + * @param[out] data : Structure instance of bmi2_sens_data. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_sensor_data(struct bmi2_sens_data *data, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiTemperature Temperature + * @brief Read temperature of the sensor. + */ + +/*! + * \ingroup bmi2ApiTemperature + * \page bmi2_api_bmi2_get_temperature_data bmi2_get_temperature_data + * \code + * int8_t bmi2_get_temperature_data(uint16_t *temp_data, struct bmi2_dev *dev); + * \endcode + * @details This API reads the raw temperature data from the register and can be + * converted into degree celsius using the below formula. + * Formula: temperature_value = (float)(((float)((int16_t)temperature_data)) / 512.0) + 23.0 + * @note Enable gyro to read temperature + * + * @param[out] temp_data : Pointer variable which stores the raw temperature value. + * @param[in] dev : Structure instance of bmi2_dev. + * + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_temperature_data(int16_t *temp_data, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiSensorD + * \page bmi2_api_bmi2_parse_sensor_data bmi2_parse_sensor_data + * \code + * int8_t bmi2_parse_sensor_data(const uint8_t *sensor_data, struct bmi2_sens_data *data, const struct bmi2_dev *dev); + * \endcode + * @details This API parses the sensor data for accelerometer, gyroscope and auxiliary sensor + * + * @param[in] sensor_data : Array of register data (24 bytes from Register 0x03 to 0x1A) + * @param[out] data : Structure instance of bmi2_sens_data. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_parse_sensor_data(const uint8_t *sensor_data, struct bmi2_sens_data *data, const struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiFIFO FIFO + * @brief FIFO operations of the sensor + */ + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_set_fifo_config bmi2_set_fifo_config + * \code + * int8_t bmi2_set_fifo_config(uint16_t config, uint8_t enable, struct bmi2_dev *dev); + * \endcode + * @details This API sets the FIFO configuration in the sensor. + * + * @param[in] config : FIFO configurations to be enabled/disabled. + * @param[in] enable : Enable/Disable FIFO configurations. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * enable | Description + * -------------|--------------- + * BMI2_DISABLE | Disables FIFO configuration. + * BMI2_ENABLE | Enables FIFO configuration. + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_fifo_config(uint16_t config, uint8_t enable, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_get_fifo_config bmi2_get_fifo_config + * \code + * int8_t bmi2_get_fifo_config(uint16_t *fifo_config, const struct bmi2_dev *dev); + * \endcode + * @details This API gets the FIFO configuration from the sensor. + * + * @param[out] fifo_config : Pointer variable to get FIFO configuration value. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_fifo_config(uint16_t *fifo_config, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_read_fifo_data bmi2_read_fifo_data + * \code + * int8_t bmi2_read_fifo_data(struct bmi2_fifo_frame *fifo, const struct bmi2_dev *dev); + * \endcode + * @details This API reads FIFO data. + * + * @param[in, out] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @note APS has to be disabled before calling this function. + * @note Dummy byte (for SPI Interface) required for FIFO data read + * must be given as part of data pointer in struct bmi2_fifo_frame + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_read_fifo_data(struct bmi2_fifo_frame *fifo, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_extract_accel bmi2_extract_accel + * \code + * int8_t bmi2_extract_accel(struct bmi2_sens_axes_data *accel_data, + * uint16_t *accel_length, + * struct bmi2_fifo_frame *fifo, + * const struct bmi2_dev *dev); + * \endcode + * @details This API parses and extracts the accelerometer frames from FIFO data read by + * the "bmi2_read_fifo_data" API and stores it in the "accel_data" structure + * instance. + * + * @param[out] accel_data : Structure instance of bmi2_sens_axes_data + * where the parsed data bytes are stored. + * @param[in,out] accel_length : Number of accelerometer frames. + * @param[in,out] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_extract_accel(struct bmi2_sens_axes_data *accel_data, + uint16_t *accel_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_extract_aux bmi2_extract_aux + * \code + * int8_t bmi2_extract_aux(struct bmi2_aux_fifo_data *aux, + * uint16_t *aux_length, + * struct bmi2_fifo_frame *fifo, + * const struct bmi2_dev *dev); + * + * \endcode + * @details This API parses and extracts the auxiliary frames from FIFO data + * read by the "bmi2_read_fifo_data" API and stores it in "aux_data" buffer. + * + * @param[out] aux : Pointer to structure where the parsed auxiliary + * data bytes are stored. + * @param[in,out] aux_length : Number of auxiliary frames. + * @param[in,out] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_extract_aux(struct bmi2_aux_fifo_data *aux, + uint16_t *aux_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_extract_gyro bmi2_extract_gyro + * \code + * int8_t bmi2_extract_gyro(struct bmi2_sens_axes_data *gyro_data, + * uint16_t *gyro_length, + * struct bmi2_fifo_frame *fifo, + * const struct bmi2_dev *dev); + * \endcode + * @details This API parses and extracts the gyroscope frames from FIFO data read by the + * "bmi2_read_fifo_data" API and stores it in the "gyro_data" + * structure instance. + * + * @param[out] gyro_data : Structure instance of bmi2_sens_axes_data + * where the parsed data bytes are stored. + * @param[in,out] gyro_length : Number of gyroscope frames. + * @param[in,out] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_extract_gyro(struct bmi2_sens_axes_data *gyro_data, + uint16_t *gyro_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiCmd Command Register + * @brief Write commands to the sensor + */ + +/*! + * \ingroup bmi2ApiCmd + * \page bmi2_api_bmi2_set_command_register bmi2_set_command_register + * \code + * int8_t bmi2_set_command_register(uint8_t command, struct bmi2_dev *dev); + * \endcode + * @details This API writes the available sensor specific commands to the sensor. + * + * @param[in] command : Commands to be given to the sensor. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Commands | Values + * ---------------------|--------------------- + * BMI2_SOFT_RESET_CMD | 0xB6 + * BMI2_FIFO_FLUSH_CMD | 0xB0 + * BMI2_USR_GAIN_CMD | 0x03 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_command_register(uint8_t command, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_set_fifo_self_wake_up bmi2_set_fifo_self_wake_up + * \code + * int8_t bmi2_set_fifo_self_wake_up(uint8_t fifo_self_wake_up, struct bmi2_dev *dev); + * \endcode + * @details This API sets the FIFO self wake up functionality in the sensor. + * + * @param[in] fifo_self_wake_up : Variable to set FIFO self wake-up. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * fifo_self_wake_up | Description + * -------------------|--------------- + * BMI2_DISABLE | Disables self wake-up. + * BMI2_ENABLE | Enables self wake-up. + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_fifo_self_wake_up(uint8_t fifo_self_wake_up, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_get_fifo_self_wake_up bmi2_get_fifo_self_wake_up + * \code + * int8_t bmi2_get_fifo_self_wake_up(uint8_t *fifo_self_wake_up, const struct bmi2_dev *dev); + * \endcode + * @details This API gets the FIFO self wake up functionality from the sensor. + * + * @param[out] fifo_self_wake_up : Pointer variable to get the status of FIFO + * self wake-up. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * fifo_self_wake_up | Description + * -------------------|--------------- + * BMI2_DISABLE | Self wake-up disabled + * BMI2_ENABLE | Self wake-up enabled. + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_fifo_self_wake_up(uint8_t *fifo_self_wake_up, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_set_fifo_wm bmi2_set_fifo_wm + * \code + * int8_t bmi2_set_fifo_wm(uint16_t fifo_wm, struct bmi2_dev *dev); + * \endcode + * @details This API sets the FIFO water mark level which is set in the sensor. + * + * @param[in] fifo_wm : Variable to set FIFO water-mark level. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_fifo_wm(uint16_t fifo_wm, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_get_fifo_wm bmi2_get_fifo_wm + * \code + * int8_t bmi2_get_fifo_wm(uint16_t *fifo_wm, const struct bmi2_dev *dev); + * \endcode + * @details This API gets the FIFO water mark level which is set in the sensor. + * + * @param[out] fifo_wm : Pointer variable to store FIFO water-mark level. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_fifo_wm(uint16_t *fifo_wm, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_set_fifo_filter_data bmi2_set_fifo_filter_data + * \code + * int8_t bmi2_set_fifo_filter_data(uint8_t sens_sel, uint8_t fifo_filter_data, struct bmi2_dev *dev); + * \endcode + * @details This API sets either filtered or un-filtered FIFO accelerometer or + * gyroscope data. + * + * @param[in] sens_sel : Selects either accelerometer or + * gyroscope sensor. + * @param[in] fifo_filter_data : Variable to set the filter data. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * sens_sel | values + * -----------------|---------- + * BMI2_ACCEL | 0x01 + * BMI2_GYRO | 0x02 + *@endverbatim + * + *@verbatim + * Value | fifo_filter_data + * ---------|--------------------- + * 0x00 | Un-filtered data + * 0x01 | Filtered data + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_fifo_filter_data(uint8_t sens_sel, uint8_t fifo_filter_data, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_get_fifo_filter_data bmi2_get_fifo_filter_data + * \code + * int8_t bmi2_get_fifo_filter_data(uint8_t sens_sel, uint8_t *fifo_filter_data, const struct bmi2_dev *dev); + * \endcode + * @details This API gets the FIFO accelerometer or gyroscope filter data. + * + * @param[in] sens_sel : Selects either accelerometer or + * gyroscope sensor. + * @param[out] fifo_filter_data : Pointer variable to get the filter data. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * sens_sel | values + * -----------------|---------- + * BMI2_ACCEL | 0x01 + * BMI2_GYRO | 0x02 + *@endverbatim + * + *@verbatim + * Value | fifo_filter_data + * ---------|--------------------- + * 0x00 | Un-filtered data + * 0x01 | Filtered data + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_fifo_filter_data(uint8_t sens_sel, uint8_t *fifo_filter_data, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_set_fifo_down_sample bmi2_set_fifo_down_sample + * \code + * int8_t bmi2_set_fifo_down_sample(uint8_t sens_sel, uint8_t fifo_down_samp, struct bmi2_dev *dev); + * \endcode + * @details This API sets the down sampling rate for FIFO accelerometer or + * gyroscope FIFO data. + * + * @param[in] sens_sel : Selects either either accelerometer or + * gyroscope sensor. + * @param[in] fifo_down_samp : Variable to set the down sampling rate. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * sens_sel | values + * ----------------|---------- + * BMI2_ACCEL | 0x01 + * BMI2_GYRO | 0x02 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_fifo_down_sample(uint8_t sens_sel, uint8_t fifo_down_samp, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_get_fifo_down_sample bmi2_get_fifo_down_sample + * \code + * int8_t bmi2_get_fifo_down_sample(uint8_t sens_sel, uint8_t *fifo_down_samp, const struct bmi2_dev *dev); + * \endcode + * @details This API gets the down sampling rate, configured for FIFO + * accelerometer or gyroscope data. + * + * @param[in] sens_sel : Selects either either accelerometer or + * gyroscope sensor. + * @param[out] fifo_down_samp : Pointer variable to store the down sampling rate + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * sens_sel | values + * ----------------|---------- + * BMI2_ACCEL | 0x01 + * BMI2_GYRO | 0x02 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_fifo_down_sample(uint8_t sens_sel, uint8_t *fifo_down_samp, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_get_fifo_length bmi2_get_fifo_length + * \code + * int8_t bmi2_get_fifo_length(uint16_t *fifo_length, const struct bmi2_dev *dev); + * \endcode + * @details This API gets the length of FIFO data available in the sensor in + * bytes. + * + * @param[out] fifo_length : Pointer variable to store the value of FIFO byte + * counter. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @note The byte counter is updated each time a complete frame is read or + * written. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_fifo_length(uint16_t *fifo_length, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFIFO + * \page bmi2_api_bmi2_get_saturation_status bmi2_get_saturation_status + * \code + * int8_t bmi2_get_saturation_status(uint8_t *status, struct bmi2_dev *dev); + * \endcode + * @details This API reads the saturation status of the sensor. + * + * @param[in] status : Pointer to read the status of saturation. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Macro | Status + * -----------------------------|--------------- + * BMI2_SATURATION_ACC_X_MASK | 0X01 + * BMI2_SATURATION_ACC_Y_MASK | 0X02 + * BMI2_SATURATION_ACC_Z_MASK | 0X04 + * BMI2_SATURATION_GYR_X_MASK | 0X08 + * BMI2_SATURATION_GYR_Y_MASK | 0X10 + * BMI2_SATURATION_GYR_Z_MASK | 0X20 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_saturation_status(uint8_t *status, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiOIS OIS + * @brief OIS operations of the sensor + */ + +/*! + * \ingroup bmi2ApiOIS + * \page bmi2_api_bmi2_set_ois_interface bmi2_set_ois_interface + * \code + * int8_t bmi2_set_ois_interface(uint8_t enable, struct bmi2_dev *dev); + * \endcode + * @details This API enables/disables OIS interface. + * + * @param[in] enable : To enable/disable OIS interface. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Disables OIS interface. + * BMI2_ENABLE | Enables OIS interface. + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_ois_interface(uint8_t enable, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiOIS + * \page bmi2_api_bmi2_get_spi3_ois_mode bmi2_get_spi3_ois_mode + * \code + * int8_t bmi2_get_spi3_ois_mode(uint8_t *enable, struct bmi2_dev *dev); + * \endcode + * @details This API gets the status of SPI OIS interface. + * + * @param[in] enable : Pointer to read SPI OIS 3/4 wire interface. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Enable SPI OIS 4 wire mode + * BMI2_ENABLE | Enable SPI OIS 3 wire mode + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_spi3_ois_mode(uint8_t *enable, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApisensorC + * \page bmi2_api_bmi2_set_drv_reg bmi2_set_drv_reg + * \code + * int8_t bmi2_set_drv_reg(uint8_t drv_reg, struct bmi2_dev *dev); + * \endcode + * @details This API sets the drive strength of the sensor + * + * @param[in] drv_reg : To set the drive strength of the sensor. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_drv_reg(uint8_t drv_reg, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApisensorC + * \page bmi2_api_bmi2_get_drv_reg bmi2_get_drv_reg + * \code + * int8_t bmi2_get_drv_reg(uint8_t *drv_reg, struct bmi2_dev *dev); + * \endcode + * @details This API gets the drive strength of the sensor + * + * @param[in] drv_reg : Pointer to get the drive strength of the sensor. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_drv_reg(uint8_t *drv_reg, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiOIS + * \page bmi2_api_bmi2_set_spi3_ois_mode bmi2_set_spi3_ois_mode + * \code + * int8_t bmi2_set_spi3_ois_mode(uint8_t enable, struct bmi2_dev *dev); + * \endcode + * @details This API sets the status of SPI OIS interface. + * + * @param[in] enable : To enable/disable SPI OIS 3/4 wire interface. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Enable SPI OIS 4 wire mode + * BMI2_ENABLE | Enable SPI OIS 3 wire mode + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_spi3_ois_mode(uint8_t enable, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiAux Auxiliary sensor + * @brief Auxiliary sensor operations of the sensor + */ + +/*! + * \ingroup bmi2ApiAux + * \page bmi2_api_bmi2_read_aux_man_mode bmi2_read_aux_man_mode + * \code + * int8_t bmi2_read_aux_man_mode(uint8_t reg_addr, uint8_t *aux_data, uint16_t len, struct bmi2_dev *dev); + * \endcode + * @details This API reads the user-defined bytes of data from the given register + * address of auxiliary sensor in manual mode. + * + * @param[in] reg_addr : Address from where data is read. + * @param[out] aux_data : Pointer to the stored buffer. + * @param[in] len : Total length of data to be read. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @note Change of BMI2_AUX_RD_ADDR is only allowed if AUX is not busy. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_read_aux_man_mode(uint8_t reg_addr, uint8_t *aux_data, uint16_t len, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiAux + * \page bmi2_api_bmi2_write_aux_man_mode bmi2_write_aux_man_mode + * \code + * int8_t bmi2_write_aux_man_mode(uint8_t reg_addr, const uint8_t *aux_data, uint16_t len, struct bmi2_dev *dev); + * \endcode + * @details This API writes the user-defined bytes of data and the address of + * auxiliary sensor where data is to be written in manual mode. + * + * @param[in] reg_addr : AUX address where data is to be written. + * @param[in] aux_data : Pointer to data to be written. + * @param[in] len : Total length of data to be written. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @note Change of BMI2_AUX_WR_ADDR is only allowed if AUX is not busy. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_write_aux_man_mode(uint8_t reg_addr, const uint8_t *aux_data, uint16_t len, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiStatus Sensor Status + * @brief Get sensor status + */ + +/*! + * \ingroup bmi2ApiStatus + * \page bmi2_api_bmi2_get_status bmi2_get_status + * \code + * int8_t bmi2_get_status(uint8_t *status, const struct bmi2_dev *dev); + * \endcode + * @details This API gets the data ready status of accelerometer, gyroscope, + * auxiliary, ready status of command decoder and busy status of auxiliary. + * + * @param[out] status : Pointer variable to the status. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Value | Status + * ---------|--------------------- + * 0x80 | DRDY_ACC + * 0x40 | DRDY_GYR + * 0x20 | DRDY_AUX + * 0x10 | CMD_RDY + * 0x04 | AUX_BUSY + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_status(uint8_t *status, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiWSync Sync commands + * @brief Write sync commands + */ + +/*! + * \ingroup bmi2ApiWSync + * \page bmi2_api_bmi2_write_sync_commands bmi2_write_sync_commands + * \code + * int8_t bmi2_write_sync_commands(const uint8_t *command, uint8_t n_comm, struct bmi2_dev *dev); + * \endcode + * @details This API can be used to write sync commands like ODR, sync period, + * frequency and phase, resolution ratio, sync time and delay time. + * + * @param[in] command : Sync command to be written. + * @param[in] n_comm : Length of the command. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_write_sync_commands(const uint8_t *command, uint8_t n_comm, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiASelftest Accel self test + * @brief Perform accel self test + */ + +/*! + * \ingroup bmi2ApiASelftest + * \page bmi2_api_bmi2_perform_accel_self_test bmi2_perform_accel_self_test + * \code + * int8_t bmi2_perform_accel_self_test(struct bmi2_dev *dev); + * \endcode + * @details This API performs self-test to check the proper functionality of the + * accelerometer sensor. + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_perform_accel_self_test(struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiInt + * \page bmi2_api_bmi2_map_feat_int bmi2_map_feat_int + * \code + * int8_t bmi2_map_feat_int(const struct bmi2_sens_int_config *sens_int, struct bmi2_dev *dev); + * \endcode + * @details This API maps/unmaps feature interrupts to that of interrupt pins. + * + * @param[in] sens_int : Structure instance of bmi2_sens_int_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_map_feat_int(uint8_t type, enum bmi2_hw_int_pin hw_int_pin, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiInt + * \page bmi2_api_bmi2_map_data_int bmi2_map_data_int + * \code + * int8_t bmi2_map_data_int(uint8_t data_int, enum bmi2_hw_int_pin int_pin, struct bmi2_dev *dev); + * \endcode + * @details This API maps/un-maps data interrupts to that of interrupt pins. + * + * @param[in] int_pin : Interrupt pin selected. + * @param[in] data_int : Type of data interrupt to be mapped. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * data_int | Mask values + * ---------------------|--------------------- + * BMI2_FFULL_INT | 0x01 + * BMI2_FWM_INT | 0x02 + * BMI2_DRDY_INT | 0x04 + * BMI2_ERR_INT | 0x08 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_map_data_int(uint8_t data_int, enum bmi2_hw_int_pin int_pin, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiRemap Remap Axes + * @brief Set / Get remap axes values from the sensor + */ + +/*! + * \ingroup bmi2ApiRemap + * \page bmi2_api_bmi2_get_remap_axes bmi2_get_remap_axes + * \code + * int8_t bmi2_get_remap_axes(struct bmi2_remap *remapped_axis, struct bmi2_dev *dev); + * \endcode + * @details This API gets the re-mapped x, y and z axes from the sensor and + * updates the values in the device structure. + * + * @param[out] remapped_axis : Structure that stores re-mapped axes. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_remap_axes(struct bmi2_remap *remapped_axis, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiRemap + * \page bmi2_api_bmi2_set_remap_axes bmi2_set_remap_axes + * \code + * int8_t bmi2_set_remap_axes(const struct bmi2_remap *remapped_axis, struct bmi2_dev *dev); + * \endcode + * @details This API sets the re-mapped x, y and z axes to the sensor and + * updates them in the device structure. + * + * @param[in] remapped_axis : Structure that stores re-mapped axes. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_remap_axes(const struct bmi2_remap *remapped_axis, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiGyroOC Gyro Offset Compensation + * @brief Gyro Offset Compensation operations of the sensor + */ + +/*! + * \ingroup bmi2ApiGyroOC + * \page bmi2_api_bmi2_set_gyro_offset_comp bmi2_set_gyro_offset_comp + * \code + * int8_t bmi2_set_gyro_offset_comp(uint8_t enable, struct bmi2_dev *dev); + * \endcode + * @details This API enables/disables gyroscope offset compensation. It adds the + * offsets defined in the offset register with gyroscope data. + * + * @param[in] enable : Enables/Disables gyroscope offset compensation. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * enable | Description + * -------------|--------------- + * BMI2_ENABLE | Enables gyroscope offset compensation. + * BMI2_DISABLE | Disables gyroscope offset compensation. + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_gyro_offset_comp(uint8_t enable, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiGyroOC + * \page bmi2_api_bmi2_set_gyro_offset_comp bmi2_get_gyro_offset_comp + * \code + * int8_t bmi2_get_gyro_offset_comp(uint8_t *offset, struct bmi2_dev *dev); + * \endcode + * @details This API reads the gyroscope offset compensation. + * + * @param[in, out] offset : The value of the offset compensation. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * enable | Description + * -------------|--------------- + * BMI2_ENABLE | Enables gyroscope offset compensation. + * BMI2_DISABLE | Disables gyroscope offset compensation. + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_gyro_offset_comp(uint8_t *offset, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiGyroOC + * \page bmi2_api_bmi2_set_gyro_offset_comp bmi2_set_gyro_gain + * \code + * int8_t bmi2_set_gyro_gain(uint8_t gyro_gain, struct bmi2_dev *dev); + * \endcode + * @details This API enables/disables gyroscope gain for Sensitivity Error Compensation. + * + * @param[in] gyro_gain : Enables/Disables gyroscope gain. + * @param[in] dev : Structure instance of bmi2_dev. + * + *@verbatim + * enable | Description + * -------------|--------------- + * BMI2_ENABLE | Enables gyroscope gain. + * BMI2_DISABLE | Disables gyroscope gain. + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_gyro_gain(uint8_t gyro_gain, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiGyroOC + * \page bmi2_api_bmi2_set_gyro_offset_comp bmi2_get_gyro_gain + * \code + * int8_t bmi2_get_gyro_gain(uint8_t *gyro_gain, struct bmi2_dev *dev); + * \endcode + * @details This API reads the Gyro gain. + * + * @param[in, out] gyro_gain : The value of the Gyro gain. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_gyro_gain(uint8_t *gyro_gain, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiGyroOC + * \page bmi2_api_bmi2_read_gyro_offset_comp_axes bmi2_read_gyro_offset_comp_axes + * \code + * int8_t bmi2_read_gyro_offset_comp_axes(struct bmi2_sens_axes_data *gyr_off_comp_axes, const struct bmi2_dev *dev); + * \endcode + * @details This API reads the gyroscope bias values for each axis which is used + * for gyroscope offset compensation. + * + * @param[out] gyr_off_comp_axes: Structure to store gyroscope offset + * compensated values. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_read_gyro_offset_comp_axes(struct bmi2_sens_axes_data *gyr_off_comp_axes, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiGyroOC + * \page bmi2_api_bmi2_write_gyro_offset_comp_axes bmi2_write_gyro_offset_comp_axes + * \code + * int8_t bmi2_write_gyro_offset_comp_axes(const struct bmi2_sens_axes_data *gyr_off_comp_axes, struct bmi2_dev *dev); + * \endcode + * @details This API writes the gyroscope bias values for each axis which is used + * for gyroscope offset compensation. + * + * @param[in] gyr_off_comp_axes : Structure to store gyroscope offset + * compensated values. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_write_gyro_offset_comp_axes(const struct bmi2_sens_axes_data *gyr_off_comp_axes, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiGyroCS Gyro cross sensitivity + * @brief Gyro Cross sensitivity operation + */ + +/*! + * \ingroup bmi2ApiGyroCS + * \page bmi2_api_bmi2_get_gyro_cross_sense bmi2_get_gyro_cross_sense + * \code + * int8_t bmi2_get_gyro_cross_sense(struct bmi2_dev *dev); + * \endcode + * @details This API updates the cross sensitivity coefficient between gyroscope's + * X and Z axes. + * + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_gyro_cross_sense(struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiInts Internal Status + * @brief Get Internal Status of the sensor + */ + +/*! + * \ingroup bmi2ApiInts + * \page bmi2_api_bmi2_get_internal_status bmi2_get_internal_status + * \code + * int8_t bmi2_get_internal_status(uint8_t *int_stat, const struct bmi2_dev *dev); + * \endcode + * @details This API gets error bits and message indicating internal status. + * + * @param[in] dev : Structure instance of bmi2_dev. + * @param[out] int_stat : Pointer variable to store error bits and + * message. + * + *@verbatim + * Internal status | *int_stat + * ---------------------|--------------------- + * BMI2_NOT_INIT | 0x00 + * BMI2_INIT_OK | 0x01 + * BMI2_INIT_ERR | 0x02 + * BMI2_DRV_ERR | 0x03 + * BMI2_SNS_STOP | 0x04 + * BMI2_NVM_ERROR | 0x05 + * BMI2_START_UP_ERROR | 0x06 + * BMI2_COMPAT_ERROR | 0x07 + * BMI2_VFM_SKIPPED | 0x10 + * BMI2_AXES_MAP_ERROR | 0x20 + * BMI2_ODR_50_HZ_ERROR | 0x40 + * BMI2_ODR_HIGH_ERROR | 0x80 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_internal_status(uint8_t *int_stat, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiInts + * \page bmi2_api_bmi2_get_internal_error_status bmi2_get_internal_error_status + * \code + * int8_t bmi2_get_internal_error_status(uint8_t *status, struct bmi2_dev *dev); + * \endcode + * @details This API gets Interanl error status. + * + * @param[in] status : Pointer variable to store the status of the error + * @param[out] int_stat : Pointer variable to store error bits and + * message. + * + *@verbatim + * Internal status | status + * ---------------------------|--------------------- + * BMI2_INTERNAL_ERROR_1_MASK | 0X02 + * BMI2_INTERNAL_ERROR_2_MASK | 0X04 + * BMI2_FEAT_ENG_DIS_MASK | 0X10 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_internal_error_status(uint8_t *status, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiInts + * \page bmi2_api_bmi2_get_err_reg_mask bmi2_get_err_reg_mask + * \code + * int8_t bmi2_get_err_reg_mask(uint8_t *err_reg, struct bmi2_dev *dev); + * \endcode + * @details This API gets error status of interrupt. + * + * @param[in] err_reg : Pointer variable to store the status of the error + * @param[out] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Internal status | err_reg + * ---------------------------|--------------------- + * BMI2_FATAL_ERR_MASK | 0X01 + * BMI2_INTERNAL_ERR_MASK | 0X1E + * BMI2_FIFO_ERR_MASK | 0X40 + * BMI2_AUX_ERR_MASK | 0x80 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_err_reg_mask(uint8_t *err_reg, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiInts + * \page bmi2_api_bmi2_set_err_reg_mask bmi2_set_err_reg_mask + * \code + * int8_t bmi2_set_err_reg_mask(uint8_t err_reg, struct bmi2_dev *dev); + * \endcode + * @details This API sets error interrupt. + * + * @param[in] err_reg : Variable to store the status of the error + * @param[out] dev : Structure instance of bmi2_dev. + * + *@verbatim + * Internal status | err_reg + * ---------------------------|--------------------- + * BMI2_FATAL_ERR_MASK | 0X01 + * BMI2_INTERNAL_ERR_MASK | 0X1E + * BMI2_FIFO_ERR_MASK | 0X40 + * BMI2_AUX_ERR_MASK | 0x80 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_err_reg_mask(uint8_t err_reg, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiFOC FOC + * @brief FOC operations of the sensor + */ + +/*! + * \ingroup bmi2ApiFOC + * \page bmi2_api_bmi2_perform_accel_foc bmi2_perform_accel_foc + * \code + * int8_t bmi2_perform_accel_foc(const struct bmi2_accel_foc_g_value *accel_g_value, struct bmi2_dev *dev); + * \endcode + * @details This API performs Fast Offset Compensation for accelerometer. + * + * @param[in] accel_g_value : This parameter selects the accel foc + * axis to be performed + * + * input format is {x, y, z, sign}. '1' to enable. '0' to disable + * + * eg to choose x axis {1, 0, 0, 0} + * eg to choose -x axis {1, 0, 0, 1} + * + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_perform_accel_foc(const struct bmi2_accel_foc_g_value *accel_g_value, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiFOC + * \page bmi2_api_bmi2_perform_gyro_foc bmi2_perform_gyro_foc + * \code + * int8_t bmi2_perform_gyro_foc(struct bmi2_dev *dev); + * \endcode + * @details This API performs Fast Offset Compensation for gyroscope. + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_perform_gyro_foc(struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiCRT CRT + * @brief CRT operations of the sensor + */ + +/*! + * \ingroup bmi2ApiCRT + * \page bmi2_api_bmi2_do_crt bmi2_do_crt + * \code + * int8_t bmi2_do_crt(struct bmi2_dev *dev); + * \endcode + * @details API performs Component Re-Trim calibration (CRT). + * + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + * + * @note CRT calibration takes approximately 500ms & maximum time out configured as 2 seconds + */ +int8_t bmi2_do_crt(struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiCRTSt CRT and self test + * @brief Enable / Abort CRT and self test operations of gyroscope + */ + +/*! + * \ingroup bmi2ApiCRTSt + * \page bmi2_api_bmi2_abort_crt_gyro_st bmi2_abort_crt_gyro_st + * \code + * int8_t bmi2_abort_crt_gyro_st(struct bmi2_dev *dev); + * \endcode + * @details This api is used to abort ongoing crt or gyro self test. + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_abort_crt_gyro_st(struct bmi2_dev *dev); + +/*! + * \ingroup bmi2ApiASelftest + * \page bmi2_api_bmi2_do_gyro_st bmi2_do_gyro_st + * \code + * int8_t bmi2_do_gyro_st + * \endcode + * @details this api is used to perform gyroscope self test. + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_do_gyro_st(struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2ApiNVM NVM + * @brief NVM operations of the sensor + */ + +/*! + * \ingroup bmi2ApiNVM + * \page bmi2_api_bmi2_nvm_prog bmi2_nvm_prog + * \code + * int8_t bmi2_nvm_prog + * \endcode + * @details This api is used for programming the non volatile memory(nvm) + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_nvm_prog(struct bmi2_dev *dev); + +/*! + * @brief This API extracts the input feature configuration + * details like page and start address from the look-up table. + * + * @param[out] feat_config : Structure that stores feature configurations. + * @param[in] type : Type of feature or sensor. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Returns the feature found flag. + * + * @retval BMI2_FALSE : Feature not found + * BMI2_TRUE : Feature found + */ +uint8_t bmi2_extract_input_feat_config(struct bmi2_feature_config *feat_config, uint8_t type, + const struct bmi2_dev *dev); + +/*! + * @brief This API is used to get the feature configuration from the + * selected page. + * + * @param[in] sw_page : Switches to the desired page. + * @param[out] feat_config : Pointer to the feature configuration. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_feat_config(uint8_t sw_page, uint8_t *feat_config, struct bmi2_dev *dev); + +/** + * \ingroup bmi2 + * \defgroup bmi2AccelOffset Accelerometer Offset Compensation + * @brief Enable / Disable Accelerometer Offset Compensation + */ + +/*! + * \ingroup bmi2AccelOffset + * \page bmi2_api_bmi2_set_accel_offset_comp bmi2_set_accel_offset_comp + * \code + * int8_t bmi2_set_accel_offset_comp(uint8_t offset_en, struct bmi2_dev *dev); + * \endcode + * @brief This internal API enables/disables the offset compensation for + * filtered and un-filtered accelerometer data. + * + * @param[in] offset_en : enables/disables offset compensation. + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_set_accel_offset_comp(uint8_t offset_en, struct bmi2_dev *dev); + +/*! + * \ingroup bmi2AccelOffset + * \page bmi2_api_bmi2_get_accel_offset_comp bmi2_get_accel_offset_comp + * \code + * int8_t bmi2_get_accel_offset_comp(uint8_t *accel_offset, struct bmi2_dev *dev); + * \endcode + * @brief This internal API reads the accelerometer offset compensation value. + * + * @param[in] accel_offset : Pointer to an array of size 3 to hold Accel Axis X, Y, Z, + * Offset Compensation. + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi2_get_accel_offset_comp(uint8_t *accel_offset, struct bmi2_dev *dev); + +#ifdef __cplusplus +} +#endif /* End of CPP guard */ + +#endif /* BMI2_H_ */ diff --git a/projects/Compass/main/include/bmi270.h b/projects/Compass/main/include/bmi270.h new file mode 100644 index 00000000..c2c85ab0 --- /dev/null +++ b/projects/Compass/main/include/bmi270.h @@ -0,0 +1,420 @@ +/** +* Copyright (c) 2023 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 bmi270.h +* @date 2023-05-03 +* @version v2.86.1 +* +*/ + +/** + * \ingroup bmi2xy + * \defgroup bmi270 BMI270 + * @brief Sensor driver for BMI270 sensor + */ + +#ifndef BMI270_H_ +#define BMI270_H_ + +/*! CPP guard */ +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************/ + +/*! Header files + ****************************************************************************/ +#include "bmi2.h" + +/***************************************************************************/ + +/*! Macro definitions + ****************************************************************************/ + +/*! @name BMI270 Chip identifier */ +#define BMI270_CHIP_ID UINT8_C(0x24) + +/*! @name BMI270 feature input start addresses */ +#define BMI270_CONFIG_ID_STRT_ADDR UINT8_C(0x00) +#define BMI270_MAX_BURST_LEN_STRT_ADDR UINT8_C(0x02) +#define BMI270_CRT_GYRO_SELF_TEST_STRT_ADDR UINT8_C(0x03) +#define BMI270_ABORT_STRT_ADDR UINT8_C(0x03) +#define BMI270_AXIS_MAP_STRT_ADDR UINT8_C(0x04) +#define BMI270_GYRO_SELF_OFF_STRT_ADDR UINT8_C(0x05) +#define BMI270_NVM_PROG_PREP_STRT_ADDR UINT8_C(0x05) +#define BMI270_GYRO_GAIN_UPDATE_STRT_ADDR UINT8_C(0x06) +#define BMI270_ANY_MOT_STRT_ADDR UINT8_C(0x0C) +#define BMI270_NO_MOT_STRT_ADDR UINT8_C(0x00) +#define BMI270_SIG_MOT_STRT_ADDR UINT8_C(0x04) +#define BMI270_STEP_CNT_1_STRT_ADDR UINT8_C(0x00) +#define BMI270_STEP_CNT_4_STRT_ADDR UINT8_C(0x02) +#define BMI270_WRIST_GEST_STRT_ADDR UINT8_C(0x06) +#define BMI270_WRIST_WEAR_WAKE_UP_STRT_ADDR UINT8_C(0x00) + +/*! @name BMI270 feature output start addresses */ +#define BMI270_STEP_CNT_OUT_STRT_ADDR UINT8_C(0x00) +#define BMI270_STEP_ACT_OUT_STRT_ADDR UINT8_C(0x04) +#define BMI270_WRIST_GEST_OUT_STRT_ADDR UINT8_C(0x06) +#define BMI270_GYR_USER_GAIN_OUT_STRT_ADDR UINT8_C(0x08) +#define BMI270_GYRO_CROSS_SENSE_STRT_ADDR UINT8_C(0x0C) +#define BMI270_NVM_VFRM_OUT_STRT_ADDR UINT8_C(0x0E) + +/*! @name Defines maximum number of pages */ +#define BMI270_MAX_PAGE_NUM UINT8_C(8) + +/*! @name Defines maximum number of feature input configurations */ +#define BMI270_MAX_FEAT_IN UINT8_C(17) + +/*! @name Defines maximum number of feature outputs */ +#define BMI270_MAX_FEAT_OUT UINT8_C(7) + +/*! @name Mask definitions for feature interrupt status bits */ +#define BMI270_SIG_MOT_STATUS_MASK UINT8_C(0x01) +#define BMI270_STEP_CNT_STATUS_MASK UINT8_C(0x02) +#define BMI270_STEP_ACT_STATUS_MASK UINT8_C(0x04) +#define BMI270_WRIST_WAKE_UP_STATUS_MASK UINT8_C(0x08) +#define BMI270_WRIST_GEST_STATUS_MASK UINT8_C(0x10) +#define BMI270_NO_MOT_STATUS_MASK UINT8_C(0x20) +#define BMI270_ANY_MOT_STATUS_MASK UINT8_C(0x40) + +/*! @name Mask definitions for feature interrupt mapping bits */ +#define BMI270_INT_SIG_MOT_MASK UINT8_C(0x01) +#define BMI270_INT_STEP_COUNTER_MASK UINT8_C(0x02) +#define BMI270_INT_STEP_DETECTOR_MASK UINT8_C(0x02) +#define BMI270_INT_STEP_ACT_MASK UINT8_C(0x04) +#define BMI270_INT_WRIST_WEAR_WAKEUP_MASK UINT8_C(0x08) +#define BMI270_INT_WRIST_GEST_MASK UINT8_C(0x10) +#define BMI270_INT_NO_MOT_MASK UINT8_C(0x20) +#define BMI270_INT_ANY_MOT_MASK UINT8_C(0x40) + +/*! @name Defines maximum number of feature interrupts */ +#define BMI270_MAX_INT_MAP UINT8_C(8) + +/***************************************************************************/ + +/*! BMI270 User Interface function prototypes + ****************************************************************************/ + +/** + * \ingroup bmi270 + * \defgroup bmi270ApiInit Initialization + * @brief Initialize the sensor and device structure + */ + +/*! + * \ingroup bmi270ApiInit + * \page bmi270_api_bmi270_init bmi270_init + * \code + * int8_t bmi270_init(struct bmi2_dev *dev); + * \endcode + * @details This API: + * 1) updates the device structure with address of the configuration file. + * 2) Initializes BMI270 sensor. + * 3) Writes the configuration file. + * 4) Updates the feature offset parameters in the device structure. + * 5) Updates the maximum number of pages, in the device structure. + * + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi270_init(struct bmi2_dev *dev); + +/** + * \ingroup bmi270 + * \defgroup bmi270ApiSensor Feature Set + * @brief Enable / Disable features of the sensor + */ + +/*! + * \ingroup bmi270ApiSensor + * \page bmi270_api_bmi270_sensor_enable bmi270_sensor_enable + * \code + * int8_t bmi270_sensor_enable(const uint8_t *sens_list, uint8_t n_sens, struct bmi2_dev *dev); + * \endcode + * @details This API selects the sensors/features to be enabled. + * + * @param[in] sens_list : Pointer to select the sensor/feature. + * @param[in] n_sens : Number of sensors selected. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @note Sensors/features that can be enabled. + * + *@verbatim + * sens_list | Values + * ----------------------------|----------- + * BMI2_ACCEL | 0 + * BMI2_GYRO | 1 + * BMI2_AUX | 2 + * BMI2_SIG_MOTION | 3 + * BMI2_ANY_MOTION | 4 + * BMI2_NO_MOTION | 5 + * BMI2_STEP_DETECTOR | 6 + * BMI2_STEP_COUNTER | 7 + * BMI2_STEP_ACTIVITY | 8 + * BMI2_GYRO_GAIN_UPDATE | 9 + * BMI2_WRIST_GESTURE | 19 + * BMI2_WRIST_WEAR_WAKE_UP | 20 + * BMI2_GYRO_SELF_OFF | 33 + *@endverbatim + * + * @note : + * example uint8_t sens_list[2] = {BMI2_ACCEL, BMI2_GYRO}; + * uint8_t n_sens = 2; + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi270_sensor_enable(const uint8_t *sens_list, uint8_t n_sens, struct bmi2_dev *dev); + +/*! + * \ingroup bmi270ApiSensor + * \page bmi270_api_bmi270_sensor_disable bmi270_sensor_disable + * \code + * int8_t bmi270_sensor_disable(const uint8_t *sens_list, uint8_t n_sens, struct bmi2_dev *dev); + * \endcode + * @details This API selects the sensors/features to be disabled. + * + * @param[in] sens_list : Pointer to select the sensor/feature. + * @param[in] n_sens : Number of sensors selected. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @note Sensors/features that can be disabled. + * + *@verbatim + * sens_list | Values + * ----------------------------|----------- + * BMI2_ACCEL | 0 + * BMI2_GYRO | 1 + * BMI2_AUX | 2 + * BMI2_SIG_MOTION | 3 + * BMI2_ANY_MOTION | 4 + * BMI2_NO_MOTION | 5 + * BMI2_STEP_DETECTOR | 6 + * BMI2_STEP_COUNTER | 7 + * BMI2_STEP_ACTIVITY | 8 + * BMI2_GYRO_GAIN_UPDATE | 9 + * BMI2_WRIST_GESTURE | 19 + * BMI2_WRIST_WEAR_WAKE_UP | 20 + * BMI2_GYRO_SELF_OFF | 33 + *@endverbatim + * + * @note : + * example uint8_t sens_list[2] = {BMI2_ACCEL, BMI2_GYRO}; + * uint8_t n_sens = 2; + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi270_sensor_disable(const uint8_t *sens_list, uint8_t n_sens, struct bmi2_dev *dev); + +/** + * \ingroup bmi270 + * \defgroup bmi270ApiSensorC Sensor Configuration + * @brief Enable / Disable feature configuration of the sensor + */ + +/*! + * \ingroup bmi270ApiSensorC + * \page bmi270_api_bmi270_set_sensor_config bmi270_set_sensor_config + * \code + * int8_t bmi270_set_sensor_config(struct bmi2_sens_config *sens_cfg, uint8_t n_sens, struct bmi2_dev *dev); + * \endcode + * @details This API sets the sensor/feature configuration. + * + * @param[in] sens_cfg : Structure instance of bmi2_sens_config. + * @param[in] n_sens : Number of sensors selected. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @note Sensors/features that can be configured + * + *@verbatim + * sens_list | Values + * ----------------------------|----------- + * BMI2_SIG_MOTION | 3 + * BMI2_ANY_MOTION | 4 + * BMI2_NO_MOTION | 5 + * BMI2_STEP_DETECTOR | 6 + * BMI2_STEP_COUNTER | 7 + * BMI2_STEP_ACTIVITY | 8 + * BMI2_WRIST_GESTURE | 19 + * BMI2_WRIST_WEAR_WAKE_UP | 20 + * BMI2_STEP_COUNTER_PARAMS | 28 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi270_set_sensor_config(struct bmi2_sens_config *sens_cfg, uint8_t n_sens, struct bmi2_dev *dev); + +/*! + * \ingroup bmi270ApiSensorC + * \page bmi270_api_bmi270_get_sensor_config bmi270_get_sensor_config + * \code + * int8_t bmi270_get_sensor_config(struct bmi2_sens_config *sens_cfg, uint8_t n_sens, struct bmi2_dev *dev); + * \endcode + * @details This API gets the sensor/feature configuration. + * + * @param[in] sens_cfg : Structure instance of bmi2_sens_config. + * @param[in] n_sens : Number of sensors selected. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @note Sensors/features whose configurations can be read. + * + *@verbatim + * sens_list | Values + * ----------------------------|----------- + * BMI2_SIG_MOTION | 3 + * BMI2_ANY_MOTION | 4 + * BMI2_NO_MOTION | 5 + * BMI2_STEP_DETECTOR | 6 + * BMI2_STEP_COUNTER | 7 + * BMI2_STEP_ACTIVITY | 8 + * BMI2_WRIST_GESTURE | 19 + * BMI2_WRIST_WEAR_WAKE_UP | 20 + * BMI2_STEP_COUNTER_PARAMS | 28 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi270_get_sensor_config(struct bmi2_sens_config *sens_cfg, uint8_t n_sens, struct bmi2_dev *dev); + +/** + * \ingroup bmi270 + * \defgroup bmi270ApiSensorD Feature Sensor Data + * @brief Get feature sensor data + */ + +/*! + * \ingroup bmi270ApiSensorD + * \page bmi270_api_bmi270_get_feature_data bmi270_get_feature_data + * \code + * int8_t bmi270_get_feature_data(struct bmi2_feat_sensor_data *feature_data, uint8_t n_sens, struct bmi2_dev *dev); + * \endcode + * @details This API gets the feature data. + * + * @param[out] feature_data : Structure instance of bmi2_feat_sensor_data. + * @param[in] n_sens : Number of sensors selected. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @note Sensors/features whose data can be read + * + *@verbatim + * sens_list | Values + * ----------------------------|----------- + * BMI2_STEP_COUNTER | 7 + * BMI2_STEP_ACTIVITY | 8 + * BMI2_WRIST_GESTURE | 19 + * BMI2_NVM_STATUS | 38 + * BMI2_VFRM_STATUS | 39 + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi270_get_feature_data(struct bmi2_feat_sensor_data *feature_data, uint8_t n_sens, struct bmi2_dev *dev); + +/** + * \ingroup bmi270 + * \defgroup bmi270ApiGyroUG Gyro User Gain + * @brief Set / Get Gyro User Gain of the sensor + */ + +/*! + * \ingroup bmi270ApiGyroUG + * \page bmi270_api_bmi270_update_gyro_user_gain bmi270_update_gyro_user_gain + * \code + * int8_t bmi270_update_gyro_user_gain(const struct bmi2_gyro_user_gain_config *user_gain, struct bmi2_dev *dev); + * \endcode + * @details This API updates the gyroscope user-gain. + * + * @param[in] user_gain : Structure that stores user-gain configurations. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi270_update_gyro_user_gain(const struct bmi2_gyro_user_gain_config *user_gain, struct bmi2_dev *dev); + +/*! + * \ingroup bmi270ApiGyroUG + * \page bmi270_api_bmi270_read_gyro_user_gain bmi270_read_gyro_user_gain + * \code + * int8_t bmi270_read_gyro_user_gain(struct bmi2_gyro_user_gain_data *gyr_usr_gain, const struct bmi2_dev *dev); + * \endcode + * @details This API reads the compensated gyroscope user-gain values. + * + * @param[out] gyr_usr_gain : Structure that stores gain values. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi270_read_gyro_user_gain(struct bmi2_gyro_user_gain_data *gyr_usr_gain, struct bmi2_dev *dev); + +/*! + * \ingroup bmi270ApiInt + * \page bmi270_api_bmi270_map_feat_int bmi270_map_feat_int + * \code + * int8_t bmi270_map_feat_int(const struct bmi2_sens_int_config *sens_int, uint8_t n_sens, struct bmi2_dev *dev) + * \endcode + * @details This API maps/unmaps feature interrupts to that of interrupt pins. + * + * @param[in] sens_int : Structure instance of bmi2_sens_int_config. + * @param[in] n_sens : Number of interrupts to be mapped. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +int8_t bmi270_map_feat_int(const struct bmi2_sens_int_config *sens_int, uint8_t n_sens, struct bmi2_dev *dev); + +/******************************************************************************/ +/*! @name C++ Guard Macros */ +/******************************************************************************/ +#ifdef __cplusplus +} +#endif /* End of CPP guard */ + +#endif /* BMI270_H_ */ diff --git a/projects/Compass/main/include/bmi270_port.h b/projects/Compass/main/include/bmi270_port.h new file mode 100644 index 00000000..dfd48ef1 --- /dev/null +++ b/projects/Compass/main/include/bmi270_port.h @@ -0,0 +1,28 @@ +/** + * @file bmi270_port.h + * @brief BMI270 Linux I2C 用户空间移植层 (M5CardputerZero) + */ + +#ifndef BMI270_PORT_H_ +#define BMI270_PORT_H_ + +#include "bmi2.h" +#include "bmi270.h" + +/* I2C 配置 */ +#define BMI270_I2C_BUS "/dev/i2c-1" /* M5CardputerZero 默认 I2C 总线 */ +#define BMI270_I2C_ADDR 0x68 /* SDO=GND: 0x68, SDO=VDDIO: 0x69 */ + +#ifdef __cplusplus +extern "C" { +#endif + +int8_t bmi270_port_init(struct bmi2_dev *dev); +void bmi270_port_deinit(struct bmi2_dev *dev); +void bmi270_print_result(int8_t rslt); + +#ifdef __cplusplus +} +#endif + +#endif /* BMI270_PORT_H_ */ diff --git a/projects/Compass/main/include/bmi2_defs.h b/projects/Compass/main/include/bmi2_defs.h new file mode 100644 index 00000000..0bc3f913 --- /dev/null +++ b/projects/Compass/main/include/bmi2_defs.h @@ -0,0 +1,3164 @@ +/** +* Copyright (c) 2025 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 bmi2_defs.h +* @date 2025-04-22 +* @version v2.113.0 +* +*/ + +#ifndef BMI2_DEFS_H_ +#define BMI2_DEFS_H_ + +/******************************************************************************/ +/*! @name Header includes */ +/******************************************************************************/ +#ifdef __KERNEL__ +#include +#include +#else +#include +#include +#include +#endif + +/******************************************************************************/ +/*! @name Common macros */ +/******************************************************************************/ +#ifdef __KERNEL__ +#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 +#endif + +/*! @name C standard macros */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +/******************************************************************************/ +/*! @name General Macro Definitions */ +/******************************************************************************/ +/*! @name Utility macros */ +#define BMI2_SET_BITS(reg_data, bitname, data) \ + ((reg_data & ~(bitname##_MASK)) | \ + ((data << bitname##_POS) & bitname##_MASK)) + +#define BMI2_GET_BITS(reg_data, bitname) \ + ((reg_data & (bitname##_MASK)) >> \ + (bitname##_POS)) + +#define BMI2_SET_BIT_POS0(reg_data, bitname, data) \ + ((reg_data & ~(bitname##_MASK)) | \ + (data & bitname##_MASK)) + +#define BMI2_GET_BIT_POS0(reg_data, bitname) (reg_data & (bitname##_MASK)) +#define BMI2_SET_BIT_VAL0(reg_data, bitname) (reg_data & ~(bitname##_MASK)) + +/*! @name For getting LSB and MSB */ +#define BMI2_GET_LSB(var) (uint8_t)(var & BMI2_SET_LOW_BYTE) +#define BMI2_GET_MSB(var) (uint8_t)((var & BMI2_SET_HIGH_BYTE) >> 8) + +#ifndef BMI2_INTF_RETURN_TYPE +#define BMI2_INTF_RETURN_TYPE int8_t +#endif + +/*! @name For defining absolute values */ +#define BMI2_ABS(a) ((a) > 0 ? (a) : -(a)) + +#define BMI2_MAX_BUFFER_SIZE UINT8_C(128) + +/*! @name LSB and MSB mask definitions */ +#define BMI2_SET_LOW_BYTE UINT16_C(0x00FF) +#define BMI2_SET_HIGH_BYTE UINT16_C(0xFF00) +#define BMI2_SET_LOW_NIBBLE UINT8_C(0x0F) + +/*! @name For enable and disable */ +#define BMI2_ENABLE UINT8_C(1) +#define BMI2_DISABLE UINT8_C(0) + +/*! @name To define TRUE or FALSE */ +#define BMI2_TRUE UINT8_C(1) +#define BMI2_FALSE UINT8_C(0) + +/*! @name Macro to define maximum length of read */ +#define BMI2_MAX_LEN UINT8_C(128) + +/*! @name To define sensor interface success code */ +#define BMI2_INTF_RET_SUCCESS INT8_C(0) + +/*! @name To define success code */ +#define BMI2_OK INT8_C(0) + +/* @name To define delay */ +#define BMI2_POWER_SAVE_MODE_DELAY_IN_US UINT16_C(450) +#define BMI2_NORMAL_MODE_DELAY_IN_US UINT8_C(2) + +/*! @name To define error codes */ +#define BMI2_E_NULL_PTR INT8_C(-1) +#define BMI2_E_COM_FAIL INT8_C(-2) +#define BMI2_E_DEV_NOT_FOUND INT8_C(-3) +#define BMI2_E_OUT_OF_RANGE INT8_C(-4) +#define BMI2_E_ACC_INVALID_CFG INT8_C(-5) +#define BMI2_E_GYRO_INVALID_CFG INT8_C(-6) +#define BMI2_E_ACC_GYR_INVALID_CFG INT8_C(-7) +#define BMI2_E_INVALID_SENSOR INT8_C(-8) +#define BMI2_E_CONFIG_LOAD INT8_C(-9) +#define BMI2_E_INVALID_PAGE INT8_C(-10) +#define BMI2_E_INVALID_FEAT_BIT INT8_C(-11) +#define BMI2_E_INVALID_INT_PIN INT8_C(-12) +#define BMI2_E_SET_APS_FAIL INT8_C(-13) +#define BMI2_E_AUX_INVALID_CFG INT8_C(-14) +#define BMI2_E_AUX_BUSY INT8_C(-15) +#define BMI2_E_SELF_TEST_FAIL INT8_C(-16) +#define BMI2_E_REMAP_ERROR INT8_C(-17) +#define BMI2_E_GYR_USER_GAIN_UPD_FAIL INT8_C(-18) +#define BMI2_E_SELF_TEST_NOT_DONE INT8_C(-19) +#define BMI2_E_INVALID_INPUT INT8_C(-20) +#define BMI2_E_INVALID_STATUS INT8_C(-21) +#define BMI2_E_CRT_ERROR INT8_C(-22) +#define BMI2_E_ST_ALREADY_RUNNING INT8_C(-23) +#define BMI2_E_CRT_READY_FOR_DL_FAIL_ABORT INT8_C(-24) +#define BMI2_E_DL_ERROR INT8_C(-25) +#define BMI2_E_PRECON_ERROR INT8_C(-26) +#define BMI2_E_ABORT_ERROR INT8_C(-27) +#define BMI2_E_GYRO_SELF_TEST_ERROR INT8_C(-28) +#define BMI2_E_GYRO_SELF_TEST_TIMEOUT INT8_C(-29) +#define BMI2_E_WRITE_CYCLE_ONGOING INT8_C(-30) +#define BMI2_E_WRITE_CYCLE_TIMEOUT INT8_C(-31) +#define BMI2_E_ST_NOT_RUNING INT8_C(-32) +#define BMI2_E_DATA_RDY_INT_FAILED INT8_C(-33) +#define BMI2_E_INVALID_FOC_POSITION INT8_C(-34) + +/*! @name To define warnings for FIFO activity */ +#define BMI2_W_FIFO_EMPTY INT8_C(1) +#define BMI2_W_PARTIAL_READ INT8_C(2) +#define BMI2_W_DUMMY_BYTE INT8_C(3) + +/*! @name Macros to define dummy frame header FIFO headerless mode */ +#define BMI2_FIFO_HEADERLESS_DUMMY_ACC UINT8_C(0x01) +#define BMI2_FIFO_HEADERLESS_DUMMY_GYR UINT8_C(0x02) +#define BMI2_FIFO_HEADERLESS_DUMMY_AUX UINT8_C(0x03) +#define BMI2_FIFO_HEADERLESS_DUMMY_BYTE_1 UINT8_C(0x7F) +#define BMI2_FIFO_HEADERLESS_DUMMY_BYTE_2 UINT8_C(0x00) +#define BMI2_FIFO_HEADERLESS_DUMMY_BYTE_3 UINT8_C(0x80) + +/*! @name Bit wise to define information */ +#define BMI2_I_MIN_VALUE UINT8_C(1) +#define BMI2_I_MAX_VALUE UINT8_C(2) + +/*! @name BMI2 register addresses */ +#define BMI2_CHIP_ID_ADDR UINT8_C(0x00) +#define BMI2_STATUS_ADDR UINT8_C(0x03) +#define BMI2_AUX_X_LSB_ADDR UINT8_C(0x04) +#define BMI2_ACC_X_LSB_ADDR UINT8_C(0x0C) +#define BMI2_GYR_X_LSB_ADDR UINT8_C(0x12) +#define BMI2_SENSORTIME_ADDR UINT8_C(0x18) +#define BMI2_EVENT_ADDR UINT8_C(0x1B) +#define BMI2_INT_STATUS_0_ADDR UINT8_C(0x1C) +#define BMI2_INT_STATUS_1_ADDR UINT8_C(0x1D) +#define BMI2_SC_OUT_0_ADDR UINT8_C(0x1E) +#define BMI2_SYNC_COMMAND_ADDR UINT8_C(0x1E) +#define BMI2_GYR_CAS_GPIO0_ADDR UINT8_C(0x1E) +#define BMI2_DSD_OUT_ADDR UINT8_C(0x1E) +#define BMI2_INTERNAL_STATUS_ADDR UINT8_C(0x21) +#define BMI2_TEMPERATURE_0_ADDR UINT8_C(0x22) +#define BMI2_TEMPERATURE_1_ADDR UINT8_C(0x23) +#define BMI2_FIFO_LENGTH_0_ADDR UINT8_C(0x24) +#define BMI2_FIFO_DATA_ADDR UINT8_C(0x26) +#define BMI2_FEAT_PAGE_ADDR UINT8_C(0x2F) +#define BMI2_FEATURES_REG_ADDR UINT8_C(0x30) +#define BMI2_ACC_CONF_ADDR UINT8_C(0x40) +#define BMI2_GYR_CONF_ADDR UINT8_C(0x42) +#define BMI2_AUX_CONF_ADDR UINT8_C(0x44) +#define BMI2_FIFO_DOWNS_ADDR UINT8_C(0x45) +#define BMI2_FIFO_WTM_0_ADDR UINT8_C(0x46) +#define BMI2_FIFO_WTM_1_ADDR UINT8_C(0x47) +#define BMI2_FIFO_CONFIG_0_ADDR UINT8_C(0x48) +#define BMI2_FIFO_CONFIG_1_ADDR UINT8_C(0x49) +#define BMI2_SATURATION_ADDR UINT8_C(0x4A) +#define BMI2_AUX_DEV_ID_ADDR UINT8_C(0x4B) +#define BMI2_AUX_IF_CONF_ADDR UINT8_C(0x4C) +#define BMI2_AUX_RD_ADDR UINT8_C(0x4D) +#define BMI2_AUX_WR_ADDR UINT8_C(0x4E) +#define BMI2_AUX_WR_DATA_ADDR UINT8_C(0x4F) +#define BMI2_ERR_REG_MSK_ADDR UINT8_C(0x52) +#define BMI2_INT1_IO_CTRL_ADDR UINT8_C(0x53) +#define BMI2_INT2_IO_CTRL_ADDR UINT8_C(0x54) +#define BMI2_INT_LATCH_ADDR UINT8_C(0x55) +#define BMI2_INT1_MAP_FEAT_ADDR UINT8_C(0x56) +#define BMI2_INT2_MAP_FEAT_ADDR UINT8_C(0x57) +#define BMI2_INT_MAP_DATA_ADDR UINT8_C(0x58) +#define BMI2_INIT_CTRL_ADDR UINT8_C(0x59) +#define BMI2_INIT_ADDR_0 UINT8_C(0x5B) +#define BMI2_INIT_ADDR_1 UINT8_C(0x5C) +#define BMI2_INIT_DATA_ADDR UINT8_C(0x5E) +#define BMI2_INTERNAL_ERR_ADDR UINT8_C(0x5F) +#define BMI2_AUX_IF_TRIM UINT8_C(0x68) +#define BMI2_GYR_CRT_CONF_ADDR UINT8_C(0x69) +#define BMI2_NVM_CONF_ADDR UINT8_C(0x6A) +#define BMI2_IF_CONF_ADDR UINT8_C(0x6B) +#define BMI2_DRV_STR_ADDR UINT8_C(0x6C) +#define BMI2_ACC_SELF_TEST_ADDR UINT8_C(0x6D) +#define BMI2_GYR_SELF_TEST_AXES_ADDR UINT8_C(0x6E) +#define BMI2_SELF_TEST_MEMS_ADDR UINT8_C(0x6F) +#define BMI2_NV_CONF_ADDR UINT8_C(0x70) +#define BMI2_ACC_OFF_COMP_0_ADDR UINT8_C(0x71) +#define BMI2_GYR_OFF_COMP_3_ADDR UINT8_C(0x74) +#define BMI2_GYR_OFF_COMP_6_ADDR UINT8_C(0x77) +#define BMI2_GYR_USR_GAIN_0_ADDR UINT8_C(0x78) +#define BMI2_PWR_CONF_ADDR UINT8_C(0x7C) +#define BMI2_PWR_CTRL_ADDR UINT8_C(0x7D) +#define BMI2_CMD_REG_ADDR UINT8_C(0x7E) + +/*! @name BMI2 I2C address */ +#define BMI2_I2C_PRIM_ADDR UINT8_C(0x68) +#define BMI2_I2C_SEC_ADDR UINT8_C(0x69) + +/*! @name BMI2 Commands */ +#define BMI2_G_TRIGGER_CMD UINT8_C(0x02) +#define BMI2_USR_GAIN_CMD UINT8_C(0x03) +#define BMI2_NVM_PROG_CMD UINT8_C(0xA0) +#define BMI2_SOFT_RESET_CMD UINT8_C(0xB6) +#define BMI2_FIFO_FLUSH_CMD UINT8_C(0xB0) + +/*! @name BMI2 sensor data bytes */ + +#define BMI2_AUX_NUM_BYTES UINT8_C(8) +#define BMI2_ACC_NUM_BYTES UINT8_C(6) +#define BMI2_GYR_NUM_BYTES UINT8_C(6) +#define BMI2_STATUS_INDEX UINT8_C(0) +#define BMI2_AUX_START_INDEX UINT8_C(1) +#define BMI2_ACC_START_INDEX UINT8_C(9) +#define BMI2_GYR_START_INDEX UINT8_C(15) +#define BMI2_ACC_GYR_AUX_SENSORTIME_NUM_BYTES UINT8_C(24) +#define BMI2_CRT_CONFIG_FILE_SIZE UINT16_C(2048) +#define BMI2_FEAT_SIZE_IN_BYTES UINT8_C(16) +#define BMI2_ACC_CONFIG_LENGTH UINT8_C(2) + +/*! @name BMI2 configuration load status */ +#define BMI2_CONFIG_LOAD_SUCCESS UINT8_C(1) +#define BMI2_CONFIG_LOAD_STATUS_MASK UINT8_C(0x0F) + +/*! @name To define BMI2 pages */ +#define BMI2_PAGE_0 UINT8_C(0) +#define BMI2_PAGE_1 UINT8_C(1) +#define BMI2_PAGE_2 UINT8_C(2) +#define BMI2_PAGE_3 UINT8_C(3) +#define BMI2_PAGE_4 UINT8_C(4) +#define BMI2_PAGE_5 UINT8_C(5) +#define BMI2_PAGE_6 UINT8_C(6) +#define BMI2_PAGE_7 UINT8_C(7) + +/*! @name Array Parameter Definitions */ +#define BMI2_PARSE_SENSOR_TIME_LSB_BYTE UINT8_C(21) +#define BMI2_PARSE_SENSOR_TIME_XLSB_BYTE UINT8_C(22) +#define BMI2_PARSE_SENSOR_TIME_MSB_BYTE UINT8_C(23) + +#define BMI2_SENSOR_TIME_XLSB_BYTE UINT8_C(1) +#define BMI2_SENSOR_TIME_MSB_BYTE UINT8_C(2) + +/*! @name Mask definitions for Gyro CRT */ +#define BMI2_GYR_RDY_FOR_DL_MASK UINT8_C(0x08) +#define BMI2_GYR_CRT_RUNNING_MASK UINT8_C(0x04) + +/*! @name mask definition for status register */ +#define BMI2_AUX_BUSY_MASK UINT8_C(0x04) +#define BMI2_CMD_RDY_MASK UINT8_C(0x10) +#define BMI2_DRDY_AUX_MASK UINT8_C(0x20) +#define BMI2_DRDY_GYR_MASK UINT8_C(0x40) +#define BMI2_DRDY_ACC_MASK UINT8_C(0x80) + +/*! @name Mask definitions for SPI read/write address */ +#define BMI2_SPI_RD_MASK UINT8_C(0x80) +#define BMI2_SPI_WR_MASK UINT8_C(0x7F) + +/*! @name Mask definitions for power configuration register */ +#define BMI2_ADV_POW_EN_MASK UINT8_C(0x01) +#define BMI2_FUP_EN_POS UINT8_C(0x02) +#define BMI2_FUP_EN_MASK UINT8_C(0x04) + +/*! @name Mask definitions for initialization control register */ +#define BMI2_CONF_LOAD_EN_MASK UINT8_C(0x01) + +/*! @name Mask definitions for power control register */ +#define BMI2_AUX_EN_MASK UINT8_C(0x01) +#define BMI2_GYR_EN_MASK UINT8_C(0x02) +#define BMI2_ACC_EN_MASK UINT8_C(0x04) +#define BMI2_TEMP_EN_MASK UINT8_C(0x08) + +/*! @name Mask definitions for sensor event flags */ +#define BMI2_EVENT_FLAG_MASK UINT8_C(0x1C) + +/*! @name Mask definitions to switch page */ +#define BMI2_SWITCH_PAGE_EN_MASK UINT8_C(0x07) + +/*! @name Mask definitions of NVM register */ +#define BMI2_NV_SPI_EN_MASK UINT8_C(0x01) +#define BMI2_NV_I2C_WD_SEL_MASK UINT8_C(0x02) +#define BMI2_NV_I2C_WD_EN_MASK UINT8_C(0x04) +#define BMI2_NV_ACC_OFFSET_MASK UINT8_C(0x08) + +/*! @name Mask definitions of DRV register */ +#define BMI2_DRV_STR_MASK UINT8_C(0xFF) + +/*! @name Mask definition for config version */ +#define BMI2_CONFIG_MAJOR_MASK UINT16_C(0x3C0) +#define BMI2_CONFIG_MINOR_MASK UINT8_C(0x3F) + +/*! @name mask and bit position for activity recognition settings */ +#define BMI2_ACT_RECG_POST_PROS_EN_DIS_MASK UINT8_C(0x01) +#define BMI2_ACT_RECG_BUFF_SIZE_MASK UINT8_C(0x0F) +#define BMI2_ACT_RECG_MIN_SEG_CONF_MASK UINT8_C(0x0F) + +/*! @name mask and bit position for activity recognition hc settings */ +#define BMI2_HC_ACT_RECG_SEGMENT_SIZE_MASK UINT8_C(0x03) +#define BMI2_HC_ACT_RECG_PP_EN_MASK UINT8_C(0x01) +#define BMI2_HC_ACT_RECG_MIN_GDI_THRES_MASK UINT16_C(0xFFFF) +#define BMI2_HC_ACT_RECG_MAX_GDI_THRES_MASK UINT16_C(0xFFFF) +#define BMI2_HC_ACT_RECG_BUF_SIZE_MASK UINT16_C(0xFFFF) +#define BMI2_HC_ACT_RECG_MIN_SEG_CONF_MASK UINT16_C(0xFFFF) + +#define BMI2_GYRO_CROSS_AXES_SENSE_MASK UINT8_C(0x7F) +#define BMI2_GYRO_CROSS_AXES_SENSE_SIGN_BIT_MASK UINT8_C(0x40) + +/*! @name Bit position definitions for Gyro CRT */ +#define BMI2_GYR_RDY_FOR_DL_POS UINT8_C(0x03) +#define BMI2_GYR_CRT_RUNNING_POS UINT8_C(0x02) + +/*! @name Bit position for status register*/ +#define BMI2_AUX_BUSY_POS UINT8_C(0x02) +#define BMI2_CMD_RDY_POS UINT8_C(0x04) +#define BMI2_DRDY_AUX_POS UINT8_C(0x05) +#define BMI2_DRDY_GYR_POS UINT8_C(0x06) +#define BMI2_DRDY_ACC_POS UINT8_C(0x07) + +/*! @name Bit position definition for internal error register */ +#define BMI2_INTERNAL_ERROR_REG_POS UINT8_C(0x00) + +/*! @name Bit position definitions for power control register */ +#define BMI2_GYR_EN_POS UINT8_C(0x01) +#define BMI2_ACC_EN_POS UINT8_C(0x02) +#define BMI2_TEMP_EN_POS UINT8_C(0x03) + +/*! @name Bit position definitions for sensor event flags */ +#define BMI2_EVENT_FLAG_POS UINT8_C(0x02) + +/*! @name Bit position definitions of NVM register */ +#define BMI2_NV_SPI_EN_POS UINT8_C(0x00) +#define BMI2_NV_I2C_WD_SEL_POS UINT8_C(0x01) +#define BMI2_NV_I2C_WD_EN_POS UINT8_C(0x02) +#define BMI2_NV_ACC_OFFSET_POS UINT8_C(0x03) + +/*! @name Mask definitions of DRV register */ +#define BMI2_DRV_STR_POS UINT8_C(0x00) + +/*! @name Bit position for major version from config */ +#define BMI2_CONFIG_MAJOR_POS UINT8_C(0x06) + +/*! @name Accelerometer and Gyroscope Filter/Noise performance modes */ +/* Power optimized mode */ +#define BMI2_POWER_OPT_MODE UINT8_C(0) + +/* Performance optimized */ +#define BMI2_PERF_OPT_MODE UINT8_C(1) + +/*! @name index for config major minor information */ +#define BMI2_CONFIG_INFO_LOWER UINT8_C(52) +#define BMI2_CONFIG_INFO_HIGHER UINT8_C(53) + +/*! @name Sensor status */ +#define BMI2_DRDY_ACC UINT8_C(0x80) +#define BMI2_DRDY_GYR UINT8_C(0x40) +#define BMI2_DRDY_AUX UINT8_C(0x20) +#define BMI2_CMD_RDY UINT8_C(0x10) +#define BMI2_AUX_BUSY UINT8_C(0x04) + +/*! @name Macro to define accelerometer configuration value for FOC */ +#define BMI2_FOC_ACC_CONF_VAL UINT8_C(0xB7) + +/*! @name Macro to define gyroscope configuration value for FOC */ +#define BMI2_FOC_GYR_CONF_VAL UINT8_C(0xB6) + +/*! @name Macro to define X Y and Z axis for an array */ +#define BMI2_X_AXIS UINT8_C(0) +#define BMI2_Y_AXIS UINT8_C(1) +#define BMI2_Z_AXIS UINT8_C(2) + +#define BMI2_FOC_INVERT_VALUE INT8_C(-1) + +/*! @name Macro for delay to read internal status */ +#define BMI2_INTERNAL_STATUS_READ_DELAY_MS UINT32_C(20000) + +/******************************************************************************/ +/*! @name Sensor Macro Definitions */ +/******************************************************************************/ +/*! @name Macros to define BMI2 sensor/feature types */ +#define BMI2_ACCEL UINT8_C(0) +#define BMI2_GYRO UINT8_C(1) +#define BMI2_AUX UINT8_C(2) +#define BMI2_SIG_MOTION UINT8_C(3) +#define BMI2_ANY_MOTION UINT8_C(4) +#define BMI2_NO_MOTION UINT8_C(5) +#define BMI2_STEP_DETECTOR UINT8_C(6) +#define BMI2_STEP_COUNTER UINT8_C(7) +#define BMI2_STEP_ACTIVITY UINT8_C(8) +#define BMI2_GYRO_GAIN_UPDATE UINT8_C(9) +#define BMI2_TILT UINT8_C(10) +#define BMI2_UP_HOLD_TO_WAKE UINT8_C(11) +#define BMI2_GLANCE_DETECTOR UINT8_C(12) +#define BMI2_WAKE_UP UINT8_C(13) +#define BMI2_ORIENTATION UINT8_C(14) +#define BMI2_HIGH_G UINT8_C(15) +#define BMI2_LOW_G UINT8_C(16) +#define BMI2_FLAT UINT8_C(17) +#define BMI2_EXT_SENS_SYNC UINT8_C(18) +#define BMI2_WRIST_GESTURE UINT8_C(19) +#define BMI2_WRIST_WEAR_WAKE_UP UINT8_C(20) +#define BMI2_WRIST_WEAR_WAKE_UP_WH UINT8_C(21) +#define BMI2_WRIST_GESTURE_WH UINT8_C(22) +#define BMI2_PRIMARY_OIS UINT8_C(23) +#define BMI2_FREE_FALL_DET UINT8_C(24) +#define BMI2_SINGLE_TAP UINT8_C(25) +#define BMI2_DOUBLE_TAP UINT8_C(26) +#define BMI2_TRIPLE_TAP UINT8_C(27) +#define BMI2_TAP UINT8_C(28) + +/* Non virtual sensor features */ +#define BMI2_STEP_COUNTER_PARAMS UINT8_C(29) +#define BMI2_TAP_DETECTOR_1 UINT8_C(30) +#define BMI2_TAP_DETECTOR_2 UINT8_C(31) +#define BMI2_TEMP UINT8_C(32) +#define BMI2_ACCEL_SELF_TEST UINT8_C(33) +#define BMI2_GYRO_SELF_OFF UINT8_C(34) +#define BMI2_ACTIVITY_RECOGNITION UINT8_C(35) +#define BMI2_MAX_BURST_LEN UINT8_C(36) +#define BMI2_SENS_MAX_NUM UINT8_C(37) +#define BMI2_AXIS_MAP UINT8_C(38) +#define BMI2_NVM_STATUS UINT8_C(39) +#define BMI2_VFRM_STATUS UINT8_C(40) +#define BMI2_GYRO_CROSS_SENSE UINT8_C(41) +#define BMI2_CRT_GYRO_SELF_TEST UINT8_C(42) +#define BMI2_ABORT_CRT_GYRO_SELF_TEST UINT8_C(43) +#define BMI2_NVM_PROG_PREP UINT8_C(44) +#define BMI2_ACTIVITY_RECOGNITION_SETTINGS UINT8_C(45) +#define BMI2_OIS_OUTPUT UINT8_C(46) +#define BMI2_CONFIG_ID UINT8_C(47) +#define BMI2_EXT_TCO UINT8_C(48) +#define BMI2_LPD UINT8_C(49) + +#define BMI2_IMU_FUSION_QUATERION UINT8_C(50) +#define BMI2_HEAD_ORIENTATION_QUATERION UINT8_C(51) +#define BMI2_HMC_CALIBRATION_PARAMS UINT8_C(52) +#define BMI2_HMC_CORRECTION_QUATERION UINT8_C(53) + +#define BMI2_WRIST_GESTURE_DETECTOR_1 UINT8_C(54) +#define BMI2_WRIST_GESTURE_DETECTOR_2 UINT8_C(55) + +#define BMI2_WRIST_WEAR_DROP_WH UINT8_C(56) +#define BMI2_DOOR_STATE_DETECTOR UINT8_C(57) +#define BMI2_SHAKE UINT8_C(58) +#define BMI2_CIRCLE UINT8_C(59) +#define BMI2_PUSH UINT8_C(60) +#define BMI2_EXTREMUM_DETECT UINT8_C(61) +#define BMI2_IOB_DETECTOR_1 UINT8_C(62) +#define BMI2_IOB_DETECTOR_2 UINT8_C(63) +#define BMI2_IOB_PICK_UP UINT8_C(64) +#define BMI2_DOOR_STATE_DETECTOR_2 UINT8_C(65) + +/*! @name Bit wise for selecting BMI2 sensors/features */ +#define BMI2_ACCEL_SENS_SEL (1) +#define BMI2_GYRO_SENS_SEL (1 << BMI2_GYRO) +#define BMI2_AUX_SENS_SEL (1 << BMI2_AUX) +#define BMI2_TEMP_SENS_SEL ((uint64_t)1 << BMI2_TEMP) +#define BMI2_ANY_MOT_SEL (1 << BMI2_ANY_MOTION) +#define BMI2_NO_MOT_SEL (1 << BMI2_NO_MOTION) +#define BMI2_TILT_SEL (1 << BMI2_TILT) +#define BMI2_ORIENT_SEL (1 << BMI2_ORIENTATION) +#define BMI2_SIG_MOTION_SEL (1 << BMI2_SIG_MOTION) +#define BMI2_STEP_DETECT_SEL (1 << BMI2_STEP_DETECTOR) +#define BMI2_STEP_COUNT_SEL (1 << BMI2_STEP_COUNTER) +#define BMI2_STEP_ACT_SEL (1 << BMI2_STEP_ACTIVITY) +#define BMI2_GYRO_GAIN_UPDATE_SEL (1 << BMI2_GYRO_GAIN_UPDATE) +#define BMI2_UP_HOLD_TO_WAKE_SEL (1 << BMI2_UP_HOLD_TO_WAKE) +#define BMI2_GLANCE_DET_SEL (1 << BMI2_GLANCE_DETECTOR) +#define BMI2_WAKE_UP_SEL (1 << BMI2_WAKE_UP) +#define BMI2_TAP_SEL (1 << BMI2_TAP) +#define BMI2_HIGH_G_SEL (1 << BMI2_HIGH_G) +#define BMI2_LOW_G_SEL (1 << BMI2_LOW_G) +#define BMI2_FLAT_SEL (1 << BMI2_FLAT) +#define BMI2_EXT_SENS_SEL (1 << BMI2_EXT_SENS_SYNC) +#define BMI2_SINGLE_TAP_SEL (1 << BMI2_SINGLE_TAP) +#define BMI2_DOUBLE_TAP_SEL (1 << BMI2_DOUBLE_TAP) +#define BMI2_TRIPLE_TAP_SEL (1 << BMI2_TRIPLE_TAP) +#define BMI2_GYRO_SELF_OFF_SEL ((uint64_t)1 << BMI2_GYRO_SELF_OFF) +#define BMI2_WRIST_GEST_SEL (1 << BMI2_WRIST_GESTURE) +#define BMI2_WRIST_WEAR_WAKE_UP_SEL (1 << BMI2_WRIST_WEAR_WAKE_UP) +#define BMI2_ACTIVITY_RECOGNITION_SEL ((uint64_t)1 << BMI2_ACTIVITY_RECOGNITION) +#define BMI2_ACCEL_SELF_TEST_SEL ((uint64_t)1 << BMI2_ACCEL_SELF_TEST) +#define BMI2_WRIST_GEST_W_SEL (1 << BMI2_WRIST_GESTURE_WH) +#define BMI2_WRIST_WEAR_WAKE_UP_WH_SEL (1 << BMI2_WRIST_WEAR_WAKE_UP_WH) +#define BMI2_PRIMARY_OIS_SEL (1 << BMI2_PRIMARY_OIS) +#define BMI2_FREE_FALL_DET_SEL (1 << BMI2_FREE_FALL_DET) +#define BMI2_EXT_TCO_SEL ((uint64_t)1 << BMI2_EXT_TCO) +#define BMI2_LPD_SEL ((uint64_t)1 << BMI2_LPD) +#define BMI2_IOB_DETECTOR_1_SEL ((uint64_t)1 << BMI2_IOB_DETECTOR_1) +#define BMI2_WRIST_WEAR_DROP_WH_SEL ((uint64_t)1 << BMI2_WRIST_WEAR_DROP_WH) +#define BMI2_DOOR_STATE_DETECTOR_SEL ((uint64_t)1 << BMI2_DOOR_STATE_DETECTOR) +#define BMI2_SHAKE_SEL ((uint64_t)1 << BMI2_SHAKE) +#define BMI2_CIRCLE_SEL ((uint64_t)1 << BMI2_CIRCLE) +#define BMI2_PUSH_SEL ((uint64_t)1 << BMI2_PUSH) +#define BMI2_EXTREMUM_DETECT_SEL ((uint64_t)1 << BMI2_EXTREMUM_DETECT) +#define BMI2_IMU_FUSION_QUATERION_SEL ((uint64_t)1 << BMI2_IMU_FUSION_QUATERION) +#define BMI2_HEAD_ORIENTATION_QUATERION_SEL ((uint64_t)1 << BMI2_HEAD_ORIENTATION_QUATERION) +#define BMI2_HMC_CALIBRATION_PARAMS_SEL ((uint64_t)1 << BMI2_HMC_CALIBRATION_PARAMS) +#define BMI2_WRIST_GESTURE_DETECTOR_1_SEL ((uint64_t)1 << BMI2_WRIST_GESTURE_DETECTOR_1) + +/*! @name Macro definitions for Internal error */ +#define BMI2_INTERNAL_ERROR_REG_MASK UINT8_C(0xFF) +#define BMI2_INTERNAL_ERROR_1_MASK UINT8_C(0x02) +#define BMI2_INTERNAL_ERROR_2_MASK UINT8_C(0x04) +#define BMI2_FEAT_ENG_DIS_MASK UINT8_C(0x10) + +/*! @name Mask definitions for saturation register */ +#define BMI2_SATURATION_REG_MASK UINT8_C(0x3F) +#define BMI2_SATURATION_ACC_X_MASK UINT8_C(0x01) +#define BMI2_SATURATION_ACC_Y_MASK UINT8_C(0x02) +#define BMI2_SATURATION_ACC_Z_MASK UINT8_C(0x04) +#define BMI2_SATURATION_GYR_X_MASK UINT8_C(0x08) +#define BMI2_SATURATION_GYR_Y_MASK UINT8_C(0x10) +#define BMI2_SATURATION_GYR_Z_MASK UINT8_C(0x20) + +/*! @name Mask definitions for BMI2 wake-up feature configuration for bmi260 */ +#define BMI2_WAKEUP_SENSITIVITY_MASK UINT8_C(0x0E) +#define BMI2_WAKEUP_SINGLE_TAP_EN_MASK UINT8_C(0x01) +#define BMI2_WAKEUP_DOUBLE_TAP_EN_MASK UINT8_C(0x02) +#define BMI2_WAKEUP_TRIPLE_TAP_EN_MASK UINT8_C(0x04) +#define BMI2_WAKEUP_DATA_REG_EN_MASK UINT8_C(0x08) +#define BMI2_WAKEUP_AXIS_SEL_MASK UINT8_C(0x03) + +/*! @name Bit position definitions for BMI2 wake-up feature configuration for bmi260 */ +#define BMI2_WAKEUP_SENSITIVITY_POS UINT8_C(0x01) +#define BMI2_WAKEUP_DOUBLE_TAP_EN_POS UINT8_C(0x01) +#define BMI2_WAKEUP_TRIPLE_TAP_EN_POS UINT8_C(0x02) +#define BMI2_WAKEUP_DATA_REG_EN_POS UINT8_C(0x03) + +/*! @name Mask definitions for BMI2 tap feature configuration for bmi260t */ +#define BMI2_TAP_SENSITIVITY_MASK UINT8_C(0x0E) +#define BMI2_TAP_SINGLE_TAP_EN_MASK UINT8_C(0x01) +#define BMI2_TAP_DOUBLE_TAP_EN_MASK UINT8_C(0x02) +#define BMI2_TAP_TRIPLE_TAP_EN_MASK UINT8_C(0x04) +#define BMI2_TAP_DATA_REG_EN_MASK UINT8_C(0x08) +#define BMI2_TAP_AXIS_SEL_MASK UINT8_C(0x03) + +/*! @name Bit position definitions for BMI2 tap feature configuration for bmi260t */ +#define BMI2_TAP_SENSITIVITY_POS UINT8_C(0x01) +#define BMI2_TAP_DOUBLE_TAP_EN_POS UINT8_C(0x01) +#define BMI2_TAP_TRIPLE_TAP_EN_POS UINT8_C(0x02) +#define BMI2_TAP_DATA_REG_EN_POS UINT8_C(0x03) + +/*! @name Mask definitions for BMI2 wake-up feature configuration for other than bmi261 */ +#define BMI2_WAKE_UP_SENSITIVITY_MASK UINT16_C(0x000E) +#define BMI2_WAKE_UP_SINGLE_TAP_EN_MASK UINT16_C(0x0010) + +/*! @name Bit position definitions for BMI2 wake-up feature configuration for other than bmi261 */ +#define BMI2_WAKE_UP_SENSITIVITY_POS UINT8_C(0x01) +#define BMI2_WAKE_UP_SINGLE_TAP_EN_POS UINT8_C(0x04) + +/*! @name Offsets from feature start address for BMI2 feature enable/disable */ +#define BMI2_ANY_MOT_FEAT_EN_OFFSET UINT8_C(0x03) +#define BMI2_NO_MOT_FEAT_EN_OFFSET UINT8_C(0x03) +#define BMI2_SIG_MOT_FEAT_EN_OFFSET UINT8_C(0x0A) +#define BMI2_STEP_COUNT_FEAT_EN_OFFSET UINT8_C(0x01) +#define BMI2_GYR_USER_GAIN_FEAT_EN_OFFSET UINT8_C(0x05) +#define BMI2_HIGH_G_FEAT_EN_OFFSET UINT8_C(0x03) +#define BMI2_LOW_G_FEAT_EN_OFFSET UINT8_C(0x03) +#define BMI2_TILT_FEAT_EN_OFFSET UINT8_C(0x00) +#define BMI2_SHAKE_FEAT_EN_OFFSET UINT8_C(0x02) +#define BMI2_CIRCLE_FEAT_EN_OFFSET UINT8_C(0x02) +#define BMI2_PUSH_FEAT_EN_OFFSET UINT8_C(0x02) +#define BMI2_IMU_FUS_FEAT_EN_OFFSET UINT8_C(0x00) +#define BMI2_HEAD_ORIENT_FEAT_EN_OFFSET UINT8_C(0x00) + +/*! @name Mask definitions for BMI2 feature enable/disable */ +#define BMI2_ANY_NO_MOT_EN_MASK UINT8_C(0x80) +#define BMI2_TILT_FEAT_EN_MASK UINT8_C(0x01) +#define BMI2_ORIENT_FEAT_EN_MASK UINT8_C(0x01) +#define BMI2_SIG_MOT_FEAT_EN_MASK UINT8_C(0x01) +#define BMI2_STEP_DET_FEAT_EN_MASK UINT8_C(0x08) +#define BMI2_STEP_COUNT_FEAT_EN_MASK UINT8_C(0x10) +#define BMI2_STEP_ACT_FEAT_EN_MASK UINT8_C(0x20) +#define BMI2_GYR_USER_GAIN_FEAT_EN_MASK UINT8_C(0x08) +#define BMI2_UP_HOLD_TO_WAKE_FEAT_EN_MASK UINT8_C(0x01) +#define BMI2_GLANCE_FEAT_EN_MASK UINT8_C(0x01) +#define BMI2_WAKE_UP_FEAT_EN_MASK UINT8_C(0x01) +#define BMI2_TAP_FEAT_EN_MASK UINT8_C(0x01) +#define BMI2_HIGH_G_FEAT_EN_MASK UINT8_C(0x80) +#define BMI2_LOW_G_FEAT_EN_MASK UINT8_C(0x10) +#define BMI2_FLAT_FEAT_EN_MASK UINT8_C(0x01) +#define BMI2_EXT_SENS_SYNC_FEAT_EN_MASK UINT8_C(0x01) +#define BMI2_GYR_SELF_OFF_CORR_FEAT_EN_MASK UINT8_C(0x02) +#define BMI2_WRIST_GEST_FEAT_EN_MASK UINT8_C(0x20) +#define BMI2_WRIST_WEAR_WAKE_UP_FEAT_EN_MASK UINT8_C(0x10) +#define BMI2_WRIST_WEAR_DROP_FEAT_EN_MASK UINT8_C(0x20) +#define BMI2_ACTIVITY_RECOG_EN_MASK UINT8_C(0x01) +#define BMI2_ACC_SELF_TEST_FEAT_EN_MASK UINT8_C(0x02) +#define BMI2_GYRO_SELF_TEST_CRT_EN_MASK UINT8_C(0x01) +#define BMI2_ABORT_FEATURE_EN_MASK UINT8_C(0x02) +#define BMI2_NVM_PREP_FEATURE_EN_MASK UINT8_C(0x04) +#define BMI2_FREE_FALL_DET_FEAT_EN_MASK UINT8_C(0x01) +#define BMI2_WRIST_GEST_WH_FEAT_EN_MASK UINT8_C(0x02) +#define BMI2_DOOR_STATE_DETECTOR_FEAT_EN_MASK UINT8_C(0x01) +#define BMI2_SHAKE_FEAT_EN_MASK UINT8_C(0x10) +#define BMI2_CIRCLE_FEAT_EN_MASK UINT8_C(0x10) +#define BMI2_PUSH_FEAT_EN_MASK UINT8_C(0x10) +#define BMI2_IMU_FUS_FEAT_EN_MASK UINT8_C(0x01) +#define BMI2_HEAD_ORIENT_FEAT_EN_MASK UINT8_C(0X01) + +/*! @name Bit position definitions for BMI2 feature enable/disable */ +#define BMI2_ANY_NO_MOT_EN_POS UINT8_C(0x07) +#define BMI2_STEP_DET_FEAT_EN_POS UINT8_C(0x03) +#define BMI2_STEP_COUNT_FEAT_EN_POS UINT8_C(0x04) +#define BMI2_STEP_ACT_FEAT_EN_POS UINT8_C(0x05) +#define BMI2_GYR_USER_GAIN_FEAT_EN_POS UINT8_C(0x03) +#define BMI2_HIGH_G_FEAT_EN_POS UINT8_C(0x07) +#define BMI2_LOW_G_FEAT_EN_POS UINT8_C(0x04) +#define BMI2_GYR_SELF_OFF_CORR_FEAT_EN_POS UINT8_C(0x01) +#define BMI2_WRIST_GEST_FEAT_EN_POS UINT8_C(0x05) +#define BMI2_WRIST_WEAR_WAKE_UP_FEAT_EN_POS UINT8_C(0x04) +#define BMI2_WRIST_WEAR_DROP_FEAT_EN_POS UINT8_C(0x05) +#define BMI2_ACC_SELF_TEST_FEAT_EN_POS UINT8_C(0x01) +#define BMI2_ABORT_FEATURE_EN_POS UINT8_C(0x1) +#define BMI2_NVM_PREP_FEATURE_EN_POS UINT8_C(0x02) +#define BMI2_WRIST_GEST_WH_FEAT_EN_POS UINT8_C(0x01) +#define BMI2_SHAKE_FEAT_EN_POS UINT8_C(0x04) +#define BMI2_CIRCLE_FEAT_EN_POS UINT8_C(0x04) +#define BMI2_PUSH_FEAT_EN_POS UINT8_C(0x04) + +/*! @name Bit position and mask definitions for BMI2 Error register */ +#define BMI2_ERR_REG_READ_MASK UINT8_C(0xFF) +#define BMI2_ERR_REG_READ_POS UINT8_C(0x00) + +#define BMI2_FATAL_ERR_MASK UINT8_C(0x01) +#define BMI2_FATAL_ERR_POS UINT8_C(0x00) + +#define BMI2_INTERNAL_ERR_MASK UINT8_C(0x1E) +#define BMI2_INTERNAL_ERR_POS UINT8_C(0x02) + +#define BMI2_FIFO_ERR_MASK UINT8_C(0x40) +#define BMI2_FIFO_ERR_POS UINT8_C(0x06) + +#define BMI2_AUX_ERR_MASK UINT8_C(0x80) +#define BMI2_AUX_ERR_POS UINT8_C(0x07) + +/*! Primary OIS low pass filter configuration position and mask */ +#define BMI2_LP_FILTER_EN_MASK UINT8_C(0x01) + +#define BMI2_LP_FILTER_CONFIG_POS UINT8_C(0x01) +#define BMI2_LP_FILTER_CONFIG_MASK UINT8_C(0x06) + +#define BMI2_PRIMARY_OIS_GYR_EN_POS UINT8_C(0x06) +#define BMI2_PRIMARY_OIS_GYR_EN_MASK UINT8_C(0x40) + +#define BMI2_PRIMARY_OIS_ACC_EN_POS UINT8_C(0x07) +#define BMI2_PRIMARY_OIS_ACC_EN_MASK UINT8_C(0x80) + +/*! @name Mask definitions for BMI2 any and no-motion feature configuration */ +#define BMI2_ANY_NO_MOT_DUR_MASK UINT16_C(0x1FFF) +#define BMI2_ANY_NO_MOT_X_SEL_MASK UINT16_C(0x2000) +#define BMI2_ANY_NO_MOT_Y_SEL_MASK UINT16_C(0x4000) +#define BMI2_ANY_NO_MOT_Z_SEL_MASK UINT16_C(0x8000) +#define BMI2_ANY_NO_MOT_THRES_MASK UINT16_C(0x07FF) +#define BMI2_ANY_MOT_INT_MASK UINT8_C(0x40) + +/*! @name Mask definitions for BMI2 no-motion interrupt mapping */ +#define BMI2_NO_MOT_INT_MASK UINT8_C(0x20) + +/*! @name Bit position definitions for BMI2 any and no-motion feature + * configuration + */ +#define BMI2_ANY_NO_MOT_X_SEL_POS UINT8_C(0x0D) +#define BMI2_ANY_NO_MOT_Y_SEL_POS UINT8_C(0x0E) +#define BMI2_ANY_NO_MOT_Z_SEL_POS UINT8_C(0x0F) + +/*! @name Mask definitions for BMI2 orientation feature configuration */ +#define BMI2_ORIENT_UP_DOWN_MASK UINT16_C(0x0002) +#define BMI2_ORIENT_SYMM_MODE_MASK UINT16_C(0x000C) +#define BMI2_ORIENT_BLOCK_MODE_MASK UINT16_C(0x0030) +#define BMI2_ORIENT_THETA_MASK UINT16_C(0x0FC0) +#define BMI2_ORIENT_HYST_MASK UINT16_C(0x07FF) + +/*! @name Bit position definitions for BMI2 orientation feature configuration */ +#define BMI2_ORIENT_UP_DOWN_POS UINT8_C(0x01) +#define BMI2_ORIENT_SYMM_MODE_POS UINT8_C(0x02) +#define BMI2_ORIENT_BLOCK_MODE_POS UINT8_C(0x04) +#define BMI2_ORIENT_THETA_POS UINT8_C(0x06) + +/*! @name Mask definitions for BMI2 sig-motion feature configuration */ +#define BMI2_SIG_MOT_PARAM_1_MASK UINT16_C(0xFFFF) +#define BMI2_SIG_MOT_PARAM_2_MASK UINT16_C(0xFFFF) +#define BMI2_SIG_MOT_PARAM_3_MASK UINT16_C(0xFFFF) +#define BMI2_SIG_MOT_PARAM_4_MASK UINT16_C(0xFFFF) +#define BMI2_SIG_MOT_PARAM_5_MASK UINT16_C(0xFFFF) + +/*! @name Mask definitions for BMI2 parameter configurations */ +#define BMI2_STEP_COUNT_PARAMS_MASK UINT16_C(0xFFFF) + +/*! @name Mask definitions for BMI2 step-counter/detector feature configuration */ +#define BMI2_STEP_COUNT_WM_LEVEL_MASK UINT16_C(0x03FF) +#define BMI2_STEP_COUNT_RST_CNT_MASK UINT16_C(0x0400) +#define BMI2_STEP_BUFFER_SIZE_MASK UINT16_C(0xFF00) +#define BMI2_STEP_COUNT_INT_MASK UINT8_C(0x02) +#define BMI2_STEP_ACT_INT_MASK UINT8_C(0x04) + +/*! @name Bit position definitions for BMI2 step-counter/detector feature + * configuration + */ +#define BMI2_STEP_COUNT_RST_CNT_POS UINT8_C(0x0A) +#define BMI2_STEP_BUFFER_SIZE_POS UINT8_C(0x08) + +/*! @name Mask definitions for BMI2 gyroscope user gain feature + * configuration + */ +#define BMI2_GYR_USER_GAIN_RATIO_X_MASK UINT16_C(0x07FF) +#define BMI2_GYR_USER_GAIN_RATIO_Y_MASK UINT16_C(0x07FF) +#define BMI2_GYR_USER_GAIN_RATIO_Z_MASK UINT16_C(0x07FF) + +/*! @name Mask definitions for BMI2 gyroscope user gain saturation status */ +#define BMI2_GYR_USER_GAIN_SAT_STAT_X_MASK UINT8_C(0x01) +#define BMI2_GYR_USER_GAIN_SAT_STAT_Y_MASK UINT8_C(0x02) +#define BMI2_GYR_USER_GAIN_SAT_STAT_Z_MASK UINT8_C(0x04) +#define BMI2_G_TRIGGER_STAT_MASK UINT8_C(0x38) + +/*! @name Bit position definitions for BMI2 gyroscope user gain saturation status */ +#define BMI2_GYR_USER_GAIN_SAT_STAT_Y_POS UINT8_C(0x01) +#define BMI2_GYR_USER_GAIN_SAT_STAT_Z_POS UINT8_C(0x02) +#define BMI2_G_TRIGGER_STAT_POS UINT8_C(0x03) + +/*! @name Mask definitions for MSB values of BMI2 gyroscope compensation */ +#define BMI2_GYR_OFF_COMP_MSB_X_MASK UINT8_C(0x03) +#define BMI2_GYR_OFF_COMP_MSB_Y_MASK UINT8_C(0x0C) +#define BMI2_GYR_OFF_COMP_MSB_Z_MASK UINT8_C(0x30) + +/*! @name Bit positions for MSB values of BMI2 gyroscope compensation */ +#define BMI2_GYR_OFF_COMP_MSB_Y_POS UINT8_C(0x02) +#define BMI2_GYR_OFF_COMP_MSB_Z_POS UINT8_C(0x04) + +/*! @name Mask definitions for MSB values of BMI2 gyroscope compensation from user input */ +#define BMI2_GYR_OFF_COMP_MSB_MASK UINT16_C(0x0300) +#define BMI2_GYR_OFF_COMP_LSB_MASK UINT16_C(0x00FF) + +/*! @name Mask definitions for BMI2 orientation status */ +#define BMI2_ORIENT_DETECT_MASK UINT8_C(0x03) +#define BMI2_ORIENT_FACE_UP_DWN_MASK UINT8_C(0x04) + +/*! @name Bit position definitions for BMI2 orientation status */ +#define BMI2_ORIENT_FACE_UP_DWN_POS UINT8_C(0x02) + +/*! @name Mask definitions for NVM-VFRM error status */ +#define BMI2_NVM_LOAD_ERR_STATUS_MASK UINT8_C(0x01) +#define BMI2_NVM_PROG_ERR_STATUS_MASK UINT8_C(0x02) +#define BMI2_NVM_ERASE_ERR_STATUS_MASK UINT8_C(0x04) +#define BMI2_NVM_END_EXCEED_STATUS_MASK UINT8_C(0x08) +#define BMI2_NVM_PRIV_ERR_STATUS_MASK UINT8_C(0x10) +#define BMI2_VFRM_LOCK_ERR_STATUS_MASK UINT8_C(0x20) +#define BMI2_VFRM_WRITE_ERR_STATUS_MASK UINT8_C(0x40) +#define BMI2_VFRM_FATAL_ERR_STATUS_MASK UINT8_C(0x80) + +/*! @name Bit positions for NVM-VFRM error status */ +#define BMI2_NVM_PROG_ERR_STATUS_POS UINT8_C(0x01) +#define BMI2_NVM_ERASE_ERR_STATUS_POS UINT8_C(0x02) +#define BMI2_NVM_END_EXCEED_STATUS_POS UINT8_C(0x03) +#define BMI2_NVM_PRIV_ERR_STATUS_POS UINT8_C(0x04) +#define BMI2_VFRM_LOCK_ERR_STATUS_POS UINT8_C(0x05) +#define BMI2_VFRM_WRITE_ERR_STATUS_POS UINT8_C(0x06) +#define BMI2_VFRM_FATAL_ERR_STATUS_POS UINT8_C(0x07) + +/*! @name Mask definitions for accelerometer self-test status */ +#define BMI2_ACC_SELF_TEST_DONE_MASK UINT8_C(0x01) +#define BMI2_ACC_X_OK_MASK UINT8_C(0x02) +#define BMI2_ACC_Y_OK_MASK UINT8_C(0x04) +#define BMI2_ACC_Z_OK_MASK UINT8_C(0x08) + +/*! @name Bit Positions for accelerometer self-test status */ +#define BMI2_ACC_X_OK_POS UINT8_C(0x01) +#define BMI2_ACC_Y_OK_POS UINT8_C(0x02) +#define BMI2_ACC_Z_OK_POS UINT8_C(0x03) + +/*! @name Mask definitions for BMI2 high-g feature configuration */ +#define BMI2_HIGH_G_THRES_MASK UINT16_C(0x7FFF) +#define BMI2_HIGH_G_HYST_MASK UINT16_C(0x0FFF) +#define BMI2_HIGH_G_X_SEL_MASK UINT16_C(0x1000) +#define BMI2_HIGH_G_Y_SEL_MASK UINT16_C(0x2000) +#define BMI2_HIGH_G_Z_SEL_MASK UINT16_C(0x4000) +#define BMI2_HIGH_G_DUR_MASK UINT16_C(0x0FFF) + +/*! @name Bit position definitions for BMI2 high-g feature configuration */ +#define BMI2_HIGH_G_X_SEL_POS UINT8_C(0x0C) +#define BMI2_HIGH_G_Y_SEL_POS UINT8_C(0x0D) +#define BMI2_HIGH_G_Z_SEL_POS UINT8_C(0x0E) + +/*! @name Mask definitions for BMI2 low-g feature configuration */ +#define BMI2_LOW_G_THRES_MASK UINT16_C(0x7FFF) +#define BMI2_LOW_G_HYST_MASK UINT16_C(0x0FFF) +#define BMI2_LOW_G_DUR_MASK UINT16_C(0x0FFF) + +/*! @name Mask definitions for BMI2 free-fall detection feature configuration */ +#define BMI2_FREE_FALL_ACCEL_SETT_MASK UINT16_C(0xFFFF) + +/*! @name Mask definitions for BMI2 flat feature configuration */ +#define BMI2_FLAT_THETA_MASK UINT16_C(0x007E) +#define BMI2_FLAT_BLOCK_MASK UINT16_C(0x0180) +#define BMI2_FLAT_HYST_MASK UINT16_C(0x003F) +#define BMI2_FLAT_HOLD_TIME_MASK UINT16_C(0x3FC0) + +/*! @name Bit position definitions for BMI2 flat feature configuration */ +#define BMI2_FLAT_THETA_POS UINT8_C(0x01) +#define BMI2_FLAT_BLOCK_POS UINT8_C(0x07) +#define BMI2_FLAT_HOLD_TIME_POS UINT8_C(0x06) + +/*! @name Mask definitions for BMI2 wrist gesture configuration */ +#define BMI2_WRIST_GEST_WEAR_ARM_MASK UINT16_C(0x0010) + +/*! @name Bit position definitions for wrist gesture configuration */ +#define BMI2_WRIST_GEST_WEAR_ARM_POS UINT8_C(0x04) + +/*! @name Mask definitions for BMI2 wrist gesture wh configuration */ +#define BMI2_WRIST_GEST_WH_DEVICE_POS_MASK UINT16_C(0x0001) +#define BMI2_WRIST_GEST_WH_INT UINT8_C(0x10) +#define BMI2_WRIST_GEST_WH_START_ADD UINT8_C(0x08) + +/*! @name Mask definitions for BMI2 wrist wear wake-up configuration */ +#define BMI2_WRIST_WAKE_UP_WH_INT_MASK UINT8_C(0x08) + +/*! @name Mask definition for BMI2 wrist wear wake-up configuration for wearable variant */ +#define BMI2_WRIST_WAKE_UP_ANGLE_LR_MASK UINT16_C(0x00FF) +#define BMI2_WRIST_WAKE_UP_ANGLE_LL_MASK UINT16_C(0xFF00) +#define BMI2_WRIST_WAKE_UP_ANGLE_PD_MASK UINT16_C(0x00FF) +#define BMI2_WRIST_WAKE_UP_ANGLE_PU_MASK UINT16_C(0xFF00) +#define BMI2_WRIST_WAKE_UP_MIN_DUR_MOVED_MASK UINT16_C(0x00FF) +#define BMI2_WRIST_WAKE_UP_MIN_DUR_QUITE_MASK UINT16_C(0xFF00) +#define BMI2_WRIST_WAKE_UP_MIN_DROP_POS_DUR_MASK UINT16_C(0x00FF) +#define BMI2_WRIST_WAKE_UP_MIN_DROP_POS_DUR_LOC_MASK UINT16_C(0xFF00) + +/*! @name Bit position definition for BMI2 wrist wear wake-up configuration for wearable variant */ +#define BMI2_WRIST_WAKE_UP_ANGLE_LL_POS UINT16_C(0x0008) +#define BMI2_WRIST_WAKE_UP_ANGLE_PU_POS UINT16_C(0x0008) +#define BMI2_WRIST_WAKE_UP_MIN_DUR_QUITE_POS UINT16_C(0x0008) +#define BMI2_WRIST_WAKE_UP_MIN_DROP_POS_DUR_LOC_POS UINT16_C(0x0008) + +/*! @name Mask definition for BMI2 shake configuration for pen variant */ +#define BMI2_SHAKE_GEST_SHAKE_NUM_MASK UINT8_C(0x07) +#define BMI2_SHAKE_GEST_THRESHOLD_MASK UINT8_C(0xF8) + +/*! @name Bit position definition for BMI2 shake configuration for pen variant */ +#define BMI2_SHAKE_GEST_THRESHOLD_POS UINT8_C(0x03) + +/*! @name Mask definition for BMI2 circle configuration for pen variant */ +#define BMI2_CIRCLE_UPPER_THRESHOLD_MASK UINT16_C(0x00FF) +#define BMI2_CIRCLE_LOWER_THRESHOLD_MASK UINT16_C(0x1F00) +#define BMI2_CIRCLE_MIN_DISTANCE_MASK UINT16_C(0x6000) + +/*! @name Bit position definition for BMI2 circle configuration for pen variant */ +#define BMI2_CIRCLE_LOWER_THRESHOLD_POS UINT8_C(0x08) +#define BMI2_CIRCLE_MIN_DISTANCE_POS UINT8_C(0x0D) + +/*! @name Mask definition for BMI2 push configuration for pen variant */ +#define BMI2_PUSH_LOW_THRESHOLD_MASK UINT16_C(0x00FF) +#define BMI2_PUSH_HIGH_THRESHOLD_MASK UINT16_C(0xFF00) + +/*! @name Bit position definition for BMI2 push configuration for pen variant */ +#define BMI2_PUSH_HIGH_THRESHOLD_POS UINT8_C(0x08) + +/*! @name Mast definnition for BMI2 extremum detection for pen variant */ +#define BMI2_EXTREMUM_IDLE_THRESHOLD_MASK UINT16_C(0x00FF) +#define BMI2_EXTREMUM_DETECT_THRESHOLD_MASK UINT16_C(0xFF00) + +/*! @name Bit position definition for BMI2 extremum detection for pen variant */ +#define BMI2_EXTREMUM_DETECT_THRESHOLD_POS UINT8_C(0x08) + +/*! @name Mask definition for BMI2 EXT TCO configuration */ +#define BMI2_EXT_TCO_MASK UINT8_C(0x01) + +/*! @name Macros to define values of BMI2 axis and its sign for re-map + * settings + */ +#define BMI2_MAP_X_AXIS UINT8_C(0x00) +#define BMI2_MAP_Y_AXIS UINT8_C(0x01) +#define BMI2_MAP_Z_AXIS UINT8_C(0x02) +#define BMI2_MAP_POSITIVE UINT8_C(0x00) +#define BMI2_MAP_NEGATIVE UINT8_C(0x01) + +/*! @name Mask definitions of BMI2 axis re-mapping */ +#define BMI2_X_AXIS_MASK UINT8_C(0x03) +#define BMI2_X_AXIS_SIGN_MASK UINT8_C(0x04) +#define BMI2_Y_AXIS_MASK UINT8_C(0x18) +#define BMI2_Y_AXIS_SIGN_MASK UINT8_C(0x20) +#define BMI2_Z_AXIS_MASK UINT8_C(0xC0) +#define BMI2_Z_AXIS_SIGN_MASK UINT8_C(0x01) + +/*! @name Bit position definitions of BMI2 axis re-mapping */ +#define BMI2_X_AXIS_SIGN_POS UINT8_C(0x02) +#define BMI2_Y_AXIS_POS UINT8_C(0x03) +#define BMI2_Y_AXIS_SIGN_POS UINT8_C(0x05) +#define BMI2_Z_AXIS_POS UINT8_C(0x06) + +/*! @name Mask definitions of BMI2 virtual frame */ +#define BMI2_EIS_VFRM_DATA_MASK UINT8_C(0x08) + +/*! @name Bit position definitions of BMI2 virtual frame */ +#define BMI2_EIS_VFRM_DATA_POS UINT8_C(3) + +/*! @name Macro to define virtual frame length */ +#define BMI2_VIRTUAL_FRAME_LEN UINT8_C(19) + +/*! @name Macros to define polarity */ +#define BMI2_NEG_SIGN UINT8_C(1) +#define BMI2_POS_SIGN UINT8_C(0) + +/*! @name Macro to define related to CRT */ +#define BMI2_CRT_READY_FOR_DOWNLOAD_US UINT16_C(2000) +#define BMI2_CRT_READY_FOR_DOWNLOAD_RETRY UINT8_C(100) + +#define BMI2_CRT_WAIT_RUNNING_US UINT16_C(10000) +#define BMI2_CRT_WAIT_RUNNING_RETRY_EXECUTION UINT8_C(200) + +#define BMI2_CRT_MIN_BURST_WORD_LENGTH UINT8_C(2) +#define BMI2_CRT_MAX_BURST_WORD_LENGTH UINT16_C(255) + +/* Reference value with positive and negative noise range in lsb */ + +/* + * For Gyro FOC, axes values after FOC must be 0 +/- 1 dps + * + * In 2000 dps, 1 dps is 16.384 (~16) + * In 1000 dps, 1 dps is 32.768 (~33) + * In 500 dps, 1 dps is 65.536 (~66) + * In 250 dps, 1 dps is 131.072 (~131) + * In 125 dps, 1 dps is 262.144 (~262) + */ +#define BMI2_GYRO_FOC_2000_DPS_REF UINT16_C(16) +#define BMI2_GYRO_FOC_1000_DPS_REF UINT16_C(33) +#define BMI2_GYRO_FOC_500_DPS_REF UINT16_C(66) +#define BMI2_GYRO_FOC_250_DPS_REF UINT16_C(131) +#define BMI2_GYRO_FOC_125_DPS_REF UINT16_C(262) + +/* Reference value with positive and negative noise range in lsb */ + +/* + * As per datasheet, Zero-g offset : +/- 20mg + * + * In range 2G, 1G is 16384. so, 16384 x 20 x (10 ^ -3) = 328 + * In range 4G, 1G is 8192. so, 8192 x 20 x (10 ^ -3) = 164 + * In range 8G, 1G is 4096. so, 4096 x 20 x (10 ^ -3) = 82 + * In range 16G, 1G is 2048. so, 2048 x 20 x (10 ^ -3) = 41 + */ +#define BMI2_ACC_FOC_2G_REF UINT16_C(16384) +#define BMI2_ACC_FOC_4G_REF UINT16_C(8192) +#define BMI2_ACC_FOC_8G_REF UINT16_C(4096) +#define BMI2_ACC_FOC_16G_REF UINT16_C(2048) + +#define BMI2_ACC_FOC_2G_OFFSET UINT16_C(328) +#define BMI2_ACC_FOC_4G_OFFSET UINT16_C(164) +#define BMI2_ACC_FOC_8G_OFFSET UINT16_C(82) +#define BMI2_ACC_FOC_16G_OFFSET UINT16_C(41) + +#define BMI2_FOC_SAMPLE_LIMIT UINT8_C(128) + +#define BMI2_ACC_2G_MAX_NOISE_LIMIT (BMI2_ACC_FOC_2G_REF + BMI2_ACC_FOC_2G_OFFSET) +#define BMI2_ACC_2G_MIN_NOISE_LIMIT (BMI2_ACC_FOC_2G_REF - BMI2_ACC_FOC_2G_OFFSET) +#define BMI2_ACC_4G_MAX_NOISE_LIMIT (BMI2_ACC_FOC_4G_REF + BMI2_ACC_FOC_4G_OFFSET) +#define BMI2_ACC_4G_MIN_NOISE_LIMIT (BMI2_ACC_FOC_4G_REF - BMI2_ACC_FOC_4G_OFFSET) +#define BMI2_ACC_8G_MAX_NOISE_LIMIT (BMI2_ACC_FOC_8G_REF + BMI2_ACC_FOC_8G_OFFSET) +#define BMI2_ACC_8G_MIN_NOISE_LIMIT (BMI2_ACC_FOC_8G_REF - BMI2_ACC_FOC_8G_OFFSET) +#define BMI2_ACC_16G_MAX_NOISE_LIMIT (BMI2_ACC_FOC_16G_REF + BMI2_ACC_FOC_16G_OFFSET) +#define BMI2_ACC_16G_MIN_NOISE_LIMIT (BMI2_ACC_FOC_16G_REF - BMI2_ACC_FOC_16G_OFFSET) + +/*! @name Bit wise selection of BMI2 sensors */ +#define BMI2_MAIN_SENSORS \ + (BMI2_ACCEL_SENS_SEL | BMI2_GYRO_SENS_SEL | BMI2_AUX_SENS_SEL | BMI2_TEMP_SENS_SEL) + +/*! @name Maximum number of BMI2 main sensors */ +#define BMI2_MAIN_SENS_MAX_NUM UINT8_C(4) + +/*! @name Macro to specify the number of step counter parameters */ +#define BMI2_STEP_CNT_N_PARAMS UINT8_C(25) + +/*! @name Macro to specify the number of free-fall accel setting parameters */ +#define BMI2_FREE_FALL_ACCEL_SET_PARAMS UINT8_C(7) + +#define BMI2_SELECT_GYRO_SELF_TEST UINT8_C(0) +#define BMI2_SELECT_CRT UINT8_C(1) + +/*! @name Macro for NVM enable */ +#define BMI2_NVM_UNLOCK_ENABLE UINT8_C(0x02) +#define BMI2_NVM_UNLOCK_DISABLE UINT8_C(0x00) + +/*! @name macro to select between gyro self test and CRT */ +#define BMI2_GYRO_SELF_TEST_SEL UINT8_C(0) +#define BMI2_CRT_SEL UINT8_C(1) + +/******************************************************************************/ +/*! @name Accelerometer Macro Definitions */ +/******************************************************************************/ +/*! @name Accelerometer Bandwidth parameters */ +#define BMI2_ACC_OSR4_AVG1 UINT8_C(0x00) +#define BMI2_ACC_OSR2_AVG2 UINT8_C(0x01) +#define BMI2_ACC_NORMAL_AVG4 UINT8_C(0x02) +#define BMI2_ACC_CIC_AVG8 UINT8_C(0x03) +#define BMI2_ACC_RES_AVG16 UINT8_C(0x04) +#define BMI2_ACC_RES_AVG32 UINT8_C(0x05) +#define BMI2_ACC_RES_AVG64 UINT8_C(0x06) +#define BMI2_ACC_RES_AVG128 UINT8_C(0x07) + +/*! @name Accelerometer Output Data Rate */ +#define BMI2_ACC_ODR_0_78HZ UINT8_C(0x01) +#define BMI2_ACC_ODR_1_56HZ UINT8_C(0x02) +#define BMI2_ACC_ODR_3_12HZ UINT8_C(0x03) +#define BMI2_ACC_ODR_6_25HZ UINT8_C(0x04) +#define BMI2_ACC_ODR_12_5HZ UINT8_C(0x05) +#define BMI2_ACC_ODR_25HZ UINT8_C(0x06) +#define BMI2_ACC_ODR_50HZ UINT8_C(0x07) +#define BMI2_ACC_ODR_100HZ UINT8_C(0x08) +#define BMI2_ACC_ODR_200HZ UINT8_C(0x09) +#define BMI2_ACC_ODR_400HZ UINT8_C(0x0A) +#define BMI2_ACC_ODR_800HZ UINT8_C(0x0B) +#define BMI2_ACC_ODR_1600HZ UINT8_C(0x0C) + +/*! @name Accelerometer G Range */ +#define BMI2_ACC_RANGE_2G UINT8_C(0x00) +#define BMI2_ACC_RANGE_4G UINT8_C(0x01) +#define BMI2_ACC_RANGE_8G UINT8_C(0x02) +#define BMI2_ACC_RANGE_16G UINT8_C(0x03) + +/*! @name Mask definitions for accelerometer configuration register */ +#define BMI2_ACC_RANGE_MASK UINT8_C(0x03) +#define BMI2_ACC_ODR_MASK UINT8_C(0x0F) +#define BMI2_ACC_BW_PARAM_MASK UINT8_C(0x70) +#define BMI2_ACC_FILTER_PERF_MODE_MASK UINT8_C(0x80) + +/*! @name Bit position definitions for accelerometer configuration register */ +#define BMI2_ACC_BW_PARAM_POS UINT8_C(0x04) +#define BMI2_ACC_FILTER_PERF_MODE_POS UINT8_C(0x07) + +/*! @name Self test macro to define range */ +#define BMI2_ACC_SELF_TEST_RANGE UINT8_C(16) + +/*! @name Self test macro to show resulting minimum and maximum difference + * signal of the axes in mg + */ +#define BMI2_ST_ACC_X_SIG_MIN_DIFF INT16_C(16000) +#define BMI2_ST_ACC_Y_SIG_MIN_DIFF INT16_C(-15000) +#define BMI2_ST_ACC_Z_SIG_MIN_DIFF INT16_C(10000) + +/*! @name Mask definitions for accelerometer self-test */ +#define BMI2_ACC_SELF_TEST_EN_MASK UINT8_C(0x01) +#define BMI2_ACC_SELF_TEST_SIGN_MASK UINT8_C(0x04) +#define BMI2_ACC_SELF_TEST_AMP_MASK UINT8_C(0x08) + +/*! @name Bit Positions for accelerometer self-test */ +#define BMI2_ACC_SELF_TEST_SIGN_POS UINT8_C(0x02) +#define BMI2_ACC_SELF_TEST_AMP_POS UINT8_C(0x03) + +/*! @name MASK definition for gyro self test status */ +#define BMI2_GYR_ST_AXES_DONE_MASK UINT8_C(0x01) +#define BMI2_GYR_AXIS_X_OK_MASK UINT8_C(0x02) +#define BMI2_GYR_AXIS_Y_OK_MASK UINT8_C(0x04) +#define BMI2_GYR_AXIS_Z_OK_MASK UINT8_C(0x08) + +/*! @name Bit position for gyro self test status */ +#define BMI2_GYR_AXIS_X_OK_POS UINT8_C(0x01) +#define BMI2_GYR_AXIS_Y_OK_POS UINT8_C(0x02) +#define BMI2_GYR_AXIS_Z_OK_POS UINT8_C(0x03) + +/******************************************************************************/ +/*! @name Gyroscope Macro Definitions */ +/******************************************************************************/ +/*! @name Gyroscope Bandwidth parameters */ +#define BMI2_GYR_OSR4_MODE UINT8_C(0x00) +#define BMI2_GYR_OSR2_MODE UINT8_C(0x01) +#define BMI2_GYR_NORMAL_MODE UINT8_C(0x02) + +/*! @name Gyroscope Output Data Rate */ +#define BMI2_GYR_ODR_25HZ UINT8_C(0x06) +#define BMI2_GYR_ODR_50HZ UINT8_C(0x07) +#define BMI2_GYR_ODR_100HZ UINT8_C(0x08) +#define BMI2_GYR_ODR_200HZ UINT8_C(0x09) +#define BMI2_GYR_ODR_400HZ UINT8_C(0x0A) +#define BMI2_GYR_ODR_800HZ UINT8_C(0x0B) +#define BMI2_GYR_ODR_1600HZ UINT8_C(0x0C) +#define BMI2_GYR_ODR_3200HZ UINT8_C(0x0D) + +/*! @name Gyroscope OIS Range */ +#define BMI2_GYR_OIS_250 UINT8_C(0x00) +#define BMI2_GYR_OIS_2000 UINT8_C(0x01) + +/*! @name Gyroscope Angular Rate Measurement Range */ +#define BMI2_GYR_RANGE_2000 UINT8_C(0x00) +#define BMI2_GYR_RANGE_1000 UINT8_C(0x01) +#define BMI2_GYR_RANGE_500 UINT8_C(0x02) +#define BMI2_GYR_RANGE_250 UINT8_C(0x03) +#define BMI2_GYR_RANGE_125 UINT8_C(0x04) + +/*! @name Mask definitions for gyroscope configuration register */ +#define BMI2_GYR_RANGE_MASK UINT8_C(0x07) +#define BMI2_GYR_OIS_RANGE_MASK UINT8_C(0x08) +#define BMI2_GYR_ODR_MASK UINT8_C(0x0F) +#define BMI2_GYR_BW_PARAM_MASK UINT8_C(0x30) +#define BMI2_GYR_NOISE_PERF_MODE_MASK UINT8_C(0x40) +#define BMI2_GYR_FILTER_PERF_MODE_MASK UINT8_C(0x80) + +/*! @name Bit position definitions for gyroscope configuration register */ +#define BMI2_GYR_OIS_RANGE_POS UINT8_C(0x03) +#define BMI2_GYR_BW_PARAM_POS UINT8_C(0x04) +#define BMI2_GYR_NOISE_PERF_MODE_POS UINT8_C(0x06) +#define BMI2_GYR_FILTER_PERF_MODE_POS UINT8_C(0x07) + +/******************************************************************************/ +/*! @name Auxiliary Macro Definitions */ +/******************************************************************************/ +/*! @name Auxiliary Output Data Rate */ +#define BMI2_AUX_ODR_RESERVED UINT8_C(0x00) +#define BMI2_AUX_ODR_0_78HZ UINT8_C(0x01) +#define BMI2_AUX_ODR_1_56HZ UINT8_C(0x02) +#define BMI2_AUX_ODR_3_12HZ UINT8_C(0x03) +#define BMI2_AUX_ODR_6_25HZ UINT8_C(0x04) +#define BMI2_AUX_ODR_12_5HZ UINT8_C(0x05) +#define BMI2_AUX_ODR_25HZ UINT8_C(0x06) +#define BMI2_AUX_ODR_50HZ UINT8_C(0x07) +#define BMI2_AUX_ODR_100HZ UINT8_C(0x08) +#define BMI2_AUX_ODR_200HZ UINT8_C(0x09) +#define BMI2_AUX_ODR_400HZ UINT8_C(0x0A) +#define BMI2_AUX_ODR_800HZ UINT8_C(0x0B) + +/*! @name Macro to define burst read lengths for both manual and auto modes */ +#define BMI2_AUX_READ_LEN_0 UINT8_C(0x00) +#define BMI2_AUX_READ_LEN_1 UINT8_C(0x01) +#define BMI2_AUX_READ_LEN_2 UINT8_C(0x02) +#define BMI2_AUX_READ_LEN_3 UINT8_C(0x03) + +#define BMI2_AUX_RD_BURST_FRM_LEN_1 UINT8_C(1) +#define BMI2_AUX_RD_BURST_FRM_LEN_2 UINT8_C(2) +#define BMI2_AUX_RD_BURST_FRM_LEN_6 UINT8_C(6) +#define BMI2_AUX_RD_BURST_FRM_LEN_8 UINT8_C(8) + +/*! @name Mask definitions for auxiliary interface configuration register */ +#define BMI2_AUX_SET_I2C_ADDR_MASK UINT8_C(0xFE) +#define BMI2_AUX_MAN_MODE_EN_MASK UINT8_C(0x80) +#define BMI2_AUX_FCU_WR_EN_MASK UINT8_C(0x40) +#define BMI2_AUX_MAN_READ_BURST_MASK UINT8_C(0x0C) +#define BMI2_AUX_READ_BURST_MASK UINT8_C(0x03) +#define BMI2_AUX_ODR_EN_MASK UINT8_C(0x0F) +#define BMI2_AUX_OFFSET_READ_OUT_MASK UINT8_C(0xF0) + +/*! @name Bit positions for auxiliary interface configuration register */ +#define BMI2_AUX_SET_I2C_ADDR_POS UINT8_C(0x01) +#define BMI2_AUX_MAN_MODE_EN_POS UINT8_C(0x07) +#define BMI2_AUX_FCU_WR_EN_POS UINT8_C(0x06) +#define BMI2_AUX_MAN_READ_BURST_POS UINT8_C(0x02) +#define BMI2_AUX_OFFSET_READ_OUT_POS UINT8_C(0x04) + +/******************************************************************************/ +/*! @name FIFO Macro Definitions */ +/******************************************************************************/ +/*! @name Macros to define virtual FIFO frame mode */ +#define BMI2_FIFO_VIRT_FRM_MODE UINT8_C(0x03) + +/*! @name FIFO Header Mask definitions */ +#define BMI2_FIFO_HEADER_ACC_FRM UINT8_C(0x84) +#define BMI2_FIFO_HEADER_AUX_FRM UINT8_C(0x90) +#define BMI2_FIFO_HEADER_GYR_FRM UINT8_C(0x88) +#define BMI2_FIFO_HEADER_GYR_ACC_FRM UINT8_C(0x8C) +#define BMI2_FIFO_HEADER_AUX_ACC_FRM UINT8_C(0x94) +#define BMI2_FIFO_HEADER_AUX_GYR_FRM UINT8_C(0x98) +#define BMI2_FIFO_HEADER_ALL_FRM UINT8_C(0x9C) +#define BMI2_FIFO_HEADER_SENS_TIME_FRM UINT8_C(0x44) +#define BMI2_FIFO_HEADER_SKIP_FRM UINT8_C(0x40) +#define BMI2_FIFO_HEADER_INPUT_CFG_FRM UINT8_C(0x48) +#define BMI2_FIFO_HEAD_OVER_READ_MSB UINT8_C(0x80) +#define BMI2_FIFO_VIRT_ACT_RECOG_FRM UINT8_C(0xC8) + +/*! @name BMI2 sensor selection for header-less frames */ +#define BMI2_FIFO_HEAD_LESS_ACC_FRM UINT8_C(0x40) +#define BMI2_FIFO_HEAD_LESS_AUX_FRM UINT8_C(0x20) +#define BMI2_FIFO_HEAD_LESS_GYR_FRM UINT8_C(0x80) +#define BMI2_FIFO_HEAD_LESS_GYR_AUX_FRM UINT8_C(0xA0) +#define BMI2_FIFO_HEAD_LESS_GYR_ACC_FRM UINT8_C(0xC0) +#define BMI2_FIFO_HEAD_LESS_AUX_ACC_FRM UINT8_C(0x60) +#define BMI2_FIFO_HEAD_LESS_ALL_FRM UINT8_C(0xE0) + +/*! @name Mask definitions for FIFO frame content configuration */ +#define BMI2_FIFO_STOP_ON_FULL UINT16_C(0x0001) +#define BMI2_FIFO_TIME_EN UINT16_C(0x0002) +#define BMI2_FIFO_TAG_INT1 UINT16_C(0x0300) +#define BMI2_FIFO_TAG_INT2 UINT16_C(0x0C00) +#define BMI2_FIFO_HEADER_EN UINT16_C(0x1000) +#define BMI2_FIFO_AUX_EN UINT16_C(0x2000) +#define BMI2_FIFO_ACC_EN UINT16_C(0x4000) +#define BMI2_FIFO_GYR_EN UINT16_C(0x8000) +#define BMI2_FIFO_ALL_EN UINT16_C(0xE000) + +/*! @name Sensortime resolution in seconds */ +#define BMI2_SENSORTIME_RESOLUTION 0.0000390625f + +/*! @name FIFO sensor data lengths */ +#define BMI2_FIFO_ACC_LENGTH UINT8_C(6) +#define BMI2_FIFO_GYR_LENGTH UINT8_C(6) +#define BMI2_FIFO_ACC_GYR_LENGTH UINT8_C(12) +#define BMI2_SENSOR_TIME_LENGTH UINT8_C(3) +#define BMI2_FIFO_CONFIG_LENGTH UINT8_C(2) +#define BMI2_FIFO_WM_LENGTH UINT8_C(2) +#define BMI2_MAX_VALUE_FIFO_FILTER UINT8_C(1) +#define BMI2_FIFO_DATA_LENGTH UINT8_C(2) +#define BMI2_FIFO_LENGTH_MSB_BYTE UINT8_C(1) +#define BMI2_FIFO_INPUT_CFG_LENGTH UINT8_C(4) +#define BMI2_FIFO_SKIP_FRM_LENGTH UINT8_C(1) + +/*! @name FIFO sensor virtual data lengths: sensor data plus sensor time */ +#define BMI2_FIFO_VIRT_ACC_LENGTH UINT8_C(9) +#define BMI2_FIFO_VIRT_GYR_LENGTH UINT8_C(9) +#define BMI2_FIFO_VIRT_AUX_LENGTH UINT8_C(11) +#define BMI2_FIFO_VIRT_ACC_AUX_LENGTH UINT8_C(17) +#define BMI2_FIFO_VIRT_GYR_AUX_LENGTH UINT8_C(17) +#define BMI2_FIFO_VIRT_ACC_GYR_LENGTH UINT8_C(15) +#define BMI2_FIFO_VIRT_ALL_LENGTH UINT8_C(23) + +/*! @name FIFO sensor virtual data lengths: activity recognition */ +#define BMI2_FIFO_VIRT_ACT_DATA_LENGTH UINT8_C(6) +#define BMI2_FIFO_VIRT_ACT_TIME_LENGTH UINT8_C(4) +#define BMI2_FIFO_VIRT_ACT_TYPE_LENGTH UINT8_C(1) +#define BMI2_FIFO_VIRT_ACT_STAT_LENGTH UINT8_C(1) + +/*! @name BMI2 FIFO data filter modes */ +#define BMI2_FIFO_UNFILTERED_DATA UINT8_C(0) +#define BMI2_FIFO_FILTERED_DATA UINT8_C(1) + +/*! @name FIFO frame masks */ +#define BMI2_FIFO_LSB_CONFIG_CHECK UINT8_C(0x00) +#define BMI2_FIFO_MSB_CONFIG_CHECK UINT8_C(0x80) +#define BMI2_FIFO_TAG_INTR_MASK UINT8_C(0xFC) + +/*! @name BMI2 Mask definitions of FIFO configuration registers */ +#define BMI2_FIFO_CONFIG_0_MASK UINT16_C(0x0003) +#define BMI2_FIFO_CONFIG_1_MASK UINT16_C(0xFF00) + +/*! @name FIFO self wake-up mask definition */ +#define BMI2_FIFO_SELF_WAKE_UP_MASK UINT8_C(0x02) + +/*! @name FIFO down sampling mask definition */ +#define BMI2_ACC_FIFO_DOWNS_MASK UINT8_C(0x70) +#define BMI2_GYR_FIFO_DOWNS_MASK UINT8_C(0x07) + +/*! @name FIFO down sampling bit positions */ +#define BMI2_ACC_FIFO_DOWNS_POS UINT8_C(0x04) + +/*! @name FIFO filter mask definition */ +#define BMI2_ACC_FIFO_FILT_DATA_MASK UINT8_C(0x80) +#define BMI2_GYR_FIFO_FILT_DATA_MASK UINT8_C(0x08) + +/*! @name FIFO filter bit positions */ +#define BMI2_ACC_FIFO_FILT_DATA_POS UINT8_C(0x07) +#define BMI2_GYR_FIFO_FILT_DATA_POS UINT8_C(0x03) + +/*! @name FIFO byte counter mask definition */ +#define BMI2_FIFO_BYTE_COUNTER_MSB_MASK UINT8_C(0x3F) + +/*! @name FIFO self wake-up bit positions */ +#define BMI2_FIFO_SELF_WAKE_UP_POS UINT8_C(0x01) + +/*! @name Mask Definitions for Virtual FIFO frames */ +#define BMI2_FIFO_VIRT_FRM_MODE_MASK UINT8_C(0xC0) +#define BMI2_FIFO_VIRT_PAYLOAD_MASK UINT8_C(0x3C) + +/*! @name Bit Positions for Virtual FIFO frames */ +#define BMI2_FIFO_VIRT_FRM_MODE_POS UINT8_C(0x06) +#define BMI2_FIFO_VIRT_PAYLOAD_POS UINT8_C(0x02) + +/******************************************************************************/ +/*! @name Interrupt Macro Definitions */ +/******************************************************************************/ +/*! @name BMI2 Interrupt Modes */ +/* Non latched */ +#define BMI2_INT_NON_LATCH UINT8_C(0) + +/* Permanently latched */ +#define BMI2_INT_LATCH UINT8_C(1) + +/*! @name BMI2 Interrupt Pin Behavior */ +#define BMI2_INT_PUSH_PULL UINT8_C(0) +#define BMI2_INT_OPEN_DRAIN UINT8_C(1) + +/*! @name BMI2 Interrupt Pin Level */ +#define BMI2_INT_ACTIVE_LOW UINT8_C(0) +#define BMI2_INT_ACTIVE_HIGH UINT8_C(1) + +/*! @name BMI2 Interrupt Output Enable */ +#define BMI2_INT_OUTPUT_DISABLE UINT8_C(0) +#define BMI2_INT_OUTPUT_ENABLE UINT8_C(1) + +/*! @name BMI2 Interrupt Input Enable */ +#define BMI2_INT_INPUT_DISABLE UINT8_C(0) +#define BMI2_INT_INPUT_ENABLE UINT8_C(1) + +/*! @name Mask definitions for interrupt pin configuration */ +#define BMI2_INT_LATCH_MASK UINT8_C(0x01) +#define BMI2_INT_LEVEL_MASK UINT8_C(0x02) +#define BMI2_INT_OPEN_DRAIN_MASK UINT8_C(0x04) +#define BMI2_INT_OUTPUT_EN_MASK UINT8_C(0x08) +#define BMI2_INT_INPUT_EN_MASK UINT8_C(0x10) + +/*! @name Bit position definitions for interrupt pin configuration */ +#define BMI2_INT_LEVEL_POS UINT8_C(0x01) +#define BMI2_INT_OPEN_DRAIN_POS UINT8_C(0x02) +#define BMI2_INT_OUTPUT_EN_POS UINT8_C(0x03) +#define BMI2_INT_INPUT_EN_POS UINT8_C(0x04) + +/*! @name Mask definitions for data interrupt mapping */ +#define BMI2_FFULL_INT UINT8_C(0x01) +#define BMI2_FWM_INT UINT8_C(0x02) +#define BMI2_DRDY_INT UINT8_C(0x04) +#define BMI2_ERR_INT UINT8_C(0x08) + +/*! @name Mask definitions for data interrupt status bits */ +#define BMI2_FFULL_INT_STATUS_MASK UINT16_C(0x0100) +#define BMI2_FWM_INT_STATUS_MASK UINT16_C(0x0200) +#define BMI2_ERR_INT_STATUS_MASK UINT16_C(0x0400) +#define BMI2_AUX_DRDY_INT_MASK UINT16_C(0x2000) +#define BMI2_GYR_DRDY_INT_MASK UINT16_C(0x4000) +#define BMI2_ACC_DRDY_INT_MASK UINT16_C(0x8000) + +/*! @name Maximum number of interrupt pins */ +#define BMI2_INT_PIN_MAX_NUM UINT8_C(2) + +/*! @name Macro for mapping feature interrupts */ +#define BMI2_FEAT_BIT_DISABLE UINT8_C(0) +#define BMI2_FEAT_BIT0 UINT8_C(1) +#define BMI2_FEAT_BIT1 UINT8_C(2) +#define BMI2_FEAT_BIT2 UINT8_C(3) +#define BMI2_FEAT_BIT3 UINT8_C(4) +#define BMI2_FEAT_BIT4 UINT8_C(5) +#define BMI2_FEAT_BIT5 UINT8_C(6) +#define BMI2_FEAT_BIT6 UINT8_C(7) +#define BMI2_FEAT_BIT7 UINT8_C(8) +#define BMI2_FEAT_BIT_MAX UINT8_C(9) + +/******************************************************************************/ +/*! @name OIS Interface Macro Definitions */ +/******************************************************************************/ +/*! @name Mask definitions for interface configuration register */ +#define BMI2_SPI3_MODE_MASK UINT8_C(0x01) +#define BMI2_SPI3_OIS_MASK UINT8_C(0x02) +#define BMI2_OIS_IF_EN_MASK UINT8_C(0x10) +#define BMI2_AUX_IF_EN_MASK UINT8_C(0x20) + +/*! @name Bit positions for OIS interface enable */ +#define BMI2_SPI3_MODE_POS UINT8_C(0x00) +#define BMI2_SPI3_OIS_POS UINT8_C(0x01) +#define BMI2_OIS_IF_EN_POS UINT8_C(0x04) +#define BMI2_AUX_IF_EN_POS UINT8_C(0x05) + +/******************************************************************************/ +/*! @name Macro Definitions for Axes re-mapping */ +/******************************************************************************/ +/*! @name Macros for the user-defined values of axes and their polarities */ +#define BMI2_X UINT8_C(0x01) +#define BMI2_NEG_X UINT8_C(0x09) +#define BMI2_Y UINT8_C(0x02) +#define BMI2_NEG_Y UINT8_C(0x0A) +#define BMI2_Z UINT8_C(0x04) +#define BMI2_NEG_Z UINT8_C(0x0C) +#define BMI2_AXIS_MASK UINT8_C(0x07) +#define BMI2_AXIS_SIGN UINT8_C(0x08) + +/******************************************************************************/ +/*! @name Macro Definitions for offset and gain compensation */ +/******************************************************************************/ +/*! @name Mask definitions of gyroscope offset compensation registers */ +#define BMI2_GYR_GAIN_EN_MASK UINT8_C(0x80) +#define BMI2_GYR_OFF_COMP_EN_MASK UINT8_C(0x40) + +/*! @name Bit positions of gyroscope offset compensation registers */ +#define BMI2_GYR_OFF_COMP_EN_POS UINT8_C(0x06) + +/*! @name Mask definitions of gyroscope user-gain registers */ +#define BMI2_GYR_USR_GAIN_X_MASK UINT8_C(0x7F) +#define BMI2_GYR_USR_GAIN_Y_MASK UINT8_C(0x7F) +#define BMI2_GYR_USR_GAIN_Z_MASK UINT8_C(0x7F) + +/*! @name Bit positions of gyroscope offset compensation registers */ +#define BMI2_GYR_GAIN_EN_POS UINT8_C(0x07) + +/******************************************************************************/ +/*! @name Macro Definitions for internal status */ +/******************************************************************************/ +#define BMI2_NOT_INIT UINT8_C(0x00) +#define BMI2_INIT_OK UINT8_C(0x01) +#define BMI2_INIT_ERR UINT8_C(0x02) +#define BMI2_DRV_ERR UINT8_C(0x03) +#define BMI2_SNS_STOP UINT8_C(0x04) +#define BMI2_NVM_ERROR UINT8_C(0x05) +#define BMI2_START_UP_ERROR UINT8_C(0x06) +#define BMI2_COMPAT_ERROR UINT8_C(0x07) +#define BMI2_VFM_SKIPPED UINT8_C(0x10) +#define BMI2_AXES_MAP_ERROR UINT8_C(0x20) +#define BMI2_ODR_50_HZ_ERROR UINT8_C(0x40) +#define BMI2_ODR_HIGH_ERROR UINT8_C(0x80) + +/******************************************************************************/ +/*! @name error status form gyro gain update status. */ +/******************************************************************************/ +#define BMI2_G_TRIGGER_NO_ERROR UINT8_C(0x00) + +#define BMI2_G_TRIGGER_PRECON_ERROR UINT8_C(0x01) +#define BMI2_G_TRIGGER_DL_ERROR UINT8_C(0x02) +#define BMI2_G_TRIGGER_ABORT_ERROR UINT8_C(0x03) + +/******************************************************************************/ +/*! @name Variant specific features selection macros */ +/******************************************************************************/ +#define BMI2_CRT_RTOSK_ENABLE UINT8_C(0x01) +#define BMI2_GYRO_CROSS_SENS_ENABLE UINT8_C(0x02) +#define BMI2_GYRO_USER_GAIN_ENABLE UINT8_C(0x08) +#define BMI2_NO_FEATURE_ENABLE UINT8_C(0x00) +#define BMI2_CRT_IN_FIFO_NOT_REQ UINT8_C(0x10) +#define BMI2_MAXIMUM_FIFO_VARIANT UINT8_C(0x20) + +/*! Pull-up configuration for ASDA */ +#define BMI2_ASDA_PUPSEL_OFF UINT8_C(0x00) +#define BMI2_ASDA_PUPSEL_40K UINT8_C(0x01) +#define BMI2_ASDA_PUPSEL_10K UINT8_C(0x02) +#define BMI2_ASDA_PUPSEL_2K UINT8_C(0x03) + +#define INITIAL_LOW_VALUE INT16_C(32767) +#define INITIAL_HIGH_VALUE INT16_C(-32768) + +/*! G values */ +/* Macro function to derive the value macro from the range macro */ +#define BMI2_GET_RANGE_VAL(range) (range##_VAL) + +#define BMI2_ACC_RANGE_2G_VAL (2.0f) +#define BMI2_ACC_RANGE_4G_VAL (4.00f) +#define BMI2_ACC_RANGE_8G_VAL (8.00f) +#define BMI2_ACC_RANGE_16G_VAL (16.00f) + +/*! dps values */ +/* Macro function to derive the value macro from the range macro */ +#define BMI2_GET_DPS_VAL(dps) (dps##_VAL) + +#define BMI2_GYR_RANGE_125_VAL (125.00f) +#define BMI2_GYR_RANGE_250_VAL (250.00f) +#define BMI2_GYR_RANGE_500_VAL (500.00f) +#define BMI2_GYR_RANGE_1000_VAL (1000.00f) +#define BMI2_GYR_RANGE_2000_VAL (2000.00f) + +/* Macros to replace constant values */ +#define BMI2_N_SENSE_COUNT_1 UINT8_C(1) +#define BMI2_N_SENSE_COUNT_2 UINT8_C(2) +#define BMI2_N_SENSE_COUNT_3 UINT8_C(3) +#define BMI2_N_SENSE_COUNT_4 UINT8_C(4) +#define BMI2_N_SENSE_COUNT_5 UINT8_C(5) +#define BMI2_N_SENSE_COUNT_6 UINT8_C(6) + +/* Any-no-motion duration values */ +#define BMI2_ANY_NO_MOT_DUR_100_MSEC UINT8_C(0x05) +#define BMI2_ANY_NO_MOT_DUR_80_MSEC UINT8_C(0x04) +#define BMI2_ANY_NO_MOT_DUR_20_MSEC UINT8_C(0x01) + +/* Any-no-motion threshold values */ +#define BMI2_ANY_MOT_THRE_DEFAULT_83_MG UINT8_C(0xAA) +#define BMI2_ANY_NO_THRE_DEFAULT_70_MG UINT8_C(0x90) +#define BMI2_ANY_NO_MOT_THRE_50_MG UINT8_C(0x68) + +/**\name BIT SLICE GET AND SET FUNCTIONS */ +#define BMI2_GET_BITSLICE(regvar, bitname) \ + ((regvar & bitname##_MASK) >> bitname##_POS) + +#define BMI2_SET_BITSLICE(regvar, bitname, val) \ + ((regvar & ~bitname##_MASK) | \ + ((val << bitname##_POS) & bitname##_MASK)) + +#define BMI2_GET_DIFF(x, y) ((x) - (y)) + +#define BMI2_SET_BIT_VAL_0(reg_data, bitname) (reg_data & ~(bitname##_MASK)) + +#define BMI2_SET_BITS_POS_0(reg_data, bitname, data) \ + ((reg_data & ~(bitname##_MASK)) | \ + (data & bitname##_MASK)) + +#define BMI2_GET_BITS_POS_0(reg_data, bitname) (reg_data & (bitname##_MASK)) + +/******************************************************************************/ +/*! @name Function Pointers */ +/******************************************************************************/ + +/*! + * @brief Bus communication function pointer which should be mapped to + * the platform specific read functions of the user + * + * @param[in] reg_addr : Register address from which data is read. + * @param[out] reg_data : Pointer to data buffer where read data is stored. + * @param[in] len : Number of bytes of data to be read. + * @param[in, out] intf_ptr : Void pointer that can enable the linking of descriptors + * for interface related call backs. + * + * retval = BMA4_INTF_RET_SUCCESS -> Success + * retval != BMA4_INTF_RET_SUCCESS -> Failure + * + */ +typedef BMI2_INTF_RETURN_TYPE (*bmi2_read_fptr_t)(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr); + +/*! + * @brief Bus communication function pointer which should be mapped to + * the platform specific write functions of the user + * + * @param[in] reg_addr : Register address to which the data is written. + * @param[in] reg_data : Pointer to data buffer in which data to be written + * is stored. + * @param[in] len : Number of bytes of data to be written. + * @param[in, out] intf_ptr : Void pointer that can enable the linking of descriptors + * for interface related call backs + * + * retval = BMA4_INTF_RET_SUCCESS -> Success + * retval != BMA4_INTF_RET_SUCCESS -> Failure + * + */ +typedef BMI2_INTF_RETURN_TYPE (*bmi2_write_fptr_t)(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, + void *intf_ptr); + +/*! + * @brief Delay function pointer which should be mapped to + * delay function of the user + * + * @param[in] period : Delay in microseconds. + * @param[in, out] intf_ptr : Void pointer that can enable the linking of descriptors + * for interface related call backs + * + */ +typedef void (*bmi2_delay_fptr_t)(uint32_t period, void *intf_ptr); + +/*! + * @brief To get the configurations for wake_up feature, since wakeup feature is different for bmi260 and bmi261. + * + * @param[out] wake_up : Void pointer to store bmi2_wake_up_config structure. + * @param[in, out] bmi2_dev : Void pointer to store bmi2_dev structure. + * + * @return Result of API execution status + * + * @retval BMI2_OK - Success. + * @retval BMI2_E_COM_FAIL - Error: Communication fail + * @retval BMI2_E_NULL_PTR - Error: Null pointer error + * @retval BMI2_E_INVALID_PAGE - Error: Invalid Page + * + */ +typedef int8_t (*bmi2_wake_up_fptr_t)(void *wake_up, void *bmi2_dev); + +/*! + * @brief To get the configurations for tap feature. + * + * @param[out] tap : Void pointer to store bmi2_tap_config structure. + * @param[in, out] bmi2_dev : Void pointer to store bmi2_dev structure. + * + * @return Result of API execution status + * + * @retval BMI2_OK - Success. + * @retval BMI2_E_COM_FAIL - Error: Communication fail + * @retval BMI2_E_NULL_PTR - Error: Null pointer error + * @retval BMI2_E_INVALID_PAGE - Error: Invalid Page + * + */ +typedef int8_t (*bmi2_tap_fptr_t)(void *tap, void *bmi2_dev); + +/******************************************************************************/ +/*! @name Enum Declarations */ +/******************************************************************************/ +/*! @name Enum to define BMI2 sensor interfaces */ +enum bmi2_intf { + BMI2_SPI_INTF = 0, + BMI2_I2C_INTF, + BMI2_I3C_INTF +}; + +/*! @name Enum to define BMI2 sensor configuration errors for accelerometer + * and gyroscope + */ +enum bmi2_sensor_config_error { + BMI2_NO_ERROR, + BMI2_ACC_ERROR, + BMI2_GYR_ERROR, + BMI2_ACC_GYR_ERROR +}; + +/*! @name Enum to define interrupt lines */ +enum bmi2_hw_int_pin { + BMI2_INT_NONE, + BMI2_INT1, + BMI2_INT2, + BMI2_INT_BOTH, + BMI2_INT_PIN_MAX +}; + +/*! @name Enum for the position of the wearable device */ +enum bmi2_wear_arm_pos { + BMI2_ARM_LEFT, + BMI2_ARM_RIGHT +}; + +/*! @name Enum to display type of activity recognition */ +enum bmi2_act_recog_type { + BMI2_ACT_UNKNOWN, + BMI2_ACT_STILL, + BMI2_ACT_WALK, + BMI2_ACT_RUN, + BMI2_ACT_BIKE, + BMI2_ACT_VEHICLE, + BMI2_ACT_TILTED +}; + +/*! @name Enum to display activity recognition status */ +enum bmi2_act_recog_stat { + BMI2_ACT_START = 1, + BMI2_ACT_END +}; + +/******************************************************************************/ +/*! @name Structure Declarations */ +/******************************************************************************/ +/*! @name Structure to store the compensated user-gain data of gyroscope */ +struct bmi2_gyro_user_gain_data +{ + /*! x-axis */ + int8_t x; + + /*! y-axis */ + int8_t y; + + /*! z-axis */ + int8_t z; +}; + +/*! @name Structure to store the re-mapped axis */ +struct bmi2_remap +{ + /*! Re-mapped x-axis */ + uint8_t x; + + /*! Re-mapped y-axis */ + uint8_t y; + + /*! Re-mapped z-axis */ + uint8_t z; +}; + +/*! @name Structure to store the value of re-mapped axis and its sign */ +struct bmi2_axes_remap +{ + /*! Re-mapped x-axis */ + uint8_t x_axis; + + /*! Re-mapped y-axis */ + uint8_t y_axis; + + /*! Re-mapped z-axis */ + uint8_t z_axis; + + /*! Re-mapped x-axis sign */ + uint8_t x_axis_sign; + + /*! Re-mapped y-axis sign */ + uint8_t y_axis_sign; + + /*! Re-mapped z-axis sign */ + uint8_t z_axis_sign; +}; + +/*! @name Structure to define the type of sensor and its interrupt pin */ +struct bmi2_sens_int_config +{ + /*! Defines the type of sensor */ + uint8_t type; + + /*! Type of interrupt pin */ + enum bmi2_hw_int_pin hw_int_pin; +}; + +/*! @name Structure to define output for activity recognition */ +struct bmi2_act_recog_output +{ + /*! Time stamp */ + uint32_t time_stamp; + + /*! current activity */ + uint8_t curr_act; + + /*! previous activity */ + uint8_t prev_act; +}; + +/*! @name Structure to define FIFO frame configuration */ +struct bmi2_fifo_frame +{ + /*! Pointer to FIFO data */ + uint8_t *data; + + /*! Number of user defined bytes of FIFO to be read */ + uint16_t length; + + /*! Defines header/header-less mode */ + uint8_t header_enable; + + /*! Enables type of data to be streamed - accelerometer, auxiliary or + * gyroscope + */ + uint16_t data_enable; + + /*! To index accelerometer bytes */ + uint16_t acc_byte_start_idx; + + /*! To index activity output bytes */ + uint16_t act_recog_byte_start_idx; + + /*! To index auxiliary bytes */ + uint16_t aux_byte_start_idx; + + /*! To index gyroscope bytes */ + uint16_t gyr_byte_start_idx; + + /*! FIFO sensor time */ + uint32_t sensor_time; + + /*! Skipped frame count */ + uint8_t skipped_frame_count; + + /*! Type of data interrupt to be mapped */ + uint8_t data_int_map; + + /*! Water-mark level for water-mark interrupt */ + uint16_t wm_lvl; + + /*! Accelerometer frame length */ + uint8_t acc_frm_len; + + /*! Gyroscope frame length */ + uint8_t gyr_frm_len; + + /*! Auxiliary frame length */ + uint8_t aux_frm_len; + + /*! Accelerometer and gyroscope frame length */ + uint8_t acc_gyr_frm_len; + + /*! Accelerometer and auxiliary frame length */ + uint8_t acc_aux_frm_len; + + /*! Gyroscope and auxiliary frame length */ + uint8_t aux_gyr_frm_len; + + /*! Accelerometer, Gyroscope and auxiliary frame length */ + uint8_t all_frm_len; +}; + +/*! @name Structure to define Interrupt pin configuration */ +struct bmi2_int_pin_cfg +{ + /*! Configure level of interrupt pin */ + uint8_t lvl; + + /*! Configure behavior of interrupt pin */ + uint8_t od; + + /*! Output enable for interrupt pin */ + uint8_t output_en; + + /*! Input enable for interrupt pin */ + uint8_t input_en; +}; + +/*! @name Structure to define interrupt pin type, mode and configurations */ +struct bmi2_int_pin_config +{ + /*! Interrupt pin type: INT1 or INT2 or BOTH */ + uint8_t pin_type; + + /*! Latched or non-latched mode*/ + uint8_t int_latch; + + /*! Structure to define Interrupt pin configuration */ + struct bmi2_int_pin_cfg pin_cfg[BMI2_INT_PIN_MAX_NUM]; +}; + +/*! @name Structure to define an array of 8 auxiliary data bytes */ +struct bmi2_aux_fifo_data +{ + /*! Auxiliary data */ + uint8_t data[8]; + + /*! Sensor time for virtual frames */ + uint32_t virt_sens_time; +}; + +/*! @name Structure to define accelerometer and gyroscope sensor axes and + * sensor time for virtual frames + */ +struct bmi2_sens_axes_data +{ + /*! Data in x-axis */ + int16_t x; + + /*! Data in y-axis */ + int16_t y; + + /*! Data in z-axis */ + int16_t z; + + /*! Sensor time for virtual frames */ + uint32_t virt_sens_time; +}; + +/*! @name Structure to define gyroscope saturation status of user gain */ +struct bmi2_gyr_user_gain_status +{ + /*! Status in x-axis */ + uint8_t sat_x; + + /*! Status in y-axis */ + uint8_t sat_y; + + /*! Status in z-axis */ + uint8_t sat_z; + + /*! G trigger status */ + uint8_t g_trigger_status; +}; + +/*! @name Structure to store the status of gyro self test result */ +struct bmi2_gyro_self_test_status +{ + /*! gyro self test axes done */ + uint8_t gyr_st_axes_done : 1; + + /*! status of gyro X-axis self test */ + uint8_t gyr_axis_x_ok : 1; + + /*! status of gyro Y-axis self test */ + uint8_t gyr_axis_y_ok : 1; + + /*! status of gyro Z-axis self test */ + uint8_t gyr_axis_z_ok : 1; +}; + +/*! @name Structure to define NVM error status */ +struct bmi2_nvm_err_status +{ + /*! NVM load action error */ + uint8_t load_error; + + /*! NVM program action error */ + uint8_t prog_error; + + /*! NVM erase action error */ + uint8_t erase_error; + + /*! NVM program limit exceeded */ + uint8_t exceed_error; + + /*! NVM privilege error */ + uint8_t privil_error; +}; + +/*! @name Structure to define VFRM error status */ +struct bmi2_vfrm_err_status +{ + /*! VFRM lock acquire error */ + uint8_t lock_error; + + /*! VFRM write error */ + uint8_t write_error; + + /*! VFRM fatal err */ + uint8_t fatal_error; +}; + +/*! @name Structure to define accelerometer self test feature status */ +struct bmi2_acc_self_test_status +{ + /*! Accelerometer test completed */ + uint8_t acc_self_test_done; + + /*! Bit is set to 1 when accelerometer X-axis test passed */ + uint8_t acc_x_ok; + + /*! Bit is set to 1 when accelerometer y-axis test passed */ + uint8_t acc_y_ok; + + /*! Bit is set to 1 when accelerometer z-axis test passed */ + uint8_t acc_z_ok; +}; + +/*! @name Structure to define orientation output */ +struct bmi2_orientation_output +{ + /*! Orientation portrait landscape */ + uint8_t portrait_landscape; + + /*! Orientation face-up down */ + uint8_t faceup_down; +}; + +/*! @name Structure to define OIS output */ +struct bmi2_ois_output +{ + /*! OIS accel x axis */ + int16_t ois_acc_x; + + /*! OIS accel y axis */ + int16_t ois_acc_y; + + /*! OIS accel z axis */ + int16_t ois_acc_z; + + /*! ois gyro x axis */ + int16_t ois_gyro_x; + + /*! OIS gyro y axis */ + int16_t ois_gyro_y; + + /*! OIS gyro z axis */ + int16_t ois_gyro_z; +}; + +/*! @name Structure to define BMI2 sensor data */ +struct bmi2_sens_data +{ + /*! Accelerometer axes data */ + struct bmi2_sens_axes_data acc; + + /*! Gyroscope axes data */ + struct bmi2_sens_axes_data gyr; + + /*! Auxiliary sensor data */ + uint8_t aux_data[BMI2_AUX_NUM_BYTES]; + + /*! Sensor time */ + uint32_t sens_time; + + /*! Status register data */ + uint8_t status; +}; + +/*! + * @brief Store Accel data in terms of RAW, g_val, ms2 + */ +struct bmi2_accel_processed_data +{ + /*! Accelerometer raw value */ + float accel_raw; + + /*! Accelerometer G value */ + float accel_g; + + /*! Accelerometer MS2 value */ + float accel_ms2; +}; + +/*! + * @brief Store Gyro data in terms of RAW, DPS, MS2 and RPS + */ +struct bmi2_gyro_processed_data +{ + /*! Gyroscope raw value */ + float gyro_raw; + + /*! Gyroscope DPS value */ + float gyro_dps; + + /*! Gyroscope MS2 value */ + float gyro_ms2; + + /*! Gyroscope RPS value */ + float gyro_rps; +}; + +/*! @name Structure to define BMI2 DSD data */ +struct bmi2_door_state_detector_output +{ + /*! Door event output */ + uint8_t door_event_output; + + /*! Heading output */ + int16_t heading_output; +}; + +/*! @name Structure to store IMU Fuion Quaterion output */ +struct bmi2_imu_fusion_quaterion_output +{ + /*! Vector_x lsb_msb output */ + int32_t vector_x; + + /*! Vector_y lsb_msb output */ + int32_t vector_y; + + /*! Vector_z lsb_msb output */ + int32_t vector_z; + + /*! Scalar_w lsb_msb output */ + int32_t scalar_w; + +}; + +/*! @name Structure to store head orientation output */ +struct bmi2_head_orientation_output +{ + /*! Vector_x lsb_msb output */ + int32_t vector_x; + + /*! Vector_y lsb_msb output */ + int32_t vector_y; + + /*! Vector_z lsb_msb output */ + int32_t vector_z; + + /*! Scalar_w lsb_msb output */ + int32_t scalar_w; + +}; + +/*! @name Structure to store HMC Correction output and feed in values*/ +struct bmi2_hmc_correction_quaterion +{ + /*! Vector_x lsb_msb output */ + int32_t vector_x; + + /*! Vector_y lsb_msb output */ + int32_t vector_y; + + /*! Vector_z lsb_msb output */ + int32_t vector_z; + + /*! Scalar_w lsb_msb output */ + int32_t scalar_w; + +}; + +/*! @name Structure to store gyro bias values*/ +struct bmi2_gyro_bias_xyz +{ + /*! x lsb_msb output */ + int16_t gyro_bias_x; + + /*! y lsb_msb output */ + int16_t gyro_bias_y; + + /*! z lsb_msb output */ + int16_t gyro_bias_z; + +}; + +struct bmi2_gyro_calib_config +{ + /*! To enable or disable gyro calibrator feature */ + uint8_t gyr_calib_offsetcorrection_en; + + /*! To apply gyro bias to XYZ */ + uint8_t apply_gyro_bias_xyz; + + /*! To store input gyro status */ + uint8_t input_gyro_calib_status; + + /*! To store output gyro status */ + uint8_t output_gyro_calib_status; + + /*! To store output gyro status */ + struct bmi2_gyro_bias_xyz input; + + /*! To store output gyro status */ + struct bmi2_gyro_bias_xyz output; +}; + +/*! @name Union to define BMI2 feature data */ +union bmi2_feature_data +{ + /*! Step counter output */ + uint32_t step_counter_output; + + /*! Step activity output */ + uint8_t activity_output; + + /*! Orientation output */ + struct bmi2_orientation_output orient_output; + + /*! High-g output */ + uint8_t high_g_output; + + /*! Gyroscope user gain saturation status */ + struct bmi2_gyr_user_gain_status gyro_user_gain_status; + + /*! NVM error status */ + struct bmi2_nvm_err_status nvm_status; + + /*! Virtual frame error status */ + struct bmi2_vfrm_err_status vfrm_status; + + /*! Wrist gesture output */ + uint8_t wrist_gesture_output; + + /*! Wrist wear wakeup output */ + uint8_t wrist_wear_wakeup_output; + + /*! Gyroscope cross sense value of z axis */ + int16_t correction_factor_zx; + + /*! Accelerometer self test feature status */ + struct bmi2_acc_self_test_status accel_self_test_output; + + /*! OIS output */ + struct bmi2_ois_output ois_output; + + /*! DSD output */ + struct bmi2_door_state_detector_output door_state_detector_output; + + /*! IMU fusion output */ + struct bmi2_imu_fusion_quaterion_output imu_fusion_quaterion_output; + + /*! Head orientation output */ + struct bmi2_head_orientation_output head_orien_quaterion_output; + + /*! HMC correction quaterion output */ + struct bmi2_hmc_correction_quaterion hmc_correction_output; + +}; + +/*! @name Structure to define type of sensor and their respective data */ +struct bmi2_feat_sensor_data +{ + /*! Defines the type of sensor */ + uint8_t type; + + /*! Defines various sensor data */ + union bmi2_feature_data sens_data; +}; + +/*! + * @brief Structure to define type of sensor and their respective data + */ +struct bmi2_sensor_data +{ + /*! Defines the type of sensor */ + uint8_t type; + + /*! Defines various sensor data */ + struct bmi2_sens_data sens_data; +}; + +/*! @name Structure to define accelerometer configuration */ +struct bmi2_accel_config +{ + /*! Output data rate in Hz */ + uint8_t odr; + + /*! Bandwidth parameter */ + uint8_t bwp; + + /*! Filter performance mode */ + uint8_t filter_perf; + + /*! g-range */ + uint8_t range; +}; + +/*! @name Structure to define gyroscope configuration */ +struct bmi2_gyro_config +{ + /*! Output data rate in Hz */ + uint8_t odr; + + /*! Bandwidth parameter */ + uint8_t bwp; + + /*! Filter performance mode */ + uint8_t filter_perf; + + /*! OIS Range */ + uint8_t ois_range; + + /*! Gyroscope Range */ + uint8_t range; + + /*! Selects noise performance */ + uint8_t noise_perf; +}; + +/*! @name Structure to define auxiliary sensor configuration */ +struct bmi2_aux_config +{ + /*! Enable/Disable auxiliary interface */ + uint8_t aux_en; + + /*! Manual or Auto mode*/ + uint8_t manual_en; + + /*! Enables FCU write command on auxiliary interface */ + uint8_t fcu_write_en; + + /*! Read burst length for manual mode */ + uint8_t man_rd_burst; + + /*! Read burst length for data mode */ + uint8_t aux_rd_burst; + + /*! Output data rate */ + uint8_t odr; + + /*! Read-out offset */ + uint8_t offset; + + /*! I2c address of auxiliary sensor */ + uint8_t i2c_device_addr; + + /*! Read address of auxiliary sensor */ + uint8_t read_addr; +}; + +/*! @name Structure to define any-motion configuration */ +struct bmi2_any_motion_config +{ + /*! Duration */ + uint16_t duration; + + /*! Acceleration slope threshold */ + uint16_t threshold; + + /*! To select per x-axis */ + uint16_t select_x; + + /*! To select per y-axis */ + uint16_t select_y; + + /*! To select per z-axis */ + uint16_t select_z; +}; + +/*! @name Structure to define no-motion configuration */ +struct bmi2_no_motion_config +{ + /*! Duration */ + uint16_t duration; + + /*! Acceleration slope threshold */ + uint16_t threshold; + + /*! To select per x-axis */ + uint16_t select_x; + + /*! To select per y-axis */ + uint16_t select_y; + + /*! To select per z-axis */ + uint16_t select_z; +}; + +/*! @name Structure to define sig-motion configuration */ +struct bmi2_sig_motion_config +{ + /*! Block size */ + uint16_t block_size; +}; + +/*! @name Structure to define EXT TCO configuration */ +struct bmi2_ext_tco +{ + /*! Enable(1) / Disable(0) hardware compensation. */ + uint8_t hw_comp_enable; +}; + +/*! @name Structure to define step counter/detector/activity configuration */ +struct bmi2_step_config +{ + /*! Water-mark level */ + uint16_t watermark_level; + + /*! Reset counter */ + uint16_t reset_counter; + + /*! Step buffer size */ + uint8_t step_buffer_size; +}; + +/*! @name Structure to define gyroscope user gain configuration */ +struct bmi2_gyro_user_gain_config +{ + /*! Gain update value for x-axis */ + uint16_t ratio_x; + + /*! Gain update value for y-axis */ + uint16_t ratio_y; + + /*! Gain update value for z-axis */ + uint16_t ratio_z; +}; + +/*! @name Structure to define wake-up configuration */ +struct bmi2_wake_up_config +{ + /*! Wake-up sensitivity */ + uint16_t sensitivity; + + /*! + * For Single tap, single_tap_en = 1 + * For Double tap, single_tap_en = 0 + */ + uint16_t single_tap_en; +}; + +/*! @name Structure to define tap configuration */ +struct bmi2_tap_config +{ + /*! Enable -> Filtered tap data, Disable -> Unfiltered data */ + uint16_t data_reg_en; + + /*! Scaling factor of threshold */ + uint16_t tap_sens_thres; + + /*! Maximum duration between each taps */ + uint16_t max_gest_dur; + + /*! Minimum quite time between the two gesture detection */ + uint16_t quite_time_after_gest; + + /*! Wait time */ + uint16_t wait_for_timeout; + + /*! Axis selection */ + uint16_t axis_sel; + + /*! Settling time of high frequency acceleration signal components after tap */ + uint16_t tap_shock_dur; + + /*! Minimum threshold for peak detection */ + uint16_t min_const_thres; +}; + +/*! @name Structure to define orientation configuration */ +struct bmi2_orient_config +{ + /*! Upside/down detection */ + uint16_t ud_en; + + /*! Symmetrical, high or low Symmetrical */ + uint16_t mode; + + /*! Blocking mode */ + uint16_t blocking; + + /*! Threshold angle */ + uint16_t theta; + + /*! Acceleration hysteresis for orientation detection */ + uint16_t hysteresis; +}; + +/*! @name Structure to define high-g configuration */ +struct bmi2_high_g_config +{ + /*! Acceleration threshold */ + uint16_t threshold; + + /*! Hysteresis */ + uint16_t hysteresis; + + /*! To select per x-axis */ + uint16_t select_x; + + /*! To select per y-axis */ + uint16_t select_y; + + /*! To select per z-axis */ + uint16_t select_z; + + /*! Duration interval */ + uint16_t duration; +}; + +/*! @name Structure to define low-g configuration */ +struct bmi2_low_g_config +{ + /*! Acceleration threshold */ + uint16_t threshold; + + /*! Hysteresis */ + uint16_t hysteresis; + + /*! Duration interval */ + uint16_t duration; +}; + +/*! @name Structure to define flat configuration */ +struct bmi2_flat_config +{ + /*! Theta angle for flat detection */ + uint16_t theta; + + /*! Blocking mode */ + uint16_t blocking; + + /*! Hysteresis for theta flat detection */ + uint16_t hysteresis; + + /*! Holds the duration */ + uint16_t hold_time; +}; + +/*! @name Structure to define wrist gesture configuration */ +struct bmi2_wrist_gest_config +{ + /*! Wearable arm (left or right) */ + uint16_t wearable_arm; + + /*! Sine of the minimum tilt angle in portrait down direction of the device when wrist is rolled + * away from user. The configuration parameter is scaled by 2048 i.e. 2048 * sin(angle). + * Range is 1448 to 1774. Default value is 1774. */ + uint16_t min_flick_peak; + + /*! Value of minimum time difference between wrist roll-out and roll-in movement during flick gesture. + * Range is 3 to 5 samples at 50Hz. Default value is 4 (i.e. 0.08 seconds). */ + uint16_t min_flick_samples; + + /*! Maximum time within which gesture movement has to be completed. Range is 150 to 250 samples at 50Hz. + * Default value is 200 (i.e. 4 seconds). */ + uint16_t max_duration; +}; + +/*! @name Structure to define wrist wear wake-up configuration */ +struct bmi2_wrist_wear_wake_up_config +{ + /*! Cosine of minimum expected attitude change of the device + * within 1 second time window when moving within focus position. + * The parameter is scaled by 2048 i.e. 2048 * cos(angle). + * Range is 1024 to 1774. Default is 1448. + */ + uint16_t min_angle_focus; + + /*! Cosine of minimum expected attitude change of the device + * within 1 second time window when moving from non-focus to focus position. + * The parameter is scaled by 2048 i.e. 2048 * cos(angle). + * Range is 1448 to 1856. Default value is 1774. + */ + uint16_t min_angle_nonfocus; + + /*! Sine of the maximum allowed downward tilt angle in + * landscape right direction of the device, when it is in focus position + * (i.e. user is able to comfortably look at the dial of wear device). + * The configuration parameter is scaled by 2048 i.e. 2048 * sin(angle). + * Range is 700 to 1024. Default value is 1024. + */ + uint16_t max_tilt_lr; + + /*! Sine of the maximum allowed downward tilt angle in + * landscape left direction of the device, when it is in focus position + * (i.e. user is able to comfortably look at the dial of wear device). + * The configuration parameter is scaled by 2048 i.e. 2048 * sin(angle). + * Range is 700 to 1024. Default value is 700. + */ + uint16_t max_tilt_ll; + + /*! Sine of the maximum allowed backward tilt angle in + * portrait down direction of the device, when it is in focus position + * (i.e. user is able to comfortably look at the dial of wear device). + * The configuration parameter is scaled by 2048 i.e. 2048 * sin(angle). + * Range is 0 to179. Default value is 179. + */ + uint16_t max_tilt_pd; + + /*! Sine of the maximum allowed forward tilt angle in + * portrait up direction of the device, when it is in focus position + * (i.e. user is able to comfortably look at the dial of wear device). + * The configuration parameter is scaled by 2048 i.e. 2048 * sin(angle). + * Range is 1774 to 1978. Default value is 1925. + */ + uint16_t max_tilt_pu; +}; + +/*! @name Structure to define wrist wear wake-up configuration for wearable configuration */ +struct bmi2_wrist_wear_wake_up_wh_config +{ + /*! Cosine of minimum expected attitude change of the device + * within 1 second time window when moving within focus position. + * The parameter is scaled by 2048 i.e. 2048 * cos(angle). + * Range is 1024 to 1774. Default is 1774. + */ + uint16_t min_angle_focus; + + /*! Cosine of minimum expected attitude change of the device + * within 1 second time window when moving from non-focus to focus position. + * The parameter is scaled by 2048 i.e. 2048 * cos(angle). + * Range is 1448 to 1856. Default value is 1522. + */ + uint16_t min_angle_nonfocus; + + /*! Sine of the maximum allowed tilt angle in + * landscape right direction of the device, when it is in focus position + * (i.e. user is able to comfortably look at the dial of wear device). + * The configuration parameter is scaled by 256 i.e. 256 * sin(angle). + * Range is 88 to 128. Default value is 128. + */ + uint8_t angle_landscape_right; + + /*! Sine of the maximum allowed tilt angle in + * landscape left direction of the device, when it is in focus position + * (i.e. user is able to comfortably look at the dial of wear device). + * The configuration parameter is scaled by 256 i.e. 256 * sin(angle). + * Range is 88 to 128. Default value is 128. + */ + uint8_t angle_landscape_left; + + /*! Sine of the maximum allowed backward tilt angle in + * portrait down direction of the device, when it is in focus position + * (i.e. user is able to comfortably look at the dial of wear device). + * The configuration parameter is scaled by 256 i.e. 256 * sin(angle). + * Range is 0 to179. Default value is 22. + */ + uint8_t angle_portrait_down; + + /*! Sine of the maximum allowed forward tilt angle in + * portrait up direction of the device, when it is in focus position + * (i.e. user is able to comfortably look at the dial of wear device). + * The configuration parameter is scaled by 256 i.e. 256 * sin(angle). + * Range is 222 to 247. Default value is 241. + */ + uint8_t angle_portrait_up; + + /*! Minimum duration the arm should be moved while performing gesture. + * Range: 1 to 10, resolution = 20 ms + */ + uint8_t min_dur_moved; + + /*! Minimum duration the arm should be static between two consecutive gestures. + * Range: 1 to 10, resolution = 20 ms + */ + uint8_t min_dur_quite; +}; + +/*! @name Structure to define primary OIS configuration */ +struct bmi2_primary_ois_config +{ + /*! Low pass filter control */ + uint8_t lp_filter_enabled; + + /*! Lp filter cut-off frequency */ + uint8_t lp_filter_config; + + /*! Enable gyroscope on OIS interface in registers + * OIS_DATA_6 till OIS_DATA_11 with minimum group delay at 6.4KHz ODR + */ + uint8_t gyr_en; + + /*! Enable accelerometer on OIS interface in registers + * OIS_DATA_0 till OIS_DATA_5 with minimum group delay at 1.6KHz ODR + */ + uint8_t acc_en; +}; + +/*! @name Structure to configure free-fall detection settings */ +struct bmi2_free_fall_det_config +{ + /*! free-fall accel settings */ + uint16_t freefall_accel_settings[BMI2_FREE_FALL_ACCEL_SET_PARAMS]; +}; + +/*! @name Structure to define wrist gesture configuration for wearable variant */ +struct bmi2_wrist_gest_w_config +{ + /*! Device in left (0) or right (1) arm. + * By default, the wearable device is assumed to be in left arm + * i.e. default value is 0. + */ + uint8_t device_position; + + /*! Minimum threshold for flick peak on y-axis */ + uint16_t min_flick_peak_y_threshold; + + /*! Minimum threshold for flick peak on z-axis */ + uint16_t min_flick_peak_z_threshold; + + /*! Maximum expected value of positive gravitational acceleration on x-axis + * when arm is in focus pose */ + uint16_t gravity_bounds_x_pos; + + /*! Maximum expected value of negative gravitational acceleration on x-axis + * when arm is in focus pose */ + uint16_t gravity_bounds_x_neg; + + /*! Maximum expected value of negative gravitational acceleration on y-axis + * when arm is in focus pose */ + uint16_t gravity_bounds_y_neg; + + /*! Maximum expected value of negative gravitational acceleration on z-axis + * when arm is in focus pose */ + uint16_t gravity_bounds_z_neg; + + /*! Exponential smoothing coefficient for adaptive peak threshold decay */ + uint16_t flick_peak_decay_coeff; + + /*! Exponential smoothing coefficient for acceleration mean estimation */ + uint16_t lp_mean_filter_coeff; + + /*! Maximum duration between 2 peaks of jiggle in samples @50Hz */ + uint16_t max_duration_jiggle_peaks; +}; + +/*! @name Structure to configure Laptop position recognition settings for bmi260lpd */ +struct bmi2_lpd_config +{ + /*! lpd enable bit */ + uint8_t lpd_enable; + + /*!Transition threshold to detect movement of laptop*/ + uint16_t transition_threshold; + + /*!Activity threshold to detect the typing and touch activity*/ + uint16_t activity_threshold; + + /*!Low 16 bits for Tan of X rotation tolerance low for on table*/ + uint16_t x_rot_tolerance_low; + + /*!High 16 bits for Tan of X rotation tolerance high for on table*/ + uint16_t x_rot_tolerance_high; + + /*!Low 16 bits for Tan of Y rotation tolerance low for on table*/ + uint16_t y_rot_tolerance_low; + + /*!High 16 bits for Tan of Y rotation tolerance high for on table*/ + uint16_t y_rot_tolerance_high; +}; + +/*! @name Structure to configure inbag/outbag feature*/ +struct bmi2_iob_config +{ + /*! IOB enable bit */ + uint8_t iob_enable; + + /*! Write the value 1 when the user has the false inbag output*/ + uint8_t false_inbag; + + /*! IOB Settings 1 */ + uint16_t settings_1; + + /*! IOB Settings 2 */ + uint16_t settings_2; + + /*! IOB Settings 3 */ + uint16_t settings_3; + + /*! IOB Settings 4 */ + uint16_t settings_4; + +}; + +/*! @name Structure to configure wrist gesture configuration */ +struct bmi2_wrist_gesture_config +{ + /* Minimum threshold for flick peak on y-axis */ + uint16_t min_flick_peak_y_threshold; + + /* Minimum threshold for flick peak on z-axis */ + uint16_t min_flick_peak_z_threshold; + + /* Maximum expected value of positive gravitational acceleration on x-axis when arm is in focus pose */ + uint16_t gravity_bounds_x_pos; + + /* Maximum expected value of negative gravitational acceleration on x-axis when arm is in focus pose */ + uint16_t gravity_bounds_x_neg; + + /* Maximum expected value of negaitive gravitational acceleration on y-axis when arm is in focus pose */ + uint16_t gravity_bounds_y_neg; + + /* Maximum expected value of negaitive gravitational acceleration on z-axis when arm is in focus pose */ + uint16_t gravity_bounds_z_neg; + + /* Exponential smoothing coefficient for adaptive peak threshold decay */ + uint16_t flick_peak_decay_coeff; + + /* Exponential smoothing coefficient for acceleration mean estimation */ + uint16_t lp_mean_filter_coeff; + + /* Maximum duration between 2 peaks of jiggle in samples */ + uint16_t max_duration_jiggle_peaks; + + /* Device in left (0) or right (1) arm. By default, the wearable device is assumed to be in left arm i.e. default + * value is 0. */ + uint16_t device_position; +}; + +/*! @name Structure to define door state detector configuration */ +struct bmi2_door_state_detector_config +{ + /*! DSD feature enable/disable */ + uint8_t dsd_enable; + + /*! Axis remap status */ + uint8_t remap_flag; + + /*! Map the desired axis sign to z axis */ + uint8_t z_sign; + + /*! Map the desired axis to z axis */ + uint8_t z_axis; + + /*! Apply gyro calibration bias */ + uint8_t gyro_calib_apply; + + /*! Initial calibration threshold */ + uint8_t init_calib_thr; + + /*! Manual reset enable */ + uint8_t reset_enable_flag; + + /*! Value of lower word of bias_x */ + uint16_t bias_x_low_word; + + /*! Value of higher word of bias_x */ + uint16_t bias_x_high_word; + + /*! Value of lower word of bias_y */ + uint16_t bias_y_low_word; + + /*! Value of higher word of bias_y */ + uint16_t bias_y_high_word; + + /* Value of lower word of bias_z */ + uint16_t bias_z_low_word; + + /*! Value of higher word of bias_z */ + uint16_t bias_z_high_word; + + /*! Value of door closed threshold */ + uint16_t door_closed_thr; +}; + +/*! @name Structure to configure shake configuration */ +struct bmi2_shake_config +{ + /*! Number of threshold crossing to be qualified as a shake gesture */ + uint8_t shake_number; + + /*! Magnitude threshold for linear component of acceleration to be qualified as shake gesture */ + uint8_t threshold; +}; + +/*! @name Structure to configure circle configuration */ +struct bmi2_circle_config +{ + /*! Upper threshold to continue the detection of possible circle gesture */ + uint8_t upper_threshold; + + /*! Lower threshold to continue the detection of possible circle gesture */ + uint8_t lower_threshold; + + /*! Minimum distance between the extrema at the beginning of circle and other extremas */ + uint8_t min_distance; +}; + +/*! @name Structure to configure push configuration */ +struct bmi2_push_config +{ + /*! Lower threshold to start the detection of possible push gesture */ + uint8_t push_low_threshold; + + /*! Higher threshold to continue the detection of possible push gesture */ + uint8_t push_high_threshold; +}; + +/*! @name Structure to configure extremum configuration */ +struct bmi2_extremum_config +{ + /*! Threshold for idle state */ + uint8_t extremum_idle_threshold; + + /*! Threshold for extremum detection */ + uint8_t extremum_detect_threshold; +}; + +/*! @name Structure to configure head orientation */ +struct bmi2_hmc_calibration_config +{ + /*! Sets the HMC calibration to Dynamic/Semi automatic */ + uint8_t hmc_mode; + + /*! Triggers HMC calibration as per HMC mode */ + uint8_t calib_trigger; + + /*! HMC Calibration Output Status */ + uint8_t hmc_calib_out_status; +}; + +struct bmi2_wrist_gest_det_config +{ + uint8_t enable; + uint16_t min_flick_peak_y_threshold; + uint16_t min_flick_peak_z_threshold; + uint16_t gravity_bound_x_neg; + uint16_t gravity_bound_x_pos; + uint16_t gravity_bound_y_neg; + uint16_t gravity_bound_z_neg; + uint16_t flick_peak_decay_coeff; + uint16_t lp_mean_filter_coeff; + uint16_t max_duration_jiggle_peaks; + uint16_t device_position; +}; + +/*! @name Union to define the sensor configurations */ +union bmi2_sens_config_types +{ + /*! Accelerometer configuration */ + struct bmi2_accel_config acc; + + /*! Gyroscope configuration */ + struct bmi2_gyro_config gyr; + + /*! Auxiliary configuration */ + struct bmi2_aux_config aux; + + /*! Any-motion configuration */ + struct bmi2_any_motion_config any_motion; + + /*! No-motion configuration */ + struct bmi2_no_motion_config no_motion; + + /*! Sig_motion configuration */ + struct bmi2_sig_motion_config sig_motion; + + /*! EXT TCO configuration */ + struct bmi2_ext_tco ext_tco; + + /*! Step counter parameter configuration */ + uint16_t step_counter_params[BMI2_STEP_CNT_N_PARAMS]; + + /*! Step counter/detector/activity configuration */ + struct bmi2_step_config step_counter; + + /*! Gyroscope user gain configuration */ + struct bmi2_gyro_user_gain_config gyro_gain_update; + + /*! Wake-up configuration */ + struct bmi2_wake_up_config wake_up_conf; + + /*! Tap configuration */ + struct bmi2_tap_config tap_conf; + + /*! Orientation configuration */ + struct bmi2_orient_config orientation; + + /*! High-g configuration */ + struct bmi2_high_g_config high_g; + + /*! Low-g configuration */ + struct bmi2_low_g_config low_g; + + /*! Flat configuration */ + struct bmi2_flat_config flat; + + /*! Wrist gesture configuration */ + struct bmi2_wrist_gest_config wrist_gest; + + /*! Wrist wear wake-up configuration */ + struct bmi2_wrist_wear_wake_up_config wrist_wear_wake_up; + + /*! Wrist gesture configuration for wearable variant */ + struct bmi2_wrist_gest_w_config wrist_gest_w; + + /*! Wrist wear wake-up configuration for wearable variant */ + struct bmi2_wrist_wear_wake_up_wh_config wrist_wear_wake_up_wh; + + /*! Primary OIS configuration */ + struct bmi2_primary_ois_config primary_ois; + + /*! Free-fall detection configurations */ + struct bmi2_free_fall_det_config free_fall_det; + + /*! Laptop position detection configurations */ + struct bmi2_lpd_config lap_pos_det; + + /*! Laptop inbag/outbag feature*/ + struct bmi2_iob_config iob; + + /*! Structure to configure wrist gesture configurations */ + struct bmi2_wrist_gesture_config wrist_g_config; + + /*! Structure to configure door state detector */ + struct bmi2_door_state_detector_config door_state_detector; + + /*! Shake configurations */ + struct bmi2_shake_config shake; + + /*! Circle configurations */ + struct bmi2_circle_config circle; + + /*1 Push configurations */ + struct bmi2_push_config push; + + /*! Extremum detect configurations */ + struct bmi2_extremum_config extremum; + + /*! Head orientation configurations */ + struct bmi2_hmc_calibration_config hmc_calibration_config; + + /*! Structure to configure wrist gest detector configurations */ + struct bmi2_wrist_gest_det_config wrist_gest_det; + +}; + +/*! @name Structure to define the type of the sensor and its configurations */ +struct bmi2_sens_config +{ + /*! Defines the type of sensor */ + uint8_t type; + + /*! Defines various sensor configurations */ + union bmi2_sens_config_types cfg; +}; + +/*! @name Structure to define the feature configuration */ +struct bmi2_feature_config +{ + /*! Defines the type of sensor */ + uint8_t type; + + /*! Page to where the feature is mapped */ + uint8_t page; + + /*! Address of the feature */ + uint8_t start_addr; +}; + +/*! @name Structure to define the feature interrupt configurations */ +struct bmi2_map_int +{ + /*! Defines the type of sensor */ + uint8_t type; + + /*! Defines the feature interrupt */ + uint8_t sens_map_int; +}; + +/*! @name Structure to define BMI2 sensor configurations */ +struct bmi2_dev +{ + /*! Chip id of BMI2 */ + uint8_t chip_id; + + /*! The interface pointer is used to enable the user + * to link their interface descriptors for reference during the + * implementation of the read and write interfaces to the + * hardware. + */ + void *intf_ptr; + + /*! To store warnings */ + uint8_t info; + + /*! Type of Interface */ + enum bmi2_intf intf; + + /*! To store interface pointer error */ + BMI2_INTF_RETURN_TYPE intf_rslt; + + /*! For switching from I2C to SPI */ + uint8_t dummy_byte; + + /*! Resolution for FOC */ + uint8_t resolution; + + /*! User set read/write length */ + uint16_t read_write_len; + + /*! Feature len */ + uint8_t feature_len; + + /*! Store load status value */ + uint8_t load_status; + + /*! Pointer to the configuration data buffer address */ + const uint8_t *config_file_ptr; + + /*! To define maximum page number */ + uint8_t page_max; + + /*! To define maximum number of input sensors/features */ + uint8_t input_sens; + + /*! To define maximum number of output sensors/features */ + uint8_t out_sens; + + /*! Indicate manual enable for auxiliary communication */ + uint8_t aux_man_en; + + /*! Defines manual read burst length for auxiliary communication */ + uint8_t aux_man_rd_burst_len; + + /*! Array of feature input configuration structure */ + const struct bmi2_feature_config *feat_config; + + /*! Array of feature output configuration structure */ + const struct bmi2_feature_config *feat_output; + + /*! Structure to maintain a copy of the re-mapped axis */ + struct bmi2_axes_remap remap; + + /*! Flag to hold enable status of sensors */ + uint64_t sens_en_stat; + + /*! Read function pointer */ + bmi2_read_fptr_t read; + + /*! Write function pointer */ + bmi2_write_fptr_t write; + + /*! Delay function pointer */ + bmi2_delay_fptr_t delay_us; + + /*! To store the gyroscope cross sensitivity value */ + int16_t gyr_cross_sens_zx; + + /* gyro enable status, used as a flag in CRT enabling and aborting */ + uint8_t gyro_en : 1; + + /* advance power saving mode status, used as a flag in CRT enabling and aborting */ + uint8_t aps_status; + + /* used as a flag to enable variant specific features like crt */ + uint16_t variant_feature; + + /* To store hold the size of config file */ + uint16_t config_size; + + /*! Function pointer to get wakeup configurations */ + bmi2_wake_up_fptr_t get_wakeup_config; + + /*! Function pointer to set wakeup configurations */ + bmi2_wake_up_fptr_t set_wakeup_config; + + /*! Function pointer to get tap configurations */ + bmi2_tap_fptr_t get_tap_config; + + /*! Function pointer to set tap configurations */ + bmi2_tap_fptr_t set_tap_config; + + /*! Array of feature interrupts configuration structure */ + struct bmi2_map_int *map_int; + + /*! To define maximum number of interrupts */ + uint8_t sens_int_map; +}; + +/*! @name Structure to enable an accel axis for foc */ +struct bmi2_accel_foc_g_value +{ + /*! '0' to disable x axis and '1' to enable x axis */ + uint8_t x; + + /*! '0' to disable y axis and '1' to enable y axis */ + uint8_t y; + + /*! '0' to disable z axis and '1' to enable z axis */ + uint8_t z; + + /*! '0' for positive input and '1' for negative input */ + uint8_t sign; +}; + +/*! @name Structure to configure activity recognition settings */ +struct bmi2_act_recg_sett +{ + /*! Enable/Disable post processing of the activity detected by the classifier */ + uint8_t pp_en; + + /*! Minimum threshold of the Gini's diversity index (GDI) for + * accepting and adding activity detected by the classifier to activity buffer + */ + uint16_t min_gdi_thres; + + /*! Maximum threshold of the Gini's diversity index (GDI) for + * rejecting the activity detected by the classifier + */ + uint16_t max_gdi_thres; + + /*! Buffer size for post processing of the activity detected by the classifier */ + uint8_t buf_size; + + /*! Minimum segments classified with moderate confidence as belonging + * to a certain activity type to be added to activity buffer. + */ + uint8_t min_seg_conf; +}; + +/*! @name Structure to configure activity recognition settings for bmi270hc */ +struct bmi2_hc_act_recg_sett +{ + /*! Static segment size for activity classification. */ + uint8_t segment_size; + + /*! Enable/Disable post processing of the activity detected */ + uint8_t pp_en; + + /*! Minimum threshold of the Gini's diversity index (GDI) */ + uint16_t min_gdi_thres; + + /*! Maximum threshold of the Gini's diversity index (GDI) */ + uint16_t max_gdi_thres; + + /*! Buffer size for post processing of the activity detected */ + uint16_t buf_size; + + /*! Minimum segments belonging to a certain activity type */ + uint16_t min_seg_conf; +}; + +#endif /* BMI2_DEFS_H_ */ diff --git a/projects/Compass/main/include/bmm150.h b/projects/Compass/main/include/bmm150.h new file mode 100644 index 00000000..b7e71302 --- /dev/null +++ b/projects/Compass/main/include/bmm150.h @@ -0,0 +1,479 @@ +/** +* Copyright (c) 2020 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 bmm150.h +* @date 2020-06-03 +* @version v2.0.0 +* +*/ + +/*! + * @defgroup bmm150 BMM150 + */ + +#ifndef _BMM150_H +#define _BMM150_H + +/*! CPP guard */ +#ifdef __cplusplus +extern "C" { +#endif + +/********************************************************************/ +/* header files */ + +#include "bmm150_defs.h" + +/********************************************************************/ +/* (extern) variable declarations */ +/********************************************************************/ +/* function prototype declarations */ + +/** + * \ingroup bmm150 + * \defgroup bmm150ApiInit Initialization + * @brief Initialize the sensor and device structure + */ + +/*! + * \ingroup bmm150ApiInit + * \page bmm150_api_bmm150_init bmm150_init + * \code + * int8_t bmm150_init(struct bmm150_dev *dev); + * \endcode + * @details This API is the entry point, Call this API before using other APIs. + * This API reads the chip-id of the sensor which is the first step to + * verify the sensor and also it configures the read mechanism of SPI and + * I2C interface. + * + * @param[in,out] dev : Structure instance of bmm150_dev + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +int8_t bmm150_init(struct bmm150_dev *dev); + +/** + * \ingroup bmm150 + * \defgroup bmm150ApiRegs Registers + * @brief Read / Write data to the given register address of the sensor + */ + +/*! + * \ingroup bmm150ApiRegs + * \page bmm150_api_bmm150_set_regs bmm150_set_regs + * \code + * int8_t bmm150_set_regs(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, struct bmm150_dev *dev); + * \endcode + * @details This API writes the given data to the register address + * of the sensor. + * + * @param[in] reg_addr : Register address from where the data to be written. + * @param[in] reg_data : Pointer to data buffer which is to be written + * in the reg_addr of sensor. + * @param[in] len : No of bytes of data to write.. + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +int8_t bmm150_set_regs(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, struct bmm150_dev *dev); + +/*! + * \ingroup bmm150ApiRegs + * \page bmm150_api_bmm150_get_regs bmm150_get_regs + * \code + * int8_t bmm150_get_regs(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, struct bmm150_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] reg_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 bmm150_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +int8_t bmm150_get_regs(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, struct bmm150_dev *dev); + +/** + * \ingroup bmm150 + * \defgroup bmm150ApiSoftreset Soft reset + * @brief Perform soft reset of the sensor + */ + +/*! + * \ingroup bmm150ApiSoftreset + * \page bmm150_api_bmm150_soft_reset bmm150_soft_reset + * \code + * int8_t bmm150_soft_reset(struct bmm150_dev *dev); + * \endcode + * @details This API is used to perform soft-reset of the sensor + * where all the registers are reset to their default values except 0x4B. + * + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +int8_t bmm150_soft_reset(struct bmm150_dev *dev); + +/** + * \ingroup bmm150 + * \defgroup bmm150ApiPowermode Power mode + * @brief Set / Get power mode of the sensor + */ + +/*! + * \ingroup bmm150ApiPowermode + * \page bmm150_api_bmm150_set_op_mode bmm150_set_op_mode + * \code + * int8_t bmm150_set_op_mode(const struct bmm150_settings *settings, struct bmm150_dev *dev); + * \endcode + * @details This API is used to set the power mode of the sensor. + * + * @param[in] settings : Structure instance of bmm150_settings. + * @param[in] dev : Structure instance of bmm150_dev. + * + *@verbatim + * settings->pwr_mode | Power mode + * ------------------------|----------------------- + * 0x00 | BMM150_POWERMODE_NORMAL + * 0x01 | BMM150_POWERMODE_FORCED + * 0x03 | BMM150_POWERMODE_SLEEP + * 0x04 | BMM150_POWERMODE_SUSPEND + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +int8_t bmm150_set_op_mode(const struct bmm150_settings *settings, struct bmm150_dev *dev); + +/*! + * \ingroup bmm150ApiPowermode + * \page bmm150_api_bmm150_get_op_mode bmm150_get_op_mode + * \code + * int8_t bmm150_get_op_mode(uint8_t *op_mode, struct bmm150_dev *dev); + * \endcode + * @details This API is used to get the power mode of the sensor. + * + * @param[out] op_mode : power mode of the sensor. + * @param[in] dev : Structure instance of bmm150_dev. + * + *@verbatim + * op_mode | Power mode + * -------------|----------------------- + * 0x00 | BMM150_POWERMODE_NORMAL + * 0x01 | BMM150_POWERMODE_FORCED + * 0x03 | BMM150_POWERMODE_SLEEP + * 0x04 | BMM150_POWERMODE_SUSPEND + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +int8_t bmm150_get_op_mode(uint8_t *op_mode, struct bmm150_dev *dev); + +/** + * \ingroup bmm150 + * \defgroup bmm150ApiPresetmode Preset mode + * @brief Set preset mode of the sensor + */ + +/*! + * \ingroup bmm150ApiPresetmode + * \page bmm150_api_bmm150_set_presetmode bmm150_set_presetmode + * \code + * int8_t bmm150_set_presetmode(struct bmm150_settings *settings, struct bmm150_dev *dev); + * \endcode + * @details This API is used to set the preset mode of the sensor. + * + * @param[in] settings : Structure instance of bmm150_settings. + * @param[in] dev : Structure instance of bmm150_dev. + * + *@verbatim + * settings->preset_mode | Preset mode + * ---------------------------|---------------------------------- + * 0x01 | BMM150_PRESETMODE_LOWPOWER + * 0x02 | BMM150_PRESETMODE_REGULAR + * 0x03 | BMM150_PRESETMODE_HIGHACCURACY + * 0x04 | BMM150_PRESETMODE_ENHANCED + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +int8_t bmm150_set_presetmode(struct bmm150_settings *settings, struct bmm150_dev *dev); + +/** + * \ingroup bmm150 + * \defgroup bmm150ApiSensorSettings Sensor Settings + * @brief Set / Get sensor settings of the sensor + */ + +/*! + * \ingroup bmm150ApiSensorSettings + * \page bmm150_api_bmm150_set_sensor_settings bmm150_set_sensor_settings + * \code + * int8_t bmm150_set_sensor_settings(uint16_t desired_settings, const struct bmm150_settings *settings, struct bmm150_dev *dev); + * \endcode + * @details This API sets the sensor settings based on the desired_settings + * and the dev structure configuration + * + * @param[in] desired_settings : Selection macro for selecting the setting. + * @param[in] settings : Structure instance of bmm150_settings. + * @param[in] dev : Structure instance of bmm150_dev. + * + * @note Assign the sensor setting macros (multiple macros can be + * set by doing a bitwise-OR operation) to the desired_settings parameter + * of this API to perform the corresponding setting. + * + * @note threshold interrupt for each axes are set by using bitwise AND + * operation of the following macros + * - BMM150_THRESHOLD_X + * - BMM150_THRESHOLD_Y + * - BMM150_THRESHOLD_Z + * + *@verbatim + * desired_settings | Selected sensor setting macros + * -------------------|-------------------------------- + * 0x0001 | BMM150_SEL_DATA_RATE + * 0x0002 | BMM150_SEL_CONTROL_MEASURE + * 0x0004 | BMM150_SEL_XY_REP + * 0x0008 | BMM150_SEL_Z_REP + * 0x0010 | BMM150_SEL_DRDY_PIN_EN + * 0x0020 | BMM150_SEL_INT_PIN_EN + * 0x0040 | BMM150_SEL_DRDY_POLARITY + * 0x0080 | BMM150_SEL_INT_LATCH + * 0x0100 | BMM150_SEL_INT_POLARITY + * 0x0200 | BMM150_SEL_DATA_OVERRUN_INT + * 0x0400 | BMM150_SEL_OVERFLOW_INT + * 0x0800 | BMM150_SEL_HIGH_THRESHOLD_INT + * 0x1000 | BMM150_SEL_LOW_THRESHOLD_INT + * 0x2000 | BMM150_SEL_LOW_THRESHOLD_SETTING + * 0x4000 | BMM150_SEL_HIGH_THRESHOLD_SETTING + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +int8_t bmm150_set_sensor_settings(uint16_t desired_settings, + const struct bmm150_settings *settings, + struct bmm150_dev *dev); + +/*! + * \ingroup bmm150ApiSensorSettings + * \page bmm150_api_bmm150_get_sensor_settings bmm150_get_sensor_settings + * \code + * int8_t bmm150_get_sensor_settings(struct bmm150_settings *settings, struct bmm150_dev *dev); + * \endcode + * @details This API gets all the sensor settings and updates the dev structure + * + * @param[in] settings : Structure instance of bmm150_settings. + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +int8_t bmm150_get_sensor_settings(struct bmm150_settings *settings, struct bmm150_dev *dev); + +/** + * \ingroup bmm150 + * \defgroup bmm150ApiMagData Read magnetometer data + * @brief Read magnetometer data + */ + +/*! + * \ingroup bmm150ApiMagData + * \page bmm150_api_bmm150_read_mag_data bmm150_read_mag_data + * \code + * int8_t bbmm150_read_mag_data(struct bmm150_mag_data *mag_data, struct bmm150_dev *dev); + * \endcode + * @details This API reads the magnetometer data from registers 0x42 to 0x49 + * and updates the dev structure with compensated mag data in micro-tesla + * + * @param[in] mag_data : Structure instance of bmm150_mag_data. + * @param[in,out] dev : Structure instance of bmm150_dev. + * + * @note The output mag data can be obtained either in int16_t or float format + * using this API. + * @note Enable the macro "BMM150_USE_FLOATING_POINT" in the bmm150_defs.h + * file and call this API to get the mag data in float, + * disable this macro to get the mag data in int16_t format + * + *@verbatim + * Mag data output(micro-tesla) | Mag data in dev structure(int16_t/float) + * --------------------------------|------------------------------------------ + * X-axis data | mag_data->x + * Y-axis data | mag_data->y + * Z-axis data | mag_data->z + *@endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +int8_t bmm150_read_mag_data(struct bmm150_mag_data *mag_data, struct bmm150_dev *dev); + +/** + * \ingroup bmm150 + * \defgroup bmm150ApiSelftest Self test + * @brief Perform self test + */ + +/*! + * \ingroup bmm150ApiSelftest + * \page bmm150_api_bmm150_perform_self_test bmm150_perform_self_test + * \code + * int8_t bmm150_perform_self_test(uint8_t self_test_mode, struct bmm150_dev *dev); + * \endcode + * @details This API is used to perform the complete self test + * (both normal and advanced) for the BMM150 sensor + * + * @param[in] self_test_mode : The type of self test to be performed + * @param[in] dev : Structure instance of bmm150_dev. + * + *@verbatim + * self_test_mode | Self test enabled + * --------------------|-------------------------- + * 0 | BMM150_SELF_TEST_NORMAL + * 1 | BMM150_SELF_TEST_ADVANCED + *@endverbatim + * + * @note The return value of this API gives us the result of self test. + * + * @note Performing advanced self test does soft reset of the sensor, User can + * set the desired settings after performing the advanced self test. + * + * @return Result of API execution status and self test result. + * @retval 0 BMM150_OK + * @retval 1 BMM150_W_NORMAL_SELF_TEST_YZ_FAIL + * @retval 2 BMM150_W_NORMAL_SELF_TEST_XZ_FAIL + * @retval 3 BMM150_W_NORMAL_SELF_TEST_Z_FAIL + * @retval 4 BMM150_W_NORMAL_SELF_TEST_XY_FAIL + * @retval 5 BMM150_W_NORMAL_SELF_TEST_Y_FAIL + * @retval 6 BMM150_W_NORMAL_SELF_TEST_X_FAIL + * @retval 7 BMM150_W_NORMAL_SELF_TEST_XYZ_FAIL + * @retval 8 BMM150_W_ADV_SELF_TEST_FAIL + */ +int8_t bmm150_perform_self_test(uint8_t self_test_mode, struct bmm150_dev *dev); + +/** + * \ingroup bmm150 + * \defgroup bmm150ApiInt Interrupt status + * @brief Obtain interrupt staus flags + */ + +/*! + * \ingroup bmm150ApiInt + * \page bmm150_api_bmm150_get_interrupt_status bmm150_get_interrupt_status + * \code + * int8_t bmm150_get_interrupt_status(struct bmm150_dev *dev); + * \endcode + * @details This API obtains the status flags of all interrupt + * which is used to check for the assertion of interrupts + * + * @param[in,out] dev : Structure instance of bmm150_dev. + * + * @note The status flags of all the interrupts are stored in the + * dev->int_status. + * + * @note The value of dev->int_status is performed a bitwise AND operation + * with predefined interrupt status macros to find the interrupt status + * which is either set or reset. + * + * Ex. + * if (dev->int_status & BMM150_INT_ASSERTED_DRDY) + * { + * Occurrence of data ready interrupt + * } else { + * No interrupt occurred + * } + * + * @return Result of API execution status and self test result. + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +int8_t bmm150_get_interrupt_status(struct bmm150_dev *dev); + +/** + * \ingroup bmm150 + * \defgroup bmm150ApiAux Compensate magnetometer data + * @brief Compensation of magnetometer data + */ + +/*! + * \ingroup bmm150ApiAux + * \page bmm150_api_bmm150_aux_mag_data bmm150_aux_mag_data + * \code + * int8_t bmm150_aux_mag_data(uint8_t *aux_data, struct bmm150_mag_data *mag_data, const struct bmm150_dev *dev); + * \endcode + * @details This API is used to compensate the raw mag data + * + * @param[in] aux_data : Raw mag data obtained from BMI160 registers + * @param[in] mag_data : Structure instance of bmm150_mag_data. + * @param[in,out] dev : Structure instance of bmm150_dev. + * + * @return Result of API execution status and self test result. + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +int8_t bmm150_aux_mag_data(uint8_t *aux_data, struct bmm150_mag_data *mag_data, const struct bmm150_dev *dev); + +#ifdef __cplusplus +} +#endif /* End of CPP guard */ + +#endif /* _BMM150_H */ diff --git a/projects/Compass/main/include/bmm150_aux_port.h b/projects/Compass/main/include/bmm150_aux_port.h new file mode 100644 index 00000000..63aa92ff --- /dev/null +++ b/projects/Compass/main/include/bmm150_aux_port.h @@ -0,0 +1,39 @@ +/** + * @file bmm150_aux_port.h + * @brief BMM150 通过 BMI270 AUX 接口访问的适配层 + * + * 将 BMM150 SensorAPI 的 read/write 回调映射到 BMI270 的 AUX 手动模式 API, + * 使 BMM150 驱动无需修改即可通过 BMI270 间接访问。 + */ + +#ifndef BMM150_AUX_PORT_H +#define BMM150_AUX_PORT_H + +#include +#include "bmi2.h" +#include "bmm150.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief 初始化 BMM150(通过 BMI270 AUX 接口) + * + * @param[out] dev BMM150 设备结构体 + * @param[in] bmi 已初始化完成的 BMI270 设备结构体 + * + * @return BMM150_OK (0) 成功,负值失败 + */ +int8_t bmm150_aux_port_init(struct bmm150_dev *dev, struct bmi2_dev *bmi); + +/** + * @brief 反初始化 BMM150 AUX 适配层 + */ +void bmm150_aux_port_deinit(struct bmm150_dev *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* BMM150_AUX_PORT_H */ diff --git a/projects/Compass/main/include/bmm150_defs.h b/projects/Compass/main/include/bmm150_defs.h new file mode 100644 index 00000000..fb332e2e --- /dev/null +++ b/projects/Compass/main/include/bmm150_defs.h @@ -0,0 +1,656 @@ +/** +* Copyright (c) 2020 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 bmm150_defs.h +* @date 2020-06-03 +* @version v2.0.0 +* +*/ + +/*! \file bmm150_defs.h */ + +#ifndef _BMM150_DEFS_H +#define _BMM150_DEFS_H + +/******************************************************************************/ +/*! @name Header includes */ +/******************************************************************************/ +#ifdef __KERNEL__ +#include +#else +#include +#include +#endif + +/******************************************************************************/ +/*! @name Common macros */ +/******************************************************************************/ +#ifdef __KERNEL__ +#if (LONG_MAX) > 0x7fffffff +#define __have_long64 1 +#elif (LONG_MAX) == 0x7fffffff +#define __have_long32 1 +#endif +#endif + +#if !defined(UINT8_C) +#define INT8_C(x) x +#if (INT_MAX) > 0x7f +#define UINT8_C(x) x +#else +#define UINT8_C(x) x##U +#endif +#endif + +#if !defined(UINT16_C) +#define INT16_C(x) x +#if (INT_MAX) > 0x7fff +#define UINT16_C(x) x +#else +#define UINT16_C(x) x##U +#endif +#endif + +#if !defined(INT32_C) && !defined(UINT32_C) +#if __have_long32 +#define INT32_C(x) x##L +#define UINT32_C(x) x##UL +#else +#define INT32_C(x) x +#define UINT32_C(x) x##U +#endif +#endif + +#if !defined(INT64_C) && !defined(UINT64_C) +#if __have_long64 +#define INT64_C(x) x##L +#define UINT64_C(x) x##UL +#else +#define INT64_C(x) x##LL +#define UINT64_C(x) x##ULL +#endif +#endif + +/*! @name C standard macros */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *) 0) +#endif +#endif + +/******************************************************************************/ +/*! @name Compiler switch macros Definitions */ +/******************************************************************************/ +#ifndef BMM150_USE_FLOATING_POINT /*< Check if floating point (using BMM150_USE_FLOATING_POINT) is enabled */ +#ifndef BMM150_USE_FIXED_POINT /*< If floating point is not enabled then enable BMM150_USE_FIXED_POINT */ +#define BMM150_USE_FIXED_POINT +#endif +#endif + +/******************************************************************************/ +/*! @name General Macro Definitions */ +/******************************************************************************/ +/*! @name API success code */ +#define BMM150_OK INT8_C(0) + +/*! @name To define TRUE or FALSE */ +#define BMM150_TRUE UINT8_C(1) +#define BMM150_FALSE UINT8_C(0) + +/*! @name API error codes */ +#define BMM150_E_NULL_PTR INT8_C(-1) +#define BMM150_E_DEV_NOT_FOUND INT8_C(-2) +#define BMM150_E_INVALID_CONFIG INT8_C(-3) +#define BMM150_E_COM_FAIL INT8_C(-4) + +/*! @name API warning codes */ +#define BMM150_W_NORMAL_SELF_TEST_YZ_FAIL INT8_C(1) +#define BMM150_W_NORMAL_SELF_TEST_XZ_FAIL INT8_C(2) +#define BMM150_W_NORMAL_SELF_TEST_Z_FAIL INT8_C(3) +#define BMM150_W_NORMAL_SELF_TEST_XY_FAIL INT8_C(4) +#define BMM150_W_NORMAL_SELF_TEST_Y_FAIL INT8_C(5) +#define BMM150_W_NORMAL_SELF_TEST_X_FAIL INT8_C(6) +#define BMM150_W_NORMAL_SELF_TEST_XYZ_FAIL INT8_C(7) +#define BMM150_W_ADV_SELF_TEST_FAIL INT8_C(8) + +/*! @name CHIP ID & SOFT RESET VALUES */ +#define BMM150_CHIP_ID UINT8_C(0x32) +#define BMM150_SET_SOFT_RESET UINT8_C(0x82) + +/*! @name POWER MODE DEFINTIONS */ +#define BMM150_POWERMODE_NORMAL UINT8_C(0x00) +#define BMM150_POWERMODE_FORCED UINT8_C(0x01) +#define BMM150_POWERMODE_SLEEP UINT8_C(0x03) +#define BMM150_POWERMODE_SUSPEND UINT8_C(0x04) + +/*! @name Power mode settings */ +#define BMM150_POWER_CNTRL_DISABLE UINT8_C(0x00) +#define BMM150_POWER_CNTRL_ENABLE UINT8_C(0x01) + +/*! @name I2C ADDRESS */ +#define BMM150_DEFAULT_I2C_ADDRESS UINT8_C(0x10) +#define BMM150_I2C_ADDRESS_CSB_LOW_SDO_HIGH UINT8_C(0x11) +#define BMM150_I2C_ADDRESS_CSB_HIGH_SDO_LOW UINT8_C(0x12) +#define BMM150_I2C_ADDRESS_CSB_HIGH_SDO_HIGH UINT8_C(0x13) + +/*! @name Sensor delay time settings */ +#define BMM150_DELAY_SOFT_RESET UINT8_C(1000) +#define BMM150_DELAY_NORMAL_SELF_TEST UINT8_C(2000) +#define BMM150_START_UP_TIME UINT8_C(3000) +#define BMM150_DELAY_ADV_SELF_TEST UINT8_C(4000) + +/*! @name ENABLE/DISABLE DEFINITIONS */ +#define BMM150_XYZ_CHANNEL_ENABLE UINT8_C(0x00) +#define BMM150_XYZ_CHANNEL_DISABLE UINT8_C(0x07) + +/*! @name Register Address */ +#define BMM150_REG_CHIP_ID UINT8_C(0x40) +#define BMM150_REG_DATA_X_LSB UINT8_C(0x42) +#define BMM150_REG_DATA_READY_STATUS UINT8_C(0x48) +#define BMM150_REG_INTERRUPT_STATUS UINT8_C(0x4A) +#define BMM150_REG_POWER_CONTROL UINT8_C(0x4B) +#define BMM150_REG_OP_MODE UINT8_C(0x4C) +#define BMM150_REG_INT_CONFIG UINT8_C(0x4D) +#define BMM150_REG_AXES_ENABLE UINT8_C(0x4E) +#define BMM150_REG_LOW_THRESHOLD UINT8_C(0x4F) +#define BMM150_REG_HIGH_THRESHOLD UINT8_C(0x50) +#define BMM150_REG_REP_XY UINT8_C(0x51) +#define BMM150_REG_REP_Z UINT8_C(0x52) + +/*! @name Macros to select the sensor settings to be set by the user + * These values are internal for API implementation. Don't relate this to + * data sheet. + */ +#define BMM150_SEL_DATA_RATE UINT16_C(1) +#define BMM150_SEL_CONTROL_MEASURE UINT16_C(1 << 1) +#define BMM150_SEL_XY_REP UINT16_C(1 << 2) +#define BMM150_SEL_Z_REP UINT16_C(1 << 3) +#define BMM150_SEL_DRDY_PIN_EN UINT16_C(1 << 4) +#define BMM150_SEL_INT_PIN_EN UINT16_C(1 << 5) +#define BMM150_SEL_DRDY_POLARITY UINT16_C(1 << 6) +#define BMM150_SEL_INT_LATCH UINT16_C(1 << 7) +#define BMM150_SEL_INT_POLARITY UINT16_C(1 << 8) +#define BMM150_SEL_DATA_OVERRUN_INT UINT16_C(1 << 9) +#define BMM150_SEL_OVERFLOW_INT UINT16_C(1 << 10) +#define BMM150_SEL_HIGH_THRESHOLD_INT UINT16_C(1 << 11) +#define BMM150_SEL_LOW_THRESHOLD_INT UINT16_C(1 << 12) +#define BMM150_SEL_LOW_THRESHOLD_SETTING UINT16_C(1 << 13) +#define BMM150_SEL_HIGH_THRESHOLD_SETTING UINT16_C(1 << 14) + +/*! @name DATA RATE DEFINITIONS */ +#define BMM150_DATA_RATE_10HZ UINT8_C(0x00) +#define BMM150_DATA_RATE_02HZ UINT8_C(0x01) +#define BMM150_DATA_RATE_06HZ UINT8_C(0x02) +#define BMM150_DATA_RATE_08HZ UINT8_C(0x03) +#define BMM150_DATA_RATE_15HZ UINT8_C(0x04) +#define BMM150_DATA_RATE_20HZ UINT8_C(0x05) +#define BMM150_DATA_RATE_25HZ UINT8_C(0x06) +#define BMM150_DATA_RATE_30HZ UINT8_C(0x07) +#define BMM150_ODR_MAX UINT8_C(0x07) +#define BMM150_ODR_MSK UINT8_C(0x38) +#define BMM150_ODR_POS UINT8_C(0x03) + +/*! @name TRIM REGISTERS */ +/* Trim Extended Registers */ +#define BMM150_DIG_X1 UINT8_C(0x5D) +#define BMM150_DIG_Y1 UINT8_C(0x5E) +#define BMM150_DIG_Z4_LSB UINT8_C(0x62) +#define BMM150_DIG_Z4_MSB UINT8_C(0x63) +#define BMM150_DIG_X2 UINT8_C(0x64) +#define BMM150_DIG_Y2 UINT8_C(0x65) +#define BMM150_DIG_Z2_LSB UINT8_C(0x68) +#define BMM150_DIG_Z2_MSB UINT8_C(0x69) +#define BMM150_DIG_Z1_LSB UINT8_C(0x6A) +#define BMM150_DIG_Z1_MSB UINT8_C(0x6B) +#define BMM150_DIG_XYZ1_LSB UINT8_C(0x6C) +#define BMM150_DIG_XYZ1_MSB UINT8_C(0x6D) +#define BMM150_DIG_Z3_LSB UINT8_C(0x6E) +#define BMM150_DIG_Z3_MSB UINT8_C(0x6F) +#define BMM150_DIG_XY2 UINT8_C(0x70) +#define BMM150_DIG_XY1 UINT8_C(0x71) + +/*! @name Threshold interrupt setting macros for x,y,z axes selection */ +#define BMM150_THRESHOLD_X UINT8_C(0x06) +#define BMM150_THRESHOLD_Y UINT8_C(0x05) +#define BMM150_THRESHOLD_Z UINT8_C(0x03) + +#define BMM150_HIGH_THRESHOLD_INT_MSK UINT8_C(0x38) +#define BMM150_HIGH_THRESHOLD_INT_POS UINT8_C(0x03) +#define BMM150_LOW_THRESHOLD_INT_MSK UINT8_C(0x07) + +/*! @name User configurable interrupt setting macros */ +#define BMM150_INT_ENABLE UINT8_C(0x01) +#define BMM150_INT_DISABLE UINT8_C(0x00) +#define BMM150_ACTIVE_HIGH_POLARITY UINT8_C(0x01) +#define BMM150_ACTIVE_LOW_POLARITY UINT8_C(0x00) +#define BMM150_LATCHED UINT8_C(0x01) +#define BMM150_NON_LATCHED UINT8_C(0x00) + +/*! @name Interrupt status */ +#define BMM150_INT_THRESHOLD_X_LOW UINT16_C(0x0001) +#define BMM150_INT_THRESHOLD_Y_LOW UINT16_C(0x0002) +#define BMM150_INT_THRESHOLD_Z_LOW UINT16_C(0x0004) +#define BMM150_INT_THRESHOLD_X_HIGH UINT16_C(0x0008) +#define BMM150_INT_THRESHOLD_Y_HIGH UINT16_C(0x0010) +#define BMM150_INT_THRESHOLD_Z_HIGH UINT16_C(0x0020) +#define BMM150_INT_DATA_OVERFLOW UINT16_C(0x0040) +#define BMM150_INT_DATA_OVERRUN UINT16_C(0x0080) +#define BMM150_INT_DATA_READY UINT16_C(0x0100) + +#define BMM150_DRDY_EN_MSK UINT8_C(0x80) +#define BMM150_DRDY_EN_POS UINT8_C(0x07) +#define BMM150_DRDY_POLARITY_MSK UINT8_C(0x04) +#define BMM150_DRDY_POLARITY_POS UINT8_C(0x02) +#define BMM150_INT_PIN_EN_MSK UINT8_C(0x40) +#define BMM150_INT_PIN_EN_POS UINT8_C(0x06) +#define BMM150_INT_LATCH_MSK UINT8_C(0x02) +#define BMM150_INT_LATCH_POS UINT8_C(0x01) +#define BMM150_INT_POLARITY_MSK UINT8_C(0x01) +#define BMM150_DRDY_STATUS_MSK UINT8_C(0x01) + +/*! @name Interrupt status macros */ +#define BMM150_INT_ASSERTED_DRDY UINT16_C(0x0100) +#define BMM150_INT_ASSERTED_LOW_THRES UINT16_C(0x0007) +#define BMM150_INT_ASSERTED_HIGH_THRES UINT16_C(0x0380) + +/*! @name Power control bit macros */ +#define BMM150_PWR_CNTRL_MSK UINT8_C(0x01) +#define BMM150_CONTROL_MEASURE_MSK UINT8_C(0x38) +#define BMM150_CONTROL_MEASURE_POS UINT8_C(0x03) +#define BMM150_POWER_CONTROL_BIT_MSK UINT8_C(0x01) +#define BMM150_POWER_CONTROL_BIT_POS UINT8_C(0x00) + +/*! @name Data macros */ +#define BMM150_DATA_X_MSK UINT8_C(0xF8) +#define BMM150_DATA_X_POS UINT8_C(0x03) + +#define BMM150_DATA_Y_MSK UINT8_C(0xF8) +#define BMM150_DATA_Y_POS UINT8_C(0x03) + +#define BMM150_DATA_Z_MSK UINT8_C(0xFE) +#define BMM150_DATA_Z_POS UINT8_C(0x01) + +#define BMM150_DATA_RHALL_MSK UINT8_C(0xFC) +#define BMM150_DATA_RHALL_POS UINT8_C(0x02) + +#define BMM150_DATA_OVERRUN_INT_MSK UINT8_C(0x80) +#define BMM150_DATA_OVERRUN_INT_POS UINT8_C(0x07) + +#define BMM150_OVERFLOW_INT_MSK UINT8_C(0x40) +#define BMM150_OVERFLOW_INT_POS UINT8_C(0x06) + +/*! @name OVERFLOW DEFINITIONS */ +#define BMM150_OVERFLOW_ADCVAL_XYAXES_FLIP INT16_C(-4096) +#define BMM150_OVERFLOW_ADCVAL_ZAXIS_HALL INT16_C(-16384) +#define BMM150_OVERFLOW_OUTPUT INT16_C(-32768) +#define BMM150_NEGATIVE_SATURATION_Z INT16_C(-32767) +#define BMM150_POSITIVE_SATURATION_Z INT16_C(32767) +#ifdef BMM150_USE_FLOATING_POINT +#define BMM150_OVERFLOW_OUTPUT_FLOAT 0.0f +#endif + +/*! @name PRESET MODE DEFINITIONS */ +#define BMM150_PRESETMODE_LOWPOWER UINT8_C(0x01) +#define BMM150_PRESETMODE_REGULAR UINT8_C(0x02) +#define BMM150_PRESETMODE_HIGHACCURACY UINT8_C(0x03) +#define BMM150_PRESETMODE_ENHANCED UINT8_C(0x04) + +#define BMM150_OP_MODE_MSK UINT8_C(0x06) +#define BMM150_OP_MODE_POS UINT8_C(0x01) + +/*! @name PRESET MODES - REPETITIONS-XY RATES */ +#define BMM150_REPXY_LOWPOWER UINT8_C(0x01) +#define BMM150_REPXY_REGULAR UINT8_C(0x04) +#define BMM150_REPXY_ENHANCED UINT8_C(0x07) +#define BMM150_REPXY_HIGHACCURACY UINT8_C(0x17) + +/*! @name PRESET MODES - REPETITIONS-Z RATES */ +#define BMM150_REPZ_LOWPOWER UINT8_C(0x01) +#define BMM150_REPZ_REGULAR UINT8_C(0x07) +#define BMM150_REPZ_ENHANCED UINT8_C(0x0D) +#define BMM150_REPZ_HIGHACCURACY UINT8_C(0x29) + +/*! @name Self test settings */ +#define BMM150_DISABLE_XY_AXIS UINT8_C(0x03) +#define BMM150_SELF_TEST_REP_Z UINT8_C(0x04) + +/*! @name Self test selection macros */ +#define BMM150_SELF_TEST_NORMAL UINT8_C(0) +#define BMM150_SELF_TEST_ADVANCED UINT8_C(1) + +/*! @name Advanced self-test current settings */ +#define BMM150_DISABLE_SELF_TEST_CURRENT UINT8_C(0x00) +#define BMM150_ENABLE_NEGATIVE_CURRENT UINT8_C(0x02) +#define BMM150_ENABLE_POSITIVE_CURRENT UINT8_C(0x03) + +/*! @name Normal self-test status */ +#define BMM150_SELF_TEST_STATUS_XYZ_FAIL UINT8_C(0x00) +#define BMM150_SELF_TEST_STATUS_SUCCESS UINT8_C(0x07) + +#define BMM150_SELF_TEST_MSK UINT8_C(0x01) +#define BMM150_ADV_SELF_TEST_MSK UINT8_C(0xC0) +#define BMM150_ADV_SELF_TEST_POS UINT8_C(0x06) + +/*! @name Register read lengths */ +#define BMM150_LEN_SELF_TEST UINT8_C(5) +#define BMM150_LEN_SETTING_DATA UINT8_C(8) +#define BMM150_LEN_XYZR_DATA UINT8_C(8) + +/*! @name Boundary check macros */ +#define BMM150_BOUNDARY_MAXIMUM UINT8_C(0) +#define BMM150_BOUNDARY_MINIMUM UINT8_C(1) + +/*! @name Macro to SET and GET BITS of a register*/ +#define BMM150_SET_BITS(reg_data, bitname, data) \ + ((reg_data & ~(bitname##_MSK)) | \ + ((data << bitname##_POS) & bitname##_MSK)) + +#define BMM150_GET_BITS(reg_data, bitname) ((reg_data & (bitname##_MSK)) >> \ + (bitname##_POS)) + +#define BMM150_SET_BITS_POS_0(reg_data, bitname, data) \ + ((reg_data & ~(bitname##_MSK)) | \ + (data & bitname##_MSK)) + +#define BMM150_GET_BITS_POS_0(reg_data, bitname) (reg_data & (bitname##_MSK)) + +/********************************************************/ + +/*! + * @brief Interface selection Enums + */ +enum bmm150_intf { + /*! SPI interface */ + BMM150_SPI_INTF, + /*! I2C interface */ + BMM150_I2C_INTF +}; + +/******************************************************************************/ +/*! @name Function Pointers */ +/******************************************************************************/ +#ifndef BMM150_INTF_RET_TYPE +#define BMM150_INTF_RET_TYPE int8_t +#endif + +#ifndef BMM150_INTF_RET_SUCCESS +#define BMM150_INTF_RET_SUCCESS INT8_C(0) +#endif + +/*! + * @brief Bus communication function pointer which should be mapped to + * the platform specific read functions of the user + * + * @param[in] reg_addr : 8bit register address of the sensor + * @param[out] reg_data : Data from the specified address + * @param[in] length : Length of the reg_data array + * @param[in,out] intf_ptr : Void pointer that can enable the linking of descriptors + * for interface related callbacks + * @retval 0 for Success + * @retval Non-zero for Failure + */ +typedef BMM150_INTF_RET_TYPE (*bmm150_read_fptr_t)(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, + void *intf_ptr); + +/*! + * @brief Bus communication function pointer which should be mapped to + * the platform specific write functions of the user + * + * @param[in] reg_addr : 8bit register address of the sensor + * @param[out] reg_data : Data to the specified address + * @param[in] length : Length of the reg_data array + * @param[in,out] intf_ptr : Void pointer that can enable the linking of descriptors + * for interface related callbacks + * @retval 0 for Success + * @retval Non-zero for Failure + * + */ +typedef BMM150_INTF_RET_TYPE (*bmm150_write_fptr_t)(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, + void *intf_ptr); + +/*! + * @brief Delay function pointer which should be mapped to + * delay function of the user + * + * @param period : The time period in microseconds + * @param[in,out] intf_ptr : Void pointer that can enable the linking of descriptors + * for interface related callbacks + */ +typedef void (*bmm150_delay_us_fptr_t)(uint32_t period, void *intf_ptr); + +/******************************************************************************/ +/*! @name Structure Declarations */ +/******************************************************************************/ + +/*! + * @brief bmm150 trim data structure + */ +struct bmm150_trim_registers +{ + /*! trim x1 data */ + int8_t dig_x1; + + /*! trim y1 data */ + int8_t dig_y1; + + /*! trim x2 data */ + int8_t dig_x2; + + /*! trim y2 data */ + int8_t dig_y2; + + /*! trim z1 data */ + uint16_t dig_z1; + + /*! trim z2 data */ + int16_t dig_z2; + + /*! trim z3 data */ + int16_t dig_z3; + + /*! trim z4 data */ + int16_t dig_z4; + + /*! trim xy1 data */ + uint8_t dig_xy1; + + /*! trim xy2 data */ + int8_t dig_xy2; + + /*! trim xyz1 data */ + uint16_t dig_xyz1; +}; + +/*! + * @brief bmm150 interrupt pin settings + */ +struct bmm150_int_ctrl_settings +{ + /*! Data ready interrupt enable */ + uint8_t drdy_pin_en; + + /*! Threshold and overflow interrupts enable */ + uint8_t int_pin_en; + + /*! Data ready interrupt polarity Active high/low */ + uint8_t drdy_polarity; + + /*! Interrupt pin - Latched or Non-latched */ + uint8_t int_latch; + + /*! Interrupt polarity Active high/low */ + uint8_t int_polarity; + + /*! Data overrun interrupt enable */ + uint8_t data_overrun_en; + + /*! Overflow interrupt enable */ + uint8_t overflow_int_en; + + /*! high interrupt enable/disable axis selection */ + uint8_t high_int_en; + + /*! low interrupt enable/disable axis selection */ + uint8_t low_int_en; + + /*! low threshold limit */ + uint8_t low_threshold; + + /*! high threshold limit */ + uint8_t high_threshold; +}; + +/*! + * @brief bmm150 sensor settings + */ +struct bmm150_settings +{ + /*! Control measurement of XYZ axes */ + uint8_t xyz_axes_control; + + /*! Power mode of sensor */ + uint8_t pwr_mode; + + /*! Data rate value (ODR) */ + uint8_t data_rate; + + /*! XY Repetitions */ + uint8_t xy_rep; + + /*! Z Repetitions */ + uint8_t z_rep; + + /*! Preset mode of sensor */ + uint8_t preset_mode; + + /*! Interrupt configuration settings */ + struct bmm150_int_ctrl_settings int_settings; +}; + +/*! + * @brief bmm150 un-compensated (raw) magnetometer data + */ +struct bmm150_raw_mag_data +{ + /*! Raw mag X data */ + int16_t raw_datax; + + /*! Raw mag Y data */ + int16_t raw_datay; + + /*! Raw mag Z data */ + int16_t raw_dataz; + + /*! Raw mag resistance value */ + uint16_t raw_data_r; +}; + +#ifdef BMM150_USE_FLOATING_POINT + +/*! + * @brief bmm150 compensated magnetometer data in float + */ +struct bmm150_mag_data +{ + /*! compensated mag X data */ + float x; + + /*! compensated mag Y data */ + float y; + + /*! compensated mag Z data */ + float z; +}; + +#else + +/*! + * @brief bmm150 compensated magnetometer data in int16_t format + */ +struct bmm150_mag_data +{ + /*! compensated mag X data */ + int16_t x; + + /*! compensated mag Y data */ + int16_t y; + + /*! compensated mag Z data */ + int16_t z; +}; + +#endif + +/*! + * @brief bmm150 device structure + */ +struct bmm150_dev +{ + /*! Chip Id */ + uint8_t chip_id; + + /*! SPI/I2C Interface */ + enum bmm150_intf intf; + + /*! + * The interface pointer is used to enable the user + * to link their interface descriptors for reference during the + * implementation of the read and write interfaces to the + * hardware. + */ + void *intf_ptr; + + /*! Variable that holds result of read/write function */ + BMM150_INTF_RET_TYPE intf_rslt; + + /*! Bus read function pointer */ + bmm150_read_fptr_t read; + + /*! Bus write function pointer */ + bmm150_write_fptr_t write; + + /*! delay(in us) function pointer */ + bmm150_delay_us_fptr_t delay_us; + + /*! Trim registers */ + struct bmm150_trim_registers trim_data; + + /*! Interrupt status */ + uint16_t int_status; + + /*! Power control bit value */ + uint8_t pwr_cntrl_bit; +}; + +#endif /* BMM150_DEFS_H_ */ diff --git a/projects/Compass/main/include/compass_app.h b/projects/Compass/main/include/compass_app.h new file mode 100644 index 00000000..2c9bbb2c --- /dev/null +++ b/projects/Compass/main/include/compass_app.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct CompassState { + bool sensorReady = false; + bool calibrating = false; + float yaw = 0.0f; + float pitch = 0.0f; + float roll = 0.0f; + float accX = 0.0f, accY = 0.0f, accZ = 0.0f; + float gyrX = 0.0f, gyrY = 0.0f, gyrZ = 0.0f; + float magX = 0.0f, magY = 0.0f, magZ = 0.0f; + float temp = 0.0f; + std::string statusText; +}; + +class ICompassView { +public: + virtual ~ICompassView() = default; + virtual void update(const CompassState& state) = 0; + virtual void setActionHandler(std::function handler) = 0; +}; + +class CompassApp { +public: + CompassApp(); + ~CompassApp(); + + bool init(); + void deinit(); + + void setView(ICompassView* view); + + // Unified action entry point + void onAction(const std::string& action); + + // Call from main loop + void poll(); + + // Build current state snapshot + CompassState getState() const; + +private: + void notifyView(); + void sensorThreadFunc(); + bool initSensors(); + void deinitSensors(); + + ICompassView* view_ = nullptr; + + mutable std::mutex stateMutex_; + CompassState state_; + + std::thread sensorThread_; + std::atomic running_{false}; + std::atomic stateDirty_{false}; + std::atomic requestCalibrate_{false}; + + // BMI270 / BMM150 handles (opaque pointers to keep header clean) + void* bmiHandle_ = nullptr; + void* bmmHandle_ = nullptr; +}; diff --git a/projects/Compass/main/include/compat/input_keys.h b/projects/Compass/main/include/compat/input_keys.h new file mode 100644 index 00000000..da5eefe3 --- /dev/null +++ b/projects/Compass/main/include/compat/input_keys.h @@ -0,0 +1,82 @@ +#pragma once +// Cross-platform KEY_* constants +// On Linux: use the real header. On macOS/Windows: define ourselves. + +#ifdef __linux__ +#include +#else + +#ifndef KEY_ESC +#define KEY_ESC 1 +#define KEY_1 2 +#define KEY_2 3 +#define KEY_3 4 +#define KEY_4 5 +#define KEY_5 6 +#define KEY_6 7 +#define KEY_7 8 +#define KEY_8 9 +#define KEY_9 10 +#define KEY_0 11 +#define KEY_BACKSPACE 14 +#define KEY_TAB 15 +#define KEY_Q 16 +#define KEY_W 17 +#define KEY_E 18 +#define KEY_R 19 +#define KEY_T 20 +#define KEY_Y 21 +#define KEY_U 22 +#define KEY_I 23 +#define KEY_O 24 +#define KEY_P 25 +#define KEY_ENTER 28 +#define KEY_LEFTCTRL 29 +#define KEY_A 30 +#define KEY_S 31 +#define KEY_D 32 +#define KEY_F 33 +#define KEY_G 34 +#define KEY_H 35 +#define KEY_J 36 +#define KEY_K 37 +#define KEY_L 38 +#define KEY_LEFTSHIFT 42 +#define KEY_Z 44 +#define KEY_X 45 +#define KEY_C 46 +#define KEY_V 47 +#define KEY_B 48 +#define KEY_N 49 +#define KEY_M 50 +#define KEY_SPACE 57 +#define KEY_LEFTALT 56 +#define KEY_CAPSLOCK 58 +#define KEY_F1 59 +#define KEY_F2 60 +#define KEY_F3 61 +#define KEY_F4 62 +#define KEY_F5 63 +#define KEY_F6 64 +#define KEY_F7 65 +#define KEY_F8 66 +#define KEY_F9 67 +#define KEY_F10 68 +#define KEY_F11 87 +#define KEY_F12 88 +#define KEY_KPENTER 96 +#define KEY_HOME 102 +#define KEY_UP 103 +#define KEY_PAGEUP 104 +#define KEY_LEFT 105 +#define KEY_RIGHT 106 +#define KEY_END 107 +#define KEY_DOWN 108 +#define KEY_PAGEDOWN 109 +#define KEY_INSERT 110 +#define KEY_DELETE 111 +#define KEY_NEXT 407 +#define KEY_PREVIOUS 412 +#endif + +#endif // __linux__ diff --git a/projects/Compass/main/include/keyboard_input.h b/projects/Compass/main/include/keyboard_input.h new file mode 100644 index 00000000..c26a5393 --- /dev/null +++ b/projects/Compass/main/include/keyboard_input.h @@ -0,0 +1,46 @@ +#ifndef __MAIN__H__ +#define __MAIN__H__ + +#include +#include +#include +#ifdef __cplusplus +extern "C" { +#endif + +// Modifier key bitmap +#define KBD_MOD_SHIFT (1u << 0) +#define KBD_MOD_CTRL (1u << 1) +#define KBD_MOD_ALT (1u << 2) +#define KBD_MOD_LOGO (1u << 3) +#define KBD_MOD_CAPS (1u << 4) +#define KBD_MOD_NUM (1u << 5) + +// Key state +#define KBD_KEY_RELEASED 0 +#define KBD_KEY_PRESSED 1 +#define KBD_KEY_REPEATED 2 + +struct key_item { + uint32_t key_code; // Linux evdev key code + uint32_t keysym; // Primary XKB keysym + uint32_t codepoint; // Unicode codepoint, 0 if none + uint32_t mods; // Modifier bitmap (KBD_MOD_*) + int key_state; // 0=released, 1=pressed, 2=repeated + char sym_name[65]; // XKB keysym name + char utf8[16]; // UTF-8 character + char flage; // Flag: needs free + STAILQ_ENTRY(key_item) entries; +}; + +STAILQ_HEAD(keyboard_queue_t, key_item); +extern struct keyboard_queue_t keyboard_queue; +extern pthread_mutex_t keyboard_mutex; +extern volatile int LVGL_HOME_KEY_FLAGE; +extern volatile int LVGL_RUN_FLAGE; +extern volatile uint32_t LV_EVENT_KEYBOARD; +void *keyboard_read_thread(void *argv); +#ifdef __cplusplus +} +#endif +#endif diff --git a/projects/Compass/main/include/ui_compass.h b/projects/Compass/main/include/ui_compass.h new file mode 100644 index 00000000..b566229f --- /dev/null +++ b/projects/Compass/main/include/ui_compass.h @@ -0,0 +1,44 @@ +#pragma once + +#include "compass_app.h" +#include "lvgl/lvgl.h" +#include + +class UiCompass : public ICompassView { +public: + UiCompass() = default; + + void init(lv_obj_t* parent); + void update(const CompassState& state) override; + void setActionHandler(std::function handler) override; + + // Keyboard event from input system + void onKeyPressed(uint32_t key_code, bool isRepeat = false); + void onCharTyped(uint32_t codepoint); + +private: + void buildUi(lv_obj_t* parent); + void createStatusBar(lv_obj_t* parent); + + lv_obj_t* parent_ = nullptr; + + // Status bar + lv_obj_t* statusBar_ = nullptr; + lv_obj_t* lblStatusText_ = nullptr; + + // Compass heading (large) + lv_obj_t* lblHeading_ = nullptr; + lv_obj_t* lblYaw_ = nullptr; + + // Sensor values + lv_obj_t* lblAccel_ = nullptr; + lv_obj_t* lblGyro_ = nullptr; + lv_obj_t* lblMag_ = nullptr; + lv_obj_t* lblTemp_ = nullptr; + lv_obj_t* lblPitchRoll_ = nullptr; + + // State cache + CompassState lastState_{}; + + std::function actionHandler_; +}; diff --git a/projects/Compass/main/src/bmi2.c b/projects/Compass/main/src/bmi2.c new file mode 100644 index 00000000..bbbf186e --- /dev/null +++ b/projects/Compass/main/src/bmi2.c @@ -0,0 +1,11702 @@ +/** +* Copyright (c) 2025 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 bmi2.c +* @date 2025-04-22 +* @version v2.113.0 +* +*/ + +/******************************************************************************/ + +/*! @name Header Files */ +/******************************************************************************/ +#include "bmi2.h" + +/***************************************************************************/ + +/*! Local structures + ****************************************************************************/ + +/*! @name Structure to define the difference in accelerometer values */ +struct bmi2_selftest_delta_limit +{ + /*! X data */ + int32_t x; + + /*! Y data */ + int32_t y; + + /*! Z data */ + int32_t z; +}; + +/*! @name Structure to store temporary accelerometer/gyroscope values */ +struct bmi2_foc_temp_value +{ + /*! X data */ + int32_t x; + + /*! Y data */ + int32_t y; + + /*! Z data */ + int32_t z; +}; + +/*! @name Structure to store accelerometer data deviation from ideal value */ +struct bmi2_offset_delta +{ + /*! X axis */ + int16_t x; + + /*! Y axis */ + int16_t y; + + /*! Z axis */ + int16_t z; +}; + +/*! @name Structure to store accelerometer offset values */ +struct bmi2_accel_offset +{ + /*! offset X data */ + uint8_t x; + + /*! offset Y data */ + uint8_t y; + + /*! offset Z data */ + uint8_t z; +}; + +/******************************************************************************/ + +/*! Local Function Prototypes + ******************************************************************************/ + +/*! + * @brief This internal API writes the configuration file. + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t write_config_file(struct bmi2_dev *dev); + +/*! + * @brief This internal API enables/disables the loading of the configuration + * file. + * + * @param[in] enable : To enable/disable configuration load. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_config_load(uint8_t enable, struct bmi2_dev *dev); + +/*! + * @brief This internal API loads the configuration file. + * + * @param[in] config_data : Pointer to the configuration file. + * @param[in] index : Variable to define array index. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t upload_file(const uint8_t *config_data, uint16_t index, uint16_t write_len, struct bmi2_dev *dev); + +/*! + * @brief This internal API sets accelerometer configurations like ODR, + * bandwidth, performance mode and g-range. + * + * @param[in,out] config : Structure instance of bmi2_accel_config. + * @param[in,out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_accel_config(struct bmi2_accel_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API validates bandwidth and performance mode of the + * accelerometer set by the user. + * + * @param[in, out] bandwidth : Pointer to bandwidth value set by the user. + * @param[in, out] perf_mode : Pointer to performance mode set by the user. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @note dev->info contains two warnings: BMI2_I_MIN_VALUE and BMI2_I_MAX_VALUE + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t validate_bw_perf_mode(uint8_t *bandwidth, uint8_t *perf_mode, struct bmi2_dev *dev); + +/*! + * @brief This internal API validates ODR and range of the accelerometer set by + * the user. + * + * @param[in, out] odr : Pointer to ODR value set by the user. + * @param[in, out] range : Pointer to range value set by the user. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @note dev->info contains two warnings: BMI2_I_MIN_VALUE and BMI2_I_MAX_VALUE + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t validate_odr_range(uint8_t *odr, uint8_t *range, struct bmi2_dev *dev); + +/*! + * @brief This internal API sets gyroscope configurations like ODR, bandwidth, + * low power/high performance mode, performance mode and range. + * + * @param[in,out] config : Structure instance of bmi2_gyro_config. + * @param[in,out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_gyro_config(struct bmi2_gyro_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API validates bandwidth, performance mode, low power/ + * high performance mode, ODR, and range set by the user. + * + * @param[in] config : Structure instance of bmi2_gyro_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @note dev->info contains two warnings: BMI2_I_MIN_VALUE and BMI2_I_MAX_VALUE + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t validate_gyro_config(struct bmi2_gyro_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API shows the error status when illegal sensor + * configuration is set. + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t cfg_error_status(struct bmi2_dev *dev); + +/*! + * @brief This internal API: + * 1) Enables/Disables auxiliary interface. + * 2) Sets auxiliary interface configurations like I2C address, manual/auto + * mode enable, manual burst read length, AUX burst read length and AUX read + * address. + * 3)It maps/un-maps data interrupts to that of hardware interrupt line. + * + * @param[in] config : Structure instance of bmi2_aux_config. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_aux_config(struct bmi2_aux_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API sets gyroscope user-gain configurations like gain + * update value for x, y and z-axis. + * + * @param[in] config : Structure instance of bmi2_gyro_user_gain_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @verbatim + *---------------------------------------------------------------------------- + * bmi2_gyro_user_gain_config| + * Structure parameters | Description + *--------------------------|-------------------------------------------------- + * ratio_x | Gain update value for x-axis + * -------------------------|--------------------------------------------------- + * ratio_y | Gain update value for y-axis + * -------------------------|--------------------------------------------------- + * ratio_z | Gain update value for z-axis + * @endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_gyro_user_gain_config(const struct bmi2_gyro_user_gain_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API enables/disables auxiliary interface. + * + * @param[in] config : Structure instance of bmi2_aux_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_aux_interface(const struct bmi2_aux_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API sets auxiliary configurations like manual/auto mode + * FCU write command enable and read burst length for both data and manual mode. + * + * @param[in] config : Structure instance of bmi2_aux_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @note Auxiliary sensor should not be busy when configuring aux_i2c_addr, + * man_rd_burst_len, aux_rd_burst_len and aux_rd_addr. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t config_aux_interface(const struct bmi2_aux_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API triggers read out offset and sets ODR of the + * auxiliary sensor. + * + * @param[in] config : Structure instance of bmi2_aux_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t config_aux(const struct bmi2_aux_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API validates auxiliary configuration set by the user. + * + * @param[in, out] config : Structure instance of bmi2_aux_config. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @note dev->info contains two warnings: BMI2_I_MIN_VALUE and BMI2_I_MAX_VALUE + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t validate_aux_config(struct bmi2_aux_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets accelerometer configurations like ODR, + * bandwidth, performance mode and g-range. + * + * @param[out] config : Structure instance of bmi2_accel_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_accel_config(struct bmi2_accel_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets gyroscope configurations like ODR, bandwidth, + * low power/ high performance mode, performance mode and range. + * + * @param[out] config : Structure instance of bmi2_gyro_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_gyro_config(struct bmi2_gyro_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API: + * 1) Gets the status of auxiliary interface enable. + * 2) Gets auxiliary interface configurations like I2C address, manual/auto + * mode enable, manual burst read length, AUX burst read length and AUX read + * address. + * 3) Gets ODR and offset. + * + * @param[out] config : Structure instance of bmi2_aux_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_aux_config(struct bmi2_aux_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets gyroscope user-gain configurations like gain + * update value for x, y and z-axis. + * + * @param[out] config : Structure instance of bmi2_gyro_user_gain_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @verbatim + *---------------------------------------------------------------------------- + * bmi2_gyro_user_gain_config| + * Structure parameters | Description + *-------------------------|-------------------------------------------------- + * ratio_x | Gain update value for x-axis + * ------------------------|--------------------------------------------------- + * ratio_y | Gain update value for y-axis + * ------------------------|--------------------------------------------------- + * ratio_z | Gain update value for z-axis + * + * @endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_gyro_gain_update_config(struct bmi2_gyro_user_gain_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets the enable status of auxiliary interface. + * + * @param[out] config : Structure instance of bmi2_aux_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_aux_interface(struct bmi2_aux_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets auxiliary configurations like manual/auto mode + * FCU write command enable and read burst length for both data and manual mode. + * + * @param[out] config : Structure instance of bmi2_aux_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_aux_interface_config(struct bmi2_aux_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets read out offset and ODR of the auxiliary + * sensor. + * + * @param[out] config : Structure instance of bmi2_aux_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_aux_cfg(struct bmi2_aux_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets the saturation status for the gyroscope user + * gain update. + * + * @param[out] user_gain_stat : Stores the saturation status of the axes. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_gyro_gain_update_status(struct bmi2_gyr_user_gain_status *user_gain, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to extract the output feature configuration + * details like page and start address from the look-up table. + * + * @param[out] feat_output : Structure that stores output feature + * configurations. + * @param[in] type : Type of feature or sensor. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Returns the feature found flag. + * + * @retval BMI2_FALSE : Feature not found + * BMI2_TRUE : Feature found + */ +static uint8_t extract_output_feat_config(struct bmi2_feature_config *feat_output, + uint8_t type, + const struct bmi2_dev *dev); + +/*! + * @brief This internal API gets the cross sensitivity coefficient between + * gyroscope's X and Z axes. + * + * @param[out] cross_sense : Pointer to the stored cross sensitivity + * coefficient. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_gyro_cross_sense(int16_t *cross_sense, struct bmi2_dev *dev); + +/*! + * @brief This internal API parses the accelerometer/gyroscope data. + * + * @param[out] data : Structure instance of bmi2_sens_axes_data. + * @param[in] reg_data : Data stored in the register. + * + * @return None + * + * @retval None + */ +static void get_acc_gyr_data(struct bmi2_sens_axes_data *data, const uint8_t *reg_data); + +/*! + * @brief This internal API gets the re-mapped accelerometer/gyroscope data. + * + * @param[out] data : Structure instance of bmi2_sens_axes_data. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return None + * + * @retval None + */ +static void get_remapped_data(struct bmi2_sens_axes_data *data, const struct bmi2_dev *dev); + +/*! + * @brief This internal API reads the user-defined bytes of data from the given + * register address of auxiliary sensor in manual mode. + * + * @param[in] reg_addr : AUX address from where data is read. + * @param[out] aux_data : Pointer to the stored auxiliary data. + * @param[in] len : Total bytes to be read. + * @param[in] burst_len : Bytes of data to be read in bursts. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t read_aux_data(uint8_t reg_addr, uint8_t *aux_data, uint16_t len, uint8_t burst_len, struct bmi2_dev *dev); + +/*! + * @brief This internal API checks the busy status of auxiliary sensor and sets + * the auxiliary register addresses when not busy. + * + * @param[in] reg_addr : Address in which AUX register address is + * set. + * @param[in] reg_data : Auxiliary register address to be set when AUX is + * not busy. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_if_aux_not_busy(uint8_t reg_addr, uint8_t reg_data, struct bmi2_dev *dev); + +/*! + * @brief his internal API maps the actual burst read length with that of the + * register value set by user. + * + * @param[out] len : Actual burst length. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t map_read_len(uint8_t *len, const struct bmi2_dev *dev); + +/*! + * @brief This internal API writes AUX write address and the user-defined bytes + * of data to the AUX sensor in manual mode. + * + * @note Change of BMI2_AUX_WR_ADDR is only allowed if AUX is not busy. + * + * @param[in] reg_addr : AUX address in which data is to be written. + * @param[in] reg_data : Data to be written + * @param[in] dev : Structure instance of bmi2_dev. + * + * @note Change of BMI2_AUX_WR_ADDR is only allowed if AUX is not busy. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t write_aux_data(uint8_t reg_addr, uint8_t reg_data, struct bmi2_dev *dev); + +/*! + * @brief This internal API maps/unmaps feature interrupts to that of interrupt + * pins. + * + * @param[in] int_pin : Interrupt pin selected. + * @param[in] feat_int : Type of feature interrupt to be mapped. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t map_feat_int(uint8_t *reg_data_array, enum bmi2_hw_int_pin int_pin, uint8_t int_mask); + +/*! + * @brief This internal API computes the number of bytes of accelerometer FIFO + * data which is to be parsed in header-less mode. + * + * @param[out] start_idx : The start index for parsing data. + * @param[out] len : Number of bytes to be parsed. + * @param[out] skip_length : Number of bytes to skip if dummy frame is obtained + * @param[in] acc_count : Number of accelerometer frames to be read. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t parse_fifo_accel_len(uint16_t *start_idx, + uint16_t *len, + uint8_t *skip_length, + const uint16_t *acc_count, + const struct bmi2_fifo_frame *fifo); + +/*! + * @brief This internal API is used to parse accelerometer data from the FIFO + * data in header mode. + * + * @param[out] acc : Structure instance of bmi2_sens_axes_data where + * the parsed accelerometer data bytes are stored. + * @param[in] accel_length : Number of accelerometer frames (x,y,z data). + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t extract_accel_header_mode(struct bmi2_sens_axes_data *acc, + uint16_t *accel_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to parse accelerometer data from the FIFO + * data in headerless mode. + * + * @param[out] acc : Structure instance of bmi2_sens_axes_data where + * the parsed accelerometer data bytes are stored. + * @param[in] accel_length : Number of accelerometer frames (x,y,z data). + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t extract_accel_headerless_mode(struct bmi2_sens_axes_data *acc, + uint16_t *accel_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to skip dummy frames in FIFO headerless mode + * + * @param[in] dummy_frame_header : Dummy frame header byte value in FIFO headerless mode + * + * ----------------------------------|-------------------- + * dummy_frame_header | Applicable to + * ----------------------------------|-------------------- + * BMI2_FIFO_HEADERLESS_DUMMY_ACC | ACCEL + * BMI2_FIFO_HEADERLESS_DUMMY_GYR | GYRO + * BMI2_FIFO_HEADERLESS_DUMMY_AUX | AUX + * ----------------------------------|-------------------- + * + * @param[in] data_index : Index value of number of bytes parsed. + * @param[in] skip_length : Number of bytes to skip if dummy frame is obtained + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t check_dummy_frame(uint8_t dummy_frame_header, + uint16_t *data_index, + uint8_t skip_length, + const struct bmi2_fifo_frame *fifo); + +/*! + * @brief This internal API is used to parse the accelerometer data from the + * FIFO data in header mode. It updates the current data + * byte to be parsed. + * + * @param[in,out] acc : Structure instance of bmi2_sens_axes_data where + * where the parsed data bytes are stored. + * @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) + * frame to be parsed. + * @param[in] frame : Either data is enabled by user in header-less + * mode or header frame value in header mode. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t unpack_accel_header_frame(struct bmi2_sens_axes_data *acc, + uint16_t *idx, + uint16_t *acc_idx, + uint8_t frame, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to parse the accelerometer data from the + * FIFO data in header-less mode. It updates the current data + * byte to be parsed. + * + * @param[in,out] acc : Structure instance of bmi2_sens_axes_data where + * where the parsed data bytes are stored. + * @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) + * frame to be parsed. + * @param[in] frame : Either data is enabled by user in header-less + * mode or header frame value in header mode. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t unpack_accel_headerless_frame(struct bmi2_sens_axes_data *acc, + uint16_t *idx, + uint16_t *acc_idx, + uint8_t frame, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to parse accelerometer data from the FIFO + * data. + * + * @param[out] acc : Structure instance of bmi2_sens_axes_data + * where the parsed data bytes are stored. + * @param[in] data_start_index : Index value of the accelerometer data bytes + * which is to be parsed from the FIFO data. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return None + * @retval None + */ +static void unpack_accel_data(struct bmi2_sens_axes_data *acc, + uint16_t data_start_index, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * @brief This internal API computes the number of bytes of gyroscope FIFO data + * which is to be parsed in header-less mode. + * + * @param[out] start_idx : The start index for parsing data. + * @param[out] len : Number of bytes to be parsed. + * @param[out] skip_length : Number of bytes to skip if dummy frame is obtained + * @param[in] gyr_count : Number of gyroscope frames to be read. + * @param[in] frame : Either data enabled by user in header-less + * mode or header frame value in header mode. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t parse_fifo_gyro_len(uint16_t *start_idx, + uint16_t *len, + uint8_t *skip_length, + const uint16_t *gyr_count, + const struct bmi2_fifo_frame *fifo); + +/*! + * @brief This internal API is used to parse the gyroscope data from the FIFO + * data in header mode It updates the current data byte to + * be parsed. + * + * @param[in,out] gyr : Structure instance of bmi2_sens_axes_data. + * @param[in,out] idx : Index value of number of bytes parsed + * @param[in,out] gyr_idx : Index value of gyroscope data (x,y,z axes) + * frame to be parsed. + * @param[in] frame : Either data is enabled by user in header-less + * mode or header frame value in header mode. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t unpack_gyro_header_frame(struct bmi2_sens_axes_data *gyr, + uint16_t *idx, + uint16_t *gyr_idx, + uint8_t frame, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to parse the gyroscope data from the FIFO + * data in header-less mode It updates the current data byte to + * be parsed. + * + * @param[in,out] gyr : Structure instance of bmi2_sens_axes_data. + * @param[in,out] idx : Index value of number of bytes parsed + * @param[in,out] gyr_idx : Index value of gyroscope data (x,y,z axes) + * frame to be parsed. + * @param[in] frame : Either data is enabled by user in header-less + * mode or header frame value in header mode. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t unpack_gyro_headerless_frame(struct bmi2_sens_axes_data *gyr, + uint16_t *idx, + uint16_t *gyr_idx, + uint8_t frame, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to parse gyroscope data from the FIFO data. + * + * @param[out] gyr : Structure instance of bmi2_sens_axes_data where + * the parsed gyroscope data bytes are stored. + * @param[in] data_start_index : Index value of the gyroscope data bytes + * which is to be parsed from the FIFO data. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return None + * @retval None + */ +static void unpack_gyro_data(struct bmi2_sens_axes_data *gyr, + uint16_t data_start_index, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to parse the gyroscope data from the + * FIFO data in header mode. + * + * @param[out] gyr : Structure instance of bmi2_sens_axes_data where + * the parsed gyroscope data bytes are stored. + * @param[in] gyro_length : Number of gyroscope frames (x,y,z data). + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t extract_gyro_header_mode(struct bmi2_sens_axes_data *gyr, + uint16_t *gyro_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to parse gyroscope data from the FIFO + * data in headerless mode. + * + * @param[out] gyr : Structure instance of bmi2_sens_axes_data where + * the parsed gyroscope data bytes are stored. + * @param[in] gyro_length : Number of gyroscope frames (x,y,z data). + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t extract_gyro_headerless_mode(struct bmi2_sens_axes_data *gyr, + uint16_t *gyro_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * @brief This API computes the number of bytes of auxiliary FIFO data + * which is to be parsed in header-less mode. + * + * @param[out] start_idx : The start index for parsing data. + * @param[out] len : Number of bytes to be parsed. + * @param[out] skip_length : Number of bytes to skip if dummy frame is obtained + * @param[in] aux_count : Number of accelerometer frames to be read. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t parse_fifo_aux_len(uint16_t *start_idx, + uint16_t *len, + uint8_t *skip_length, + const uint16_t *aux_count, + const struct bmi2_fifo_frame *fifo); + +/*! + * @brief This API is used to parse auxiliary data from the FIFO data. + * + * @param[out] aux : Pointer to buffer where the parsed auxiliary data + * bytes are stored. + * @param[in] aux_length : Number of auxiliary frames (x,y,z data). + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t extract_aux_header_mode(struct bmi2_aux_fifo_data *aux, + uint16_t *aux_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to parse auxiliary data from the FIFO + * data in headerless mode. + * + * @param[out] aux : Structure instance of bmi2_sens_axes_data where + * the parsed auxiliary data bytes are stored. + * @param[in] aux_length : Number of auxiliary frames (x,y,z data). + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t extract_aux_headerless_mode(struct bmi2_aux_fifo_data *aux, + uint16_t *aux_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * @brief This API is used to parse the auxiliary data from the FIFO data in + * both header and header-less mode. It updates the current data byte to be + * parsed. + * + * @param[out] aux : Pointer to structure where the parsed auxiliary data + * bytes are stored. + * @param[in,out] idx : Index value of number of bytes parsed + * @param[in,out] aux_idx : Index value of auxiliary data (x,y,z axes) + * frame to be parsed + * @param[in] frame : Either data is enabled by user in header-less + * mode or header frame value in header mode. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t unpack_aux_frame(struct bmi2_aux_fifo_data *aux, + uint16_t *idx, + uint16_t *aux_idx, + uint8_t frame, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * @brief This API is used to parse auxiliary data from the FIFO data. + * + * @param[out] aux : Pointer to structure where the parsed + * auxiliary data bytes are stored. + * @param[in] data_start_index : Index value of the auxiliary data bytes which + * is to be parsed from the FIFO data. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * + * @return None + * @retval None + */ +static void unpack_aux_data(struct bmi2_aux_fifo_data *aux, + uint16_t data_start_index, + const struct bmi2_fifo_frame *fifo); + +/*! + * @brief This internal API is used to reset the FIFO related configurations + * in the FIFO frame structure for the next FIFO read. + * + * @param[in, out] fifo : Structure instance of bmi2_fifo_frame. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t reset_fifo_frame_structure(struct bmi2_fifo_frame *fifo, struct bmi2_dev *dev); + +/*! + * @brief This internal API checks whether the FIFO data read is an empty frame. + * If empty frame, index is moved to the last byte. + * + * @param[in,out] data_index : The index of the current data to be parsed from + * FIFO data. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * + * @return Result of API execution status + * + * @retval BMI2_OK - Success. + * @retval BMI2_W_FIFO_EMPTY - Warning : FIFO is empty + * @retval BMI2_W_PARTIAL_READ - Warning : There are more frames to be read + */ +static int8_t check_empty_fifo(uint16_t *data_index, const struct bmi2_fifo_frame *fifo); + +/*! + * @brief This internal 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 the current frame. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t move_next_frame(uint16_t *data_index, uint8_t current_frame_length, const struct bmi2_fifo_frame *fifo); + +/*! + * @brief This internal API is used to parse and store the sensor time from the + * FIFO data. + * + * @param[in,out] data_index : Index of the FIFO data which has the sensor time. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t unpack_sensortime_frame(uint16_t *data_index, struct bmi2_fifo_frame *fifo); + +/*! + * @brief This internal API is used to parse and store the skipped frame count + * from the FIFO data. + * + * @param[in,out] data_index : Index of the FIFO data which contains skipped + * frame count. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t unpack_skipped_frame(uint16_t *data_index, struct bmi2_fifo_frame *fifo); + +/*! + * @brief This internal API enables and configures the accelerometer which is + * needed for self-test operation. It also sets the amplitude for the self-test. + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t pre_self_test_config(struct bmi2_dev *dev); + +/*! + * @brief This internal API performs the steps needed for self-test operation + * before reading the accelerometer self-test data. + * + * @param[in] sign : Selects sign of self-test excitation + * @param[in] dev : Structure instance of bmi2_dev. + * + * sign | Description + * -------------|--------------- + * BMI2_ENABLE | positive excitation + * BMI2_DISABLE | negative excitation + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t self_test_config(uint8_t sign, struct bmi2_dev *dev); + +/*! + * @brief This internal API enables or disables the accelerometer self-test + * feature in the sensor. + * + * @param[in] enable : Enables/ Disables self-test. + * @param[in] dev : Structure instance of bmi2_dev. + * + * sign | Description + * -------------|--------------- + * BMI2_ENABLE | Enables self-test + * BMI2_DISABLE | Disables self-test + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_accel_self_test_enable(uint8_t enable, struct bmi2_dev *dev); + +/*! + * @brief This internal API selects the sign for accelerometer self-test + * excitation. + * + * @param[in] sign : Selects sign of self-test excitation + * @param[in] dev : Structure instance of bmi2_dev. + * + * sign | Description + * -------------|--------------- + * BMI2_ENABLE | positive excitation + * BMI2_DISABLE | negative excitation + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_acc_self_test_sign(uint8_t sign, struct bmi2_dev *dev); + +/*! + * @brief This internal API sets the amplitude of the accelerometer self-test + * deflection in the sensor. + * + * @param[in] amp : Select amplitude of the self-test deflection. + * @param[in] dev : Structure instance of bmi2_dev. + * + * amp | Description + * -------------|--------------- + * BMI2_ENABLE | self-test amplitude is high + * BMI2_DISABLE | self-test amplitude is low + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_accel_self_test_amp(uint8_t amp, struct bmi2_dev *dev); + +/*! + * @brief This internal API reads the accelerometer data for x,y and z axis from + * the sensor. The data units is in LSB format. + * + * @param[out] accel : Buffer to store the acceleration value. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t read_accel_xyz(struct bmi2_sens_axes_data *accel, struct bmi2_dev *dev); + +/*! + * @brief This internal API converts LSB value of accelerometer axes to form + * 'g' to 'mg' for self-test. + * + * @param[in] acc_data_diff : Stores the acceleration value difference in g. + * @param[out]acc_data_diff_mg : Stores the acceleration value difference in mg. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return None + * @retval None + */ +static void convert_lsb_g(const struct bmi2_selftest_delta_limit *acc_data_diff, + struct bmi2_selftest_delta_limit *acc_data_diff_mg, + const struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to calculate the power of a value. + * + * @param[in] base : base for power calculation. + * @param[in] resolution : exponent for power calculation. + * + * @return the calculated power + * @retval the power value + */ +static int32_t power(int16_t base, uint8_t resolution); + +/*! + * @brief This internal API validates the accelerometer self-test data and + * decides the result of self-test operation. + * + * @param[in] accel_data_diff : Stores the acceleration value difference. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t validate_self_test(const struct bmi2_selftest_delta_limit *accel_data_diff); + +/*! + * @brief This internal API gets the re-mapped x, y and z axes from the sensor. + * + * @param[out] remap : Structure that stores local copy of re-mapped axes. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_remap_axes(struct bmi2_axes_remap *remap, struct bmi2_dev *dev); + +/*! + * @brief This internal API sets the re-mapped x, y and z axes in the sensor. + * + * @param[in] remap : Structure that stores local copy of re-mapped axes. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_remap_axes(const struct bmi2_axes_remap *remap, struct bmi2_dev *dev); + +/*! + * @brief Interface to get max burst length + * + * @param[in] max_burst_len : Pointer to store max burst length + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_maxburst_len(uint8_t *max_burst_len, struct bmi2_dev *dev); + +/*! + * @brief This internal API sets the max burst length. + * + * @param[in] write_len_byte : read & write length + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_maxburst_len(const uint16_t write_len_byte, struct bmi2_dev *dev); + +/*! + * @brief This internal API parses virtual frame header from the FIFO data. + * + * @param[in, out] frame_header : FIFO frame header. + * @param[in, out] data_index : Index value of the FIFO data bytes + * from which sensor frame header is to be parsed + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * + * @return None + * @retval None + */ +static void parse_if_virtual_header(uint8_t *frame_header, uint16_t *data_index, const struct bmi2_fifo_frame *fifo); + +/*! + * @brief This internal API gets sensor time from the accelerometer and + * gyroscope virtual frames and updates in the data structure. + * + * @param[out] sens : Sensor data structure + * @param[in, out] idx : Index of FIFO from where the data is to retrieved. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * + * @return None + * @retval None + */ +static void unpack_virt_sensor_time(struct bmi2_sens_axes_data *sens, uint16_t *idx, + const struct bmi2_fifo_frame *fifo); + +/*! + * @brief This internal API gets sensor time from the auxiliary virtual + * frames and updates in the data structure. + * + * @param[out] aux : Auxiliary sensor data structure + * @param[in, out] idx : Index of FIFO from where the data is to retrieved. + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * + * @return None + * @retval None + */ +static void unpack_virt_aux_sensor_time(struct bmi2_aux_fifo_data *aux, + uint16_t *idx, + const struct bmi2_fifo_frame *fifo); + +/*! + * @brief This internal API clips the gyroscope cross-axis sensitivity within signed 16-bit limit + * + * @param[in] value : Gyro cross-axis sensitivity value to be saturated. + * @param[out] saturation_val : Saturation value to be clipped. + * + * @return Result of API execution status + * + * @return Result of API execution status + * @retval Saturated gyro cross-axis sensitivity value + */ +static int16_t saturate(int32_t value, uint16_t saturation_val); + +/*! + * @brief This internal API corrects the gyroscope cross-axis sensitivity + * between the z and the x axis. + * + * @param[in] dev : Structure instance of bmi2_dev. + * @param[out] gyr_data : Structure instance of gyroscope data + * + * @return Result of API execution status + * + * @return None + * @retval None + */ +static void comp_gyro_cross_axis_sensitivity(struct bmi2_sens_axes_data *gyr_data, const struct bmi2_dev *dev); + +/*! + * @brief This internal API saves the configurations before performing FOC. + * + * @param[out] acc_cfg : Accelerometer configuration value + * @param[out] aps : Advance power mode value + * @param[out] acc_en : Accelerometer enable value + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t save_accel_foc_config(struct bmi2_accel_config *acc_cfg, + uint8_t *aps, + uint8_t *acc_en, + struct bmi2_dev *dev); + +/*! + * @brief This internal API performs Fast Offset Compensation for accelerometer. + * + * @param[in] accel_g_value : This parameter selects the accel foc + * axis to be performed + * + * input format is {x, y, z, sign}. '1' to enable. '0' to disable + * + * eg to choose x axis {1, 0, 0, 0} + * eg to choose -x axis {1, 0, 0, 1} + * + * @param[in] acc_cfg : Accelerometer configuration value + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t perform_accel_foc(const struct bmi2_accel_foc_g_value *accel_g_value, + const struct bmi2_accel_config *acc_cfg, + struct bmi2_dev *dev); + +/*! + * @brief This internal sets configurations for performing accelerometer FOC. + * + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_accel_foc_config(struct bmi2_dev *dev); + +/*! + * @brief This internal API converts the range value into accelerometer + * corresponding integer value. + * + * @param[in] range_in : Input range value. + * @param[out] range_out : Stores the integer value of range. + * + * @return None + * @retval None + */ +static void map_accel_range(uint8_t range_in, uint8_t *range_out); + +/*! + * @brief This internal API compensate the accelerometer data against gravity. + * + * @param[in] lsb_per_g : LSB value pre 1g. + * @param[in] g_val : G reference value of all axis. + * @param[in] data : Accelerometer data + * @param[out] comp_data : Stores the data that is compensated by taking the + * difference in accelerometer data and lsb_per_g + * value. + * + * @return None + * @retval None + */ +static void comp_for_gravity(uint16_t lsb_per_g, + const struct bmi2_accel_foc_g_value *g_val, + const struct bmi2_sens_axes_data *data, + struct bmi2_offset_delta *comp_data); + +/*! + * @brief This internal API scales the compensated accelerometer data according + * to the offset register resolution. + * + * @param[in] range : G-range of the accelerometer. + * @param[out] comp_data : Data that is compensated by taking the + * difference in accelerometer data and lsb_per_g + * value. + * @param[out] data : Stores offset data + * + * @return None + * @retval None + */ +static void scale_accel_offset(uint8_t range, const struct bmi2_offset_delta *comp_data, + struct bmi2_accel_offset *data); + +/*! + * @brief This internal API finds the bit position of 3.9mg according to given + * range and resolution. + * + * @param[in] range : G-range of the accelerometer. + * + * @return Result of API execution status + * @retval Bit position of 3.9mg + */ +static int8_t get_bit_pos_3_9mg(uint8_t range); + +/*! + * @brief This internal API inverts the accelerometer offset data. + * + * @param[out] offset_data : Stores the inverted offset data + * + * @return None + * @retval None + */ +static void invert_accel_offset(struct bmi2_accel_offset *offset_data); + +/*! + * @brief This internal API writes the offset data in the offset compensation + * register. + * + * @param[in] offset : offset data + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t write_accel_offset(const struct bmi2_accel_offset *offset, struct bmi2_dev *dev); + +/*! + * @brief This internal API restores the configurations saved before performing + * accelerometer FOC. + * + * @param[in] acc_cfg : Accelerometer configuration value + * @param[in] acc_en : Accelerometer enable value + * @param[in] aps : Advance power mode value + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t restore_accel_foc_config(struct bmi2_accel_config *acc_cfg, + uint8_t aps, + uint8_t acc_en, + struct bmi2_dev *dev); + +/*! + * @brief This internal API saves the configurations before performing gyroscope + * FOC. + * + * @param[out] gyr_cfg : Gyroscope configuration value + * @param[out] gyr_en : Gyroscope enable value + * @param[out] aps : Advance power mode value + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t save_gyro_config(struct bmi2_gyro_config *gyr_cfg, uint8_t *aps, uint8_t *gyr_en, struct bmi2_dev *dev); + +/*! + * @brief This internal sets configurations for performing gyroscope FOC. + * + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_gyro_foc_config(struct bmi2_dev *dev); + +/*! + * @brief This internal API inverts the gyroscope offset data. + * + * @param[out] offset_data : Stores the inverted offset data + * + * @return None + * @retval None + */ +static void invert_gyro_offset(struct bmi2_sens_axes_data *offset_data); + +/*! + * @brief This internal API restores the gyroscope configurations saved + * before performing FOC. + * + * @param[in] gyr_cfg : Gyroscope configuration value + * @param[in] gyr_en : Gyroscope enable value + * @param[in] aps : Advance power mode value + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t restore_gyro_config(struct bmi2_gyro_config *gyr_cfg, uint8_t aps, uint8_t gyr_en, struct bmi2_dev *dev); + +/*! + * @brief This internal API saturates the gyroscope data value before writing to + * to 10 bit offset register. + * + * @param[in, out] gyr_off : Gyroscope data to be stored in offset register + * + * @return None + * @retval None + */ +static void saturate_gyro_data(struct bmi2_sens_axes_data *gyr_off); + +/*! + * @brief This internal API reads the gyroscope data for x, y and z axis from + * the sensor. + * + * @param[out] gyro : Buffer to store the gyroscope value. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t read_gyro_xyz(struct bmi2_sens_axes_data *gyro, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to check the boundary conditions. + * + * @param[in] dev : Structure instance of bmi2_dev. + * @param[in,out] val : Pointer to the value to be validated. + * @param[in] min : minimum value. + * @param[in] max : maximum value. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t check_boundary_val(uint8_t *val, uint8_t min, uint8_t max, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to validate the device pointer for + * null conditions. + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t null_ptr_check(const struct bmi2_dev *dev); + +/*! + * @brief This updates the result for CRT or gyro self-test. + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t crt_gyro_st_update_result(struct bmi2_dev *dev); + +/*! + * @brief This function is to get the st_status status. + * + * @param[in] *st_status: gets the crt running status + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_st_running(uint8_t *st_status, struct bmi2_dev *dev); + +/*! + * @brief This function is to set crt bit to running. + * + * @param[in] enable + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_st_running(uint8_t st_status, struct bmi2_dev *dev); + +/*! + * @brief This function is to make the initial changes for CRT. + * Disable the gyro, OIS, aps + * Note: For the purpose of preparing CRT Gyro, OIS and APS are disabled + * + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t crt_prepare_setup(struct bmi2_dev *dev); + +/*! + * @brief This API is to run the crt process for both max burst length 0 and non zero condition. + * + * @param[in] gyro_st_crt : Update the gyro self-test crt enable bit + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t do_gtrigger_test(uint8_t gyro_st_crt, struct bmi2_dev *dev); + +/*! + * @brief This function is to get the rdy for dl bit status + * this will toggle from 0 to 1 and visevers according to the + * dowload status + * + * @param[in] *rdy_for_dl: gets the rdy_for_dl status + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_rdy_for_dl(uint8_t *rdy_for_dl, struct bmi2_dev *dev); + +/*! + * @brief This function is to write the config file in the given location for crt. + * which inter checks the status of the rdy_for_dl bit and also the crt running, and + * wirtes the given size. + * + * @param[in] write_len: length of the words to be written + * @param[in] config_file_size: length of the words to be written + * @param[in] start_index: provide the start index from where config file has to written + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t write_crt_config_file(uint16_t write_len, + uint16_t config_file_size, + uint16_t start_index, + struct bmi2_dev *dev); + +/*! + * @brief This function is to check for rdy_for_dl bit to toggle for CRT process + * + * @param[in] retry_complete: wait for given time to toggle + * @param[in] download_ready: get the status for rdy_for_dl + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t wait_rdy_for_dl_toggle(uint8_t retry_complete, uint8_t download_ready, struct bmi2_dev *dev); + +/*! + * @brief This function is to wait till the CRT or gyro self-test process is completed + * + * @param[in] retry_complete: wait for given time to complete the crt process + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t wait_st_running(uint8_t retry_complete, struct bmi2_dev *dev); + +/*! + * @brief This function is to complete the crt process if max burst length is not zero + * this checks for the crt status and rdy_for_dl bit to toggle + * + * @param[in] last_byte_flag: to provide the last toggled state + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t process_crt_download(uint8_t last_byte_flag, struct bmi2_dev *dev); + +/*! + * @brief This api is used to enable the gyro self-test or crt. + * + * @param[in] gyro_st_crt : Update the gyro self-test crt enable bit + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t select_self_test(uint8_t gyro_st_crt, struct bmi2_dev *dev); + +/*! + * @brief This api is used to enable/disable abort. + * + * @param[in] abort_enable : Variable to enable the abort feature. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t abort_bmi2(uint8_t abort_enable, struct bmi2_dev *dev); + +/*! + * @brief This api is use to wait till gyro self-test is completed and update the status of gyro + * self-test. + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t gyro_self_test_completed(struct bmi2_gyro_self_test_status *gyro_st_result, struct bmi2_dev *dev); + +/*! + * @brief This api is used to trigger the preparation for system for NVM programming. + * + * @param[out] nvm_prep : Pointer to variable to store the status of nvm_prep_prog. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_nvm_prep_prog(uint8_t nvm_prep, struct bmi2_dev *dev); + +/*! + * @brief This internal api gets major and minor version for config file + * + * @param[out] config_major : Pointer to store the major version + * @param[out] config_minor : Pointer to store the minor version + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t extract_config_file(uint8_t *config_major, uint8_t *config_minor, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to map the interrupts to the sensor. + * + * @param[in] map_int : Structure instance of bmi2_map_int. + * @param[in] type : Type of feature or sensor. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return None + * @retval None + */ +static void extract_feat_int_map(struct bmi2_map_int *map_int, uint8_t type, const struct bmi2_dev *dev); + +/*! + * @brief This internal API selects the sensors/features to be enabled or + * disabled. + * + * @param[in] sens_list : Pointer to select the sensor. + * @param[in] n_sens : Number of sensors selected. + * @param[out] sensor_sel : Gets the selected sensor. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t select_sensor(const uint8_t *sens_list, uint8_t n_sens, uint64_t *sensor_sel); + +/*! + * @brief This internal API enables the selected sensor/features. + * + * @param[in] sensor_sel : Selects the desired sensor. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t sensor_enable(uint64_t sensor_sel, struct bmi2_dev *dev); + +/*! + * @brief This internal API disables the selected sensor/features. + * + * @param[in] sensor_sel : Selects the desired sensor. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t sensor_disable(uint64_t sensor_sel, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to test gyro CRT. + * + * @param[in] max_burst_length : Variable to store maximum burst length. + * @param[in] gyro_st_crt : Update the gyro self-test crt enable bit. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t gyro_crt_test(uint8_t max_burst_length, uint8_t gyro_st_crt, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to unpack virtual auxillary sensortime data. + * + * @param[out] aux : Pointer to structure where the parsed auxiliary data + * bytes are stored. + * @param[in,out] idx : Index value of number of bytes parsed + * @param[in,out] aux_idx : Index value of auxiliary data (x,y,z axes) + * frame to be parsed + * @param[in] fifo : Structure instance of bmi2_fifo_frame. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return None + * + * @retval None + */ +static void unpack_virtual_aux_data(struct bmi2_aux_fifo_data *aux, + uint16_t *idx, + const uint16_t *aux_idx, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to verify the right position of the sensor before doing accel FOC. + * + * @param[in] sens_list : Sensor type + * @param[in] accel_g_axis : Accel Foc axis and sign input + * @param[in] dev : Structure instance of bmi2_dev + * + * @return Result of API execution status + * + * @return 0 -> Success + * @return < 0 -> Fail + */ +static int8_t verify_foc_position(uint8_t sens_list, + const struct bmi2_accel_foc_g_value *accel_g_axis, + struct bmi2_dev *dev); + +/*! + * @brief This internal API reads and provides average for 128 samples of sensor data for accel FOC. + * + * @param[in] sens_list : Sensor type. + * @param[in] temp_foc_data : To store data samples + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * + * @return 0 -> Success + * @return < 0 -> Fail + */ +static int8_t get_average_of_sensor_data(uint8_t sens_list, + struct bmi2_foc_temp_value *temp_foc_data, + struct bmi2_dev *dev); + +/*! + * @brief This internal API validates accel FOC position as per the range + * + * @param[in] sens_list : Sensor type + * @param[in] accel_g_axis : Accel axis to FOC. + * @param[in] avg_foc_data : Average value of sensor sample datas + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * + * @return 0 -> Success + * @return < 0 -> Fail + */ +static int8_t validate_foc_position(uint8_t sens_list, + const struct bmi2_accel_foc_g_value *accel_g_axis, + struct bmi2_sens_axes_data avg_foc_data, + struct bmi2_dev *dev); + +/*! + * @brief This internal API validates accel FOC axis given as input + * + * @param[in] avg_foc_data : Average value of sensor sample datas + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * + * @return 0 -> Success + * @return < 0 -> Fail + */ +static int8_t validate_foc_accel_axis(int16_t avg_foc_data, struct bmi2_dev *dev); + +/******************************************************************************/ +/*! @name User Interface Definitions */ +/******************************************************************************/ + +/*! + * @brief This API is the entry point for bmi2 sensor. It selects between + * I2C/SPI interface, based on user selection. It reads and validates the + * chip-id of the sensor. + */ +int8_t bmi2_sec_init(struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to assign chip id */ + uint8_t chip_id = 0; + + /* Structure to define the default values for axes re-mapping */ + struct bmi2_axes_remap axes_remap = { + .x_axis = BMI2_MAP_X_AXIS, .x_axis_sign = BMI2_POS_SIGN, .y_axis = BMI2_MAP_Y_AXIS, + .y_axis_sign = BMI2_POS_SIGN, .z_axis = BMI2_MAP_Z_AXIS, .z_axis_sign = BMI2_POS_SIGN + }; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + + if (rslt == BMI2_OK) + { + /* Set APS flag as after reset, the sensor is on advance power save mode */ + dev->aps_status = BMI2_ENABLE; + + /* Performing a dummy read to bring interface back to SPI from I2C interface */ + if (dev->intf == BMI2_SPI_INTF) + { + rslt = bmi2_get_regs(BMI2_CHIP_ID_ADDR, &chip_id, 1, dev); + } + + if (rslt == BMI2_OK) + { + /* Read chip-id of the BMI2 sensor */ + rslt = bmi2_get_regs(BMI2_CHIP_ID_ADDR, &chip_id, 1, dev); + + if (rslt == BMI2_OK) + { + /* Validate chip-id */ + if (chip_id == dev->chip_id) + { + /* Assign resolution to the structure */ + dev->resolution = 16; + + /* Set manual enable flag */ + dev->aux_man_en = 1; + + /* Set the default values for axis + * re-mapping in the device structure + */ + dev->remap = axes_remap; + + /* Perform soft-reset to bring all register values to their + * default values + */ + rslt = bmi2_soft_reset(dev); + } + else + { + /* Storing the chip-id value read from + * the register to identify the sensor + */ + dev->chip_id = chip_id; + rslt = BMI2_E_DEV_NOT_FOUND; + } + } + } + } + + return rslt; +} + +/*! + * @brief This API reads the data from the given register address of bmi2 + * sensor. + * + * @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 - 0x26, 0x5E. + */ +int8_t bmi2_get_regs(uint8_t reg_addr, uint8_t *data, uint16_t len, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define loop */ + uint16_t index = 0; + + /* Variable to define temporary buffer */ + uint8_t temp_buf[BMI2_MAX_LEN]; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (data != NULL)) + { + /* Configuring reg_addr for SPI Interface */ + if (dev->intf == BMI2_SPI_INTF) + { + reg_addr = (reg_addr | BMI2_SPI_RD_MASK); + } + + dev->intf_rslt = dev->read(reg_addr, temp_buf, (len + dev->dummy_byte), dev->intf_ptr); + + if (dev->aps_status == BMI2_ENABLE) + { + dev->delay_us(450, dev->intf_ptr); + } + else + { + dev->delay_us(2, dev->intf_ptr); + } + + if (dev->intf_rslt == BMI2_INTF_RET_SUCCESS) + { + /* Read the data from the position next to dummy byte */ + while (index < len) + { + data[index] = temp_buf[index + dev->dummy_byte]; + index++; + } + } + else + { + rslt = BMI2_E_COM_FAIL; + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API writes data to the given register address of bmi2 sensor. + */ +int8_t bmi2_set_regs(uint8_t reg_addr, const uint8_t *data, uint16_t len, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + uint16_t loop; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (data != NULL)) + { + /* Configuring reg_addr for SPI Interface */ + if (dev->intf == BMI2_SPI_INTF) + { + reg_addr = (reg_addr & BMI2_SPI_WR_MASK); + } + + /* Writing Byte by byte and delay for Low power mode of the sensor is 450 us */ + if (dev->aps_status == BMI2_ENABLE) + { + for (loop = 0; loop < len; loop++) + { + dev->intf_rslt = dev->write((uint8_t)((uint16_t)reg_addr + loop), &data[loop], 1, dev->intf_ptr); + dev->delay_us(BMI2_POWER_SAVE_MODE_DELAY_IN_US, dev->intf_ptr); + if (dev->intf_rslt != BMI2_INTF_RET_SUCCESS) + { + break; + } + } + } + /* Burst write and delay for Normal mode of the sensor is 2 us */ + else + { + dev->intf_rslt = dev->write(reg_addr, data, len, dev->intf_ptr); + dev->delay_us(BMI2_NORMAL_MODE_DELAY_IN_US, dev->intf_ptr); + } + + /* Updating the advance power saver flag */ + if (reg_addr == BMI2_PWR_CONF_ADDR) + { + if (*data & BMI2_ADV_POW_EN_MASK) + { + dev->aps_status = BMI2_ENABLE; + } + else + { + dev->aps_status = BMI2_DISABLE; + } + } + + if (dev->intf_rslt != BMI2_INTF_RET_SUCCESS) + { + rslt = BMI2_E_COM_FAIL; + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API resets bmi2 sensor. All registers are overwritten with + * their default values. + */ +int8_t bmi2_soft_reset(struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define soft reset value */ + uint8_t data = BMI2_SOFT_RESET_CMD; + + /* Variable to read the dummy byte */ + uint8_t dummy_read = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Reset bmi2 device */ + rslt = bmi2_set_regs(BMI2_CMD_REG_ADDR, &data, 1, dev); + dev->delay_us(2000, dev->intf_ptr); + + /* Set APS flag as after soft reset the sensor is on advance power save mode */ + dev->aps_status = BMI2_ENABLE; + + /* Performing a dummy read to bring interface back to SPI from + * I2C after a soft-reset + */ + if ((rslt == BMI2_OK) && (dev->intf == BMI2_SPI_INTF)) + { + rslt = bmi2_get_regs(BMI2_CHIP_ID_ADDR, &dummy_read, 1, dev); + } + + if (rslt == BMI2_OK) + { + /* Write the configuration file */ + rslt = bmi2_write_config_file(dev); + } + + /* Reset the sensor status flag in the device structure */ + if (rslt == BMI2_OK) + { + dev->sens_en_stat = 0; + } + } + + return rslt; +} + +/*! + * @brief This API is used to get the config file major and minor version information. + */ +int8_t bmi2_get_config_file_version(uint8_t *config_major, uint8_t *config_minor, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* NULL pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (config_major != NULL) && (config_minor != NULL)) + { + /* Extract the config file identification from the dmr page and get the major and minor version */ + rslt = extract_config_file(config_major, config_minor, dev); + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API enables/disables the advance power save mode in the sensor. + */ +int8_t bmi2_set_adv_power_save(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + rslt = bmi2_get_regs(BMI2_PWR_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + reg_data = BMI2_SET_BIT_POS0(reg_data, BMI2_ADV_POW_EN, enable); + rslt = bmi2_set_regs(BMI2_PWR_CONF_ADDR, ®_data, 1, dev); + + if (rslt != BMI2_OK) + { + /* Return error if enable/disable APS fails */ + rslt = BMI2_E_SET_APS_FAIL; + } + + if (rslt == BMI2_OK) + { + dev->aps_status = BMI2_GET_BIT_POS0(reg_data, BMI2_ADV_POW_EN); + } + } + } + + return rslt; +} + +/*! + * @brief This API gets the status of advance power save mode in the sensor. + */ +int8_t bmi2_get_adv_power_save(uint8_t *aps_status, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (aps_status != NULL)) + { + rslt = bmi2_get_regs(BMI2_PWR_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + *aps_status = BMI2_GET_BIT_POS0(reg_data, BMI2_ADV_POW_EN); + dev->aps_status = *aps_status; + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API loads the configuration file into the bmi2 sensor. + */ +int8_t bmi2_write_config_file(struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to know the load status */ + uint8_t load_status = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (dev->config_size != 0)) + { + /* Bytes written are multiples of 2 */ + if ((dev->read_write_len % 2) != 0) + { + dev->read_write_len = dev->read_write_len - 1; + } + + if (dev->read_write_len < 2) + { + dev->read_write_len = 2; + } + + /* Write the configuration file */ + rslt = write_config_file(dev); + if (rslt == BMI2_OK) + { + /* Check the configuration load status */ + rslt = bmi2_get_internal_status(&load_status, dev); + + load_status &= BMI2_CONFIG_LOAD_STATUS_MASK; + + dev->load_status = load_status; + + /* Return error if loading not successful */ + if ((rslt == BMI2_OK) && (load_status != BMI2_CONFIG_LOAD_SUCCESS)) + { + rslt = BMI2_E_CONFIG_LOAD; + } + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API sets: + * 1) The input output configuration of the selected interrupt pin: + * INT1 or INT2. + * 2) The interrupt mode: permanently latched or non-latched. + */ +int8_t bmi2_set_int_pin_config(const struct bmi2_int_pin_config *int_cfg, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define data array */ + uint8_t data_array[3] = { 0 }; + + /* Variable to store register data */ + uint8_t reg_data = 0; + + /* Variable to define type of interrupt pin */ + uint8_t int_pin = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (int_cfg != NULL)) + { + /* Copy the pin type to a local variable */ + int_pin = int_cfg->pin_type; + if ((int_pin > BMI2_INT_NONE) && (int_pin < BMI2_INT_PIN_MAX)) + { + /* Get the previous configuration data */ + rslt = bmi2_get_regs(BMI2_INT1_IO_CTRL_ADDR, data_array, 3, dev); + if (rslt == BMI2_OK) + { + /* Set interrupt pin 1 configuration */ + if ((int_pin == BMI2_INT1) || (int_pin == BMI2_INT_BOTH)) + { + /* Configure active low or high */ + reg_data = BMI2_SET_BITS(data_array[0], BMI2_INT_LEVEL, int_cfg->pin_cfg[0].lvl); + + /* Configure push-pull or open drain */ + reg_data = BMI2_SET_BITS(reg_data, BMI2_INT_OPEN_DRAIN, int_cfg->pin_cfg[0].od); + + /* Configure output enable */ + reg_data = BMI2_SET_BITS(reg_data, BMI2_INT_OUTPUT_EN, int_cfg->pin_cfg[0].output_en); + + /* Configure input enable */ + reg_data = BMI2_SET_BITS(reg_data, BMI2_INT_INPUT_EN, int_cfg->pin_cfg[0].input_en); + + /* Copy the data to be written in the respective array */ + data_array[0] = reg_data; + } + + /* Set interrupt pin 2 configuration */ + if ((int_pin == BMI2_INT2) || (int_pin == BMI2_INT_BOTH)) + { + /* Configure active low or high */ + reg_data = BMI2_SET_BITS(data_array[1], BMI2_INT_LEVEL, int_cfg->pin_cfg[1].lvl); + + /* Configure push-pull or open drain */ + reg_data = BMI2_SET_BITS(reg_data, BMI2_INT_OPEN_DRAIN, int_cfg->pin_cfg[1].od); + + /* Configure output enable */ + reg_data = BMI2_SET_BITS(reg_data, BMI2_INT_OUTPUT_EN, int_cfg->pin_cfg[1].output_en); + + /* Configure input enable */ + reg_data = BMI2_SET_BITS(reg_data, BMI2_INT_INPUT_EN, int_cfg->pin_cfg[1].input_en); + + /* Copy the data to be written in the respective array */ + data_array[1] = reg_data; + } + + /* Configure the interrupt mode */ + data_array[2] = BMI2_SET_BIT_POS0(data_array[2], BMI2_INT_LATCH, int_cfg->int_latch); + + /* Set the configurations simultaneously as + * INT1_IO_CTRL, INT2_IO_CTRL, and INT_LATCH lie + * in consecutive addresses + */ + rslt = bmi2_set_regs(BMI2_INT1_IO_CTRL_ADDR, data_array, 3, dev); + } + } + else + { + rslt = BMI2_E_INVALID_INT_PIN; + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API gets: + * 1) The input output configuration of the selected interrupt pin: + * INT1 or INT2. + * 2) The interrupt mode: permanently latched or non-latched. + */ +int8_t bmi2_get_int_pin_config(struct bmi2_int_pin_config *int_cfg, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define data array */ + uint8_t data_array[3] = { 0 }; + + /* Variable to define type of interrupt pin */ + uint8_t int_pin = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (int_cfg != NULL)) + { + /* Copy the pin type to a local variable */ + int_pin = int_cfg->pin_type; + + /* Get the previous configuration data */ + rslt = bmi2_get_regs(BMI2_INT1_IO_CTRL_ADDR, data_array, 3, dev); + + if (rslt == BMI2_OK) + { + /* Get interrupt pin 1 configuration */ + if ((int_pin == BMI2_INT1) || (int_pin == BMI2_INT_BOTH)) + { + /* Get active low or high */ + int_cfg->pin_cfg[0].lvl = BMI2_GET_BITS(data_array[0], BMI2_INT_LEVEL); + + /* Get push-pull or open drain */ + int_cfg->pin_cfg[0].od = BMI2_GET_BITS(data_array[0], BMI2_INT_OPEN_DRAIN); + + /* Get output enable */ + int_cfg->pin_cfg[0].output_en = BMI2_GET_BITS(data_array[0], BMI2_INT_OUTPUT_EN); + + /* Get input enable */ + int_cfg->pin_cfg[0].input_en = BMI2_GET_BITS(data_array[0], BMI2_INT_INPUT_EN); + } + + /* Get interrupt pin 2 configuration */ + if ((int_pin == BMI2_INT2) || (int_pin == BMI2_INT_BOTH)) + { + /* Get active low or high */ + int_cfg->pin_cfg[1].lvl = BMI2_GET_BITS(data_array[1], BMI2_INT_LEVEL); + + /* Get push-pull or open drain */ + int_cfg->pin_cfg[1].od = BMI2_GET_BITS(data_array[1], BMI2_INT_OPEN_DRAIN); + + /* Get output enable */ + int_cfg->pin_cfg[1].output_en = BMI2_GET_BITS(data_array[1], BMI2_INT_OUTPUT_EN); + + /* Get input enable */ + int_cfg->pin_cfg[1].input_en = BMI2_GET_BITS(data_array[1], BMI2_INT_INPUT_EN); + } + + /* Get interrupt mode */ + int_cfg->int_latch = BMI2_GET_BIT_POS0(data_array[2], BMI2_INT_LATCH); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API gets the interrupt status of both feature and data + * interrupts + */ +int8_t bmi2_get_int_status(uint16_t *int_status, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to store data */ + uint8_t data_array[2] = { 0 }; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (int_status != NULL)) + { + /* Get the interrupt status */ + rslt = bmi2_get_regs(BMI2_INT_STATUS_0_ADDR, data_array, 2, dev); + if (rslt == BMI2_OK) + { + *int_status = (uint16_t) data_array[0] | ((uint16_t) data_array[1] << 8); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API selects the sensors/features to be enabled. + */ +int8_t bmi2_sensor_enable(const uint8_t *sens_list, uint8_t n_sens, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to select sensor */ + uint64_t sensor_sel = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (sens_list != NULL)) + { + /* Get the selected sensors */ + rslt = select_sensor(sens_list, n_sens, &sensor_sel); + if (rslt == BMI2_OK) + { + /* Enable the selected sensors */ + rslt = sensor_enable(sensor_sel, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API selects the sensors/features to be disabled. + */ +int8_t bmi2_sensor_disable(const uint8_t *sens_list, uint8_t n_sens, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to select sensor */ + uint64_t sensor_sel = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (sens_list != NULL)) + { + /* Get the selected sensors */ + rslt = select_sensor(sens_list, n_sens, &sensor_sel); + if (rslt == BMI2_OK) + { + /* Disable the selected sensors */ + rslt = sensor_disable(sensor_sel, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API sets the sensor/feature configuration. + */ +int8_t bmi2_set_sensor_config(struct bmi2_sens_config *sens_cfg, uint8_t n_sens, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define loop */ + uint8_t loop; + + /* Variable to get the status of advance power save */ + uint8_t aps_stat = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (sens_cfg != NULL)) + { + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + + for (loop = 0; loop < n_sens; loop++) + { + /* Disable Advance power save if enabled for auxiliary + * and feature configurations + */ + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if + * enabled + */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + if (rslt == BMI2_OK) + { + switch (sens_cfg[loop].type) + { + /* Set accelerometer configuration */ + case BMI2_ACCEL: + rslt = set_accel_config(&sens_cfg[loop].cfg.acc, dev); + break; + + /* Set gyroscope configuration */ + case BMI2_GYRO: + rslt = set_gyro_config(&sens_cfg[loop].cfg.gyr, dev); + break; + + /* Set auxiliary configuration */ + case BMI2_AUX: + rslt = set_aux_config(&sens_cfg[loop].cfg.aux, dev); + break; + + /* Set gyroscope user gain configuration */ + case BMI2_GYRO_GAIN_UPDATE: + rslt = set_gyro_user_gain_config(&sens_cfg[loop].cfg.gyro_gain_update, dev); + break; + + default: + rslt = BMI2_E_INVALID_SENSOR; + break; + } + } + + /* Return error if any of the set configurations fail */ + if (rslt != BMI2_OK) + { + break; + } + } + + /* Enable Advance power save if disabled while configuring and + * not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API gets the sensor/feature configuration. + */ +int8_t bmi2_get_sensor_config(struct bmi2_sens_config *sens_cfg, uint8_t n_sens, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define loop */ + uint8_t loop; + + /* Variable to get the status of advance power save */ + uint8_t aps_stat = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (sens_cfg != NULL)) + { + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + for (loop = 0; loop < n_sens; loop++) + { + /* Disable Advance power save if enabled for auxiliary + * and feature configurations + */ + if ((sens_cfg[loop].type >= BMI2_MAIN_SENS_MAX_NUM) || (sens_cfg[loop].type == BMI2_AUX)) + { + + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if + * enabled + */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + } + + if (rslt == BMI2_OK) + { + switch (sens_cfg[loop].type) + { + /* Get accelerometer configuration */ + case BMI2_ACCEL: + rslt = get_accel_config(&sens_cfg[loop].cfg.acc, dev); + break; + + /* Get gyroscope configuration */ + case BMI2_GYRO: + rslt = get_gyro_config(&sens_cfg[loop].cfg.gyr, dev); + break; + + /* Get auxiliary configuration */ + case BMI2_AUX: + rslt = get_aux_config(&sens_cfg[loop].cfg.aux, dev); + break; + + /* Get gyroscope user gain configuration */ + case BMI2_GYRO_GAIN_UPDATE: + rslt = get_gyro_gain_update_config(&sens_cfg[loop].cfg.gyro_gain_update, dev); + break; + + default: + rslt = BMI2_E_INVALID_SENSOR; + break; + } + } + + /* Return error if any of the get configurations fail */ + if (rslt != BMI2_OK) + { + break; + } + } + + /* Enable Advance power save if disabled while configuring and + * not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API gets the feature data gyroscope user-gain update and gyroscope cross sensitivity. + */ +int8_t bmi2_get_feature_data(struct bmi2_feat_sensor_data *feat_sensor_data, uint8_t n_sens, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define loop */ + uint8_t loop; + + /* Variable to get the status of advance power save */ + uint8_t aps_stat = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (feat_sensor_data != NULL)) + { + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + for (loop = 0; loop < n_sens; loop++) + { + /* Disable Advance power save if enabled for feature + * configurations + */ + if (feat_sensor_data[loop].type >= BMI2_MAIN_SENS_MAX_NUM) + { + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if + * enabled + */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + } + + if (rslt == BMI2_OK) + { + switch (feat_sensor_data[loop].type) + { + case BMI2_GYRO_CROSS_SENSE: + + /* Get Gyroscope cross sense value of z axis */ + rslt = get_gyro_cross_sense(&feat_sensor_data[loop].sens_data.correction_factor_zx, dev); + break; + + case BMI2_GYRO_GAIN_UPDATE: + + /* Get saturation status of gyroscope user gain update */ + rslt = + get_gyro_gain_update_status(&feat_sensor_data[loop].sens_data.gyro_user_gain_status, dev); + break; + default: + rslt = BMI2_E_INVALID_SENSOR; + break; + } + + /* Return error if any of the get sensor data fails */ + if (rslt != BMI2_OK) + { + break; + } + } + + /* Enable Advance power save if disabled while + * configuring and not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API gets the sensor data for accelerometer, gyroscope, + * auxiliary sensor with sensortime + */ +int8_t bmi2_get_sensor_data(struct bmi2_sens_data *data, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to hold register sensor data */ + uint8_t sensor_data[BMI2_ACC_GYR_AUX_SENSORTIME_NUM_BYTES]; + + /* Null-pointer check */ + if (data != NULL) + { + rslt = bmi2_get_regs(BMI2_STATUS_ADDR, sensor_data, BMI2_ACC_GYR_AUX_SENSORTIME_NUM_BYTES, dev); + + if (rslt == BMI2_OK) + { + rslt = bmi2_parse_sensor_data(sensor_data, data, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API reads the raw temperature data from the register and can be + * converted into degree celsius. + */ +int8_t bmi2_get_temperature_data(int16_t *temp_data, struct bmi2_dev *dev) +{ + /* Variable to store result of API */ + int8_t rslt; + + /* Array to define data stored in register */ + uint8_t reg_data[2] = { 0 }; + + if (temp_data != NULL) + { + /* Read the sensor data */ + rslt = bmi2_get_regs(BMI2_TEMPERATURE_0_ADDR, reg_data, 2, dev); + + if (rslt == BMI2_OK) + { + *temp_data = (int16_t)(reg_data[0] | ((int16_t)reg_data[1] << 8)); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API parses the sensor data for accelerometer, gyroscope, + * auxiliary sensor with sensortime + */ +int8_t bmi2_parse_sensor_data(const uint8_t *sensor_data, struct bmi2_sens_data *data, const struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variables to define loop */ + uint8_t count = 0; + + uint8_t index = 0; + + uint32_t sensor_time_byte3 = 0; + uint16_t sensor_time_byte2 = 0; + uint8_t sensor_time_byte1 = 0; + + rslt = null_ptr_check(dev); + + if ((rslt == BMI2_OK) && (data != NULL)) + { + /* Update auxiliary sensor data length */ + index = BMI2_AUX_START_INDEX; + + /* Get the 8 bytes of auxiliary data */ + do + { + *(data->aux_data + count++) = (sensor_data[index++]); + } while (count < BMI2_AUX_NUM_BYTES); + + /* Update accelerometer sensor data length */ + index = BMI2_ACC_START_INDEX; + + /* Get accelerometer data from the register */ + get_acc_gyr_data(&data->acc, &sensor_data[index]); + + /* Get the re-mapped accelerometer data */ + get_remapped_data(&data->acc, dev); + + /* Update gyroscope sensor data length */ + index = BMI2_GYR_START_INDEX; + + /* Get gyroscope data from the register */ + get_acc_gyr_data(&data->gyr, &sensor_data[index]); + + /* Get the compensated gyroscope data */ + comp_gyro_cross_axis_sensitivity(&data->gyr, dev); + + /* Get the re-mapped gyroscope data */ + get_remapped_data(&data->gyr, dev); + + sensor_time_byte3 = sensor_data[BMI2_PARSE_SENSOR_TIME_MSB_BYTE] << 16; + sensor_time_byte2 = sensor_data[BMI2_PARSE_SENSOR_TIME_XLSB_BYTE] << 8; + sensor_time_byte1 = sensor_data[BMI2_PARSE_SENSOR_TIME_LSB_BYTE]; + + data->sens_time = (uint32_t)(sensor_time_byte3 | sensor_time_byte2 | sensor_time_byte1); + + /* Store status register value in structure */ + data->status = sensor_data[BMI2_STATUS_INDEX]; + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API sets the FIFO configuration in the sensor. + */ +int8_t bmi2_set_fifo_config(uint16_t config, uint8_t enable, struct bmi2_dev *dev) +{ + int8_t rslt; + uint8_t data[2] = { 0 }; + uint8_t max_burst_len = 0; + + /* Variable to store data of FIFO configuration register 0 */ + uint8_t fifo_config_0 = (uint8_t)(config & BMI2_FIFO_CONFIG_0_MASK); + + /* Variable to store data of FIFO configuration register 1 */ + uint8_t fifo_config_1 = (uint8_t)((config & BMI2_FIFO_CONFIG_1_MASK) >> 8); + + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + rslt = bmi2_get_regs(BMI2_FIFO_CONFIG_0_ADDR, data, BMI2_FIFO_CONFIG_LENGTH, dev); + if (rslt == BMI2_OK) + { + /* Get data to set FIFO configuration register 0 */ + if (fifo_config_0 > 0) + { + if (enable == BMI2_ENABLE) + { + data[0] = data[0] | fifo_config_0; + } + else + { + data[0] = data[0] & (~fifo_config_0); + } + } + + /* Get data to set FIFO configuration register 1 */ + if (enable == BMI2_ENABLE) + { + data[1] = data[1] | fifo_config_1; + if (dev->variant_feature & BMI2_CRT_RTOSK_ENABLE) + { + /* Burst length is needed for CRT + * FIFO enable will reset the default values + * So configure the max burst length again. + */ + rslt = get_maxburst_len(&max_burst_len, dev); + if (rslt == BMI2_OK && max_burst_len == 0) + { + rslt = set_maxburst_len(BMI2_CRT_MIN_BURST_WORD_LENGTH, dev); + } + } + } + else + { + data[1] = data[1] & (~fifo_config_1); + } + + /* Set the FIFO configurations */ + if (rslt == BMI2_OK) + { + rslt = bmi2_set_regs(BMI2_FIFO_CONFIG_0_ADDR, data, 2, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API reads the FIFO configuration from the sensor. + */ +int8_t bmi2_get_fifo_config(uint16_t *fifo_config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to store data */ + uint8_t data[2] = { 0 }; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (fifo_config != NULL)) + { + /* Get the FIFO configuration value */ + rslt = bmi2_get_regs(BMI2_FIFO_CONFIG_0_ADDR, data, BMI2_FIFO_CONFIG_LENGTH, dev); + if (rslt == BMI2_OK) + { + (*fifo_config) = (uint16_t)((uint16_t) data[0] & BMI2_FIFO_CONFIG_0_MASK); + (*fifo_config) |= (uint16_t)(((uint16_t) data[1] << 8) & BMI2_FIFO_CONFIG_1_MASK); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API reads the FIFO data. + * NOTE : Dummy byte (for SPI Interface) required for FIFO data read + * must be given as part of data pointer in struct bmi2_fifo_frame + */ +int8_t bmi2_read_fifo_data(struct bmi2_fifo_frame *fifo, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to store FIFO configuration data */ + uint8_t config_data[2] = { 0 }; + + /* Variable to define FIFO address */ + uint8_t addr = BMI2_FIFO_DATA_ADDR; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (fifo != NULL)) + { + /* Clear the FIFO data structure */ + rslt = reset_fifo_frame_structure(fifo, dev); + + if (rslt == BMI2_OK) + { + /* Configuring reg_addr for SPI Interface */ + if (dev->intf == BMI2_SPI_INTF) + { + addr = (addr | BMI2_SPI_RD_MASK); + } + + /* Read FIFO data */ + dev->intf_rslt = dev->read(addr, fifo->data, (uint32_t)fifo->length, dev->intf_ptr); + + /* Provide delay based on advanced power saving mode status */ + if (dev->aps_status == BMI2_ENABLE) + { + dev->delay_us(450, dev->intf_ptr); + } + else + { + dev->delay_us(2, dev->intf_ptr); + } + + /* If interface read fails, update rslt variable with communication failure */ + if (dev->intf_rslt != BMI2_INTF_RET_SUCCESS) + { + rslt = BMI2_E_COM_FAIL; + } + + if (rslt == BMI2_OK) + { + /* Get the set FIFO frame configurations */ + rslt = bmi2_get_regs(BMI2_FIFO_CONFIG_0_ADDR, config_data, 2, dev); + if (rslt == BMI2_OK) + { + /* Get FIFO header status */ + fifo->header_enable = (uint8_t)((config_data[1]) & (BMI2_FIFO_HEADER_EN >> 8)); + + /* Get sensor enable status, of which the data is to be read */ + fifo->data_enable = + (uint16_t)(((config_data[0]) | ((uint16_t) config_data[1] << 8)) & BMI2_FIFO_ALL_EN); + } + } + else + { + rslt = BMI2_E_COM_FAIL; + } + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API parses and extracts the accelerometer frames from FIFO data + * read by the "bmi2_read_fifo_data" API and stores it in the "accel_data" + * structure instance. + */ +int8_t bmi2_extract_accel(struct bmi2_sens_axes_data *accel_data, + uint16_t *accel_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + + if ((rslt == BMI2_OK) && (accel_data != NULL) && (accel_length != NULL) && (fifo != NULL)) + { + /* Check if this is the first iteration of data unpacking + * if yes, then consider dummy byte on SPI + */ + if (fifo->acc_byte_start_idx == 0) + { + /* Dummy byte included */ + fifo->acc_byte_start_idx = dev->dummy_byte; + } + + /* Parsing the FIFO data in header-less mode */ + if (fifo->header_enable == 0) + { + /* Parsing the FIFO data in headerless mode */ + rslt = extract_accel_headerless_mode(accel_data, accel_length, fifo, dev); + } + else + { + /* Parsing the FIFO data in header mode */ + rslt = extract_accel_header_mode(accel_data, accel_length, fifo, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API parses and extracts the gyroscope frames from FIFO data + * read by the "bmi2_read_fifo_data" API and stores it in the "gyro_data" + * structure instance. + */ +int8_t bmi2_extract_gyro(struct bmi2_sens_axes_data *gyro_data, + uint16_t *gyro_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (gyro_data != NULL) && (gyro_length != NULL) && (fifo != NULL)) + { + /* Check if this is the first iteration of data unpacking + * if yes, then consider dummy byte on SPI + */ + if (fifo->gyr_byte_start_idx == 0) + { + /* Dummy byte included */ + fifo->gyr_byte_start_idx = dev->dummy_byte; + } + + /* Parsing the FIFO data in header-less mode */ + if (fifo->header_enable == 0) + { + /* Parsing the FIFO data in headerless mode */ + rslt = extract_gyro_headerless_mode(gyro_data, gyro_length, fifo, dev); + } + else + { + /* Parsing the FIFO data in header mode */ + rslt = extract_gyro_header_mode(gyro_data, gyro_length, fifo, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API parses and extracts the auxiliary frames from FIFO data + * read by the "bmi2_read_fifo_data" API and stores it in "aux_data" buffer. + */ +int8_t bmi2_extract_aux(struct bmi2_aux_fifo_data *aux, + uint16_t *aux_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (aux != NULL) && (aux_length != NULL) && (fifo != NULL)) + { + /* Check if this is the first iteration of data unpacking + * if yes, then consider dummy byte on SPI + */ + if (fifo->aux_byte_start_idx == 0) + { + /* Dummy byte included */ + fifo->aux_byte_start_idx = dev->dummy_byte; + } + + /* Parsing the FIFO data in header-less mode */ + if (fifo->header_enable == 0) + { + /* Parsing the FIFO data in headerless mode */ + rslt = extract_aux_headerless_mode(aux, aux_length, fifo, dev); + } + else + { + /* Parsing the FIFO data in header mode */ + rslt = extract_aux_header_mode(aux, aux_length, fifo, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API writes the available sensor specific commands to the sensor. + */ +int8_t bmi2_set_command_register(uint8_t command, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Set the command in the command register */ + rslt = bmi2_set_regs(BMI2_CMD_REG_ADDR, &command, 1, dev); + } + + return rslt; +} + +/* + * @brief This API sets the FIFO self wake up functionality in the sensor. + */ +int8_t bmi2_set_fifo_self_wake_up(uint8_t fifo_self_wake_up, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Set FIFO self wake-up */ + rslt = bmi2_get_regs(BMI2_PWR_CONF_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + data = BMI2_SET_BITS(data, BMI2_FIFO_SELF_WAKE_UP, fifo_self_wake_up); + rslt = bmi2_set_regs(BMI2_PWR_CONF_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API gets the status of FIFO self wake up functionality from + * the sensor. + */ +int8_t bmi2_get_fifo_self_wake_up(uint8_t *fifo_self_wake_up, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (fifo_self_wake_up != NULL)) + { + /* Get the status of FIFO self wake-up */ + rslt = bmi2_get_regs(BMI2_PWR_CONF_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + (*fifo_self_wake_up) = BMI2_GET_BITS(data, BMI2_FIFO_SELF_WAKE_UP); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/* + * @brief This API sets which error flag will trigger the error interrupt once enabled. + */ +int8_t bmi2_set_err_reg_mask(uint8_t err_reg, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Get Error interrupt mask */ + rslt = bmi2_get_regs(BMI2_ERR_REG_MSK_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + /* Set Error interrupt mask */ + data = BMI2_SET_BIT_POS0(data, BMI2_ERR_REG_READ, err_reg); + rslt = bmi2_set_regs(BMI2_ERR_REG_MSK_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/* + * @brief This API gets which error flag will trigger the error interrupt once enabled. + */ +int8_t bmi2_get_err_reg_mask(uint8_t *err_reg, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check mask */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (err_reg != NULL)) + { + /* Get Error interrupt mask */ + rslt = bmi2_get_regs(BMI2_ERR_REG_MSK_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + (*err_reg) = BMI2_GET_BIT_POS0(data, BMI2_ERR_REG_READ); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/* + * @brief This API sets the Fast power up functionality in the sensor. + */ +int8_t bmi2_set_fast_power_up(uint8_t fast_power_up, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Get fast power up */ + rslt = bmi2_get_regs(BMI2_PWR_CONF_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + /* Set fast power up */ + data = BMI2_SET_BITS(data, BMI2_FUP_EN, fast_power_up); + rslt = bmi2_set_regs(BMI2_PWR_CONF_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API gets the status of Fast power up functionality from + * the sensor. + */ +int8_t bmi2_get_fast_power_up(uint8_t *fast_power_up, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (fast_power_up != NULL)) + { + /* Get the status of Fast power up */ + rslt = bmi2_get_regs(BMI2_PWR_CONF_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + (*fast_power_up) = BMI2_GET_BITS(data, BMI2_FUP_EN); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/* + * @brief This API sets the SPI enable in the sensor. + */ +int8_t bmi2_set_spi_en(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Get spi enable */ + rslt = bmi2_get_regs(BMI2_NV_CONF_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + /* Set spi enable */ + data = BMI2_SET_BIT_POS0(data, BMI2_NV_SPI_EN, enable); + rslt = bmi2_set_regs(BMI2_NV_CONF_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API gets the status of SPI enable functionality from + * the sensor. + */ +int8_t bmi2_get_spi_en(uint8_t *enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (enable != NULL)) + { + /* Get the status of spi enable */ + rslt = bmi2_get_regs(BMI2_NV_CONF_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + (*enable) = BMI2_GET_BIT_POS0(data, BMI2_NV_SPI_EN); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/* + * @brief This API sets the i2c watchdog enable in the sensor. + */ +int8_t bmi2_set_i2c_wdt_en(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Get i2c watchdog enable */ + rslt = bmi2_get_regs(BMI2_NV_CONF_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + /* Set i2c watchdog enable */ + data = BMI2_SET_BITS(data, BMI2_NV_I2C_WD_EN, enable); + rslt = bmi2_set_regs(BMI2_NV_CONF_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API gets the status of i2c watchdog enable functionality from + * the sensor. + */ +int8_t bmi2_get_i2c_wdt_en(uint8_t *enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (enable != NULL)) + { + /* Get the status of i2c watchdog enable */ + rslt = bmi2_get_regs(BMI2_NV_CONF_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + (*enable) = BMI2_GET_BITS(data, BMI2_NV_I2C_WD_EN); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/* + * @brief This API sets the i2c watchdog select in the sensor. + */ +int8_t bmi2_set_i2c_wdt_sel(uint8_t watchdog_select, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Get i2c watchdog select */ + rslt = bmi2_get_regs(BMI2_NV_CONF_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + /* Set i2c watchdog select */ + data = BMI2_SET_BITS(data, BMI2_NV_I2C_WD_SEL, watchdog_select); + rslt = bmi2_set_regs(BMI2_NV_CONF_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API gets the status of i2c watchdog select functionality from + * the sensor. + */ +int8_t bmi2_get_i2c_wdt_sel(uint8_t *watchdog_select, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (watchdog_select != NULL)) + { + /* Get the status of i2c watchdog select */ + rslt = bmi2_get_regs(BMI2_NV_CONF_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + (*watchdog_select) = BMI2_GET_BITS(data, BMI2_NV_I2C_WD_SEL); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/* + * @brief This API sets the drive strength in the sensor. + */ +int8_t bmi2_set_drv_reg(uint8_t drv_reg, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Get drive strength */ + rslt = bmi2_get_regs(BMI2_DRV_STR_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + /* Set drive strength */ + data = BMI2_SET_BIT_POS0(data, BMI2_DRV_STR, drv_reg); + rslt = bmi2_set_regs(BMI2_DRV_STR_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API gets the status of drive strength functionality from + * the sensor. + */ +int8_t bmi2_get_drv_reg(uint8_t *drv_reg, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (drv_reg != NULL)) + { + /* Get the status of drive strength */ + rslt = bmi2_get_regs(BMI2_DRV_STR_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + (*drv_reg) = BMI2_GET_BIT_POS0(data, BMI2_DRV_STR); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API sets the FIFO water-mark level in the sensor. + */ +int8_t bmi2_set_fifo_wm(uint16_t fifo_wm, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to store data */ + uint8_t data[2] = { 0 }; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Get LSB value of FIFO water-mark */ + data[0] = BMI2_GET_LSB(fifo_wm); + + /* Get MSB value of FIFO water-mark */ + data[1] = BMI2_GET_MSB(fifo_wm); + + /* Set the FIFO water-mark level */ + rslt = bmi2_set_regs(BMI2_FIFO_WTM_0_ADDR, data, 2, dev); + } + + return rslt; +} + +/*! + * @brief This API reads the FIFO water mark level set in the sensor. + */ +int8_t bmi2_get_fifo_wm(uint16_t *fifo_wm, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to to store data */ + uint8_t data[2] = { 0 }; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (fifo_wm != NULL)) + { + /* Read the FIFO water mark level */ + rslt = bmi2_get_regs(BMI2_FIFO_WTM_0_ADDR, data, BMI2_FIFO_WM_LENGTH, dev); + if (rslt == BMI2_OK) + { + (*fifo_wm) = (uint16_t)((uint16_t) data[1] << 8) | (data[0]); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API sets either filtered or un-filtered FIFO accelerometer or + * gyroscope data. + */ +int8_t bmi2_set_fifo_filter_data(uint8_t sens_sel, uint8_t fifo_filter_data, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + switch (sens_sel) + { + case BMI2_ACCEL: + + /* Validate filter mode */ + if (fifo_filter_data <= BMI2_MAX_VALUE_FIFO_FILTER) + { + /* Set the accelerometer FIFO filter data */ + rslt = bmi2_get_regs(BMI2_FIFO_DOWNS_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + data = BMI2_SET_BITS(data, BMI2_ACC_FIFO_FILT_DATA, fifo_filter_data); + rslt = bmi2_set_regs(BMI2_FIFO_DOWNS_ADDR, &data, 1, dev); + } + } + else + { + rslt = BMI2_E_OUT_OF_RANGE; + } + + break; + case BMI2_GYRO: + + /* Validate filter mode */ + if (fifo_filter_data <= BMI2_MAX_VALUE_FIFO_FILTER) + { + /* Set the gyroscope FIFO filter data */ + rslt = bmi2_get_regs(BMI2_FIFO_DOWNS_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + data = BMI2_SET_BITS(data, BMI2_GYR_FIFO_FILT_DATA, fifo_filter_data); + rslt = bmi2_set_regs(BMI2_FIFO_DOWNS_ADDR, &data, 1, dev); + } + } + else + { + rslt = BMI2_E_OUT_OF_RANGE; + } + + break; + default: + rslt = BMI2_E_INVALID_SENSOR; + break; + } + } + + return rslt; +} + +/*! + * @brief This API gets the FIFO accelerometer or gyroscope filter data. + */ +int8_t bmi2_get_fifo_filter_data(uint8_t sens_sel, uint8_t *fifo_filter_data, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store FIFO filter mode */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (fifo_filter_data != NULL)) + { + switch (sens_sel) + { + case BMI2_ACCEL: + + /* Read the accelerometer FIFO filter data */ + rslt = bmi2_get_regs(BMI2_FIFO_DOWNS_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + (*fifo_filter_data) = BMI2_GET_BITS(data, BMI2_ACC_FIFO_FILT_DATA); + } + + break; + case BMI2_GYRO: + + /* Read the gyroscope FIFO filter data */ + rslt = bmi2_get_regs(BMI2_FIFO_DOWNS_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + (*fifo_filter_data) = BMI2_GET_BITS(data, BMI2_GYR_FIFO_FILT_DATA); + } + + break; + default: + rslt = BMI2_E_INVALID_SENSOR; + break; + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API sets the down-sampling rates for accelerometer or gyroscope + * FIFO data. + */ +int8_t bmi2_set_fifo_down_sample(uint8_t sens_sel, uint8_t fifo_down_samp, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store sampling rate */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + switch (sens_sel) + { + case BMI2_ACCEL: + + /* Set the accelerometer FIFO down sampling rate */ + rslt = bmi2_get_regs(BMI2_FIFO_DOWNS_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + data = BMI2_SET_BITS(data, BMI2_ACC_FIFO_DOWNS, fifo_down_samp); + rslt = bmi2_set_regs(BMI2_FIFO_DOWNS_ADDR, &data, 1, dev); + } + + break; + case BMI2_GYRO: + + /* Set the gyroscope FIFO down sampling rate */ + rslt = bmi2_get_regs(BMI2_FIFO_DOWNS_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + data = BMI2_SET_BIT_POS0(data, BMI2_GYR_FIFO_DOWNS, fifo_down_samp); + rslt = bmi2_set_regs(BMI2_FIFO_DOWNS_ADDR, &data, 1, dev); + } + + break; + default: + rslt = BMI2_E_INVALID_SENSOR; + break; + } + } + + return rslt; +} + +/*! + * @brief This API reads the down sampling rates which is configured for + * accelerometer or gyroscope FIFO data. + */ +int8_t bmi2_get_fifo_down_sample(uint8_t sens_sel, uint8_t *fifo_down_samp, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store sampling rate */ + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (fifo_down_samp != NULL)) + { + switch (sens_sel) + { + case BMI2_ACCEL: + + /* Read the accelerometer FIFO down data sampling rate */ + rslt = bmi2_get_regs(BMI2_FIFO_DOWNS_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + (*fifo_down_samp) = BMI2_GET_BITS(data, BMI2_ACC_FIFO_DOWNS); + } + + break; + case BMI2_GYRO: + + /* Read the gyroscope FIFO down data sampling rate */ + rslt = bmi2_get_regs(BMI2_FIFO_DOWNS_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + (*fifo_down_samp) = BMI2_GET_BIT_POS0(data, BMI2_GYR_FIFO_DOWNS); + } + + break; + default: + rslt = BMI2_E_INVALID_SENSOR; + break; + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API gets the length of FIFO data available in the sensor in + * bytes. + */ +int8_t bmi2_get_fifo_length(uint16_t *fifo_length, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define byte index */ + uint8_t index = 0; + + /* Array to store FIFO data length */ + uint8_t data[BMI2_FIFO_DATA_LENGTH] = { 0 }; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (fifo_length != NULL)) + { + /* Read FIFO length */ + rslt = bmi2_get_regs(BMI2_FIFO_LENGTH_0_ADDR, data, BMI2_FIFO_DATA_LENGTH, dev); + if (rslt == BMI2_OK) + { + /* Get the MSB byte index */ + index = BMI2_FIFO_LENGTH_MSB_BYTE; + + /* Get the MSB byte of FIFO length */ + data[index] = BMI2_GET_BIT_POS0(data[index], BMI2_FIFO_BYTE_COUNTER_MSB); + + /* Get total FIFO length */ + (*fifo_length) = ((data[index] << 8) | data[index - 1]); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API reads the user-defined bytes of data from the given register + * address of auxiliary sensor in manual mode. + * + * @note Change of BMI2_AUX_RD_ADDR is only allowed if AUX is not busy. + */ +int8_t bmi2_read_aux_man_mode(uint8_t reg_addr, uint8_t *aux_data, uint16_t len, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store burst length */ + uint8_t burst_len = 0; + + /* Variable to define APS status */ + uint8_t aps_stat = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (aux_data != NULL)) + { + /* Validate if manual mode */ + if (dev->aux_man_en) + { + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + if (aps_stat == BMI2_ENABLE) + { + /* Disable APS if enabled */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + if (rslt == BMI2_OK) + { + /* Map the register value set to that of burst + * length + */ + rslt = map_read_len(&burst_len, dev); + if (rslt == BMI2_OK) + { + /* Read auxiliary data */ + rslt = read_aux_data(reg_addr, aux_data, len, burst_len, dev); + } + } + + /* Enable Advance power save if disabled for reading + * data and not when already disabled + */ + if ((rslt == BMI2_OK) && (aps_stat == BMI2_ENABLE)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + else + { + rslt = BMI2_E_AUX_INVALID_CFG; + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API writes the user-defined bytes of data and the address of + * auxiliary sensor where data is to be written in manual mode. + * + * @note Change of BMI2_AUX_WR_ADDR is only allowed if AUX is not busy. + */ +int8_t bmi2_write_aux_man_mode(uint8_t reg_addr, const uint8_t *aux_data, uint16_t len, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define loop */ + uint8_t loop = 0; + + /* Variable to define APS status */ + uint8_t aps_stat = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (aux_data != NULL)) + { + /* Validate if manual mode */ + if (dev->aux_man_en) + { + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + if (aps_stat == BMI2_ENABLE) + { + /* Disable APS if enabled */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + /* Byte write data in the corresponding address */ + if (rslt == BMI2_OK) + { + for (; ((loop < len) && (rslt == BMI2_OK)); loop++) + { + rslt = write_aux_data((reg_addr + loop), aux_data[loop], dev); + dev->delay_us(1000, dev->intf_ptr); + } + } + + /* Enable Advance power save if disabled for writing + * data and not when already disabled + */ + if ((rslt == BMI2_OK) && (aps_stat == BMI2_ENABLE)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + else + { + rslt = BMI2_E_AUX_INVALID_CFG; + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API gets the data ready status of accelerometer, gyroscope, + * auxiliary, command decoder and busy status of auxiliary. + */ +int8_t bmi2_get_status(uint8_t *status, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (status != NULL)) + { + rslt = bmi2_get_regs(BMI2_STATUS_ADDR, status, 1, dev); + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API enables/disables OIS interface. + */ +int8_t bmi2_set_ois_interface(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + rslt = bmi2_get_regs(BMI2_IF_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + /* Enable/Disable OIS interface */ + reg_data = BMI2_SET_BITS(reg_data, BMI2_OIS_IF_EN, enable); + if (enable) + { + /* Disable auxiliary interface if OIS is enabled */ + reg_data = BMI2_SET_BIT_VAL0(reg_data, BMI2_AUX_IF_EN); + } + + /* Set the OIS interface configurations */ + rslt = bmi2_set_regs(BMI2_IF_CONF_ADDR, ®_data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the configuration for SPI3 interface mode. + */ +int8_t bmi2_set_spi3_interface_mode(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Get the SPI3 Mode configurations */ + rslt = bmi2_get_regs(BMI2_IF_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + /* SPI3 Mode interface config */ + reg_data = BMI2_SET_BIT_POS0(reg_data, BMI2_SPI3_MODE, enable); + + /* Set the SPI3 Mode configurations */ + rslt = bmi2_set_regs(BMI2_IF_CONF_ADDR, ®_data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API gets the configuration for SPI3 interface mode. + */ +int8_t bmi2_get_spi3_interface_mode(uint8_t *enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (enable != NULL)) + { + rslt = bmi2_get_regs(BMI2_IF_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + /* Get SPI3 Mode interface config */ + (*enable) = BMI2_GET_BIT_POS0(reg_data, BMI2_SPI3_MODE); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API sets the configuration for SPI3 OIS interface mode. + */ +int8_t bmi2_set_spi3_ois_mode(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Get the SPI3 ois configurations */ + rslt = bmi2_get_regs(BMI2_IF_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + /* SPI3 ois interface config */ + reg_data = BMI2_SET_BITS(reg_data, BMI2_SPI3_OIS, enable); + + /* Set the SPI3 ois configurations */ + rslt = bmi2_set_regs(BMI2_IF_CONF_ADDR, ®_data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API gets the configuration for SPI3 OIS interface mode. + */ +int8_t bmi2_get_spi3_ois_mode(uint8_t *enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (enable != NULL)) + { + rslt = bmi2_get_regs(BMI2_IF_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + /* Get SPI3 Mode interface config */ + (*enable) = BMI2_GET_BITS(reg_data, BMI2_SPI3_OIS); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API gets the internal error status of the sensor. + */ +int8_t bmi2_get_internal_error_status(uint8_t *status, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (status != NULL)) + { + rslt = bmi2_get_regs(BMI2_INTERNAL_ERR_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + /* Get internal error status */ + (*status) = BMI2_GET_BIT_POS0(reg_data, BMI2_INTERNAL_ERROR_REG); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API gets saturation status of the sensor. + */ +int8_t bmi2_get_saturation_status(uint8_t *status, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (status != NULL)) + { + rslt = bmi2_get_regs(BMI2_SATURATION_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + /* Get saturation status */ + (*status) = BMI2_GET_BIT_POS0(reg_data, BMI2_SATURATION_REG); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API can be used to write sync commands like ODR, sync period, + * frequency and phase, resolution ratio, sync time and delay time. + */ +int8_t bmi2_write_sync_commands(const uint8_t *command, uint8_t n_comm, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (command != NULL)) + { + rslt = bmi2_set_regs(BMI2_SYNC_COMMAND_ADDR, command, n_comm, dev); + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API performs self-test to check the proper functionality of the + * accelerometer sensor. + */ +int8_t bmi2_perform_accel_self_test(struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store self-test result */ + int8_t st_rslt = 0; + + /* Structure to define positive accelerometer axes */ + struct bmi2_sens_axes_data positive = { 0, 0, 0, 0 }; + + /* Structure to define negative accelerometer axes */ + struct bmi2_sens_axes_data negative = { 0, 0, 0, 0 }; + + /* Structure for difference of accelerometer values in g */ + struct bmi2_selftest_delta_limit accel_data_diff = { 0, 0, 0 }; + + /* Structure for difference of accelerometer values in mg */ + struct bmi2_selftest_delta_limit accel_data_diff_mg = { 0, 0, 0 }; + + /* Initialize the polarity of self-test as positive */ + int8_t sign = BMI2_ENABLE; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Sets the configuration required before enabling self-test */ + rslt = pre_self_test_config(dev); + + /* Wait for greater than 2 milliseconds */ + dev->delay_us(3000, dev->intf_ptr); + if (rslt == BMI2_OK) + { + do + { + /* Select positive first, then negative polarity + * after enabling self-test + */ + rslt = self_test_config((uint8_t) sign, dev); + if (rslt == BMI2_OK) + { + /* Wait for greater than 50 milli-sec */ + dev->delay_us(51000, dev->intf_ptr); + + /* If polarity is positive */ + if (sign == BMI2_ENABLE) + { + /* Read and store positive acceleration value */ + rslt = read_accel_xyz(&positive, dev); + } + /* If polarity is negative */ + else if (sign == BMI2_DISABLE) + { + /* Read and store negative acceleration value */ + rslt = read_accel_xyz(&negative, dev); + } + } + else + { + /* Break if error */ + break; + } + + /* Break if error */ + if (rslt != BMI2_OK) + { + break; + } + + /* Turn the polarity of self-test negative */ + sign--; + } while (sign >= 0); + if (rslt == BMI2_OK) + { + /* Subtract -ve acceleration values from that of +ve values */ + accel_data_diff.x = (positive.x) - (negative.x); + accel_data_diff.y = (positive.y) - (negative.y); + accel_data_diff.z = (positive.z) - (negative.z); + + /* Convert differences of acceleration values + * from 'g' to 'mg' + */ + convert_lsb_g(&accel_data_diff, &accel_data_diff_mg, dev); + + /* Validate self-test for acceleration values + * in mg and get the self-test result + */ + st_rslt = validate_self_test(&accel_data_diff_mg); + + /* Trigger a soft reset after performing self-test */ + rslt = bmi2_soft_reset(dev); + + /* Return the self-test result */ + if (rslt == BMI2_OK) + { + rslt = st_rslt; + } + } + } + } + + return rslt; +} + +/*! + * @brief This API maps/unmaps feature interrupts to that of interrupt pins. + */ +int8_t bmi2_map_feat_int(uint8_t type, enum bmi2_hw_int_pin hw_int_pin, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define the value of feature interrupts */ + uint8_t feat_int = 0; + + /* Array to store the interrupt mask bits */ + uint8_t data_array[2] = { 0 }; + + /* Structure to define map the interrupts */ + struct bmi2_map_int map_int = { 0 }; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Read interrupt map1 and map2 and register */ + rslt = bmi2_get_regs(BMI2_INT1_MAP_FEAT_ADDR, data_array, 2, dev); + + if (rslt == BMI2_OK) + { + /* Get the value of the feature interrupt to be mapped */ + extract_feat_int_map(&map_int, type, dev); + + feat_int = map_int.sens_map_int; + + /* Map the interrupts */ + rslt = map_feat_int(data_array, hw_int_pin, feat_int); + + /* Map the interrupts to INT1 and INT2 map register */ + if (rslt == BMI2_OK) + { + rslt = bmi2_set_regs(BMI2_INT1_MAP_FEAT_ADDR, &data_array[0], 1, dev); + if (rslt == BMI2_OK) + { + rslt = bmi2_set_regs(BMI2_INT2_MAP_FEAT_ADDR, &data_array[1], 1, dev); + } + } + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API maps/un-maps data interrupts to that of interrupt pins. + */ +int8_t bmi2_map_data_int(uint8_t data_int, enum bmi2_hw_int_pin int_pin, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to mask interrupt pin 1 - lower nibble */ + uint8_t int1_mask = data_int; + + /* Variable to mask interrupt pin 2 - higher nibble */ + uint8_t int2_mask = (uint8_t)(data_int << 4); + + /* Variable to store register data */ + uint8_t reg_data = 0; + + /* Read interrupt map1 and map2 and register */ + rslt = bmi2_get_regs(BMI2_INT_MAP_DATA_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + if (int_pin < BMI2_INT_PIN_MAX) + { + switch (int_pin) + { + case BMI2_INT_NONE: + + /* Un-Map the corresponding data + * interrupt to both interrupt pin 1 and 2 + */ + reg_data &= ~(int1_mask | int2_mask); + break; + case BMI2_INT1: + + /* Map the corresponding data interrupt to + * interrupt pin 1 + */ + reg_data |= int1_mask; + break; + case BMI2_INT2: + + /* Map the corresponding data interrupt to + * interrupt pin 2 + */ + reg_data |= int2_mask; + break; + case BMI2_INT_BOTH: + + /* Map the corresponding data + * interrupt to both interrupt pin 1 and 2 + */ + reg_data |= (int1_mask | int2_mask); + break; + default: + break; + } + + /* Set the interrupts in the map register */ + rslt = bmi2_set_regs(BMI2_INT_MAP_DATA_ADDR, ®_data, 1, dev); + } + else + { + /* Return error if invalid pin selection */ + rslt = BMI2_E_INVALID_INT_PIN; + } + } + + return rslt; +} + +/*! + * @brief This API gets the re-mapped x, y and z axes from the sensor and + * updates the values in the device structure. + */ +int8_t bmi2_get_remap_axes(struct bmi2_remap *remapped_axis, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Initialize the local structure for axis re-mapping */ + struct bmi2_axes_remap remap = { 0, 0, 0, 0, 0, 0 }; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (remapped_axis != NULL)) + { + /* Get the re-mapped axes from the sensor */ + rslt = get_remap_axes(&remap, dev); + if (rslt == BMI2_OK) + { + /* Store the re-mapped x-axis value in device structure + * and its user-value in the interface structure + */ + switch (remap.x_axis) + { + case BMI2_MAP_X_AXIS: + + /* If mapped to x-axis */ + dev->remap.x_axis = BMI2_MAP_X_AXIS; + remapped_axis->x = BMI2_X; + break; + case BMI2_MAP_Y_AXIS: + + /* If mapped to y-axis */ + dev->remap.x_axis = BMI2_MAP_Y_AXIS; + remapped_axis->x = BMI2_Y; + break; + case BMI2_MAP_Z_AXIS: + + /* If mapped to z-axis */ + dev->remap.x_axis = BMI2_MAP_Z_AXIS; + remapped_axis->x = BMI2_Z; + break; + default: + break; + } + + /* Store the re-mapped x-axis sign in device structure + * and its user-value in the interface structure + */ + if (remap.x_axis_sign) + { + /* If x-axis is mapped to -ve sign */ + dev->remap.x_axis_sign = BMI2_NEG_SIGN; + remapped_axis->x |= BMI2_AXIS_SIGN; + } + else + { + dev->remap.x_axis_sign = BMI2_POS_SIGN; + } + + /* Store the re-mapped y-axis value in device structure + * and its user-value in the interface structure + */ + switch (remap.y_axis) + { + case BMI2_MAP_X_AXIS: + + /* If mapped to x-axis */ + dev->remap.y_axis = BMI2_MAP_X_AXIS; + remapped_axis->y = BMI2_X; + break; + case BMI2_MAP_Y_AXIS: + + /* If mapped to y-axis */ + dev->remap.y_axis = BMI2_MAP_Y_AXIS; + remapped_axis->y = BMI2_Y; + break; + case BMI2_MAP_Z_AXIS: + + /* If mapped to z-axis */ + dev->remap.y_axis = BMI2_MAP_Z_AXIS; + remapped_axis->y = BMI2_Z; + break; + default: + break; + } + + /* Store the re-mapped y-axis sign in device structure + * and its user-value in the interface structure + */ + if (remap.y_axis_sign) + { + /* If y-axis is mapped to -ve sign */ + dev->remap.y_axis_sign = BMI2_NEG_SIGN; + remapped_axis->y |= BMI2_AXIS_SIGN; + } + else + { + dev->remap.y_axis_sign = BMI2_POS_SIGN; + } + + /* Store the re-mapped z-axis value in device structure + * and its user-value in the interface structure + */ + switch (remap.z_axis) + { + case BMI2_MAP_X_AXIS: + + /* If mapped to x-axis */ + dev->remap.z_axis = BMI2_MAP_X_AXIS; + remapped_axis->z = BMI2_X; + break; + case BMI2_MAP_Y_AXIS: + + /* If mapped to y-axis */ + dev->remap.z_axis = BMI2_MAP_Y_AXIS; + remapped_axis->z = BMI2_Y; + break; + case BMI2_MAP_Z_AXIS: + + /* If mapped to z-axis */ + dev->remap.z_axis = BMI2_MAP_Z_AXIS; + remapped_axis->z = BMI2_Z; + break; + default: + break; + } + + /* Store the re-mapped z-axis sign in device structure + * and its user-value in the interface structure + */ + if (remap.z_axis_sign) + { + /* If z-axis is mapped to -ve sign */ + dev->remap.z_axis_sign = BMI2_NEG_SIGN; + remapped_axis->z |= BMI2_AXIS_SIGN; + } + else + { + dev->remap.z_axis_sign = BMI2_POS_SIGN; + } + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API sets the re-mapped x, y and z axes to the sensor and + * updates the them in the device structure. + */ +int8_t bmi2_set_remap_axes(const struct bmi2_remap *remapped_axis, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store all the re-mapped axes */ + uint8_t remap_axes = 0; + + /* Variable to store the re-mapped x-axes */ + uint8_t remap_x = 0; + + /* Variable to store the re-mapped y-axes */ + uint8_t remap_y = 0; + + /* Variable to store the re-mapped z-axes */ + uint8_t remap_z = 0; + + /* Initialize the local structure for axis re-mapping */ + struct bmi2_axes_remap remap = { 0, 0, 0, 0, 0, 0 }; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (remapped_axis != NULL)) + { + /* Check whether all the axes are re-mapped */ + remap_axes = remapped_axis->x | remapped_axis->y | remapped_axis->z; + + /* If all the axes are re-mapped */ + if ((remap_axes & BMI2_AXIS_MASK) == BMI2_AXIS_MASK) + { + /* Get the re-mapped value of x, y and z axis */ + remap_x = remapped_axis->x & BMI2_AXIS_MASK; + remap_y = remapped_axis->y & BMI2_AXIS_MASK; + remap_z = remapped_axis->z & BMI2_AXIS_MASK; + + /* Store the value of re-mapped x-axis in both + * device structure and the local structure + */ + switch (remap_x) + { + case BMI2_X: + + /* If mapped to x-axis */ + dev->remap.x_axis = BMI2_MAP_X_AXIS; + remap.x_axis = BMI2_MAP_X_AXIS; + break; + case BMI2_Y: + + /* If mapped to y-axis */ + dev->remap.x_axis = BMI2_MAP_Y_AXIS; + remap.x_axis = BMI2_MAP_Y_AXIS; + break; + case BMI2_Z: + + /* If mapped to z-axis */ + dev->remap.x_axis = BMI2_MAP_Z_AXIS; + remap.x_axis = BMI2_MAP_Z_AXIS; + break; + default: + break; + } + + /* Store the re-mapped x-axis sign in the device + * structure and its value in local structure + */ + if (remapped_axis->x & BMI2_AXIS_SIGN) + { + /* If x-axis is mapped to -ve sign */ + dev->remap.x_axis_sign = BMI2_NEG_SIGN; + remap.x_axis_sign = BMI2_MAP_NEGATIVE; + } + else + { + dev->remap.x_axis_sign = BMI2_POS_SIGN; + remap.x_axis_sign = BMI2_MAP_POSITIVE; + } + + /* Store the value of re-mapped y-axis in both + * device structure and the local structure + */ + switch (remap_y) + { + case BMI2_X: + + /* If mapped to x-axis */ + dev->remap.y_axis = BMI2_MAP_X_AXIS; + remap.y_axis = BMI2_MAP_X_AXIS; + break; + case BMI2_Y: + + /* If mapped to y-axis */ + dev->remap.y_axis = BMI2_MAP_Y_AXIS; + remap.y_axis = BMI2_MAP_Y_AXIS; + break; + case BMI2_Z: + + /* If mapped to z-axis */ + dev->remap.y_axis = BMI2_MAP_Z_AXIS; + remap.y_axis = BMI2_MAP_Z_AXIS; + break; + default: + break; + } + + /* Store the re-mapped y-axis sign in the device + * structure and its value in local structure + */ + if (remapped_axis->y & BMI2_AXIS_SIGN) + { + /* If y-axis is mapped to -ve sign */ + dev->remap.y_axis_sign = BMI2_NEG_SIGN; + remap.y_axis_sign = BMI2_MAP_NEGATIVE; + } + else + { + dev->remap.y_axis_sign = BMI2_POS_SIGN; + remap.y_axis_sign = BMI2_MAP_POSITIVE; + } + + /* Store the value of re-mapped z-axis in both + * device structure and the local structure + */ + switch (remap_z) + { + case BMI2_X: + + /* If mapped to x-axis */ + dev->remap.z_axis = BMI2_MAP_X_AXIS; + remap.z_axis = BMI2_MAP_X_AXIS; + break; + case BMI2_Y: + + /* If mapped to y-axis */ + dev->remap.z_axis = BMI2_MAP_Y_AXIS; + remap.z_axis = BMI2_MAP_Y_AXIS; + break; + case BMI2_Z: + + /* If mapped to z-axis */ + dev->remap.z_axis = BMI2_MAP_Z_AXIS; + remap.z_axis = BMI2_MAP_Z_AXIS; + break; + default: + break; + } + + /* Store the re-mapped z-axis sign in the device + * structure and its value in local structure + */ + if (remapped_axis->z & BMI2_AXIS_SIGN) + { + /* If z-axis is mapped to -ve sign */ + dev->remap.z_axis_sign = BMI2_NEG_SIGN; + remap.z_axis_sign = BMI2_MAP_NEGATIVE; + } + else + { + dev->remap.z_axis_sign = BMI2_POS_SIGN; + remap.z_axis_sign = BMI2_MAP_POSITIVE; + } + + /* Set the re-mapped axes in the sensor */ + rslt = set_remap_axes(&remap, dev); + } + else + { + rslt = BMI2_E_REMAP_ERROR; + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API enables/disables gyroscope offset compensation. It adds the + * offsets defined in the offset register with gyroscope data. + */ +int8_t bmi2_set_gyro_offset_comp(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define register data */ + uint8_t reg_data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Get the status of gyroscope offset enable */ + rslt = bmi2_get_regs(BMI2_GYR_OFF_COMP_6_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + reg_data = BMI2_SET_BITS(reg_data, BMI2_GYR_OFF_COMP_EN, enable); + + /* Enable/Disable gyroscope offset compensation */ + rslt = bmi2_set_regs(BMI2_GYR_OFF_COMP_6_ADDR, ®_data, 1, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API reads the gyroscope offset compensation. + */ +int8_t bmi2_get_gyro_offset_comp(uint8_t *offset, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define register data */ + uint8_t reg_data = 0; + + /* Get the status of gyroscope gain */ + rslt = bmi2_get_regs(BMI2_GYR_OFF_COMP_6_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + (*offset) = BMI2_GET_BITS(reg_data, BMI2_GYR_OFF_COMP_EN); + } + + return rslt; +} + +/*! + * @brief This API enables/disables gyroscope gain for Sensitivity Error Compensation. + */ +int8_t bmi2_set_gyro_gain(uint8_t gyro_gain, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define register data */ + uint8_t reg_data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Get the status of gyroscope gain enable */ + rslt = bmi2_get_regs(BMI2_GYR_OFF_COMP_6_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + reg_data = BMI2_SET_BITS(reg_data, BMI2_GYR_GAIN_EN, gyro_gain); + + /* Enable/Disable gyroscope gain */ + rslt = bmi2_set_regs(BMI2_GYR_OFF_COMP_6_ADDR, ®_data, 1, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API reads the Gyro gain. + */ +int8_t bmi2_get_gyro_gain(uint8_t *gyro_gain, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define register data */ + uint8_t reg_data = 0; + + /* Get the status of gyroscope gain */ + rslt = bmi2_get_regs(BMI2_GYR_OFF_COMP_6_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + (*gyro_gain) = BMI2_GET_BITS(reg_data, BMI2_GYR_GAIN_EN); + } + + return rslt; +} + +/*! + * @brief This API reads the gyroscope bias values for each axis which is used + * for gyroscope offset compensation. + */ +int8_t bmi2_read_gyro_offset_comp_axes(struct bmi2_sens_axes_data *gyr_off_comp_axes, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define register data */ + uint8_t reg_data[4] = { 0 }; + + /* Variable to store LSB value of offset compensation for x-axis */ + uint8_t gyr_off_lsb_x; + + /* Variable to store LSB value of offset compensation for y-axis */ + uint8_t gyr_off_lsb_y; + + /* Variable to store LSB value of offset compensation for z-axis */ + uint8_t gyr_off_lsb_z; + + /* Variable to store MSB value of offset compensation for x-axis */ + uint8_t gyr_off_msb_x; + + /* Variable to store MSB value of offset compensation for y-axis */ + uint8_t gyr_off_msb_y; + + /* Variable to store MSB value of offset compensation for z-axis */ + uint8_t gyr_off_msb_z; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (gyr_off_comp_axes != NULL)) + { + /* Get the gyroscope compensated offset values */ + rslt = bmi2_get_regs(BMI2_GYR_OFF_COMP_3_ADDR, reg_data, 4, dev); + if (rslt == BMI2_OK) + { + /* Get LSB and MSB values of offset compensation for + * x, y and z axis + */ + gyr_off_lsb_x = reg_data[0]; + gyr_off_lsb_y = reg_data[1]; + gyr_off_lsb_z = reg_data[2]; + gyr_off_msb_x = reg_data[3] & BMI2_GYR_OFF_COMP_MSB_X_MASK; + gyr_off_msb_y = reg_data[3] & BMI2_GYR_OFF_COMP_MSB_Y_MASK; + gyr_off_msb_z = reg_data[3] & BMI2_GYR_OFF_COMP_MSB_Z_MASK; + + /* Gyroscope offset compensation value for x-axis */ + gyr_off_comp_axes->x = + ((int16_t)(((gyr_off_msb_x << 8) | gyr_off_lsb_x) << BMI2_N_SENSE_COUNT_6) >> BMI2_N_SENSE_COUNT_6); + + /* Gyroscope offset compensation value for y-axis */ + gyr_off_comp_axes->y = + ((int16_t)(((gyr_off_msb_y << 6) | gyr_off_lsb_y) << BMI2_N_SENSE_COUNT_6) >> BMI2_N_SENSE_COUNT_6); + + /* Gyroscope offset compensation value for z-axis */ + gyr_off_comp_axes->z = + ((int16_t)(((gyr_off_msb_z << 4) | gyr_off_lsb_z) << BMI2_N_SENSE_COUNT_6) >> BMI2_N_SENSE_COUNT_6); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API writes the gyroscope bias values for each axis which is used + * for gyroscope offset compensation. + */ +int8_t bmi2_write_gyro_offset_comp_axes(const struct bmi2_sens_axes_data *gyr_off_comp_axes, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define register data */ + uint8_t reg_data[4] = { 0 }; + + /* Variable to store MSB value of offset compensation for x-axis */ + uint8_t gyr_off_msb_x; + + /* Variable to store MSB value of offset compensation for y-axis */ + uint8_t gyr_off_msb_y; + + /* Variable to store MSB value of offset compensation for z-axis */ + uint8_t gyr_off_msb_z; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (gyr_off_comp_axes != NULL)) + { + /* Get the gyroscope offset compensation values of axes */ + rslt = bmi2_get_regs(BMI2_GYR_OFF_COMP_3_ADDR, reg_data, 4, dev); + + if (rslt == BMI2_OK) + { + /* Get MSB value of x-axis from user-input */ + gyr_off_msb_x = (uint8_t)((gyr_off_comp_axes->x & BMI2_GYR_OFF_COMP_MSB_MASK) >> 8); + + /* Get MSB value of y-axis from user-input */ + gyr_off_msb_y = (uint8_t)((gyr_off_comp_axes->y & BMI2_GYR_OFF_COMP_MSB_MASK) >> 8); + + /* Get MSB value of z-axis from user-input */ + gyr_off_msb_z = (uint8_t)((gyr_off_comp_axes->z & BMI2_GYR_OFF_COMP_MSB_MASK) >> 8); + + /* Get LSB value of x-axis from user-input */ + reg_data[0] = (uint8_t)(gyr_off_comp_axes->x & BMI2_GYR_OFF_COMP_LSB_MASK); + + /* Get LSB value of y-axis from user-input */ + reg_data[1] = (uint8_t)(gyr_off_comp_axes->y & BMI2_GYR_OFF_COMP_LSB_MASK); + + /* Get LSB value of z-axis from user-input */ + reg_data[2] = (uint8_t)(gyr_off_comp_axes->z & BMI2_GYR_OFF_COMP_LSB_MASK); + + /* Get MSB value of x-axis to be set */ + reg_data[3] = BMI2_SET_BIT_POS0(reg_data[3], BMI2_GYR_OFF_COMP_MSB_X, gyr_off_msb_x); + + /* Get MSB value of y-axis to be set */ + reg_data[3] = BMI2_SET_BITS(reg_data[3], BMI2_GYR_OFF_COMP_MSB_Y, gyr_off_msb_y); + + /* Get MSB value of z-axis to be set */ + reg_data[3] = BMI2_SET_BITS(reg_data[3], BMI2_GYR_OFF_COMP_MSB_Z, gyr_off_msb_z); + + /* Set the offset compensation values of axes */ + rslt = bmi2_set_regs(BMI2_GYR_OFF_COMP_3_ADDR, reg_data, 4, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API updates the cross sensitivity coefficient between gyroscope's + * X and Z axes. + */ +int8_t bmi2_get_gyro_cross_sense(struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + struct bmi2_feat_sensor_data data; + + /* Check if the feature is supported by this variant */ + if (dev->variant_feature & BMI2_GYRO_CROSS_SENS_ENABLE) + { + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Select the feature whose data is to be acquired */ + data.type = BMI2_GYRO_CROSS_SENSE; + + /* Get the respective data */ + rslt = bmi2_get_feature_data(&data, 1, dev); + if (rslt == BMI2_OK) + { + /* Update the gyroscope cross sense value of z axis + * in the device structure + */ + dev->gyr_cross_sens_zx = data.sens_data.correction_factor_zx; + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This API gets Error bits and message indicating internal status. + */ +int8_t bmi2_get_internal_status(uint8_t *int_stat, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (int_stat != NULL)) + { + /* Wait till ASIC is initialized */ + dev->delay_us(BMI2_INTERNAL_STATUS_READ_DELAY_MS, dev->intf_ptr); + + /* Get the error bits and message */ + rslt = bmi2_get_regs(BMI2_INTERNAL_STATUS_ADDR, int_stat, 1, dev); + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API performs Fast Offset Compensation for accelerometer. + */ +int8_t bmi2_perform_accel_foc(const struct bmi2_accel_foc_g_value *accel_g_value, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Structure to define the accelerometer configurations */ + struct bmi2_accel_config acc_cfg = { 0, 0, 0, 0 }; + + /* Variable to store status of advance power save */ + uint8_t aps = 0; + + /* Variable to store status of accelerometer enable */ + uint8_t acc_en = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (accel_g_value != NULL)) + { + /* Check for input validity */ + if ((((BMI2_ABS(accel_g_value->x)) + (BMI2_ABS(accel_g_value->y)) + (BMI2_ABS(accel_g_value->z))) == 1) && + ((accel_g_value->sign == 1) || (accel_g_value->sign == 0))) + { + rslt = verify_foc_position(BMI2_ACCEL, accel_g_value, dev); + + /* Save accelerometer configurations, accelerometer + * enable status and advance power save status + */ + if (rslt == BMI2_OK) + { + rslt = save_accel_foc_config(&acc_cfg, &aps, &acc_en, dev); + } + + /* Set configurations for FOC */ + if (rslt == BMI2_OK) + { + rslt = set_accel_foc_config(dev); + } + + /* Perform accelerometer FOC */ + if (rslt == BMI2_OK) + { + rslt = perform_accel_foc(accel_g_value, &acc_cfg, dev); + } + + /* Restore the saved configurations */ + if (rslt == BMI2_OK) + { + rslt = restore_accel_foc_config(&acc_cfg, aps, acc_en, dev); + } + } + else + { + rslt = BMI2_E_INVALID_INPUT; + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API performs Fast Offset Compensation for gyroscope. + */ +int8_t bmi2_perform_gyro_foc(struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Structure to define the gyroscope configurations */ + struct bmi2_gyro_config gyr_cfg = { 0, 0, 0, 0, 0, 0 }; + + /* Variable to store status of advance power save */ + uint8_t aps = 0; + + /* Variable to store status of gyroscope enable */ + uint8_t gyr_en = 0; + + /* Array of structure to store gyroscope data */ + struct bmi2_sens_axes_data gyr_value[128]; + + /* Structure to store gyroscope data temporarily */ + struct bmi2_foc_temp_value temp = { 0, 0, 0 }; + + /* Variable to store status read from the status register */ + uint8_t reg_status = 0; + + /* Variable to define count */ + uint8_t loop = 0; + + /* Structure to store the offset values to be stored in the register */ + struct bmi2_sens_axes_data gyro_offset = { 0, 0, 0, 0 }; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Save gyroscope configurations, gyroscope enable + * status and advance power save status + */ + rslt = save_gyro_config(&gyr_cfg, &aps, &gyr_en, dev); + + /* Set configurations for gyroscope FOC */ + if (rslt == BMI2_OK) + { + rslt = set_gyro_foc_config(dev); + } + + /* Perform FOC */ + if (rslt == BMI2_OK) + { + for (loop = 0; loop < 128; loop++) + { + /* Giving a delay of more than 40ms since ODR is configured as 25Hz */ + dev->delay_us(50000, dev->intf_ptr); + + /* Get gyroscope data ready interrupt status */ + rslt = bmi2_get_status(®_status, dev); + + /* Read 128 samples of gyroscope data on data ready interrupt */ + if ((rslt == BMI2_OK) && (reg_status & BMI2_DRDY_GYR)) + { + rslt = read_gyro_xyz(&gyr_value[loop], dev); + if (rslt == BMI2_OK) + { + /* Store the data in a temporary structure */ + temp.x = temp.x + (int32_t)gyr_value[loop].x; + temp.y = temp.y + (int32_t)gyr_value[loop].y; + temp.z = temp.z + (int32_t)gyr_value[loop].z; + } + } + + if (rslt != BMI2_OK) + { + break; + } + else if ((reg_status & BMI2_DRDY_GYR) != BMI2_DRDY_GYR) + { + rslt = BMI2_E_INVALID_STATUS; + break; + } + } + + if (rslt == BMI2_OK) + { + /* Take average of x, y and z data for lesser + * noise. It is same as offset data since lsb/dps + * is same for both data and offset register + */ + gyro_offset.x = (int16_t)(temp.x / 128); + gyro_offset.y = (int16_t)(temp.y / 128); + gyro_offset.z = (int16_t)(temp.z / 128); + + /* Saturate gyroscope data since the offset + * registers are of 10 bit value where as the + * gyroscope data is of 16 bit value + */ + saturate_gyro_data(&gyro_offset); + + /* Invert the gyroscope offset data */ + invert_gyro_offset(&gyro_offset); + + /* Write offset data in the gyroscope offset + * compensation register + */ + rslt = bmi2_write_gyro_offset_comp_axes(&gyro_offset, dev); + } + + /* Enable gyroscope offset compensation */ + if (rslt == BMI2_OK) + { + rslt = bmi2_set_gyro_offset_comp(BMI2_ENABLE, dev); + } + + /* Restore the saved gyroscope configurations */ + if (rslt == BMI2_OK) + { + rslt = restore_gyro_config(&gyr_cfg, aps, gyr_en, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API is used to get the feature configuration from the + * selected page. + */ +int8_t bmi2_get_feat_config(uint8_t sw_page, uint8_t *feat_config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define bytes remaining to read */ + uint8_t bytes_remain = BMI2_FEAT_SIZE_IN_BYTES; + + /* Variable to define the read-write length */ + uint8_t read_write_len = 0; + + /* Variable to define the feature configuration address */ + uint8_t addr = BMI2_FEATURES_REG_ADDR; + + /* Variable to define index */ + uint8_t index = 0; + + if ((feat_config == NULL) || (dev == NULL)) + { + rslt = BMI2_E_NULL_PTR; + } + else + { + /* Check whether the page is valid */ + if (sw_page < dev->page_max) + { + /* Switch page */ + rslt = bmi2_set_regs(BMI2_FEAT_PAGE_ADDR, &sw_page, 1, dev); + + /* If user length is less than feature length */ + if ((rslt == BMI2_OK) && (dev->read_write_len < BMI2_FEAT_SIZE_IN_BYTES)) + { + /* Read-write should be even */ + if ((dev->read_write_len % 2) != 0) + { + dev->read_write_len--; + } + + while (bytes_remain > 0) + { + if (bytes_remain >= dev->read_write_len) + { + /* Read from the page */ + rslt = bmi2_get_regs(addr, &feat_config[index], dev->read_write_len, dev); + + /* Update index */ + index += (uint8_t) dev->read_write_len; + + /* Update address */ + addr += (uint8_t) dev->read_write_len; + + /* Update read-write length */ + read_write_len += (uint8_t) dev->read_write_len; + } + else + { + /* Read from the page */ + rslt = bmi2_get_regs(addr, (uint8_t *) (feat_config + index), (uint16_t) bytes_remain, dev); + + /* Update read-write length */ + read_write_len += bytes_remain; + } + + /* Remaining bytes */ + bytes_remain = BMI2_FEAT_SIZE_IN_BYTES - read_write_len; + + if (rslt != BMI2_OK) + { + break; + } + } + } + else if (rslt == BMI2_OK) + { + /* Get configuration from the page */ + rslt = bmi2_get_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + } + } + else + { + rslt = BMI2_E_INVALID_PAGE; + } + } + + return rslt; +} + +/*! + * @brief This API is used to extract the input feature configuration + * details from the look-up table. + */ +uint8_t bmi2_extract_input_feat_config(struct bmi2_feature_config *feat_config, uint8_t type, + const struct bmi2_dev *dev) +{ + /* Variable to define loop */ + uint8_t loop = 0; + + /* Variable to set flag */ + uint8_t feat_found = BMI2_FALSE; + + /* Search for the input feature from the input configuration array */ + while (loop < dev->input_sens) + { + if (dev->feat_config[loop].type == type) + { + *feat_config = dev->feat_config[loop]; + feat_found = BMI2_TRUE; + break; + } + + loop++; + } + + /* Return flag */ + return feat_found; +} + +/***************************************************************************/ + +/*! Local Function Definitions + ****************************************************************************/ + +/*! @cond DOXYGEN_SUPRESS */ + +/* Suppressing doxygen warnings triggered for same static function names present across various sensor variant + * directories */ + +/*! + * @brief This internal API writes the configuration file. + */ +static int8_t write_config_file(struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to update the configuration file index */ + uint16_t index = 0; + + /* config file size */ + uint16_t config_size = dev->config_size; + + /* Variable to get the remainder */ + uint8_t remain = (uint8_t)(config_size % dev->read_write_len); + + /* Variable to get the balance bytes */ + uint16_t bal_byte = 0; + + /* Variable to define temporary read/write length */ + uint16_t read_write_len = 0; + + /* Disable advanced power save mode */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + if (rslt == BMI2_OK) + { + /* Disable loading of the configuration */ + rslt = set_config_load(BMI2_DISABLE, dev); + if (rslt == BMI2_OK) + { + if (!remain) + { + /* Write the configuration file */ + for (index = 0; (index < config_size) && (rslt == BMI2_OK); index += dev->read_write_len) + { + rslt = upload_file((dev->config_file_ptr + index), index, dev->read_write_len, dev); + } + } + else + { + /* Get the balance bytes */ + bal_byte = (uint16_t) config_size - (uint16_t) remain; + + /* Write the configuration file for the balancem bytes */ + for (index = 0; (index < bal_byte) && (rslt == BMI2_OK); index += dev->read_write_len) + { + rslt = upload_file((dev->config_file_ptr + index), index, dev->read_write_len, dev); + } + + if (rslt == BMI2_OK) + { + /* Update length in a temporary variable */ + read_write_len = dev->read_write_len; + + /* Write the remaining bytes in 2 bytes length */ + dev->read_write_len = 2; + + /* Write the configuration file for the remaining bytes */ + for (index = bal_byte; + (index < config_size) && (rslt == BMI2_OK); + index += dev->read_write_len) + { + rslt = upload_file((dev->config_file_ptr + index), index, dev->read_write_len, dev); + } + + /* Restore the user set length back from the temporary variable */ + dev->read_write_len = read_write_len; + } + } + + if (rslt == BMI2_OK) + { + /* Enable loading of the configuration */ + rslt = set_config_load(BMI2_ENABLE, dev); + + if (rslt == BMI2_OK) + { + /* Enable advanced power save mode */ + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This internal API enables/disables the loading of the configuration + * file. + */ +static int8_t set_config_load(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data = 0; + + rslt = bmi2_get_regs(BMI2_INIT_CTRL_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + reg_data = BMI2_SET_BIT_POS0(reg_data, BMI2_CONF_LOAD_EN, enable); + rslt = bmi2_set_regs(BMI2_INIT_CTRL_ADDR, ®_data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This internal API loads the configuration file. + */ +static int8_t upload_file(const uint8_t *config_data, uint16_t index, uint16_t write_len, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to store address */ + uint8_t addr_array[2] = { 0 }; + + if (config_data != NULL) + { + /* Store 0 to 3 bits of address in first byte */ + addr_array[0] = (uint8_t)((index / 2) & 0x0F); + + /* Store 4 to 11 bits of address in the second byte */ + addr_array[1] = (uint8_t)((index / 2) >> 4); + + /* Write the 2 bytes of address in consecutive locations */ + rslt = bmi2_set_regs(BMI2_INIT_ADDR_0, addr_array, 2, dev); + if (rslt == BMI2_OK) + { + /* Burst write configuration file data corresponding to user set length */ + rslt = bmi2_set_regs(BMI2_INIT_DATA_ADDR, (uint8_t *)config_data, write_len, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This internal API validates bandwidth and performance mode of the + * accelerometer set by the user. + */ +static int8_t validate_bw_perf_mode(uint8_t *bandwidth, uint8_t *perf_mode, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Validate and auto-correct performance mode */ + rslt = check_boundary_val(perf_mode, BMI2_POWER_OPT_MODE, BMI2_PERF_OPT_MODE, dev); + if (rslt == BMI2_OK) + { + /* Validate and auto-correct bandwidth parameter */ + if (*perf_mode == BMI2_PERF_OPT_MODE) + { + /* Validate for continuous filter mode */ + rslt = check_boundary_val(bandwidth, BMI2_ACC_OSR4_AVG1, BMI2_ACC_CIC_AVG8, dev); + } + else + { + /* Validate for CIC averaging mode */ + rslt = check_boundary_val(bandwidth, BMI2_ACC_OSR4_AVG1, BMI2_ACC_RES_AVG128, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API validates ODR and range of the accelerometer set by + * the user. + */ +static int8_t validate_odr_range(uint8_t *odr, uint8_t *range, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Validate and auto correct ODR */ + rslt = check_boundary_val(odr, BMI2_ACC_ODR_0_78HZ, BMI2_ACC_ODR_1600HZ, dev); + if (rslt == BMI2_OK) + { + /* Validate and auto correct Range */ + rslt = check_boundary_val(range, BMI2_ACC_RANGE_2G, BMI2_ACC_RANGE_16G, dev); + } + + return rslt; +} + +/*! + * @brief This internal API validates bandwidth, performance mode, low power/ + * high performance mode, ODR, and range set by the user. + */ +static int8_t validate_gyro_config(struct bmi2_gyro_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Validate and auto-correct performance mode */ + rslt = check_boundary_val(&config->filter_perf, BMI2_POWER_OPT_MODE, BMI2_PERF_OPT_MODE, dev); + if (rslt == BMI2_OK) + { + /* Validate and auto-correct bandwidth parameter */ + rslt = check_boundary_val(&config->bwp, BMI2_GYR_OSR4_MODE, BMI2_GYR_NORMAL_MODE, dev); + if (rslt == BMI2_OK) + { + /* Validate and auto-correct low power/high-performance parameter */ + rslt = check_boundary_val(&config->noise_perf, BMI2_POWER_OPT_MODE, BMI2_PERF_OPT_MODE, dev); + if (rslt == BMI2_OK) + { + /* Validate and auto-correct ODR parameter */ + rslt = check_boundary_val(&config->odr, BMI2_GYR_ODR_25HZ, BMI2_GYR_ODR_3200HZ, dev); + if (rslt == BMI2_OK) + { + /* Validate and auto-correct OIS range */ + rslt = check_boundary_val(&config->ois_range, BMI2_GYR_OIS_250, BMI2_GYR_OIS_2000, dev); + if (rslt == BMI2_OK) + { + /* Validate and auto-correct range parameter */ + rslt = check_boundary_val(&config->range, BMI2_GYR_RANGE_2000, BMI2_GYR_RANGE_125, dev); + } + } + } + } + } + + return rslt; +} + +/*! + * @brief This internal API shows the error status when illegal sensor + * configuration is set. + */ +static int8_t cfg_error_status(struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data; + + /* Get error status of the set sensor configuration */ + rslt = bmi2_get_regs(BMI2_EVENT_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + reg_data = BMI2_GET_BITS(reg_data, BMI2_EVENT_FLAG); + switch (reg_data) + { + case BMI2_NO_ERROR: + rslt = BMI2_OK; + break; + case BMI2_ACC_ERROR: + rslt = BMI2_E_ACC_INVALID_CFG; + break; + case BMI2_GYR_ERROR: + rslt = BMI2_E_GYRO_INVALID_CFG; + break; + case BMI2_ACC_GYR_ERROR: + rslt = BMI2_E_ACC_GYR_INVALID_CFG; + break; + default: + break; + } + } + + return rslt; +} + +/*! + * @brief This internal API: + * 1) Enables/Disables auxiliary interface. + * 2) Sets auxiliary interface configurations like I2C address, manual/auto + * mode enable, manual burst read length, AUX burst read length and AUX read + * address. + * 3)It maps/un-maps data interrupts to that of hardware interrupt line. + */ +static int8_t set_aux_config(struct bmi2_aux_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Validate auxiliary configurations */ + rslt = validate_aux_config(config, dev); + if (rslt == BMI2_OK) + { + /* Enable/Disable auxiliary interface */ + rslt = set_aux_interface(config, dev); + if (rslt == BMI2_OK) + { + /* Set the auxiliary interface configurations */ + rslt = config_aux_interface(config, dev); + if (rslt == BMI2_OK) + { + /* Set read out offset and ODR */ + rslt = config_aux(config, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This internal API sets gyroscope user-gain configurations like gain + * update value for x, y and z-axis. + */ +static int8_t set_gyro_user_gain_config(const struct bmi2_gyro_user_gain_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to define index */ + uint8_t index = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for user-gain */ + struct bmi2_feature_config user_gain_config = { 0, 0, 0 }; + + /* Copy the feature configuration address to a local pointer */ + uint16_t *data_p = (uint16_t *) (void *)feat_config; + + /* Search for user-gain feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&user_gain_config, BMI2_GYRO_GAIN_UPDATE, dev); + if (feat_found) + { + /* Get the configuration from the page where user-gain feature resides */ + rslt = bmi2_get_feat_config(user_gain_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for user-gain select */ + idx = user_gain_config.start_addr; + + /* Get offset in words since all the features are set in words length */ + idx = idx / 2; + + /* Set ratio_x */ + *(data_p + idx) = BMI2_SET_BIT_POS0(*(data_p + idx), BMI2_GYR_USER_GAIN_RATIO_X, config->ratio_x); + + /* Increment offset by 1 word to set ratio_y */ + idx++; + + /* Set ratio_y */ + *(data_p + idx) = BMI2_SET_BIT_POS0(*(data_p + idx), BMI2_GYR_USER_GAIN_RATIO_Y, config->ratio_y); + + /* Increment offset by 1 word to set ratio_z */ + idx++; + + /* Set ratio_z */ + *(data_p + idx) = BMI2_SET_BIT_POS0(*(data_p + idx), BMI2_GYR_USER_GAIN_RATIO_Z, config->ratio_z); + + /* Increment offset by 1 more word to get the total length in words */ + idx++; + + /* Get total length in bytes to copy from local pointer to the array */ + idx = (uint8_t)(idx * 2) - user_gain_config.start_addr; + + /* Copy the bytes to be set back to the array */ + for (index = 0; index < idx; index++) + { + feat_config[user_gain_config.start_addr + + index] = *((uint8_t *) data_p + user_gain_config.start_addr + index); + } + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API enables/disables auxiliary interface. + */ +static int8_t set_aux_interface(const struct bmi2_aux_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data; + + rslt = bmi2_get_regs(BMI2_IF_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + reg_data = BMI2_SET_BITS(reg_data, BMI2_AUX_IF_EN, config->aux_en); + + /* Enable/Disable auxiliary interface */ + rslt = bmi2_set_regs(BMI2_IF_CONF_ADDR, ®_data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This internal API sets auxiliary configurations like manual/auto mode + * FCU write command enable and read burst length for both data and manual mode. + * + * @note Auxiliary sensor should not be busy when configuring aux_i2c_addr, + * man_rd_burst_len, aux_rd_burst_len and aux_rd_addr. + */ +static int8_t config_aux_interface(const struct bmi2_aux_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data[2] = { 0 }; + + /* Variable to store status */ + uint8_t status = 0; + + /* Variable to define count */ + uint8_t count = 0; + + rslt = bmi2_get_regs(BMI2_AUX_DEV_ID_ADDR, reg_data, 2, dev); + if (rslt == BMI2_OK) + { + /* Set I2C address for AUX sensor */ + reg_data[0] = BMI2_SET_BITS(reg_data[0], BMI2_AUX_SET_I2C_ADDR, config->i2c_device_addr); + + /* Set the AUX IF to either manual or auto mode */ + reg_data[1] = BMI2_SET_BITS(reg_data[1], BMI2_AUX_MAN_MODE_EN, config->manual_en); + + /* Enables FCU write command on AUX IF for auxiliary sensors that need a trigger */ + reg_data[1] = BMI2_SET_BITS(reg_data[1], BMI2_AUX_FCU_WR_EN, config->fcu_write_en); + + /* Set the burst read length for manual mode */ + reg_data[1] = BMI2_SET_BITS(reg_data[1], BMI2_AUX_MAN_READ_BURST, config->man_rd_burst); + + /* Set the burst read length for data mode */ + reg_data[1] = BMI2_SET_BIT_POS0(reg_data[1], BMI2_AUX_READ_BURST, config->aux_rd_burst); + for (;;) + { + /* Check if auxiliary sensor is busy */ + rslt = bmi2_get_status(&status, dev); + if ((rslt == BMI2_OK) && (!(status & BMI2_AUX_BUSY))) + { + /* Set the configurations if AUX is not busy */ + rslt = bmi2_set_regs(BMI2_AUX_DEV_ID_ADDR, reg_data, 2, dev); + dev->delay_us(1000, dev->intf_ptr); + if (rslt == BMI2_OK) + { + /* If data mode */ + if (!config->manual_en) + { + /* Disable manual enable flag in device structure */ + dev->aux_man_en = 0; + + /* Set the read address of the AUX sensor */ + rslt = bmi2_set_regs(BMI2_AUX_RD_ADDR, (uint8_t *) &config->read_addr, 1, dev); + dev->delay_us(1000, dev->intf_ptr); + } + else + { + /* Enable manual enable flag in device structure */ + dev->aux_man_en = 1; + + /* Update manual read burst length in device structure */ + dev->aux_man_rd_burst_len = config->man_rd_burst; + } + } + + /* Break after setting the register */ + break; + } + + /* Increment count after every 10 seconds */ + dev->delay_us(10000, dev->intf_ptr); + count++; + + /* Break after 2 seconds if AUX still busy - since slowest ODR is 0.78Hz*/ + if (count > 20) + { + rslt = BMI2_E_AUX_BUSY; + break; + } + } + } + + return rslt; +} + +/*! + * @brief This internal API triggers read out offset and sets ODR of the + * auxiliary sensor. + */ +static int8_t config_aux(const struct bmi2_aux_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data; + + rslt = bmi2_get_regs(BMI2_AUX_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + /* Trigger read out offset */ + reg_data = BMI2_SET_BITS(reg_data, BMI2_AUX_OFFSET_READ_OUT, config->offset); + + /* Set ODR */ + reg_data = BMI2_SET_BIT_POS0(reg_data, BMI2_AUX_ODR_EN, config->odr); + + /* Set auxiliary configuration register */ + rslt = bmi2_set_regs(BMI2_AUX_CONF_ADDR, ®_data, 1, dev); + dev->delay_us(1000, dev->intf_ptr); + } + + return rslt; +} + +/*! + * @brief This internal API checks the busy status of auxiliary sensor and sets + * the auxiliary register addresses when not busy. + */ +static int8_t set_if_aux_not_busy(uint8_t reg_addr, uint8_t reg_data, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to get status of AUX_BUSY */ + uint8_t status = 0; + + /* Variable to define count for time-out */ + uint8_t count = 0; + + for (;;) + { + /* Check if AUX is busy */ + rslt = bmi2_get_status(&status, dev); + + /* Set the registers if not busy */ + if ((rslt == BMI2_OK) && (!(status & BMI2_AUX_BUSY))) + { + rslt = bmi2_set_regs(reg_addr, ®_data, 1, dev); + dev->delay_us(1000, dev->intf_ptr); + + /* Break after setting the register */ + break; + } + + /* Increment count after every 10 seconds */ + dev->delay_us(10000, dev->intf_ptr); + count++; + + /* Break after 2 seconds if AUX still busy - since slowest ODR is 0.78Hz*/ + if (count > 20) + { + rslt = BMI2_E_AUX_BUSY; + break; + } + } + + return rslt; +} + +/*! + * @brief This internal API validates auxiliary configuration set by the user. + */ +static int8_t validate_aux_config(struct bmi2_aux_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Validate ODR for auxiliary sensor */ + rslt = check_boundary_val(&config->odr, BMI2_AUX_ODR_0_78HZ, BMI2_AUX_ODR_800HZ, dev); + + return rslt; +} + +/*! + * @brief This internal API gets accelerometer configurations like ODR, + * bandwidth, performance mode and g-range. + */ +static int8_t get_accel_config(struct bmi2_accel_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to store data */ + uint8_t data_array[2] = { 0 }; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (config != NULL)) + { + /* Read the sensor configuration details */ + rslt = bmi2_get_regs(BMI2_ACC_CONF_ADDR, data_array, 2, dev); + if (rslt == BMI2_OK) + { + /* Get accelerometer performance mode */ + config->filter_perf = BMI2_GET_BITS(data_array[0], BMI2_ACC_FILTER_PERF_MODE); + + /* Get accelerometer bandwidth */ + config->bwp = BMI2_GET_BITS(data_array[0], BMI2_ACC_BW_PARAM); + + /* Get accelerometer ODR */ + config->odr = BMI2_GET_BIT_POS0(data_array[0], BMI2_ACC_ODR); + + /* Get accelerometer range */ + config->range = BMI2_GET_BIT_POS0(data_array[1], BMI2_ACC_RANGE); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This internal API gets gyroscope configurations like ODR, bandwidth, + * low power/high performance mode, performance mode and range. + */ +static int8_t get_gyro_config(struct bmi2_gyro_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to store data */ + uint8_t data_array[2] = { 0 }; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (config != NULL)) + { + /* Read the sensor configuration details */ + rslt = bmi2_get_regs(BMI2_GYR_CONF_ADDR, data_array, 2, dev); + if (rslt == BMI2_OK) + { + /* Get gyroscope performance mode */ + config->filter_perf = BMI2_GET_BITS(data_array[0], BMI2_GYR_FILTER_PERF_MODE); + + /* Get gyroscope noise performance mode */ + config->noise_perf = BMI2_GET_BITS(data_array[0], BMI2_GYR_NOISE_PERF_MODE); + + /* Get gyroscope bandwidth */ + config->bwp = BMI2_GET_BITS(data_array[0], BMI2_GYR_BW_PARAM); + + /* Get gyroscope ODR */ + config->odr = BMI2_GET_BIT_POS0(data_array[0], BMI2_GYR_ODR); + + /* Get gyroscope OIS range */ + config->ois_range = BMI2_GET_BITS(data_array[1], BMI2_GYR_OIS_RANGE); + + /* Get gyroscope range */ + config->range = BMI2_GET_BIT_POS0(data_array[1], BMI2_GYR_RANGE); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This internal API: + * 1) Gets the status of auxiliary interface enable. + * 2) Gets auxiliary interface configurations like I2C address, manual/auto + * mode enable, manual burst read length, AUX burst read length and AUX read + * address. + * 3) Gets ODR and offset. + */ +static int8_t get_aux_config(struct bmi2_aux_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (config != NULL)) + { + /* Get enable status of auxiliary interface */ + rslt = get_aux_interface(config, dev); + if (rslt == BMI2_OK) + { + /* Get the auxiliary interface configurations */ + rslt = get_aux_interface_config(config, dev); + if (rslt == BMI2_OK) + { + /* Get read out offset and ODR */ + rslt = get_aux_cfg(config, dev); + } + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This internal API gets gyroscope user-gain configurations like gain + * update value for x, y and z-axis. + */ +static int8_t get_gyro_gain_update_config(struct bmi2_gyro_user_gain_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to define LSB */ + uint16_t lsb = 0; + + /* Variable to define MSB */ + uint16_t msb = 0; + + /* Variable to define a word */ + uint16_t lsb_msb = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for user-gain */ + struct bmi2_feature_config user_gain_config = { 0, 0, 0 }; + + /* Search for user-gain feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&user_gain_config, BMI2_GYRO_GAIN_UPDATE, dev); + if (feat_found) + { + /* Get the configuration from the page where user-gain feature resides */ + rslt = bmi2_get_feat_config(user_gain_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for user-gain select */ + idx = user_gain_config.start_addr; + + /* Get word to calculate ratio_x */ + lsb = (uint16_t) feat_config[idx++]; + msb = ((uint16_t) feat_config[idx++] << 8); + lsb_msb = lsb | msb; + + /* Get ratio_x */ + config->ratio_x = lsb_msb & BMI2_GYR_USER_GAIN_RATIO_X_MASK; + + /* Get word to calculate ratio_y */ + lsb = (uint16_t) feat_config[idx++]; + msb = ((uint16_t) feat_config[idx++] << 8); + lsb_msb = lsb | msb; + + /* Get ratio_y */ + config->ratio_y = lsb_msb & BMI2_GYR_USER_GAIN_RATIO_Y_MASK; + + /* Get word to calculate ratio_z */ + lsb = (uint16_t) feat_config[idx++]; + msb = ((uint16_t) feat_config[idx++] << 8); + lsb_msb = lsb | msb; + + /* Get ratio_z */ + config->ratio_z = lsb_msb & BMI2_GYR_USER_GAIN_RATIO_Z_MASK; + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API gets the enable status of auxiliary interface. + */ +static int8_t get_aux_interface(struct bmi2_aux_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data; + + /* Get the enable status of auxiliary interface */ + rslt = bmi2_get_regs(BMI2_IF_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + config->aux_en = BMI2_GET_BITS(reg_data, BMI2_AUX_IF_EN); + } + + return rslt; +} + +/*! + * @brief This internal API gets auxiliary configurations like manual/auto mode + * FCU write command enable and read burst length for both data and manual mode. + */ +static int8_t get_aux_interface_config(struct bmi2_aux_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data[2] = { 0 }; + + rslt = bmi2_get_regs(BMI2_AUX_DEV_ID_ADDR, reg_data, 2, dev); + if (rslt == BMI2_OK) + { + /* Get I2C address for auxiliary sensor */ + config->i2c_device_addr = BMI2_GET_BITS(reg_data[0], BMI2_AUX_SET_I2C_ADDR); + + /* Get the AUX IF to either manual or auto mode */ + config->manual_en = BMI2_GET_BITS(reg_data[1], BMI2_AUX_MAN_MODE_EN); + + /* Enables FCU write command on AUX IF for auxiliary sensors that need a trigger */ + config->fcu_write_en = BMI2_GET_BITS(reg_data[1], BMI2_AUX_FCU_WR_EN); + + /* Get the burst read length for manual mode */ + config->man_rd_burst = BMI2_GET_BITS(reg_data[1], BMI2_AUX_MAN_READ_BURST); + + /* Get the burst read length for data mode */ + config->aux_rd_burst = BMI2_GET_BIT_POS0(reg_data[1], BMI2_AUX_READ_BURST); + + /* If data mode, get the read address of the auxiliary sensor from where data is to be read */ + if (!config->manual_en) + { + rslt = bmi2_get_regs(BMI2_AUX_RD_ADDR, &config->read_addr, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API gets read out offset and ODR of the auxiliary + * sensor. + */ +static int8_t get_aux_cfg(struct bmi2_aux_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data; + + rslt = bmi2_get_regs(BMI2_AUX_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + /* Get read out offset */ + config->offset = BMI2_GET_BITS(reg_data, BMI2_AUX_OFFSET_READ_OUT); + + /* Get ODR */ + config->odr = BMI2_GET_BIT_POS0(reg_data, BMI2_AUX_ODR_EN); + } + + return rslt; +} + +/*! + * @brief This internal API maps/un-maps feature interrupts to that of interrupt + * pins. + */ +static int8_t map_feat_int(uint8_t *reg_data_array, enum bmi2_hw_int_pin int_pin, uint8_t int_mask) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Check for NULL error */ + if (reg_data_array != NULL) + { + /* Check validity on interrupt pin selection */ + if (int_pin < BMI2_INT_PIN_MAX) + { + switch (int_pin) + { + case BMI2_INT_NONE: + + /* Un-Map the corresponding feature interrupt to interrupt pin 1 and 2 */ + reg_data_array[0] &= ~(int_mask); + reg_data_array[1] &= ~(int_mask); + break; + case BMI2_INT1: + + /* Map the corresponding feature interrupt to interrupt pin 1 */ + reg_data_array[0] |= int_mask; + + /* Un-map the corresponding feature interrupt to interrupt pin 2 */ + reg_data_array[1] &= ~(int_mask); + break; + case BMI2_INT2: + + /* Map the corresponding feature interrupt to interrupt pin 2 */ + reg_data_array[1] |= int_mask; + + /* Un-map the corresponding feature interrupt to interrupt pin 1 */ + reg_data_array[0] &= ~(int_mask); + break; + case BMI2_INT_BOTH: + + /* Map the corresponding feature interrupt to interrupt pin 1 and 2 */ + reg_data_array[0] |= int_mask; + reg_data_array[1] |= int_mask; + break; + default: + break; + } + } + else + { + /* Return error if invalid pin selection */ + rslt = BMI2_E_INVALID_INT_PIN; + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This internal API gets the accelerometer/gyroscope data. + */ +static void get_acc_gyr_data(struct bmi2_sens_axes_data *data, const uint8_t *reg_data) +{ + /* Variables to store msb value */ + uint8_t msb; + + /* Variables to store lsb value */ + uint8_t lsb; + + /* Variables to store both msb and lsb value */ + uint16_t msb_lsb; + + /* Variables to define index */ + uint8_t index = 0; + + /* Read x-axis data */ + lsb = reg_data[index++]; + msb = reg_data[index++]; + msb_lsb = ((uint16_t) msb << 8) | (uint16_t) lsb; + data->x = (int16_t) msb_lsb; + + /* Read y-axis data */ + lsb = reg_data[index++]; + msb = reg_data[index++]; + msb_lsb = ((uint16_t) msb << 8) | (uint16_t) lsb; + data->y = (int16_t) msb_lsb; + + /* Read z-axis data */ + lsb = reg_data[index++]; + msb = reg_data[index++]; + msb_lsb = ((uint16_t) msb << 8) | (uint16_t) lsb; + data->z = (int16_t) msb_lsb; +} + +/*! + * @brief This internal API gets the re-mapped accelerometer/gyroscope data. + */ +static void get_remapped_data(struct bmi2_sens_axes_data *data, const struct bmi2_dev *dev) +{ + /* Array to defined the re-mapped sensor data */ + int16_t remap_data[3] = { 0 }; + int16_t pos_multiplier = INT16_C(1); + int16_t neg_multiplier = INT16_C(-1); + + /* Fill the array with the un-mapped sensor data */ + remap_data[0] = data->x; + remap_data[1] = data->y; + remap_data[2] = data->z; + + /* Get the re-mapped x axis data */ + if (dev->remap.x_axis_sign == BMI2_POS_SIGN) + { + data->x = (int16_t)(remap_data[dev->remap.x_axis] * pos_multiplier); + } + else + { + data->x = (int16_t)(remap_data[dev->remap.x_axis] * neg_multiplier); + } + + /* Get the re-mapped y axis data */ + if (dev->remap.y_axis_sign == BMI2_POS_SIGN) + { + data->y = (int16_t)(remap_data[dev->remap.y_axis] * pos_multiplier); + } + else + { + data->y = (int16_t)(remap_data[dev->remap.y_axis] * neg_multiplier); + } + + /* Get the re-mapped z axis data */ + if (dev->remap.z_axis_sign == BMI2_POS_SIGN) + { + data->z = (int16_t)(remap_data[dev->remap.z_axis] * pos_multiplier); + } + else + { + data->z = (int16_t)(remap_data[dev->remap.z_axis] * neg_multiplier); + } +} + +/*! + * @brief This internal API reads the user-defined bytes of data from the given + * register address of auxiliary sensor in manual mode. + */ +static int8_t read_aux_data(uint8_t reg_addr, uint8_t *aux_data, uint16_t len, uint8_t burst_len, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Array to store the register data */ + uint8_t reg_data[15] = { 0 }; + + /* Variable to define number of bytes to read */ + uint16_t read_length = 0; + + /* Variable to define loop */ + uint8_t loop = 0; + + /* Variable to define counts to get the correct array index */ + uint8_t count = 0; + + /* Variable to define index for the array */ + uint8_t idx = 0; + + while (len > 0) + { + /* Set the read address if AUX is not busy */ + rslt = set_if_aux_not_busy(BMI2_AUX_RD_ADDR, reg_addr, dev); + if (rslt == BMI2_OK) + { + /* Read data from bmi2 data register */ + rslt = bmi2_get_regs(BMI2_AUX_X_LSB_ADDR, reg_data, (uint16_t) burst_len, dev); + dev->delay_us(1000, dev->intf_ptr); + if (rslt == BMI2_OK) + { + /* Get number of bytes to be read */ + if (len < burst_len) + { + read_length = (uint8_t) len; + } + else + { + read_length = burst_len; + } + + /* Update array index and store the data */ + for (loop = 0; loop < read_length; loop++) + { + idx = loop + count; + aux_data[idx] = reg_data[loop]; + } + } + } + + /* Update address for the next read */ + reg_addr += burst_len; + + /* Update count for the array index */ + count += burst_len; + + /* Update no of bytes left to read */ + len -= read_length; + } + + return rslt; +} + +/*! + * @brief This internal API writes AUX write address and the user-defined bytes + * of data to the AUX sensor in manual mode. + * + * @note Change of BMI2_AUX_WR_ADDR is only allowed if AUX is not busy. + */ +static int8_t write_aux_data(uint8_t reg_addr, uint8_t reg_data, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Write data to be written to the AUX sensor in bmi2 register */ + rslt = bmi2_set_regs(BMI2_AUX_WR_DATA_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + /* Write the AUX address where data is to be stored when AUX is not busy */ + rslt = set_if_aux_not_busy(BMI2_AUX_WR_ADDR, reg_addr, dev); + } + + return rslt; +} + +/*! + * @brief This internal API maps the actual burst read length with that of the + * register value set by user. + */ +static int8_t map_read_len(uint8_t *len, const struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Get the burst read length against the values set by the user */ + switch (dev->aux_man_rd_burst_len) + { + case BMI2_AUX_READ_LEN_0: + *len = 1; + break; + case BMI2_AUX_READ_LEN_1: + *len = 2; + break; + case BMI2_AUX_READ_LEN_2: + *len = 6; + break; + case BMI2_AUX_READ_LEN_3: + *len = 8; + break; + default: + rslt = BMI2_E_AUX_INVALID_CFG; + break; + } + + return rslt; +} + +/*! + * @brief This internal API computes the number of bytes of accelerometer FIFO + * data which is to be parsed in header-less mode. + */ +static int8_t parse_fifo_accel_len(uint16_t *start_idx, + uint16_t *len, + uint8_t *skip_length, + const uint16_t *acc_count, + const struct bmi2_fifo_frame *fifo) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Data start index */ + (*start_idx) = fifo->acc_byte_start_idx; + + /* If only accelerometer is enabled */ + if (fifo->data_enable == BMI2_FIFO_ACC_EN) + { + /* Number of bytes to be read */ + (*len) = (uint16_t)((*acc_count) * BMI2_FIFO_ACC_LENGTH); + + /* Number of bytes to skip in case dummy frame is obtained */ + (*skip_length) = BMI2_FIFO_ACC_LENGTH; + } + /* If only accelerometer and auxiliary are enabled */ + else if (fifo->data_enable == (BMI2_FIFO_ACC_EN | BMI2_FIFO_AUX_EN)) + { + /* Number of bytes to be read */ + (*len) = (uint16_t)((*acc_count) * (BMI2_FIFO_ACC_LENGTH + fifo->aux_frm_len)); + + /* Number of bytes to skip in case dummy frame is obtained */ + (*skip_length) = (BMI2_FIFO_ACC_LENGTH + fifo->aux_frm_len); + + /* Data start index */ + (*start_idx) = fifo->acc_byte_start_idx + fifo->aux_frm_len; + } + /* If only accelerometer and gyroscope are enabled */ + else if (fifo->data_enable == (BMI2_FIFO_ACC_EN | BMI2_FIFO_GYR_EN)) + { + /* Number of bytes to be read */ + (*len) = (uint16_t)((*acc_count) * BMI2_FIFO_ACC_GYR_LENGTH); + + /* Number of bytes to skip in case dummy frame is obtained */ + (*skip_length) = BMI2_FIFO_ACC_GYR_LENGTH; + + /* Data start index */ + (*start_idx) = fifo->acc_byte_start_idx + BMI2_FIFO_GYR_LENGTH; + } + /* If only accelerometer, gyroscope and auxiliary are enabled */ + else if (fifo->data_enable == (BMI2_FIFO_ACC_EN | BMI2_FIFO_GYR_EN | BMI2_FIFO_AUX_EN)) + { + /* Number of bytes to be read */ + (*len) = (uint16_t)((*acc_count) * (BMI2_FIFO_ACC_LENGTH + BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len)); + + /* Number of bytes to skip in case dummy frame is obtained */ + (*skip_length) = (BMI2_FIFO_ACC_LENGTH + BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + + /* Data start index */ + (*start_idx) = fifo->acc_byte_start_idx + (BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + } + else + { + /* Move the data index to the last byte to mark completion when + * no sensors or sensors apart from accelerometer are enabled + */ + (*start_idx) = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + } + + /* If more data is requested than available */ + if ((*len) > fifo->length) + { + (*len) = fifo->length; + } + + return rslt; +} + +/*! + * @brief This internal API is used to skip dummy frames in FIFO headerless mode + */ +static int8_t check_dummy_frame(uint8_t dummy_frame_header, + uint16_t *data_index, + uint8_t skip_length, + const struct bmi2_fifo_frame *fifo) +{ + int8_t rslt; + + /* Validate data index */ + if (((*data_index) + 6) <= fifo->length) + { + /* Check if FIFO contains dummy frame */ + if (((fifo->data[(*data_index)] == dummy_frame_header) && + (fifo->data[(*data_index) + 1] == BMI2_FIFO_HEADERLESS_DUMMY_BYTE_1) && + (fifo->data[(*data_index) + 2] == BMI2_FIFO_HEADERLESS_DUMMY_BYTE_2)) && + ((fifo->data[(*data_index) + 3] == BMI2_FIFO_HEADERLESS_DUMMY_BYTE_3))) + { + /* Move the data index to next frame */ + (*data_index) = (*data_index) + skip_length; + + /* Dummy byte parsed */ + rslt = BMI2_W_DUMMY_BYTE; + } + else + { + /* Valid frame */ + rslt = BMI2_OK; + } + } + else + { + /* Move the data index to the last byte to mark completion */ + (*data_index) = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + } + + return rslt; + +} + +/*! + * @brief This internal API is used to parse the accelerometer data from the + * FIFO in headerless mode. + */ +static int8_t extract_accel_headerless_mode(struct bmi2_sens_axes_data *acc, + uint16_t *accel_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + int8_t rslt; + + /* Variable to index the bytes */ + uint16_t data_index = 0; + + /* Variable to define the data enable byte */ + uint8_t data_enable; + + /* Variable to index accelerometer frames */ + uint16_t accel_index = 0; + + /* Variable to store the number of bytes to be read */ + uint16_t data_read_length = 0; + + /* Number of bytes to skip in case dummy frame is obtained */ + uint8_t skip_length = 0; + + /* Get the number of accelerometer bytes to be read */ + rslt = parse_fifo_accel_len(&data_index, &data_read_length, &skip_length, accel_length, fifo); + + /* Convert word to byte since all sensor enables are in a byte */ + data_enable = (uint8_t)(fifo->data_enable >> 8); + + for (; (data_index < data_read_length) && (rslt != BMI2_W_FIFO_EMPTY);) + { + rslt = check_dummy_frame(BMI2_FIFO_HEADERLESS_DUMMY_ACC, &data_index, skip_length, fifo); + + /* Unpack only if Valid frame is present */ + if (rslt == BMI2_OK) + { + /* Unpack frame to get the accelerometer data */ + rslt = unpack_accel_headerless_frame(acc, &data_index, &accel_index, data_enable, fifo, dev); + + if (rslt != BMI2_W_FIFO_EMPTY) + { + /* Check for the availability of next two bytes of FIFO data */ + rslt = check_empty_fifo(&data_index, fifo); + } + } + } + + /* Update number of accelerometer frames to be read */ + (*accel_length) = accel_index; + + /* Update the accelerometer byte index */ + fifo->acc_byte_start_idx = data_index; + + return rslt; +} + +/*! + * @brief This internal API is used to parse the accelerometer data from the + * FIFO in header mode. + */ +static int8_t extract_accel_header_mode(struct bmi2_sens_axes_data *acc, + uint16_t *accel_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Variable to define header frame */ + uint8_t frame_header = 0; + + /* Variable to index the data bytes */ + uint16_t data_index; + + /* Variable to index accelerometer frames */ + uint16_t accel_index = 0; + + /* Variable to indicate accelerometer frames read */ + uint16_t frame_to_read = *accel_length; + + for (data_index = fifo->acc_byte_start_idx; data_index < fifo->length;) + { + /* Get frame header byte */ + frame_header = fifo->data[data_index] & BMI2_FIFO_TAG_INTR_MASK; + + /* Parse virtual header if S4S is enabled */ + parse_if_virtual_header(&frame_header, &data_index, fifo); + + /* Index shifted to next byte where data starts */ + data_index++; + switch (frame_header) + { + /* If header defines accelerometer frame */ + case BMI2_FIFO_HEADER_ACC_FRM: + case BMI2_FIFO_HEADER_AUX_ACC_FRM: + case BMI2_FIFO_HEADER_GYR_ACC_FRM: + case BMI2_FIFO_HEADER_ALL_FRM: + + /* Unpack from normal frames */ + rslt = unpack_accel_header_frame(acc, &data_index, &accel_index, frame_header, fifo, dev); + break; + + /* If header defines only gyroscope frame */ + case BMI2_FIFO_HEADER_GYR_FRM: + rslt = move_next_frame(&data_index, fifo->gyr_frm_len, fifo); + break; + + /* If header defines only auxiliary frame */ + case BMI2_FIFO_HEADER_AUX_FRM: + rslt = move_next_frame(&data_index, fifo->aux_frm_len, fifo); + break; + + /* If header defines only auxiliary and gyroscope frame */ + case BMI2_FIFO_HEADER_AUX_GYR_FRM: + rslt = move_next_frame(&data_index, fifo->aux_gyr_frm_len, fifo); + break; + + /* If header defines sensor time frame */ + case BMI2_FIFO_HEADER_SENS_TIME_FRM: + rslt = unpack_sensortime_frame(&data_index, fifo); + break; + + /* If header defines skip frame */ + case BMI2_FIFO_HEADER_SKIP_FRM: + rslt = unpack_skipped_frame(&data_index, fifo); + break; + + /* If header defines Input configuration frame */ + case BMI2_FIFO_HEADER_INPUT_CFG_FRM: + rslt = move_next_frame(&data_index, BMI2_FIFO_INPUT_CFG_LENGTH, fifo); + break; + + /* If header defines invalid frame or end of valid data */ + case BMI2_FIFO_HEAD_OVER_READ_MSB: + + /* Move the data index to the last byte to mark completion */ + data_index = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + break; + case BMI2_FIFO_VIRT_ACT_RECOG_FRM: + rslt = move_next_frame(&data_index, BMI2_FIFO_VIRT_ACT_DATA_LENGTH, fifo); + break; + default: + + /* Move the data index to the last byte in case of invalid values */ + data_index = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + break; + } + + /* Break if Number of frames to be read is complete or FIFO is mpty */ + if ((frame_to_read == accel_index) || (rslt == BMI2_W_FIFO_EMPTY)) + { + break; + } + } + + /* Update the accelerometer frame index */ + (*accel_length) = accel_index; + + /* Update the accelerometer byte index */ + fifo->acc_byte_start_idx = data_index; + + return rslt; +} + +/*! + * @brief This internal API is used to parse the accelerometer data from the + * FIFO data in header-less mode. It updates the current data + * byte to be parsed. + */ +static int8_t unpack_accel_headerless_frame(struct bmi2_sens_axes_data *acc, + uint16_t *idx, + uint16_t *acc_idx, + uint8_t frame, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + switch (frame) + { + /* If frame contains only accelerometer data */ + case BMI2_FIFO_HEAD_LESS_ACC_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->acc_frm_len) > fifo->length) + { + /* Update the data index as complete*/ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the accelerometer data */ + unpack_accel_data(&acc[(*acc_idx)], *idx, fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + BMI2_FIFO_ACC_LENGTH; + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&acc[(*acc_idx)], idx, fifo); + } + + /* Update accelerometer frame index */ + (*acc_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer and gyroscope data */ + case BMI2_FIFO_HEAD_LESS_GYR_ACC_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->acc_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the accelerometer data */ + unpack_accel_data(&acc[(*acc_idx)], *idx, fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + BMI2_FIFO_ACC_GYR_LENGTH; + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&acc[(*acc_idx)], idx, fifo); + } + + /* Update accelerometer frame index */ + (*acc_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer and auxiliary data */ + case BMI2_FIFO_HEAD_LESS_AUX_ACC_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->acc_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the accelerometer data */ + unpack_accel_data(&acc[(*acc_idx)], *idx, fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + (BMI2_FIFO_ACC_LENGTH + fifo->aux_frm_len); + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&acc[(*acc_idx)], idx, fifo); + } + + /* Update accelerometer frame index */ + (*acc_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer, gyroscope and auxiliary data */ + case BMI2_FIFO_HEAD_LESS_ALL_FRM: + + /* Partially read, then skip the data*/ + if ((*idx + fifo->acc_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the accelerometer data */ + unpack_accel_data(&acc[(*acc_idx)], *idx, fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + (BMI2_FIFO_ACC_LENGTH + BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&acc[(*acc_idx)], idx, fifo); + } + + /* Update accelerometer frame index */ + (*acc_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains gyroscope and auxiliary data */ + case BMI2_FIFO_HEAD_LESS_GYR_AUX_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->aux_gyr_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains only auxiliary data */ + case BMI2_FIFO_HEAD_LESS_AUX_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->aux_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains only gyroscope data */ + case BMI2_FIFO_HEAD_LESS_GYR_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->gyr_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + default: + + /* Move the data index to the last byte in case of invalid values */ + (*idx) = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + break; + } + + return rslt; +} + +/*! + * @brief This internal API is used to parse the accelerometer data from the + * FIFO data in header mode. It updates the current data + * byte to be parsed. + */ +static int8_t unpack_accel_header_frame(struct bmi2_sens_axes_data *acc, + uint16_t *idx, + uint16_t *acc_idx, + uint8_t frame, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + switch (frame) + { + /* If frame contains only accelerometer data */ + case BMI2_FIFO_HEADER_ACC_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->acc_frm_len) > fifo->length) + { + /* Update the data index as complete*/ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the accelerometer data */ + unpack_accel_data(&acc[(*acc_idx)], *idx, fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + BMI2_FIFO_ACC_LENGTH; + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&acc[(*acc_idx)], idx, fifo); + } + + /* Update accelerometer frame index */ + (*acc_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer and gyroscope data */ + case BMI2_FIFO_HEADER_GYR_ACC_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->acc_gyr_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the accelerometer data */ + unpack_accel_data(&acc[(*acc_idx)], ((*idx) + BMI2_FIFO_GYR_LENGTH), fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + BMI2_FIFO_ACC_GYR_LENGTH; + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&acc[(*acc_idx)], idx, fifo); + } + + /* Update accelerometer frame index */ + (*acc_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer and auxiliary data */ + case BMI2_FIFO_HEADER_AUX_ACC_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->acc_aux_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the accelerometer data */ + unpack_accel_data(&acc[(*acc_idx)], ((*idx) + fifo->aux_frm_len), fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + (BMI2_FIFO_ACC_LENGTH + fifo->aux_frm_len); + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&acc[(*acc_idx)], idx, fifo); + } + + /* Update accelerometer frame index */ + (*acc_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer, gyroscope and auxiliary data */ + case BMI2_FIFO_HEADER_ALL_FRM: + + /* Partially read, then skip the data*/ + if ((*idx + fifo->all_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the accelerometer data */ + unpack_accel_data(&acc[(*acc_idx)], ((*idx) + (BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len)), fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + (BMI2_FIFO_ACC_LENGTH + BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&acc[(*acc_idx)], idx, fifo); + } + + /* Update accelerometer frame index */ + (*acc_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains gyroscope and auxiliary data */ + case BMI2_FIFO_HEADER_AUX_GYR_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->aux_gyr_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains only auxiliary data */ + case BMI2_FIFO_HEADER_AUX_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->aux_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains only gyroscope data */ + case BMI2_FIFO_HEADER_GYR_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->gyr_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + default: + + /* Move the data index to the last byte in case of invalid values */ + (*idx) = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + break; + } + + return rslt; +} + +/*! + * @brief This internal API is used to parse accelerometer data from the + * FIFO data. + */ +static void unpack_accel_data(struct bmi2_sens_axes_data *acc, + uint16_t data_start_index, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + /* Variables to store LSB value */ + uint16_t data_lsb; + + /* Variables to store MSB value */ + uint16_t data_msb; + + /* Accelerometer raw x data */ + data_lsb = fifo->data[data_start_index++]; + data_msb = fifo->data[data_start_index++]; + acc->x = (int16_t)((data_msb << 8) | data_lsb); + + /* Accelerometer raw y data */ + data_lsb = fifo->data[data_start_index++]; + data_msb = fifo->data[data_start_index++]; + acc->y = (int16_t)((data_msb << 8) | data_lsb); + + /* Accelerometer raw z data */ + data_lsb = fifo->data[data_start_index++]; + data_msb = fifo->data[data_start_index++]; + acc->z = (int16_t)((data_msb << 8) | data_lsb); + + /* Get the re-mapped accelerometer data */ + get_remapped_data(acc, dev); +} + +/*! + * @brief This internal API computes the number of bytes of gyroscope FIFO data + * which is to be parsed in header-less mode. + */ +static int8_t parse_fifo_gyro_len(uint16_t *start_idx, + uint16_t *len, + uint8_t *skip_length, + const uint16_t *gyr_count, + const struct bmi2_fifo_frame *fifo) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Data start index */ + (*start_idx) = fifo->gyr_byte_start_idx; + + /* If only gyroscope is enabled */ + if (fifo->data_enable == BMI2_FIFO_GYR_EN) + { + /* Number of bytes to be read */ + (*len) = (uint16_t)((*gyr_count) * BMI2_FIFO_GYR_LENGTH); + + /* Number of bytes to skip in case dummy frame is obtained */ + (*skip_length) = BMI2_FIFO_GYR_LENGTH; + } + /* If only gyroscope and auxiliary are enabled */ + else if (fifo->data_enable == (BMI2_FIFO_GYR_EN | BMI2_FIFO_AUX_EN)) + { + /* Number of bytes to be read */ + (*len) = (uint16_t)((*gyr_count) * (BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len)); + + /* Number of bytes to skip in case dummy frame is obtained */ + (*skip_length) = (BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + + /* Data start index */ + (*start_idx) = fifo->gyr_byte_start_idx + fifo->aux_frm_len; + } + /* If only accelerometer and gyroscope are enabled */ + else if (fifo->data_enable == (BMI2_FIFO_ACC_EN | BMI2_FIFO_GYR_EN)) + { + /* Number of bytes to be read */ + (*len) = (uint16_t)((*gyr_count) * BMI2_FIFO_ACC_GYR_LENGTH); + + /* Number of bytes to skip in case dummy frame is obtained */ + (*skip_length) = BMI2_FIFO_ACC_GYR_LENGTH; + } + /* If only accelerometer, gyroscope and auxiliary are enabled */ + else if (fifo->data_enable == (BMI2_FIFO_GYR_EN | BMI2_FIFO_AUX_EN | BMI2_FIFO_ACC_EN)) + { + /* Number of bytes to be read */ + (*len) = (uint16_t)((*gyr_count) * (BMI2_FIFO_ACC_LENGTH + BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len)); + + /* Number of bytes to skip in case dummy frame is obtained */ + (*skip_length) = (BMI2_FIFO_ACC_LENGTH + BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + + /* Data start index */ + (*start_idx) = fifo->gyr_byte_start_idx + fifo->aux_frm_len; + } + else + { + /* Move the data index to the last byte to mark completion when + * no sensors or sensors apart from gyroscope are enabled + */ + (*start_idx) = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + } + + /* If more data is requested than available */ + if (((*len)) > fifo->length) + { + (*len) = fifo->length; + } + + return rslt; +} + +/*! + * @brief This internal API is used to parse the gyroscope data from the + * FIFO in headerless mode. + */ +static int8_t extract_gyro_headerless_mode(struct bmi2_sens_axes_data *gyr, + uint16_t *gyro_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + int8_t rslt; + + /* Variable to index the bytes */ + uint16_t data_index = 0; + + /* Variable to index gyroscope frames */ + uint16_t gyro_index = 0; + + /* Variable to store the number of bytes to be read */ + uint16_t data_read_length = 0; + + /* Variable to define the data enable byte */ + uint8_t data_enable; + + /* Variable to hold number of bytes to skip in case dummy frame is obtained */ + uint8_t skip_length = 0; + + /* Get the number of gyro bytes to be read */ + rslt = parse_fifo_gyro_len(&data_index, &data_read_length, &skip_length, gyro_length, fifo); + + /* Convert word to byte since all sensor enables are in a byte */ + data_enable = (uint8_t)(fifo->data_enable >> 8); + + for (; (data_index < data_read_length) && (rslt != BMI2_W_FIFO_EMPTY);) + { + rslt = check_dummy_frame(BMI2_FIFO_HEADERLESS_DUMMY_GYR, &data_index, skip_length, fifo); + + /* Unpack only if Valid frame is present */ + if (rslt == BMI2_OK) + { + /* Unpack frame to get gyroscope data */ + rslt = unpack_gyro_headerless_frame(gyr, &data_index, &gyro_index, data_enable, fifo, dev); + + if (rslt != BMI2_W_FIFO_EMPTY) + { + /* Check for the availability of next two bytes of FIFO data */ + rslt = check_empty_fifo(&data_index, fifo); + } + } + } + + /* Update number of gyroscope frames to be read */ + (*gyro_length) = gyro_index; + + /* Update the gyroscope byte index */ + fifo->gyr_byte_start_idx = data_index; + + return rslt; +} + +/*! + * @brief This internal API is used to parse the gyroscope data from the + * FIFO data in header mode. + */ +static int8_t extract_gyro_header_mode(struct bmi2_sens_axes_data *gyr, + uint16_t *gyro_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Variable to define header frame */ + uint8_t frame_header = 0; + + /* Variable to index the data bytes */ + uint16_t data_index; + + /* Variable to index gyroscope frames */ + uint16_t gyro_index = 0; + + /* Variable to indicate gyroscope frames read */ + uint16_t frame_to_read = (*gyro_length); + + for (data_index = fifo->gyr_byte_start_idx; data_index < fifo->length;) + { + /* Get frame header byte */ + frame_header = fifo->data[data_index] & BMI2_FIFO_TAG_INTR_MASK; + + /* Parse virtual header if S4S is enabled */ + parse_if_virtual_header(&frame_header, &data_index, fifo); + + /* Index shifted to next byte where data starts */ + data_index++; + switch (frame_header) + { + /* If header defines gyroscope frame */ + case BMI2_FIFO_HEADER_GYR_FRM: + case BMI2_FIFO_HEADER_GYR_ACC_FRM: + case BMI2_FIFO_HEADER_AUX_GYR_FRM: + case BMI2_FIFO_HEADER_ALL_FRM: + + /* Unpack from normal frames */ + rslt = unpack_gyro_header_frame(gyr, &data_index, &gyro_index, frame_header, fifo, dev); + break; + + /* If header defines only accelerometer frame */ + case BMI2_FIFO_HEADER_ACC_FRM: + rslt = move_next_frame(&data_index, fifo->acc_frm_len, fifo); + break; + + /* If header defines only auxiliary frame */ + case BMI2_FIFO_HEADER_AUX_FRM: + rslt = move_next_frame(&data_index, fifo->aux_frm_len, fifo); + break; + + /* If header defines only auxiliary and accelerometer frame */ + case BMI2_FIFO_HEADER_AUX_ACC_FRM: + rslt = move_next_frame(&data_index, fifo->acc_aux_frm_len, fifo); + break; + + /* If header defines sensor time frame */ + case BMI2_FIFO_HEADER_SENS_TIME_FRM: + rslt = unpack_sensortime_frame(&data_index, fifo); + break; + + /* If header defines skip frame */ + case BMI2_FIFO_HEADER_SKIP_FRM: + rslt = unpack_skipped_frame(&data_index, fifo); + break; + + /* If header defines Input configuration frame */ + case BMI2_FIFO_HEADER_INPUT_CFG_FRM: + rslt = move_next_frame(&data_index, BMI2_FIFO_INPUT_CFG_LENGTH, fifo); + break; + + /* If header defines invalid frame or end of valid data */ + case BMI2_FIFO_HEAD_OVER_READ_MSB: + + /* Move the data index to the last byte */ + data_index = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + break; + case BMI2_FIFO_VIRT_ACT_RECOG_FRM: + rslt = move_next_frame(&data_index, BMI2_FIFO_VIRT_ACT_DATA_LENGTH, fifo); + break; + default: + + /* Move the data index to the last byte in case of invalid values */ + data_index = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + break; + } + + /* Break if number of frames to be read is complete or FIFO is empty */ + if ((frame_to_read == gyro_index) || (rslt == BMI2_W_FIFO_EMPTY)) + { + break; + } + } + + /* Update the gyroscope frame index */ + (*gyro_length) = gyro_index; + + /* Update the gyroscope byte index */ + fifo->gyr_byte_start_idx = data_index; + + return rslt; +} + +/*! + * @brief This internal API is used to parse the gyroscope data from the FIFO + * data in header mode. It updates the current data byte to + * be parsed. + */ +static int8_t unpack_gyro_header_frame(struct bmi2_sens_axes_data *gyr, + uint16_t *idx, + uint16_t *gyr_idx, + uint8_t frame, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + switch (frame) + { + /* If frame contains only gyroscope data */ + case BMI2_FIFO_HEADER_GYR_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->gyr_frm_len) > fifo->length) + { + /* Update the data index as complete*/ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the gyroscope data */ + unpack_gyro_data(&gyr[(*gyr_idx)], *idx, fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + BMI2_FIFO_GYR_LENGTH; + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&gyr[(*gyr_idx)], idx, fifo); + } + + /* Update gyroscope frame index */ + (*gyr_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer and gyroscope data */ + case BMI2_FIFO_HEADER_GYR_ACC_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->acc_gyr_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the gyroscope data */ + unpack_gyro_data(&gyr[(*gyr_idx)], (*idx), fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + BMI2_FIFO_ACC_GYR_LENGTH; + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&gyr[(*gyr_idx)], idx, fifo); + } + + /* Update gyroscope frame index */ + (*gyr_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains gyroscope and auxiliary data */ + case BMI2_FIFO_HEADER_AUX_GYR_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->aux_gyr_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the gyroscope data */ + unpack_gyro_data(&gyr[(*gyr_idx)], ((*idx) + fifo->aux_frm_len), fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + (BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&gyr[(*gyr_idx)], idx, fifo); + } + + /* Update gyroscope frame index */ + (*gyr_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer, gyroscope and auxiliary data */ + case BMI2_FIFO_HEADER_ALL_FRM: + + /* Partially read, then skip the data*/ + if ((*idx + fifo->all_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the gyroscope data */ + unpack_gyro_data(&gyr[(*gyr_idx)], ((*idx) + fifo->aux_frm_len), fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + (BMI2_FIFO_ACC_LENGTH + BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&gyr[(*gyr_idx)], idx, fifo); + } + + /* Update gyroscope frame index */ + (*gyr_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer and auxiliary data */ + case BMI2_FIFO_HEADER_AUX_ACC_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->acc_aux_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains only auxiliary data */ + case BMI2_FIFO_HEADER_AUX_FRM: + case BMI2_FIFO_HEAD_LESS_AUX_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->aux_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains only accelerometer data */ + case BMI2_FIFO_HEADER_ACC_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->acc_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + default: + + /* Move the data index to the last byte in case of invalid values */ + (*idx) = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + break; + } + + return rslt; +} + +/*! + * @brief This internal API is used to parse the gyroscope data from the FIFO + * data in header-less mode. It updates the current data byte to + * be parsed. + */ +static int8_t unpack_gyro_headerless_frame(struct bmi2_sens_axes_data *gyr, + uint16_t *idx, + uint16_t *gyr_idx, + uint8_t frame, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + switch (frame) + { + /* If frame contains only gyroscope data */ + case BMI2_FIFO_HEAD_LESS_GYR_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->gyr_frm_len) > fifo->length) + { + /* Update the data index as complete*/ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the gyroscope data */ + unpack_gyro_data(&gyr[(*gyr_idx)], *idx, fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + BMI2_FIFO_GYR_LENGTH; + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&gyr[(*gyr_idx)], idx, fifo); + } + + /* Update gyroscope frame index */ + (*gyr_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer and gyroscope data */ + case BMI2_FIFO_HEAD_LESS_GYR_ACC_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->acc_gyr_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the gyroscope data */ + unpack_gyro_data(&gyr[(*gyr_idx)], (*idx), fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + BMI2_FIFO_ACC_GYR_LENGTH; + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&gyr[(*gyr_idx)], idx, fifo); + } + + /* Update gyroscope frame index */ + (*gyr_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains gyroscope and auxiliary data */ + case BMI2_FIFO_HEAD_LESS_GYR_AUX_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->aux_gyr_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the gyroscope data */ + unpack_gyro_data(&gyr[(*gyr_idx)], (*idx), fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + (BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&gyr[(*gyr_idx)], idx, fifo); + } + + /* Update gyroscope frame index */ + (*gyr_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer, gyroscope and auxiliary data */ + case BMI2_FIFO_HEAD_LESS_ALL_FRM: + + /* Partially read, then skip the data*/ + if ((*idx + fifo->all_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the gyroscope data */ + unpack_gyro_data(&gyr[(*gyr_idx)], (*idx), fifo, dev); + + /* Update data index */ + (*idx) = (*idx) + (BMI2_FIFO_ACC_LENGTH + BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_sensor_time(&gyr[(*gyr_idx)], idx, fifo); + } + + /* Update gyroscope frame index */ + (*gyr_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer and auxiliary data */ + case BMI2_FIFO_HEAD_LESS_AUX_ACC_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->acc_aux_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains only auxiliary data */ + case BMI2_FIFO_HEAD_LESS_AUX_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->aux_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains only accelerometer data */ + case BMI2_FIFO_HEAD_LESS_ACC_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->acc_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + default: + + /* Move the data index to the last byte in case of invalid values */ + (*idx) = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + break; + } + + return rslt; +} + +/*! + * @brief This internal API is used to parse gyroscope data from the FIFO data. + */ +static void unpack_gyro_data(struct bmi2_sens_axes_data *gyr, + uint16_t data_start_index, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + /* Variables to store LSB value */ + uint16_t data_lsb; + + /* Variables to store MSB value */ + uint16_t data_msb; + + /* Gyroscope raw x data */ + data_lsb = fifo->data[data_start_index++]; + data_msb = fifo->data[data_start_index++]; + gyr->x = (int16_t)((data_msb << 8) | data_lsb); + + /* Gyroscope raw y data */ + data_lsb = fifo->data[data_start_index++]; + data_msb = fifo->data[data_start_index++]; + gyr->y = (int16_t)((data_msb << 8) | data_lsb); + + /* Gyroscope raw z data */ + data_lsb = fifo->data[data_start_index++]; + data_msb = fifo->data[data_start_index++]; + gyr->z = (int16_t)((data_msb << 8) | data_lsb); + + /* Get the compensated gyroscope data */ + comp_gyro_cross_axis_sensitivity(gyr, dev); + + /* Get the re-mapped gyroscope data */ + get_remapped_data(gyr, dev); +} + +/*! + * @brief This API computes the number of bytes of auxiliary FIFO data which is + * to be parsed in header-less mode. + */ +static int8_t parse_fifo_aux_len(uint16_t *start_idx, + uint16_t *len, + uint8_t *skip_length, + const uint16_t *aux_count, + const struct bmi2_fifo_frame *fifo) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Data start index */ + *start_idx = fifo->aux_byte_start_idx; + + /* If only auxiliary is enabled */ + if (fifo->data_enable == BMI2_FIFO_AUX_EN) + { + /* Number of bytes to be read */ + (*len) = (uint16_t)((*aux_count) * fifo->aux_frm_len); + + /* Number of bytes to skip in case dummy frame is obtained */ + (*skip_length) = fifo->aux_frm_len; + } + /* If only accelerometer and auxiliary are enabled */ + else if (fifo->data_enable == (BMI2_FIFO_AUX_EN | BMI2_FIFO_ACC_EN)) + { + /* Number of bytes to be read */ + (*len) = (uint16_t)((*aux_count) * (BMI2_FIFO_ACC_LENGTH + fifo->aux_frm_len)); + + /* Number of bytes to skip in case dummy frame is obtained */ + (*skip_length) = (BMI2_FIFO_ACC_LENGTH + fifo->aux_frm_len); + } + /* If only accelerometer and gyroscope are enabled */ + else if (fifo->data_enable == (BMI2_FIFO_AUX_EN | BMI2_FIFO_GYR_EN)) + { + /* Number of bytes to be read */ + (*len) = (uint16_t)((*aux_count) * (BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len)); + + /* Number of bytes to skip in case dummy frame is obtained */ + (*skip_length) = (BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + } + /* If only accelerometer, gyroscope and auxiliary are enabled */ + else if (fifo->data_enable == (BMI2_FIFO_AUX_EN | BMI2_FIFO_GYR_EN | BMI2_FIFO_ACC_EN)) + { + /* Number of bytes to be read */ + (*len) = (uint16_t)((*aux_count) * (BMI2_FIFO_ACC_LENGTH + BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len)); + + /* Number of bytes to skip in case dummy frame is obtained */ + (*skip_length) = (BMI2_FIFO_ACC_LENGTH + BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + } + else + { + /* Move the data index to the last byte to mark completion */ + (*start_idx) = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + } + + /* If more data is requested than available */ + if (((*len)) > fifo->length) + { + (*len) = fifo->length; + } + + return rslt; +} + +/*! + * @brief This internal API is used to parse the auxiliary data from the + * FIFO in headerless mode. + */ +static int8_t extract_aux_headerless_mode(struct bmi2_aux_fifo_data *aux, + uint16_t *aux_length, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + int8_t rslt; + + /* Variable to index the bytes */ + uint16_t data_index = 0; + + /* Variable to index auxiliary frames */ + uint16_t aux_index = 0; + + /* Variable to store the number of bytes to be read */ + uint16_t data_read_length = 0; + + /* Variable to define the data enable byte */ + uint8_t data_enable; + + /* Number of bytes to skip in case dummy frame is obtained */ + uint8_t skip_length = 0; + + rslt = parse_fifo_aux_len(&data_index, &data_read_length, &skip_length, aux_length, fifo); + + /* Convert word to byte since all sensor enables are in a byte */ + data_enable = (uint8_t)(fifo->data_enable >> 8); + + for (; (data_index < data_read_length) && (rslt != BMI2_W_FIFO_EMPTY);) + { + rslt = check_dummy_frame(BMI2_FIFO_HEADERLESS_DUMMY_AUX, &data_index, skip_length, fifo); + + /* Unpack only if Valid frame is present */ + if (rslt == BMI2_OK) + { + /* Unpack frame to get auxiliary data */ + rslt = unpack_aux_frame(aux, &data_index, &aux_index, data_enable, fifo, dev); + + if (rslt != BMI2_W_FIFO_EMPTY) + { + /* Check for the availability of next two bytes of FIFO data */ + rslt = check_empty_fifo(&data_index, fifo); + } + } + } + + /* Update number of auxiliary frames to be read */ + *aux_length = aux_index; + + /* Update the auxiliary byte index */ + fifo->aux_byte_start_idx = data_index; + + return rslt; +} + +/*! + * @brief This API is used to parse the auxiliary data from the FIFO data in + * header mode. + */ +static int8_t extract_aux_header_mode(struct bmi2_aux_fifo_data *aux, + uint16_t *aux_len, + struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Variable to define header frame */ + uint8_t frame_header = 0; + + /* Variable to index the data bytes */ + uint16_t data_index; + + /* Variable to index gyroscope frames */ + uint16_t aux_index = 0; + + /* Variable to indicate auxiliary frames read */ + uint16_t frame_to_read = *aux_len; + + for (data_index = fifo->aux_byte_start_idx; data_index < fifo->length;) + { + /* Get frame header byte */ + frame_header = fifo->data[data_index] & BMI2_FIFO_TAG_INTR_MASK; + + /* Parse virtual header if S4S is enabled */ + parse_if_virtual_header(&frame_header, &data_index, fifo); + + /* Index shifted to next byte where data starts */ + data_index++; + switch (frame_header) + { + /* If header defines auxiliary frame */ + case BMI2_FIFO_HEADER_AUX_FRM: + case BMI2_FIFO_HEADER_AUX_ACC_FRM: + case BMI2_FIFO_HEADER_AUX_GYR_FRM: + case BMI2_FIFO_HEADER_ALL_FRM: + + /* Unpack from normal frames */ + rslt = unpack_aux_frame(aux, &data_index, &aux_index, frame_header, fifo, dev); + break; + + /* If header defines only accelerometer frame */ + case BMI2_FIFO_HEADER_ACC_FRM: + rslt = move_next_frame(&data_index, fifo->acc_frm_len, fifo); + break; + + /* If header defines only gyroscope frame */ + case BMI2_FIFO_HEADER_GYR_FRM: + rslt = move_next_frame(&data_index, fifo->gyr_frm_len, fifo); + break; + + /* If header defines only gyroscope and accelerometer frame */ + case BMI2_FIFO_HEADER_GYR_ACC_FRM: + rslt = move_next_frame(&data_index, fifo->acc_gyr_frm_len, fifo); + break; + + /* If header defines sensor time frame */ + case BMI2_FIFO_HEADER_SENS_TIME_FRM: + rslt = unpack_sensortime_frame(&data_index, fifo); + break; + + /* If header defines skip frame */ + case BMI2_FIFO_HEADER_SKIP_FRM: + rslt = unpack_skipped_frame(&data_index, fifo); + break; + + /* If header defines Input configuration frame */ + case BMI2_FIFO_HEADER_INPUT_CFG_FRM: + rslt = move_next_frame(&data_index, BMI2_FIFO_INPUT_CFG_LENGTH, fifo); + break; + + /* If header defines invalid frame or end of valid data */ + case BMI2_FIFO_HEAD_OVER_READ_MSB: + + /* Move the data index to the last byte */ + data_index = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + break; + case BMI2_FIFO_VIRT_ACT_RECOG_FRM: + rslt = move_next_frame(&data_index, BMI2_FIFO_VIRT_ACT_DATA_LENGTH, fifo); + break; + default: + + /* Move the data index to the last byte in case + * of invalid values + */ + data_index = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + break; + } + + /* Break if number of frames to be read is complete or FIFO is + * empty + */ + if ((frame_to_read == aux_index) || (rslt == BMI2_W_FIFO_EMPTY)) + { + break; + } + } + + /* Update the gyroscope frame index */ + (*aux_len) = aux_index; + + /* Update the gyroscope byte index */ + fifo->aux_byte_start_idx = data_index; + + return rslt; +} + +/*! + * @brief This API is used to parse the auxiliary frame from the FIFO data in + * both header mode and header-less mode and update the data_index value which + * is used to store the index of the current data byte which is parsed. + */ +static int8_t unpack_aux_frame(struct bmi2_aux_fifo_data *aux, + uint16_t *idx, + uint16_t *aux_idx, + uint8_t frame, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + switch (frame) + { + /* If frame contains only auxiliary data */ + case BMI2_FIFO_HEADER_AUX_FRM: + case BMI2_FIFO_HEAD_LESS_AUX_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->aux_frm_len) > fifo->length) + { + /* Update the data index as complete*/ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the auxiliary data */ + unpack_aux_data(&aux[(*aux_idx)], (*idx), fifo); + + /* Update data index */ + (*idx) = (*idx) + fifo->aux_frm_len; + + unpack_virtual_aux_data(&aux[(*aux_idx)], idx, aux_idx, fifo, dev); + + /* Update auxiliary frame index */ + (*aux_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer and auxiliary data */ + case BMI2_FIFO_HEADER_AUX_ACC_FRM: + case BMI2_FIFO_HEAD_LESS_AUX_ACC_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->acc_aux_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the auxiliary data */ + unpack_aux_data(&aux[(*aux_idx)], (*idx), fifo); + + /* Update data index */ + (*idx) = (*idx) + (BMI2_FIFO_ACC_LENGTH + fifo->aux_frm_len); + + unpack_virtual_aux_data(&aux[(*aux_idx)], idx, aux_idx, fifo, dev); + + /* Update auxiliary frame index */ + (*aux_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains gyroscope and auxiliary data */ + case BMI2_FIFO_HEADER_AUX_GYR_FRM: + case BMI2_FIFO_HEAD_LESS_GYR_AUX_FRM: + + /* Partially read, then skip the data */ + if (((*idx) + fifo->aux_gyr_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the auxiliary data */ + unpack_aux_data(&aux[(*aux_idx)], (*idx), fifo); + + /* Update data index */ + (*idx) = (*idx) + (BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + + unpack_virtual_aux_data(&aux[(*aux_idx)], idx, aux_idx, fifo, dev); + + /* Update auxiliary frame index */ + (*aux_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer, gyroscope and auxiliary data */ + case BMI2_FIFO_HEADER_ALL_FRM: + case BMI2_FIFO_HEAD_LESS_ALL_FRM: + + /* Partially read, then skip the data */ + if ((*idx + fifo->all_frm_len) > fifo->length) + { + /* Move the data index to the last byte */ + (*idx) = fifo->length; + + rslt = BMI2_OK; + break; + } + + /* Get the auxiliary data */ + unpack_aux_data(&aux[(*aux_idx)], (*idx), fifo); + + /* Update data index */ + (*idx) = (*idx) + (BMI2_FIFO_ACC_LENGTH + BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + + unpack_virtual_aux_data(&aux[(*aux_idx)], idx, aux_idx, fifo, dev); + + /* Update auxiliary frame index */ + (*aux_idx)++; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains only accelerometer data */ + case BMI2_FIFO_HEADER_ACC_FRM: + case BMI2_FIFO_HEAD_LESS_ACC_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->acc_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains only gyroscope data */ + case BMI2_FIFO_HEADER_GYR_FRM: + case BMI2_FIFO_HEAD_LESS_GYR_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->gyr_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + + /* If frame contains accelerometer and gyroscope data */ + case BMI2_FIFO_HEADER_GYR_ACC_FRM: + case BMI2_FIFO_HEAD_LESS_GYR_ACC_FRM: + + /* Update data index */ + (*idx) = (*idx) + fifo->acc_gyr_frm_len; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + break; + default: + + /* Move the data index to the last byte in case of + * invalid values + */ + (*idx) = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + break; + } + + return rslt; +} + +/*! + * @brief This internal API is used to unpack virtual auxillary sensortime data. + */ +static void unpack_virtual_aux_data(struct bmi2_aux_fifo_data *aux, + uint16_t *idx, + const uint16_t *aux_idx, + const struct bmi2_fifo_frame *fifo, + const struct bmi2_dev *dev) +{ + /* Get virtual sensor time if S4S is enabled */ + if (dev->sens_en_stat & BMI2_EXT_SENS_SEL) + { + unpack_virt_aux_sensor_time(&aux[(*aux_idx)], idx, fifo); + } +} + +/*! + * @brief This internal API is used to parse auxiliary data from the FIFO data. + */ +static void unpack_aux_data(struct bmi2_aux_fifo_data *aux, + uint16_t data_start_index, + const struct bmi2_fifo_frame *fifo) +{ + /* Variables to store index */ + uint16_t idx = 0; + + /* Get auxiliary data */ + for (; idx < 8; idx++) + { + aux->data[idx] = fifo->data[data_start_index++]; + } +} + +/*! + * @brief This internal API parses virtual frame header from the FIFO data. + */ +static void parse_if_virtual_header(uint8_t *frame_header, uint16_t *data_index, const struct bmi2_fifo_frame *fifo) +{ + /* Variable to extract virtual header byte */ + uint8_t virtual_header_mode; + + /* Extract virtual header mode from the frame header */ + virtual_header_mode = BMI2_GET_BITS(*frame_header, BMI2_FIFO_VIRT_FRM_MODE); + + /* If the extracted header byte is a virtual header */ + if (virtual_header_mode == BMI2_FIFO_VIRT_FRM_MODE) + { + /* If frame header is not activity recognition header */ + if (*frame_header != 0xC8) + { + /* Index shifted to next byte where sensor frame is present */ + (*data_index) = (*data_index) + 1; + + /* Get the sensor frame header */ + *frame_header = fifo->data[*data_index] & BMI2_FIFO_TAG_INTR_MASK; + } + } +} + +/*! + * @brief This internal API gets sensor time from the accelerometer and + * gyroscope virtual frames and updates in the data structure. + */ +static void unpack_virt_sensor_time(struct bmi2_sens_axes_data *sens, uint16_t *idx, const struct bmi2_fifo_frame *fifo) +{ + /* Variables to define 3 bytes of sensor time */ + uint32_t sensor_time_byte3; + uint16_t sensor_time_byte2; + uint8_t sensor_time_byte1; + + /* Get sensor time from the FIFO data */ + sensor_time_byte3 = (uint32_t)(fifo->data[(*idx) + BMI2_SENSOR_TIME_MSB_BYTE] << 16); + sensor_time_byte2 = (uint16_t) fifo->data[(*idx) + BMI2_SENSOR_TIME_XLSB_BYTE] << 8; + sensor_time_byte1 = fifo->data[(*idx)]; + + /* Store sensor time in the sensor data structure */ + sens->virt_sens_time = (uint32_t)(sensor_time_byte3 | sensor_time_byte2 | sensor_time_byte1); + + /* Move the data index by 3 bytes */ + (*idx) = (*idx) + BMI2_SENSOR_TIME_LENGTH; +} + +/*! + * @brief This internal API gets sensor time from the auxiliary virtual + * frames and updates in the data structure. + */ +static void unpack_virt_aux_sensor_time(struct bmi2_aux_fifo_data *aux, + uint16_t *idx, + const struct bmi2_fifo_frame *fifo) +{ + /* Variables to define 3 bytes of sensor time */ + uint32_t sensor_time_byte3; + uint16_t sensor_time_byte2; + uint8_t sensor_time_byte1; + + /* Get sensor time from the FIFO data */ + sensor_time_byte3 = (uint32_t)(fifo->data[(*idx) + BMI2_SENSOR_TIME_MSB_BYTE] << 16); + sensor_time_byte2 = (uint16_t) fifo->data[(*idx) + BMI2_SENSOR_TIME_XLSB_BYTE] << 8; + sensor_time_byte1 = fifo->data[(*idx)]; + + /* Store sensor time in the sensor data structure */ + aux->virt_sens_time = (uint32_t)(sensor_time_byte3 | sensor_time_byte2 | sensor_time_byte1); + + /* Move the data index by 3 bytes */ + (*idx) = (*idx) + BMI2_SENSOR_TIME_LENGTH; +} + +/*! + * @brief This internal API is used to reset the FIFO related configurations in + * the FIFO frame structure for the next FIFO read. + */ +static int8_t reset_fifo_frame_structure(struct bmi2_fifo_frame *fifo, struct bmi2_dev *dev) +{ + int8_t rslt; + + struct bmi2_aux_config config; + + /* Reset FIFO data structure */ + fifo->acc_byte_start_idx = 0; + fifo->aux_byte_start_idx = 0; + fifo->gyr_byte_start_idx = 0; + fifo->sensor_time = 0; + fifo->skipped_frame_count = 0; + fifo->act_recog_byte_start_idx = 0; + + /* Get default configurations for the type of feature selected. */ + rslt = get_aux_interface_config(&config, dev); + + if (rslt == BMI2_OK) + { + /* If S4S is enabled */ + if ((dev->sens_en_stat & BMI2_EXT_SENS_SEL) == BMI2_EXT_SENS_SEL) + { + fifo->acc_frm_len = BMI2_FIFO_VIRT_ACC_LENGTH; + fifo->gyr_frm_len = BMI2_FIFO_VIRT_GYR_LENGTH; + fifo->aux_frm_len = BMI2_FIFO_VIRT_AUX_LENGTH; + fifo->acc_gyr_frm_len = BMI2_FIFO_VIRT_ACC_GYR_LENGTH; + fifo->acc_aux_frm_len = BMI2_FIFO_VIRT_ACC_AUX_LENGTH; + fifo->aux_gyr_frm_len = BMI2_FIFO_VIRT_GYR_AUX_LENGTH; + fifo->all_frm_len = BMI2_FIFO_VIRT_ALL_LENGTH; + + /* If S4S is not enabled */ + } + else + { + if (config.aux_rd_burst == BMI2_AUX_READ_LEN_0) + { + fifo->aux_frm_len = BMI2_AUX_RD_BURST_FRM_LEN_1; + } + + if (config.aux_rd_burst == BMI2_AUX_READ_LEN_1) + { + fifo->aux_frm_len = BMI2_AUX_RD_BURST_FRM_LEN_2; + } + + if (config.aux_rd_burst == BMI2_AUX_READ_LEN_2) + { + fifo->aux_frm_len = BMI2_AUX_RD_BURST_FRM_LEN_6; + } + + if (config.aux_rd_burst == BMI2_AUX_READ_LEN_3) + { + fifo->aux_frm_len = BMI2_AUX_RD_BURST_FRM_LEN_8; + } + + fifo->acc_frm_len = BMI2_FIFO_ACC_LENGTH; + fifo->gyr_frm_len = BMI2_FIFO_GYR_LENGTH; + fifo->acc_gyr_frm_len = BMI2_FIFO_ACC_GYR_LENGTH; + fifo->acc_aux_frm_len = (BMI2_FIFO_ACC_LENGTH + fifo->aux_frm_len); + fifo->aux_gyr_frm_len = (BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + fifo->all_frm_len = (BMI2_FIFO_ACC_LENGTH + BMI2_FIFO_GYR_LENGTH + fifo->aux_frm_len); + } + } + + return rslt; +} + +/*! + * @brief This API internal checks whether the FIFO data read is an empty frame. + * If empty frame, index is moved to the last byte. + */ +static int8_t check_empty_fifo(uint16_t *data_index, const struct bmi2_fifo_frame *fifo) +{ + /* Variables to define error */ + int8_t rslt = BMI2_OK; + + /* Validate data index */ + if (((*data_index) + 6) < fifo->length) + { + /* Check if FIFO is empty */ + if (((fifo->data[(*data_index)] == BMI2_FIFO_LSB_CONFIG_CHECK) && + (fifo->data[(*data_index) + 1] == BMI2_FIFO_MSB_CONFIG_CHECK) && + (fifo->data[(*data_index) + 2] == BMI2_FIFO_LSB_CONFIG_CHECK)) && + ((fifo->data[(*data_index) + 3] == BMI2_FIFO_MSB_CONFIG_CHECK) && + (fifo->data[(*data_index) + 4] == BMI2_FIFO_LSB_CONFIG_CHECK)) && + ((fifo->data[(*data_index) + 5] == BMI2_FIFO_MSB_CONFIG_CHECK))) + { + /* Move the data index to the last byte to mark completion */ + (*data_index) = fifo->length; + + /* FIFO is empty */ + rslt = BMI2_W_FIFO_EMPTY; + } + else + { + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + } + } + + return rslt; +} + +/*! + * @brief This internal 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 int8_t move_next_frame(uint16_t *data_index, uint8_t current_frame_length, const struct bmi2_fifo_frame *fifo) +{ + /* Variables to define error */ + int8_t rslt = BMI2_OK; + + /* Validate data index */ + if (((*data_index) + current_frame_length) > fifo->length) + { + /* Move the data index to the last byte */ + (*data_index) = fifo->length; + + rslt = BMI2_OK; + } + else + { + /* Move the data index to next frame */ + (*data_index) = (*data_index) + current_frame_length; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + } + + return rslt; +} + +/*! + * @brief This internal API is used to parse and store the sensor time from the + * FIFO data. + */ +static int8_t unpack_sensortime_frame(uint16_t *data_index, struct bmi2_fifo_frame *fifo) +{ + /* Variables to define error */ + int8_t rslt = BMI2_OK; + + /* Variables to define 3 bytes of sensor time */ + uint32_t sensor_time_byte3 = 0; + uint16_t sensor_time_byte2 = 0; + uint8_t sensor_time_byte1 = 0; + + /* Validate data index */ + if (((*data_index) + BMI2_SENSOR_TIME_LENGTH) > fifo->length) + { + /* Move the data index to the last byte */ + (*data_index) = fifo->length; + + rslt = BMI2_OK; + } + else + { + /* Get sensor time from the FIFO data */ + sensor_time_byte3 = fifo->data[(*data_index) + BMI2_SENSOR_TIME_MSB_BYTE] << 16; + sensor_time_byte2 = fifo->data[(*data_index) + BMI2_SENSOR_TIME_XLSB_BYTE] << 8; + sensor_time_byte1 = fifo->data[(*data_index)]; + + /* Update sensor time in the FIFO structure */ + fifo->sensor_time = (uint32_t)(sensor_time_byte3 | sensor_time_byte2 | sensor_time_byte1); + + /* Move the data index by 3 bytes */ + (*data_index) = (*data_index) + BMI2_SENSOR_TIME_LENGTH; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + } + + return rslt; +} + +/*! + * @brief This internal API is used to parse and store the skipped frame count + * from the FIFO data. + */ +static int8_t unpack_skipped_frame(uint16_t *data_index, struct bmi2_fifo_frame *fifo) +{ + /* Variables to define error */ + int8_t rslt = BMI2_OK; + + /* Validate data index */ + if ((*data_index) >= fifo->length) + { + /* Update the data index to the last byte */ + (*data_index) = fifo->length; + + rslt = BMI2_OK; + } + else + { + /* Update skipped frame count in the FIFO structure */ + fifo->skipped_frame_count = fifo->data[(*data_index)]; + + /* Move the data index by 1 byte */ + (*data_index) = (*data_index) + 1; + + /* More frames could be read */ + rslt = BMI2_W_PARTIAL_READ; + } + + return rslt; +} + +/*! + * @brief This internal API enables and configures the accelerometer which is + * needed for self-test operation. It also sets the amplitude for the self-test. + */ +static int8_t pre_self_test_config(struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Structure to define sensor configurations */ + struct bmi2_sens_config sens_cfg; + + /* List the sensors to be selected */ + uint8_t sens_sel = BMI2_ACCEL; + + /* Enable accelerometer */ + rslt = bmi2_sensor_enable(&sens_sel, 1, dev); + dev->delay_us(1000, dev->intf_ptr); + + /* Enable self-test amplitude */ + if (rslt == BMI2_OK) + { + rslt = set_accel_self_test_amp(BMI2_ENABLE, dev); + } + + if (rslt == BMI2_OK) + { + /* Select accelerometer for sensor configurations */ + sens_cfg.type = BMI2_ACCEL; + + /* Get the default values */ + rslt = bmi2_get_sensor_config(&sens_cfg, 1, dev); + if (rslt == BMI2_OK) + { + /* Set the configurations required for self-test */ + sens_cfg.cfg.acc.odr = BMI2_ACC_ODR_1600HZ; + sens_cfg.cfg.acc.bwp = BMI2_ACC_NORMAL_AVG4; + sens_cfg.cfg.acc.filter_perf = BMI2_PERF_OPT_MODE; + sens_cfg.cfg.acc.range = BMI2_ACC_RANGE_16G; + + /* Set accelerometer configurations */ + rslt = bmi2_set_sensor_config(&sens_cfg, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API performs the steps needed for self-test operation + * before reading the accelerometer self-test data. + */ +static int8_t self_test_config(uint8_t sign, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Enable the accelerometer self-test feature */ + rslt = set_accel_self_test_enable(BMI2_ENABLE, dev); + if (rslt == BMI2_OK) + { + /* Selects the sign of accelerometer self-test excitation */ + rslt = set_acc_self_test_sign(sign, dev); + } + + return rslt; +} + +/*! + * @brief This internal API enables or disables the accelerometer self-test + * feature in the sensor. + */ +static int8_t set_accel_self_test_enable(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define data */ + uint8_t data = 0; + + /* Enable/Disable self-test feature */ + rslt = bmi2_get_regs(BMI2_ACC_SELF_TEST_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + data = BMI2_SET_BIT_POS0(data, BMI2_ACC_SELF_TEST_EN, enable); + rslt = bmi2_set_regs(BMI2_ACC_SELF_TEST_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This internal API selects the sign for accelerometer self-test + * excitation. + */ +static int8_t set_acc_self_test_sign(uint8_t sign, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define data */ + uint8_t data = 0; + + /* Select the sign for self-test excitation */ + rslt = bmi2_get_regs(BMI2_ACC_SELF_TEST_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + data = BMI2_SET_BITS(data, BMI2_ACC_SELF_TEST_SIGN, sign); + rslt = bmi2_set_regs(BMI2_ACC_SELF_TEST_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This internal API sets the amplitude of the accelerometer self-test + * deflection in the sensor. + */ +static int8_t set_accel_self_test_amp(uint8_t amp, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define data */ + uint8_t data = 0; + + /* Select amplitude of the self-test deflection */ + rslt = bmi2_get_regs(BMI2_ACC_SELF_TEST_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + data = BMI2_SET_BITS(data, BMI2_ACC_SELF_TEST_AMP, amp); + rslt = bmi2_set_regs(BMI2_ACC_SELF_TEST_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This internal API reads the accelerometer data for x,y and z axis from + * the sensor. The data units is in LSB format. + */ +static int8_t read_accel_xyz(struct bmi2_sens_axes_data *accel, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define LSB */ + uint16_t lsb = 0; + + /* Variable to define MSB */ + uint16_t msb = 0; + + /* Array to define data buffer */ + uint8_t data[BMI2_ACC_NUM_BYTES] = { 0 }; + + rslt = bmi2_get_regs(BMI2_ACC_X_LSB_ADDR, data, BMI2_ACC_NUM_BYTES, dev); + if (rslt == BMI2_OK) + { + /* Accelerometer data x axis */ + msb = data[1]; + lsb = data[0]; + accel->x = (int16_t)((msb << 8) | lsb); + + /* Accelerometer data y axis */ + msb = data[3]; + lsb = data[2]; + accel->y = (int16_t)((msb << 8) | lsb); + + /* Accelerometer data z axis */ + msb = data[5]; + lsb = data[4]; + accel->z = (int16_t)((msb << 8) | lsb); + } + + return rslt; +} + +/*! + * @brief This internal API reads the gyroscope data for x, y and z axis from + * the sensor. The data units is in LSB format. + */ +static int8_t read_gyro_xyz(struct bmi2_sens_axes_data *gyro, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define LSB */ + uint16_t lsb = 0; + + /* Variable to define MSB */ + uint16_t msb = 0; + + /* Array to define data buffer */ + uint8_t data[BMI2_GYR_NUM_BYTES] = { 0 }; + + rslt = bmi2_get_regs(BMI2_GYR_X_LSB_ADDR, data, BMI2_GYR_NUM_BYTES, dev); + if (rslt == BMI2_OK) + { + /* Gyroscope data x axis */ + msb = data[1]; + lsb = data[0]; + gyro->x = (int16_t)((msb << 8) | lsb); + + /* Gyroscope data y axis */ + msb = data[3]; + lsb = data[2]; + gyro->y = (int16_t)((msb << 8) | lsb); + + /* Gyroscope data z axis */ + msb = data[5]; + lsb = data[4]; + gyro->z = (int16_t)((msb << 8) | lsb); + } + + return rslt; +} + +/*! + * @brief This internal API converts LSB value of accelerometer axes to form + * 'g' to 'mg' for self-test. + */ +static void convert_lsb_g(const struct bmi2_selftest_delta_limit *acc_data_diff, + struct bmi2_selftest_delta_limit *acc_data_diff_mg, + const struct bmi2_dev *dev) +{ + /* Variable to define LSB/g value of axes */ + uint32_t lsb_per_g; + + /* Range considered for self-test is +/-16g */ + uint8_t range = BMI2_ACC_SELF_TEST_RANGE; + + /* lsb_per_g for the respective resolution and 16g range */ + lsb_per_g = (uint32_t)(power(2, dev->resolution) / (2 * range)); + + /* Accelerometer x value in mg */ + acc_data_diff_mg->x = (acc_data_diff->x / (int32_t) lsb_per_g) * 1000; + + /* Accelerometer y value in mg */ + acc_data_diff_mg->y = (acc_data_diff->y / (int32_t) lsb_per_g) * 1000; + + /* Accelerometer z value in mg */ + acc_data_diff_mg->z = (acc_data_diff->z / (int32_t) lsb_per_g) * 1000; +} + +/*! + * @brief This internal API is used to calculate the power of a value. + */ +static int32_t power(int16_t base, uint8_t resolution) +{ + /* Initialize loop */ + uint8_t loop = 1; + + /* Initialize variable to store the power of 2 value */ + int32_t value = 1; + + for (; loop <= resolution; loop++) + { + value = (int32_t)(value * base); + } + + return value; +} + +/*! + * @brief This internal API validates the accelerometer self-test data and + * decides the result of self-test operation. + */ +static int8_t validate_self_test(const struct bmi2_selftest_delta_limit *accel_data_diff) +{ + /* Variable to define error */ + int8_t rslt; + + /* As per the data sheet, The actually measured signal differences should be significantly + * larger than the minimum differences for each axis in order for the self-test to pass. + */ + if ((accel_data_diff->x > BMI2_ST_ACC_X_SIG_MIN_DIFF) && (accel_data_diff->y < BMI2_ST_ACC_Y_SIG_MIN_DIFF) && + (accel_data_diff->z > BMI2_ST_ACC_Z_SIG_MIN_DIFF)) + { + /* Self-test pass */ + rslt = BMI2_OK; + } + else + { + /* Self-test fail*/ + rslt = BMI2_E_SELF_TEST_FAIL; + } + + return rslt; +} + +/*! + * @brief This internal API gets the re-mapped x, y and z axes from the sensor. + */ +static int8_t get_remap_axes(struct bmi2_axes_remap *remap, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for axis re-mapping */ + struct bmi2_feature_config remap_config = { 0, 0, 0 }; + + /* Variable to get the status of advance power save */ + uint8_t aps_stat; + + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if enabled */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + if (rslt == BMI2_OK) + { + /* Search for axis re-mapping and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&remap_config, BMI2_AXIS_MAP, dev); + if (feat_found) + { + rslt = bmi2_get_feat_config(remap_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for axis re-mapping */ + idx = remap_config.start_addr; + + /* Get the re-mapped x-axis */ + remap->x_axis = BMI2_GET_BIT_POS0(feat_config[idx], BMI2_X_AXIS); + + /* Get the re-mapped x-axis polarity */ + remap->x_axis_sign = BMI2_GET_BITS(feat_config[idx], BMI2_X_AXIS_SIGN); + + /* Get the re-mapped y-axis */ + remap->y_axis = BMI2_GET_BITS(feat_config[idx], BMI2_Y_AXIS); + + /* Get the re-mapped y-axis polarity */ + remap->y_axis_sign = BMI2_GET_BITS(feat_config[idx], BMI2_Y_AXIS_SIGN); + + /* Get the re-mapped z-axis */ + remap->z_axis = BMI2_GET_BITS(feat_config[idx], BMI2_Z_AXIS); + + /* Increment byte to fetch the next data */ + idx++; + + /* Get the re-mapped z-axis polarity */ + remap->z_axis_sign = BMI2_GET_BIT_POS0(feat_config[idx], BMI2_Z_AXIS_SIGN); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + /* Enable Advance power save if disabled while configuring and + * not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API sets the re-mapped x, y and z axes in the sensor. + */ +static int8_t set_remap_axes(const struct bmi2_axes_remap *remap, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to define the register address */ + uint8_t reg_addr = 0; + + /* Variable to set the re-mapped x-axes in the sensor */ + uint8_t x_axis = 0; + + /* Variable to set the re-mapped y-axes in the sensor */ + uint8_t y_axis = 0; + + /* Variable to set the re-mapped z-axes in the sensor */ + uint8_t z_axis = 0; + + /* Variable to set the re-mapped x-axes sign in the sensor */ + uint8_t x_axis_sign = 0; + + /* Variable to set the re-mapped y-axes sign in the sensor */ + uint8_t y_axis_sign = 0; + + /* Variable to set the re-mapped z-axes sign in the sensor */ + uint8_t z_axis_sign = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for axis re-mapping */ + struct bmi2_feature_config remap_config = { 0, 0, 0 }; + + /* Variable to get the status of advance power save */ + uint8_t aps_stat; + + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if enabled */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + if (rslt == BMI2_OK) + { + /* Search for axis-re-mapping and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&remap_config, BMI2_AXIS_MAP, dev); + if (feat_found) + { + /* Get the configuration from the page where axis re-mapping feature resides */ + rslt = bmi2_get_feat_config(remap_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes */ + idx = remap_config.start_addr; + + /* Set the value of re-mapped x-axis */ + x_axis = remap->x_axis & BMI2_X_AXIS_MASK; + + /* Set the value of re-mapped x-axis sign */ + x_axis_sign = ((remap->x_axis_sign << BMI2_X_AXIS_SIGN_POS) & BMI2_X_AXIS_SIGN_MASK); + + /* Set the value of re-mapped y-axis */ + y_axis = ((remap->y_axis << BMI2_Y_AXIS_POS) & BMI2_Y_AXIS_MASK); + + /* Set the value of re-mapped y-axis sign */ + y_axis_sign = ((remap->y_axis_sign << BMI2_Y_AXIS_SIGN_POS) & BMI2_Y_AXIS_SIGN_MASK); + + /* Set the value of re-mapped z-axis */ + z_axis = ((remap->z_axis << BMI2_Z_AXIS_POS) & BMI2_Z_AXIS_MASK); + + /* Set the value of re-mapped z-axis sign */ + z_axis_sign = remap->z_axis_sign & BMI2_Z_AXIS_SIGN_MASK; + + /* Arrange axes in the first byte */ + feat_config[idx] = x_axis | x_axis_sign | y_axis | y_axis_sign | z_axis; + + /* Increment the index */ + idx++; + + /* Cannot OR in the second byte since it holds + * gyroscope self-offset correction bit + */ + feat_config[idx] = BMI2_SET_BIT_POS0(feat_config[idx], BMI2_Z_AXIS_SIGN, z_axis_sign); + + /* Update the register address */ + reg_addr = BMI2_FEATURES_REG_ADDR + remap_config.start_addr; + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(reg_addr, &feat_config[remap_config.start_addr], 2, dev); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + /* Enable Advance power save if disabled while configuring and + * not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API clips the gyroscope cross-axis sensitivity within signed 16-bit limit + */ +static int16_t saturate(int32_t value, uint16_t saturation_val) +{ + int16_t retval = (int16_t)value; + + if (value > (int32_t)saturation_val) + { + retval = (int16_t)saturation_val; + } + else if (value < (-(int32_t)saturation_val - 1)) + { + retval = -(int16_t)saturation_val - 1; + } + + return retval; +} + +/*! + * @brief This internal API corrects the gyroscope cross-axis sensitivity + * between the z and the x axis. + */ +static void comp_gyro_cross_axis_sensitivity(struct bmi2_sens_axes_data *gyr_data, const struct bmi2_dev *dev) +{ + uint16_t int16_max = UINT16_C(32767); + + /* Get the compensated gyroscope x-axis */ + gyr_data->x = + saturate((int32_t)(gyr_data->x - (int16_t)(((int32_t) dev->gyr_cross_sens_zx * (int32_t) gyr_data->z) / 512)), + int16_max); +} + +/*! + * @brief This internal API is used to validate the boundary conditions. + */ +static int8_t check_boundary_val(uint8_t *val, uint8_t min, uint8_t max, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + if (val != NULL) + { + /* Check if value is below minimum value */ + if (*val < min) + { + /* Auto correct the invalid value to minimum value */ + *val = min; + dev->info |= BMI2_I_MIN_VALUE; + } + + /* Check if value is above maximum value */ + if (*val > max) + { + /* Auto correct the invalid value to maximum value */ + *val = max; + dev->info |= BMI2_I_MAX_VALUE; + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This internal API saves the configurations before performing FOC. + */ +static int8_t save_accel_foc_config(struct bmi2_accel_config *acc_cfg, + uint8_t *aps, + uint8_t *acc_en, + struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to get the status from PWR_CTRL register */ + uint8_t pwr_ctrl_data = 0; + + /* Get accelerometer configurations to be saved */ + rslt = get_accel_config(acc_cfg, dev); + if (rslt == BMI2_OK) + { + /* Get accelerometer enable status to be saved */ + rslt = bmi2_get_regs(BMI2_PWR_CTRL_ADDR, &pwr_ctrl_data, 1, dev); + *acc_en = BMI2_GET_BITS(pwr_ctrl_data, BMI2_ACC_EN); + + /* Get advance power save mode to be saved */ + if (rslt == BMI2_OK) + { + rslt = bmi2_get_adv_power_save(aps, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal sets configurations for performing accelerometer FOC. + */ +static int8_t set_accel_foc_config(struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to select the sensor */ + uint8_t sens_list = BMI2_ACCEL; + + /* Variable to set the accelerometer configuration value */ + uint8_t acc_conf_data = BMI2_FOC_ACC_CONF_VAL; + + /* Disabling offset compensation */ + rslt = bmi2_set_accel_offset_comp(BMI2_DISABLE, dev); + if (rslt == BMI2_OK) + { + /* Set accelerometer configurations to 50Hz, continuous mode, CIC mode */ + rslt = bmi2_set_regs(BMI2_ACC_CONF_ADDR, &acc_conf_data, 1, dev); + if (rslt == BMI2_OK) + { + /* Set accelerometer to normal mode by enabling it */ + rslt = bmi2_sensor_enable(&sens_list, 1, dev); + + if (rslt == BMI2_OK) + { + /* Disable advance power save mode */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This internal API performs Fast Offset Compensation for accelerometer. + */ +static int8_t perform_accel_foc(const struct bmi2_accel_foc_g_value *accel_g_value, + const struct bmi2_accel_config *acc_cfg, + struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_E_INVALID_STATUS; + + /* Variable to define count */ + uint8_t loop; + + /* Variable to store status read from the status register */ + uint8_t reg_status = 0; + + /* Array of structure to store accelerometer data */ + struct bmi2_sens_axes_data accel_value[128] = { { 0 } }; + + /* Structure to store accelerometer data temporarily */ + struct bmi2_foc_temp_value temp = { 0, 0, 0 }; + + /* Structure to store the average of accelerometer data */ + struct bmi2_sens_axes_data accel_avg = { 0, 0, 0, 0 }; + + /* Variable to define LSB per g value */ + uint16_t lsb_per_g = 0; + + /* Variable to define range */ + uint8_t range = 0; + + /* Structure to store accelerometer data deviation from ideal value */ + struct bmi2_offset_delta delta = { 0, 0, 0 }; + + /* Structure to store accelerometer offset values */ + struct bmi2_accel_offset offset = { 0, 0, 0 }; + + /* Variable tries max 5 times for interrupt then generates timeout */ + uint8_t try_cnt; + + for (loop = 0; loop < 128; loop++) + { + try_cnt = 5; + while (try_cnt && (!(reg_status & BMI2_DRDY_ACC))) + { + /* 20ms delay for 50Hz ODR */ + dev->delay_us(20000, dev->intf_ptr); + rslt = bmi2_get_status(®_status, dev); + try_cnt--; + } + + if ((rslt == BMI2_OK) && (reg_status & BMI2_DRDY_ACC)) + { + rslt = read_accel_xyz(&accel_value[loop], dev); + } + + if (rslt == BMI2_OK) + { + rslt = read_accel_xyz(&accel_value[loop], dev); + } + + if (rslt == BMI2_OK) + { + /* Store the data in a temporary structure */ + temp.x = temp.x + (int32_t)accel_value[loop].x; + temp.y = temp.y + (int32_t)accel_value[loop].y; + temp.z = temp.z + (int32_t)accel_value[loop].z; + } + else + { + break; + } + } + + if (rslt == BMI2_OK) + { + /* Take average of x, y and z data for lesser noise */ + accel_avg.x = (int16_t)(temp.x / 128); + accel_avg.y = (int16_t)(temp.y / 128); + accel_avg.z = (int16_t)(temp.z / 128); + + /* Get the exact range value */ + map_accel_range(acc_cfg->range, &range); + + /* Get the smallest possible measurable acceleration level given the range and + * resolution */ + lsb_per_g = (uint16_t)(power(2, dev->resolution) / (2 * range)); + + /* Compensate acceleration data against gravity */ + comp_for_gravity(lsb_per_g, accel_g_value, &accel_avg, &delta); + + /* Scale according to offset register resolution */ + scale_accel_offset(range, &delta, &offset); + + /* Invert the accelerometer offset data */ + invert_accel_offset(&offset); + + /* Write offset data in the offset compensation register */ + rslt = write_accel_offset(&offset, dev); + + /* Enable offset compensation */ + if (rslt == BMI2_OK) + { + rslt = bmi2_set_accel_offset_comp(BMI2_ENABLE, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API enables/disables the offset compensation for + * filtered and un-filtered accelerometer data. + */ +int8_t bmi2_set_accel_offset_comp(uint8_t offset_en, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t data = 0; + + /* Enable/Disable offset compensation */ + rslt = bmi2_get_regs(BMI2_NV_CONF_ADDR, &data, 1, dev); + if (rslt == BMI2_OK) + { + data = BMI2_SET_BITS(data, BMI2_NV_ACC_OFFSET, offset_en); + rslt = bmi2_set_regs(BMI2_NV_CONF_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This internal API reads the accelerometer offset compensation value. + */ +int8_t bmi2_get_accel_offset_comp(uint8_t *accel_offset, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + uint8_t count = 0; + + while (count < 3) + { + rslt = bmi2_get_regs(BMI2_ACC_OFF_COMP_0_ADDR + count, accel_offset + count, 1, dev); + if (rslt != BMI2_OK) + { + break; + } + + count++; + } + + return rslt; +} + +/*! + * @brief This internal API converts the accelerometer range value into + * corresponding integer value. + */ +static void map_accel_range(uint8_t range_in, uint8_t *range_out) +{ + switch (range_in) + { + case BMI2_ACC_RANGE_2G: + *range_out = 2; + break; + case BMI2_ACC_RANGE_4G: + *range_out = 4; + break; + case BMI2_ACC_RANGE_8G: + *range_out = 8; + break; + case BMI2_ACC_RANGE_16G: + *range_out = 16; + break; + default: + + /* By default RANGE 8G is set */ + *range_out = 8; + break; + } +} + +/*! + * @brief This internal API compensate the accelerometer data against gravity. + */ +static void comp_for_gravity(uint16_t lsb_per_g, + const struct bmi2_accel_foc_g_value *g_val, + const struct bmi2_sens_axes_data *data, + struct bmi2_offset_delta *comp_data) +{ + /* Array to store the accelerometer values in LSB */ + int16_t accel_value_lsb[3] = { 0 }; + + /* Convert g-value to LSB */ + accel_value_lsb[BMI2_X_AXIS] = (int16_t)(lsb_per_g * g_val->x); + accel_value_lsb[BMI2_Y_AXIS] = (int16_t)(lsb_per_g * g_val->y); + accel_value_lsb[BMI2_Z_AXIS] = (int16_t)(lsb_per_g * g_val->z); + + /* Get the compensated values for X, Y and Z axis */ + comp_data->x = (data->x - accel_value_lsb[BMI2_X_AXIS]); + comp_data->y = (data->y - accel_value_lsb[BMI2_Y_AXIS]); + comp_data->z = (data->z - accel_value_lsb[BMI2_Z_AXIS]); +} + +/*! + * @brief This internal API scales the compensated accelerometer data according + * to the offset register resolution. + * + * @note The bit position is always greater than 0 since accelerometer data is + * 16 bit wide. + */ +static void scale_accel_offset(uint8_t range, const struct bmi2_offset_delta *comp_data, struct bmi2_accel_offset *data) +{ + /* Variable to store the position of bit having 3.9mg resolution */ + int8_t bit_pos_3_9mg; + + /* Variable to store the position previous of bit having 3.9mg resolution */ + int8_t bit_pos_3_9mg_prev_bit; + + /* Variable to store the round-off value */ + uint8_t round_off; + + /* Find the bit position of 3.9mg */ + bit_pos_3_9mg = get_bit_pos_3_9mg(range); + + /* Round off, consider if the next bit is high */ + bit_pos_3_9mg_prev_bit = bit_pos_3_9mg - 1; + round_off = (uint8_t)(power(2, ((uint8_t) bit_pos_3_9mg_prev_bit))); + + /* Scale according to offset register resolution */ + data->x = (uint8_t)((comp_data->x + round_off) / power(2, ((uint8_t) bit_pos_3_9mg))); + data->y = (uint8_t)((comp_data->y + round_off) / power(2, ((uint8_t) bit_pos_3_9mg))); + data->z = (uint8_t)((comp_data->z + round_off) / power(2, ((uint8_t) bit_pos_3_9mg))); +} + +/*! + * @brief This internal API finds the bit position of 3.9mg according to given + * range and resolution. + */ +static int8_t get_bit_pos_3_9mg(uint8_t range) +{ + /* Variable to store the bit position of 3.9mg resolution */ + int8_t bit_pos_3_9mg; + + /* Variable to shift the bits according to the resolution */ + uint32_t divisor = 1; + + /* Scaling factor to get the bit position of 3.9 mg resolution */ + int16_t scale_factor = -1; + + /* Variable to store temporary value */ + uint16_t temp; + + /* Shift left by the times of resolution */ + divisor = divisor << 16; + + /* Get the bit position to be shifted */ + temp = (uint16_t)(divisor / (range * 256)); + + /* Get the scaling factor until bit position is shifted to last bit */ + while (temp != 1) + { + scale_factor++; + temp = temp >> 1; + } + + /* Scaling factor is the bit position of 3.9 mg resolution */ + bit_pos_3_9mg = (int8_t) scale_factor; + + return bit_pos_3_9mg; +} + +/*! + * @brief This internal API inverts the accelerometer offset data. + */ +static void invert_accel_offset(struct bmi2_accel_offset *offset_data) +{ + /* Get the offset data */ + offset_data->x = (uint8_t)((offset_data->x) * (-1)); + offset_data->y = (uint8_t)((offset_data->y) * (-1)); + offset_data->z = (uint8_t)((offset_data->z) * (-1)); +} + +/*! + * @brief This internal API writes the offset data in the offset compensation + * register. + */ +static int8_t write_accel_offset(const struct bmi2_accel_offset *offset, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to store the offset data */ + uint8_t data_array[3] = { 0 }; + + data_array[0] = offset->x; + data_array[1] = offset->y; + data_array[2] = offset->z; + + /* Offset values are written in the offset register */ + rslt = bmi2_set_regs(BMI2_ACC_OFF_COMP_0_ADDR, data_array, 3, dev); + + return rslt; +} + +/*! + * @brief This internal API restores the configurations saved before performing + * accelerometer FOC. + */ +static int8_t restore_accel_foc_config(struct bmi2_accel_config *acc_cfg, + uint8_t aps, + uint8_t acc_en, + struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to get the status from PWR_CTRL register */ + uint8_t pwr_ctrl_data = 0; + + /* Restore the saved accelerometer configurations */ + rslt = set_accel_config(acc_cfg, dev); + if (rslt == BMI2_OK) + { + /* Restore the saved accelerometer enable status */ + rslt = bmi2_get_regs(BMI2_PWR_CTRL_ADDR, &pwr_ctrl_data, 1, dev); + if (rslt == BMI2_OK) + { + pwr_ctrl_data = BMI2_SET_BITS(pwr_ctrl_data, BMI2_ACC_EN, acc_en); + rslt = bmi2_set_regs(BMI2_PWR_CTRL_ADDR, &pwr_ctrl_data, 1, dev); + + /* Restore the saved advance power save */ + if (rslt == BMI2_OK) + { + rslt = bmi2_set_adv_power_save(aps, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This internal API sets accelerometer configurations like ODR, + * bandwidth, performance mode and g-range. + */ +static int8_t set_accel_config(struct bmi2_accel_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data; + + /* Array to store the default value of accelerometer configuration + * reserved registers + */ + uint8_t data_array[2] = { 0 }; + + /* Validate bandwidth and performance mode */ + rslt = validate_bw_perf_mode(&config->bwp, &config->filter_perf, dev); + if (rslt == BMI2_OK) + { + /* Validate ODR and range */ + rslt = validate_odr_range(&config->odr, &config->range, dev); + if (rslt == BMI2_OK) + { + /* Set accelerometer performance mode */ + reg_data = BMI2_SET_BITS(data_array[0], BMI2_ACC_FILTER_PERF_MODE, config->filter_perf); + + /* Set accelerometer bandwidth */ + reg_data = BMI2_SET_BITS(reg_data, BMI2_ACC_BW_PARAM, config->bwp); + + /* Set accelerometer ODR */ + reg_data = BMI2_SET_BIT_POS0(reg_data, BMI2_ACC_ODR, config->odr); + + /* Copy the register data to the array */ + data_array[0] = reg_data; + + /* Set accelerometer range */ + reg_data = BMI2_SET_BIT_POS0(data_array[1], BMI2_ACC_RANGE, config->range); + + /* Copy the register data to the array */ + data_array[1] = reg_data; + + /* Write accelerometer configuration to ACC_CONFand + * ACC_RANGE registers simultaneously as they lie in consecutive places + */ + rslt = bmi2_set_regs(BMI2_ACC_CONF_ADDR, data_array, 2, dev); + + /* Get error status to check for invalid configurations */ + if (rslt == BMI2_OK) + { + rslt = cfg_error_status(dev); + } + } + } + + return rslt; +} + +/*! + * @brief This internal API sets gyroscope configurations like ODR, bandwidth, + * low power/high performance mode, performance mode and range. It also + * maps/un-maps data interrupts to that of hardware interrupt line. + */ +static int8_t set_gyro_config(struct bmi2_gyro_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store data */ + uint8_t reg_data; + + /* Array to store the default value of gyroscope configuration reserved registers */ + uint8_t data_array[2] = { 0 }; + + /* Validate gyroscope configurations */ + rslt = validate_gyro_config(config, dev); + if (rslt == BMI2_OK) + { + /* Set gyroscope performance mode */ + reg_data = BMI2_SET_BITS(data_array[0], BMI2_GYR_FILTER_PERF_MODE, config->filter_perf); + + /* Set gyroscope noise performance mode */ + reg_data = BMI2_SET_BITS(reg_data, BMI2_GYR_NOISE_PERF_MODE, config->noise_perf); + + /* Set gyroscope bandwidth */ + reg_data = BMI2_SET_BITS(reg_data, BMI2_GYR_BW_PARAM, config->bwp); + + /* Set gyroscope ODR */ + reg_data = BMI2_SET_BIT_POS0(reg_data, BMI2_GYR_ODR, config->odr); + + /* Copy the register data to the array */ + data_array[0] = reg_data; + + /* Set gyroscope OIS range */ + reg_data = BMI2_SET_BITS(data_array[1], BMI2_GYR_OIS_RANGE, config->ois_range); + + /* Set gyroscope range */ + reg_data = BMI2_SET_BIT_POS0(reg_data, BMI2_GYR_RANGE, config->range); + + /* Copy the register data to the array */ + data_array[1] = reg_data; + + /* Write accelerometer configuration to GYR_CONF and GYR_RANGE + * registers simultaneously as they lie in consecutive places + */ + rslt = bmi2_set_regs(BMI2_GYR_CONF_ADDR, data_array, 2, dev); + + /* Get error status to check for invalid configurations */ + if (rslt == BMI2_OK) + { + rslt = cfg_error_status(dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API saves the configurations before performing gyroscope + * FOC. + */ +static int8_t save_gyro_config(struct bmi2_gyro_config *gyr_cfg, uint8_t *aps, uint8_t *gyr_en, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to get the status from PWR_CTRL register */ + uint8_t pwr_ctrl_data = 0; + + /* Get gyroscope configurations to be saved */ + rslt = get_gyro_config(gyr_cfg, dev); + if (rslt == BMI2_OK) + { + /* Get gyroscope enable status to be saved */ + rslt = bmi2_get_regs(BMI2_PWR_CTRL_ADDR, &pwr_ctrl_data, 1, dev); + *gyr_en = BMI2_GET_BITS(pwr_ctrl_data, BMI2_GYR_EN); + + /* Get advance power save mode to be saved */ + if (rslt == BMI2_OK) + { + rslt = bmi2_get_adv_power_save(aps, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal sets configurations for performing gyroscope FOC. + */ +static int8_t set_gyro_foc_config(struct bmi2_dev *dev) +{ + int8_t rslt; + + /* Variable to select the sensor */ + uint8_t sens_list = BMI2_GYRO; + + /* Array to set the gyroscope configuration value (ODR, Performance mode + * and bandwidth) and gyroscope range + */ + uint8_t gyr_conf_data[2] = { BMI2_FOC_GYR_CONF_VAL, BMI2_GYR_RANGE_2000 }; + + /* Disabling gyroscope offset compensation */ + rslt = bmi2_set_gyro_offset_comp(BMI2_DISABLE, dev); + if (rslt == BMI2_OK) + { + /* Set gyroscope configurations to 25Hz, continuous mode, + * CIC mode, and 2000 dps range + */ + rslt = bmi2_set_regs(BMI2_GYR_CONF_ADDR, gyr_conf_data, 2, dev); + if (rslt == BMI2_OK) + { + /* Set gyroscope to normal mode by enabling it */ + rslt = bmi2_sensor_enable(&sens_list, 1, dev); + + if (rslt == BMI2_OK) + { + /* Disable advance power save mode */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This internal API inverts the gyroscope offset data. + */ +static void invert_gyro_offset(struct bmi2_sens_axes_data *offset_data) +{ + /* Invert the values */ + offset_data->x = (int16_t)((offset_data->x) * (-1)); + offset_data->y = (int16_t)((offset_data->y) * (-1)); + offset_data->z = (int16_t)((offset_data->z) * (-1)); +} + +/*! + * @brief This internal API restores the gyroscope configurations saved + * before performing FOC. + */ +static int8_t restore_gyro_config(struct bmi2_gyro_config *gyr_cfg, uint8_t aps, uint8_t gyr_en, struct bmi2_dev *dev) +{ + int8_t rslt; + uint8_t pwr_ctrl_data = 0; + + /* Restore the saved gyroscope configurations */ + rslt = set_gyro_config(gyr_cfg, dev); + if (rslt == BMI2_OK) + { + /* Restore the saved gyroscope enable status */ + rslt = bmi2_get_regs(BMI2_PWR_CTRL_ADDR, &pwr_ctrl_data, 1, dev); + if (rslt == BMI2_OK) + { + pwr_ctrl_data = BMI2_SET_BITS(pwr_ctrl_data, BMI2_GYR_EN, gyr_en); + rslt = bmi2_set_regs(BMI2_PWR_CTRL_ADDR, &pwr_ctrl_data, 1, dev); + + /* Restore the saved advance power save */ + if (rslt == BMI2_OK) + { + rslt = bmi2_set_adv_power_save(aps, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This internal API saturates the gyroscope data value before writing to + * to 10 bit offset register. + */ +static void saturate_gyro_data(struct bmi2_sens_axes_data *gyr_off) +{ + if (gyr_off->x > 511) + { + gyr_off->x = 511; + } + + if (gyr_off->x < -512) + { + gyr_off->x = -512; + } + + if (gyr_off->y > 511) + { + gyr_off->y = 511; + } + + if (gyr_off->y < -512) + { + gyr_off->y = -512; + } + + if (gyr_off->z > 511) + { + gyr_off->z = 511; + } + + if (gyr_off->z < -512) + { + gyr_off->z = -512; + } +} + +/*! + * @brief This internal API is used to validate the device structure pointer for + * null conditions. + */ +static int8_t null_ptr_check(const struct bmi2_dev *dev) +{ + int8_t rslt = BMI2_OK; + + if ((dev == NULL) || (dev->read == NULL) || (dev->write == NULL) || (dev->delay_us == NULL)) + { + /* Device structure pointer is not valid */ + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This internal API is to get the status of st_status from gry_crt_conf register + */ +static int8_t get_st_running(uint8_t *st_status, struct bmi2_dev *dev) +{ + int8_t rslt; + uint8_t reg_data = 0; + + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Get the status of crt running */ + rslt = bmi2_get_regs(BMI2_GYR_CRT_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + (*st_status) = BMI2_GET_BITS(reg_data, BMI2_GYR_CRT_RUNNING); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API enables/disables the CRT running. + */ +static int8_t set_st_running(uint8_t st_status, struct bmi2_dev *dev) +{ + int8_t rslt; + uint8_t reg_data = 0; + + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + rslt = bmi2_get_regs(BMI2_GYR_CRT_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + reg_data = BMI2_SET_BITS(reg_data, BMI2_GYR_CRT_RUNNING, st_status); + rslt = bmi2_set_regs(BMI2_GYR_CRT_CONF_ADDR, ®_data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API gets the status of rdy for dl bit. + */ +static int8_t get_rdy_for_dl(uint8_t *rdy_for_dl, struct bmi2_dev *dev) +{ + int8_t rslt; + uint8_t reg_data = 0; + + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Get the status of rdy_fo_dl */ + rslt = bmi2_get_regs(BMI2_GYR_CRT_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + (*rdy_for_dl) = BMI2_GET_BITS(reg_data, BMI2_GYR_RDY_FOR_DL); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API does the crt process if max burst length is not zero. + */ +static int8_t process_crt_download(uint8_t last_byte_flag, struct bmi2_dev *dev) +{ + int8_t rslt; + uint8_t rdy_for_dl = 0; + uint8_t cmd = BMI2_G_TRIGGER_CMD; + + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + rslt = get_rdy_for_dl(&rdy_for_dl, dev); + } + + /* Trigger next CRT command */ + if (rslt == BMI2_OK) + { + rslt = bmi2_set_regs(BMI2_CMD_REG_ADDR, &cmd, 1, dev); + } + + if ((!last_byte_flag) && (rslt == BMI2_OK)) + { + rslt = wait_rdy_for_dl_toggle(BMI2_CRT_READY_FOR_DOWNLOAD_RETRY, rdy_for_dl, dev); + } + + return rslt; +} + +/*! + * @brief This API to write the 2kb size of crt configuration + */ +static int8_t write_crt_config_file(uint16_t write_len, + uint16_t config_file_size, + uint16_t start_index, + struct bmi2_dev *dev) +{ + int8_t rslt = BMI2_OK; + uint16_t index = 0; + uint8_t last_byte_flag = 0; + uint8_t remain = (uint8_t)(config_file_size % write_len); + uint16_t balance_byte = 0; + + if (!remain) + { + + /* Write the configuration file */ + for (index = start_index; + (index < (start_index + config_file_size)) && (rslt == BMI2_OK); + index += write_len) + { + rslt = upload_file((dev->config_file_ptr + index), index, write_len, dev); + if (index >= ((start_index + config_file_size) - (write_len))) + { + last_byte_flag = 1; + } + + if (rslt == BMI2_OK) + { + rslt = process_crt_download(last_byte_flag, dev); + } + } + } + else + { + /* Get the balance bytes */ + balance_byte = (uint16_t)start_index + (uint16_t)config_file_size - (uint16_t)remain; + + /* Write the configuration file for the balance bytes */ + for (index = start_index; (index < balance_byte) && (rslt == BMI2_OK); index += write_len) + { + rslt = upload_file((dev->config_file_ptr + index), index, write_len, dev); + if (rslt == BMI2_OK) + { + rslt = process_crt_download(last_byte_flag, dev); + } + } + + if (rslt == BMI2_OK) + { + /* Write the remaining bytes in 2 bytes length */ + write_len = 2; + rslt = set_maxburst_len(write_len, dev); + + /* Write the configuration file for the remaining bytes */ + for (index = balance_byte; + (index < (start_index + config_file_size)) && (rslt == BMI2_OK); + index += write_len) + { + rslt = upload_file((dev->config_file_ptr + index), index, write_len, dev); + if (index < ((start_index + config_file_size) - write_len)) + { + last_byte_flag = 1; + } + + if (rslt == BMI2_OK) + { + rslt = process_crt_download(last_byte_flag, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API is to wait till the rdy for dl bit toggles after every pack of bytes. + */ +static int8_t wait_rdy_for_dl_toggle(uint8_t retry_complete, uint8_t download_ready, struct bmi2_dev *dev) +{ + int8_t rslt = BMI2_OK; + uint8_t dl_ready = 0; + uint8_t st_status = 0; + + while ((rslt == BMI2_OK) && (retry_complete--)) + { + rslt = get_rdy_for_dl(&dl_ready, dev); + if (download_ready != dl_ready) + { + break; + } + + dev->delay_us(BMI2_CRT_READY_FOR_DOWNLOAD_US, dev->intf_ptr); + } + + if ((rslt == BMI2_OK) && (download_ready == dl_ready)) + { + rslt = BMI2_E_CRT_READY_FOR_DL_FAIL_ABORT; + } + + if (rslt == BMI2_OK) + { + rslt = get_st_running(&st_status, dev); + if ((rslt == BMI2_OK) && (st_status == 0)) + { + rslt = BMI2_E_ST_ALREADY_RUNNING; + } + } + + return rslt; +} + +/*! + * @brief This API is to wait till crt status complete. + */ +static int8_t wait_st_running(uint8_t retry_complete, struct bmi2_dev *dev) +{ + uint8_t st_status = 1; + int8_t rslt = BMI2_OK; + + while (retry_complete--) + { + rslt = get_st_running(&st_status, dev); + if ((rslt == BMI2_OK) && (st_status == 0)) + { + break; + } + + dev->delay_us(BMI2_CRT_WAIT_RUNNING_US, dev->intf_ptr); + } + + if ((rslt == BMI2_OK) && (st_status == 1)) + { + rslt = BMI2_E_ST_ALREADY_RUNNING; + } + + return rslt; +} + +/*! + * @brief This api is used to perform gyroscope self-test. + */ +int8_t bmi2_do_gyro_st(struct bmi2_dev *dev) +{ + int8_t rslt; + + rslt = do_gtrigger_test(BMI2_SELECT_GYRO_SELF_TEST, dev); + + return rslt; +} + +/*! + * @brief This API is to run the CRT process for both max burst length 0 and non zero condition. + */ +int8_t bmi2_do_crt(struct bmi2_dev *dev) +{ + int8_t rslt; + + rslt = do_gtrigger_test(BMI2_SELECT_CRT, dev); + + return rslt; +} + +/*! + * @brief This API is to run the crt process for both max burst length 0 and non zero condition. + */ +static int8_t do_gtrigger_test(uint8_t gyro_st_crt, struct bmi2_dev *dev) +{ + int8_t rslt; + uint8_t st_status = 0; + uint8_t max_burst_length = 0; + struct bmi2_gyro_self_test_status gyro_st_result = { 0 }; + + /* Variable to get the status of advance power save */ + uint8_t aps_stat = 0; + + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Check if the variant supports this feature */ + if (dev->variant_feature & BMI2_CRT_RTOSK_ENABLE) + { + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if enabled */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + /* Get max burst length */ + if (rslt == BMI2_OK) + { + rslt = get_maxburst_len(&max_burst_length, dev); + } + + /* Checking for CRT running status */ + if (rslt == BMI2_OK) + { + rslt = get_st_running(&st_status, dev); + } + + /* CRT is not running and Max burst length is zero */ + if (st_status == 0) + { + rslt = gyro_crt_test(max_burst_length, gyro_st_crt, dev); + } + else + { + rslt = BMI2_E_ST_ALREADY_RUNNING; + } + + if (rslt == BMI2_OK) + { + if (gyro_st_crt == BMI2_SELECT_GYRO_SELF_TEST) + { + rslt = gyro_self_test_completed(&gyro_st_result, dev); + } + } + + /* Enable Advance power save if disabled while configuring and + * not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + } + + return rslt; +} + +/*! + * @brief This API to set up environment for processing the crt. + */ +static int8_t crt_prepare_setup(struct bmi2_dev *dev) +{ + int8_t rslt; + + /* Variable to select the sensor */ + uint8_t sens_list = BMI2_GYRO; + + rslt = null_ptr_check(dev); + + if (rslt == BMI2_OK) + { + /* Disable gyroscope */ + rslt = bmi2_sensor_disable(&sens_list, 1, dev); + } + + /* Disable FIFO for all sensors */ + if (rslt == BMI2_OK) + { + rslt = bmi2_set_fifo_config(BMI2_FIFO_ALL_EN, BMI2_DISABLE, dev); + } + + if (rslt == BMI2_OK) + { + /* Enable accelerometer */ + sens_list = BMI2_ACCEL; + rslt = bmi2_sensor_enable(&sens_list, 1, dev); + } + + if (rslt == BMI2_OK) + { + /* Disable Abort after 1 msec */ + dev->delay_us(1000, dev->intf_ptr); + rslt = abort_bmi2(BMI2_DISABLE, dev); + } + + return rslt; +} + +/*! + * @brief This API is to update the CRT or gyro self-test final result. + */ +static int8_t crt_gyro_st_update_result(struct bmi2_dev *dev) +{ + int8_t rslt; + struct bmi2_gyr_user_gain_status user_gain_stat = { 0, 0, 0, 0 }; + + rslt = null_ptr_check(dev); + + /* CRT status has to be read from the config register map */ + if (rslt == BMI2_OK) + { + rslt = get_gyro_gain_update_status(&user_gain_stat, dev); + } + + if (rslt == BMI2_OK) + { + switch (user_gain_stat.g_trigger_status) + { + case BMI2_G_TRIGGER_NO_ERROR: + + /* CRT is successful - Reset the Max Burst Length */ + rslt = set_maxburst_len(0, dev); + break; + + case BMI2_G_TRIGGER_DL_ERROR: + + /* CRT is Download Error - Keep non zero value for Max Burst Length */ + rslt = set_maxburst_len(dev->read_write_len, dev); + if (rslt == BMI2_OK) + { + rslt = BMI2_E_DL_ERROR; + } + + break; + case BMI2_G_TRIGGER_ABORT_ERROR: + + /* Command is aborted either by host via the block bit or due to motion + * detection. Keep non zero value for Max Burst Length + */ + rslt = set_maxburst_len(dev->read_write_len, dev); + if (rslt == BMI2_OK) + { + rslt = BMI2_E_ABORT_ERROR; + } + + break; + + case BMI2_G_TRIGGER_PRECON_ERROR: + + /* Pre-condition to start the feature was not completed. */ + rslt = BMI2_E_PRECON_ERROR; + break; + + default: + rslt = BMI2_E_INVALID_STATUS; + + break; + } + } + + return rslt; +} + +/*! + * @brief This internal API gets the max burst length. + */ +static int8_t get_maxburst_len(uint8_t *max_burst_len, struct bmi2_dev *dev) +{ + int8_t rslt = BMI2_OK; + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + uint8_t idx = 0; + uint8_t feat_found = 0; + struct bmi2_feature_config maxburst_length_bytes = { 0, 0, 0 }; + uint8_t aps_stat; + + if ((dev->variant_feature & BMI2_CRT_IN_FIFO_NOT_REQ) != 0) + { + *max_burst_len = 0; + + return BMI2_OK; + } + + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if enabled */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + if (rslt == BMI2_OK) + { + /* Search for max burst length */ + feat_found = bmi2_extract_input_feat_config(&maxburst_length_bytes, BMI2_MAX_BURST_LEN, dev); + if (feat_found) + { + rslt = bmi2_get_feat_config(maxburst_length_bytes.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for max burst length */ + idx = maxburst_length_bytes.start_addr; + + /* Get the max burst length */ + *max_burst_len = feat_config[idx]; + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + /* Enable Advance power save if disabled while configuring and + * not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API sets the max burst length. + */ +static int8_t set_maxburst_len(const uint16_t write_len_byte, struct bmi2_dev *dev) +{ + int8_t rslt = BMI2_OK; + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + uint8_t idx = 0; + uint8_t reg_addr = 0; + uint8_t max_burst_len = 0; + uint8_t feat_found = 0; + struct bmi2_feature_config maxburst_length_bytes = { 0, 0, 0 }; + uint8_t aps_stat; + uint16_t burst_len = write_len_byte / 2; + + /* for variant that support crt outside fifo, do not modify the max burst len */ + if ((dev->variant_feature & BMI2_CRT_IN_FIFO_NOT_REQ) != 0) + { + return BMI2_OK; + } + + /* Max burst length is only 1 byte */ + if (burst_len > BMI2_CRT_MAX_BURST_WORD_LENGTH) + { + max_burst_len = UINT8_C(0xFF); + } + else + { + max_burst_len = (uint8_t)burst_len; + } + + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if enabled */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + if (rslt == BMI2_OK) + { + /* Search for axis-re-mapping and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&maxburst_length_bytes, BMI2_MAX_BURST_LEN, dev); + if (feat_found) + { + /* Get the configuration from the page where axis + * re-mapping feature resides + */ + rslt = bmi2_get_feat_config(maxburst_length_bytes.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes */ + idx = maxburst_length_bytes.start_addr; + + /* update Max burst length */ + feat_config[idx] = max_burst_len; + + /* Update the register address */ + reg_addr = BMI2_FEATURES_REG_ADDR + maxburst_length_bytes.start_addr; + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(reg_addr, &feat_config[maxburst_length_bytes.start_addr], 2, dev); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + /* Enable Advance power save if disabled while configuring and + * not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + + return rslt; +} + +/*! + * @brief This api is used to trigger the preparation for system for NVM programming. + */ +static int8_t set_nvm_prep_prog(uint8_t nvm_prep, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + uint8_t reg_addr = 0; + + /* Initialize feature configuration for nvm preparation*/ + struct bmi2_feature_config nvm_config = { 0, 0, 0 }; + + /* Search for bmi2 gyro self offset correction feature as nvm program preparation feature is + * present in the same Word and extract its configuration details + */ + feat_found = bmi2_extract_input_feat_config(&nvm_config, BMI2_NVM_PROG_PREP, dev); + if (feat_found) + { + /* Get the configuration from the page where nvm preparation feature enable feature + * resides */ + rslt = bmi2_get_feat_config(nvm_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for nvm preparation feature enable */ + idx = nvm_config.start_addr; + + /* update nvm_prog_prep enable bit */ + feat_config[idx] = BMI2_SET_BITS(feat_config[idx], BMI2_NVM_PREP_FEATURE_EN, nvm_prep); + + /* Update the register address */ + reg_addr = BMI2_FEATURES_REG_ADDR + nvm_config.start_addr - 1; + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(reg_addr, &feat_config[nvm_config.start_addr - 1], 2, dev); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This api is used to enable the CRT. + */ +static int8_t select_self_test(uint8_t gyro_st_crt, struct bmi2_dev *dev) +{ + int8_t rslt; + + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + uint8_t idx = 0; + + uint8_t feat_found; + uint8_t reg_addr = 0; + + struct bmi2_feature_config gyro_self_test_crt_config = { 0, 0, 0 }; + + /* Search for bmi2 crt gyro self-test feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&gyro_self_test_crt_config, BMI2_CRT_GYRO_SELF_TEST, dev); + if (feat_found) + { + /* Get the configuration from the page where gyro self-test and crt enable feature + * resides */ + rslt = bmi2_get_feat_config(gyro_self_test_crt_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes */ + idx = gyro_self_test_crt_config.start_addr; + + /* Update the gyro self-test crt enable bit */ + feat_config[idx] = BMI2_SET_BIT_POS0(feat_config[idx], BMI2_GYRO_SELF_TEST_CRT_EN, gyro_st_crt); + + /* Update the register address */ + reg_addr = BMI2_FEATURES_REG_ADDR + (gyro_self_test_crt_config.start_addr - 1); + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(reg_addr, &feat_config[gyro_self_test_crt_config.start_addr - 1], 2, dev); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This api is used to abort ongoing crt or gyro self-test. + */ +int8_t bmi2_abort_crt_gyro_st(struct bmi2_dev *dev) +{ + int8_t rslt = BMI2_OK; + uint8_t aps_stat; + uint8_t st_running = 0; + uint8_t cmd = BMI2_G_TRIGGER_CMD; + + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if enabled */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + /* Checking for ST running status */ + if (rslt == BMI2_OK) + { + rslt = get_st_running(&st_running, dev); + if (rslt == BMI2_OK) + { + /* ST is not running */ + if (st_running == 0) + { + rslt = BMI2_E_ST_NOT_RUNING; + } + } + } + + if (rslt == BMI2_OK) + { + rslt = abort_bmi2(BMI2_ENABLE, dev); + } + + /* send the g trigger command */ + if (rslt == BMI2_OK) + { + rslt = bmi2_set_regs(BMI2_CMD_REG_ADDR, &cmd, 1, dev); + } + + if (rslt == BMI2_OK) + { + /* wait until st_status = 0 or time out is 2 seconds */ + rslt = wait_st_running(BMI2_CRT_WAIT_RUNNING_RETRY_EXECUTION, dev); + } + + /* Check G trigger status for error */ + if (rslt == BMI2_OK) + { + rslt = crt_gyro_st_update_result(dev); + if (rslt == BMI2_E_ABORT_ERROR) + { + rslt = BMI2_OK; + } + else + { + rslt = BMI2_E_ABORT_ERROR; + } + } + + /* Enable Advance power save if disabled while configuring and + * not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + + return rslt; +} + +/*! + * @brief This api is used to enable/disable abort. + */ +static int8_t abort_bmi2(uint8_t abort_enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + uint8_t reg_addr = 0; + + /* Initialize feature configuration for blocking a feature */ + struct bmi2_feature_config block_config = { 0, 0, 0 }; + + /* Search for bmi2 Abort feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&block_config, BMI2_ABORT_CRT_GYRO_SELF_TEST, dev); + if (feat_found) + { + /* Get the configuration from the page where abort(block) feature resides */ + rslt = bmi2_get_feat_config(block_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes */ + idx = block_config.start_addr; + + /* update the gyro self-test crt abort enable bit */ + feat_config[idx] = BMI2_SET_BITS(feat_config[idx], BMI2_ABORT_FEATURE_EN, abort_enable); + + /* Update the register address */ + reg_addr = BMI2_FEATURES_REG_ADDR + (block_config.start_addr - 1); + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(reg_addr, &feat_config[block_config.start_addr - 1], 2, dev); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This api is use to wait till gyro self-test is completed and update the status of gyro + * self-test. + */ +static int8_t gyro_self_test_completed(struct bmi2_gyro_self_test_status *gyro_st_result, struct bmi2_dev *dev) +{ + int8_t rslt; + uint8_t reg_data; + + rslt = bmi2_get_regs(BMI2_GYR_SELF_TEST_AXES_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + gyro_st_result->gyr_st_axes_done = BMI2_GET_BIT_POS0(reg_data, BMI2_GYR_ST_AXES_DONE); + if (gyro_st_result->gyr_st_axes_done == 0x01) + { + gyro_st_result->gyr_axis_x_ok = BMI2_GET_BITS(reg_data, BMI2_GYR_AXIS_X_OK); + gyro_st_result->gyr_axis_y_ok = BMI2_GET_BITS(reg_data, BMI2_GYR_AXIS_Y_OK); + gyro_st_result->gyr_axis_z_ok = BMI2_GET_BITS(reg_data, BMI2_GYR_AXIS_Z_OK); + } + else + { + rslt = BMI2_E_SELF_TEST_NOT_DONE; + } + } + + return rslt; +} + +/*! @brief This api is used for programming the non volatile memory(nvm) */ +int8_t bmi2_nvm_prog(struct bmi2_dev *dev) +{ + int8_t rslt = BMI2_OK; + + /* Variable to get the status of advance power save */ + uint8_t aps_stat; + uint8_t status; + uint8_t cmd_rdy; + uint8_t reg_data; + uint8_t write_timeout = 100; + + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if enabled */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + /* Check the Write status and proceed only if there is no ongoing write cycle */ + if (rslt == BMI2_OK) + { + rslt = bmi2_get_status(&status, dev); + + cmd_rdy = BMI2_GET_BITS(status, BMI2_CMD_RDY); + if (cmd_rdy) + { + rslt = set_nvm_prep_prog(BMI2_ENABLE, dev); + if (rslt == BMI2_OK) + { + dev->delay_us(40000, dev->intf_ptr); + + /* Set the NVM_CONF.nvm_prog_en bit in order to enable the NVM + * programming */ + reg_data = BMI2_NVM_UNLOCK_ENABLE; + rslt = bmi2_set_regs(BMI2_NVM_CONF_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + /* Send NVM prog command to command register */ + reg_data = BMI2_NVM_PROG_CMD; + rslt = bmi2_set_regs(BMI2_CMD_REG_ADDR, ®_data, 1, dev); + } + + /* Wait till write operation is completed */ + if (rslt == BMI2_OK) + { + while (write_timeout--) + { + rslt = bmi2_get_status(&status, dev); + if (rslt == BMI2_OK) + { + cmd_rdy = BMI2_GET_BITS(status, BMI2_CMD_RDY); + + /* Nvm is complete once cmd_rdy is 1, break if 1 */ + if (cmd_rdy) + { + break; + } + + /* Wait till cmd_rdy becomes 1 indicating + * nvm process completes */ + dev->delay_us(20000, dev->intf_ptr); + } + } + } + + if ((rslt == BMI2_OK) && (cmd_rdy != BMI2_TRUE)) + { + rslt = BMI2_E_WRITE_CYCLE_ONGOING; + } + } + } + else + { + rslt = BMI2_E_WRITE_CYCLE_ONGOING; + } + } + + if (rslt == BMI2_OK) + { + /* perform soft reset */ + rslt = bmi2_soft_reset(dev); + } + + return rslt; +} + +/*! + * @brief This internal API extract the identification feature from the DMR page + * and retrieve the config file major and minor version. + */ +static int8_t extract_config_file(uint8_t *config_major, uint8_t *config_minor, struct bmi2_dev *dev) +{ + /* Variable to define the result */ + int8_t rslt = BMI2_OK; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to define LSB */ + uint16_t lsb = 0; + + /* Variable to define MSB */ + uint16_t msb = 0; + + /* Variable to define a word */ + uint16_t lsb_msb = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Variable to define advance power save mode status */ + uint8_t aps_stat; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Initialize feature configuration for config file identification */ + struct bmi2_feature_config config_id = { 0, 0, 0 }; + + /* Check the power mode status */ + aps_stat = dev->aps_status; + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if enabled */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + if (rslt == BMI2_OK) + { + /* Search for config file identification feature and extract its configuration + * details */ + feat_found = bmi2_extract_input_feat_config(&config_id, BMI2_CONFIG_ID, dev); + if (feat_found) + { + /* Get the configuration from the page where config file identification + * feature resides */ + rslt = bmi2_get_feat_config(config_id.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for config file identification */ + idx = config_id.start_addr; + + /* Get word to calculate config file identification */ + lsb = (uint16_t) feat_config[idx++]; + msb = ((uint16_t) feat_config[idx++] << 8); + lsb_msb = lsb | msb; + + /* Get major and minor version */ + *config_major = BMI2_GET_BITS(lsb_msb, BMI2_CONFIG_MAJOR); + *config_minor = BMI2_GET_BIT_POS0(lsb, BMI2_CONFIG_MINOR); + } + } + + /* Enable Advance power save if disabled while configuring and + * not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + *@brief This internal API is used to map the interrupts to the sensor. + */ +static void extract_feat_int_map(struct bmi2_map_int *map_int, uint8_t type, const struct bmi2_dev *dev) +{ + /* Variable to define loop */ + uint8_t loop = 0; + + /* Search for the interrupts from the input configuration array */ + while (loop < dev->sens_int_map) + { + if (dev->map_int[loop].type == type) + { + *map_int = dev->map_int[loop]; + break; + } + + loop++; + } +} + +/*! + * @brief This internal API gets the saturation status for the gyroscope user + * gain update. + */ +static int8_t get_gyro_gain_update_status(struct bmi2_gyr_user_gain_status *user_gain_stat, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variables to define index */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature output for gyroscope user gain status */ + struct bmi2_feature_config user_gain_cfg = { 0, 0, 0 }; + + /* Search for gyroscope user gain status output feature and extract its + * configuration details + */ + feat_found = extract_output_feat_config(&user_gain_cfg, BMI2_GYRO_GAIN_UPDATE, dev); + if (feat_found) + { + /* Get the feature output configuration for gyroscope user gain status */ + rslt = bmi2_get_feat_config(user_gain_cfg.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for gyroscope user gain status */ + idx = user_gain_cfg.start_addr; + + /* Get the saturation status for x-axis */ + user_gain_stat->sat_x = BMI2_GET_BIT_POS0(feat_config[idx], BMI2_GYR_USER_GAIN_SAT_STAT_X); + + /* Get the saturation status for y-axis */ + user_gain_stat->sat_y = BMI2_GET_BITS(feat_config[idx], BMI2_GYR_USER_GAIN_SAT_STAT_Y); + + /* Get the saturation status for z-axis */ + user_gain_stat->sat_z = BMI2_GET_BITS(feat_config[idx], BMI2_GYR_USER_GAIN_SAT_STAT_Z); + + /* Get g trigger status */ + user_gain_stat->g_trigger_status = BMI2_GET_BITS(feat_config[idx], BMI2_G_TRIGGER_STAT); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API is used to extract the output feature configuration + * details from the look-up table. + */ +static uint8_t extract_output_feat_config(struct bmi2_feature_config *feat_output, + uint8_t type, + const struct bmi2_dev *dev) +{ + /* Variable to define loop */ + uint8_t loop = 0; + + /* Variable to set flag */ + uint8_t feat_found = BMI2_FALSE; + + /* Search for the output feature from the output configuration array */ + while (loop < dev->out_sens) + { + if (dev->feat_output[loop].type == type) + { + *feat_output = dev->feat_output[loop]; + feat_found = BMI2_TRUE; + break; + } + + loop++; + } + + /* Return flag */ + return feat_found; +} + +/*! + * @brief This internal API gets the cross sensitivity coefficient between + * gyroscope's X and Z axes. + */ +static int8_t get_gyro_cross_sense(int16_t *cross_sense, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define index */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + uint8_t corr_fact_zx; + + /* Initialize feature output for gyroscope cross sensitivity */ + struct bmi2_feature_config cross_sense_out_config = { 0, 0, 0 }; + + if (dev->variant_feature & BMI2_MAXIMUM_FIFO_VARIANT) + { + /* For maximum_fifo variant fetch the correction factor from GPIO0 */ + rslt = bmi2_get_regs(BMI2_GYR_CAS_GPIO0_ADDR, &corr_fact_zx, 1, dev); + if (rslt == BMI2_OK) + { + /* Get the gyroscope cross sensitivity coefficient */ + if (corr_fact_zx & BMI2_GYRO_CROSS_AXES_SENSE_SIGN_BIT_MASK) + { + *cross_sense = (int16_t)(((int16_t)corr_fact_zx) - 128); + } + else + { + *cross_sense = (int16_t)(corr_fact_zx); + } + } + } + else + { + /* Search for gyroscope cross sensitivity feature and extract its configuration details */ + feat_found = extract_output_feat_config(&cross_sense_out_config, BMI2_GYRO_CROSS_SENSE, dev); + if (feat_found) + { + /* Get the feature output configuration for gyroscope cross sensitivity + * feature */ + rslt = bmi2_get_feat_config(cross_sense_out_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for gyroscope cross sensitivity output */ + idx = cross_sense_out_config.start_addr; + + /* discard the MSB as GYR_CAS is of only 7 bit */ + feat_config[idx] = feat_config[idx] & BMI2_GYRO_CROSS_AXES_SENSE_MASK; + + /* Get the gyroscope cross sensitivity coefficient */ + if (feat_config[idx] & BMI2_GYRO_CROSS_AXES_SENSE_SIGN_BIT_MASK) + { + *cross_sense = (int16_t)(((int16_t)feat_config[idx]) - 128); + } + else + { + *cross_sense = (int16_t)(feat_config[idx]); + } + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + } + + return rslt; +} + +/*! + * @brief This internal API selects the sensor/features to be enabled or + * disabled. + */ +static int8_t select_sensor(const uint8_t *sens_list, uint8_t n_sens, uint64_t *sensor_sel) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Variable to define loop */ + uint8_t count; + + for (count = 0; count < n_sens; count++) + { + switch (sens_list[count]) + { + case BMI2_ACCEL: + *sensor_sel |= BMI2_ACCEL_SENS_SEL; + break; + case BMI2_GYRO: + *sensor_sel |= BMI2_GYRO_SENS_SEL; + break; + case BMI2_AUX: + *sensor_sel |= BMI2_AUX_SENS_SEL; + break; + case BMI2_TEMP: + *sensor_sel |= BMI2_TEMP_SENS_SEL; + break; + default: + rslt = BMI2_E_INVALID_SENSOR; + break; + } + } + + return rslt; +} + +/*! + * @brief This internal API enables the selected sensor/features. + */ +static int8_t sensor_enable(uint64_t sensor_sel, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store register values */ + uint8_t reg_data = 0; + + rslt = bmi2_get_regs(BMI2_PWR_CTRL_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + /* Enable accelerometer */ + if (sensor_sel & BMI2_ACCEL_SENS_SEL) + { + reg_data = BMI2_SET_BITS(reg_data, BMI2_ACC_EN, BMI2_ENABLE); + } + + /* Enable gyroscope */ + if (sensor_sel & BMI2_GYRO_SENS_SEL) + { + reg_data = BMI2_SET_BITS(reg_data, BMI2_GYR_EN, BMI2_ENABLE); + } + + /* Enable auxiliary sensor */ + if (sensor_sel & BMI2_AUX_SENS_SEL) + { + reg_data = BMI2_SET_BIT_POS0(reg_data, BMI2_AUX_EN, BMI2_ENABLE); + } + + /* Enable temperature sensor */ + if (sensor_sel & BMI2_TEMP_SENS_SEL) + { + reg_data = BMI2_SET_BITS(reg_data, BMI2_TEMP_EN, BMI2_ENABLE); + } + + /* Enable the sensors that are set in the power control register */ + if (sensor_sel & BMI2_MAIN_SENSORS) + { + rslt = bmi2_set_regs(BMI2_PWR_CTRL_ADDR, ®_data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API disables the selected sensors/features. + */ +static int8_t sensor_disable(uint64_t sensor_sel, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store register values */ + uint8_t reg_data = 0; + + rslt = bmi2_get_regs(BMI2_PWR_CTRL_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + /* Disable accelerometer */ + if (sensor_sel & BMI2_ACCEL_SENS_SEL) + { + reg_data = BMI2_SET_BIT_VAL0(reg_data, BMI2_ACC_EN); + } + + /* Disable gyroscope */ + if (sensor_sel & BMI2_GYRO_SENS_SEL) + { + reg_data = BMI2_SET_BIT_VAL0(reg_data, BMI2_GYR_EN); + } + + /* Disable auxiliary sensor */ + if (sensor_sel & BMI2_AUX_SENS_SEL) + { + reg_data = BMI2_SET_BIT_VAL0(reg_data, BMI2_AUX_EN); + } + + /* Disable temperature sensor */ + if (sensor_sel & BMI2_TEMP_SENS_SEL) + { + reg_data = BMI2_SET_BIT_VAL0(reg_data, BMI2_TEMP_EN); + } + + /* Enable the sensors that are set in the power control register */ + if (sensor_sel & BMI2_MAIN_SENSORS) + { + rslt = bmi2_set_regs(BMI2_PWR_CTRL_ADDR, ®_data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API is used to test gyro CRT. + */ +static int8_t gyro_crt_test(uint8_t max_burst_length, uint8_t gyro_st_crt, struct bmi2_dev *dev) +{ + int8_t rslt; + int8_t rslt_crt = BMI2_OK; + uint8_t cmd = BMI2_G_TRIGGER_CMD; + uint8_t download_ready = 0; + + rslt = set_st_running(BMI2_ENABLE, dev); + + /* Preparing the setup */ + if (rslt == BMI2_OK) + { + rslt = crt_prepare_setup(dev); + } + + /* Enable the gyro self-test, CRT */ + if (rslt == BMI2_OK) + { + rslt = select_self_test(gyro_st_crt, dev); + } + + /* Check if FIFO is unchanged by checking the max burst length */ + if ((rslt == BMI2_OK) && (max_burst_length == 0)) + { + /* Trigger CRT */ + rslt = bmi2_set_regs(BMI2_CMD_REG_ADDR, &cmd, 1, dev); + if (rslt == BMI2_OK) + { + /* Wait until st_status = 0 or time out is 2 seconds */ + rslt = wait_st_running(BMI2_CRT_WAIT_RUNNING_RETRY_EXECUTION, dev); + + /* CRT Running wait & check is successful */ + if (rslt == BMI2_OK) + { + rslt = crt_gyro_st_update_result(dev); + } + } + } + else + { + /* FIFO may be used */ + if (rslt == BMI2_OK) + { + if (dev->read_write_len < 2) + { + dev->read_write_len = 2; + } + + if (dev->read_write_len > (BMI2_CRT_MAX_BURST_WORD_LENGTH * 2)) + { + dev->read_write_len = BMI2_CRT_MAX_BURST_WORD_LENGTH * 2; + } + + /* Reset the max burst length to default value */ + rslt = set_maxburst_len(dev->read_write_len, dev); + } + + if (rslt == BMI2_OK) + { + rslt = get_rdy_for_dl(&download_ready, dev); + } + + /* Trigger CRT */ + if (rslt == BMI2_OK) + { + rslt = bmi2_set_regs(BMI2_CMD_REG_ADDR, &cmd, 1, dev); + } + + /* Wait till either ready for download toggle or crt running = 0 */ + if (rslt == BMI2_OK) + { + rslt = wait_rdy_for_dl_toggle(BMI2_CRT_READY_FOR_DOWNLOAD_RETRY, download_ready, dev); + if (rslt == BMI2_OK) + { + rslt = write_crt_config_file(dev->read_write_len, BMI2_CRT_CONFIG_FILE_SIZE, 0x1800, dev); + } + + if (rslt == BMI2_OK) + { + rslt = wait_st_running(BMI2_CRT_WAIT_RUNNING_RETRY_EXECUTION, dev); + rslt_crt = crt_gyro_st_update_result(dev); + if (rslt == BMI2_OK) + { + rslt = rslt_crt; + } + } + } + } + + return rslt; +} + +/*! + * @brief This internal API verifies and allows only the correct position to do Fast Offset Compensation for + * accelerometer. + */ +static int8_t verify_foc_position(uint8_t sens_list, + const struct bmi2_accel_foc_g_value *accel_g_axis, + struct bmi2_dev *dev) +{ + /* Variable to store result of API */ + int8_t rslt; + + /* Structure to define accelerometer sensor axes */ + struct bmi2_sens_axes_data avg_foc_data = { 0 }; + + /* Structure to store temporary accelerometer values */ + struct bmi2_foc_temp_value temp_foc_data = { 0 }; + + rslt = get_average_of_sensor_data(sens_list, &temp_foc_data, dev); + + if (rslt == BMI2_OK) + { + if (sens_list == BMI2_ACCEL) + { + /* Taking modulus to make negative values as positive */ + if ((accel_g_axis->x == 1) && (accel_g_axis->sign == 1)) + { + temp_foc_data.x = temp_foc_data.x * BMI2_FOC_INVERT_VALUE; + } + else if ((accel_g_axis->y == 1) && (accel_g_axis->sign == 1)) + { + temp_foc_data.y = temp_foc_data.y * BMI2_FOC_INVERT_VALUE; + } + else if ((accel_g_axis->z == 1) && (accel_g_axis->sign == 1)) + { + temp_foc_data.z = temp_foc_data.z * BMI2_FOC_INVERT_VALUE; + } + } + + avg_foc_data.x = (int16_t)(temp_foc_data.x); + avg_foc_data.y = (int16_t)(temp_foc_data.y); + avg_foc_data.z = (int16_t)(temp_foc_data.z); + + rslt = validate_foc_position(sens_list, accel_g_axis, avg_foc_data, dev); + } + + return rslt; +} + +/*! + * @brief This internal API reads and provides average for 128 samples of sensor data for accel FOC. + */ +static int8_t get_average_of_sensor_data(uint8_t sens_list, + struct bmi2_foc_temp_value *temp_foc_data, + struct bmi2_dev *dev) +{ + /* Variable to store result of API */ + int8_t rslt; + + /* Structure to store sensor data */ + struct bmi2_sens_data sensor_data; + + uint8_t sample_count = 0; + uint8_t datardy_try_cnt; + uint8_t drdy_status = 0; + + rslt = null_ptr_check(dev); + + if (rslt == BMI2_OK) + { + /* Read sensor values before FOC */ + while (sample_count < BMI2_FOC_SAMPLE_LIMIT) + { + datardy_try_cnt = 5; + do + { + dev->delay_us(20000, dev->intf_ptr); + rslt = bmi2_get_status(&drdy_status, dev); + datardy_try_cnt--; + } while ((rslt == BMI2_OK) && (!(drdy_status)) && (datardy_try_cnt)); + + if ((rslt != BMI2_OK) || (datardy_try_cnt == 0)) + { + rslt = BMI2_E_DATA_RDY_INT_FAILED; + break; + } + + rslt = bmi2_get_sensor_data(&sensor_data, dev); + + if (rslt == BMI2_OK) + { + if (sens_list == BMI2_ACCEL) + { + temp_foc_data->x += sensor_data.acc.x; + temp_foc_data->y += sensor_data.acc.y; + temp_foc_data->z += sensor_data.acc.z; + } + else if (sens_list == BMI2_GYRO) + { + temp_foc_data->x += sensor_data.gyr.x; + temp_foc_data->y += sensor_data.gyr.y; + temp_foc_data->z += sensor_data.gyr.z; + } + } + else + { + break; + } + + sample_count++; + } + + if (rslt == BMI2_OK) + { + temp_foc_data->x = (temp_foc_data->x / BMI2_FOC_SAMPLE_LIMIT); + temp_foc_data->y = (temp_foc_data->y / BMI2_FOC_SAMPLE_LIMIT); + temp_foc_data->z = (temp_foc_data->z / BMI2_FOC_SAMPLE_LIMIT); + } + } + + return rslt; +} + +/*! + * @brief This internal API validates accel FOC position as per the range + */ +static int8_t validate_foc_position(uint8_t sens_list, + const struct bmi2_accel_foc_g_value *accel_g_axis, + struct bmi2_sens_axes_data avg_foc_data, + struct bmi2_dev *dev) +{ + /* Variable to store result of API */ + int8_t rslt = BMI2_E_INVALID_INPUT; + + if (sens_list == BMI2_ACCEL) + { + if (accel_g_axis->x == 1) + { + rslt = validate_foc_accel_axis(avg_foc_data.x, dev); + } + else if (accel_g_axis->y == 1) + { + rslt = validate_foc_accel_axis(avg_foc_data.y, dev); + } + else + { + rslt = validate_foc_accel_axis(avg_foc_data.z, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API validates accel FOC axis given as input + */ +static int8_t validate_foc_accel_axis(int16_t avg_foc_data, struct bmi2_dev *dev) +{ + /* Variable to store result of API */ + int8_t rslt; + + /* Structure to store sensor configurations */ + struct bmi2_sens_config sens_cfg = { 0 }; + + /* Variable to store accel range */ + uint8_t range; + + /* Assign the accel type */ + sens_cfg.type = BMI2_ACCEL; + + /* Get accel configurations */ + rslt = bmi2_get_sensor_config(&sens_cfg, 1, dev); + + if (rslt == BMI2_OK) + { + /* Assign accel range to variable */ + range = sens_cfg.cfg.acc.range; + + /* Reference LSB value of 2G */ + if ((range == BMI2_ACC_RANGE_2G) && (avg_foc_data > BMI2_ACC_2G_MIN_NOISE_LIMIT) && + (avg_foc_data < BMI2_ACC_2G_MAX_NOISE_LIMIT)) + { + rslt = BMI2_OK; + } + /* Reference LSB value of 4G */ + else if ((range == BMI2_ACC_RANGE_4G) && (avg_foc_data > BMI2_ACC_4G_MIN_NOISE_LIMIT) && + (avg_foc_data < BMI2_ACC_4G_MAX_NOISE_LIMIT)) + { + rslt = BMI2_OK; + } + /* Reference LSB value of 8G */ + else if ((range == BMI2_ACC_RANGE_8G) && (avg_foc_data > BMI2_ACC_8G_MIN_NOISE_LIMIT) && + (avg_foc_data < BMI2_ACC_8G_MAX_NOISE_LIMIT)) + { + rslt = BMI2_OK; + } + /* Reference LSB value of 16G */ + else if ((range == BMI2_ACC_RANGE_16G) && (avg_foc_data > BMI2_ACC_16G_MIN_NOISE_LIMIT) && + (avg_foc_data < BMI2_ACC_16G_MAX_NOISE_LIMIT)) + { + rslt = BMI2_OK; + } + else + { + rslt = BMI2_E_INVALID_FOC_POSITION; + } + } + + return rslt; +} + +/*! @endcond */ diff --git a/projects/Compass/main/src/bmi270.c b/projects/Compass/main/src/bmi270.c new file mode 100644 index 00000000..20f27bb0 --- /dev/null +++ b/projects/Compass/main/src/bmi270.c @@ -0,0 +1,4488 @@ +/** +* Copyright (c) 2023 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 bmi270.c +* @date 2023-05-03 +* @version v2.86.1 +* +*/ + +/***************************************************************************/ + +/*! Header files + ****************************************************************************/ +#include "bmi270.h" + +/***************************************************************************/ + +/*! Global Variable + ****************************************************************************/ + +/*! @name Global array that stores the configuration file of BMI270 */ +const uint8_t bmi270_config_file[] = { + 0xc8, 0x2e, 0x00, 0x2e, 0x80, 0x2e, 0x3d, 0xb1, 0xc8, 0x2e, 0x00, 0x2e, 0x80, 0x2e, 0x91, 0x03, 0x80, 0x2e, 0xbc, + 0xb0, 0x80, 0x2e, 0xa3, 0x03, 0xc8, 0x2e, 0x00, 0x2e, 0x80, 0x2e, 0x00, 0xb0, 0x50, 0x30, 0x21, 0x2e, 0x59, 0xf5, + 0x10, 0x30, 0x21, 0x2e, 0x6a, 0xf5, 0x80, 0x2e, 0x3b, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x19, 0x01, 0x00, 0x22, + 0x00, 0x75, 0x00, 0x00, 0x10, 0x00, 0x10, 0xd1, 0x00, 0xb3, 0x43, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, + 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, + 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, + 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, + 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, + 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, + 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0xe0, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x19, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0xe0, 0xaa, 0x38, 0x05, 0xe0, 0x90, 0x30, 0xfa, 0x00, 0x96, 0x00, 0x4b, 0x09, 0x11, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x2d, 0x01, 0xd4, 0x7b, 0x3b, 0x01, 0xdb, 0x7a, 0x04, 0x00, 0x3f, 0x7b, 0xcd, 0x6c, 0xc3, 0x04, 0x85, 0x09, 0xc3, + 0x04, 0xec, 0xe6, 0x0c, 0x46, 0x01, 0x00, 0x27, 0x00, 0x19, 0x00, 0x96, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x0c, 0x00, + 0xf0, 0x3c, 0x00, 0x01, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x05, 0x00, 0xee, + 0x06, 0x04, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x04, 0x00, 0xa8, 0x05, 0xee, 0x06, 0x00, 0x04, 0xbc, 0x02, 0xb3, 0x00, + 0x85, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xb4, 0x00, 0x01, 0x00, 0xb9, 0x00, 0x01, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2e, 0x00, 0xc1, 0xfd, 0x2d, 0xde, + 0x00, 0xeb, 0x00, 0xda, 0x00, 0x00, 0x0c, 0xff, 0x0f, 0x00, 0x04, 0xc0, 0x00, 0x5b, 0xf5, 0xc9, 0x01, 0x1e, 0xf2, + 0x80, 0x00, 0x3f, 0xff, 0x19, 0xf4, 0x58, 0xf5, 0x66, 0xf5, 0x64, 0xf5, 0xc0, 0xf1, 0xf0, 0x00, 0xe0, 0x00, 0xcd, + 0x01, 0xd3, 0x01, 0xdb, 0x01, 0xff, 0x7f, 0xff, 0x01, 0xe4, 0x00, 0x74, 0xf7, 0xf3, 0x00, 0xfa, 0x00, 0xff, 0x3f, + 0xca, 0x03, 0x6c, 0x38, 0x56, 0xfe, 0x44, 0xfd, 0xbc, 0x02, 0xf9, 0x06, 0x00, 0xfc, 0x12, 0x02, 0xae, 0x01, 0x58, + 0xfa, 0x9a, 0xfd, 0x77, 0x05, 0xbb, 0x02, 0x96, 0x01, 0x95, 0x01, 0x7f, 0x01, 0x82, 0x01, 0x89, 0x01, 0x87, 0x01, + 0x88, 0x01, 0x8a, 0x01, 0x8c, 0x01, 0x8f, 0x01, 0x8d, 0x01, 0x92, 0x01, 0x91, 0x01, 0xdd, 0x00, 0x9f, 0x01, 0x7e, + 0x01, 0xdb, 0x00, 0xb6, 0x01, 0x70, 0x69, 0x26, 0xd3, 0x9c, 0x07, 0x1f, 0x05, 0x9d, 0x00, 0x00, 0x08, 0xbc, 0x05, + 0x37, 0xfa, 0xa2, 0x01, 0xaa, 0x01, 0xa1, 0x01, 0xa8, 0x01, 0xa0, 0x01, 0xa8, 0x05, 0xb4, 0x01, 0xb4, 0x01, 0xce, + 0x00, 0xd0, 0x00, 0xfc, 0x00, 0xc5, 0x01, 0xff, 0xfb, 0xb1, 0x00, 0x00, 0x38, 0x00, 0x30, 0xfd, 0xf5, 0xfc, 0xf5, + 0xcd, 0x01, 0xa0, 0x00, 0x5f, 0xff, 0x00, 0x40, 0xff, 0x00, 0x00, 0x80, 0x6d, 0x0f, 0xeb, 0x00, 0x7f, 0xff, 0xc2, + 0xf5, 0x68, 0xf7, 0xb3, 0xf1, 0x67, 0x0f, 0x5b, 0x0f, 0x61, 0x0f, 0x80, 0x0f, 0x58, 0xf7, 0x5b, 0xf7, 0x83, 0x0f, + 0x86, 0x00, 0x72, 0x0f, 0x85, 0x0f, 0xc6, 0xf1, 0x7f, 0x0f, 0x6c, 0xf7, 0x00, 0xe0, 0x00, 0xff, 0xd1, 0xf5, 0x87, + 0x0f, 0x8a, 0x0f, 0xff, 0x03, 0xf0, 0x3f, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0xb9, 0x00, 0x2d, 0xf5, 0xca, 0xf5, + 0xcb, 0x01, 0x20, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x50, 0x98, 0x2e, + 0xd7, 0x0e, 0x50, 0x32, 0x98, 0x2e, 0xfa, 0x03, 0x00, 0x30, 0xf0, 0x7f, 0x00, 0x2e, 0x00, 0x2e, 0xd0, 0x2e, 0x00, + 0x2e, 0x01, 0x80, 0x08, 0xa2, 0xfb, 0x2f, 0x98, 0x2e, 0xba, 0x03, 0x21, 0x2e, 0x19, 0x00, 0x01, 0x2e, 0xee, 0x00, + 0x00, 0xb2, 0x07, 0x2f, 0x01, 0x2e, 0x19, 0x00, 0x00, 0xb2, 0x03, 0x2f, 0x01, 0x50, 0x03, 0x52, 0x98, 0x2e, 0x07, + 0xcc, 0x01, 0x2e, 0xdd, 0x00, 0x00, 0xb2, 0x27, 0x2f, 0x05, 0x2e, 0x8a, 0x00, 0x05, 0x52, 0x98, 0x2e, 0xc7, 0xc1, + 0x03, 0x2e, 0xe9, 0x00, 0x40, 0xb2, 0xf0, 0x7f, 0x08, 0x2f, 0x01, 0x2e, 0x19, 0x00, 0x00, 0xb2, 0x04, 0x2f, 0x00, + 0x30, 0x21, 0x2e, 0xe9, 0x00, 0x98, 0x2e, 0xb4, 0xb1, 0x01, 0x2e, 0x18, 0x00, 0x00, 0xb2, 0x10, 0x2f, 0x05, 0x50, + 0x98, 0x2e, 0x4d, 0xc3, 0x05, 0x50, 0x98, 0x2e, 0x5a, 0xc7, 0x98, 0x2e, 0xf9, 0xb4, 0x98, 0x2e, 0x54, 0xb2, 0x98, + 0x2e, 0x67, 0xb6, 0x98, 0x2e, 0x17, 0xb2, 0x10, 0x30, 0x21, 0x2e, 0x77, 0x00, 0x01, 0x2e, 0xef, 0x00, 0x00, 0xb2, + 0x04, 0x2f, 0x98, 0x2e, 0x7a, 0xb7, 0x00, 0x30, 0x21, 0x2e, 0xef, 0x00, 0x01, 0x2e, 0xd4, 0x00, 0x04, 0xae, 0x0b, + 0x2f, 0x01, 0x2e, 0xdd, 0x00, 0x00, 0xb2, 0x07, 0x2f, 0x05, 0x52, 0x98, 0x2e, 0x8e, 0x0e, 0x00, 0xb2, 0x02, 0x2f, + 0x10, 0x30, 0x21, 0x2e, 0x7d, 0x00, 0x01, 0x2e, 0x7d, 0x00, 0x00, 0x90, 0x90, 0x2e, 0xf1, 0x02, 0x01, 0x2e, 0xd7, + 0x00, 0x00, 0xb2, 0x04, 0x2f, 0x98, 0x2e, 0x2f, 0x0e, 0x00, 0x30, 0x21, 0x2e, 0x7b, 0x00, 0x01, 0x2e, 0x7b, 0x00, + 0x00, 0xb2, 0x12, 0x2f, 0x01, 0x2e, 0xd4, 0x00, 0x00, 0x90, 0x02, 0x2f, 0x98, 0x2e, 0x1f, 0x0e, 0x09, 0x2d, 0x98, + 0x2e, 0x81, 0x0d, 0x01, 0x2e, 0xd4, 0x00, 0x04, 0x90, 0x02, 0x2f, 0x50, 0x32, 0x98, 0x2e, 0xfa, 0x03, 0x00, 0x30, + 0x21, 0x2e, 0x7b, 0x00, 0x01, 0x2e, 0x7c, 0x00, 0x00, 0xb2, 0x90, 0x2e, 0x09, 0x03, 0x01, 0x2e, 0x7c, 0x00, 0x01, + 0x31, 0x01, 0x08, 0x00, 0xb2, 0x04, 0x2f, 0x98, 0x2e, 0x47, 0xcb, 0x10, 0x30, 0x21, 0x2e, 0x77, 0x00, 0x81, 0x30, + 0x01, 0x2e, 0x7c, 0x00, 0x01, 0x08, 0x00, 0xb2, 0x61, 0x2f, 0x03, 0x2e, 0x89, 0x00, 0x01, 0x2e, 0xd4, 0x00, 0x98, + 0xbc, 0x98, 0xb8, 0x05, 0xb2, 0x0f, 0x58, 0x23, 0x2f, 0x07, 0x90, 0x09, 0x54, 0x00, 0x30, 0x37, 0x2f, 0x15, 0x41, + 0x04, 0x41, 0xdc, 0xbe, 0x44, 0xbe, 0xdc, 0xba, 0x2c, 0x01, 0x61, 0x00, 0x0f, 0x56, 0x4a, 0x0f, 0x0c, 0x2f, 0xd1, + 0x42, 0x94, 0xb8, 0xc1, 0x42, 0x11, 0x30, 0x05, 0x2e, 0x6a, 0xf7, 0x2c, 0xbd, 0x2f, 0xb9, 0x80, 0xb2, 0x08, 0x22, + 0x98, 0x2e, 0xc3, 0xb7, 0x21, 0x2d, 0x61, 0x30, 0x23, 0x2e, 0xd4, 0x00, 0x98, 0x2e, 0xc3, 0xb7, 0x00, 0x30, 0x21, + 0x2e, 0x5a, 0xf5, 0x18, 0x2d, 0xe1, 0x7f, 0x50, 0x30, 0x98, 0x2e, 0xfa, 0x03, 0x0f, 0x52, 0x07, 0x50, 0x50, 0x42, + 0x70, 0x30, 0x0d, 0x54, 0x42, 0x42, 0x7e, 0x82, 0xe2, 0x6f, 0x80, 0xb2, 0x42, 0x42, 0x05, 0x2f, 0x21, 0x2e, 0xd4, + 0x00, 0x10, 0x30, 0x98, 0x2e, 0xc3, 0xb7, 0x03, 0x2d, 0x60, 0x30, 0x21, 0x2e, 0xd4, 0x00, 0x01, 0x2e, 0xd4, 0x00, + 0x06, 0x90, 0x18, 0x2f, 0x01, 0x2e, 0x76, 0x00, 0x0b, 0x54, 0x07, 0x52, 0xe0, 0x7f, 0x98, 0x2e, 0x7a, 0xc1, 0xe1, + 0x6f, 0x08, 0x1a, 0x40, 0x30, 0x08, 0x2f, 0x21, 0x2e, 0xd4, 0x00, 0x20, 0x30, 0x98, 0x2e, 0xaf, 0xb7, 0x50, 0x32, + 0x98, 0x2e, 0xfa, 0x03, 0x05, 0x2d, 0x98, 0x2e, 0x38, 0x0e, 0x00, 0x30, 0x21, 0x2e, 0xd4, 0x00, 0x00, 0x30, 0x21, + 0x2e, 0x7c, 0x00, 0x18, 0x2d, 0x01, 0x2e, 0xd4, 0x00, 0x03, 0xaa, 0x01, 0x2f, 0x98, 0x2e, 0x45, 0x0e, 0x01, 0x2e, + 0xd4, 0x00, 0x3f, 0x80, 0x03, 0xa2, 0x01, 0x2f, 0x00, 0x2e, 0x02, 0x2d, 0x98, 0x2e, 0x5b, 0x0e, 0x30, 0x30, 0x98, + 0x2e, 0xce, 0xb7, 0x00, 0x30, 0x21, 0x2e, 0x7d, 0x00, 0x50, 0x32, 0x98, 0x2e, 0xfa, 0x03, 0x01, 0x2e, 0x77, 0x00, + 0x00, 0xb2, 0x24, 0x2f, 0x98, 0x2e, 0xf5, 0xcb, 0x03, 0x2e, 0xd5, 0x00, 0x11, 0x54, 0x01, 0x0a, 0xbc, 0x84, 0x83, + 0x86, 0x21, 0x2e, 0xc9, 0x01, 0xe0, 0x40, 0x13, 0x52, 0xc4, 0x40, 0x82, 0x40, 0xa8, 0xb9, 0x52, 0x42, 0x43, 0xbe, + 0x53, 0x42, 0x04, 0x0a, 0x50, 0x42, 0xe1, 0x7f, 0xf0, 0x31, 0x41, 0x40, 0xf2, 0x6f, 0x25, 0xbd, 0x08, 0x08, 0x02, + 0x0a, 0xd0, 0x7f, 0x98, 0x2e, 0xa8, 0xcf, 0x06, 0xbc, 0xd1, 0x6f, 0xe2, 0x6f, 0x08, 0x0a, 0x80, 0x42, 0x98, 0x2e, + 0x58, 0xb7, 0x00, 0x30, 0x21, 0x2e, 0xee, 0x00, 0x21, 0x2e, 0x77, 0x00, 0x21, 0x2e, 0xdd, 0x00, 0x80, 0x2e, 0xf4, + 0x01, 0x1a, 0x24, 0x22, 0x00, 0x80, 0x2e, 0xec, 0x01, 0x10, 0x50, 0xfb, 0x7f, 0x98, 0x2e, 0xf3, 0x03, 0x57, 0x50, + 0xfb, 0x6f, 0x01, 0x30, 0x71, 0x54, 0x11, 0x42, 0x42, 0x0e, 0xfc, 0x2f, 0xc0, 0x2e, 0x01, 0x42, 0xf0, 0x5f, 0x80, + 0x2e, 0x00, 0xc1, 0xfd, 0x2d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x01, + 0x34, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x50, 0xe7, 0x7f, 0xf6, 0x7f, 0x06, 0x32, 0x0f, 0x2e, 0x61, 0xf5, 0xfe, 0x09, 0xc0, 0xb3, 0x04, + 0x2f, 0x17, 0x30, 0x2f, 0x2e, 0xef, 0x00, 0x2d, 0x2e, 0x61, 0xf5, 0xf6, 0x6f, 0xe7, 0x6f, 0xe0, 0x5f, 0xc8, 0x2e, + 0x20, 0x50, 0xe7, 0x7f, 0xf6, 0x7f, 0x46, 0x30, 0x0f, 0x2e, 0xa4, 0xf1, 0xbe, 0x09, 0x80, 0xb3, 0x06, 0x2f, 0x0d, + 0x2e, 0xd4, 0x00, 0x84, 0xaf, 0x02, 0x2f, 0x16, 0x30, 0x2d, 0x2e, 0x7b, 0x00, 0x86, 0x30, 0x2d, 0x2e, 0x60, 0xf5, + 0xf6, 0x6f, 0xe7, 0x6f, 0xe0, 0x5f, 0xc8, 0x2e, 0x01, 0x2e, 0x77, 0xf7, 0x09, 0xbc, 0x0f, 0xb8, 0x00, 0xb2, 0x10, + 0x50, 0xfb, 0x7f, 0x10, 0x30, 0x0b, 0x2f, 0x03, 0x2e, 0x8a, 0x00, 0x96, 0xbc, 0x9f, 0xb8, 0x40, 0xb2, 0x05, 0x2f, + 0x03, 0x2e, 0x68, 0xf7, 0x9e, 0xbc, 0x9f, 0xb8, 0x40, 0xb2, 0x07, 0x2f, 0x03, 0x2e, 0x7e, 0x00, 0x41, 0x90, 0x01, + 0x2f, 0x98, 0x2e, 0xdc, 0x03, 0x03, 0x2c, 0x00, 0x30, 0x21, 0x2e, 0x7e, 0x00, 0xfb, 0x6f, 0xf0, 0x5f, 0xb8, 0x2e, + 0x20, 0x50, 0xe0, 0x7f, 0xfb, 0x7f, 0x00, 0x2e, 0x27, 0x50, 0x98, 0x2e, 0x3b, 0xc8, 0x29, 0x50, 0x98, 0x2e, 0xa7, + 0xc8, 0x01, 0x50, 0x98, 0x2e, 0x55, 0xcc, 0xe1, 0x6f, 0x2b, 0x50, 0x98, 0x2e, 0xe0, 0xc9, 0xfb, 0x6f, 0x00, 0x30, + 0xe0, 0x5f, 0x21, 0x2e, 0x7e, 0x00, 0xb8, 0x2e, 0x73, 0x50, 0x01, 0x30, 0x57, 0x54, 0x11, 0x42, 0x42, 0x0e, 0xfc, + 0x2f, 0xb8, 0x2e, 0x21, 0x2e, 0x59, 0xf5, 0x10, 0x30, 0xc0, 0x2e, 0x21, 0x2e, 0x4a, 0xf1, 0x90, 0x50, 0xf7, 0x7f, + 0xe6, 0x7f, 0xd5, 0x7f, 0xc4, 0x7f, 0xb3, 0x7f, 0xa1, 0x7f, 0x90, 0x7f, 0x82, 0x7f, 0x7b, 0x7f, 0x98, 0x2e, 0x35, + 0xb7, 0x00, 0xb2, 0x90, 0x2e, 0x97, 0xb0, 0x03, 0x2e, 0x8f, 0x00, 0x07, 0x2e, 0x91, 0x00, 0x05, 0x2e, 0xb1, 0x00, + 0x3f, 0xba, 0x9f, 0xb8, 0x01, 0x2e, 0xb1, 0x00, 0xa3, 0xbd, 0x4c, 0x0a, 0x05, 0x2e, 0xb1, 0x00, 0x04, 0xbe, 0xbf, + 0xb9, 0xcb, 0x0a, 0x4f, 0xba, 0x22, 0xbd, 0x01, 0x2e, 0xb3, 0x00, 0xdc, 0x0a, 0x2f, 0xb9, 0x03, 0x2e, 0xb8, 0x00, + 0x0a, 0xbe, 0x9a, 0x0a, 0xcf, 0xb9, 0x9b, 0xbc, 0x01, 0x2e, 0x97, 0x00, 0x9f, 0xb8, 0x93, 0x0a, 0x0f, 0xbc, 0x91, + 0x0a, 0x0f, 0xb8, 0x90, 0x0a, 0x25, 0x2e, 0x18, 0x00, 0x05, 0x2e, 0xc1, 0xf5, 0x2e, 0xbd, 0x2e, 0xb9, 0x01, 0x2e, + 0x19, 0x00, 0x31, 0x30, 0x8a, 0x04, 0x00, 0x90, 0x07, 0x2f, 0x01, 0x2e, 0xd4, 0x00, 0x04, 0xa2, 0x03, 0x2f, 0x01, + 0x2e, 0x18, 0x00, 0x00, 0xb2, 0x0c, 0x2f, 0x19, 0x50, 0x05, 0x52, 0x98, 0x2e, 0x4d, 0xb7, 0x05, 0x2e, 0x78, 0x00, + 0x80, 0x90, 0x10, 0x30, 0x01, 0x2f, 0x21, 0x2e, 0x78, 0x00, 0x25, 0x2e, 0xdd, 0x00, 0x98, 0x2e, 0x3e, 0xb7, 0x00, + 0xb2, 0x02, 0x30, 0x01, 0x30, 0x04, 0x2f, 0x01, 0x2e, 0x19, 0x00, 0x00, 0xb2, 0x00, 0x2f, 0x21, 0x30, 0x01, 0x2e, + 0xea, 0x00, 0x08, 0x1a, 0x0e, 0x2f, 0x23, 0x2e, 0xea, 0x00, 0x33, 0x30, 0x1b, 0x50, 0x0b, 0x09, 0x01, 0x40, 0x17, + 0x56, 0x46, 0xbe, 0x4b, 0x08, 0x4c, 0x0a, 0x01, 0x42, 0x0a, 0x80, 0x15, 0x52, 0x01, 0x42, 0x00, 0x2e, 0x01, 0x2e, + 0x18, 0x00, 0x00, 0xb2, 0x1f, 0x2f, 0x03, 0x2e, 0xc0, 0xf5, 0xf0, 0x30, 0x48, 0x08, 0x47, 0xaa, 0x74, 0x30, 0x07, + 0x2e, 0x7a, 0x00, 0x61, 0x22, 0x4b, 0x1a, 0x05, 0x2f, 0x07, 0x2e, 0x66, 0xf5, 0xbf, 0xbd, 0xbf, 0xb9, 0xc0, 0x90, + 0x0b, 0x2f, 0x1d, 0x56, 0x2b, 0x30, 0xd2, 0x42, 0xdb, 0x42, 0x01, 0x04, 0xc2, 0x42, 0x04, 0xbd, 0xfe, 0x80, 0x81, + 0x84, 0x23, 0x2e, 0x7a, 0x00, 0x02, 0x42, 0x02, 0x32, 0x25, 0x2e, 0x62, 0xf5, 0x05, 0x2e, 0xd6, 0x00, 0x81, 0x84, + 0x25, 0x2e, 0xd6, 0x00, 0x02, 0x31, 0x25, 0x2e, 0x60, 0xf5, 0x05, 0x2e, 0x8a, 0x00, 0x0b, 0x50, 0x90, 0x08, 0x80, + 0xb2, 0x0b, 0x2f, 0x05, 0x2e, 0xca, 0xf5, 0xf0, 0x3e, 0x90, 0x08, 0x25, 0x2e, 0xca, 0xf5, 0x05, 0x2e, 0x59, 0xf5, + 0xe0, 0x3f, 0x90, 0x08, 0x25, 0x2e, 0x59, 0xf5, 0x90, 0x6f, 0xa1, 0x6f, 0xb3, 0x6f, 0xc4, 0x6f, 0xd5, 0x6f, 0xe6, + 0x6f, 0xf7, 0x6f, 0x7b, 0x6f, 0x82, 0x6f, 0x70, 0x5f, 0xc8, 0x2e, 0xc0, 0x50, 0x90, 0x7f, 0xe5, 0x7f, 0xd4, 0x7f, + 0xc3, 0x7f, 0xb1, 0x7f, 0xa2, 0x7f, 0x87, 0x7f, 0xf6, 0x7f, 0x7b, 0x7f, 0x00, 0x2e, 0x01, 0x2e, 0x60, 0xf5, 0x60, + 0x7f, 0x98, 0x2e, 0x35, 0xb7, 0x02, 0x30, 0x63, 0x6f, 0x15, 0x52, 0x50, 0x7f, 0x62, 0x7f, 0x5a, 0x2c, 0x02, 0x32, + 0x1a, 0x09, 0x00, 0xb3, 0x14, 0x2f, 0x00, 0xb2, 0x03, 0x2f, 0x09, 0x2e, 0x18, 0x00, 0x00, 0x91, 0x0c, 0x2f, 0x43, + 0x7f, 0x98, 0x2e, 0x97, 0xb7, 0x1f, 0x50, 0x02, 0x8a, 0x02, 0x32, 0x04, 0x30, 0x25, 0x2e, 0x64, 0xf5, 0x15, 0x52, + 0x50, 0x6f, 0x43, 0x6f, 0x44, 0x43, 0x25, 0x2e, 0x60, 0xf5, 0xd9, 0x08, 0xc0, 0xb2, 0x36, 0x2f, 0x98, 0x2e, 0x3e, + 0xb7, 0x00, 0xb2, 0x06, 0x2f, 0x01, 0x2e, 0x19, 0x00, 0x00, 0xb2, 0x02, 0x2f, 0x50, 0x6f, 0x00, 0x90, 0x0a, 0x2f, + 0x01, 0x2e, 0x79, 0x00, 0x00, 0x90, 0x19, 0x2f, 0x10, 0x30, 0x21, 0x2e, 0x79, 0x00, 0x00, 0x30, 0x98, 0x2e, 0xdc, + 0x03, 0x13, 0x2d, 0x01, 0x2e, 0xc3, 0xf5, 0x0c, 0xbc, 0x0f, 0xb8, 0x12, 0x30, 0x10, 0x04, 0x03, 0xb0, 0x26, 0x25, + 0x21, 0x50, 0x03, 0x52, 0x98, 0x2e, 0x4d, 0xb7, 0x10, 0x30, 0x21, 0x2e, 0xee, 0x00, 0x02, 0x30, 0x60, 0x7f, 0x25, + 0x2e, 0x79, 0x00, 0x60, 0x6f, 0x00, 0x90, 0x05, 0x2f, 0x00, 0x30, 0x21, 0x2e, 0xea, 0x00, 0x15, 0x50, 0x21, 0x2e, + 0x64, 0xf5, 0x15, 0x52, 0x23, 0x2e, 0x60, 0xf5, 0x02, 0x32, 0x50, 0x6f, 0x00, 0x90, 0x02, 0x2f, 0x03, 0x30, 0x27, + 0x2e, 0x78, 0x00, 0x07, 0x2e, 0x60, 0xf5, 0x1a, 0x09, 0x00, 0x91, 0xa3, 0x2f, 0x19, 0x09, 0x00, 0x91, 0xa0, 0x2f, + 0x90, 0x6f, 0xa2, 0x6f, 0xb1, 0x6f, 0xc3, 0x6f, 0xd4, 0x6f, 0xe5, 0x6f, 0x7b, 0x6f, 0xf6, 0x6f, 0x87, 0x6f, 0x40, + 0x5f, 0xc8, 0x2e, 0xc0, 0x50, 0xe7, 0x7f, 0xf6, 0x7f, 0x26, 0x30, 0x0f, 0x2e, 0x61, 0xf5, 0x2f, 0x2e, 0x7c, 0x00, + 0x0f, 0x2e, 0x7c, 0x00, 0xbe, 0x09, 0xa2, 0x7f, 0x80, 0x7f, 0x80, 0xb3, 0xd5, 0x7f, 0xc4, 0x7f, 0xb3, 0x7f, 0x91, + 0x7f, 0x7b, 0x7f, 0x0b, 0x2f, 0x23, 0x50, 0x1a, 0x25, 0x12, 0x40, 0x42, 0x7f, 0x74, 0x82, 0x12, 0x40, 0x52, 0x7f, + 0x00, 0x2e, 0x00, 0x40, 0x60, 0x7f, 0x98, 0x2e, 0x6a, 0xd6, 0x81, 0x30, 0x01, 0x2e, 0x7c, 0x00, 0x01, 0x08, 0x00, + 0xb2, 0x42, 0x2f, 0x03, 0x2e, 0x89, 0x00, 0x01, 0x2e, 0x89, 0x00, 0x97, 0xbc, 0x06, 0xbc, 0x9f, 0xb8, 0x0f, 0xb8, + 0x00, 0x90, 0x23, 0x2e, 0xd8, 0x00, 0x10, 0x30, 0x01, 0x30, 0x2a, 0x2f, 0x03, 0x2e, 0xd4, 0x00, 0x44, 0xb2, 0x05, + 0x2f, 0x47, 0xb2, 0x00, 0x30, 0x2d, 0x2f, 0x21, 0x2e, 0x7c, 0x00, 0x2b, 0x2d, 0x03, 0x2e, 0xfd, 0xf5, 0x9e, 0xbc, + 0x9f, 0xb8, 0x40, 0x90, 0x14, 0x2f, 0x03, 0x2e, 0xfc, 0xf5, 0x99, 0xbc, 0x9f, 0xb8, 0x40, 0x90, 0x0e, 0x2f, 0x03, + 0x2e, 0x49, 0xf1, 0x25, 0x54, 0x4a, 0x08, 0x40, 0x90, 0x08, 0x2f, 0x98, 0x2e, 0x35, 0xb7, 0x00, 0xb2, 0x10, 0x30, + 0x03, 0x2f, 0x50, 0x30, 0x21, 0x2e, 0xd4, 0x00, 0x10, 0x2d, 0x98, 0x2e, 0xaf, 0xb7, 0x00, 0x30, 0x21, 0x2e, 0x7c, + 0x00, 0x0a, 0x2d, 0x05, 0x2e, 0x69, 0xf7, 0x2d, 0xbd, 0x2f, 0xb9, 0x80, 0xb2, 0x01, 0x2f, 0x21, 0x2e, 0x7d, 0x00, + 0x23, 0x2e, 0x7c, 0x00, 0xe0, 0x31, 0x21, 0x2e, 0x61, 0xf5, 0xf6, 0x6f, 0xe7, 0x6f, 0x80, 0x6f, 0xa2, 0x6f, 0xb3, + 0x6f, 0xc4, 0x6f, 0xd5, 0x6f, 0x7b, 0x6f, 0x91, 0x6f, 0x40, 0x5f, 0xc8, 0x2e, 0x60, 0x51, 0x0a, 0x25, 0x36, 0x88, + 0xf4, 0x7f, 0xeb, 0x7f, 0x00, 0x32, 0x31, 0x52, 0x32, 0x30, 0x13, 0x30, 0x98, 0x2e, 0x15, 0xcb, 0x0a, 0x25, 0x33, + 0x84, 0xd2, 0x7f, 0x43, 0x30, 0x05, 0x50, 0x2d, 0x52, 0x98, 0x2e, 0x95, 0xc1, 0xd2, 0x6f, 0x27, 0x52, 0x98, 0x2e, + 0xd7, 0xc7, 0x2a, 0x25, 0xb0, 0x86, 0xc0, 0x7f, 0xd3, 0x7f, 0xaf, 0x84, 0x29, 0x50, 0xf1, 0x6f, 0x98, 0x2e, 0x4d, + 0xc8, 0x2a, 0x25, 0xae, 0x8a, 0xaa, 0x88, 0xf2, 0x6e, 0x2b, 0x50, 0xc1, 0x6f, 0xd3, 0x6f, 0xf4, 0x7f, 0x98, 0x2e, + 0xb6, 0xc8, 0xe0, 0x6e, 0x00, 0xb2, 0x32, 0x2f, 0x33, 0x54, 0x83, 0x86, 0xf1, 0x6f, 0xc3, 0x7f, 0x04, 0x30, 0x30, + 0x30, 0xf4, 0x7f, 0xd0, 0x7f, 0xb2, 0x7f, 0xe3, 0x30, 0xc5, 0x6f, 0x56, 0x40, 0x45, 0x41, 0x28, 0x08, 0x03, 0x14, + 0x0e, 0xb4, 0x08, 0xbc, 0x82, 0x40, 0x10, 0x0a, 0x2f, 0x54, 0x26, 0x05, 0x91, 0x7f, 0x44, 0x28, 0xa3, 0x7f, 0x98, + 0x2e, 0xd9, 0xc0, 0x08, 0xb9, 0x33, 0x30, 0x53, 0x09, 0xc1, 0x6f, 0xd3, 0x6f, 0xf4, 0x6f, 0x83, 0x17, 0x47, 0x40, + 0x6c, 0x15, 0xb2, 0x6f, 0xbe, 0x09, 0x75, 0x0b, 0x90, 0x42, 0x45, 0x42, 0x51, 0x0e, 0x32, 0xbc, 0x02, 0x89, 0xa1, + 0x6f, 0x7e, 0x86, 0xf4, 0x7f, 0xd0, 0x7f, 0xb2, 0x7f, 0x04, 0x30, 0x91, 0x6f, 0xd6, 0x2f, 0xeb, 0x6f, 0xa0, 0x5e, + 0xb8, 0x2e, 0x03, 0x2e, 0x97, 0x00, 0x1b, 0xbc, 0x60, 0x50, 0x9f, 0xbc, 0x0c, 0xb8, 0xf0, 0x7f, 0x40, 0xb2, 0xeb, + 0x7f, 0x2b, 0x2f, 0x03, 0x2e, 0x7f, 0x00, 0x41, 0x40, 0x01, 0x2e, 0xc8, 0x00, 0x01, 0x1a, 0x11, 0x2f, 0x37, 0x58, + 0x23, 0x2e, 0xc8, 0x00, 0x10, 0x41, 0xa0, 0x7f, 0x38, 0x81, 0x01, 0x41, 0xd0, 0x7f, 0xb1, 0x7f, 0x98, 0x2e, 0x64, + 0xcf, 0xd0, 0x6f, 0x07, 0x80, 0xa1, 0x6f, 0x11, 0x42, 0x00, 0x2e, 0xb1, 0x6f, 0x01, 0x42, 0x11, 0x30, 0x01, 0x2e, + 0xfc, 0x00, 0x00, 0xa8, 0x03, 0x30, 0xcb, 0x22, 0x4a, 0x25, 0x01, 0x2e, 0x7f, 0x00, 0x3c, 0x89, 0x35, 0x52, 0x05, + 0x54, 0x98, 0x2e, 0xc4, 0xce, 0xc1, 0x6f, 0xf0, 0x6f, 0x98, 0x2e, 0x95, 0xcf, 0x04, 0x2d, 0x01, 0x30, 0xf0, 0x6f, + 0x98, 0x2e, 0x95, 0xcf, 0xeb, 0x6f, 0xa0, 0x5f, 0xb8, 0x2e, 0x03, 0x2e, 0xb3, 0x00, 0x02, 0x32, 0xf0, 0x30, 0x03, + 0x31, 0x30, 0x50, 0x8a, 0x08, 0x08, 0x08, 0xcb, 0x08, 0xe0, 0x7f, 0x80, 0xb2, 0xf3, 0x7f, 0xdb, 0x7f, 0x25, 0x2f, + 0x03, 0x2e, 0xca, 0x00, 0x41, 0x90, 0x04, 0x2f, 0x01, 0x30, 0x23, 0x2e, 0xca, 0x00, 0x98, 0x2e, 0x3f, 0x03, 0xc0, + 0xb2, 0x05, 0x2f, 0x03, 0x2e, 0xda, 0x00, 0x00, 0x30, 0x41, 0x04, 0x23, 0x2e, 0xda, 0x00, 0x98, 0x2e, 0x92, 0xb2, + 0x10, 0x25, 0xf0, 0x6f, 0x00, 0xb2, 0x05, 0x2f, 0x01, 0x2e, 0xda, 0x00, 0x02, 0x30, 0x10, 0x04, 0x21, 0x2e, 0xda, + 0x00, 0x40, 0xb2, 0x01, 0x2f, 0x23, 0x2e, 0xc8, 0x01, 0xdb, 0x6f, 0xe0, 0x6f, 0xd0, 0x5f, 0x80, 0x2e, 0x95, 0xcf, + 0x01, 0x30, 0xe0, 0x6f, 0x98, 0x2e, 0x95, 0xcf, 0x11, 0x30, 0x23, 0x2e, 0xca, 0x00, 0xdb, 0x6f, 0xd0, 0x5f, 0xb8, + 0x2e, 0xd0, 0x50, 0x0a, 0x25, 0x33, 0x84, 0x55, 0x50, 0xd2, 0x7f, 0xe2, 0x7f, 0x03, 0x8c, 0xc0, 0x7f, 0xbb, 0x7f, + 0x00, 0x30, 0x05, 0x5a, 0x39, 0x54, 0x51, 0x41, 0xa5, 0x7f, 0x96, 0x7f, 0x80, 0x7f, 0x98, 0x2e, 0xd9, 0xc0, 0x05, + 0x30, 0xf5, 0x7f, 0x20, 0x25, 0x91, 0x6f, 0x3b, 0x58, 0x3d, 0x5c, 0x3b, 0x56, 0x98, 0x2e, 0x67, 0xcc, 0xc1, 0x6f, + 0xd5, 0x6f, 0x52, 0x40, 0x50, 0x43, 0xc1, 0x7f, 0xd5, 0x7f, 0x10, 0x25, 0x98, 0x2e, 0xfe, 0xc9, 0x10, 0x25, 0x98, + 0x2e, 0x74, 0xc0, 0x86, 0x6f, 0x30, 0x28, 0x92, 0x6f, 0x82, 0x8c, 0xa5, 0x6f, 0x6f, 0x52, 0x69, 0x0e, 0x39, 0x54, + 0xdb, 0x2f, 0x19, 0xa0, 0x15, 0x30, 0x03, 0x2f, 0x00, 0x30, 0x21, 0x2e, 0x81, 0x01, 0x0a, 0x2d, 0x01, 0x2e, 0x81, + 0x01, 0x05, 0x28, 0x42, 0x36, 0x21, 0x2e, 0x81, 0x01, 0x02, 0x0e, 0x01, 0x2f, 0x98, 0x2e, 0xf3, 0x03, 0x57, 0x50, + 0x12, 0x30, 0x01, 0x40, 0x98, 0x2e, 0xfe, 0xc9, 0x51, 0x6f, 0x0b, 0x5c, 0x8e, 0x0e, 0x3b, 0x6f, 0x57, 0x58, 0x02, + 0x30, 0x21, 0x2e, 0x95, 0x01, 0x45, 0x6f, 0x2a, 0x8d, 0xd2, 0x7f, 0xcb, 0x7f, 0x13, 0x2f, 0x02, 0x30, 0x3f, 0x50, + 0xd2, 0x7f, 0xa8, 0x0e, 0x0e, 0x2f, 0xc0, 0x6f, 0x53, 0x54, 0x02, 0x00, 0x51, 0x54, 0x42, 0x0e, 0x10, 0x30, 0x59, + 0x52, 0x02, 0x30, 0x01, 0x2f, 0x00, 0x2e, 0x03, 0x2d, 0x50, 0x42, 0x42, 0x42, 0x12, 0x30, 0xd2, 0x7f, 0x80, 0xb2, + 0x03, 0x2f, 0x00, 0x30, 0x21, 0x2e, 0x80, 0x01, 0x12, 0x2d, 0x01, 0x2e, 0xc9, 0x00, 0x02, 0x80, 0x05, 0x2e, 0x80, + 0x01, 0x11, 0x30, 0x91, 0x28, 0x00, 0x40, 0x25, 0x2e, 0x80, 0x01, 0x10, 0x0e, 0x05, 0x2f, 0x01, 0x2e, 0x7f, 0x01, + 0x01, 0x90, 0x01, 0x2f, 0x98, 0x2e, 0xf3, 0x03, 0x00, 0x2e, 0xa0, 0x41, 0x01, 0x90, 0xa6, 0x7f, 0x90, 0x2e, 0xe3, + 0xb4, 0x01, 0x2e, 0x95, 0x01, 0x00, 0xa8, 0x90, 0x2e, 0xe3, 0xb4, 0x5b, 0x54, 0x95, 0x80, 0x82, 0x40, 0x80, 0xb2, + 0x02, 0x40, 0x2d, 0x8c, 0x3f, 0x52, 0x96, 0x7f, 0x90, 0x2e, 0xc2, 0xb3, 0x29, 0x0e, 0x76, 0x2f, 0x01, 0x2e, 0xc9, + 0x00, 0x00, 0x40, 0x81, 0x28, 0x45, 0x52, 0xb3, 0x30, 0x98, 0x2e, 0x0f, 0xca, 0x5d, 0x54, 0x80, 0x7f, 0x00, 0x2e, + 0xa1, 0x40, 0x72, 0x7f, 0x82, 0x80, 0x82, 0x40, 0x60, 0x7f, 0x98, 0x2e, 0xfe, 0xc9, 0x10, 0x25, 0x98, 0x2e, 0x74, + 0xc0, 0x62, 0x6f, 0x05, 0x30, 0x87, 0x40, 0xc0, 0x91, 0x04, 0x30, 0x05, 0x2f, 0x05, 0x2e, 0x83, 0x01, 0x80, 0xb2, + 0x14, 0x30, 0x00, 0x2f, 0x04, 0x30, 0x05, 0x2e, 0xc9, 0x00, 0x73, 0x6f, 0x81, 0x40, 0xe2, 0x40, 0x69, 0x04, 0x11, + 0x0f, 0xe1, 0x40, 0x16, 0x30, 0xfe, 0x29, 0xcb, 0x40, 0x02, 0x2f, 0x83, 0x6f, 0x83, 0x0f, 0x22, 0x2f, 0x47, 0x56, + 0x13, 0x0f, 0x12, 0x30, 0x77, 0x2f, 0x49, 0x54, 0x42, 0x0e, 0x12, 0x30, 0x73, 0x2f, 0x00, 0x91, 0x0a, 0x2f, 0x01, + 0x2e, 0x8b, 0x01, 0x19, 0xa8, 0x02, 0x30, 0x6c, 0x2f, 0x63, 0x50, 0x00, 0x2e, 0x17, 0x42, 0x05, 0x42, 0x68, 0x2c, + 0x12, 0x30, 0x0b, 0x25, 0x08, 0x0f, 0x50, 0x30, 0x02, 0x2f, 0x21, 0x2e, 0x83, 0x01, 0x03, 0x2d, 0x40, 0x30, 0x21, + 0x2e, 0x83, 0x01, 0x2b, 0x2e, 0x85, 0x01, 0x5a, 0x2c, 0x12, 0x30, 0x00, 0x91, 0x2b, 0x25, 0x04, 0x2f, 0x63, 0x50, + 0x02, 0x30, 0x17, 0x42, 0x17, 0x2c, 0x02, 0x42, 0x98, 0x2e, 0xfe, 0xc9, 0x10, 0x25, 0x98, 0x2e, 0x74, 0xc0, 0x05, + 0x2e, 0xc9, 0x00, 0x81, 0x84, 0x5b, 0x30, 0x82, 0x40, 0x37, 0x2e, 0x83, 0x01, 0x02, 0x0e, 0x07, 0x2f, 0x5f, 0x52, + 0x40, 0x30, 0x62, 0x40, 0x41, 0x40, 0x91, 0x0e, 0x01, 0x2f, 0x21, 0x2e, 0x83, 0x01, 0x05, 0x30, 0x2b, 0x2e, 0x85, + 0x01, 0x12, 0x30, 0x36, 0x2c, 0x16, 0x30, 0x15, 0x25, 0x81, 0x7f, 0x98, 0x2e, 0xfe, 0xc9, 0x10, 0x25, 0x98, 0x2e, + 0x74, 0xc0, 0x19, 0xa2, 0x16, 0x30, 0x15, 0x2f, 0x05, 0x2e, 0x97, 0x01, 0x80, 0x6f, 0x82, 0x0e, 0x05, 0x2f, 0x01, + 0x2e, 0x86, 0x01, 0x06, 0x28, 0x21, 0x2e, 0x86, 0x01, 0x0b, 0x2d, 0x03, 0x2e, 0x87, 0x01, 0x5f, 0x54, 0x4e, 0x28, + 0x91, 0x42, 0x00, 0x2e, 0x82, 0x40, 0x90, 0x0e, 0x01, 0x2f, 0x21, 0x2e, 0x88, 0x01, 0x02, 0x30, 0x13, 0x2c, 0x05, + 0x30, 0xc0, 0x6f, 0x08, 0x1c, 0xa8, 0x0f, 0x16, 0x30, 0x05, 0x30, 0x5b, 0x50, 0x09, 0x2f, 0x02, 0x80, 0x2d, 0x2e, + 0x82, 0x01, 0x05, 0x42, 0x05, 0x80, 0x00, 0x2e, 0x02, 0x42, 0x3e, 0x80, 0x00, 0x2e, 0x06, 0x42, 0x02, 0x30, 0x90, + 0x6f, 0x3e, 0x88, 0x01, 0x40, 0x04, 0x41, 0x4c, 0x28, 0x01, 0x42, 0x07, 0x80, 0x10, 0x25, 0x24, 0x40, 0x00, 0x40, + 0x00, 0xa8, 0xf5, 0x22, 0x23, 0x29, 0x44, 0x42, 0x7a, 0x82, 0x7e, 0x88, 0x43, 0x40, 0x04, 0x41, 0x00, 0xab, 0xf5, + 0x23, 0xdf, 0x28, 0x43, 0x42, 0xd9, 0xa0, 0x14, 0x2f, 0x00, 0x90, 0x02, 0x2f, 0xd2, 0x6f, 0x81, 0xb2, 0x05, 0x2f, + 0x63, 0x54, 0x06, 0x28, 0x90, 0x42, 0x85, 0x42, 0x09, 0x2c, 0x02, 0x30, 0x5b, 0x50, 0x03, 0x80, 0x29, 0x2e, 0x7e, + 0x01, 0x2b, 0x2e, 0x82, 0x01, 0x05, 0x42, 0x12, 0x30, 0x2b, 0x2e, 0x83, 0x01, 0x45, 0x82, 0x00, 0x2e, 0x40, 0x40, + 0x7a, 0x82, 0x02, 0xa0, 0x08, 0x2f, 0x63, 0x50, 0x3b, 0x30, 0x15, 0x42, 0x05, 0x42, 0x37, 0x80, 0x37, 0x2e, 0x7e, + 0x01, 0x05, 0x42, 0x12, 0x30, 0x01, 0x2e, 0xc9, 0x00, 0x02, 0x8c, 0x40, 0x40, 0x84, 0x41, 0x7a, 0x8c, 0x04, 0x0f, + 0x03, 0x2f, 0x01, 0x2e, 0x8b, 0x01, 0x19, 0xa4, 0x04, 0x2f, 0x2b, 0x2e, 0x82, 0x01, 0x98, 0x2e, 0xf3, 0x03, 0x12, + 0x30, 0x81, 0x90, 0x61, 0x52, 0x08, 0x2f, 0x65, 0x42, 0x65, 0x42, 0x43, 0x80, 0x39, 0x84, 0x82, 0x88, 0x05, 0x42, + 0x45, 0x42, 0x85, 0x42, 0x05, 0x43, 0x00, 0x2e, 0x80, 0x41, 0x00, 0x90, 0x90, 0x2e, 0xe1, 0xb4, 0x65, 0x54, 0xc1, + 0x6f, 0x80, 0x40, 0x00, 0xb2, 0x43, 0x58, 0x69, 0x50, 0x44, 0x2f, 0x55, 0x5c, 0xb7, 0x87, 0x8c, 0x0f, 0x0d, 0x2e, + 0x96, 0x01, 0xc4, 0x40, 0x36, 0x2f, 0x41, 0x56, 0x8b, 0x0e, 0x2a, 0x2f, 0x0b, 0x52, 0xa1, 0x0e, 0x0a, 0x2f, 0x05, + 0x2e, 0x8f, 0x01, 0x14, 0x25, 0x98, 0x2e, 0xfe, 0xc9, 0x4b, 0x54, 0x02, 0x0f, 0x69, 0x50, 0x05, 0x30, 0x65, 0x54, + 0x15, 0x2f, 0x03, 0x2e, 0x8e, 0x01, 0x4d, 0x5c, 0x8e, 0x0f, 0x3a, 0x2f, 0x05, 0x2e, 0x8f, 0x01, 0x98, 0x2e, 0xfe, + 0xc9, 0x4f, 0x54, 0x82, 0x0f, 0x05, 0x30, 0x69, 0x50, 0x65, 0x54, 0x30, 0x2f, 0x6d, 0x52, 0x15, 0x30, 0x42, 0x8c, + 0x45, 0x42, 0x04, 0x30, 0x2b, 0x2c, 0x84, 0x43, 0x6b, 0x52, 0x42, 0x8c, 0x00, 0x2e, 0x85, 0x43, 0x15, 0x30, 0x24, + 0x2c, 0x45, 0x42, 0x8e, 0x0f, 0x20, 0x2f, 0x0d, 0x2e, 0x8e, 0x01, 0xb1, 0x0e, 0x1c, 0x2f, 0x23, 0x2e, 0x8e, 0x01, + 0x1a, 0x2d, 0x0e, 0x0e, 0x17, 0x2f, 0xa1, 0x0f, 0x15, 0x2f, 0x23, 0x2e, 0x8d, 0x01, 0x13, 0x2d, 0x98, 0x2e, 0x74, + 0xc0, 0x43, 0x54, 0xc2, 0x0e, 0x0a, 0x2f, 0x65, 0x50, 0x04, 0x80, 0x0b, 0x30, 0x06, 0x82, 0x0b, 0x42, 0x79, 0x80, + 0x41, 0x40, 0x12, 0x30, 0x25, 0x2e, 0x8c, 0x01, 0x01, 0x42, 0x05, 0x30, 0x69, 0x50, 0x65, 0x54, 0x84, 0x82, 0x43, + 0x84, 0xbe, 0x8c, 0x84, 0x40, 0x86, 0x41, 0x26, 0x29, 0x94, 0x42, 0xbe, 0x8e, 0xd5, 0x7f, 0x19, 0xa1, 0x43, 0x40, + 0x0b, 0x2e, 0x8c, 0x01, 0x84, 0x40, 0xc7, 0x41, 0x5d, 0x29, 0x27, 0x29, 0x45, 0x42, 0x84, 0x42, 0xc2, 0x7f, 0x01, + 0x2f, 0xc0, 0xb3, 0x1d, 0x2f, 0x05, 0x2e, 0x94, 0x01, 0x99, 0xa0, 0x01, 0x2f, 0x80, 0xb3, 0x13, 0x2f, 0x80, 0xb3, + 0x18, 0x2f, 0xc0, 0xb3, 0x16, 0x2f, 0x12, 0x40, 0x01, 0x40, 0x92, 0x7f, 0x98, 0x2e, 0x74, 0xc0, 0x92, 0x6f, 0x10, + 0x0f, 0x20, 0x30, 0x03, 0x2f, 0x10, 0x30, 0x21, 0x2e, 0x7e, 0x01, 0x0a, 0x2d, 0x21, 0x2e, 0x7e, 0x01, 0x07, 0x2d, + 0x20, 0x30, 0x21, 0x2e, 0x7e, 0x01, 0x03, 0x2d, 0x10, 0x30, 0x21, 0x2e, 0x7e, 0x01, 0xc2, 0x6f, 0x01, 0x2e, 0xc9, + 0x00, 0xbc, 0x84, 0x02, 0x80, 0x82, 0x40, 0x00, 0x40, 0x90, 0x0e, 0xd5, 0x6f, 0x02, 0x2f, 0x15, 0x30, 0x98, 0x2e, + 0xf3, 0x03, 0x41, 0x91, 0x05, 0x30, 0x07, 0x2f, 0x67, 0x50, 0x3d, 0x80, 0x2b, 0x2e, 0x8f, 0x01, 0x05, 0x42, 0x04, + 0x80, 0x00, 0x2e, 0x05, 0x42, 0x02, 0x2c, 0x00, 0x30, 0x00, 0x30, 0xa2, 0x6f, 0x98, 0x8a, 0x86, 0x40, 0x80, 0xa7, + 0x05, 0x2f, 0x98, 0x2e, 0xf3, 0x03, 0xc0, 0x30, 0x21, 0x2e, 0x95, 0x01, 0x06, 0x25, 0x1a, 0x25, 0xe2, 0x6f, 0x76, + 0x82, 0x96, 0x40, 0x56, 0x43, 0x51, 0x0e, 0xfb, 0x2f, 0xbb, 0x6f, 0x30, 0x5f, 0xb8, 0x2e, 0x01, 0x2e, 0xb8, 0x00, + 0x01, 0x31, 0x41, 0x08, 0x40, 0xb2, 0x20, 0x50, 0xf2, 0x30, 0x02, 0x08, 0xfb, 0x7f, 0x01, 0x30, 0x10, 0x2f, 0x05, + 0x2e, 0xcc, 0x00, 0x81, 0x90, 0xe0, 0x7f, 0x03, 0x2f, 0x23, 0x2e, 0xcc, 0x00, 0x98, 0x2e, 0x55, 0xb6, 0x98, 0x2e, + 0x1d, 0xb5, 0x10, 0x25, 0xfb, 0x6f, 0xe0, 0x6f, 0xe0, 0x5f, 0x80, 0x2e, 0x95, 0xcf, 0x98, 0x2e, 0x95, 0xcf, 0x10, + 0x30, 0x21, 0x2e, 0xcc, 0x00, 0xfb, 0x6f, 0xe0, 0x5f, 0xb8, 0x2e, 0x00, 0x51, 0x05, 0x58, 0xeb, 0x7f, 0x2a, 0x25, + 0x89, 0x52, 0x6f, 0x5a, 0x89, 0x50, 0x13, 0x41, 0x06, 0x40, 0xb3, 0x01, 0x16, 0x42, 0xcb, 0x16, 0x06, 0x40, 0xf3, + 0x02, 0x13, 0x42, 0x65, 0x0e, 0xf5, 0x2f, 0x05, 0x40, 0x14, 0x30, 0x2c, 0x29, 0x04, 0x42, 0x08, 0xa1, 0x00, 0x30, + 0x90, 0x2e, 0x52, 0xb6, 0xb3, 0x88, 0xb0, 0x8a, 0xb6, 0x84, 0xa4, 0x7f, 0xc4, 0x7f, 0xb5, 0x7f, 0xd5, 0x7f, 0x92, + 0x7f, 0x73, 0x30, 0x04, 0x30, 0x55, 0x40, 0x42, 0x40, 0x8a, 0x17, 0xf3, 0x08, 0x6b, 0x01, 0x90, 0x02, 0x53, 0xb8, + 0x4b, 0x82, 0xad, 0xbe, 0x71, 0x7f, 0x45, 0x0a, 0x09, 0x54, 0x84, 0x7f, 0x98, 0x2e, 0xd9, 0xc0, 0xa3, 0x6f, 0x7b, + 0x54, 0xd0, 0x42, 0xa3, 0x7f, 0xf2, 0x7f, 0x60, 0x7f, 0x20, 0x25, 0x71, 0x6f, 0x75, 0x5a, 0x77, 0x58, 0x79, 0x5c, + 0x75, 0x56, 0x98, 0x2e, 0x67, 0xcc, 0xb1, 0x6f, 0x62, 0x6f, 0x50, 0x42, 0xb1, 0x7f, 0xb3, 0x30, 0x10, 0x25, 0x98, + 0x2e, 0x0f, 0xca, 0x84, 0x6f, 0x20, 0x29, 0x71, 0x6f, 0x92, 0x6f, 0xa5, 0x6f, 0x76, 0x82, 0x6a, 0x0e, 0x73, 0x30, + 0x00, 0x30, 0xd0, 0x2f, 0xd2, 0x6f, 0xd1, 0x7f, 0xb4, 0x7f, 0x98, 0x2e, 0x2b, 0xb7, 0x15, 0xbd, 0x0b, 0xb8, 0x02, + 0x0a, 0xc2, 0x6f, 0xc0, 0x7f, 0x98, 0x2e, 0x2b, 0xb7, 0x15, 0xbd, 0x0b, 0xb8, 0x42, 0x0a, 0xc0, 0x6f, 0x08, 0x17, + 0x41, 0x18, 0x89, 0x16, 0xe1, 0x18, 0xd0, 0x18, 0xa1, 0x7f, 0x27, 0x25, 0x16, 0x25, 0x98, 0x2e, 0x79, 0xc0, 0x8b, + 0x54, 0x90, 0x7f, 0xb3, 0x30, 0x82, 0x40, 0x80, 0x90, 0x0d, 0x2f, 0x7d, 0x52, 0x92, 0x6f, 0x98, 0x2e, 0x0f, 0xca, + 0xb2, 0x6f, 0x90, 0x0e, 0x06, 0x2f, 0x8b, 0x50, 0x14, 0x30, 0x42, 0x6f, 0x51, 0x6f, 0x14, 0x42, 0x12, 0x42, 0x01, + 0x42, 0x00, 0x2e, 0x31, 0x6f, 0x98, 0x2e, 0x74, 0xc0, 0x41, 0x6f, 0x80, 0x7f, 0x98, 0x2e, 0x74, 0xc0, 0x82, 0x6f, + 0x10, 0x04, 0x43, 0x52, 0x01, 0x0f, 0x05, 0x2e, 0xcb, 0x00, 0x00, 0x30, 0x04, 0x30, 0x21, 0x2f, 0x51, 0x6f, 0x43, + 0x58, 0x8c, 0x0e, 0x04, 0x30, 0x1c, 0x2f, 0x85, 0x88, 0x41, 0x6f, 0x04, 0x41, 0x8c, 0x0f, 0x04, 0x30, 0x16, 0x2f, + 0x84, 0x88, 0x00, 0x2e, 0x04, 0x41, 0x04, 0x05, 0x8c, 0x0e, 0x04, 0x30, 0x0f, 0x2f, 0x82, 0x88, 0x31, 0x6f, 0x04, + 0x41, 0x04, 0x05, 0x8c, 0x0e, 0x04, 0x30, 0x08, 0x2f, 0x83, 0x88, 0x00, 0x2e, 0x04, 0x41, 0x8c, 0x0f, 0x04, 0x30, + 0x02, 0x2f, 0x21, 0x2e, 0xad, 0x01, 0x14, 0x30, 0x00, 0x91, 0x14, 0x2f, 0x03, 0x2e, 0xa1, 0x01, 0x41, 0x90, 0x0e, + 0x2f, 0x03, 0x2e, 0xad, 0x01, 0x14, 0x30, 0x4c, 0x28, 0x23, 0x2e, 0xad, 0x01, 0x46, 0xa0, 0x06, 0x2f, 0x81, 0x84, + 0x8d, 0x52, 0x48, 0x82, 0x82, 0x40, 0x21, 0x2e, 0xa1, 0x01, 0x42, 0x42, 0x5c, 0x2c, 0x02, 0x30, 0x05, 0x2e, 0xaa, + 0x01, 0x80, 0xb2, 0x02, 0x30, 0x55, 0x2f, 0x03, 0x2e, 0xa9, 0x01, 0x92, 0x6f, 0xb3, 0x30, 0x98, 0x2e, 0x0f, 0xca, + 0xb2, 0x6f, 0x90, 0x0f, 0x00, 0x30, 0x02, 0x30, 0x4a, 0x2f, 0xa2, 0x6f, 0x87, 0x52, 0x91, 0x00, 0x85, 0x52, 0x51, + 0x0e, 0x02, 0x2f, 0x00, 0x2e, 0x43, 0x2c, 0x02, 0x30, 0xc2, 0x6f, 0x7f, 0x52, 0x91, 0x0e, 0x02, 0x30, 0x3c, 0x2f, + 0x51, 0x6f, 0x81, 0x54, 0x98, 0x2e, 0xfe, 0xc9, 0x10, 0x25, 0xb3, 0x30, 0x21, 0x25, 0x98, 0x2e, 0x0f, 0xca, 0x32, + 0x6f, 0xc0, 0x7f, 0xb3, 0x30, 0x12, 0x25, 0x98, 0x2e, 0x0f, 0xca, 0x42, 0x6f, 0xb0, 0x7f, 0xb3, 0x30, 0x12, 0x25, + 0x98, 0x2e, 0x0f, 0xca, 0xb2, 0x6f, 0x90, 0x28, 0x83, 0x52, 0x98, 0x2e, 0xfe, 0xc9, 0xc2, 0x6f, 0x90, 0x0f, 0x00, + 0x30, 0x02, 0x30, 0x1d, 0x2f, 0x05, 0x2e, 0xa1, 0x01, 0x80, 0xb2, 0x12, 0x30, 0x0f, 0x2f, 0x42, 0x6f, 0x03, 0x2e, + 0xab, 0x01, 0x91, 0x0e, 0x02, 0x30, 0x12, 0x2f, 0x52, 0x6f, 0x03, 0x2e, 0xac, 0x01, 0x91, 0x0f, 0x02, 0x30, 0x0c, + 0x2f, 0x21, 0x2e, 0xaa, 0x01, 0x0a, 0x2c, 0x12, 0x30, 0x03, 0x2e, 0xcb, 0x00, 0x8d, 0x58, 0x08, 0x89, 0x41, 0x40, + 0x11, 0x43, 0x00, 0x43, 0x25, 0x2e, 0xa1, 0x01, 0xd4, 0x6f, 0x8f, 0x52, 0x00, 0x43, 0x3a, 0x89, 0x00, 0x2e, 0x10, + 0x43, 0x10, 0x43, 0x61, 0x0e, 0xfb, 0x2f, 0x03, 0x2e, 0xa0, 0x01, 0x11, 0x1a, 0x02, 0x2f, 0x02, 0x25, 0x21, 0x2e, + 0xa0, 0x01, 0xeb, 0x6f, 0x00, 0x5f, 0xb8, 0x2e, 0x91, 0x52, 0x10, 0x30, 0x02, 0x30, 0x95, 0x56, 0x52, 0x42, 0x4b, + 0x0e, 0xfc, 0x2f, 0x8d, 0x54, 0x88, 0x82, 0x93, 0x56, 0x80, 0x42, 0x53, 0x42, 0x40, 0x42, 0x42, 0x86, 0x83, 0x54, + 0xc0, 0x2e, 0xc2, 0x42, 0x00, 0x2e, 0xa3, 0x52, 0x00, 0x51, 0x52, 0x40, 0x47, 0x40, 0x1a, 0x25, 0x01, 0x2e, 0x97, + 0x00, 0x8f, 0xbe, 0x72, 0x86, 0xfb, 0x7f, 0x0b, 0x30, 0x7c, 0xbf, 0xa5, 0x50, 0x10, 0x08, 0xdf, 0xba, 0x70, 0x88, + 0xf8, 0xbf, 0xcb, 0x42, 0xd3, 0x7f, 0x6c, 0xbb, 0xfc, 0xbb, 0xc5, 0x0a, 0x90, 0x7f, 0x1b, 0x7f, 0x0b, 0x43, 0xc0, + 0xb2, 0xe5, 0x7f, 0xb7, 0x7f, 0xa6, 0x7f, 0xc4, 0x7f, 0x90, 0x2e, 0x1c, 0xb7, 0x07, 0x2e, 0xd2, 0x00, 0xc0, 0xb2, + 0x0b, 0x2f, 0x97, 0x52, 0x01, 0x2e, 0xcd, 0x00, 0x82, 0x7f, 0x98, 0x2e, 0xbb, 0xcc, 0x0b, 0x30, 0x37, 0x2e, 0xd2, + 0x00, 0x82, 0x6f, 0x90, 0x6f, 0x1a, 0x25, 0x00, 0xb2, 0x8b, 0x7f, 0x14, 0x2f, 0xa6, 0xbd, 0x25, 0xbd, 0xb6, 0xb9, + 0x2f, 0xb9, 0x80, 0xb2, 0xd4, 0xb0, 0x0c, 0x2f, 0x99, 0x54, 0x9b, 0x56, 0x0b, 0x30, 0x0b, 0x2e, 0xb1, 0x00, 0xa1, + 0x58, 0x9b, 0x42, 0xdb, 0x42, 0x6c, 0x09, 0x2b, 0x2e, 0xb1, 0x00, 0x8b, 0x42, 0xcb, 0x42, 0x86, 0x7f, 0x73, 0x84, + 0xa7, 0x56, 0xc3, 0x08, 0x39, 0x52, 0x05, 0x50, 0x72, 0x7f, 0x63, 0x7f, 0x98, 0x2e, 0xc2, 0xc0, 0xe1, 0x6f, 0x62, + 0x6f, 0xd1, 0x0a, 0x01, 0x2e, 0xcd, 0x00, 0xd5, 0x6f, 0xc4, 0x6f, 0x72, 0x6f, 0x97, 0x52, 0x9d, 0x5c, 0x98, 0x2e, + 0x06, 0xcd, 0x23, 0x6f, 0x90, 0x6f, 0x99, 0x52, 0xc0, 0xb2, 0x04, 0xbd, 0x54, 0x40, 0xaf, 0xb9, 0x45, 0x40, 0xe1, + 0x7f, 0x02, 0x30, 0x06, 0x2f, 0xc0, 0xb2, 0x02, 0x30, 0x03, 0x2f, 0x9b, 0x5c, 0x12, 0x30, 0x94, 0x43, 0x85, 0x43, + 0x03, 0xbf, 0x6f, 0xbb, 0x80, 0xb3, 0x20, 0x2f, 0x06, 0x6f, 0x26, 0x01, 0x16, 0x6f, 0x6e, 0x03, 0x45, 0x42, 0xc0, + 0x90, 0x29, 0x2e, 0xce, 0x00, 0x9b, 0x52, 0x14, 0x2f, 0x9b, 0x5c, 0x00, 0x2e, 0x93, 0x41, 0x86, 0x41, 0xe3, 0x04, + 0xae, 0x07, 0x80, 0xab, 0x04, 0x2f, 0x80, 0x91, 0x0a, 0x2f, 0x86, 0x6f, 0x73, 0x0f, 0x07, 0x2f, 0x83, 0x6f, 0xc0, + 0xb2, 0x04, 0x2f, 0x54, 0x42, 0x45, 0x42, 0x12, 0x30, 0x04, 0x2c, 0x11, 0x30, 0x02, 0x2c, 0x11, 0x30, 0x11, 0x30, + 0x02, 0xbc, 0x0f, 0xb8, 0xd2, 0x7f, 0x00, 0xb2, 0x0a, 0x2f, 0x01, 0x2e, 0xfc, 0x00, 0x05, 0x2e, 0xc7, 0x01, 0x10, + 0x1a, 0x02, 0x2f, 0x21, 0x2e, 0xc7, 0x01, 0x03, 0x2d, 0x02, 0x2c, 0x01, 0x30, 0x01, 0x30, 0xb0, 0x6f, 0x98, 0x2e, + 0x95, 0xcf, 0xd1, 0x6f, 0xa0, 0x6f, 0x98, 0x2e, 0x95, 0xcf, 0xe2, 0x6f, 0x9f, 0x52, 0x01, 0x2e, 0xce, 0x00, 0x82, + 0x40, 0x50, 0x42, 0x0c, 0x2c, 0x42, 0x42, 0x11, 0x30, 0x23, 0x2e, 0xd2, 0x00, 0x01, 0x30, 0xb0, 0x6f, 0x98, 0x2e, + 0x95, 0xcf, 0xa0, 0x6f, 0x01, 0x30, 0x98, 0x2e, 0x95, 0xcf, 0x00, 0x2e, 0xfb, 0x6f, 0x00, 0x5f, 0xb8, 0x2e, 0x83, + 0x86, 0x01, 0x30, 0x00, 0x30, 0x94, 0x40, 0x24, 0x18, 0x06, 0x00, 0x53, 0x0e, 0x4f, 0x02, 0xf9, 0x2f, 0xb8, 0x2e, + 0xa9, 0x52, 0x00, 0x2e, 0x60, 0x40, 0x41, 0x40, 0x0d, 0xbc, 0x98, 0xbc, 0xc0, 0x2e, 0x01, 0x0a, 0x0f, 0xb8, 0xab, + 0x52, 0x53, 0x3c, 0x52, 0x40, 0x40, 0x40, 0x4b, 0x00, 0x82, 0x16, 0x26, 0xb9, 0x01, 0xb8, 0x41, 0x40, 0x10, 0x08, + 0x97, 0xb8, 0x01, 0x08, 0xc0, 0x2e, 0x11, 0x30, 0x01, 0x08, 0x43, 0x86, 0x25, 0x40, 0x04, 0x40, 0xd8, 0xbe, 0x2c, + 0x0b, 0x22, 0x11, 0x54, 0x42, 0x03, 0x80, 0x4b, 0x0e, 0xf6, 0x2f, 0xb8, 0x2e, 0x9f, 0x50, 0x10, 0x50, 0xad, 0x52, + 0x05, 0x2e, 0xd3, 0x00, 0xfb, 0x7f, 0x00, 0x2e, 0x13, 0x40, 0x93, 0x42, 0x41, 0x0e, 0xfb, 0x2f, 0x98, 0x2e, 0xa5, + 0xb7, 0x98, 0x2e, 0x87, 0xcf, 0x01, 0x2e, 0xd9, 0x00, 0x00, 0xb2, 0xfb, 0x6f, 0x0b, 0x2f, 0x01, 0x2e, 0x69, 0xf7, + 0xb1, 0x3f, 0x01, 0x08, 0x01, 0x30, 0xf0, 0x5f, 0x23, 0x2e, 0xd9, 0x00, 0x21, 0x2e, 0x69, 0xf7, 0x80, 0x2e, 0x7a, + 0xb7, 0xf0, 0x5f, 0xb8, 0x2e, 0x01, 0x2e, 0xc0, 0xf8, 0x03, 0x2e, 0xfc, 0xf5, 0x15, 0x54, 0xaf, 0x56, 0x82, 0x08, + 0x0b, 0x2e, 0x69, 0xf7, 0xcb, 0x0a, 0xb1, 0x58, 0x80, 0x90, 0xdd, 0xbe, 0x4c, 0x08, 0x5f, 0xb9, 0x59, 0x22, 0x80, + 0x90, 0x07, 0x2f, 0x03, 0x34, 0xc3, 0x08, 0xf2, 0x3a, 0x0a, 0x08, 0x02, 0x35, 0xc0, 0x90, 0x4a, 0x0a, 0x48, 0x22, + 0xc0, 0x2e, 0x23, 0x2e, 0xfc, 0xf5, 0x10, 0x50, 0xfb, 0x7f, 0x98, 0x2e, 0x56, 0xc7, 0x98, 0x2e, 0x49, 0xc3, 0x10, + 0x30, 0xfb, 0x6f, 0xf0, 0x5f, 0x21, 0x2e, 0xcc, 0x00, 0x21, 0x2e, 0xca, 0x00, 0xb8, 0x2e, 0x03, 0x2e, 0xd3, 0x00, + 0x16, 0xb8, 0x02, 0x34, 0x4a, 0x0c, 0x21, 0x2e, 0x2d, 0xf5, 0xc0, 0x2e, 0x23, 0x2e, 0xd3, 0x00, 0x03, 0xbc, 0x21, + 0x2e, 0xd5, 0x00, 0x03, 0x2e, 0xd5, 0x00, 0x40, 0xb2, 0x10, 0x30, 0x21, 0x2e, 0x77, 0x00, 0x01, 0x30, 0x05, 0x2f, + 0x05, 0x2e, 0xd8, 0x00, 0x80, 0x90, 0x01, 0x2f, 0x23, 0x2e, 0x6f, 0xf5, 0xc0, 0x2e, 0x21, 0x2e, 0xd9, 0x00, 0x11, + 0x30, 0x81, 0x08, 0x01, 0x2e, 0x6a, 0xf7, 0x71, 0x3f, 0x23, 0xbd, 0x01, 0x08, 0x02, 0x0a, 0xc0, 0x2e, 0x21, 0x2e, + 0x6a, 0xf7, 0x30, 0x25, 0x00, 0x30, 0x21, 0x2e, 0x5a, 0xf5, 0x10, 0x50, 0x21, 0x2e, 0x7b, 0x00, 0x21, 0x2e, 0x7c, + 0x00, 0xfb, 0x7f, 0x98, 0x2e, 0xc3, 0xb7, 0x40, 0x30, 0x21, 0x2e, 0xd4, 0x00, 0xfb, 0x6f, 0xf0, 0x5f, 0x03, 0x25, + 0x80, 0x2e, 0xaf, 0xb7, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, + 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, + 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, + 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x01, 0x2e, 0x5d, 0xf7, 0x08, 0xbc, 0x80, 0xac, 0x0e, 0xbb, 0x02, 0x2f, + 0x00, 0x30, 0x41, 0x04, 0x82, 0x06, 0xc0, 0xa4, 0x00, 0x30, 0x11, 0x2f, 0x40, 0xa9, 0x03, 0x2f, 0x40, 0x91, 0x0d, + 0x2f, 0x00, 0xa7, 0x0b, 0x2f, 0x80, 0xb3, 0xb3, 0x58, 0x02, 0x2f, 0x90, 0xa1, 0x26, 0x13, 0x20, 0x23, 0x80, 0x90, + 0x10, 0x30, 0x01, 0x2f, 0xcc, 0x0e, 0x00, 0x2f, 0x00, 0x30, 0xb8, 0x2e, 0xb5, 0x50, 0x18, 0x08, 0x08, 0xbc, 0x88, + 0xb6, 0x0d, 0x17, 0xc6, 0xbd, 0x56, 0xbc, 0xb7, 0x58, 0xda, 0xba, 0x04, 0x01, 0x1d, 0x0a, 0x10, 0x50, 0x05, 0x30, + 0x32, 0x25, 0x45, 0x03, 0xfb, 0x7f, 0xf6, 0x30, 0x21, 0x25, 0x98, 0x2e, 0x37, 0xca, 0x16, 0xb5, 0x9a, 0xbc, 0x06, + 0xb8, 0x80, 0xa8, 0x41, 0x0a, 0x0e, 0x2f, 0x80, 0x90, 0x02, 0x2f, 0x2d, 0x50, 0x48, 0x0f, 0x09, 0x2f, 0xbf, 0xa0, + 0x04, 0x2f, 0xbf, 0x90, 0x06, 0x2f, 0xb7, 0x54, 0xca, 0x0f, 0x03, 0x2f, 0x00, 0x2e, 0x02, 0x2c, 0xb7, 0x52, 0x2d, + 0x52, 0xf2, 0x33, 0x98, 0x2e, 0xd9, 0xc0, 0xfb, 0x6f, 0xf1, 0x37, 0xc0, 0x2e, 0x01, 0x08, 0xf0, 0x5f, 0xbf, 0x56, + 0xb9, 0x54, 0xd0, 0x40, 0xc4, 0x40, 0x0b, 0x2e, 0xfd, 0xf3, 0xbf, 0x52, 0x90, 0x42, 0x94, 0x42, 0x95, 0x42, 0x05, + 0x30, 0xc1, 0x50, 0x0f, 0x88, 0x06, 0x40, 0x04, 0x41, 0x96, 0x42, 0xc5, 0x42, 0x48, 0xbe, 0x73, 0x30, 0x0d, 0x2e, + 0xd8, 0x00, 0x4f, 0xba, 0x84, 0x42, 0x03, 0x42, 0x81, 0xb3, 0x02, 0x2f, 0x2b, 0x2e, 0x6f, 0xf5, 0x06, 0x2d, 0x05, + 0x2e, 0x77, 0xf7, 0xbd, 0x56, 0x93, 0x08, 0x25, 0x2e, 0x77, 0xf7, 0xbb, 0x54, 0x25, 0x2e, 0xc2, 0xf5, 0x07, 0x2e, + 0xfd, 0xf3, 0x42, 0x30, 0xb4, 0x33, 0xda, 0x0a, 0x4c, 0x00, 0x27, 0x2e, 0xfd, 0xf3, 0x43, 0x40, 0xd4, 0x3f, 0xdc, + 0x08, 0x43, 0x42, 0x00, 0x2e, 0x00, 0x2e, 0x43, 0x40, 0x24, 0x30, 0xdc, 0x0a, 0x43, 0x42, 0x04, 0x80, 0x03, 0x2e, + 0xfd, 0xf3, 0x4a, 0x0a, 0x23, 0x2e, 0xfd, 0xf3, 0x61, 0x34, 0xc0, 0x2e, 0x01, 0x42, 0x00, 0x2e, 0x60, 0x50, 0x1a, + 0x25, 0x7a, 0x86, 0xe0, 0x7f, 0xf3, 0x7f, 0x03, 0x25, 0xc3, 0x52, 0x41, 0x84, 0xdb, 0x7f, 0x33, 0x30, 0x98, 0x2e, + 0x16, 0xc2, 0x1a, 0x25, 0x7d, 0x82, 0xf0, 0x6f, 0xe2, 0x6f, 0x32, 0x25, 0x16, 0x40, 0x94, 0x40, 0x26, 0x01, 0x85, + 0x40, 0x8e, 0x17, 0xc4, 0x42, 0x6e, 0x03, 0x95, 0x42, 0x41, 0x0e, 0xf4, 0x2f, 0xdb, 0x6f, 0xa0, 0x5f, 0xb8, 0x2e, + 0xb0, 0x51, 0xfb, 0x7f, 0x98, 0x2e, 0xe8, 0x0d, 0x5a, 0x25, 0x98, 0x2e, 0x0f, 0x0e, 0xcb, 0x58, 0x32, 0x87, 0xc4, + 0x7f, 0x65, 0x89, 0x6b, 0x8d, 0xc5, 0x5a, 0x65, 0x7f, 0xe1, 0x7f, 0x83, 0x7f, 0xa6, 0x7f, 0x74, 0x7f, 0xd0, 0x7f, + 0xb6, 0x7f, 0x94, 0x7f, 0x17, 0x30, 0xc7, 0x52, 0xc9, 0x54, 0x51, 0x7f, 0x00, 0x2e, 0x85, 0x6f, 0x42, 0x7f, 0x00, + 0x2e, 0x51, 0x41, 0x45, 0x81, 0x42, 0x41, 0x13, 0x40, 0x3b, 0x8a, 0x00, 0x40, 0x4b, 0x04, 0xd0, 0x06, 0xc0, 0xac, + 0x85, 0x7f, 0x02, 0x2f, 0x02, 0x30, 0x51, 0x04, 0xd3, 0x06, 0x41, 0x84, 0x05, 0x30, 0x5d, 0x02, 0xc9, 0x16, 0xdf, + 0x08, 0xd3, 0x00, 0x8d, 0x02, 0xaf, 0xbc, 0xb1, 0xb9, 0x59, 0x0a, 0x65, 0x6f, 0x11, 0x43, 0xa1, 0xb4, 0x52, 0x41, + 0x53, 0x41, 0x01, 0x43, 0x34, 0x7f, 0x65, 0x7f, 0x26, 0x31, 0xe5, 0x6f, 0xd4, 0x6f, 0x98, 0x2e, 0x37, 0xca, 0x32, + 0x6f, 0x75, 0x6f, 0x83, 0x40, 0x42, 0x41, 0x23, 0x7f, 0x12, 0x7f, 0xf6, 0x30, 0x40, 0x25, 0x51, 0x25, 0x98, 0x2e, + 0x37, 0xca, 0x14, 0x6f, 0x20, 0x05, 0x70, 0x6f, 0x25, 0x6f, 0x69, 0x07, 0xa2, 0x6f, 0x31, 0x6f, 0x0b, 0x30, 0x04, + 0x42, 0x9b, 0x42, 0x8b, 0x42, 0x55, 0x42, 0x32, 0x7f, 0x40, 0xa9, 0xc3, 0x6f, 0x71, 0x7f, 0x02, 0x30, 0xd0, 0x40, + 0xc3, 0x7f, 0x03, 0x2f, 0x40, 0x91, 0x15, 0x2f, 0x00, 0xa7, 0x13, 0x2f, 0x00, 0xa4, 0x11, 0x2f, 0x84, 0xbd, 0x98, + 0x2e, 0x79, 0xca, 0x55, 0x6f, 0xb7, 0x54, 0x54, 0x41, 0x82, 0x00, 0xf3, 0x3f, 0x45, 0x41, 0xcb, 0x02, 0xf6, 0x30, + 0x98, 0x2e, 0x37, 0xca, 0x35, 0x6f, 0xa4, 0x6f, 0x41, 0x43, 0x03, 0x2c, 0x00, 0x43, 0xa4, 0x6f, 0x35, 0x6f, 0x17, + 0x30, 0x42, 0x6f, 0x51, 0x6f, 0x93, 0x40, 0x42, 0x82, 0x00, 0x41, 0xc3, 0x00, 0x03, 0x43, 0x51, 0x7f, 0x00, 0x2e, + 0x94, 0x40, 0x41, 0x41, 0x4c, 0x02, 0xc4, 0x6f, 0xd1, 0x56, 0x63, 0x0e, 0x74, 0x6f, 0x51, 0x43, 0xa5, 0x7f, 0x8a, + 0x2f, 0x09, 0x2e, 0xd8, 0x00, 0x01, 0xb3, 0x21, 0x2f, 0xcb, 0x58, 0x90, 0x6f, 0x13, 0x41, 0xb6, 0x6f, 0xe4, 0x7f, + 0x00, 0x2e, 0x91, 0x41, 0x14, 0x40, 0x92, 0x41, 0x15, 0x40, 0x17, 0x2e, 0x6f, 0xf5, 0xb6, 0x7f, 0xd0, 0x7f, 0xcb, + 0x7f, 0x98, 0x2e, 0x00, 0x0c, 0x07, 0x15, 0xc2, 0x6f, 0x14, 0x0b, 0x29, 0x2e, 0x6f, 0xf5, 0xc3, 0xa3, 0xc1, 0x8f, + 0xe4, 0x6f, 0xd0, 0x6f, 0xe6, 0x2f, 0x14, 0x30, 0x05, 0x2e, 0x6f, 0xf5, 0x14, 0x0b, 0x29, 0x2e, 0x6f, 0xf5, 0x18, + 0x2d, 0xcd, 0x56, 0x04, 0x32, 0xb5, 0x6f, 0x1c, 0x01, 0x51, 0x41, 0x52, 0x41, 0xc3, 0x40, 0xb5, 0x7f, 0xe4, 0x7f, + 0x98, 0x2e, 0x1f, 0x0c, 0xe4, 0x6f, 0x21, 0x87, 0x00, 0x43, 0x04, 0x32, 0xcf, 0x54, 0x5a, 0x0e, 0xef, 0x2f, 0x15, + 0x54, 0x09, 0x2e, 0x77, 0xf7, 0x22, 0x0b, 0x29, 0x2e, 0x77, 0xf7, 0xfb, 0x6f, 0x50, 0x5e, 0xb8, 0x2e, 0x10, 0x50, + 0x01, 0x2e, 0xd4, 0x00, 0x00, 0xb2, 0xfb, 0x7f, 0x51, 0x2f, 0x01, 0xb2, 0x48, 0x2f, 0x02, 0xb2, 0x42, 0x2f, 0x03, + 0x90, 0x56, 0x2f, 0xd7, 0x52, 0x79, 0x80, 0x42, 0x40, 0x81, 0x84, 0x00, 0x40, 0x42, 0x42, 0x98, 0x2e, 0x93, 0x0c, + 0xd9, 0x54, 0xd7, 0x50, 0xa1, 0x40, 0x98, 0xbd, 0x82, 0x40, 0x3e, 0x82, 0xda, 0x0a, 0x44, 0x40, 0x8b, 0x16, 0xe3, + 0x00, 0x53, 0x42, 0x00, 0x2e, 0x43, 0x40, 0x9a, 0x02, 0x52, 0x42, 0x00, 0x2e, 0x41, 0x40, 0x15, 0x54, 0x4a, 0x0e, + 0x3a, 0x2f, 0x3a, 0x82, 0x00, 0x30, 0x41, 0x40, 0x21, 0x2e, 0x85, 0x0f, 0x40, 0xb2, 0x0a, 0x2f, 0x98, 0x2e, 0xb1, + 0x0c, 0x98, 0x2e, 0x45, 0x0e, 0x98, 0x2e, 0x5b, 0x0e, 0xfb, 0x6f, 0xf0, 0x5f, 0x00, 0x30, 0x80, 0x2e, 0xce, 0xb7, + 0xdd, 0x52, 0xd3, 0x54, 0x42, 0x42, 0x4f, 0x84, 0x73, 0x30, 0xdb, 0x52, 0x83, 0x42, 0x1b, 0x30, 0x6b, 0x42, 0x23, + 0x30, 0x27, 0x2e, 0xd7, 0x00, 0x37, 0x2e, 0xd4, 0x00, 0x21, 0x2e, 0xd6, 0x00, 0x7a, 0x84, 0x17, 0x2c, 0x42, 0x42, + 0x30, 0x30, 0x21, 0x2e, 0xd4, 0x00, 0x12, 0x2d, 0x21, 0x30, 0x00, 0x30, 0x23, 0x2e, 0xd4, 0x00, 0x21, 0x2e, 0x7b, + 0xf7, 0x0b, 0x2d, 0x17, 0x30, 0x98, 0x2e, 0x51, 0x0c, 0xd5, 0x50, 0x0c, 0x82, 0x72, 0x30, 0x2f, 0x2e, 0xd4, 0x00, + 0x25, 0x2e, 0x7b, 0xf7, 0x40, 0x42, 0x00, 0x2e, 0xfb, 0x6f, 0xf0, 0x5f, 0xb8, 0x2e, 0x70, 0x50, 0x0a, 0x25, 0x39, + 0x86, 0xfb, 0x7f, 0xe1, 0x32, 0x62, 0x30, 0x98, 0x2e, 0xc2, 0xc4, 0xb5, 0x56, 0xa5, 0x6f, 0xab, 0x08, 0x91, 0x6f, + 0x4b, 0x08, 0xdf, 0x56, 0xc4, 0x6f, 0x23, 0x09, 0x4d, 0xba, 0x93, 0xbc, 0x8c, 0x0b, 0xd1, 0x6f, 0x0b, 0x09, 0xcb, + 0x52, 0xe1, 0x5e, 0x56, 0x42, 0xaf, 0x09, 0x4d, 0xba, 0x23, 0xbd, 0x94, 0x0a, 0xe5, 0x6f, 0x68, 0xbb, 0xeb, 0x08, + 0xbd, 0xb9, 0x63, 0xbe, 0xfb, 0x6f, 0x52, 0x42, 0xe3, 0x0a, 0xc0, 0x2e, 0x43, 0x42, 0x90, 0x5f, 0xd1, 0x50, 0x03, + 0x2e, 0x25, 0xf3, 0x13, 0x40, 0x00, 0x40, 0x9b, 0xbc, 0x9b, 0xb4, 0x08, 0xbd, 0xb8, 0xb9, 0x98, 0xbc, 0xda, 0x0a, + 0x08, 0xb6, 0x89, 0x16, 0xc0, 0x2e, 0x19, 0x00, 0x62, 0x02, 0x10, 0x50, 0xfb, 0x7f, 0x98, 0x2e, 0x81, 0x0d, 0x01, + 0x2e, 0xd4, 0x00, 0x31, 0x30, 0x08, 0x04, 0xfb, 0x6f, 0x01, 0x30, 0xf0, 0x5f, 0x23, 0x2e, 0xd6, 0x00, 0x21, 0x2e, + 0xd7, 0x00, 0xb8, 0x2e, 0x01, 0x2e, 0xd7, 0x00, 0x03, 0x2e, 0xd6, 0x00, 0x48, 0x0e, 0x01, 0x2f, 0x80, 0x2e, 0x1f, + 0x0e, 0xb8, 0x2e, 0xe3, 0x50, 0x21, 0x34, 0x01, 0x42, 0x82, 0x30, 0xc1, 0x32, 0x25, 0x2e, 0x62, 0xf5, 0x01, 0x00, + 0x22, 0x30, 0x01, 0x40, 0x4a, 0x0a, 0x01, 0x42, 0xb8, 0x2e, 0xe3, 0x54, 0xf0, 0x3b, 0x83, 0x40, 0xd8, 0x08, 0xe5, + 0x52, 0x83, 0x42, 0x00, 0x30, 0x83, 0x30, 0x50, 0x42, 0xc4, 0x32, 0x27, 0x2e, 0x64, 0xf5, 0x94, 0x00, 0x50, 0x42, + 0x40, 0x42, 0xd3, 0x3f, 0x84, 0x40, 0x7d, 0x82, 0xe3, 0x08, 0x40, 0x42, 0x83, 0x42, 0xb8, 0x2e, 0xdd, 0x52, 0x00, + 0x30, 0x40, 0x42, 0x7c, 0x86, 0xb9, 0x52, 0x09, 0x2e, 0x70, 0x0f, 0xbf, 0x54, 0xc4, 0x42, 0xd3, 0x86, 0x54, 0x40, + 0x55, 0x40, 0x94, 0x42, 0x85, 0x42, 0x21, 0x2e, 0xd7, 0x00, 0x42, 0x40, 0x25, 0x2e, 0xfd, 0xf3, 0xc0, 0x42, 0x7e, + 0x82, 0x05, 0x2e, 0x7d, 0x00, 0x80, 0xb2, 0x14, 0x2f, 0x05, 0x2e, 0x89, 0x00, 0x27, 0xbd, 0x2f, 0xb9, 0x80, 0x90, + 0x02, 0x2f, 0x21, 0x2e, 0x6f, 0xf5, 0x0c, 0x2d, 0x07, 0x2e, 0x71, 0x0f, 0x14, 0x30, 0x1c, 0x09, 0x05, 0x2e, 0x77, + 0xf7, 0xbd, 0x56, 0x47, 0xbe, 0x93, 0x08, 0x94, 0x0a, 0x25, 0x2e, 0x77, 0xf7, 0xe7, 0x54, 0x50, 0x42, 0x4a, 0x0e, + 0xfc, 0x2f, 0xb8, 0x2e, 0x50, 0x50, 0x02, 0x30, 0x43, 0x86, 0xe5, 0x50, 0xfb, 0x7f, 0xe3, 0x7f, 0xd2, 0x7f, 0xc0, + 0x7f, 0xb1, 0x7f, 0x00, 0x2e, 0x41, 0x40, 0x00, 0x40, 0x48, 0x04, 0x98, 0x2e, 0x74, 0xc0, 0x1e, 0xaa, 0xd3, 0x6f, + 0x14, 0x30, 0xb1, 0x6f, 0xe3, 0x22, 0xc0, 0x6f, 0x52, 0x40, 0xe4, 0x6f, 0x4c, 0x0e, 0x12, 0x42, 0xd3, 0x7f, 0xeb, + 0x2f, 0x03, 0x2e, 0x86, 0x0f, 0x40, 0x90, 0x11, 0x30, 0x03, 0x2f, 0x23, 0x2e, 0x86, 0x0f, 0x02, 0x2c, 0x00, 0x30, + 0xd0, 0x6f, 0xfb, 0x6f, 0xb0, 0x5f, 0xb8, 0x2e, 0x40, 0x50, 0xf1, 0x7f, 0x0a, 0x25, 0x3c, 0x86, 0xeb, 0x7f, 0x41, + 0x33, 0x22, 0x30, 0x98, 0x2e, 0xc2, 0xc4, 0xd3, 0x6f, 0xf4, 0x30, 0xdc, 0x09, 0x47, 0x58, 0xc2, 0x6f, 0x94, 0x09, + 0xeb, 0x58, 0x6a, 0xbb, 0xdc, 0x08, 0xb4, 0xb9, 0xb1, 0xbd, 0xe9, 0x5a, 0x95, 0x08, 0x21, 0xbd, 0xf6, 0xbf, 0x77, + 0x0b, 0x51, 0xbe, 0xf1, 0x6f, 0xeb, 0x6f, 0x52, 0x42, 0x54, 0x42, 0xc0, 0x2e, 0x43, 0x42, 0xc0, 0x5f, 0x50, 0x50, + 0xf5, 0x50, 0x31, 0x30, 0x11, 0x42, 0xfb, 0x7f, 0x7b, 0x30, 0x0b, 0x42, 0x11, 0x30, 0x02, 0x80, 0x23, 0x33, 0x01, + 0x42, 0x03, 0x00, 0x07, 0x2e, 0x80, 0x03, 0x05, 0x2e, 0xd3, 0x00, 0x23, 0x52, 0xe2, 0x7f, 0xd3, 0x7f, 0xc0, 0x7f, + 0x98, 0x2e, 0xb6, 0x0e, 0xd1, 0x6f, 0x08, 0x0a, 0x1a, 0x25, 0x7b, 0x86, 0xd0, 0x7f, 0x01, 0x33, 0x12, 0x30, 0x98, + 0x2e, 0xc2, 0xc4, 0xd1, 0x6f, 0x08, 0x0a, 0x00, 0xb2, 0x0d, 0x2f, 0xe3, 0x6f, 0x01, 0x2e, 0x80, 0x03, 0x51, 0x30, + 0xc7, 0x86, 0x23, 0x2e, 0x21, 0xf2, 0x08, 0xbc, 0xc0, 0x42, 0x98, 0x2e, 0xa5, 0xb7, 0x00, 0x2e, 0x00, 0x2e, 0xd0, + 0x2e, 0xb0, 0x6f, 0x0b, 0xb8, 0x03, 0x2e, 0x1b, 0x00, 0x08, 0x1a, 0xb0, 0x7f, 0x70, 0x30, 0x04, 0x2f, 0x21, 0x2e, + 0x21, 0xf2, 0x00, 0x2e, 0x00, 0x2e, 0xd0, 0x2e, 0x98, 0x2e, 0x6d, 0xc0, 0x98, 0x2e, 0x5d, 0xc0, 0xed, 0x50, 0x98, + 0x2e, 0x44, 0xcb, 0xef, 0x50, 0x98, 0x2e, 0x46, 0xc3, 0xf1, 0x50, 0x98, 0x2e, 0x53, 0xc7, 0x35, 0x50, 0x98, 0x2e, + 0x64, 0xcf, 0x10, 0x30, 0x98, 0x2e, 0xdc, 0x03, 0x20, 0x26, 0xc0, 0x6f, 0x02, 0x31, 0x12, 0x42, 0xab, 0x33, 0x0b, + 0x42, 0x37, 0x80, 0x01, 0x30, 0x01, 0x42, 0xf3, 0x37, 0xf7, 0x52, 0xfb, 0x50, 0x44, 0x40, 0xa2, 0x0a, 0x42, 0x42, + 0x8b, 0x31, 0x09, 0x2e, 0x5e, 0xf7, 0xf9, 0x54, 0xe3, 0x08, 0x83, 0x42, 0x1b, 0x42, 0x23, 0x33, 0x4b, 0x00, 0xbc, + 0x84, 0x0b, 0x40, 0x33, 0x30, 0x83, 0x42, 0x0b, 0x42, 0xe0, 0x7f, 0xd1, 0x7f, 0x98, 0x2e, 0x58, 0xb7, 0xd1, 0x6f, + 0x80, 0x30, 0x40, 0x42, 0x03, 0x30, 0xe0, 0x6f, 0xf3, 0x54, 0x04, 0x30, 0x00, 0x2e, 0x00, 0x2e, 0x01, 0x89, 0x62, + 0x0e, 0xfa, 0x2f, 0x43, 0x42, 0x11, 0x30, 0xfb, 0x6f, 0xc0, 0x2e, 0x01, 0x42, 0xb0, 0x5f, 0xc1, 0x4a, 0x00, 0x00, + 0x6d, 0x57, 0x00, 0x00, 0x77, 0x8e, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xd3, 0xff, 0xff, 0xff, 0xe5, 0xff, 0xff, + 0xff, 0xee, 0xe1, 0xff, 0xff, 0x7c, 0x13, 0x00, 0x00, 0x46, 0xe6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2e, 0x00, 0xc1, 0x80, + 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, + 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, + 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, + 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, + 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, + 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, + 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, + 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, + 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, + 0x2e, 0x00, 0xc1 +}; + +/*! @name Global array that stores the feature input configuration of BMI270 */ +const struct bmi2_feature_config bmi270_feat_in[BMI270_MAX_FEAT_IN] = { + { .type = BMI2_CONFIG_ID, .page = BMI2_PAGE_1, .start_addr = BMI270_CONFIG_ID_STRT_ADDR }, + { .type = BMI2_MAX_BURST_LEN, .page = BMI2_PAGE_1, .start_addr = BMI270_MAX_BURST_LEN_STRT_ADDR }, + { .type = BMI2_CRT_GYRO_SELF_TEST, .page = BMI2_PAGE_1, .start_addr = BMI270_CRT_GYRO_SELF_TEST_STRT_ADDR }, + { .type = BMI2_ABORT_CRT_GYRO_SELF_TEST, .page = BMI2_PAGE_1, .start_addr = BMI270_ABORT_STRT_ADDR }, + { .type = BMI2_AXIS_MAP, .page = BMI2_PAGE_1, .start_addr = BMI270_AXIS_MAP_STRT_ADDR }, + { .type = BMI2_GYRO_SELF_OFF, .page = BMI2_PAGE_1, .start_addr = BMI270_GYRO_SELF_OFF_STRT_ADDR }, + { .type = BMI2_NVM_PROG_PREP, .page = BMI2_PAGE_1, .start_addr = BMI270_NVM_PROG_PREP_STRT_ADDR }, + { .type = BMI2_GYRO_GAIN_UPDATE, .page = BMI2_PAGE_1, .start_addr = BMI270_GYRO_GAIN_UPDATE_STRT_ADDR }, + { .type = BMI2_ANY_MOTION, .page = BMI2_PAGE_1, .start_addr = BMI270_ANY_MOT_STRT_ADDR }, + { .type = BMI2_NO_MOTION, .page = BMI2_PAGE_2, .start_addr = BMI270_NO_MOT_STRT_ADDR }, + { .type = BMI2_SIG_MOTION, .page = BMI2_PAGE_2, .start_addr = BMI270_SIG_MOT_STRT_ADDR }, + { .type = BMI2_STEP_COUNTER_PARAMS, .page = BMI2_PAGE_3, .start_addr = BMI270_STEP_CNT_1_STRT_ADDR }, + { .type = BMI2_STEP_DETECTOR, .page = BMI2_PAGE_6, .start_addr = BMI270_STEP_CNT_4_STRT_ADDR }, + { .type = BMI2_STEP_COUNTER, .page = BMI2_PAGE_6, .start_addr = BMI270_STEP_CNT_4_STRT_ADDR }, + { .type = BMI2_STEP_ACTIVITY, .page = BMI2_PAGE_6, .start_addr = BMI270_STEP_CNT_4_STRT_ADDR }, + { .type = BMI2_WRIST_GESTURE, .page = BMI2_PAGE_6, .start_addr = BMI270_WRIST_GEST_STRT_ADDR }, + { .type = BMI2_WRIST_WEAR_WAKE_UP, .page = BMI2_PAGE_7, .start_addr = BMI270_WRIST_WEAR_WAKE_UP_STRT_ADDR }, +}; + +/*! @name Global array that stores the feature output configuration */ +const struct bmi2_feature_config bmi270_feat_out[BMI270_MAX_FEAT_OUT] = { + { .type = BMI2_STEP_COUNTER, .page = BMI2_PAGE_0, .start_addr = BMI270_STEP_CNT_OUT_STRT_ADDR }, + { .type = BMI2_STEP_ACTIVITY, .page = BMI2_PAGE_0, .start_addr = BMI270_STEP_ACT_OUT_STRT_ADDR }, + { .type = BMI2_WRIST_GESTURE, .page = BMI2_PAGE_0, .start_addr = BMI270_WRIST_GEST_OUT_STRT_ADDR }, + { .type = BMI2_GYRO_GAIN_UPDATE, .page = BMI2_PAGE_0, .start_addr = BMI270_GYR_USER_GAIN_OUT_STRT_ADDR }, + { .type = BMI2_GYRO_CROSS_SENSE, .page = BMI2_PAGE_0, .start_addr = BMI270_GYRO_CROSS_SENSE_STRT_ADDR }, + { .type = BMI2_NVM_STATUS, .page = BMI2_PAGE_0, .start_addr = BMI270_NVM_VFRM_OUT_STRT_ADDR }, + { .type = BMI2_VFRM_STATUS, .page = BMI2_PAGE_0, .start_addr = BMI270_NVM_VFRM_OUT_STRT_ADDR } +}; + +/*! @name Global array that stores the feature interrupts of BMI270 */ +struct bmi2_map_int bmi270_map_int[BMI270_MAX_INT_MAP] = { + { .type = BMI2_SIG_MOTION, .sens_map_int = BMI270_INT_SIG_MOT_MASK }, + { .type = BMI2_STEP_COUNTER, .sens_map_int = BMI270_INT_STEP_COUNTER_MASK }, + { .type = BMI2_STEP_DETECTOR, .sens_map_int = BMI270_INT_STEP_DETECTOR_MASK }, + { .type = BMI2_STEP_ACTIVITY, .sens_map_int = BMI270_INT_STEP_ACT_MASK }, + { .type = BMI2_WRIST_GESTURE, .sens_map_int = BMI270_INT_WRIST_GEST_MASK }, + { .type = BMI2_WRIST_WEAR_WAKE_UP, .sens_map_int = BMI270_INT_WRIST_WEAR_WAKEUP_MASK }, + { .type = BMI2_ANY_MOTION, .sens_map_int = BMI270_INT_ANY_MOT_MASK }, + { .type = BMI2_NO_MOTION, .sens_map_int = BMI270_INT_NO_MOT_MASK }, +}; + +/******************************************************************************/ + +/*! Local Function Prototypes + ******************************************************************************/ + +/*! + * @brief This internal API is used to validate the device pointer for + * null conditions. + * + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t null_ptr_check(const struct bmi2_dev *dev); + +/*! + * @brief This internal API enables the selected sensor/features. + * + * @param[in] sensor_sel : Selects the desired sensor. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t sensor_enable(uint64_t sensor_sel, struct bmi2_dev *dev); + +/*! + * @brief This internal API disables the selected sensor/features. + * + * @param[in] sensor_sel : Selects the desired sensor. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t sensor_disable(uint64_t sensor_sel, struct bmi2_dev *dev); + +/*! + * @brief This internal API selects the sensors/features to be enabled or + * disabled. + * + * @param[in] sens_list : Pointer to select the sensor. + * @param[in] n_sens : Number of sensors selected. + * @param[out] sensor_sel : Gets the selected sensor. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t select_sensor(const uint8_t *sens_list, uint8_t n_sens, uint64_t *sensor_sel); + +/*! + * @brief This internal API is used to enable/disable any-motion feature. + * + * @param[in] dev : Structure instance of bmi2_dev. + * @param[in] enable : Enables/Disables any-motion. + * + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Disables any-motion. + * BMI2_ENABLE | Enables any-motion. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_any_motion(uint8_t enable, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to enable/disable no-motion feature. + * + * @param[in] dev : Structure instance of bmi2_dev. + * @param[in] enable : Enables/Disables no-motion. + * + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Disables no-motion. + * BMI2_ENABLE | Enables no-motion. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_no_motion(uint8_t enable, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to enable/disable sig-motion feature. + * + * @param[in] dev : Structure instance of bmi2_dev. + * @param[in] enable : Enables/Disables sig-motion. + * + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Disables sig-motion. + * BMI2_ENABLE | Enables sig-motion. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_sig_motion(uint8_t enable, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to enable/disable step detector feature. + * + * @param[in] dev : Structure instance of bmi2_dev. + * @param[in] enable : Enables/Disables step-detector. + * + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Disables step detector + * BMI2_ENABLE | Enables step detector + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_step_detector(uint8_t enable, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to enable/disable step counter feature. + * + * @param[in] dev : Structure instance of bmi2_dev. + * @param[in] enable : Enables/Disables step counter. + * + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Disables step counter + * BMI2_ENABLE | Enables step counter + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_step_counter(uint8_t enable, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to enable/disable step activity detection. + * + * @param[in] dev : Structure instance of bmi2_dev. + * @param[in] enable : Enables/Disables step activity. + * + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Disables step activity + * BMI2_ENABLE | Enables step activity + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_step_activity(uint8_t enable, struct bmi2_dev *dev); + +/*! + * @brief This internal API gives an option to enable offset correction + * feature of gyroscope, either internally or by the host. + * + * @param[in] enable : Enables/Disables self-offset correction. + * @param[in] dev : Structure instance of bmi2_dev. + * + * enable | Description + * -------------|--------------- + * BMI2_ENABLE | gyroscope offset correction values are set internally + * BMI2_DISABLE | gyroscope offset correction values has to be set by host + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_gyro_self_offset_corr(uint8_t enable, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to enable/disable gyroscope user gain + * feature. + * + * @param[in] dev : Structure instance of bmi2_dev. + * @param[in] enable : Enables/Disables gyroscope user gain. + * + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Disables gyroscope user gain + * BMI2_ENABLE | Enables gyroscope user gain + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_gyro_user_gain(uint8_t enable, struct bmi2_dev *dev); + +/*! + * @brief This internal API enables the wrist gesture feature. + * + * @param[in] dev : Structure instance of bmi2_dev. + * @param[in] enable : Enables/Disables wrist gesture. + * + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Disables wrist gesture + * BMI2_ENABLE | Enables wrist gesture + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_wrist_gesture(uint8_t enable, struct bmi2_dev *dev); + +/*! + * @brief This internal API enables the wrist wear wake up feature. + * + * @param[in] dev : Structure instance of bmi2_dev. + * @param[in] enable : Enables/Disables wrist wear wake up. + * + * Enable | Description + * -------------|--------------- + * BMI2_DISABLE | Disables wrist wear wake up + * BMI2_ENABLE | Enables wrist wear wake up + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_wrist_wear_wake_up(uint8_t enable, struct bmi2_dev *dev); + +/*! + * @brief This internal API sets any-motion configurations like axes select, + * duration, threshold and output-configuration. + * + * @param[in] config : Structure instance of bmi2_any_motion_config. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @verbatim + *---------------------------------------------------------------------------- + * bmi2_any_motion_config | + * Structure parameters | Description + *--------------------------|-------------------------------------------------- + * | Defines the number of consecutive data points for + * | which the threshold condition must be respected, + * | for interrupt assertion. It is expressed in 50 Hz + * duration | samples (20 msec). + * | Range is 0 to 163sec. + * | Default value is 5 = 100ms. + * -------------------------|--------------------------------------------------- + * | Slope threshold value for in 5.11g format. + * threshold | Range is 0 to 1g. + * | Default value is 0xAA = 83mg. + * -------------------------|--------------------------------------------------- + * x_sel, y_sel, z_sel | Selects the feature on a per-axis basis + * -------------------------|--------------------------------------------------- + * @endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_any_motion_config(const struct bmi2_any_motion_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API sets no-motion configurations like axes select, + * duration, threshold and output-configuration. + * + * @param[in] config : Structure instance of bmi2_no_motion_config. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @verbatim + *---------------------------------------------------------------------------- + * bmi2_no_motion_config | + * Structure parameters | Description + *--------------------------|-------------------------------------------------- + * | Defines the number of consecutive data points for + * | which the threshold condition must be respected, + * | for interrupt assertion. It is expressed in 50 Hz + * duration | samples (20 msec). + * | Range is 0 to 163sec. + * | Default value is 5 = 100ms. + * -------------------------|--------------------------------------------------- + * | Slope threshold value for in 5.11g format. + * threshold | Range is 0 to 1g. + * | Default value is 0x90 = 70mg. + * -------------------------|--------------------------------------------------- + * x_sel, y_sel, z_sel | Selects the feature on a per-axis basis + * -------------------------|--------------------------------------------------- + * @endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_no_motion_config(const struct bmi2_no_motion_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API sets sig-motion configurations like block-size, + * output-configuration and other parameters. + * + * @param[in] config : Structure instance of bmi2_sig_motion_config. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + *---------------------------------------------------------------------------- + * bmi2_sig_motion_config | + * Structure parameters | Description + * -------------------------|--------------------------------------------------- + * | Defines the duration after which the significant + * block_size | motion interrupt is triggered. It is expressed in + * | 50 Hz samples (20 ms). Default value is 0xFA=5sec. + *--------------------------|--------------------------------------------------- + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_sig_motion_config(const struct bmi2_sig_motion_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API sets step counter parameter configurations. + * + * @param[in] step_count_params : Array that stores parameters 1 to 25. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_step_count_params_config(const uint16_t *step_count_params, struct bmi2_dev *dev); + +/*! + * @brief This internal API sets step counter/detector/activity configurations. + * + * @param[in] config : Structure instance of bmi2_step_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + *--------------------------------------------------------------------------- + * bmi2_step_config | + * Structure parameters | Description + *--------------------------|-------------------------------------------------- + * | The Step-counter will trigger output every time + * | the number of steps are counted. Holds implicitly + * water-mark level | a 20x factor, so the range is 0 to 10230, + * | with resolution of 20 steps. + * -------------------------|--------------------------------------------------- + * reset counter | Flag to reset the counted steps. + * -------------------------|--------------------------------------------------- + * @endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_step_config(const struct bmi2_step_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API sets wrist gesture configurations like wearable-arm, + * and output-configuration. + * + * @param[in] config : Structure instance of bmi2_wrist_gest_config. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @verbatim + * -------------------------------------------------------------------------------------------- + * bmi2_wrist_gest_config | + * Structure parameters | Description + * -------------------------------------------------------------------------------------------- + * | Device in left (0) or right (1) arm. By default, + * wearable_arm | the wearable device is assumed to be in left arm + * | i.e. default value is 0. + * -------------------------------------------------------------------------------------------- + * | Sine of the minimum tilt angle in portrait down + * | direction of the device when wrist is rolled away + * | (roll-out) from user. + * min_flick_peak | The configuration parameter is scaled by 2048 + * | i.e. 2048 * sin(angle). + * | Range is 1448 to 1774. Default value is 1774. + * -------------------------------------------------------------------------------------------- + * | Value of minimum time difference between wrist + * | roll-out and roll-in movement during flick gesture. + * min_flick_samples | Range is 3 to 5 samples at 50Hz (i.e. 0.06 to 0.1 seconds). + * | Default value is 4 (i.e. 0.08 seconds). + * -------------------------------------------------------------------------------------------- + * | Maximum time within which gesture movement has to be completed. + * max_duration | Range is 150 to 250 samples at 50Hz (i.e. 3 to 5 seconds). + * | Default value is 200 (i.e. 4 seconds). + * -------------------------------------------------------------------------------------------- + * @endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_wrist_gest_config(const struct bmi2_wrist_gest_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API sets wrist wear wake-up configurations like + * output-configuration. + * + * @param[in] config : Structure instance of + * bmi2_wrist_wear_wake_up_config. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @verbatim + *----------------------------------------------------------------------------- + * bmi2_wrist_wear_wake_up_config | + * Structure parameters | Description + *----------------------------------|------------------------------------------- + * | To set the wrist wear wake-up parameters like + * | min_angle_focus, min_angle_nonfocus, + * wrist_wear_wakeup_params | max_tilt_lr, max_tilt_ll, + * | max_tilt_pd and max_tilt_pu. + * ---------------------------------|------------------------------------------- + * @endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_wrist_wear_wake_up_config(const struct bmi2_wrist_wear_wake_up_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets any-motion configurations like axes select, + * duration, threshold and output-configuration. + * + * @param[out] config : Structure instance of bmi2_any_motion_config. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @verbatim + *---------------------------------------------------------------------------- + * bmi2_any_motion_config | + * Structure parameters | Description + *--------------------------|-------------------------------------------------- + * | Defines the number of consecutive data points for + * | which the threshold condition must be respected, + * | for interrupt assertion. It is expressed in 50 Hz + * duration | samples (20 msec). + * | Range is 0 to 163sec. + * | Default value is 5 = 100ms. + * -------------------------|--------------------------------------------------- + * | Slope threshold value for in 5.11g format. + * threshold | Range is 0 to 1g. + * | Default value is 0xAA = 83mg. + * -------------------------|--------------------------------------------------- + * select_x, select_y, | + * select_z | Selects the feature on a per-axis basis + * -------------------------|--------------------------------------------------- + * @endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_any_motion_config(struct bmi2_any_motion_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets no-motion configurations like axes select, + * duration, threshold and output-configuration. + * + * @param[out] config : Structure instance of bmi2_no_motion_config. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @verbatim + *---------------------------------------------------------------------------- + * bmi2_no_motion_config | + * Structure parameters | Description + *--------------------------|-------------------------------------------------- + * | Defines the number of consecutive data points for + * | which the threshold condition must be respected, + * | for interrupt assertion. It is expressed in 50 Hz + * duration | samples (20 msec). + * | Range is 0 to 163sec. + * | Default value is 5 = 100ms. + * -------------------------|--------------------------------------------------- + * | Slope threshold value for in 5.11g format. + * threshold | Range is 0 to 1g. + * | Default value is 0x90 = 70mg. + * -------------------------|--------------------------------------------------- + * select_x, select_y, | + * select_z | Selects the feature on a per-axis basis + * -------------------------|--------------------------------------------------- + * @endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_no_motion_config(struct bmi2_no_motion_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets sig-motion configurations like block-size, + * output-configuration and other parameters. + * + * @param[out] config : Structure instance of bmi2_sig_motion_config. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + *---------------------------------------------------------------------------- + * bmi2_sig_motion_config | + * Structure parameters | Description + * -------------------------|--------------------------------------------------- + * | Defines the duration after which the significant + * block_size | motion interrupt is triggered. It is expressed in + * | 50 Hz samples (20 ms). Default value is 0xFA=5sec. + *--------------------------|--------------------------------------------------- + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_sig_motion_config(struct bmi2_sig_motion_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets step counter parameter configurations. + * + * @param[in] step_count_params : Array that stores parameters 1 to 25. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_step_count_params_config(uint16_t *step_count_params, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets step counter/detector/activity configurations. + * + * @param[out] config : Structure instance of bmi2_step_config. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @verbatim + *---------------------------------------------------------------------------- + * bmi2_step_config | + * Structure parameters | Description + *--------------------------|-------------------------------------------------- + * | The Step-counter will trigger output every time + * | the number of steps are counted. Holds implicitly + * water-mark level | a 20x factor, so the range is 0 to 20460, + * | with resolution of 20 steps. + * -------------------------|--------------------------------------------------- + * reset counter | Flag to reset the counted steps. + * -------------------------|--------------------------------------------------- + * @endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_step_config(struct bmi2_step_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets wrist gesture configurations like wearable-arm, + * and output-configuration. + * + * @param[out] config : Structure instance of bmi2_wrist_gest_config. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @verbatim + * -------------------------------------------------------------------------------------------- + * bmi2_wrist_gest_config | + * Structure parameters | Description + * -------------------------------------------------------------------------------------------- + * | Device in left (0) or right (1) arm. By default, + * wearable_arm | the wearable device is assumed to be in left arm + * | i.e. default value is 0. + * -------------------------------------------------------------------------------------------- + * | Sine of the minimum tilt angle in portrait down + * | direction of the device when wrist is rolled away + * | (roll-out) from user. + * min_flick_peak | The configuration parameter is scaled by 2048 + * | i.e. 2048 * sin(angle). + * | Range is 1448 to 1774. Default value is 1774. + * -------------------------------------------------------------------------------------------- + * | Value of minimum time difference between wrist + * | roll-out and roll-in movement during flick gesture. + * min_flick_samples | Range is 3 to 5 samples at 50Hz (i.e. 0.06 to 0.1 seconds). + * | Default value is 4 (i.e. 0.08 seconds). + * -------------------------------------------------------------------------------------------- + * | Maximum time within which gesture movement has to be completed. + * max_duration | Range is 150 to 250 samples at 50Hz (i.e. 3 to 5 seconds). + * | Default value is 200 (i.e. 4 seconds). + * -------------------------------------------------------------------------------------------- + * @endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_wrist_gest_config(struct bmi2_wrist_gest_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets wrist wear wake-up configurations like + * output-configuration. + * + * @param[out] config : Structure instance of + * bmi2_wrist_wear_wake_up_config. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @verbatim + *----------------------------------------------------------------------------- + * bmi2_wrist_wear_wake_up_config | + * Structure parameters | Description + *----------------------------------|------------------------------------------- + * | To set the wrist wear wake-up parameters like + * | min_angle_focus, min_angle_nonfocus, + * wrist_wear_wakeup_params | max_tilt_lr, max_tilt_ll, + * | max_tilt_pd and max_tilt_pu. + * ---------------------------------|------------------------------------------- + * @endverbatim + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_wrist_wear_wake_up_config(struct bmi2_wrist_wear_wake_up_config *config, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets the output values of wrist gesture. + * + * @param[out] wrist_gest : Pointer to the stored wrist gesture. + * @param[in] dev : Structure instance of bmi2_dev. + * + * *wrist_gest | Output + * -------------|------------ + * 0x00 | UNKNOWN + * 0x01 | PUSH_ARM_DOWN + * 0x02 | PIVOT_UP + * 0x03 | WRIST_SHAKE_JIGGLE + * 0x04 | FLICK_IN + * 0x05 | FLICK_OUT + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_wrist_gest_status(uint8_t *wrist_gest, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets the output values of step activity. + * + * @param[out] step_act : Pointer to the stored step activity data. + * @param[in] dev : Structure instance of bmi2_dev. + * + * *step_act | Output + * -----------|------------ + * 0x00 | STILL + * 0x01 | WALKING + * 0x02 | RUNNING + * 0x03 | UNKNOWN + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fails + */ +static int8_t get_step_activity_output(uint8_t *step_act, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets the output values of step counter. + * + * @param[out] step_count : Pointer to the stored step counter data. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_step_counter_output(uint32_t *step_count, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets the error status related to NVM. + * + * @param[out] nvm_err_stat : Stores the NVM error status. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_nvm_error_status(struct bmi2_nvm_err_status *nvm_err_stat, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets the error status related to virtual frames. + * + * @param[out] vfrm_err_stat : Stores the VFRM related error status. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_vfrm_error_status(struct bmi2_vfrm_err_status *vfrm_err_stat, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to get enable status of gyroscope user gain + * update. + * + * @param[out] status : Stores status of gyroscope user gain update. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_user_gain_upd_status(uint8_t *status, struct bmi2_dev *dev); + +/*! + * @brief This internal API enables/disables compensation of the gain defined + * in the GAIN register. + * + * @param[in] enable : Enables/Disables gain compensation + * @param[in] dev : Structure instance of bmi2_dev. + * + * enable | Description + * -------------|--------------- + * BMI2_ENABLE | Enable gain compensation. + * BMI2_DISABLE | Disable gain compensation. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t enable_gyro_gain(uint8_t enable, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to extract the output feature configuration + * details like page and start address from the look-up table. + * + * @param[out] feat_output : Structure that stores output feature + * configurations. + * @param[in] type : Type of feature or sensor. + * @param[in] dev : Structure instance of bmi2_dev. + * + * @return Returns the feature found flag. + * + * @retval BMI2_FALSE : Feature not found + * BMI2_TRUE : Feature found + */ +static uint8_t extract_output_feat_config(struct bmi2_feature_config *feat_output, + uint8_t type, + const struct bmi2_dev *dev); + +/*! + * @brief This internal API sets feature configuration to the sensor. + * + * @param[in] sens_cfg : Structure instance of bmi2_sens_config. + * @param[in] loop : Variable to loop the sensor feature. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t set_feat_config(const struct bmi2_sens_config *sens_cfg, uint8_t loop, struct bmi2_dev *dev); + +/*! + * @brief This internal API gets feature configuration from the sensor. + * + * @param[in] sens_cfg : Structure instance of bmi2_sens_config. + * @param[in] loop : Variable to loop the sensor feature. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t get_feat_config(struct bmi2_sens_config *sens_cfg, uint8_t loop, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to enable main sensors like accel, gyro, aux and temperature. + * + * @param[in] sensor_sel : Enables the selected sensor. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t enable_main_sensors(uint64_t sensor_sel, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to enable sensor features. + * + * @param[in] sensor_sel : Enables features of selected sensor. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t enable_sensor_features(uint64_t sensor_sel, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to disable main sensors like accel, gyro, aux and temperature. + * + * @param[in] sensor_sel : Disables the selected sensor. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t disable_main_sensors(uint64_t sensor_sel, struct bmi2_dev *dev); + +/*! + * @brief This internal API is used to disable sensor features. + * + * @param[in] sensor_sel : Disables features of selected sensor. + * @param[in, out] dev : Structure instance of bmi2_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval < 0 -> Fail + */ +static int8_t disable_sensor_features(uint64_t sensor_sel, struct bmi2_dev *dev); + +/***************************************************************************/ + +/*! User Interface Definitions + ****************************************************************************/ + +/*! + * @brief This API: + * 1) updates the device structure with address of the configuration file. + * 2) Initializes BMI270 sensor. + * 3) Writes the configuration file. + * 4) Updates the feature offset parameters in the device structure. + * 5) Updates the maximum number of pages, in the device structure. + */ +int8_t bmi270_init(struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if (rslt == BMI2_OK) + { + /* Assign chip id of BMI270 */ + dev->chip_id = BMI270_CHIP_ID; + + /* get the size of config array */ + dev->config_size = sizeof(bmi270_config_file); + + /* Enable the variant specific features if any */ + dev->variant_feature = BMI2_GYRO_CROSS_SENS_ENABLE | BMI2_CRT_RTOSK_ENABLE; + + /* An extra dummy byte is read during SPI read */ + if (dev->intf == BMI2_SPI_INTF) + { + dev->dummy_byte = 1; + } + else + { + dev->dummy_byte = 0; + } + + /* If configuration file pointer is not assigned any address */ + if (!dev->config_file_ptr) + { + /* Give the address of the configuration file array to + * the device pointer + */ + dev->config_file_ptr = bmi270_config_file; + } + + /* Initialize BMI2 sensor */ + rslt = bmi2_sec_init(dev); + if (rslt == BMI2_OK) + { + /* Assign the offsets of the feature input + * configuration to the device structure + */ + dev->feat_config = bmi270_feat_in; + + /* Assign the offsets of the feature output to + * the device structure + */ + dev->feat_output = bmi270_feat_out; + + /* Assign the maximum number of pages to the + * device structure + */ + dev->page_max = BMI270_MAX_PAGE_NUM; + + /* Assign maximum number of input sensors/ + * features to device structure + */ + dev->input_sens = BMI270_MAX_FEAT_IN; + + /* Assign maximum number of output sensors/ + * features to device structure + */ + dev->out_sens = BMI270_MAX_FEAT_OUT; + + /* Assign the offsets of the feature interrupt + * to the device structure + */ + dev->map_int = bmi270_map_int; + + /* Assign maximum number of feature interrupts + * to device structure + */ + dev->sens_int_map = BMI270_MAX_INT_MAP; + + /* Get the gyroscope cross axis sensitivity */ + rslt = bmi2_get_gyro_cross_sense(dev); + } + } + + return rslt; +} + +/*! + * @brief This API selects the sensors/features to be enabled. + */ +int8_t bmi270_sensor_enable(const uint8_t *sens_list, uint8_t n_sens, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to select sensor */ + uint64_t sensor_sel = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (sens_list != NULL)) + { + /* Get the selected sensors */ + rslt = select_sensor(sens_list, n_sens, &sensor_sel); + if (rslt == BMI2_OK) + { + /* Enable the selected sensors */ + rslt = sensor_enable(sensor_sel, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API selects the sensors/features to be disabled. + */ +int8_t bmi270_sensor_disable(const uint8_t *sens_list, uint8_t n_sens, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to select sensor */ + uint64_t sensor_sel = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (sens_list != NULL)) + { + /* Get the selected sensors */ + rslt = select_sensor(sens_list, n_sens, &sensor_sel); + if (rslt == BMI2_OK) + { + /* Disable the selected sensors */ + rslt = sensor_disable(sensor_sel, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API sets the sensor/feature configuration. + */ +int8_t bmi270_set_sensor_config(struct bmi2_sens_config *sens_cfg, uint8_t n_sens, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define loop */ + uint8_t loop; + + /* Variable to get the status of advance power save */ + uint8_t aps_stat = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (sens_cfg != NULL)) + { + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + + for (loop = 0; loop < n_sens; loop++) + { + if ((sens_cfg[loop].type == BMI2_ACCEL) || (sens_cfg[loop].type == BMI2_GYRO) || + (sens_cfg[loop].type == BMI2_AUX) || (sens_cfg[loop].type == BMI2_GYRO_GAIN_UPDATE)) + { + rslt = bmi2_set_sensor_config(&sens_cfg[loop], 1, dev); + } + else + { + /* Disable Advance power save if enabled for auxiliary + * and feature configurations + */ + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if + * enabled + */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + if (rslt == BMI2_OK) + { + rslt = set_feat_config(sens_cfg, loop, dev); + } + /* Return error if any of the set configurations fail */ + else + { + break; + } + } + } + + /* Enable Advance power save if disabled while configuring and + * not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API gets the sensor/feature configuration. + */ +int8_t bmi270_get_sensor_config(struct bmi2_sens_config *sens_cfg, uint8_t n_sens, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define loop */ + uint8_t loop; + + /* Variable to get the status of advance power save */ + uint8_t aps_stat = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (sens_cfg != NULL)) + { + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + for (loop = 0; loop < n_sens; loop++) + { + if ((sens_cfg[loop].type == BMI2_ACCEL) || (sens_cfg[loop].type == BMI2_GYRO) || + (sens_cfg[loop].type == BMI2_AUX) || (sens_cfg[loop].type == BMI2_GYRO_GAIN_UPDATE)) + { + rslt = bmi2_get_sensor_config(&sens_cfg[loop], 1, dev); + } + else + { + /* Disable Advance power save if enabled for auxiliary + * and feature configurations + */ + if ((sens_cfg[loop].type >= BMI2_MAIN_SENS_MAX_NUM) || (sens_cfg[loop].type == BMI2_AUX)) + { + + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if + * enabled + */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + } + + if (rslt == BMI2_OK) + { + rslt = get_feat_config(sens_cfg, loop, dev); + } + /* Return error if any of the get configurations fail */ + else + { + break; + } + } + } + + /* Enable Advance power save if disabled while configuring and + * not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API gets the feature data. + */ +int8_t bmi270_get_feature_data(struct bmi2_feat_sensor_data *feature_data, uint8_t n_sens, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define loop */ + uint8_t loop; + + /* Variable to get the status of advance power save */ + uint8_t aps_stat = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (feature_data != NULL)) + { + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + for (loop = 0; loop < n_sens; loop++) + { + if ((feature_data[loop].type == BMI2_GYRO_GAIN_UPDATE) || + (feature_data[loop].type == BMI2_GYRO_CROSS_SENSE)) + { + rslt = bmi2_get_feature_data(&feature_data[loop], 1, dev); + } + else + { + /* Disable Advance power save if enabled for feature + * configurations + */ + if (feature_data[loop].type >= BMI2_MAIN_SENS_MAX_NUM) + { + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if + * enabled + */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + } + + if (rslt == BMI2_OK) + { + switch (feature_data[loop].type) + { + case BMI2_STEP_COUNTER: + + /* Get step counter output */ + rslt = get_step_counter_output(&feature_data[loop].sens_data.step_counter_output, dev); + break; + case BMI2_STEP_ACTIVITY: + + /* Get step activity output */ + rslt = get_step_activity_output(&feature_data[loop].sens_data.activity_output, dev); + break; + case BMI2_NVM_STATUS: + + /* Get NVM error status */ + rslt = get_nvm_error_status(&feature_data[loop].sens_data.nvm_status, dev); + break; + case BMI2_VFRM_STATUS: + + /* Get VFRM error status */ + rslt = get_vfrm_error_status(&feature_data[loop].sens_data.vfrm_status, dev); + break; + case BMI2_WRIST_GESTURE: + + /* Get wrist gesture status */ + rslt = get_wrist_gest_status(&feature_data[loop].sens_data.wrist_gesture_output, dev); + break; + default: + rslt = BMI2_E_INVALID_SENSOR; + break; + } + + /* Return error if any of the get sensor data fails */ + if (rslt != BMI2_OK) + { + break; + } + } + } + + /* Enable Advance power save if disabled while + * configuring and not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API updates the gyroscope user-gain. + */ +int8_t bmi270_update_gyro_user_gain(const struct bmi2_gyro_user_gain_config *user_gain, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to select sensor */ + uint8_t sens_sel[2] = { BMI2_GYRO, BMI2_GYRO_GAIN_UPDATE }; + + /* Structure to define sensor configurations */ + struct bmi2_sens_config sens_cfg; + + /* Variable to store status of user-gain update module */ + uint8_t status = 0; + + /* Variable to define count */ + uint8_t count = 100; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (user_gain != NULL)) + { + /* Select type of feature */ + sens_cfg.type = BMI2_GYRO_GAIN_UPDATE; + + /* Get the user gain configurations */ + rslt = bmi270_get_sensor_config(&sens_cfg, 1, dev); + if (rslt == BMI2_OK) + { + /* Get the user-defined ratio */ + sens_cfg.cfg.gyro_gain_update = *user_gain; + + /* Set rate ratio for all axes */ + rslt = bmi270_set_sensor_config(&sens_cfg, 1, dev); + } + + /* Disable gyroscope */ + if (rslt == BMI2_OK) + { + rslt = bmi270_sensor_disable(&sens_sel[0], 1, dev); + } + + /* Enable gyroscope user-gain update module */ + if (rslt == BMI2_OK) + { + rslt = bmi270_sensor_enable(&sens_sel[1], 1, dev); + } + + /* Set the command to trigger the computation */ + if (rslt == BMI2_OK) + { + rslt = bmi2_set_command_register(BMI2_USR_GAIN_CMD, dev); + } + + if (rslt == BMI2_OK) + { + /* Poll until enable bit of user-gain update is 0 */ + while (count--) + { + rslt = get_user_gain_upd_status(&status, dev); + if ((rslt == BMI2_OK) && (status == 0)) + { + /* Enable compensation of gain defined + * in the GAIN register + */ + rslt = enable_gyro_gain(BMI2_ENABLE, dev); + + /* Enable gyroscope */ + if (rslt == BMI2_OK) + { + rslt = bmi270_sensor_enable(&sens_sel[0], 1, dev); + } + + break; + } + + dev->delay_us(10000, dev->intf_ptr); + } + + /* Return error if user-gain update is failed */ + if ((rslt == BMI2_OK) && (status != 0)) + { + rslt = BMI2_E_GYR_USER_GAIN_UPD_FAIL; + } + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API reads the compensated gyroscope user-gain values. + */ +int8_t bmi270_read_gyro_user_gain(struct bmi2_gyro_user_gain_data *gyr_usr_gain, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define register data */ + uint8_t reg_data[3] = { 0 }; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (gyr_usr_gain != NULL)) + { + /* Get the gyroscope compensated gain values */ + rslt = bmi2_get_regs(BMI2_GYR_USR_GAIN_0_ADDR, reg_data, 3, dev); + if (rslt == BMI2_OK) + { + /* Gyroscope user gain correction X-axis */ + gyr_usr_gain->x = (int8_t)BMI2_GET_BIT_POS0(reg_data[0], BMI2_GYR_USR_GAIN_X); + + /* Gyroscope user gain correction Y-axis */ + gyr_usr_gain->y = (int8_t)BMI2_GET_BIT_POS0(reg_data[1], BMI2_GYR_USR_GAIN_Y); + + /* Gyroscope user gain correction z-axis */ + gyr_usr_gain->z = (int8_t)BMI2_GET_BIT_POS0(reg_data[2], BMI2_GYR_USR_GAIN_Z); + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API maps/unmaps feature interrupts to that of interrupt pins. + */ +int8_t bmi270_map_feat_int(const struct bmi2_sens_int_config *sens_int, uint8_t n_sens, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define loop */ + uint8_t loop; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if ((rslt == BMI2_OK) && (sens_int != NULL)) + { + for (loop = 0; loop < n_sens; loop++) + { + switch (sens_int[loop].type) + { + case BMI2_SIG_MOTION: + case BMI2_WRIST_GESTURE: + case BMI2_ANY_MOTION: + case BMI2_NO_MOTION: + case BMI2_STEP_COUNTER: + case BMI2_STEP_DETECTOR: + case BMI2_STEP_ACTIVITY: + case BMI2_WRIST_WEAR_WAKE_UP: + + rslt = bmi2_map_feat_int(sens_int[loop].type, sens_int[loop].hw_int_pin, dev); + break; + default: + rslt = BMI2_E_INVALID_SENSOR; + break; + } + + /* Return error if interrupt mapping fails */ + if (rslt != BMI2_OK) + { + break; + } + } + } + else + { + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/***************************************************************************/ + +/*! Local Function Definitions + ****************************************************************************/ + +/*! + * @brief This internal API is used to validate the device structure pointer for + * null conditions. + */ +static int8_t null_ptr_check(const struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + if ((dev == NULL) || (dev->read == NULL) || (dev->write == NULL) || (dev->delay_us == NULL)) + { + /* Device structure pointer is not valid */ + rslt = BMI2_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This internal API selects the sensor/features to be enabled or + * disabled. + */ +static int8_t select_sensor(const uint8_t *sens_list, uint8_t n_sens, uint64_t *sensor_sel) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Variable to define loop */ + uint8_t count; + + for (count = 0; count < n_sens; count++) + { + switch (sens_list[count]) + { + case BMI2_ACCEL: + *sensor_sel |= BMI2_ACCEL_SENS_SEL; + break; + case BMI2_GYRO: + *sensor_sel |= BMI2_GYRO_SENS_SEL; + break; + case BMI2_AUX: + *sensor_sel |= BMI2_AUX_SENS_SEL; + break; + case BMI2_TEMP: + *sensor_sel |= BMI2_TEMP_SENS_SEL; + break; + case BMI2_SIG_MOTION: + *sensor_sel |= BMI2_SIG_MOTION_SEL; + break; + case BMI2_ANY_MOTION: + *sensor_sel |= BMI2_ANY_MOT_SEL; + break; + case BMI2_NO_MOTION: + *sensor_sel |= BMI2_NO_MOT_SEL; + break; + case BMI2_STEP_DETECTOR: + *sensor_sel |= BMI2_STEP_DETECT_SEL; + break; + case BMI2_STEP_COUNTER: + *sensor_sel |= BMI2_STEP_COUNT_SEL; + break; + case BMI2_STEP_ACTIVITY: + *sensor_sel |= BMI2_STEP_ACT_SEL; + break; + case BMI2_GYRO_GAIN_UPDATE: + *sensor_sel |= BMI2_GYRO_GAIN_UPDATE_SEL; + break; + case BMI2_GYRO_SELF_OFF: + *sensor_sel |= BMI2_GYRO_SELF_OFF_SEL; + break; + case BMI2_WRIST_GESTURE: + *sensor_sel |= BMI2_WRIST_GEST_SEL; + break; + case BMI2_WRIST_WEAR_WAKE_UP: + *sensor_sel |= BMI2_WRIST_WEAR_WAKE_UP_SEL; + break; + default: + rslt = BMI2_E_INVALID_SENSOR; + break; + } + } + + return rslt; +} + +/*! + * @brief This internal API enables the selected sensor/features. + */ +static int8_t sensor_enable(uint64_t sensor_sel, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to get the status of advance power save */ + uint8_t aps_stat = 0; + + rslt = enable_main_sensors(sensor_sel, dev); + + if ((rslt == BMI2_OK) && (sensor_sel & ~(BMI2_MAIN_SENSORS))) + { + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if enabled */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + if (rslt == BMI2_OK) + { + rslt = enable_sensor_features(sensor_sel, dev); + + /* Enable Advance power save if disabled while + * configuring and not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This internal API disables the selected sensors/features. + */ +static int8_t sensor_disable(uint64_t sensor_sel, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to get the status of advance power save */ + uint8_t aps_stat = 0; + + rslt = disable_main_sensors(sensor_sel, dev); + + if ((rslt == BMI2_OK) && (sensor_sel & ~(BMI2_MAIN_SENSORS))) + { + /* Get status of advance power save mode */ + aps_stat = dev->aps_status; + if (aps_stat == BMI2_ENABLE) + { + /* Disable advance power save if enabled */ + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + if (rslt == BMI2_OK) + { + rslt = disable_sensor_features(sensor_sel, dev); + + /* Enable Advance power save if disabled while + * configuring and not when already disabled + */ + if ((aps_stat == BMI2_ENABLE) && (rslt == BMI2_OK)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This internal API is used to enable/disable any motion feature. + */ +static int8_t set_any_motion(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for any-motion */ + struct bmi2_feature_config any_mot_config = { 0, 0, 0 }; + + /* Search for any-motion feature and extract its configurations details */ + feat_found = bmi2_extract_input_feat_config(&any_mot_config, BMI2_ANY_MOTION, dev); + if (feat_found) + { + /* Get the configuration from the page where any-motion feature resides */ + rslt = bmi2_get_feat_config(any_mot_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for enable/disable of any-motion axes */ + idx = any_mot_config.start_addr + BMI2_ANY_MOT_FEAT_EN_OFFSET; + + /* Set the feature enable bit */ + feat_config[idx] = BMI2_SET_BITS(feat_config[idx], BMI2_ANY_NO_MOT_EN, enable); + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + + if ((rslt == BMI2_OK) && (enable == BMI2_ENABLE)) + { + dev->sens_en_stat |= BMI2_ANY_MOT_SEL; + } + else + { + dev->sens_en_stat &= ~BMI2_ANY_MOT_SEL; + } + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API is used to enable/disable no-motion feature. + */ +static int8_t set_no_motion(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for no-motion */ + struct bmi2_feature_config no_mot_config = { 0, 0, 0 }; + + /* Search for no-motion feature and extract its configurations details */ + feat_found = bmi2_extract_input_feat_config(&no_mot_config, BMI2_NO_MOTION, dev); + if (feat_found) + { + /* Get the configuration from the page where any/no-motion feature resides */ + rslt = bmi2_get_feat_config(no_mot_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for enable/disable of no-motion axes */ + idx = no_mot_config.start_addr + BMI2_NO_MOT_FEAT_EN_OFFSET; + + /* Set the feature enable bit */ + feat_config[idx] = BMI2_SET_BITS(feat_config[idx], BMI2_ANY_NO_MOT_EN, enable); + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + + if ((rslt == BMI2_OK) && (enable == BMI2_ENABLE)) + { + dev->sens_en_stat |= BMI2_NO_MOT_SEL; + } + else + { + dev->sens_en_stat &= ~BMI2_NO_MOT_SEL; + } + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API is used to enable/disable step detector feature. + */ +static int8_t set_step_detector(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for step detector */ + struct bmi2_feature_config step_det_config = { 0, 0, 0 }; + + /* Search for step detector feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&step_det_config, BMI2_STEP_DETECTOR, dev); + if (feat_found) + { + /* Get the configuration from the page where step detector feature resides */ + rslt = bmi2_get_feat_config(step_det_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for enable/disable of step detector */ + idx = step_det_config.start_addr + BMI2_STEP_COUNT_FEAT_EN_OFFSET; + + /* Set the feature enable bit */ + feat_config[idx] = BMI2_SET_BITS(feat_config[idx], BMI2_STEP_DET_FEAT_EN, enable); + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + + if ((rslt == BMI2_OK) && (enable == BMI2_ENABLE)) + { + dev->sens_en_stat |= BMI2_STEP_DETECT_SEL; + } + else + { + dev->sens_en_stat &= ~BMI2_STEP_DETECT_SEL; + } + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API is used to enable/disable step counter feature. + */ +static int8_t set_step_counter(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for step counter */ + struct bmi2_feature_config step_count_config = { 0, 0, 0 }; + + /* Search for step counter feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&step_count_config, BMI2_STEP_COUNTER, dev); + if (feat_found) + { + /* Get the configuration from the page where step-counter feature resides */ + rslt = bmi2_get_feat_config(step_count_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for enable/disable of step counter */ + idx = step_count_config.start_addr + BMI2_STEP_COUNT_FEAT_EN_OFFSET; + + /* Set the feature enable bit */ + feat_config[idx] = BMI2_SET_BITS(feat_config[idx], BMI2_STEP_COUNT_FEAT_EN, enable); + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + + if ((rslt == BMI2_OK) && (enable == BMI2_ENABLE)) + { + dev->sens_en_stat |= BMI2_STEP_COUNT_SEL; + } + else + { + dev->sens_en_stat &= ~BMI2_STEP_COUNT_SEL; + } + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API is used to enable/disable sig-motion feature. + */ +static int8_t set_sig_motion(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for sig-motion */ + struct bmi2_feature_config sig_mot_config = { 0, 0, 0 }; + + /* Search for sig-motion feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&sig_mot_config, BMI2_SIG_MOTION, dev); + if (feat_found) + { + /* Get the configuration from the page where sig-motion feature resides */ + rslt = bmi2_get_feat_config(sig_mot_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for enable/disable of sig-motion */ + idx = sig_mot_config.start_addr + BMI2_SIG_MOT_FEAT_EN_OFFSET; + + /* Set the feature enable bit */ + feat_config[idx] = BMI2_SET_BIT_POS0(feat_config[idx], BMI2_SIG_MOT_FEAT_EN, enable); + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + + if ((rslt == BMI2_OK) && (enable == BMI2_ENABLE)) + { + dev->sens_en_stat |= BMI2_SIG_MOTION_SEL; + } + else + { + dev->sens_en_stat &= ~BMI2_SIG_MOTION_SEL; + } + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API is used to enable/disable step activity detection. + */ +static int8_t set_step_activity(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for step activity */ + struct bmi2_feature_config step_act_config = { 0, 0, 0 }; + + /* Search for step activity feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&step_act_config, BMI2_STEP_ACTIVITY, dev); + if (feat_found) + { + /* Get the configuration from the page where step-activity + * feature resides + */ + rslt = bmi2_get_feat_config(step_act_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for enable/disable of step activity */ + idx = step_act_config.start_addr + BMI2_STEP_COUNT_FEAT_EN_OFFSET; + + /* Set the feature enable bit */ + feat_config[idx] = BMI2_SET_BITS(feat_config[idx], BMI2_STEP_ACT_FEAT_EN, enable); + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + + if ((rslt == BMI2_OK) && (enable == BMI2_ENABLE)) + { + dev->sens_en_stat |= BMI2_STEP_ACT_SEL; + } + else + { + dev->sens_en_stat &= ~BMI2_STEP_ACT_SEL; + } + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API gives an option to enable self-offset correction + * feature of gyroscope, either internally or by the host. + */ +static int8_t set_gyro_self_offset_corr(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for self-offset correction */ + struct bmi2_feature_config self_off_corr_cfg = { 0, 0, 0 }; + + /* Search for self-offset correction and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&self_off_corr_cfg, BMI2_GYRO_SELF_OFF, dev); + if (feat_found) + { + /* Get the configuration from the page where self-offset + * correction feature resides + */ + rslt = bmi2_get_feat_config(self_off_corr_cfg.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for enable/disable of self-offset correction */ + idx = self_off_corr_cfg.start_addr; + + /* Set the feature enable bit */ + feat_config[idx] = BMI2_SET_BITS(feat_config[idx], BMI2_GYR_SELF_OFF_CORR_FEAT_EN, enable); + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + + if ((rslt == BMI2_OK) && (enable == BMI2_ENABLE)) + { + dev->sens_en_stat |= BMI2_GYRO_SELF_OFF_SEL; + } + else + { + dev->sens_en_stat &= ~BMI2_GYRO_SELF_OFF_SEL; + } + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API enables the wrist gesture feature. + */ +static int8_t set_wrist_gesture(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for wrist gesture */ + struct bmi2_feature_config wrist_gest_cfg = { 0, 0, 0 }; + + /* Search for wrist gesture and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&wrist_gest_cfg, BMI2_WRIST_GESTURE, dev); + if (feat_found) + { + /* Get the configuration from the page where wrist gesture feature resides */ + rslt = bmi2_get_feat_config(wrist_gest_cfg.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for enable/disable of wrist gesture */ + idx = wrist_gest_cfg.start_addr; + + /* Set the feature enable bit */ + feat_config[idx] = BMI2_SET_BITS(feat_config[idx], BMI2_WRIST_GEST_FEAT_EN, enable); + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + + if ((rslt == BMI2_OK) && (enable == BMI2_ENABLE)) + { + dev->sens_en_stat |= BMI2_WRIST_GEST_SEL; + } + else + { + dev->sens_en_stat &= ~BMI2_WRIST_GEST_SEL; + } + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API enables the wrist wear wake up feature. + */ +static int8_t set_wrist_wear_wake_up(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for wrist wear wake up */ + struct bmi2_feature_config wrist_wake_up_cfg = { 0, 0, 0 }; + + /* Search for wrist wear wake up and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&wrist_wake_up_cfg, BMI2_WRIST_WEAR_WAKE_UP, dev); + if (feat_found) + { + /* Get the configuration from the page where wrist wear wake up + * feature resides + */ + rslt = bmi2_get_feat_config(wrist_wake_up_cfg.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for enable/disable of wrist wear wake up */ + idx = wrist_wake_up_cfg.start_addr; + + /* Set the feature enable bit */ + feat_config[idx] = BMI2_SET_BITS(feat_config[idx], BMI2_WRIST_WEAR_WAKE_UP_FEAT_EN, enable); + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + + if ((rslt == BMI2_OK) && (enable == BMI2_ENABLE)) + { + dev->sens_en_stat |= BMI2_WRIST_WEAR_WAKE_UP_SEL; + } + else + { + dev->sens_en_stat &= ~BMI2_WRIST_WEAR_WAKE_UP_SEL; + } + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API is used to enable/disable gyroscope user gain + * feature. + */ +static int8_t set_gyro_user_gain(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for gyroscope user gain */ + struct bmi2_feature_config gyr_user_gain_cfg = { 0, 0, 0 }; + + /* Search for user gain feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&gyr_user_gain_cfg, BMI2_GYRO_GAIN_UPDATE, dev); + if (feat_found) + { + /* Get the configuration from the page where user gain feature resides */ + rslt = bmi2_get_feat_config(gyr_user_gain_cfg.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for enable/disable of user gain */ + idx = gyr_user_gain_cfg.start_addr + BMI2_GYR_USER_GAIN_FEAT_EN_OFFSET; + + /* Set the feature enable bit */ + feat_config[idx] = BMI2_SET_BITS(feat_config[idx], BMI2_GYR_USER_GAIN_FEAT_EN, enable); + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + + if ((rslt == BMI2_OK) && (enable == BMI2_ENABLE)) + { + dev->sens_en_stat |= BMI2_GYRO_GAIN_UPDATE_SEL; + } + else + { + dev->sens_en_stat &= ~BMI2_GYRO_GAIN_UPDATE_SEL; + } + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API sets any-motion configurations like axes select, + * duration, threshold and output-configuration. + */ +static int8_t set_any_motion_config(const struct bmi2_any_motion_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to define count */ + uint8_t index = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for any motion */ + struct bmi2_feature_config any_mot_config = { 0, 0, 0 }; + + /* Copy the feature configuration address to a local pointer */ + uint16_t *data_p = (uint16_t *) (void *)feat_config; + + /* Search for any-motion feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&any_mot_config, BMI2_ANY_MOTION, dev); + if (feat_found) + { + /* Get the configuration from the page where any-motion feature resides */ + rslt = bmi2_get_feat_config(any_mot_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for any-motion select */ + idx = any_mot_config.start_addr; + + /* Get offset in words since all the features are set in words length */ + idx = idx / 2; + + /* Set duration */ + *(data_p + idx) = BMI2_SET_BIT_POS0(*(data_p + idx), BMI2_ANY_NO_MOT_DUR, config->duration); + + /* Set x-select */ + *(data_p + idx) = BMI2_SET_BITS(*(data_p + idx), BMI2_ANY_NO_MOT_X_SEL, config->select_x); + + /* Set y-select */ + *(data_p + idx) = BMI2_SET_BITS(*(data_p + idx), BMI2_ANY_NO_MOT_Y_SEL, config->select_y); + + /* Set z-select */ + *(data_p + idx) = BMI2_SET_BITS(*(data_p + idx), BMI2_ANY_NO_MOT_Z_SEL, config->select_z); + + /* Increment offset by 1 word to set threshold and output configuration */ + idx++; + + /* Set threshold */ + *(data_p + idx) = BMI2_SET_BIT_POS0(*(data_p + idx), BMI2_ANY_NO_MOT_THRES, config->threshold); + + /* Increment offset by 1 more word to get the total length in words */ + idx++; + + /* Get total length in bytes to copy from local pointer to the array */ + idx = (uint8_t)(idx * 2) - any_mot_config.start_addr; + + /* Copy the bytes to be set back to the array */ + for (index = 0; index < idx; index++) + { + feat_config[any_mot_config.start_addr + + index] = *((uint8_t *) data_p + any_mot_config.start_addr + index); + } + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API sets no-motion configurations like axes select, + * duration, threshold and output-configuration. + */ +static int8_t set_no_motion_config(const struct bmi2_no_motion_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to define count */ + uint8_t index = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for no-motion */ + struct bmi2_feature_config no_mot_config = { 0, 0, 0 }; + + /* Copy the feature configuration address to a local pointer */ + uint16_t *data_p = (uint16_t *) (void *)feat_config; + + /* Search for no-motion feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&no_mot_config, BMI2_NO_MOTION, dev); + if (feat_found) + { + /* Get the configuration from the page where no-motion feature resides */ + rslt = bmi2_get_feat_config(no_mot_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for no-motion select */ + idx = no_mot_config.start_addr; + + /* Get offset in words since all the features are set in words length */ + idx = idx / 2; + + /* Set duration */ + *(data_p + idx) = BMI2_SET_BIT_POS0(*(data_p + idx), BMI2_ANY_NO_MOT_DUR, config->duration); + + /* Set x-select */ + *(data_p + idx) = BMI2_SET_BITS(*(data_p + idx), BMI2_ANY_NO_MOT_X_SEL, config->select_x); + + /* Set y-select */ + *(data_p + idx) = BMI2_SET_BITS(*(data_p + idx), BMI2_ANY_NO_MOT_Y_SEL, config->select_y); + + /* Set z-select */ + *(data_p + idx) = BMI2_SET_BITS(*(data_p + idx), BMI2_ANY_NO_MOT_Z_SEL, config->select_z); + + /* Increment offset by 1 word to set threshold and output configuration */ + idx++; + + /* Set threshold */ + *(data_p + idx) = BMI2_SET_BIT_POS0(*(data_p + idx), BMI2_ANY_NO_MOT_THRES, config->threshold); + + /* Increment offset by 1 more word to get the total length in words */ + idx++; + + /* Get total length in bytes to copy from local pointer to the array */ + idx = (uint8_t)(idx * 2) - no_mot_config.start_addr; + + /* Copy the bytes to be set back to the array */ + for (index = 0; index < idx; index++) + { + feat_config[no_mot_config.start_addr + + index] = *((uint8_t *) data_p + no_mot_config.start_addr + index); + } + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API sets sig-motion configurations like block-size, + * output-configuration and other parameters. + */ +static int8_t set_sig_motion_config(const struct bmi2_sig_motion_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to define index */ + uint8_t index = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for sig-motion */ + struct bmi2_feature_config sig_mot_config = { 0, 0, 0 }; + + /* Copy the feature configuration address to a local pointer */ + uint16_t *data_p = (uint16_t *) (void *)feat_config; + + /* Search for sig-motion feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&sig_mot_config, BMI2_SIG_MOTION, dev); + if (feat_found) + { + /* Get the configuration from the page where sig-motion feature resides */ + rslt = bmi2_get_feat_config(sig_mot_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for sig-motion select */ + idx = sig_mot_config.start_addr; + + /* Get offset in words since all the features are set in words length */ + idx = idx / 2; + + /* Set parameter 1 */ + *(data_p + idx) = BMI2_SET_BIT_POS0(*(data_p + idx), BMI2_SIG_MOT_PARAM_1, config->block_size); + + /* Increment offset by 1 more word to get the total length in words */ + idx++; + + /* Get total length in bytes to copy from local pointer to the array */ + idx = (uint8_t)(idx * 2) - sig_mot_config.start_addr; + + /* Copy the bytes to be set back to the array */ + for (index = 0; index < idx; index++) + { + feat_config[sig_mot_config.start_addr + + index] = *((uint8_t *) data_p + sig_mot_config.start_addr + index); + } + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API sets step counter parameter configurations. + */ +static int8_t set_step_count_params_config(const uint16_t *step_count_params, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define index */ + uint8_t index = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for step counter parameters */ + struct bmi2_feature_config step_params_config = { 0, 0, 0 }; + + /* Variable to index the page number */ + uint8_t page_idx; + + /* Variable to define the start page */ + uint8_t start_page; + + /* Variable to define start address of the parameters */ + uint8_t start_addr; + + /* Variable to define number of bytes */ + uint8_t n_bytes = (BMI2_STEP_CNT_N_PARAMS * 2); + + /* Variable to store number of pages */ + uint8_t n_pages = (n_bytes / 16); + + /* Variable to define the end page */ + uint8_t end_page; + + /* Variable to define the remaining bytes to be read */ + uint8_t remain_len; + + /* Variable to define the maximum words(16 bytes or 8 words) to be read in a page */ + uint8_t max_len = 8; + + /* Variable index bytes in a page */ + uint8_t page_byte_idx; + + /* Variable to index the parameters */ + uint8_t param_idx = 0; + + /* Copy the feature configuration address to a local pointer */ + uint16_t *data_p = (uint16_t *) (void *)feat_config; + + /* Search for step counter parameter feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&step_params_config, BMI2_STEP_COUNTER_PARAMS, dev); + if (feat_found) + { + /* Get the start page for the step counter parameters */ + start_page = step_params_config.page; + + /* Get the end page for the step counter parameters */ + end_page = start_page + n_pages; + + /* Get the start address for the step counter parameters */ + start_addr = step_params_config.start_addr; + + /* Get the remaining length of bytes to be read */ + remain_len = (uint8_t)((n_bytes - (n_pages * 16)) + start_addr); + for (page_idx = start_page; page_idx <= end_page; page_idx++) + { + /* Get the configuration from the respective page */ + rslt = bmi2_get_feat_config(page_idx, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Start from address 0x00 when switched to next page */ + if (page_idx > start_page) + { + start_addr = 0; + } + + /* Remaining number of words to be read in the page */ + if (page_idx == end_page) + { + max_len = (remain_len / 2); + } + + /* Get offset in words since all the features are set in words length */ + page_byte_idx = start_addr / 2; + for (; page_byte_idx < max_len;) + { + /* Set parameters 1 to 25 */ + *(data_p + page_byte_idx) = BMI2_SET_BIT_POS0(*(data_p + page_byte_idx), + BMI2_STEP_COUNT_PARAMS, + step_count_params[param_idx]); + + /* Increment offset by 1 word to set to the next parameter */ + page_byte_idx++; + + /* Increment to next parameter */ + param_idx++; + } + + /* Get total length in bytes to copy from local pointer to the array */ + page_byte_idx = (uint8_t)(page_byte_idx * 2) - step_params_config.start_addr; + + /* Copy the bytes to be set back to the array */ + for (index = 0; index < page_byte_idx; index++) + { + feat_config[step_params_config.start_addr + + index] = *((uint8_t *) data_p + step_params_config.start_addr + index); + } + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + } + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/* @brief This internal API sets step counter configurations like water-mark + * level, reset-counter and output-configuration step detector and activity. + */ +static int8_t set_step_config(const struct bmi2_step_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to define index */ + uint8_t index = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for step counter 4 */ + struct bmi2_feature_config step_count_config = { 0, 0, 0 }; + + /* Copy the feature configuration address to a local pointer */ + uint16_t *data_p = (uint16_t *) (void *)feat_config; + + /* Search for step counter feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&step_count_config, BMI2_STEP_COUNTER, dev); + if (feat_found) + { + /* Get the configuration from the page where step counter resides */ + rslt = bmi2_get_feat_config(step_count_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes */ + idx = step_count_config.start_addr; + + /* Get offset in words since all the features are set in words length */ + idx = idx / 2; + + /* Set water-mark level */ + *(data_p + idx) = BMI2_SET_BIT_POS0(*(data_p + idx), BMI2_STEP_COUNT_WM_LEVEL, config->watermark_level); + + /* Set reset-counter */ + *(data_p + idx) = BMI2_SET_BITS(*(data_p + idx), BMI2_STEP_COUNT_RST_CNT, config->reset_counter); + + /* Increment offset by 1 word to set output + * configuration of step detector and step activity + */ + idx++; + + /* Get total length in bytes to copy from local pointer to the array */ + idx = (uint8_t)(idx * 2) - step_count_config.start_addr; + + /* Copy the bytes to be set back to the array */ + for (index = 0; index < idx; index++) + { + feat_config[step_count_config.start_addr + + index] = *((uint8_t *) data_p + step_count_config.start_addr + index); + } + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API sets wrist gesture configurations like wearable-arm, + * and output-configuration. + */ +static int8_t set_wrist_gest_config(const struct bmi2_wrist_gest_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to define index */ + uint8_t index = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for wrist gesture */ + struct bmi2_feature_config wrist_gest_config = { 0, 0, 0 }; + + /* Copy the feature configuration address to a local pointer */ + uint16_t *data_p = (uint16_t *) (void *)feat_config; + + /* Search for wrist gesture feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&wrist_gest_config, BMI2_WRIST_GESTURE, dev); + if (feat_found) + { + /* Get the configuration from the page where wrist gesture feature resides */ + rslt = bmi2_get_feat_config(wrist_gest_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for gesture select */ + idx = wrist_gest_config.start_addr; + + /* Get offset in words since all the features are set in words length */ + idx = idx / 2; + + /* Set wearable arm */ + *(data_p + idx) = BMI2_SET_BITS(*(data_p + idx), BMI2_WRIST_GEST_WEAR_ARM, config->wearable_arm); + + /* Increment offset by 1 more word to set minimum tilt angle (min_flick_peak) */ + idx++; + *(data_p + idx) = config->min_flick_peak; + + /* Increment offset by 1 more word to set min_flick_samples */ + idx++; + *(data_p + idx) = config->min_flick_samples; + + /* Increment offset by 1 more word to set max time within gesture moment has to be completed */ + idx++; + *(data_p + idx) = config->max_duration; + + /* Increment offset by 1 more word to get the total length in words */ + idx++; + + /* Get total length in bytes to copy from local pointer to the array */ + idx = (uint8_t)(idx * 2) - wrist_gest_config.start_addr; + + /* Copy the bytes to be set back to the array */ + for (index = 0; index < idx; index++) + { + feat_config[wrist_gest_config.start_addr + + index] = *((uint8_t *) data_p + wrist_gest_config.start_addr + index); + } + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API sets wrist wear wake-up configurations like + * output-configuration. + */ +static int8_t set_wrist_wear_wake_up_config(const struct bmi2_wrist_wear_wake_up_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to define index */ + uint8_t index = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for wrist wear wake-up */ + struct bmi2_feature_config wrist_wake_up_config = { 0, 0, 0 }; + + /* Copy the feature configuration address to a local pointer */ + uint16_t *data_p = (uint16_t *) (void *)feat_config; + + /* Search for wrist wear wake-up feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&wrist_wake_up_config, BMI2_WRIST_WEAR_WAKE_UP, dev); + if (feat_found) + { + /* Get the configuration from the page where wrist wear wake-up feature resides */ + rslt = bmi2_get_feat_config(wrist_wake_up_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for wrist wear wake-up select */ + idx = wrist_wake_up_config.start_addr; + + /* Get offset in words since all the features are set in words length */ + idx = idx / 2; + + /* Increment offset by 1 more word to set min_angle_focus */ + idx++; + + *(data_p + idx) = config->min_angle_focus; + + /* Increment offset by 1 more word to set min_angle_nonfocus */ + idx++; + *(data_p + idx) = config->min_angle_nonfocus; + + /* Increment offset by 1 more word to set max_tilt_lr */ + idx++; + *(data_p + idx) = config->max_tilt_lr; + + /* Increment offset by 1 more word to set max_tilt_ll */ + idx++; + *(data_p + idx) = config->max_tilt_ll; + + /* Increment offset by 1 more word to set max_tilt_pd */ + idx++; + *(data_p + idx) = config->max_tilt_pd; + + /* Increment offset by 1 more word to set max_tilt_pu */ + idx++; + *(data_p + idx) = config->max_tilt_pu; + + /* Increment offset by 1 more word to get the total length in words */ + idx++; + + /* Get total length in bytes to copy from local pointer to the array */ + idx = (uint8_t)(idx * 2) - wrist_wake_up_config.start_addr; + + /* Copy the bytes to be set back to the array */ + for (index = 0; index < idx; index++) + { + feat_config[wrist_wake_up_config.start_addr + + index] = *((uint8_t *) data_p + wrist_wake_up_config.start_addr + index); + } + + /* Set the configuration back to the page */ + rslt = bmi2_set_regs(BMI2_FEATURES_REG_ADDR, feat_config, BMI2_FEAT_SIZE_IN_BYTES, dev); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API gets any-motion configurations like axes select, + * duration, threshold and output-configuration. + */ +static int8_t get_any_motion_config(struct bmi2_any_motion_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to define LSB */ + uint16_t lsb; + + /* Variable to define MSB */ + uint16_t msb; + + /* Variable to define a word */ + uint16_t lsb_msb; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for any-motion */ + struct bmi2_feature_config any_mot_config = { 0, 0, 0 }; + + /* Search for any-motion feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&any_mot_config, BMI2_ANY_MOTION, dev); + if (feat_found) + { + /* Get the configuration from the page where any-motion feature resides */ + rslt = bmi2_get_feat_config(any_mot_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for feature enable for any-motion */ + idx = any_mot_config.start_addr; + + /* Get word to calculate duration, x, y and z select */ + lsb = (uint16_t) feat_config[idx++]; + msb = ((uint16_t) feat_config[idx++] << 8); + lsb_msb = lsb | msb; + + /* Get duration */ + config->duration = lsb_msb & BMI2_ANY_NO_MOT_DUR_MASK; + + /* Get x-select */ + config->select_x = (lsb_msb & BMI2_ANY_NO_MOT_X_SEL_MASK) >> BMI2_ANY_NO_MOT_X_SEL_POS; + + /* Get y-select */ + config->select_y = (lsb_msb & BMI2_ANY_NO_MOT_Y_SEL_MASK) >> BMI2_ANY_NO_MOT_Y_SEL_POS; + + /* Get z-select */ + config->select_z = (lsb_msb & BMI2_ANY_NO_MOT_Z_SEL_MASK) >> BMI2_ANY_NO_MOT_Z_SEL_POS; + + /* Get word to calculate threshold, output configuration from the same word */ + lsb = (uint16_t) feat_config[idx++]; + msb = ((uint16_t) feat_config[idx++] << 8); + lsb_msb = lsb | msb; + + /* Get threshold */ + config->threshold = lsb_msb & BMI2_ANY_NO_MOT_THRES_MASK; + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API gets no-motion configurations like axes select, + * duration, threshold and output-configuration. + */ +static int8_t get_no_motion_config(struct bmi2_no_motion_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to define LSB */ + uint16_t lsb = 0; + + /* Variable to define MSB */ + uint16_t msb = 0; + + /* Variable to define a word */ + uint16_t lsb_msb = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for no-motion */ + struct bmi2_feature_config no_mot_config = { 0, 0, 0 }; + + /* Search for no-motion feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&no_mot_config, BMI2_NO_MOTION, dev); + if (feat_found) + { + /* Get the configuration from the page where no-motion feature resides */ + rslt = bmi2_get_feat_config(no_mot_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for feature enable for no-motion */ + idx = no_mot_config.start_addr; + + /* Get word to calculate duration, x, y and z select */ + lsb = (uint16_t) feat_config[idx++]; + msb = ((uint16_t) feat_config[idx++] << 8); + lsb_msb = lsb | msb; + + /* Get duration */ + config->duration = lsb_msb & BMI2_ANY_NO_MOT_DUR_MASK; + + /* Get x-select */ + config->select_x = (lsb_msb & BMI2_ANY_NO_MOT_X_SEL_MASK) >> BMI2_ANY_NO_MOT_X_SEL_POS; + + /* Get y-select */ + config->select_y = (lsb_msb & BMI2_ANY_NO_MOT_Y_SEL_MASK) >> BMI2_ANY_NO_MOT_Y_SEL_POS; + + /* Get z-select */ + config->select_z = (lsb_msb & BMI2_ANY_NO_MOT_Z_SEL_MASK) >> BMI2_ANY_NO_MOT_Z_SEL_POS; + + /* Get word to calculate threshold, output configuration from the same word */ + lsb = (uint16_t) feat_config[idx++]; + msb = ((uint16_t) feat_config[idx++] << 8); + lsb_msb = lsb | msb; + + /* Get threshold */ + config->threshold = lsb_msb & BMI2_ANY_NO_MOT_THRES_MASK; + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API gets sig-motion configurations like block-size, + * output-configuration and other parameters. + */ +static int8_t get_sig_motion_config(struct bmi2_sig_motion_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to define LSB */ + uint16_t lsb = 0; + + /* Variable to define MSB */ + uint16_t msb = 0; + + /* Variable to define a word */ + uint16_t lsb_msb = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration sig-motion */ + struct bmi2_feature_config sig_mot_config = { 0, 0, 0 }; + + /* Search for sig-motion feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&sig_mot_config, BMI2_SIG_MOTION, dev); + if (feat_found) + { + /* Get the configuration from the page where sig-motion feature resides */ + rslt = bmi2_get_feat_config(sig_mot_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for feature enable for sig-motion */ + idx = sig_mot_config.start_addr; + + /* Get word to calculate parameter 1 */ + lsb = (uint16_t) feat_config[idx++]; + msb = ((uint16_t) feat_config[idx++] << 8); + lsb_msb = lsb | msb; + + /* Get parameter 1 */ + config->block_size = lsb_msb & BMI2_SIG_MOT_PARAM_1_MASK; + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API gets step counter parameter configurations. + */ +static int8_t get_step_count_params_config(uint16_t *step_count_params, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Variable to define LSB */ + uint16_t lsb = 0; + + /* Variable to define MSB */ + uint16_t msb = 0; + + /* Variable to define a word */ + uint16_t lsb_msb = 0; + + /* Initialize feature configuration for step counter 1 */ + struct bmi2_feature_config step_params_config = { 0, 0, 0 }; + + /* Variable to index the page number */ + uint8_t page_idx; + + /* Variable to define the start page */ + uint8_t start_page; + + /* Variable to define start address of the parameters */ + uint8_t start_addr; + + /* Variable to define number of bytes */ + uint8_t n_bytes = (BMI2_STEP_CNT_N_PARAMS * 2); + + /* Variable to store number of pages */ + uint8_t n_pages = (n_bytes / 16); + + /* Variable to define the end page */ + uint8_t end_page; + + /* Variable to define the remaining bytes to be read */ + uint8_t remain_len; + + /* Variable to define the maximum words to be read in a page */ + uint8_t max_len = BMI2_FEAT_SIZE_IN_BYTES; + + /* Variable index bytes in a page */ + uint8_t page_byte_idx; + + /* Variable to index the parameters */ + uint8_t param_idx = 0; + + /* Search for step counter parameter feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&step_params_config, BMI2_STEP_COUNTER_PARAMS, dev); + if (feat_found) + { + /* Get the start page for the step counter parameters */ + start_page = step_params_config.page; + + /* Get the end page for the step counter parameters */ + end_page = start_page + n_pages; + + /* Get the start address for the step counter parameters */ + start_addr = step_params_config.start_addr; + + /* Get the remaining length of bytes to be read */ + remain_len = (uint8_t)((n_bytes - (n_pages * 16)) + start_addr); + for (page_idx = start_page; page_idx <= end_page; page_idx++) + { + /* Get the configuration from the respective page */ + rslt = bmi2_get_feat_config(page_idx, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Start from address 0x00 when switched to next page */ + if (page_idx > start_page) + { + start_addr = 0; + } + + /* Remaining number of bytes to be read in the page */ + if (page_idx == end_page) + { + max_len = remain_len; + } + + /* Get the offset */ + page_byte_idx = start_addr; + while (page_byte_idx < max_len) + { + /* Get word to calculate the parameter*/ + lsb = (uint16_t) feat_config[page_byte_idx++]; + if (page_byte_idx < max_len) + { + msb = ((uint16_t) feat_config[page_byte_idx++] << 8); + } + + lsb_msb = lsb | msb; + + /* Get parameters 1 to 25 */ + step_count_params[param_idx] = lsb_msb & BMI2_STEP_COUNT_PARAMS_MASK; + + /* Increment to next parameter */ + param_idx++; + } + } + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API gets step counter/detector/activity configurations. + */ +static int8_t get_step_config(struct bmi2_step_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to define LSB */ + uint16_t lsb = 0; + + /* Variable to define MSB */ + uint16_t msb = 0; + + /* Variable to define a word */ + uint16_t lsb_msb = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for step counter */ + struct bmi2_feature_config step_count_config = { 0, 0, 0 }; + + /* Search for step counter 4 feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&step_count_config, BMI2_STEP_COUNTER, dev); + if (feat_found) + { + /* Get the configuration from the page where step counter 4 parameter resides */ + rslt = bmi2_get_feat_config(step_count_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for feature enable for step counter/detector/activity */ + idx = step_count_config.start_addr; + + /* Get word to calculate water-mark level and reset counter */ + lsb = (uint16_t) feat_config[idx++]; + msb = ((uint16_t) feat_config[idx++] << 8); + lsb_msb = lsb | msb; + + /* Get water-mark level */ + config->watermark_level = lsb_msb & BMI2_STEP_COUNT_WM_LEVEL_MASK; + + /* Get reset counter */ + config->reset_counter = (lsb_msb & BMI2_STEP_COUNT_RST_CNT_MASK) >> BMI2_STEP_COUNT_RST_CNT_POS; + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API gets wrist gesture configurations like wearable-arm, + * and output-configuration. + */ +static int8_t get_wrist_gest_config(struct bmi2_wrist_gest_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for wrist gesture */ + struct bmi2_feature_config wrist_gest_config = { 0, 0, 0 }; + + /* Copy the feature configuration address to a local pointer */ + uint16_t *data_p = (uint16_t *) (void *)feat_config; + + /* Search for wrist gesture feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&wrist_gest_config, BMI2_WRIST_GESTURE, dev); + if (feat_found) + { + /* Get the configuration from the page where wrist gesture feature resides */ + rslt = bmi2_get_feat_config(wrist_gest_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for wrist gesture select */ + idx = wrist_gest_config.start_addr; + + /* Get offset in words since all the features are set in words length */ + idx = idx / 2; + + /* Get wearable arm */ + config->wearable_arm = (*(data_p + idx) & BMI2_WRIST_GEST_WEAR_ARM_MASK) >> BMI2_WRIST_GEST_WEAR_ARM_POS; + + /* Increment the offset by 1 word to get min_flick_peak */ + idx++; + config->min_flick_peak = *(data_p + idx); + + /* Increment the offset by 1 word to get min_flick_samples */ + idx++; + config->min_flick_samples = *(data_p + idx); + + /* Increment the offset by 1 word to get max_duration */ + idx++; + config->max_duration = *(data_p + idx); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API gets wrist wear wake-up configurations like + * output-configuration. + */ +static int8_t get_wrist_wear_wake_up_config(struct bmi2_wrist_wear_wake_up_config *config, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature configuration for wrist wear wake-up */ + struct bmi2_feature_config wrist_wake_up_config = { 0, 0, 0 }; + + /* Copy the feature configuration address to a local pointer */ + uint16_t *data_p = (uint16_t *) (void *)feat_config; + + /* Search for wrist wear wake-up feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&wrist_wake_up_config, BMI2_WRIST_WEAR_WAKE_UP, dev); + if (feat_found) + { + /* Get the configuration from the page where wrist wear wake-up feature resides */ + rslt = bmi2_get_feat_config(wrist_wake_up_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for wrist wear wake-up select */ + idx = wrist_wake_up_config.start_addr; + + /* Get offset in words since all the features are set in words length */ + idx = idx / 2; + + /* Increment the offset value by 1 word to get min_angle_focus */ + idx++; + + config->min_angle_focus = *(data_p + idx); + + /* Increment the offset value by 1 word to get min_angle_nonfocus */ + idx++; + config->min_angle_nonfocus = *(data_p + idx); + + /* Increment the offset value by 1 word to get max_tilt_lr */ + idx++; + config->max_tilt_lr = *(data_p + idx); + + /* Increment the offset value by 1 word to get max_tilt_ll */ + idx++; + config->max_tilt_ll = *(data_p + idx); + + /* Increment the offset value by 1 word to get max_tilt_pd */ + idx++; + config->max_tilt_pd = *(data_p + idx); + + /* Increment the offset value by 1 word to get max_tilt_pu */ + idx++; + config->max_tilt_pu = *(data_p + idx); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API gets the output values of the wrist gesture. + */ +static int8_t get_wrist_gest_status(uint8_t *wrist_gest, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variables to define index */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature output for wrist gesture */ + struct bmi2_feature_config wrist_gest_out_config = { 0, 0, 0 }; + + /* Search for wrist gesture feature and extract its configuration details */ + feat_found = extract_output_feat_config(&wrist_gest_out_config, BMI2_WRIST_GESTURE, dev); + if (feat_found) + { + /* Get the feature output configuration for wrist gesture */ + rslt = bmi2_get_feat_config(wrist_gest_out_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for wrist gesture output */ + idx = wrist_gest_out_config.start_addr; + + /* Get the wrist gesture output */ + *wrist_gest = feat_config[idx]; + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API gets the output values of step counter. + */ +static int8_t get_step_counter_output(uint32_t *step_count, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variables to define index */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature output for step counter */ + struct bmi2_feature_config step_cnt_out_config = { 0, 0, 0 }; + + /* Search for step counter output feature and extract its configuration details */ + feat_found = extract_output_feat_config(&step_cnt_out_config, BMI2_STEP_COUNTER, dev); + if (feat_found) + { + /* Get the feature output configuration for step-counter */ + rslt = bmi2_get_feat_config(step_cnt_out_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for step counter output */ + idx = step_cnt_out_config.start_addr; + + /* Get the step counter output in 4 bytes */ + *step_count = (uint32_t) feat_config[idx++]; + *step_count |= ((uint32_t) feat_config[idx++] << 8); + *step_count |= ((uint32_t) feat_config[idx++] << 16); + *step_count |= ((uint32_t) feat_config[idx++] << 24); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API gets the error status related to NVM. + */ +static int8_t get_nvm_error_status(struct bmi2_nvm_err_status *nvm_err_stat, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variables to define index */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature output for NVM error status */ + struct bmi2_feature_config nvm_err_cfg = { 0, 0, 0 }; + + /* Search for NVM error status feature and extract its configuration details */ + feat_found = extract_output_feat_config(&nvm_err_cfg, BMI2_NVM_STATUS, dev); + if (feat_found) + { + /* Get the feature output configuration for NVM error status */ + rslt = bmi2_get_feat_config(nvm_err_cfg.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for NVM error status */ + idx = nvm_err_cfg.start_addr; + + /* Increment index to get the error status */ + idx++; + + /* Error when NVM load action fails */ + nvm_err_stat->load_error = BMI2_GET_BIT_POS0(feat_config[idx], BMI2_NVM_LOAD_ERR_STATUS); + + /* Error when NVM program action fails */ + nvm_err_stat->prog_error = BMI2_GET_BITS(feat_config[idx], BMI2_NVM_PROG_ERR_STATUS); + + /* Error when NVM erase action fails */ + nvm_err_stat->erase_error = BMI2_GET_BITS(feat_config[idx], BMI2_NVM_ERASE_ERR_STATUS); + + /* Error when NVM program limit is exceeded */ + nvm_err_stat->exceed_error = BMI2_GET_BITS(feat_config[idx], BMI2_NVM_END_EXCEED_STATUS); + + /* Error when NVM privilege mode is not acquired */ + nvm_err_stat->privil_error = BMI2_GET_BITS(feat_config[idx], BMI2_NVM_PRIV_ERR_STATUS); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API is used to get enable status of gyroscope user gain + * update. + */ +static int8_t get_user_gain_upd_status(uint8_t *status, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variable to define the array offset */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Variable to check APS status */ + uint8_t aps_stat = 0; + + /* Initialize feature configuration for gyroscope user gain */ + struct bmi2_feature_config gyr_user_gain_cfg = { 0, 0, 0 }; + + /* Search for user gain feature and extract its configuration details */ + feat_found = bmi2_extract_input_feat_config(&gyr_user_gain_cfg, BMI2_GYRO_GAIN_UPDATE, dev); + if (feat_found) + { + /* Disable advance power save */ + aps_stat = dev->aps_status; + if (aps_stat == BMI2_ENABLE) + { + rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev); + } + + if (rslt == BMI2_OK) + { + /* Get the configuration from the page where user gain feature resides */ + rslt = bmi2_get_feat_config(gyr_user_gain_cfg.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset for enable/disable of user gain */ + idx = gyr_user_gain_cfg.start_addr + BMI2_GYR_USER_GAIN_FEAT_EN_OFFSET; + + /* Set the feature enable status */ + *status = BMI2_GET_BITS(feat_config[idx], BMI2_GYR_USER_GAIN_FEAT_EN); + } + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + /* Enable Advance power save if disabled while configuring and not when already disabled */ + if ((rslt == BMI2_OK) && (aps_stat == BMI2_ENABLE)) + { + rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev); + } + + return rslt; +} + +/*! + * @brief This internal API gets the output values of step activity. + */ +static int8_t get_step_activity_output(uint8_t *step_act, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variables to define index */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature output for step activity */ + struct bmi2_feature_config step_act_out_config = { 0, 0, 0 }; + + /* Search for step activity output feature and extract its configuration details */ + feat_found = extract_output_feat_config(&step_act_out_config, BMI2_STEP_ACTIVITY, dev); + if (feat_found) + { + /* Get the feature output configuration for step-activity */ + rslt = bmi2_get_feat_config(step_act_out_config.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for step activity output */ + idx = step_act_out_config.start_addr; + + /* Get the step activity output */ + *step_act = feat_config[idx]; + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API gets the error status related to virtual frames. + */ +static int8_t get_vfrm_error_status(struct bmi2_vfrm_err_status *vfrm_err_stat, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Array to define the feature configuration */ + uint8_t feat_config[BMI2_FEAT_SIZE_IN_BYTES] = { 0 }; + + /* Variables to define index */ + uint8_t idx = 0; + + /* Variable to set flag */ + uint8_t feat_found; + + /* Initialize feature output for VFRM error status */ + struct bmi2_feature_config vfrm_err_cfg = { 0, 0, 0 }; + + /* Search for VFRM error status feature and extract its configuration details */ + feat_found = extract_output_feat_config(&vfrm_err_cfg, BMI2_VFRM_STATUS, dev); + if (feat_found) + { + /* Get the feature output configuration for VFRM error status */ + rslt = bmi2_get_feat_config(vfrm_err_cfg.page, feat_config, dev); + if (rslt == BMI2_OK) + { + /* Define the offset in bytes for VFRM error status */ + idx = vfrm_err_cfg.start_addr; + + /* Increment index to get the error status */ + idx++; + + /* Internal error while acquiring lock for FIFO */ + vfrm_err_stat->lock_error = BMI2_GET_BITS(feat_config[idx], BMI2_VFRM_LOCK_ERR_STATUS); + + /* Internal error while writing byte into FIFO */ + vfrm_err_stat->write_error = BMI2_GET_BITS(feat_config[idx], BMI2_VFRM_WRITE_ERR_STATUS); + + /* Internal error while writing into FIFO */ + vfrm_err_stat->fatal_error = BMI2_GET_BITS(feat_config[idx], BMI2_VFRM_FATAL_ERR_STATUS); + } + } + else + { + rslt = BMI2_E_INVALID_SENSOR; + } + + return rslt; +} + +/*! + * @brief This internal API enables/disables compensation of the gain defined + * in the GAIN register. + */ +static int8_t enable_gyro_gain(uint8_t enable, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to define register data */ + uint8_t reg_data = 0; + + rslt = bmi2_get_regs(BMI2_GYR_OFF_COMP_6_ADDR, ®_data, 1, dev); + if (rslt == BMI2_OK) + { + reg_data = BMI2_SET_BITS(reg_data, BMI2_GYR_GAIN_EN, enable); + rslt = bmi2_set_regs(BMI2_GYR_OFF_COMP_6_ADDR, ®_data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This internal API is used to extract the output feature configuration + * details from the look-up table. + */ +static uint8_t extract_output_feat_config(struct bmi2_feature_config *feat_output, + uint8_t type, + const struct bmi2_dev *dev) +{ + /* Variable to define loop */ + uint8_t loop = 0; + + /* Variable to set flag */ + uint8_t feat_found = BMI2_FALSE; + + /* Search for the output feature from the output configuration array */ + while (loop < dev->out_sens) + { + if (dev->feat_output[loop].type == type) + { + *feat_output = dev->feat_output[loop]; + feat_found = BMI2_TRUE; + break; + } + + loop++; + } + + /* Return flag */ + return feat_found; +} + +/*! + * @brief This internal API sets feature configuration to the sensor. + */ +static int8_t set_feat_config(const struct bmi2_sens_config *sens_cfg, uint8_t loop, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + switch (sens_cfg[loop].type) + { + /* Set any motion configuration */ + case BMI2_ANY_MOTION: + rslt = set_any_motion_config(&sens_cfg[loop].cfg.any_motion, dev); + break; + + /* Set no motion configuration */ + case BMI2_NO_MOTION: + rslt = set_no_motion_config(&sens_cfg[loop].cfg.no_motion, dev); + break; + + /* Set sig-motion configuration */ + case BMI2_SIG_MOTION: + rslt = set_sig_motion_config(&sens_cfg[loop].cfg.sig_motion, dev); + break; + + /* Set the step counter parameters */ + case BMI2_STEP_COUNTER_PARAMS: + rslt = set_step_count_params_config(sens_cfg[loop].cfg.step_counter_params, dev); + break; + + /* Set step counter/detector/activity configuration */ + case BMI2_STEP_DETECTOR: + case BMI2_STEP_COUNTER: + case BMI2_STEP_ACTIVITY: + rslt = set_step_config(&sens_cfg[loop].cfg.step_counter, dev); + break; + + /* Set the wrist gesture configuration */ + case BMI2_WRIST_GESTURE: + rslt = set_wrist_gest_config(&sens_cfg[loop].cfg.wrist_gest, dev); + break; + + /* Set the wrist wear wake-up configuration */ + case BMI2_WRIST_WEAR_WAKE_UP: + rslt = set_wrist_wear_wake_up_config(&sens_cfg[loop].cfg.wrist_wear_wake_up, dev); + break; + + default: + rslt = BMI2_E_INVALID_SENSOR; + break; + } + + return rslt; +} + +/*! + * @brief This internal API gets feature configuration from the sensor. + */ +static int8_t get_feat_config(struct bmi2_sens_config *sens_cfg, uint8_t loop, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + switch (sens_cfg[loop].type) + { + /* Get sig-motion configuration */ + case BMI2_SIG_MOTION: + rslt = get_sig_motion_config(&sens_cfg[loop].cfg.sig_motion, dev); + break; + + /* Get any motion configuration */ + case BMI2_ANY_MOTION: + rslt = get_any_motion_config(&sens_cfg[loop].cfg.any_motion, dev); + break; + + /* Get no motion configuration */ + case BMI2_NO_MOTION: + rslt = get_no_motion_config(&sens_cfg[loop].cfg.no_motion, dev); + break; + + /* Set the step counter parameters */ + case BMI2_STEP_COUNTER_PARAMS: + rslt = get_step_count_params_config(sens_cfg[loop].cfg.step_counter_params, dev); + break; + + /* Get step counter/detector/activity configuration */ + case BMI2_STEP_DETECTOR: + case BMI2_STEP_COUNTER: + case BMI2_STEP_ACTIVITY: + rslt = get_step_config(&sens_cfg[loop].cfg.step_counter, dev); + break; + + /* Get the wrist gesture configuration */ + case BMI2_WRIST_GESTURE: + rslt = get_wrist_gest_config(&sens_cfg[loop].cfg.wrist_gest, dev); + break; + + /* Get the wrist wear wake-up configuration */ + case BMI2_WRIST_WEAR_WAKE_UP: + rslt = get_wrist_wear_wake_up_config(&sens_cfg[loop].cfg.wrist_wear_wake_up, dev); + break; + + default: + rslt = BMI2_E_INVALID_SENSOR; + break; + } + + return rslt; +} + +/*! + * @brief This internal API is used to enable main sensors like accel, gyro, aux and temperature. + */ +static int8_t enable_main_sensors(uint64_t sensor_sel, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store register values */ + uint8_t reg_data; + + rslt = bmi2_get_regs(BMI2_PWR_CTRL_ADDR, ®_data, 1, dev); + + if (rslt == BMI2_OK) + { + /* Enable accelerometer */ + if (sensor_sel & BMI2_ACCEL_SENS_SEL) + { + reg_data = BMI2_SET_BITS(reg_data, BMI2_ACC_EN, BMI2_ENABLE); + } + + /* Enable gyroscope */ + if (sensor_sel & BMI2_GYRO_SENS_SEL) + { + reg_data = BMI2_SET_BITS(reg_data, BMI2_GYR_EN, BMI2_ENABLE); + } + + /* Enable auxiliary sensor */ + if (sensor_sel & BMI2_AUX_SENS_SEL) + { + reg_data = BMI2_SET_BIT_POS0(reg_data, BMI2_AUX_EN, BMI2_ENABLE); + } + + /* Enable temperature sensor */ + if (sensor_sel & BMI2_TEMP_SENS_SEL) + { + reg_data = BMI2_SET_BITS(reg_data, BMI2_TEMP_EN, BMI2_ENABLE); + } + + /* Enable the sensors that are set in the power control register */ + if (sensor_sel & BMI2_MAIN_SENSORS) + { + rslt = bmi2_set_regs(BMI2_PWR_CTRL_ADDR, ®_data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API is used to enable sensor features. + */ +static int8_t enable_sensor_features(uint64_t sensor_sel, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Enable sig-motion feature */ + if (sensor_sel & BMI2_SIG_MOTION_SEL) + { + rslt = set_sig_motion(BMI2_ENABLE, dev); + } + + /* Enable any motion feature */ + if (sensor_sel & BMI2_ANY_MOT_SEL) + { + rslt = set_any_motion(BMI2_ENABLE, dev); + } + + /* Enable no motion feature */ + if (sensor_sel & BMI2_NO_MOT_SEL) + { + rslt = set_no_motion(BMI2_ENABLE, dev); + } + + /* Enable step detector feature */ + if (sensor_sel & BMI2_STEP_DETECT_SEL) + { + rslt = set_step_detector(BMI2_ENABLE, dev); + } + + /* Enable step counter feature */ + if (sensor_sel & BMI2_STEP_COUNT_SEL) + { + rslt = set_step_counter(BMI2_ENABLE, dev); + } + + /* Enable step activity feature */ + if (sensor_sel & BMI2_STEP_ACT_SEL) + { + rslt = set_step_activity(BMI2_ENABLE, dev); + } + + /* Enable gyroscope user gain */ + if (sensor_sel & BMI2_GYRO_GAIN_UPDATE_SEL) + { + rslt = set_gyro_user_gain(BMI2_ENABLE, dev); + } + + /* Enable gyroscope self-offset correction feature */ + if (sensor_sel & BMI2_GYRO_SELF_OFF_SEL) + { + rslt = set_gyro_self_offset_corr(BMI2_ENABLE, dev); + } + + /* Enable wrist gesture feature for wearable variant */ + if (sensor_sel & BMI2_WRIST_GEST_SEL) + { + rslt = set_wrist_gesture(BMI2_ENABLE, dev); + } + + /* Enable wrist wear wake-up feature */ + if (sensor_sel & BMI2_WRIST_WEAR_WAKE_UP_SEL) + { + rslt = set_wrist_wear_wake_up(BMI2_ENABLE, dev); + } + + return rslt; +} + +/*! + * @brief This internal API is used to disable main sensors like accel, gyro, aux and temperature. + */ +static int8_t disable_main_sensors(uint64_t sensor_sel, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt; + + /* Variable to store register values */ + uint8_t reg_data; + + rslt = bmi2_get_regs(BMI2_PWR_CTRL_ADDR, ®_data, 1, dev); + + if (rslt == BMI2_OK) + { + /* Disable accelerometer */ + if (sensor_sel & BMI2_ACCEL_SENS_SEL) + { + reg_data = BMI2_SET_BIT_VAL0(reg_data, BMI2_ACC_EN); + } + + /* Disable gyroscope */ + if (sensor_sel & BMI2_GYRO_SENS_SEL) + { + reg_data = BMI2_SET_BIT_VAL0(reg_data, BMI2_GYR_EN); + } + + /* Disable auxiliary sensor */ + if (sensor_sel & BMI2_AUX_SENS_SEL) + { + reg_data = BMI2_SET_BIT_VAL0(reg_data, BMI2_AUX_EN); + } + + /* Disable temperature sensor */ + if (sensor_sel & BMI2_TEMP_SENS_SEL) + { + reg_data = BMI2_SET_BIT_VAL0(reg_data, BMI2_TEMP_EN); + } + + /* Disable the sensors that are set in the power control register */ + if (sensor_sel & BMI2_MAIN_SENSORS) + { + rslt = bmi2_set_regs(BMI2_PWR_CTRL_ADDR, ®_data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API is used to disable sensor features. + */ +static int8_t disable_sensor_features(uint64_t sensor_sel, struct bmi2_dev *dev) +{ + /* Variable to define error */ + int8_t rslt = BMI2_OK; + + /* Disable sig-motion feature */ + if (sensor_sel & BMI2_SIG_MOTION_SEL) + { + rslt = set_sig_motion(BMI2_DISABLE, dev); + } + + /* Disable any-motion feature */ + if (sensor_sel & BMI2_ANY_MOT_SEL) + { + rslt = set_any_motion(BMI2_DISABLE, dev); + } + + /* Disable no-motion feature */ + if (sensor_sel & BMI2_NO_MOT_SEL) + { + rslt = set_no_motion(BMI2_DISABLE, dev); + } + + /* Disable step detector feature */ + if (sensor_sel & BMI2_STEP_DETECT_SEL) + { + rslt = set_step_detector(BMI2_DISABLE, dev); + } + + /* Disable step counter feature */ + if (sensor_sel & BMI2_STEP_COUNT_SEL) + { + rslt = set_step_counter(BMI2_DISABLE, dev); + } + + /* Disable step activity feature */ + if (sensor_sel & BMI2_STEP_ACT_SEL) + { + rslt = set_step_activity(BMI2_DISABLE, dev); + } + + /* Disable gyroscope user gain */ + if (sensor_sel & BMI2_GYRO_GAIN_UPDATE_SEL) + { + rslt = set_gyro_user_gain(BMI2_DISABLE, dev); + } + + /* Disable gyroscope self-offset correction feature */ + if (sensor_sel & BMI2_GYRO_SELF_OFF_SEL) + { + rslt = set_gyro_self_offset_corr(BMI2_DISABLE, dev); + } + + /* Disable wrist gesture feature for wearable variant*/ + if (sensor_sel & BMI2_WRIST_GEST_SEL) + { + rslt = set_wrist_gesture(BMI2_DISABLE, dev); + } + + /* Enable wrist wear wake-up feature */ + if (sensor_sel & BMI2_WRIST_WEAR_WAKE_UP_SEL) + { + rslt = set_wrist_wear_wake_up(BMI2_DISABLE, dev); + } + + return rslt; +} diff --git a/projects/Compass/main/src/bmi270_port.c b/projects/Compass/main/src/bmi270_port.c new file mode 100644 index 00000000..153528f9 --- /dev/null +++ b/projects/Compass/main/src/bmi270_port.c @@ -0,0 +1,131 @@ +/** + * @file bmi270_port.c + * @brief BMI270 Linux I2C 用户空间移植层实现 (M5CardputerZero) + * @note 使用 /dev/i2c-x + ioctl(I2C_RDWR) 实现带 Repeated Start 的连续读写 + */ + +#include "bmi270_port.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 静态文件句柄,生命周期与进程相同 */ +static int i2c_fd = -1; + +/* I2C 读回调:I2C_RDWR 保证写寄存器地址和读数据之间是 Repeated Start */ +static int8_t bmi270_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr) +{ + (void)intf_ptr; + + struct i2c_msg msgs[2]; + struct i2c_rdwr_ioctl_data msgset; + + msgs[0].addr = BMI270_I2C_ADDR; + msgs[0].flags = 0; /* 写方向 */ + msgs[0].len = 1; + msgs[0].buf = ®_addr; + + msgs[1].addr = BMI270_I2C_ADDR; + msgs[1].flags = I2C_M_RD; /* 读方向 */ + msgs[1].len = len; + msgs[1].buf = reg_data; + + msgset.msgs = msgs; + msgset.nmsgs = 2; + + if (ioctl(i2c_fd, I2C_RDWR, &msgset) < 0) { + perror("[BMI270] i2c read ioctl"); + return BMI2_E_COM_FAIL; + } + return BMI2_OK; +} + +/* I2C 写回调 */ +static int8_t bmi270_i2c_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, void *intf_ptr) +{ + (void)intf_ptr; + + uint8_t *buf = malloc(len + 1); + if (!buf) return BMI2_E_COM_FAIL; + + buf[0] = reg_addr; + memcpy(buf + 1, reg_data, len); + + struct i2c_msg msg; + struct i2c_rdwr_ioctl_data msgset; + + msg.addr = BMI270_I2C_ADDR; + msg.flags = 0; + msg.len = len + 1; + msg.buf = buf; + + msgset.msgs = &msg; + msgset.nmsgs = 1; + + int8_t rslt = BMI2_OK; + if (ioctl(i2c_fd, I2C_RDWR, &msgset) < 0) { + perror("[BMI270] i2c write ioctl"); + rslt = BMI2_E_COM_FAIL; + } + + free(buf); + return rslt; +} + +/* 微秒延时回调 */ +static void bmi270_delay_us(uint32_t period, void *intf_ptr) +{ + (void)intf_ptr; + usleep(period); +} + +int8_t bmi270_port_init(struct bmi2_dev *dev) +{ + if (!dev) return BMI2_E_NULL_PTR; + + i2c_fd = open(BMI270_I2C_BUS, O_RDWR); + if (i2c_fd < 0) { + perror("[BMI270] open i2c bus"); + return BMI2_E_COM_FAIL; + } + + /* 上电等待:至少 100~200ms */ + usleep(200000); + + dev->intf = BMI2_I2C_INTF; + dev->read = bmi270_i2c_read; + dev->write = bmi270_i2c_write; + dev->delay_us = bmi270_delay_us; + dev->intf_ptr = &i2c_fd; + dev->read_write_len = 46; + + return BMI2_OK; +} + +void bmi270_port_deinit(struct bmi2_dev *dev) +{ + (void)dev; + if (i2c_fd >= 0) { + close(i2c_fd); + i2c_fd = -1; + } +} + +void bmi270_print_result(int8_t rslt) +{ + switch (rslt) { + case BMI2_OK: break; + case BMI2_E_NULL_PTR: printf("[BMI270] Error: Null pointer\n"); break; + case BMI2_E_COM_FAIL: printf("[BMI270] Error: Communication failure\n"); break; + case BMI2_E_DEV_NOT_FOUND: printf("[BMI270] Error: Device not found\n"); break; + case BMI2_E_INVALID_SENSOR:printf("[BMI270] Error: Invalid sensor\n"); break; + case BMI2_E_CONFIG_LOAD: printf("[BMI270] Error: Config load error\n"); break; + default: printf("[BMI270] Error: Unknown code %d\n", rslt); break; + } +} diff --git a/projects/Compass/main/src/bmm150.c b/projects/Compass/main/src/bmm150.c new file mode 100644 index 00000000..8e6f4fa1 --- /dev/null +++ b/projects/Compass/main/src/bmm150.c @@ -0,0 +1,2187 @@ +/** +* Copyright (c) 2020 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 bmm150.c +* @date 2020-06-03 +* @version v2.0.0 +* +*/ + +/*! @file bmm150.c + * @brief Sensor driver for BMM150 sensor */ +#include "bmm150.h" + +/************************** Internal macros *******************************/ +/* Sensor ODR, Repetition and axes enable/disable settings */ +#define MODE_SETTING_SEL UINT16_C(0x000F) + +/* Interrupt pin settings like polarity,latch and int_pin enable */ +#define INTERRUPT_PIN_SETTING_SEL UINT16_C(0x01F0) + +/* Settings to enable/disable interrupts */ +#define INTERRUPT_CONFIG_SEL UINT16_C(0x1E00) + +/* Interrupt settings for configuring threshold values */ +#define INTERRUPT_THRESHOLD_CONFIG_SEL UINT16_C(0x6000) + +/********************** Static function declarations ************************/ + +/*! + * @brief This internal API is used to validate the device pointer for + * null conditions. + * + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t null_ptr_check(const struct bmm150_dev *dev); + +/*! + * @brief This internal API sets/resets the power control bit of 0x4B register. + * + * @param[in] pwrcntrl_bit : Variable used to select/deselect the suspend mode. + * @param[in,out] dev : Structure instance of bmm150_dev + * + * pwrcntrl_bit | power mode + * -----------------|------------------------- + * 0x00 | Suspend mode + * 0x01 | Sleep/Active modes + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t set_power_control_bit(uint8_t pwrcntrl_bit, struct bmm150_dev *dev); + +/*! + * @brief This internal API reads the trim registers of the sensor and stores + * the trim values in the "trim_data" of device structure. + * + * @param[in,out] dev : Structure instance of bmm150_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t read_trim_registers(struct bmm150_dev *dev); + +/*! + * @brief This internal API writes the op_mode value in the Opmode bits + * (bits 1 and 2) of 0x4C register. + * + * op_mode | Power mode + * ------------|----------------------- + * 0x00 | BMM150_POWERMODE_NORMAL + * 0x01 | BMM150_POWERMODE_FORCED + * 0x03 | BMM150_POWERMODE_SLEEP + * + * @param[in,out] dev : Structure instance of bmm150_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t write_op_mode(uint8_t op_mode, struct bmm150_dev *dev); + +/*! + * @brief This internal API sets the device from suspend to sleep mode + * by setting the power control bit to '1' of 0x4B register + * + * @param[in,out] dev : Structure instance of bmm150_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t suspend_to_sleep_mode(struct bmm150_dev *dev); + +/*! + * @brief This internal API sets the xy repetition value in the 0x51 register. + * + * @param[in] settings : Structure instance of bmm150_settings. + * @param[in,out] dev : Structure instance of bmm150_dev + * + * settings->xy_rep | nXY(XY Repetitions) + * -------------------------|----------------------- + * 0x00 | 1 + * 0x01 | 3 + * 0x02 | 5 + * . | . + * . | . + * 0xFF | 511 + * + * @note number of XY Repetitions nXY = 1+2(settings->xy_rep) + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t set_xy_rep(const struct bmm150_settings *settings, struct bmm150_dev *dev); + +/*! + * @brief This internal API sets the z repetition value in the 0x52 register. + * + * @param[in] settings : Structure instance of bmm150_settings. + * @param[in,out] dev : Structure instance of bmm150_dev + * + * settings->z_rep | nZ(Z Repetitions) + * -------------------------|----------------------- + * 0x00 | 1 + * 0x01 | 2 + * 0x02 | 3 + * . | . + * . | . + * 0xFF | 256 + * + * @note number of Z Repetitions nZ = 1+(settings->z_rep) + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t set_z_rep(const struct bmm150_settings *settings, struct bmm150_dev *dev); + +/*! + * @brief This internal API is used to set the output data rate of the sensor + * + * @param[in] settings : Structure instance of bmm150_settings. + * @param[in] dev : Structure instance of bmm150_dev. + * + * settings->data_rate | Data rate (ODR) + * -------------------------|----------------------- + * 0x00 | BMM150_DATA_RATE_10HZ + * 0x01 | BMM150_DATA_RATE_02HZ + * 0x02 | BMM150_DATA_RATE_06HZ + * 0x03 | BMM150_DATA_RATE_08HZ + * 0x04 | BMM150_DATA_RATE_15HZ + * 0x05 | BMM150_DATA_RATE_20HZ + * 0x06 | BMM150_DATA_RATE_25HZ + * 0x07 | BMM150_DATA_RATE_30HZ + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t set_odr(const struct bmm150_settings *settings, struct bmm150_dev *dev); + +/*! + * @brief This internal API sets the preset mode ODR and repetition settings. + * @param[in] settings : Structure instance of bmm150_settings + * @param[in] dev : Structure instance of bmm150_dev + * + * API settings | Representation + * ---------------------|------------------------------ + * settings->data_rate | Output Data Rate (ODR) + * settings->xy_rep | XY repetition value + * settings->z_rep | Z-repetition value + * + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t set_odr_xyz_rep(const struct bmm150_settings *settings, struct bmm150_dev *dev); + +/*! + * @brief This internal API is used to enable or disable the magnetic + * measurement of x,y,z axes based on the value of xyz_axes_control. + * + * @param[in] settings : Structure instance of bmm150_settings. + * @param[in] dev : Structure instance of bmm150_dev. + * + * settings->xyz_axes_control | Measurement axes/channel + * ---------------------------|-------------------------- + * Bit 0 | X - Channel + * Bit 1 | Y - Channel + * Bit 2 | Z - Channel + * + * @note Setting 1 - Disables Channel measurement + * @note Setting 0 - Enables Channel measurement + * + * settings->xyz_axes_control | Measurement axes Enabled/disabled + * -------------------------------|------------------------------------ + * 0x01 | Disables X axis (Y,Z axes enabled) + * 0x02 | Disables Y axis (X,Z axes enabled) + * 0x04 | Disables Z axis (X,Y axes enabled) + * 0x07 | Disables all X,Y,Z axes measurement + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t set_control_measurement_xyz(const struct bmm150_settings *settings, struct bmm150_dev *dev); + +/*! + * @brief This internal API is used to identify the settings which the user + * wants to modify in the sensor. + * + * @param[in] sub_settings : Contains the settings subset to identify particular + * group of settings which the user is interested to change. + * @param[in] settings : Contains the user specified settings. + * + * @return Indicates whether user is interested to modify the settings which + * are related to sub_settings. + * @retval True -> User wants to modify this group of settings + * @retval False -> User does not want to modify this group of settings + */ +static uint8_t are_settings_changed(uint16_t sub_settings, uint16_t settings); + +/*! + * @brief This API sets the ODR , measurement axes control , + * repetition values of xy,z. + * + * @param[in] desired_settings : Contains the settings which user wants to + * change. + * @param[in] settings : Structure instance of bmm150_settings. + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t mode_settings(uint16_t desired_settings, const struct bmm150_settings *settings, struct bmm150_dev *dev); + +/*! + * @brief This internal API is used to parse and store the sensor + * settings in the device structure + * + * @param[in] reg_data : Pointer of an array consisting all sensor + * setting data from 0x4B to 0x52 registers. + * @param[in] settings : Structure instance of bmm150_settings. + * + */ +static void parse_setting(const uint8_t *reg_data, struct bmm150_settings *settings); + +/*! + * @brief This API is used to enable the interrupts and map them to the + * corresponding interrupt pins and specify the pin characteristics like the + * polarity , latch settings for the interrupt pins. + * + * @note The other interrupts can be latched or non-latched but, + * Data ready interrupt is always cleared after reading out the data + * + * @param[in] desired_settings : Contains the settings which user wants to + * change. + * @param[in] settings : Structure instance of bmm150_settings. + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t interrupt_pin_settings(uint16_t desired_settings, + const struct bmm150_settings *settings, + struct bmm150_dev *dev); + +/*! + * @brief This API is used to enable data overrun , overflow interrupts and + * enable/disable high/low threshold interrupts for x,y,z axis based on the + * threshold values set by the user in the High threshold (0x50) and + * Low threshold (0x4F) registers. + * + * @param[in] desired_settings : Contains the settings which user wants to + * change. + * @param[in] settings : Structure instance of bmm150_settings. + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t interrupt_config(uint16_t desired_settings, const struct bmm150_settings *settings, + struct bmm150_dev *dev); + +/*! + * @brief This API is used to write the user specified High/Low threshold value + * as a reference to generate the high/low threshold interrupt. + * + * @param[in] desired_settings : Contains the settings which user wants to + * change. + * @param[in] settings : Structure instance of bmm150_settings. + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t interrupt_threshold_settings(uint16_t desired_settings, + const struct bmm150_settings *settings, + struct bmm150_dev *dev); + +#ifdef BMM150_USE_FLOATING_POINT + +/*! + * @brief This internal API is used to obtain the compensated + * magnetometer X axis data in float. + * + * @param[in] mag_data_x : The value of raw X data + * @param[in] data_rhall : The value of raw RHALL data + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of compensated X data value in float + */ +static float compensate_x(int16_t mag_data_x, uint16_t data_rhall, const struct bmm150_dev *dev); + +/*! + * @brief This internal API is used to obtain the compensated + * magnetometer Y axis data in float. + * + * @param[in] mag_data_y : The value of raw Y data + * @param[in] data_rhall : The value of raw RHALL data + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of compensated Y data value in float + */ +static float compensate_y(int16_t mag_data_y, uint16_t data_rhall, const struct bmm150_dev *dev); + +/*! + * @brief This internal API is used to obtain the compensated + * magnetometer Z axis data in float. + * + * @param[in] mag_data_z : The value of raw Z data + * @param[in] data_rhall : The value of raw RHALL data + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of compensated Z data value in float + */ +static float compensate_z(int16_t mag_data_z, uint16_t data_rhall, const struct bmm150_dev *dev); + +#else + +/*! + * @brief This internal API is used to obtain the compensated + * magnetometer X axis data in int16_t. + * + * @param[in] mag_data_x : The value of raw X data + * @param[in] data_rhall : The value of raw RHALL data + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of compensated X data value in int16_t format + */ +static int16_t compensate_x(int16_t mag_data_x, uint16_t data_rhall, const struct bmm150_dev *dev); + +/*! + * @brief This internal API is used to obtain the compensated + * magnetometer Y axis data in int16_t. + * + * @param[in] mag_data_y : The value of raw Y data + * @param[in] data_rhall : The value of raw RHALL data + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of compensated Y data value in int16_t format + */ +static int16_t compensate_y(int16_t mag_data_y, uint16_t data_rhall, const struct bmm150_dev *dev); + +/*! + * @brief This internal API is used to obtain the compensated + * magnetometer Z axis data in int16_t. + * + * @param[in] mag_data_z : The value of raw Z data + * @param[in] data_rhall : The value of raw RHALL data + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of compensated Z data value in int16_t format + */ +static int16_t compensate_z(int16_t mag_data_z, uint16_t data_rhall, const struct bmm150_dev *dev); + +#endif + +/*! + * @brief This internal API is used to perform the normal self test + * of the sensor and return the self test result as return value + * + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t perform_normal_self_test(struct bmm150_dev *dev); + +/*! + * @brief This internal API is used to enable the normal self test by setting + * the Self Test bit (bit0) of the 0x4C register, + * which triggers the start of self test + * + * @param[out] self_test_enable : The value of self test bit0 in 0x4C register + * @param[in] dev : Structure instance of bmm150_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t enable_normal_self_test(uint8_t *self_test_enable, struct bmm150_dev *dev); + +/*! + * @brief This internal API is used to validate the results of normal self test + * by using the self test status available in the bit0 of registers 0x42,0x44 + * and 0x46. + * + * @param[in] dev : Structure instance of bmm150_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t validate_normal_self_test(struct bmm150_dev *dev); + +/*! + * @brief This internal API is used to perform advanced self test for Z axis + * + * @param[in] dev : Structure instance of bmm150_dev + * + * @return Result of API execution status + * @retval zero -> Success / +ve value -> Warning / -ve value -> Error + * + * Return value | Status of self-test + *----------------------|--------------------------- + * 0 | BMM150_OK + * 8 | BMM150_W_ADV_SELF_TEST_FAIL + */ +static int8_t perform_adv_self_test(struct bmm150_dev *dev); + +/*! + * @brief This internal API is used to set the desired power mode , + * axes control and repetition settings for advanced self test + * + * @param[in] dev : Structure instance of bmm150_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t adv_self_test_settings(struct bmm150_dev *dev); + +/*! + * @brief This internal API is used to set the positive or negative value of + * self-test current and obtain the corresponding magnetometer z axis data + * + * @param[in] self_test_current : Self test current either positive/negative + * @param[out] data_z : Z-axis Magnetometer data + * @param[in] dev : Structure instance of bmm150_dev + * + * self_test_current | Self-test current Direction + *-------------------------|------------------------------ + * 0x03 | BMM150_ENABLE_POSITIVE_CURRENT + * 0x02 | BMM150_ENABLE_NEGATIVE_CURRENT + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t adv_self_test_measurement(uint8_t self_test_current, int16_t *data_z, struct bmm150_dev *dev); + +/*! + * @brief This internal API is used to get the difference between the + * Z axis mag data obtained by positive and negative self-test current + * and validate whether the advanced self test is done successfully or not. + * + * @param[in] positive_data_z : Z-axis Mag data by positive self-test current + * @param[in] negative_data_z : Z-axis Mag data by negative self-test current + * + * + * @return Result of API execution status + * @retval zero -> Success / +ve value -> Warning / -ve value -> Error + * + * Return value | Status of self-test + *----------------------|--------------------------- + * 0 | BMM150_OK + * 8 | BMM150_W_ADV_SELF_TEST_FAIL + */ +static int8_t validate_adv_self_test(int16_t positive_data_z, int16_t negative_data_z); + +/*! + * @brief This internal API is used to set the self test current value in + * the Adv. ST bits (bit6 and bit7) of 0x4C register + * + * @param[in] self_test_current : Self test current value (+ve/-ve) + * @param[in] dev : Structure instance of bmm150_dev + * + * self_test_current | Self-test current Direction + *-------------------------|------------------------------ + * 0x00 | BMM150_DISABLE_SELF_TEST_CURRENT + * 0x02 | BMM150_ENABLE_NEGATIVE_CURRENT + * 0x03 | BMM150_ENABLE_POSITIVE_CURRENT + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval >0 -> Warning + * @retval <0 -> Fail + */ +static int8_t set_adv_self_test_current(uint8_t self_test_current, struct bmm150_dev *dev); + +/********************** Global function definitions ************************/ + +/*! + * @brief This API is the entry point, Call this API before using other APIs. + * This API reads the chip-id of the sensor which is the first step to + * verify the sensor and updates the trim parameters of the sensor. + */ +int8_t bmm150_init(struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t chip_id = 0; + + /* Power up the sensor from suspend to sleep mode */ + rslt = set_power_control_bit(BMM150_POWER_CNTRL_ENABLE, dev); + + if (rslt == BMM150_OK) + { + /* Start-up time delay of 3ms */ + dev->delay_us(BMM150_START_UP_TIME, dev->intf_ptr); + + /* Chip ID of the sensor is read */ + rslt = bmm150_get_regs(BMM150_REG_CHIP_ID, &chip_id, 1, dev); + + /* Proceed if everything is fine until now */ + if (rslt == BMM150_OK) + { + /* Check for chip id validity */ + if (chip_id == BMM150_CHIP_ID) + { + dev->chip_id = chip_id; + + /* Function to update trim values */ + rslt = read_trim_registers(dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API writes the given data to the register address + * of the sensor. + */ +int8_t bmm150_set_regs(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, struct bmm150_dev *dev) +{ + int8_t rslt; + + /* Check for null pointer in the device structure */ + rslt = null_ptr_check(dev); + + /* Proceed if null check is fine */ + if ((rslt == BMM150_OK) && (reg_data != NULL) && (len != 0)) + { + /* Write the data to the reg_addr */ + + /* SPI write requires to set The MSB of reg_addr as 0 + * but in default the MSB is always 0 + */ + dev->intf_rslt = dev->write(reg_addr, reg_data, len, dev->intf_ptr); + } + else + { + rslt = BMM150_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API reads the data from the given register address of the sensor. + */ +int8_t bmm150_get_regs(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, struct bmm150_dev *dev) +{ + int8_t rslt; + + /* Check for null pointer in the device structure */ + rslt = null_ptr_check(dev); + + /* Proceed if null check is fine */ + if ((rslt == BMM150_OK) && (reg_data != NULL)) + { + if (dev->intf != BMM150_I2C_INTF) + { + /* If interface selected is SPI */ + reg_addr = reg_addr | 0x80; + } + + /* Read the data from the reg_addr */ + dev->intf_rslt = dev->read(reg_addr, reg_data, len, dev->intf_ptr); + } + else + { + rslt = BMM150_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API is used to perform soft-reset of the sensor + * where all the registers are reset to their default values except 0x4B. + */ +int8_t bmm150_soft_reset(struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t reg_data; + + rslt = bmm150_get_regs(BMM150_REG_POWER_CONTROL, ®_data, 1, dev); + + if (rslt == BMM150_OK) + { + reg_data = reg_data | BMM150_SET_SOFT_RESET; + rslt = bmm150_set_regs(BMM150_REG_POWER_CONTROL, ®_data, 1, dev); + dev->delay_us(BMM150_DELAY_SOFT_RESET, dev->intf_ptr); + } + + return rslt; +} + +/*! + * @brief This API is used to set the power mode of the sensor. + */ +int8_t bmm150_set_op_mode(const struct bmm150_settings *settings, struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t pwr_mode; + + /* Check for null pointer in the device structure */ + rslt = null_ptr_check(dev); + + /* Proceed if null check is fine */ + if (rslt == BMM150_OK) + { + pwr_mode = settings->pwr_mode; + + /* Select the power mode to set */ + switch (pwr_mode) + { + case BMM150_POWERMODE_NORMAL: + + /* If the sensor is in suspend mode + * put the device to sleep mode + */ + rslt = suspend_to_sleep_mode(dev); + if (rslt == BMM150_OK) + { + /* write the op mode */ + rslt = write_op_mode(pwr_mode, dev); + } + + break; + case BMM150_POWERMODE_FORCED: + + /* If the sensor is in suspend mode + * put the device to sleep mode + */ + rslt = suspend_to_sleep_mode(dev); + if (rslt == BMM150_OK) + { + /* write the op mode */ + rslt = write_op_mode(pwr_mode, dev); + } + + break; + case BMM150_POWERMODE_SLEEP: + + /* If the sensor is in suspend mode + * put the device to sleep mode + */ + rslt = suspend_to_sleep_mode(dev); + if (rslt == BMM150_OK) + { + /* write the op mode */ + rslt = write_op_mode(pwr_mode, dev); + } + + break; + case BMM150_POWERMODE_SUSPEND: + + /* Set the power control bit to zero */ + rslt = set_power_control_bit(BMM150_POWER_CNTRL_DISABLE, dev); + break; + default: + rslt = BMM150_E_INVALID_CONFIG; + break; + } + } + + return rslt; +} + +/*! + * @brief This API is used to get the power mode of the sensor. + */ +int8_t bmm150_get_op_mode(uint8_t *op_mode, struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t reg_data; + + /* Check for null pointer in the device structure */ + rslt = null_ptr_check(dev); + + /* Proceed if null check is fine */ + if (rslt == BMM150_OK) + { + if (dev->pwr_cntrl_bit == BMM150_POWER_CNTRL_DISABLE) + { + /* Power mode set is suspend mode */ + *op_mode = BMM150_POWERMODE_SUSPEND; + } + else + { + /* Power mode set is stored in the op_mode */ + rslt = bmm150_get_regs(BMM150_REG_OP_MODE, ®_data, 1, dev); + *op_mode = BMM150_GET_BITS(reg_data, BMM150_OP_MODE); + } + } + + return rslt; +} + +/*! + * @brief This API is used to set the preset mode of the sensor. + */ +int8_t bmm150_set_presetmode(struct bmm150_settings *settings, struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t preset_mode; + + /* Check for null pointer in the device structure */ + rslt = null_ptr_check(dev); + + /* Proceed if null check is fine */ + if (rslt == BMM150_OK) + { + preset_mode = settings->preset_mode; + + switch (preset_mode) + { + case BMM150_PRESETMODE_LOWPOWER: + + /* Set the data rate x,y,z repetition + * for Low Power mode + */ + settings->data_rate = BMM150_DATA_RATE_10HZ; + settings->xy_rep = BMM150_REPXY_LOWPOWER; + settings->z_rep = BMM150_REPZ_LOWPOWER; + rslt = set_odr_xyz_rep(settings, dev); + break; + case BMM150_PRESETMODE_REGULAR: + + /* Set the data rate x,y,z repetition + * for Regular mode + */ + settings->data_rate = BMM150_DATA_RATE_10HZ; + settings->xy_rep = BMM150_REPXY_REGULAR; + settings->z_rep = BMM150_REPZ_REGULAR; + rslt = set_odr_xyz_rep(settings, dev); + break; + case BMM150_PRESETMODE_HIGHACCURACY: + + /* Set the data rate x,y,z repetition + * for High Accuracy mode * + */ + settings->data_rate = BMM150_DATA_RATE_20HZ; + settings->xy_rep = BMM150_REPXY_HIGHACCURACY; + settings->z_rep = BMM150_REPZ_HIGHACCURACY; + rslt = set_odr_xyz_rep(settings, dev); + break; + case BMM150_PRESETMODE_ENHANCED: + + /* Set the data rate x,y,z repetition + * for Enhanced Accuracy mode + */ + settings->data_rate = BMM150_DATA_RATE_10HZ; + settings->xy_rep = BMM150_REPXY_ENHANCED; + settings->z_rep = BMM150_REPZ_ENHANCED; + rslt = set_odr_xyz_rep(settings, dev); + break; + default: + rslt = BMM150_E_INVALID_CONFIG; + break; + } + } + + return rslt; +} + +/*! + * @brief This API sets the sensor settings based on the desired_settings + * and the dev structure configuration + */ +int8_t bmm150_set_sensor_settings(uint16_t desired_settings, + const struct bmm150_settings *settings, + struct bmm150_dev *dev) +{ + int8_t rslt; + + /* Check for null pointer in the device structure */ + rslt = null_ptr_check(dev); + + /* Proceed if null check is fine */ + if (rslt == BMM150_OK) + { + if (are_settings_changed(MODE_SETTING_SEL, desired_settings)) + { + /* ODR, Control measurement, XY,Z repetition values */ + rslt = mode_settings(desired_settings, settings, dev); + } + + if ((!rslt) && are_settings_changed(INTERRUPT_PIN_SETTING_SEL, desired_settings)) + { + /* Interrupt pin settings */ + rslt = interrupt_pin_settings(desired_settings, settings, dev); + } + + if ((!rslt) && are_settings_changed(INTERRUPT_CONFIG_SEL, desired_settings)) + { + /* Interrupt configuration settings */ + rslt = interrupt_config(desired_settings, settings, dev); + } + + if ((!rslt) && are_settings_changed(INTERRUPT_THRESHOLD_CONFIG_SEL, desired_settings)) + { + /* Interrupt threshold settings */ + rslt = interrupt_threshold_settings(desired_settings, settings, dev); + } + } + + return rslt; +} + +/*! + * @brief This API gets the sensor settings and updates the dev structure + */ +int8_t bmm150_get_sensor_settings(struct bmm150_settings *settings, struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t setting[BMM150_LEN_SETTING_DATA] = { 0 }; + + /* Read the entire sensor settings */ + rslt = bmm150_get_regs(BMM150_REG_POWER_CONTROL, setting, BMM150_LEN_SETTING_DATA, dev); + + if (rslt == BMM150_OK) + { + /* Parse and store the settings */ + parse_setting(setting, settings); + } + + return rslt; +} + +/*! + * @brief This API is used to read the magnetometer data from registers + * 0x42 to 0x49 and update the dev structure with the + * compensated mag data in micro-tesla. + */ +int8_t bmm150_read_mag_data(struct bmm150_mag_data *mag_data, struct bmm150_dev *dev) +{ + int8_t rslt; + int16_t msb_data; + uint8_t reg_data[BMM150_LEN_XYZR_DATA] = { 0 }; + struct bmm150_raw_mag_data raw_mag_data; + + /* Read the mag data registers */ + rslt = bmm150_get_regs(BMM150_REG_DATA_X_LSB, reg_data, BMM150_LEN_XYZR_DATA, dev); + + if (rslt == BMM150_OK) + { + /* Mag X axis data */ + reg_data[0] = BMM150_GET_BITS(reg_data[0], BMM150_DATA_X); + + /* Shift the MSB data to left by 5 bits */ + /* Multiply by 32 to get the shift left by 5 value */ + msb_data = ((int16_t)((int8_t)reg_data[1])) * 32; + + /* Raw mag X axis data */ + raw_mag_data.raw_datax = (int16_t)(msb_data | reg_data[0]); + + /* Mag Y axis data */ + reg_data[2] = BMM150_GET_BITS(reg_data[2], BMM150_DATA_Y); + + /* Shift the MSB data to left by 5 bits */ + /* Multiply by 32 to get the shift left by 5 value */ + msb_data = ((int16_t)((int8_t)reg_data[3])) * 32; + + /* Raw mag Y axis data */ + raw_mag_data.raw_datay = (int16_t)(msb_data | reg_data[2]); + + /* Mag Z axis data */ + reg_data[4] = BMM150_GET_BITS(reg_data[4], BMM150_DATA_Z); + + /* Shift the MSB data to left by 7 bits */ + /* Multiply by 128 to get the shift left by 7 value */ + msb_data = ((int16_t)((int8_t)reg_data[5])) * 128; + + /* Raw mag Z axis data */ + raw_mag_data.raw_dataz = (int16_t)(msb_data | reg_data[4]); + + /* Mag R-HALL data */ + reg_data[6] = BMM150_GET_BITS(reg_data[6], BMM150_DATA_RHALL); + raw_mag_data.raw_data_r = (uint16_t)(((uint16_t)reg_data[7] << 6) | reg_data[6]); + + /* Compensated Mag X data in int16_t format */ + mag_data->x = compensate_x(raw_mag_data.raw_datax, raw_mag_data.raw_data_r, dev); + + /* Compensated Mag Y data in int16_t format */ + mag_data->y = compensate_y(raw_mag_data.raw_datay, raw_mag_data.raw_data_r, dev); + + /* Compensated Mag Z data in int16_t format */ + mag_data->z = compensate_z(raw_mag_data.raw_dataz, raw_mag_data.raw_data_r, dev); + } + + return rslt; +} + +/*! + * @brief This API is used to perform the complete self test + * (both normal and advanced) for the BMM150 sensor + */ +int8_t bmm150_perform_self_test(uint8_t self_test_mode, struct bmm150_dev *dev) +{ + int8_t rslt; + int8_t self_test_rslt = 0; + struct bmm150_settings settings; + + /* Check for null pointer in the device structure */ + rslt = null_ptr_check(dev); + + /* Proceed if null check is fine */ + if (rslt == BMM150_OK) + { + switch (self_test_mode) + { + case BMM150_SELF_TEST_NORMAL: + + /* Set the sensor in sleep mode */ + settings.pwr_mode = BMM150_POWERMODE_SLEEP; + rslt = bmm150_set_op_mode(&settings, dev); + + if (rslt == BMM150_OK) + { + /* Perform the normal self test */ + rslt = perform_normal_self_test(dev); + } + + break; + case BMM150_SELF_TEST_ADVANCED: + + /* Perform the advanced self test */ + rslt = perform_adv_self_test(dev); + + /* Check to ensure bus error does not occur */ + if (rslt >= BMM150_OK) + { + /* Store the status of self test result */ + self_test_rslt = rslt; + + /* Perform soft reset */ + rslt = bmm150_soft_reset(dev); + } + + /* Check to ensure bus operations are success */ + if (rslt == BMM150_OK) + { + /* Restore self_test_rslt as return value */ + rslt = self_test_rslt; + } + + break; + default: + rslt = BMM150_E_INVALID_CONFIG; + break; + } + } + + return rslt; +} + +/*! + * @brief This API is used to get the status flags of all interrupt + * which is used to check for the assertion of interrupts + */ +int8_t bmm150_get_interrupt_status(struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t interrupt_status; + uint8_t data_ready_status; + + /* Read the data ready status from the register 0x48 */ + rslt = bmm150_get_regs(BMM150_REG_DATA_READY_STATUS, &data_ready_status, 1, dev); + + if (rslt == BMM150_OK) + { + /* Read the interrupt status from the register 0x50 */ + rslt = bmm150_get_regs(BMM150_REG_INTERRUPT_STATUS, &interrupt_status, 1, dev); + + if (rslt == BMM150_OK) + { + /* Mask and store the data ready status bit */ + data_ready_status = BMM150_GET_BITS_POS_0(data_ready_status, BMM150_DRDY_STATUS); + + /* Store the entire interrupt status in dev */ + dev->int_status = (data_ready_status << 8) | interrupt_status; + } + } + + return rslt; +} + +/****************************************************************************/ +/**\name BMM150 as Auxiliary Mag */ + +/*! + * @brief This API is used to compensate the raw mag data + */ +int8_t bmm150_aux_mag_data(uint8_t *aux_data, struct bmm150_mag_data *mag_data, const struct bmm150_dev *dev) +{ + int8_t rslt; + int16_t msb_data; + struct bmm150_raw_mag_data raw_mag_data; + + /* Check for null pointer in the device structure */ + rslt = null_ptr_check(dev); + + /* Proceed if null check is fine */ + if ((rslt == BMM150_OK) && (aux_data != NULL)) + { + /* Mag X axis data */ + aux_data[0] = BMM150_GET_BITS(aux_data[0], BMM150_DATA_X); + + /* Shift the MSB data to left by 5 bits */ + /* Multiply by 32 to get the shift left by 5 value */ + msb_data = ((int16_t)((int8_t)aux_data[1])) * 32; + + /* Raw mag X axis data */ + raw_mag_data.raw_datax = (int16_t)(msb_data | aux_data[0]); + + /* Mag Y axis data */ + aux_data[2] = BMM150_GET_BITS(aux_data[2], BMM150_DATA_Y); + + /* Shift the MSB data to left by 5 bits */ + /* Multiply by 32 to get the shift left by 5 value */ + msb_data = ((int16_t)((int8_t)aux_data[3])) * 32; + + /* Raw mag Y axis data */ + raw_mag_data.raw_datay = (int16_t)(msb_data | aux_data[2]); + + /* Mag Z axis data */ + aux_data[4] = BMM150_GET_BITS(aux_data[4], BMM150_DATA_Z); + + /* Shift the MSB data to left by 7 bits */ + /* Multiply by 128 to get the shift left by 7 value */ + msb_data = ((int16_t)((int8_t)aux_data[5])) * 128; + + /* Raw mag Z axis data */ + raw_mag_data.raw_dataz = (int16_t)(msb_data | aux_data[4]); + + /* Mag R-HALL data */ + aux_data[6] = BMM150_GET_BITS(aux_data[6], BMM150_DATA_RHALL); + raw_mag_data.raw_data_r = (uint16_t)(((uint16_t)aux_data[7] << 6) | aux_data[6]); + + /* Compensated Mag X data in int16_t format */ + mag_data->x = compensate_x(raw_mag_data.raw_datax, raw_mag_data.raw_data_r, dev); + + /* Compensated Mag Y data in int16_t format */ + mag_data->y = compensate_y(raw_mag_data.raw_datay, raw_mag_data.raw_data_r, dev); + + /* Compensated Mag Z data in int16_t format */ + mag_data->z = compensate_z(raw_mag_data.raw_dataz, raw_mag_data.raw_data_r, dev); + } + + return rslt; +} + +/****************************************************************************/ +/**\name INTERNAL APIs */ + +/*! + * @brief This internal API is used to validate the device structure pointer for + * null conditions. + */ +static int8_t null_ptr_check(const struct bmm150_dev *dev) +{ + int8_t rslt; + + if ((dev == NULL) || (dev->read == NULL) || (dev->write == NULL) || (dev->delay_us == NULL) || + (dev->intf_ptr == NULL)) + { + /* Device structure pointer is not valid */ + rslt = BMM150_E_NULL_PTR; + } + else + { + /* Device structure is fine */ + rslt = BMM150_OK; + } + + return rslt; +} + +/*! + * @brief This internal API sets/resets the power control bit of 0x4B register. + */ +static int8_t set_power_control_bit(uint8_t pwrcntrl_bit, struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t reg_data = 0; + + /* Power control register 0x4B is read */ + rslt = bmm150_get_regs(BMM150_REG_POWER_CONTROL, ®_data, 1, dev); + + /* Proceed if everything is fine until now */ + if (rslt == BMM150_OK) + { + /* Sets the value of power control bit */ + reg_data = BMM150_SET_BITS_POS_0(reg_data, BMM150_PWR_CNTRL, pwrcntrl_bit); + rslt = bmm150_set_regs(BMM150_REG_POWER_CONTROL, ®_data, 1, dev); + + if (rslt == BMM150_OK) + { + /* Store the power control bit + * value in dev structure + */ + dev->pwr_cntrl_bit = pwrcntrl_bit; + } + } + + return rslt; +} + +/*! + * @brief This internal API reads the trim registers of the sensor and stores + * the trim values in the "trim_data" of device structure. + */ +static int8_t read_trim_registers(struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t trim_x1y1[2] = { 0 }; + uint8_t trim_xyz_data[4] = { 0 }; + uint8_t trim_xy1xy2[10] = { 0 }; + uint16_t temp_msb = 0; + + /* Trim register value is read */ + rslt = bmm150_get_regs(BMM150_DIG_X1, trim_x1y1, 2, dev); + + if (rslt == BMM150_OK) + { + rslt = bmm150_get_regs(BMM150_DIG_Z4_LSB, trim_xyz_data, 4, dev); + + if (rslt == BMM150_OK) + { + rslt = bmm150_get_regs(BMM150_DIG_Z2_LSB, trim_xy1xy2, 10, dev); + + if (rslt == BMM150_OK) + { + /* Trim data which is read is updated + * in the device structure + */ + dev->trim_data.dig_x1 = (int8_t)trim_x1y1[0]; + dev->trim_data.dig_y1 = (int8_t)trim_x1y1[1]; + dev->trim_data.dig_x2 = (int8_t)trim_xyz_data[2]; + dev->trim_data.dig_y2 = (int8_t)trim_xyz_data[3]; + temp_msb = ((uint16_t)trim_xy1xy2[3]) << 8; + dev->trim_data.dig_z1 = (uint16_t)(temp_msb | trim_xy1xy2[2]); + temp_msb = ((uint16_t)trim_xy1xy2[1]) << 8; + dev->trim_data.dig_z2 = (int16_t)(temp_msb | trim_xy1xy2[0]); + temp_msb = ((uint16_t)trim_xy1xy2[7]) << 8; + dev->trim_data.dig_z3 = (int16_t)(temp_msb | trim_xy1xy2[6]); + temp_msb = ((uint16_t)trim_xyz_data[1]) << 8; + dev->trim_data.dig_z4 = (int16_t)(temp_msb | trim_xyz_data[0]); + dev->trim_data.dig_xy1 = trim_xy1xy2[9]; + dev->trim_data.dig_xy2 = (int8_t)trim_xy1xy2[8]; + temp_msb = ((uint16_t)(trim_xy1xy2[5] & 0x7F)) << 8; + dev->trim_data.dig_xyz1 = (uint16_t)(temp_msb | trim_xy1xy2[4]); + } + } + } + + return rslt; +} + +/*! + * @brief This internal API writes the op_mode value in the Opmode bits + * (bits 1 and 2) of 0x4C register. + */ +static int8_t write_op_mode(uint8_t op_mode, struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t reg_data; + + /* Read the 0x4C register */ + rslt = bmm150_get_regs(BMM150_REG_OP_MODE, ®_data, 1, dev); + + if (rslt == BMM150_OK) + { + /* Set the op_mode value in Opmode bits of 0x4C */ + reg_data = BMM150_SET_BITS(reg_data, BMM150_OP_MODE, op_mode); + rslt = bmm150_set_regs(BMM150_REG_OP_MODE, ®_data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This internal API sets the device from suspend to sleep mode + * by setting the power control bit to '1' of 0x4B register + */ +static int8_t suspend_to_sleep_mode(struct bmm150_dev *dev) +{ + int8_t rslt; + + /* Check for null pointer in the device structure */ + rslt = null_ptr_check(dev); + + /* Proceed if null check is fine */ + if (rslt == BMM150_OK) + { + if (dev->pwr_cntrl_bit == BMM150_POWER_CNTRL_DISABLE) + { + rslt = set_power_control_bit(BMM150_POWER_CNTRL_ENABLE, dev); + + /* Start-up time delay of 3ms */ + dev->delay_us(BMM150_START_UP_TIME, dev->intf_ptr); + } + } + + return rslt; +} + +/*! + * @brief This internal API sets the xy repetition value in the 0x51 register. + */ +static int8_t set_xy_rep(const struct bmm150_settings *settings, struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t rep_xy; + + /* Set the xy repetition */ + rep_xy = settings->xy_rep; + rslt = bmm150_set_regs(BMM150_REG_REP_XY, &rep_xy, 1, dev); + + return rslt; +} + +/*! + * @brief This internal API sets the z repetition value in the 0x52 register. + */ +static int8_t set_z_rep(const struct bmm150_settings *settings, struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t rep_z; + + /* Set the z repetition */ + rep_z = settings->z_rep; + rslt = bmm150_set_regs(BMM150_REG_REP_Z, &rep_z, 1, dev); + + return rslt; +} + +/*! + * @brief This internal API is used to set the output data rate of the sensor. + */ +static int8_t set_odr(const struct bmm150_settings *settings, struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t reg_data; + + /* Read the 0x4C register */ + rslt = bmm150_get_regs(BMM150_REG_OP_MODE, ®_data, 1, dev); + + if (rslt == BMM150_OK) + { + /* Set the ODR value */ + reg_data = BMM150_SET_BITS(reg_data, BMM150_ODR, settings->data_rate); + rslt = bmm150_set_regs(BMM150_REG_OP_MODE, ®_data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This internal API sets the preset mode ODR and repetition settings. + */ +static int8_t set_odr_xyz_rep(const struct bmm150_settings *settings, struct bmm150_dev *dev) +{ + int8_t rslt; + + /* Set the ODR */ + rslt = set_odr(settings, dev); + + if (rslt == BMM150_OK) + { + /* Set the XY-repetitions number */ + rslt = set_xy_rep(settings, dev); + + if (rslt == BMM150_OK) + { + /* Set the Z-repetitions number */ + rslt = set_z_rep(settings, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API is used to enable or disable the magnetic + * measurement of x,y,z axes based on the value of xyz_axes_control. + */ +static int8_t set_control_measurement_xyz(const struct bmm150_settings *settings, struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t reg_data; + + rslt = bmm150_get_regs(BMM150_REG_AXES_ENABLE, ®_data, 1, dev); + + if (rslt == BMM150_OK) + { + /* Set the axes to be enabled/disabled */ + reg_data = BMM150_SET_BITS(reg_data, BMM150_CONTROL_MEASURE, settings->xyz_axes_control); + rslt = bmm150_set_regs(BMM150_REG_AXES_ENABLE, ®_data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This internal API is used to identify the settings which the user + * wants to modify in the sensor. + */ +static uint8_t are_settings_changed(uint16_t sub_settings, uint16_t desired_settings) +{ + uint8_t settings_changed; + + if (sub_settings & desired_settings) + { + /* User wants to modify this particular settings */ + settings_changed = BMM150_TRUE; + } + else + { + /* User don't want to modify this particular settings */ + settings_changed = BMM150_FALSE; + } + + return settings_changed; +} + +/*! + * @brief This API sets the ODR , measurement axes control , + * repetition values of xy,z. + */ +static int8_t mode_settings(uint16_t desired_settings, const struct bmm150_settings *settings, struct bmm150_dev *dev) +{ + int8_t rslt = BMM150_E_INVALID_CONFIG; + + if (desired_settings & BMM150_SEL_DATA_RATE) + { + /* Sets the ODR */ + rslt = set_odr(settings, dev); + } + + if (desired_settings & BMM150_SEL_CONTROL_MEASURE) + { + /* Enables/Disables the control measurement axes */ + rslt = set_control_measurement_xyz(settings, dev); + } + + if (desired_settings & BMM150_SEL_XY_REP) + { + /* Sets the XY repetition */ + rslt = set_xy_rep(settings, dev); + } + + if (desired_settings & BMM150_SEL_Z_REP) + { + /* Sets the Z repetition */ + rslt = set_z_rep(settings, dev); + } + + return rslt; +} + +/*! + * @brief This internal API is used to parse and store the sensor + * settings in the device structure + */ +static void parse_setting(const uint8_t *reg_data, struct bmm150_settings *settings) +{ + /* Parse all the w/r registers and update the + * current sensor settings in the dev structure + */ + settings->z_rep = reg_data[7]; + settings->xy_rep = reg_data[6]; + settings->int_settings.high_threshold = reg_data[5]; + settings->int_settings.low_threshold = reg_data[4]; + settings->xyz_axes_control = BMM150_GET_BITS(reg_data[3], BMM150_CONTROL_MEASURE); + settings->int_settings.drdy_pin_en = BMM150_GET_BITS(reg_data[3], BMM150_DRDY_EN); + settings->int_settings.int_pin_en = BMM150_GET_BITS(reg_data[3], BMM150_INT_PIN_EN); + settings->int_settings.drdy_polarity = BMM150_GET_BITS(reg_data[3], BMM150_DRDY_POLARITY); + settings->int_settings.int_latch = BMM150_GET_BITS(reg_data[3], BMM150_INT_LATCH); + settings->int_settings.int_polarity = BMM150_GET_BITS_POS_0(reg_data[3], BMM150_INT_POLARITY); + settings->int_settings.data_overrun_en = BMM150_GET_BITS(reg_data[2], BMM150_DATA_OVERRUN_INT); + settings->int_settings.overflow_int_en = BMM150_GET_BITS(reg_data[2], BMM150_OVERFLOW_INT); + settings->int_settings.high_int_en = BMM150_GET_BITS(reg_data[2], BMM150_HIGH_THRESHOLD_INT); + settings->int_settings.low_int_en = BMM150_GET_BITS_POS_0(reg_data[2], BMM150_LOW_THRESHOLD_INT); + settings->data_rate = BMM150_GET_BITS(reg_data[1], BMM150_ODR); +} + +/*! + * @brief This API is used to enable the interrupts and map them to the + * corresponding interrupt pins and specify the pin characteristics like the + * polarity , latch settings for the interrupt pins. + */ +static int8_t interrupt_pin_settings(uint16_t desired_settings, + const struct bmm150_settings *settings, + struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t reg_data; + struct bmm150_int_ctrl_settings int_settings; + + rslt = bmm150_get_regs(BMM150_REG_AXES_ENABLE, ®_data, 1, dev); + + if (rslt == BMM150_OK) + { + int_settings = settings->int_settings; + if (desired_settings & BMM150_SEL_DRDY_PIN_EN) + { + /* Enables the Data ready interrupt and + * maps it to the DRDY pin of the sensor + */ + reg_data = BMM150_SET_BITS(reg_data, BMM150_DRDY_EN, int_settings.drdy_pin_en); + } + + if (desired_settings & BMM150_SEL_INT_PIN_EN) + { + /* Sets interrupt pin enable */ + reg_data = BMM150_SET_BITS(reg_data, BMM150_INT_PIN_EN, int_settings.int_pin_en); + } + + if (desired_settings & BMM150_SEL_DRDY_POLARITY) + { + /* Sets Data ready pin's polarity */ + reg_data = BMM150_SET_BITS(reg_data, BMM150_DRDY_POLARITY, int_settings.drdy_polarity); + } + + if (desired_settings & BMM150_SEL_INT_LATCH) + { + /* Sets Interrupt in latched or non-latched mode */ + reg_data = BMM150_SET_BITS(reg_data, BMM150_INT_LATCH, int_settings.int_latch); + } + + if (desired_settings & BMM150_SEL_INT_POLARITY) + { + /* Sets Interrupt pin's polarity */ + reg_data = BMM150_SET_BITS_POS_0(reg_data, BMM150_INT_POLARITY, int_settings.int_polarity); + } + + /* Set the interrupt configurations in the 0x4E register */ + rslt = bmm150_set_regs(BMM150_REG_AXES_ENABLE, ®_data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API is used to enable data overrun , overflow interrupts and + * enable/disable high/low threshold interrupts for x,y,z axis based on the + * threshold values set by the user in the High threshold (0x50) and + * Low threshold (0x4F) registers. + */ +static int8_t interrupt_config(uint16_t desired_settings, const struct bmm150_settings *settings, + struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t reg_data; + struct bmm150_int_ctrl_settings int_settings; + + rslt = bmm150_get_regs(BMM150_REG_INT_CONFIG, ®_data, 1, dev); + + if (rslt == BMM150_OK) + { + int_settings = settings->int_settings; + if (desired_settings & BMM150_SEL_DATA_OVERRUN_INT) + { + /* Sets Data overrun interrupt */ + reg_data = BMM150_SET_BITS(reg_data, BMM150_DATA_OVERRUN_INT, int_settings.data_overrun_en); + } + + if (desired_settings & BMM150_SEL_OVERFLOW_INT) + { + /* Sets Data overflow interrupt */ + reg_data = BMM150_SET_BITS(reg_data, BMM150_OVERFLOW_INT, int_settings.overflow_int_en); + } + + if (desired_settings & BMM150_SEL_HIGH_THRESHOLD_INT) + { + /* Sets high threshold interrupt */ + reg_data = BMM150_SET_BITS(reg_data, BMM150_HIGH_THRESHOLD_INT, int_settings.high_int_en); + } + + if (desired_settings & BMM150_SEL_LOW_THRESHOLD_INT) + { + /* Sets low threshold interrupt */ + reg_data = BMM150_SET_BITS_POS_0(reg_data, BMM150_LOW_THRESHOLD_INT, int_settings.low_int_en); + } + + /* Set the interrupt configurations in the 0x4D register */ + rslt = bmm150_set_regs(BMM150_REG_INT_CONFIG, ®_data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API is used to write the user specified High/Low threshold value + * as a reference to generate the high/low threshold interrupt. + */ +static int8_t interrupt_threshold_settings(uint16_t desired_settings, + const struct bmm150_settings *settings, + struct bmm150_dev *dev) +{ + int8_t rslt = BMM150_E_INVALID_CONFIG; + uint8_t reg_data; + + if (desired_settings & BMM150_SEL_LOW_THRESHOLD_SETTING) + { + /* Sets the Low threshold value to trigger interrupt */ + reg_data = settings->int_settings.low_threshold; + rslt = bmm150_set_regs(BMM150_REG_LOW_THRESHOLD, ®_data, 1, dev); + } + + if (desired_settings & BMM150_SEL_HIGH_THRESHOLD_SETTING) + { + /* Sets the High threshold value to trigger interrupt */ + reg_data = settings->int_settings.high_threshold; + rslt = bmm150_set_regs(BMM150_REG_HIGH_THRESHOLD, ®_data, 1, dev); + } + + return rslt; +} + +#ifdef BMM150_USE_FLOATING_POINT + +/*! + * @brief This internal API is used to obtain the compensated + * magnetometer x axis data(micro-tesla) in float. + */ +static float compensate_x(int16_t mag_data_x, uint16_t data_rhall, const struct bmm150_dev *dev) +{ + float retval = 0; + float process_comp_x0; + float process_comp_x1; + float process_comp_x2; + float process_comp_x3; + float process_comp_x4; + + /* Overflow condition check */ + if ((mag_data_x != BMM150_OVERFLOW_ADCVAL_XYAXES_FLIP) && (data_rhall != 0) && (dev->trim_data.dig_xyz1 != 0)) + { + /* Processing compensation equations */ + process_comp_x0 = (((float)dev->trim_data.dig_xyz1) * 16384.0f / data_rhall); + retval = (process_comp_x0 - 16384.0f); + process_comp_x1 = ((float)dev->trim_data.dig_xy2) * (retval * retval / 268435456.0f); + process_comp_x2 = process_comp_x1 + retval * ((float)dev->trim_data.dig_xy1) / 16384.0f; + process_comp_x3 = ((float)dev->trim_data.dig_x2) + 160.0f; + process_comp_x4 = mag_data_x * ((process_comp_x2 + 256.0f) * process_comp_x3); + retval = ((process_comp_x4 / 8192.0f) + (((float)dev->trim_data.dig_x1) * 8.0f)) / 16.0f; + } + else + { + /* Overflow, set output to 0.0f */ + retval = BMM150_OVERFLOW_OUTPUT_FLOAT; + } + + return retval; +} + +/*! + * @brief This internal API is used to obtain the compensated + * magnetometer y axis data(micro-tesla) in float. + */ +static float compensate_y(int16_t mag_data_y, uint16_t data_rhall, const struct bmm150_dev *dev) +{ + float retval = 0; + float process_comp_y0; + float process_comp_y1; + float process_comp_y2; + float process_comp_y3; + float process_comp_y4; + + /* Overflow condition check */ + if ((mag_data_y != BMM150_OVERFLOW_ADCVAL_XYAXES_FLIP) && (data_rhall != 0) && (dev->trim_data.dig_xyz1 != 0)) + { + /* Processing compensation equations */ + process_comp_y0 = ((float)dev->trim_data.dig_xyz1) * 16384.0f / data_rhall; + retval = process_comp_y0 - 16384.0f; + process_comp_y1 = ((float)dev->trim_data.dig_xy2) * (retval * retval / 268435456.0f); + process_comp_y2 = process_comp_y1 + retval * ((float)dev->trim_data.dig_xy1) / 16384.0f; + process_comp_y3 = ((float)dev->trim_data.dig_y2) + 160.0f; + process_comp_y4 = mag_data_y * (((process_comp_y2) + 256.0f) * process_comp_y3); + retval = ((process_comp_y4 / 8192.0f) + (((float)dev->trim_data.dig_y1) * 8.0f)) / 16.0f; + } + else + { + /* Overflow, set output to 0.0f */ + retval = BMM150_OVERFLOW_OUTPUT_FLOAT; + } + + return retval; +} + +/*! + * @brief This internal API is used to obtain the compensated + * magnetometer z axis data(micro-tesla) in float. + */ +static float compensate_z(int16_t mag_data_z, uint16_t data_rhall, const struct bmm150_dev *dev) +{ + float retval = 0; + float process_comp_z0; + float process_comp_z1; + float process_comp_z2; + float process_comp_z3; + float process_comp_z4; + float process_comp_z5; + + /* Overflow condition check */ + if ((mag_data_z != BMM150_OVERFLOW_ADCVAL_ZAXIS_HALL) && (dev->trim_data.dig_z2 != 0) && + (dev->trim_data.dig_z1 != 0) && (dev->trim_data.dig_xyz1 != 0) && (data_rhall != 0)) + { + /* Processing compensation equations */ + process_comp_z0 = ((float)mag_data_z) - ((float)dev->trim_data.dig_z4); + process_comp_z1 = ((float)data_rhall) - ((float)dev->trim_data.dig_xyz1); + process_comp_z2 = (((float)dev->trim_data.dig_z3) * process_comp_z1); + process_comp_z3 = ((float)dev->trim_data.dig_z1) * ((float)data_rhall) / 32768.0f; + process_comp_z4 = ((float)dev->trim_data.dig_z2) + process_comp_z3; + process_comp_z5 = (process_comp_z0 * 131072.0f) - process_comp_z2; + retval = (process_comp_z5 / ((process_comp_z4) * 4.0f)) / 16.0f; + } + else + { + /* Overflow, set output to 0.0f */ + retval = BMM150_OVERFLOW_OUTPUT_FLOAT; + } + + return retval; +} + +#else + +/*! + * @brief This internal API is used to obtain the compensated + * magnetometer X axis data(micro-tesla) in int16_t. + */ +static int16_t compensate_x(int16_t mag_data_x, uint16_t data_rhall, const struct bmm150_dev *dev) +{ + int16_t retval; + uint16_t process_comp_x0 = 0; + int32_t process_comp_x1; + uint16_t process_comp_x2; + int32_t process_comp_x3; + int32_t process_comp_x4; + int32_t process_comp_x5; + int32_t process_comp_x6; + int32_t process_comp_x7; + int32_t process_comp_x8; + int32_t process_comp_x9; + int32_t process_comp_x10; + + /* Overflow condition check */ + if (mag_data_x != BMM150_OVERFLOW_ADCVAL_XYAXES_FLIP) + { + if (data_rhall != 0) + { + /* Availability of valid data */ + process_comp_x0 = data_rhall; + } + else if (dev->trim_data.dig_xyz1 != 0) + { + process_comp_x0 = dev->trim_data.dig_xyz1; + } + else + { + process_comp_x0 = 0; + } + + if (process_comp_x0 != 0) + { + /* Processing compensation equations */ + process_comp_x1 = ((int32_t)dev->trim_data.dig_xyz1) * 16384; + process_comp_x2 = ((uint16_t)(process_comp_x1 / process_comp_x0)) - ((uint16_t)0x4000); + retval = ((int16_t)process_comp_x2); + process_comp_x3 = (((int32_t)retval) * ((int32_t)retval)); + process_comp_x4 = (((int32_t)dev->trim_data.dig_xy2) * (process_comp_x3 / 128)); + process_comp_x5 = (int32_t)(((int16_t)dev->trim_data.dig_xy1) * 128); + process_comp_x6 = ((int32_t)retval) * process_comp_x5; + process_comp_x7 = (((process_comp_x4 + process_comp_x6) / 512) + ((int32_t)0x100000)); + process_comp_x8 = ((int32_t)(((int16_t)dev->trim_data.dig_x2) + ((int16_t)0xA0))); + process_comp_x9 = ((process_comp_x7 * process_comp_x8) / 4096); + process_comp_x10 = ((int32_t)mag_data_x) * process_comp_x9; + retval = ((int16_t)(process_comp_x10 / 8192)); + retval = (retval + (((int16_t)dev->trim_data.dig_x1) * 8)) / 16; + } + else + { + retval = BMM150_OVERFLOW_OUTPUT; + } + } + else + { + /* Overflow condition */ + retval = BMM150_OVERFLOW_OUTPUT; + } + + return retval; +} + +/*! + * @brief This internal API is used to obtain the compensated + * magnetometer Y axis data(micro-tesla) in int16_t. + */ +static int16_t compensate_y(int16_t mag_data_y, uint16_t data_rhall, const struct bmm150_dev *dev) +{ + int16_t retval; + uint16_t process_comp_y0 = 0; + int32_t process_comp_y1; + uint16_t process_comp_y2; + int32_t process_comp_y3; + int32_t process_comp_y4; + int32_t process_comp_y5; + int32_t process_comp_y6; + int32_t process_comp_y7; + int32_t process_comp_y8; + int32_t process_comp_y9; + + /* Overflow condition check */ + if (mag_data_y != BMM150_OVERFLOW_ADCVAL_XYAXES_FLIP) + { + if (data_rhall != 0) + { + /* Availability of valid data */ + process_comp_y0 = data_rhall; + } + else if (dev->trim_data.dig_xyz1 != 0) + { + process_comp_y0 = dev->trim_data.dig_xyz1; + } + else + { + process_comp_y0 = 0; + } + + if (process_comp_y0 != 0) + { + /* Processing compensation equations */ + process_comp_y1 = (((int32_t)dev->trim_data.dig_xyz1) * 16384) / process_comp_y0; + process_comp_y2 = ((uint16_t)process_comp_y1) - ((uint16_t)0x4000); + retval = ((int16_t)process_comp_y2); + process_comp_y3 = ((int32_t) retval) * ((int32_t)retval); + process_comp_y4 = ((int32_t)dev->trim_data.dig_xy2) * (process_comp_y3 / 128); + process_comp_y5 = ((int32_t)(((int16_t)dev->trim_data.dig_xy1) * 128)); + process_comp_y6 = ((process_comp_y4 + (((int32_t)retval) * process_comp_y5)) / 512); + process_comp_y7 = ((int32_t)(((int16_t)dev->trim_data.dig_y2) + ((int16_t)0xA0))); + process_comp_y8 = (((process_comp_y6 + ((int32_t)0x100000)) * process_comp_y7) / 4096); + process_comp_y9 = (((int32_t)mag_data_y) * process_comp_y8); + retval = (int16_t)(process_comp_y9 / 8192); + retval = (retval + (((int16_t)dev->trim_data.dig_y1) * 8)) / 16; + } + else + { + retval = BMM150_OVERFLOW_OUTPUT; + } + } + else + { + /* Overflow condition */ + retval = BMM150_OVERFLOW_OUTPUT; + } + + return retval; +} + +/*! + * @brief This internal API is used to obtain the compensated + * magnetometer Z axis data(micro-tesla) in int16_t. + */ +static int16_t compensate_z(int16_t mag_data_z, uint16_t data_rhall, const struct bmm150_dev *dev) +{ + int32_t retval; + int16_t process_comp_z0; + int32_t process_comp_z1; + int32_t process_comp_z2; + int32_t process_comp_z3; + int16_t process_comp_z4; + + if (mag_data_z != BMM150_OVERFLOW_ADCVAL_ZAXIS_HALL) + { + if ((dev->trim_data.dig_z2 != 0) && (dev->trim_data.dig_z1 != 0) && (data_rhall != 0) && + (dev->trim_data.dig_xyz1 != 0)) + { + /*Processing compensation equations */ + process_comp_z0 = ((int16_t)data_rhall) - ((int16_t) dev->trim_data.dig_xyz1); + process_comp_z1 = (((int32_t)dev->trim_data.dig_z3) * ((int32_t)(process_comp_z0))) / 4; + process_comp_z2 = (((int32_t)(mag_data_z - dev->trim_data.dig_z4)) * 32768); + process_comp_z3 = ((int32_t)dev->trim_data.dig_z1) * (((int16_t)data_rhall) * 2); + process_comp_z4 = (int16_t)((process_comp_z3 + (32768)) / 65536); + retval = ((process_comp_z2 - process_comp_z1) / (dev->trim_data.dig_z2 + process_comp_z4)); + + /* Saturate result to +/- 2 micro-tesla */ + if (retval > BMM150_POSITIVE_SATURATION_Z) + { + retval = BMM150_POSITIVE_SATURATION_Z; + } + else if (retval < BMM150_NEGATIVE_SATURATION_Z) + { + retval = BMM150_NEGATIVE_SATURATION_Z; + } + + /* Conversion of LSB to micro-tesla */ + retval = retval / 16; + } + else + { + retval = BMM150_OVERFLOW_OUTPUT; + } + } + else + { + /* Overflow condition */ + retval = BMM150_OVERFLOW_OUTPUT; + } + + return (int16_t)retval; +} + +#endif + +/*! + * @brief This internal API is used to perform the normal self test + * of the sensor and return the self test result as return value + */ +static int8_t perform_normal_self_test(struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t self_test_bit; + + /* Triggers the start of normal self test */ + rslt = enable_normal_self_test(&self_test_bit, dev); + + /* Check for self test completion status */ + if ((rslt == BMM150_OK) && (self_test_bit == 0)) + { + /* Validates the self test results for all 3 axes */ + rslt = validate_normal_self_test(dev); + } + + return rslt; +} + +/*! + * @brief This internal API is used to enable the normal self test by setting + * the Self Test bit (bit0) of the 0x4C register, + * which triggers the start of self test + */ +static int8_t enable_normal_self_test(uint8_t *self_test_enable, struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t reg_data; + uint8_t self_test_val; + + /* Read the data from register 0x4C */ + rslt = bmm150_get_regs(BMM150_REG_OP_MODE, ®_data, 1, dev); + + if (rslt == BMM150_OK) + { + /* Set the Self Test bit(bit0) of the 0x4C register */ + self_test_val = 1; + reg_data = BMM150_SET_BITS_POS_0(reg_data, BMM150_SELF_TEST, self_test_val); + + /* Write the data to 0x4C register to trigger self test */ + rslt = bmm150_set_regs(BMM150_REG_OP_MODE, ®_data, 1, dev); + dev->delay_us(BMM150_DELAY_NORMAL_SELF_TEST, dev->intf_ptr); + + if (rslt == BMM150_OK) + { + /* Read the data from register 0x4C */ + rslt = bmm150_get_regs(BMM150_REG_OP_MODE, ®_data, 1, dev); + + /* Self Test bit(bit0) is stored in self_test_enable, + * It will be reset to zero after the self test is over + */ + *self_test_enable = BMM150_GET_BITS_POS_0(reg_data, BMM150_SELF_TEST); + } + } + + return rslt; +} + +/*! + * @brief This internal API is used to validate the results of normal self test + * by using the self test status available in the bit0 of registers 0x42,0x44 + * and 0x46. + */ +static int8_t validate_normal_self_test(struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t status; + uint8_t self_test_rslt[5]; + + /* Read the data from register 0x42 to 0x46 */ + rslt = bmm150_get_regs(BMM150_REG_DATA_X_LSB, self_test_rslt, BMM150_LEN_SELF_TEST, dev); + + if (rslt == BMM150_OK) + { + /* Parse and get the self test status bits */ + /* X-Self-Test (bit0) of 0x42 register is stored */ + self_test_rslt[0] = BMM150_GET_BITS_POS_0(self_test_rslt[0], BMM150_SELF_TEST); + + /* Y-Self-Test (bit0) of 0x44 register is stored */ + self_test_rslt[2] = BMM150_GET_BITS_POS_0(self_test_rslt[2], BMM150_SELF_TEST); + + /* Z-Self-Test (bit0) of 0x46 register is stored */ + self_test_rslt[4] = BMM150_GET_BITS_POS_0(self_test_rslt[4], BMM150_SELF_TEST); + + /* Combine the self test status and store it in the first + * 3 bits of the status variable for processing + */ + status = (uint8_t)((self_test_rslt[4] << 2) | (self_test_rslt[2] << 1) | self_test_rslt[0]); + + /* Validate status and store Self test result in "rslt" */ + if (status == BMM150_SELF_TEST_STATUS_SUCCESS) + { + /* Self test is success when all status bits are set */ + rslt = BMM150_OK; + } + else + { + if (status == BMM150_SELF_TEST_STATUS_XYZ_FAIL) + { + /* Self test - all axis fail condition */ + rslt = BMM150_W_NORMAL_SELF_TEST_XYZ_FAIL; + } + else + { + /* Self test - some axis fail condition */ + rslt = (int8_t)status; + } + } + } + + return rslt; +} + +/*! + * @brief This internal API is used to perform advanced self test for Z axis + */ +static int8_t perform_adv_self_test(struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t self_test_current; + int16_t positive_data_z; + int16_t negative_data_z; + + /* Set the desired power mode ,axes control and repetition settings */ + rslt = adv_self_test_settings(dev); + + if (rslt == BMM150_OK) + { + /* Measure the Z axes data with positive self-test current */ + self_test_current = BMM150_ENABLE_POSITIVE_CURRENT; + rslt = adv_self_test_measurement(self_test_current, &positive_data_z, dev); + + if (rslt == BMM150_OK) + { + /* Measure the Z axes data with + * negative self-test current + */ + self_test_current = BMM150_ENABLE_NEGATIVE_CURRENT; + rslt = adv_self_test_measurement(self_test_current, &negative_data_z, dev); + + if (rslt == BMM150_OK) + { + /* Disable self-test current */ + self_test_current = BMM150_DISABLE_SELF_TEST_CURRENT; + rslt = set_adv_self_test_current(self_test_current, dev); + + if (rslt == BMM150_OK) + { + /* Validate the advanced self test */ + rslt = validate_adv_self_test(positive_data_z, negative_data_z); + } + } + } + } + + return rslt; +} + +/*! + * @brief This internal API is used to set the desired power mode , + * axes control and repetition settings for advanced self test + */ +static int8_t adv_self_test_settings(struct bmm150_dev *dev) +{ + int8_t rslt; + struct bmm150_settings settings; + + /* Set the power mode as sleep mode */ + settings.pwr_mode = BMM150_POWERMODE_SLEEP; + rslt = bmm150_set_op_mode(&settings, dev); + + if (rslt == BMM150_OK) + { + /* Disable XY-axis measurement */ + settings.xyz_axes_control = BMM150_DISABLE_XY_AXIS; + rslt = set_control_measurement_xyz(&settings, dev); + + if (rslt == BMM150_OK) + { + /* Repetition value is set as 0x04 */ + settings.z_rep = BMM150_SELF_TEST_REP_Z; + rslt = set_z_rep(&settings, dev); + } + } + + return rslt; +} + +/*! + * @brief This internal API is used to set the positive or negative value of + * self-test current and obtain the corresponding magnetometer z axis data + */ +static int8_t adv_self_test_measurement(uint8_t self_test_current, int16_t *data_z, struct bmm150_dev *dev) +{ + int8_t rslt; + struct bmm150_settings settings; + struct bmm150_mag_data mag_data; + + /* Set the advanced self test current as positive or + * negative based on the value of parameter "self_test_current" + */ + rslt = set_adv_self_test_current(self_test_current, dev); + + if (rslt == BMM150_OK) + { + /* Set the device in forced mode */ + settings.pwr_mode = BMM150_POWERMODE_FORCED; + rslt = bmm150_set_op_mode(&settings, dev); + + /* Delay to ensure measurement is complete */ + dev->delay_us(BMM150_DELAY_ADV_SELF_TEST, dev->intf_ptr); + + if (rslt == BMM150_OK) + { + /* Read Mag data and store the value of Z axis data */ + rslt = bmm150_read_mag_data(&mag_data, dev); + + if (rslt == BMM150_OK) + { + /* Mag Z axis data is stored */ + *data_z = mag_data.z; + } + } + } + + return rslt; +} + +/*! + * @brief This internal API is used to get the difference between the + * Z axis mag data obtained by positive and negative self-test current + * and validate whether the advanced self test is done successfully or not. + */ +static int8_t validate_adv_self_test(int16_t positive_data_z, int16_t negative_data_z) +{ + int32_t adv_self_test_rslt; + int8_t rslt; + + /* Advanced self test difference between the Z axis mag data + * obtained by the positive and negative self-test current + */ + adv_self_test_rslt = positive_data_z - negative_data_z; + + /* Advanced self test validation */ + /*Value of adv_self_test_rslt should be in between 180-240 micro-tesla */ + if ((adv_self_test_rslt > 180) && (adv_self_test_rslt < 240)) + { + /* Advanced self test success */ + rslt = BMM150_OK; + } + else + { + /* Advanced self test fail */ + rslt = BMM150_W_ADV_SELF_TEST_FAIL; + } + + return rslt; +} + +/*! + * @brief This internal API is used to set the self test current value in + * the Adv. ST bits (bit6 and bit7) of 0x4C register + */ +static int8_t set_adv_self_test_current(uint8_t self_test_current, struct bmm150_dev *dev) +{ + int8_t rslt; + uint8_t reg_data; + + /* Read the 0x4C register */ + rslt = bmm150_get_regs(BMM150_REG_OP_MODE, ®_data, 1, dev); + + if (rslt == BMM150_OK) + { + /* Set the self test current value in the Adv. ST bits + * (bit6 and bit7) of 0x4c register + */ + reg_data = BMM150_SET_BITS(reg_data, BMM150_ADV_SELF_TEST, self_test_current); + rslt = bmm150_set_regs(BMM150_REG_OP_MODE, ®_data, 1, dev); + } + + return rslt; +} diff --git a/projects/Compass/main/src/bmm150_aux_port.c b/projects/Compass/main/src/bmm150_aux_port.c new file mode 100644 index 00000000..dc40bea0 --- /dev/null +++ b/projects/Compass/main/src/bmm150_aux_port.c @@ -0,0 +1,70 @@ +/** + * @file bmm150_aux_port.c + * @brief BMM150 通过 BMI270 AUX 接口访问的适配层实现 + */ + +#include "bmm150_aux_port.h" +#include + +/* 全局引用 BMI270 实例,供 AUX 读写回调使用 */ +static struct bmi2_dev *g_bmi270 = NULL; + +/*! AUX 读回调:将 BMM150 的寄存器读请求转发给 BMI270 AUX 手动模式 */ +static BMM150_INTF_RET_TYPE bmm150_aux_read(uint8_t reg_addr, uint8_t *reg_data, + uint32_t len, void *intf_ptr) +{ + (void)intf_ptr; + + if (!g_bmi270) { + return BMM150_E_NULL_PTR; + } + + int8_t rslt = bmi2_read_aux_man_mode(reg_addr, reg_data, (uint16_t)len, g_bmi270); + return (rslt == BMI2_OK) ? BMM150_INTF_RET_SUCCESS : BMM150_E_COM_FAIL; +} + +/*! AUX 写回调:将 BMM150 的寄存器写请求转发给 BMI270 AUX 手动模式 */ +static BMM150_INTF_RET_TYPE bmm150_aux_write(uint8_t reg_addr, const uint8_t *reg_data, + uint32_t len, void *intf_ptr) +{ + (void)intf_ptr; + + if (!g_bmi270) { + return BMM150_E_NULL_PTR; + } + + int8_t rslt = bmi2_write_aux_man_mode(reg_addr, reg_data, (uint16_t)len, g_bmi270); + return (rslt == BMI2_OK) ? BMM150_INTF_RET_SUCCESS : BMM150_E_COM_FAIL; +} + +/*! 延时回调 */ +static void bmm150_delay_us(uint32_t period, void *intf_ptr) +{ + (void)intf_ptr; + usleep(period); +} + +/***************************************************************************/ + +int8_t bmm150_aux_port_init(struct bmm150_dev *dev, struct bmi2_dev *bmi) +{ + if (!dev || !bmi) { + return BMM150_E_NULL_PTR; + } + + g_bmi270 = bmi; + + dev->read = bmm150_aux_read; + dev->write = bmm150_aux_write; + dev->delay_us = bmm150_delay_us; + dev->intf = BMM150_I2C_INTF; + dev->intf_ptr = (void *)0x01; /* dummy non-NULL, not used by callbacks */ + + return bmm150_init(dev); +} + +void bmm150_aux_port_deinit(struct bmm150_dev *dev) +{ + (void)dev; + g_bmi270 = NULL; +} diff --git a/projects/Compass/main/src/compass_app.cpp b/projects/Compass/main/src/compass_app.cpp new file mode 100644 index 00000000..a3676c30 --- /dev/null +++ b/projects/Compass/main/src/compass_app.cpp @@ -0,0 +1,383 @@ +#include "compass_app.h" +#include "bmi270_port.h" +#include "bmm150_aux_port.h" +#include "bmi2.h" +#include "bmi270.h" +#include "bmm150.h" + +#include +#include +#include +#include + +/* ---------- 内部传感器上下文 ---------- */ +struct SensorCtx { + struct bmi2_dev bmi; + struct bmm150_dev bmm; + uint8_t bmm150_addr = 0; + bool magCalValid = false; + float magBiasX = 0.0f, magBiasY = 0.0f, magBiasZ = 0.0f; +}; + +/* ---------- 单位转换 ---------- */ +static float lsb_to_mps2(int16_t val, float g_range, uint8_t bit_width) +{ + float half_scale = (float)(1 << bit_width) / 2.0f; + return (val * g_range * 9.80665f) / half_scale; +} + +static float lsb_to_dps(int16_t val, float dps_range, uint8_t bit_width) +{ + float half_scale = (float)(1 << bit_width) / 2.0f; + return (val * dps_range) / half_scale; +} + +/* ---------- BMI270 传感器配置 ---------- */ +static int8_t set_accel_config(struct bmi2_dev *dev) +{ + struct bmi2_sens_config config = {0}; + int8_t rslt = bmi2_get_sensor_config(&config, 1, dev); + if (rslt != BMI2_OK) return rslt; + + config.cfg.acc.odr = BMI2_ACC_ODR_100HZ; + config.cfg.acc.range = BMI2_ACC_RANGE_4G; + config.cfg.acc.bwp = BMI2_ACC_NORMAL_AVG4; + config.cfg.acc.filter_perf = BMI2_PERF_OPT_MODE; + return bmi2_set_sensor_config(&config, 1, dev); +} + +static int8_t set_gyro_config(struct bmi2_dev *dev) +{ + struct bmi2_sens_config config = {0}; + int8_t rslt = bmi2_get_sensor_config(&config, 1, dev); + if (rslt != BMI2_OK) return rslt; + + config.cfg.gyr.odr = BMI2_GYR_ODR_100HZ; + config.cfg.gyr.range = BMI2_GYR_RANGE_2000; + config.cfg.gyr.bwp = BMI2_GYR_NORMAL_MODE; + config.cfg.gyr.filter_perf = BMI2_PERF_OPT_MODE; + config.cfg.gyr.noise_perf = BMI2_PERF_OPT_MODE; + return bmi2_set_sensor_config(&config, 1, dev); +} + +/* ---------- AUX 地址穷举扫描 ---------- */ +static int8_t aux_scan_for_bmm150(struct bmi2_dev *bmi, uint8_t *found_addr) +{ + int8_t rslt; + uint8_t test_addrs[] = { 0x10, 0x11, 0x12, 0x13 }; + + for (int i = 0; i < 4; i++) { + uint8_t addr = test_addrs[i]; + struct bmi2_sens_config cfg = {0}; + + cfg.type = BMI2_AUX; + rslt = bmi2_get_sensor_config(&cfg, 1, bmi); + if (rslt != BMI2_OK) continue; + + cfg.cfg.aux.aux_en = 1; + cfg.cfg.aux.manual_en = 1; + cfg.cfg.aux.i2c_device_addr = addr; + cfg.cfg.aux.man_rd_burst = BMI2_AUX_READ_LEN_3; + + rslt = bmi2_set_sensor_config(&cfg, 1, bmi); + if (rslt != BMI2_OK) continue; + + uint8_t pwrcntrl = 0x01; + int8_t w_rslt = bmi2_write_aux_man_mode(BMM150_REG_POWER_CONTROL, &pwrcntrl, 1, bmi); + if (w_rslt == BMI2_OK) { + usleep(4000); + } + + uint8_t chip_id = 0; + rslt = bmi2_read_aux_man_mode(0x40, &chip_id, 1, bmi); + if (rslt == BMI2_OK && chip_id == 0x32) { + *found_addr = addr; + return BMI2_OK; + } + } + return BMI2_E_DEV_NOT_FOUND; +} + +/* ---------- 磁力计 8 字校准 ---------- */ +static int mag_calibrate_8figure(struct bmm150_dev *bmm, struct bmi2_dev *bmi, + float *bias_x, float *bias_y, float *bias_z, + std::atomic* running) +{ + int16_t min_x = INT16_MAX, max_x = INT16_MIN; + int16_t min_y = INT16_MAX, max_y = INT16_MIN; + int16_t min_z = INT16_MAX, max_z = INT16_MIN; + uint32_t samples = 0; + uint32_t elapsed = 0; + struct bmm150_mag_data mag; + int8_t rslt; + + while (elapsed < 5000 && running->load()) { + rslt = bmm150_read_mag_data(&mag, bmm); + if (rslt == BMM150_OK) { + if (mag.x < min_x) min_x = mag.x; + if (mag.x > max_x) max_x = mag.x; + if (mag.y < min_y) min_y = mag.y; + if (mag.y > max_y) max_y = mag.y; + if (mag.z < min_z) min_z = mag.z; + if (mag.z > max_z) max_z = mag.z; + samples++; + } + usleep(20000); + elapsed += 20; + } + + if (samples < 10) return -1; + + *bias_x = ((float)min_x + (float)max_x) / 2.0f / 16.0f; + *bias_y = ((float)min_y + (float)max_y) / 2.0f / 16.0f; + *bias_z = ((float)min_z + (float)max_z) / 2.0f / 16.0f; + return 0; +} + +/* ---------- CompassApp ---------- */ + +CompassApp::CompassApp() = default; +CompassApp::~CompassApp() { deinit(); } + +bool CompassApp::init() +{ + if (running_.load()) return true; + + if (!initSensors()) { + state_.statusText = "Sensor init failed"; + return false; + } + + running_ = true; + sensorThread_ = std::thread(&CompassApp::sensorThreadFunc, this); + return true; +} + +void CompassApp::deinit() +{ + running_ = false; + if (sensorThread_.joinable()) { + sensorThread_.join(); + } + deinitSensors(); +} + +void CompassApp::setView(ICompassView* view) +{ + view_ = view; +} + +void CompassApp::onAction(const std::string& action) +{ + if (action == "calibrate") { + requestCalibrate_ = true; + } +} + +void CompassApp::poll() +{ + if (stateDirty_.exchange(false)) { + notifyView(); + } +} + +CompassState CompassApp::getState() const +{ + std::lock_guard lock(stateMutex_); + return state_; +} + +void CompassApp::notifyView() +{ + if (!view_) return; + CompassState s = getState(); + view_->update(s); +} + +bool CompassApp::initSensors() +{ + SensorCtx* ctx = new SensorCtx(); + std::memset(ctx, 0, sizeof(SensorCtx)); + + int8_t rslt = bmi270_port_init(&ctx->bmi); + if (rslt != BMI2_OK) { + delete ctx; + return false; + } + + rslt = bmi270_init(&ctx->bmi); + if (rslt != BMI2_OK) { + bmi270_port_deinit(&ctx->bmi); + delete ctx; + return false; + } + + rslt = set_accel_config(&ctx->bmi); + if (rslt != BMI2_OK) { + bmi270_port_deinit(&ctx->bmi); + delete ctx; + return false; + } + + rslt = set_gyro_config(&ctx->bmi); + if (rslt != BMI2_OK) { + bmi270_port_deinit(&ctx->bmi); + delete ctx; + return false; + } + + uint8_t bmi_sens_list[] = { BMI2_ACCEL, BMI2_GYRO }; + bmi2_sensor_enable(bmi_sens_list, sizeof(bmi_sens_list), &ctx->bmi); + + rslt = aux_scan_for_bmm150(&ctx->bmi, &ctx->bmm150_addr); + if (rslt != BMI2_OK) { + bmi270_port_deinit(&ctx->bmi); + delete ctx; + return false; + } + + rslt = bmm150_aux_port_init(&ctx->bmm, &ctx->bmi); + if (rslt != BMM150_OK || ctx->bmm.chip_id != BMM150_CHIP_ID) { + bmi270_port_deinit(&ctx->bmi); + delete ctx; + return false; + } + + struct bmm150_settings bmm_settings = {0}; + bmm_settings.pwr_mode = BMM150_POWERMODE_NORMAL; + bmm150_set_op_mode(&bmm_settings, &ctx->bmm); + bmm_settings.preset_mode = BMM150_PRESETMODE_REGULAR; + bmm150_set_presetmode(&bmm_settings, &ctx->bmm); + + bmiHandle_ = ctx; + bmmHandle_ = &ctx->bmm; + + { + std::lock_guard lock(stateMutex_); + state_.statusText = "Sensor OK"; + state_.sensorReady = true; + } + stateDirty_ = true; + return true; +} + +void CompassApp::deinitSensors() +{ + if (!bmiHandle_) return; + SensorCtx* ctx = static_cast(bmiHandle_); + + struct bmm150_settings bmm_settings = {0}; + bmm_settings.pwr_mode = BMM150_POWERMODE_SUSPEND; + bmm150_set_op_mode(&bmm_settings, &ctx->bmm); + bmm150_aux_port_deinit(&ctx->bmm); + + bmi270_port_deinit(&ctx->bmi); + delete ctx; + bmiHandle_ = nullptr; + bmmHandle_ = nullptr; +} + +void CompassApp::sensorThreadFunc() +{ + SensorCtx* ctx = static_cast(bmiHandle_); + if (!ctx) return; + + struct bmi2_sens_data sens_data = {0}; + struct bmm150_mag_data mag_data = {0}; + + while (running_.load()) { + if (requestCalibrate_.exchange(false)) { + { + std::lock_guard lock(stateMutex_); + state_.calibrating = true; + state_.statusText = "Calibrating..."; + } + stateDirty_ = true; + + float bx, by, bz; + int rc = mag_calibrate_8figure(&ctx->bmm, &ctx->bmi, &bx, &by, &bz, &running_); + + { + std::lock_guard lock(stateMutex_); + state_.calibrating = false; + if (rc == 0) { + ctx->magCalValid = true; + ctx->magBiasX = bx; + ctx->magBiasY = by; + ctx->magBiasZ = bz; + state_.statusText = "Calib OK"; + } else { + state_.statusText = "Calib failed"; + } + } + stateDirty_ = true; + continue; + } + + int8_t rslt = bmi2_get_sensor_data(&sens_data, &ctx->bmi); + if (rslt != BMI2_OK) { + usleep(20000); + continue; + } + + float acc_x = 0, acc_y = 0, acc_z = 0; + float gyr_x = 0, gyr_y = 0, gyr_z = 0; + float temp_c = 0; + + if (sens_data.status & BMI2_DRDY_ACC) { + acc_x = lsb_to_mps2(sens_data.acc.x, 4.0f, ctx->bmi.resolution); + acc_y = lsb_to_mps2(sens_data.acc.y, 4.0f, ctx->bmi.resolution); + acc_z = lsb_to_mps2(sens_data.acc.z, 4.0f, ctx->bmi.resolution); + } + + if (sens_data.status & BMI2_DRDY_GYR) { + gyr_x = lsb_to_dps(sens_data.gyr.x, 2000.0f, ctx->bmi.resolution); + gyr_y = lsb_to_dps(sens_data.gyr.y, 2000.0f, ctx->bmi.resolution); + gyr_z = lsb_to_dps(sens_data.gyr.z, 2000.0f, ctx->bmi.resolution); + } + + int16_t temp_raw = 0; + bmi2_get_temperature_data(&temp_raw, &ctx->bmi); + temp_c = ((float)temp_raw / 512.0f) + 23.0f; + + rslt = bmm150_read_mag_data(&mag_data, &ctx->bmm); + float mag_x_ut = 0, mag_y_ut = 0, mag_z_ut = 0; + if (rslt == BMM150_OK) { + mag_x_ut = mag_data.x / 16.0f; + mag_y_ut = mag_data.y / 16.0f; + mag_z_ut = mag_data.z / 16.0f; + + if (ctx->magCalValid) { + mag_x_ut -= ctx->magBiasX; + mag_y_ut -= ctx->magBiasY; + mag_z_ut -= ctx->magBiasZ; + } + } + + float pitch = atan2f(-acc_x, sqrtf(acc_y * acc_y + acc_z * acc_z)); + float roll = atan2f(acc_y, acc_z); + + float sin_p = sinf(pitch), cos_p = cosf(pitch); + float sin_r = sinf(roll), cos_r = cosf(roll); + + float mag_x_h = mag_x_ut * cos_p + mag_z_ut * sin_p; + float mag_y_h = mag_x_ut * sin_r * sin_p + mag_y_ut * cos_r - mag_z_ut * sin_r * cos_p; + + float yaw = atan2f(-mag_y_h, mag_x_h) * 180.0f / (float)M_PI; + if (yaw < 0.0f) yaw += 360.0f; + + { + std::lock_guard lock(stateMutex_); + state_.yaw = yaw; + state_.pitch = pitch * 180.0f / (float)M_PI; + state_.roll = roll * 180.0f / (float)M_PI; + state_.accX = acc_x; state_.accY = acc_y; state_.accZ = acc_z; + state_.gyrX = gyr_x; state_.gyrY = gyr_y; state_.gyrZ = gyr_z; + state_.magX = mag_x_ut; state_.magY = mag_y_ut; state_.magZ = mag_z_ut; + state_.temp = temp_c; + } + stateDirty_ = true; + + usleep(50000); /* 50 ms => 20 Hz */ + } +} diff --git a/projects/Compass/main/src/main.cpp b/projects/Compass/main/src/main.cpp new file mode 100644 index 00000000..ca064323 --- /dev/null +++ b/projects/Compass/main/src/main.cpp @@ -0,0 +1,254 @@ +#include "keyboard_input.h" +#include "compat/input_keys.h" +#include "global_config.h" +#include "lvgl/lvgl.h" +#include "compass_app.h" +#include "ui_compass.h" + +#include +#include +#include +#include +#include +#include + +#if LV_USE_SDL +#include "lvgl/src/drivers/sdl/lv_sdl_mouse.h" +#include "lvgl/src/drivers/sdl/lv_sdl_window.h" +#include "lvgl/src/drivers/sdl/lv_sdl_keyboard.h" +#endif + +#if LV_USE_EVDEV +#include +#endif + +namespace { + +constexpr int kScreenWidth = 320; +constexpr int kScreenHeight = 170; + +volatile sig_atomic_t g_quit_requested = 0; +lv_obj_t *g_root = nullptr; +lv_indev_t *g_keyboard_indev = nullptr; +lv_group_t *g_group = nullptr; +UiCompass *g_ui = nullptr; +CompassApp *g_app = nullptr; + +void request_quit() +{ + g_quit_requested = 1; +} + +void handle_signal(int) +{ + request_quit(); +} + +int get_st7789v_fbdev(char *dev_path, size_t buf_size) +{ + if (!dev_path || buf_size == 0) return -1; + FILE *fp = std::fopen("/proc/fb", "r"); + if (!fp) return -1; + char line[256]; + int fb_num = -1; + while (std::fgets(line, sizeof(line), fp)) { + if (std::strstr(line, "fb_st7789v") && std::sscanf(line, "%d", &fb_num) == 1) break; + } + std::fclose(fp); + if (fb_num < 0) return -1; + std::snprintf(dev_path, buf_size, "/dev/fb%d", fb_num); + return 0; +} + +void handle_keyboard_event(lv_event_t *event) +{ + uint32_t key_code = 0; + bool pressed = false; + + if (lv_event_get_code(event) == static_cast(LV_EVENT_KEYBOARD)) { + auto *key = static_cast(lv_event_get_param(event)); + if (!key || key->key_state == KBD_KEY_RELEASED) return; + key_code = key->key_code; + bool isRepeat = (key->key_state == KBD_KEY_REPEATED); + if (g_ui && key->codepoint >= 32 && key->codepoint < 127 && !isRepeat) { + g_ui->onCharTyped(key->codepoint); + } + if (g_ui) g_ui->onKeyPressed(key_code, isRepeat); + return; + } +#if LV_USE_SDL + else if (lv_event_get_code(event) == LV_EVENT_KEY) { + lv_indev_t *indev = lv_indev_active(); + if (lv_indev_get_state(indev) != LV_INDEV_STATE_PRESSED) return; + uint32_t lv_key = lv_indev_get_key(indev); + switch (lv_key) { + case LV_KEY_ENTER: key_code = KEY_ENTER; break; + case LV_KEY_ESC: key_code = KEY_ESC; break; + case LV_KEY_LEFT: key_code = KEY_LEFT; break; + case LV_KEY_RIGHT: key_code = KEY_RIGHT; break; + case 'p': case 'P': key_code = KEY_P; break; + case 's': case 'S': key_code = KEY_S; break; + case ' ': key_code = KEY_SPACE; break; + case '1': key_code = KEY_F4; break; + case '2': key_code = KEY_F5; break; + case '3': key_code = KEY_F6; break; + case '4': key_code = KEY_F7; break; + case '5': key_code = KEY_F8; break; + default: key_code = lv_key; break; + } + if (g_ui && lv_key >= 32 && lv_key < 127) { + g_ui->onCharTyped(lv_key); + } + pressed = true; + } +#endif + + if (!pressed) return; + if (key_code == KEY_ESC) { + request_quit(); + return; + } + if (g_ui) g_ui->onKeyPressed(key_code); +} + +#if LV_USE_EVDEV +int evdev_to_lv_key(uint16_t code) +{ + switch (code) { + case KEY_UP: return LV_KEY_UP; + case KEY_DOWN: return LV_KEY_DOWN; + case KEY_RIGHT: return LV_KEY_RIGHT; + case KEY_LEFT: return LV_KEY_LEFT; + case KEY_ESC: return LV_KEY_ESC; + case KEY_DELETE: return LV_KEY_DEL; + case KEY_BACKSPACE: return LV_KEY_BACKSPACE; + case KEY_ENTER: return LV_KEY_ENTER; + case KEY_TAB: return KEY_TAB; + case KEY_HOME: return LV_KEY_HOME; + case KEY_END: return LV_KEY_END; + default: return code; + } +} + +void keypad_read_cb(lv_indev_t *, lv_indev_data_t *data) +{ + data->state = LV_INDEV_STATE_RELEASED; + data->continue_reading = false; + pthread_mutex_lock(&keyboard_mutex); + if (!STAILQ_EMPTY(&keyboard_queue)) { + key_item *elm = STAILQ_FIRST(&keyboard_queue); + STAILQ_REMOVE_HEAD(&keyboard_queue, entries); + if (g_root) { + lv_obj_send_event(g_root, static_cast(LV_EVENT_KEYBOARD), elm); + } + data->key = evdev_to_lv_key(elm->key_code); + data->state = static_cast(elm->key_state); + data->continue_reading = !STAILQ_EMPTY(&keyboard_queue); + std::free(elm); + } + pthread_mutex_unlock(&keyboard_mutex); +} + +void lv_linux_indev_init() +{ + const char *keyboard_device = getenv("LV_LINUX_KEYBOARD_DEVICE"); + if (!keyboard_device) keyboard_device = "/dev/input/by-path/platform-3f804000.i2c-event"; + pthread_t thread_id; + pthread_create(&thread_id, nullptr, keyboard_read_thread, const_cast(keyboard_device)); + pthread_detach(thread_id); + g_keyboard_indev = lv_indev_create(); + lv_indev_set_type(g_keyboard_indev, LV_INDEV_TYPE_KEYPAD); + lv_indev_set_read_cb(g_keyboard_indev, keypad_read_cb); +} +#endif + +#if LV_USE_LINUX_FBDEV +void lv_linux_disp_init() +{ + char fbdev[64] = {}; + const char *device = getenv("LV_LINUX_FBDEV_DEVICE"); + if (!device && get_st7789v_fbdev(fbdev, sizeof(fbdev)) == 0) device = fbdev; + if (!device) device = "/dev/fb0"; + lv_display_t *disp = lv_linux_fbdev_create(); + if (disp) lv_linux_fbdev_set_file(disp, device); +} + +#if !LV_USE_EVDEV && !LV_USE_LIBINPUT +void lv_linux_indev_init() {} +#endif + +#elif LV_USE_SDL +void lv_linux_disp_init() +{ + lv_display_t *disp = lv_sdl_window_create(kScreenWidth, kScreenHeight); + lv_sdl_window_set_title(disp, "Compass"); +} + +void lv_linux_indev_init() +{ + lv_sdl_mouse_create(); + g_keyboard_indev = lv_sdl_keyboard_create(); +} +#else +#error Unsupported display configuration +#endif + +} // namespace + +int main() +{ + std::signal(SIGINT, handle_signal); + std::signal(SIGTERM, handle_signal); + std::signal(SIGPIPE, SIG_IGN); + + CompassApp app; + UiCompass ui; + g_app = &app; + + ui.setActionHandler([&](const std::string& action) { + if (action == "quit") { + request_quit(); + } else { + app.onAction(action); + } + }); + + if (!app.init()) { + /* Even if sensors fail, show UI with error */ + } + + lv_init(); + lv_linux_disp_init(); + LV_EVENT_KEYBOARD = lv_event_register_id(); + lv_linux_indev_init(); + + g_root = lv_screen_active(); + lv_obj_set_size(g_root, kScreenWidth, kScreenHeight); + lv_obj_clear_flag(g_root, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_bg_color(g_root, lv_color_hex(0xE8E8EC), 0); + lv_obj_set_style_bg_opa(g_root, LV_OPA_COVER, 0); + + g_ui = &ui; + ui.init(g_root); + app.setView(&ui); + + lv_obj_add_event_cb(g_root, handle_keyboard_event, + static_cast(LV_EVENT_KEYBOARD), nullptr); +#if LV_USE_SDL + lv_obj_add_event_cb(g_root, handle_keyboard_event, LV_EVENT_KEY, nullptr); +#endif + + g_group = lv_group_create(); + lv_group_add_obj(g_group, g_root); + lv_group_focus_obj(g_root); + if (g_keyboard_indev) lv_indev_set_group(g_keyboard_indev, g_group); + + while (!g_quit_requested) { + app.poll(); + lv_timer_handler(); + usleep(5000); + } + + app.deinit(); + return 0; +} diff --git a/projects/Compass/main/src/ui_compass.cpp b/projects/Compass/main/src/ui_compass.cpp new file mode 100644 index 00000000..7121e0a7 --- /dev/null +++ b/projects/Compass/main/src/ui_compass.cpp @@ -0,0 +1,165 @@ +#include "ui_compass.h" +#include "compat/input_keys.h" + +#include +#include + +namespace { + +constexpr int kScreenWidth = 320; +constexpr int kScreenHeight = 170; + +} // namespace + +void UiCompass::init(lv_obj_t* parent) +{ + parent_ = parent; + buildUi(parent); +} + +void UiCompass::setActionHandler(std::function handler) +{ + actionHandler_ = handler; +} + +void UiCompass::buildUi(lv_obj_t* parent) +{ + /* 顶部状态栏 */ + createStatusBar(parent); + + /* 主内容区 */ + lv_obj_t* content = lv_obj_create(parent); + lv_obj_remove_style_all(content); + lv_obj_set_size(content, kScreenWidth, kScreenHeight - 20); + lv_obj_set_pos(content, 0, 20); + lv_obj_set_flex_flow(content, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(content, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); + lv_obj_set_style_pad_top(content, 4, 0); + lv_obj_set_style_pad_left(content, 4, 0); + lv_obj_set_style_pad_right(content, 4, 0); + lv_obj_set_style_pad_bottom(content, 4, 0); + lv_obj_set_style_pad_row(content, 2, 0); + + /* 航向角大字 */ + lv_obj_t* yawRow = lv_obj_create(content); + lv_obj_remove_style_all(yawRow); + lv_obj_set_size(yawRow, kScreenWidth - 8, 30); + lv_obj_set_flex_flow(yawRow, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(yawRow, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + + lblHeading_ = lv_label_create(yawRow); + lv_label_set_text(lblHeading_, "YAW:"); + lv_obj_set_style_text_font(lblHeading_, &lv_font_montserrat_16, 0); + lv_obj_set_style_text_color(lblHeading_, lv_color_hex(0x333333), 0); + + lblYaw_ = lv_label_create(yawRow); + lv_label_set_text(lblYaw_, "---"); + lv_obj_set_style_text_font(lblYaw_, &lv_font_montserrat_26, 0); + lv_obj_set_style_text_color(lblYaw_, lv_color_hex(0xE74C3C), 0); + + /* Pitch / Roll */ + lblPitchRoll_ = lv_label_create(content); + lv_label_set_text(lblPitchRoll_, "P:--- R:---"); + lv_obj_set_style_text_font(lblPitchRoll_, &lv_font_montserrat_10, 0); + lv_obj_set_style_text_color(lblPitchRoll_, lv_color_hex(0x666666), 0); + + /* 加速度 */ + lblAccel_ = lv_label_create(content); + lv_label_set_text(lblAccel_, "ACC: --- --- ---"); + lv_obj_set_style_text_font(lblAccel_, &lv_font_montserrat_10, 0); + lv_obj_set_style_text_color(lblAccel_, lv_color_hex(0x333333), 0); + + /* 陀螺仪 */ + lblGyro_ = lv_label_create(content); + lv_label_set_text(lblGyro_, "GYR: --- --- ---"); + lv_obj_set_style_text_font(lblGyro_, &lv_font_montserrat_10, 0); + lv_obj_set_style_text_color(lblGyro_, lv_color_hex(0x333333), 0); + + /* 磁力计 */ + lblMag_ = lv_label_create(content); + lv_label_set_text(lblMag_, "MAG: --- --- ---"); + lv_obj_set_style_text_font(lblMag_, &lv_font_montserrat_10, 0); + lv_obj_set_style_text_color(lblMag_, lv_color_hex(0x333333), 0); + + /* 温度 */ + lblTemp_ = lv_label_create(content); + lv_label_set_text(lblTemp_, "T: ---"); + lv_obj_set_style_text_font(lblTemp_, &lv_font_montserrat_10, 0); + lv_obj_set_style_text_color(lblTemp_, lv_color_hex(0x333333), 0); +} + +void UiCompass::createStatusBar(lv_obj_t* parent) +{ + statusBar_ = lv_obj_create(parent); + lv_obj_remove_style_all(statusBar_); + lv_obj_set_size(statusBar_, kScreenWidth, 20); + lv_obj_set_pos(statusBar_, 0, 0); + lv_obj_set_style_bg_color(statusBar_, lv_color_hex(0x555555), 0); + lv_obj_set_style_bg_opa(statusBar_, LV_OPA_COVER, 0); + + lblStatusText_ = lv_label_create(statusBar_); + lv_obj_set_pos(lblStatusText_, 4, 2); + lv_label_set_text(lblStatusText_, "Compass"); + lv_obj_set_style_text_font(lblStatusText_, &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblStatusText_, lv_color_white(), 0); +} + +void UiCompass::update(const CompassState& state) +{ + if (!parent_) return; + + char buf[128]; + + if (lblStatusText_ && state.statusText != lastState_.statusText) { + lv_label_set_text(lblStatusText_, state.statusText.c_str()); + } + + if (lblYaw_) { + std::snprintf(buf, sizeof(buf), "%.1f\xc2\xb0", state.yaw); + lv_label_set_text(lblYaw_, buf); + } + + if (lblPitchRoll_) { + std::snprintf(buf, sizeof(buf), "P:%+.1f\xc2\xb0 R:%+.1f\xc2\xb0", state.pitch, state.roll); + lv_label_set_text(lblPitchRoll_, buf); + } + + if (lblAccel_) { + std::snprintf(buf, sizeof(buf), "ACC:%+.2f %+.2f %+.2f", state.accX, state.accY, state.accZ); + lv_label_set_text(lblAccel_, buf); + } + + if (lblGyro_) { + std::snprintf(buf, sizeof(buf), "GYR:%+.1f %+.1f %+.1f", state.gyrX, state.gyrY, state.gyrZ); + lv_label_set_text(lblGyro_, buf); + } + + if (lblMag_) { + std::snprintf(buf, sizeof(buf), "MAG:%+.1f %+.1f %+.1f", state.magX, state.magY, state.magZ); + lv_label_set_text(lblMag_, buf); + } + + if (lblTemp_) { + std::snprintf(buf, sizeof(buf), "T:%.1f\xc2\xb0" "C", state.temp); + lv_label_set_text(lblTemp_, buf); + } + + lastState_ = state; +} + +void UiCompass::onKeyPressed(uint32_t key_code, bool isRepeat) +{ + (void)isRepeat; + if (!actionHandler_) return; + + if (key_code == KEY_F4) { + actionHandler_("calibrate"); + } else if (key_code == KEY_F8 || key_code == KEY_ESC) { + actionHandler_("quit"); + } +} + +void UiCompass::onCharTyped(uint32_t codepoint) +{ + (void)codepoint; +} diff --git a/projects/Compass/share/images/Compass.png b/projects/Compass/share/images/Compass.png new file mode 100644 index 0000000000000000000000000000000000000000..cbb65550e0e05bcf3afcdafa5c45b44d51a41407 GIT binary patch literal 4215 zcmV--5Qy)IP)^ulgAfyx`N)bYcFHzO@1FcF_ zS|Pp^ifBq9lu*=`R#b68fl^v2QlwH9RUq+!gbzq@B|h*0P3J$mv-{t@nepy<<8aQc z^wY)OwJ)CE?SE$eGdpwDRjs3Sw2s!%I$B5TXdSJi8IBbz0@qx-YO<}}cdWzLH^=e! zgo&I8>F!;KC?n7(BW4inmk~GU8IX}M=p8ii2San5j;`RbYuh>}SFE@?@P+lTvaMsb zy{qSxFE~t0R1%s;hLxlRe_-&`s&?P(7fM8D_rM2M5m$zczK}_>v^zX`8WSFE#pf`N>nw4#}CBG{~BH$ApieyPFSrQa(bIQ;d(J{XVzdsx= zkp7{RAp^6jV~OsTwvO)OLJ{GtUWnyoFT)PpIsHRvLk36!y8OMzms|$K)@Y)1#meB< zz<}yqveaDw9OohT3RESru3{EmTLhG6$5|U9#21k>Uw%}<+Z*NVt zWXNUk@9}zt)6*Y0dr3kS)ENuOfL2zR)+k@K?6J?F0m~gV9krES0 zRZY_MMAIWCnR;TGQN#!%t|ODdfhID@faX8oN$A3zK4?v;DsT`10tRCRMkuzNWKgnJ z;*^9$2CwJ`X*A(NdI#bkczSoJnzkS#q>H#RWYlyq z$*{{XrNkBWiguT1boUIMYB+0axjQ0RX`NvYQ;EI%Nae+6$m;*TBLvPR_wEZnk}!I>|SGtcfIpP!u-0_X0S`La;P9XD+xXAU1BpFi@5kT>bM zJ8$|%y^Nd=x>K+W?Jg-9LV$*{#E?SAxhYvX1io*Pv1czC|IgnX0phTX(aC$r?XP|2 zh}zV>78$eCH0GCbl)p)_Z`ED zLfPE`ttlL)%Fq3RjQ#73Bj8#broQ?38|1*BK6M0Ki-nwidOP{_@3%Q(j$Vs}%pJOu zeEQHsj+i@iSS%wqMl1NyIvJc$B*$bx`0Dca9CP3I@b%5{6gpH$G9l2k#dSvQ-0QBu zb;kZb%)0{D8S`)Ma|ISM|KNkJ5XxXeYC;(R^h-bn5bO)jH6(6>`(F9P6}STb4?N%sp$v-9C4Cf zi+kWFXZDi!?!Ma*CujD_W#q@DGCUR{Podk>A+gCFr2N`RN5r@9u%tsOn-7wyXWn&0 zb;}`(km+sf$h)VuJ7Q*Xoh21Avu-_k_s9`P?73lsTm~SK;h~U-ay&znn~F_Nk?c<& zC8bw?BP93uqa;qR5#^@Jn`g<_jy+E%pE@T5&Q-P?6y~OOZ@r$J_|YbE?!|3F;M}xL z2G31Rulp)Fv1^8$JA7Ekuj#p&8`i63)Cn0C3V}3KgF>{qQL4eEWtS+Fb^26cN+@Jh zCcn7Lr?>5Y-s?a!H|QZ=WS2 zFFh+AGJtb%jZ%h02*@yn%=Kkowsn z_Au3qDfG(oPdOeks6PL+R7R?>S}Nlb2oX=!vDX#YW|Ap@BVEW|FuVbND#S?&9S;Ze zZNAw+He?``0c7PuxQvE{@W;kfb8YQHnlXj$cE~^~qo@#)!$R`zh2ZD-R1=@=LYgs! zUU>2`$3q6iCy&cz$b{suki3^d>Sv+b(;>~6LQn6Sc06Q|*}X?9BVC-3%Fu-5y%ZvT zBw)Fu(9=7oN&aU)mJS&d=(!C2d6kShAvsLVOCj>-GnRFtP}Q0e$gl{p$?#A}O!=@( zOU6XK5P=MjglG@stDanH$*>8L$iPBiT2%u=Vh)5pPBz4AxL_G8yU8gj5E=Wemp(jS1mv zg{U5|vs}@!Q(7aHp$RE?Eu{X@V|xXDxn`leopp~ZaJRGWWil*6Y%7aOs6frY?2TMY;?m&H@9 z9b)f&H)9HY(+^&DJY=xxhc8QIm_lk&As}N>Lf{Ep)j}?IAr0Jscz0JI??e1S;SsyfRhg+8|RFxmLMlhPprI5$qksbpl!Yvn@nHA6;2Lhy>6 zswIw=bD~hyni9#73dwUJMUR9i)|+a{Fl7jZJZO_$X8-p zE-CaQx2-2{o!BWIGI;C6E_%%dr3{M@n+y+yB$UhI*=00i3jMDA>m3gn+(pI7WEesO zGCUHJFoj6gAGC+4W=x^~^_NqQhYVhSW`|S;6C#n}fe>>obk$;zb|L@1``hH`jW>}U zwFwdp#YuDslVHR+!wJ&fJ4`xsIx_`IQ6q~fF}i>pP-b-YM>PgwB$%mD#!Rx9aT3g# z^iaA&3DPx`WF(3iovcQQle3U419{S$D3F0viG@9jkjh%A3@ijznQK@GEPAP0nG?$x zh~zX0A*%p`VNHTYlElJEDk5y4i6~jg0cAr%SKb6fFb3l!KntFO3*R8W+Cmq7e~u{v z6@E!8d<&HV!xzmML}<^P%g9x(lgi*iKn5&m){qdsD}ZV#RW1W2JG2Lf%)7x-_z70{ zbwpC6qc38>1$0@ki>MfYh|Z8kz<~rtVS>>WP8l!>w7{(tNTOn=$neFo8u1+QQ&O}w z1~QlsMNgf}Xh;a&ra`r?EpOTehiKvU3^Nf?TG(~a!ncy9Legf~p>*LpA!jXoMxkop zgI6k|Ybe2RB7xIWTtj^Hy*Y*e3=+Vo7D-aauZ!>}@{B=R4hsp%lrB-FcHauL0_h#6aq3bG?aNJ1b%^R#b+*rhn@O6ff7wKq(Z0+ z2s?5iAYcRu8PUGxLa6j$K_vvsSj)g+r(QtGgh?n{U?NgQ(v6S_;WA*HWKlw3ucdk+ zRjH89{s45zEvQ&oVCX0@cF~D`4Xeun8!kuS=uEM?)AF0%0dWo6mLu#@D(8 z2CaaDvf<)`6_)%a-4!Z`L^w zWuNo_w5BWqvQ}szaB#>0Od%r{A$}?V-~|l-_9gSC5E^#;$&ztCSkl6hA*8%k=Q>7r zx}wMb0;89(205ZD@y3xVZLWqpyj zE~F=#HA0U8YU>+!fB@dw!Jl{l7dRmMd4D|5kkvuF@UZ|<*b#m5?S&u6R;}c!Bvhzr zLa1<&3?P;)8$!B+{qC2Eg?;+)Ze%bONVv0%E+mp3u?j%#e89)=03-mnbSwg>T)-&s zSk6Q*d?w%`DsYM+Krej$wNQYg!pCTZUq^m~q-m!xU%g%^BBOT{xeQpHw7t{sej#O8 z4jXR`2cuoG{WIcv8hwzynIVA|I7btJ3OvFkWG&bQ7zK_b7~#eypc@O&3*Gp-uJBC) z413{Mq=nB30tMQjDNisNSxD83WaMkcPH!M1O7D~vS2wsJC2X3=g}@d*vdv6j%LXnb zLciBbh-IPJ}*U~Dgum2EY= zgC&>JlHp7SaBbTvZ}-QA9mcG?Sf-Vgd`re92vzn2_IAZPSOSn%fmnIP%b@RLuQPx^ zZ|KtQhYNeV+IO*pRsVUtawW>pnzrSomthTGY0IXGfF`D-hp%NWBd7Jiai?!d!j!6_ zhwVhM5WMjfEY<~e4E$$d?+l<>W$@n(A1=t+;zXfc2DGL;ufW~jEwFI)@8lqH6J z`e17~*!)T(GS}=fU{Hwz;~9`bZ#Zk)I$B5TXdSJib+nGw(K?p+_&;X)hv|XWeINh; N002ovPDHLkV1jxQ*xmpD literal 0 HcmV?d00001 diff --git a/projects/Recorder/tools/llm_pack.py b/projects/Recorder/tools/llm_pack.py index 8d645ae0..50011710 100755 --- a/projects/Recorder/tools/llm_pack.py +++ b/projects/Recorder/tools/llm_pack.py @@ -166,7 +166,7 @@ def create_recorder_deb(version='0.1.0', src_folder='../dist', revision='m5stack os.system('rm -rf ./*.deb m5stack_*') sys.exit(0) - version = '0.2.0' + version = '0.2.1' src_folder = '../dist' revision = 'm5stack1' From dc3e0e027775599a165e8a38a2e598a4bc4c6098 Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Tue, 2 Jun 2026 15:29:43 +0800 Subject: [PATCH 09/11] Update Compass UI and add disc image --- projects/Compass/main/include/ui_compass.h | 36 ++- projects/Compass/main/src/img_compass_disc.c | 161 ++++++++++++ projects/Compass/main/src/ui_compass.cpp | 257 +++++++++++-------- 3 files changed, 340 insertions(+), 114 deletions(-) create mode 100644 projects/Compass/main/src/img_compass_disc.c diff --git a/projects/Compass/main/include/ui_compass.h b/projects/Compass/main/include/ui_compass.h index b566229f..536f1cce 100644 --- a/projects/Compass/main/include/ui_compass.h +++ b/projects/Compass/main/include/ui_compass.h @@ -2,6 +2,7 @@ #include "compass_app.h" #include "lvgl/lvgl.h" +#include #include class UiCompass : public ICompassView { @@ -18,24 +19,33 @@ class UiCompass : public ICompassView { private: void buildUi(lv_obj_t* parent); - void createStatusBar(lv_obj_t* parent); - lv_obj_t* parent_ = nullptr; + // Layout constants + static constexpr int kScreenW = 320; + static constexpr int kScreenH = 170; + static constexpr int kDiscDia = 100; + static constexpr int kCompassImgSize = 120; + static constexpr int kBottomH = 20; + static constexpr int kBtnW = kScreenW / 5; // 64 - // Status bar - lv_obj_t* statusBar_ = nullptr; - lv_obj_t* lblStatusText_ = nullptr; + lv_obj_t* parent_ = nullptr; - // Compass heading (large) - lv_obj_t* lblHeading_ = nullptr; + // Left panel: Compass + lv_obj_t* lblCompassTitle_ = nullptr; + lv_obj_t* compassDisc_ = nullptr; lv_obj_t* lblYaw_ = nullptr; - // Sensor values - lv_obj_t* lblAccel_ = nullptr; - lv_obj_t* lblGyro_ = nullptr; - lv_obj_t* lblMag_ = nullptr; - lv_obj_t* lblTemp_ = nullptr; - lv_obj_t* lblPitchRoll_ = nullptr; + // Right panel: IMU + lv_obj_t* lblImuTitle_ = nullptr; + lv_obj_t* levelDisc_ = nullptr; + lv_obj_t* centerDot_ = nullptr; + lv_obj_t* levelDot_ = nullptr; + lv_obj_t* lblAcc_ = nullptr; + lv_obj_t* lblGyr_ = nullptr; + + // Bottom bar (5-segment like Recorder) + lv_obj_t* bottomBar_ = nullptr; + std::array lblBottomBtns_{}; // State cache CompassState lastState_{}; diff --git a/projects/Compass/main/src/img_compass_disc.c b/projects/Compass/main/src/img_compass_disc.c new file mode 100644 index 00000000..d0e646bc --- /dev/null +++ b/projects/Compass/main/src/img_compass_disc.c @@ -0,0 +1,161 @@ + +#if defined(LV_LVGL_H_INCLUDE_SIMPLE) +#include "lvgl.h" +#elif defined(LV_LVGL_H_INCLUDE_SYSTEM) +#include +#elif defined(LV_BUILD_TEST) +#include "../lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_COMPASS_DISC +#define LV_ATTRIBUTE_IMG_COMPASS_DISC +#endif + +static const +LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_COMPASS_DISC +uint8_t img_compass_disc_map[] = { + + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x1b,0xf7,0x1b,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x93,0xfd,0x93,0xfd,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xc9,0xfb,0xc9,0xfb,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x98,0xf6,0x26,0xfb,0x26,0xfb,0x98,0xf6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xef,0xfc,0x26,0xfb,0x26,0xfb,0xef,0xfc,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xf7,0x67,0xfb,0x26,0xfb,0x26,0xfb,0x67,0xfb,0x7d,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x51,0x8c,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x9e,0xf7,0x15,0xf6,0x26,0xfb,0x26,0xfb,0x26,0xfb,0x26,0xfb,0x15,0xf6,0x9e,0xf7,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x51,0x8c,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0xeb,0x5a,0x9e,0xf7,0xfb,0xde,0x9e,0xf7,0x9e,0xf7,0xb6,0xb5,0x9e,0xf7,0x9e,0xf7,0x59,0xce,0x9e,0xf7,0x4c,0xfc,0x26,0xfb,0x26,0xfb,0x26,0xfb,0x26,0xfb,0x4c,0xfc,0x9e,0xf7,0x9a,0xd6,0x5d,0xef,0x9e,0xf7,0xf7,0xbd,0x5d,0xef,0x9e,0xf7,0x1c,0xe7,0x9e,0xf7,0x9e,0xf7,0xeb,0x5a,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0xcf,0x7b,0x1c,0xe7,0xba,0xd6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x1b,0xf7,0x26,0xfb,0x26,0xfb,0x26,0xfb,0x26,0xfb,0x26,0xfb,0x26,0xfb,0x1b,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0x5d,0xef,0x1c,0xe7,0x6d,0x6b,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xba,0xd6,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0x75,0xad,0xd7,0xbd,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xf7,0x77,0xf6,0x77,0xf6,0x77,0xf6,0x77,0xf6,0x77,0xf6,0x77,0xf6,0x3c,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xd7,0xbd,0xf3,0x9c,0x9e,0xf7,0x9e,0xf7,0x38,0xc6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xfb,0xde,0x9e,0xf7,0x9e,0xf7,0xfb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0x30,0x84,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x30,0x84,0xdb,0xde,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x79,0xce,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xf7,0xbd,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x5d,0xef,0x9e,0xf7,0xdb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0x9e,0xf7,0x7d,0xef,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0xf7,0xbd,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x30,0x84,0xba,0xd6,0x9e,0xf7,0x96,0xb5,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x79,0xce,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0x30,0x84,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x1c,0xe7,0x2c,0x63,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x79,0xce,0x6d,0x6b,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x34,0xa5,0x18,0xc6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x18,0xc6,0x34,0xa5,0x9e,0xf7,0x1c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x18,0xc6,0x9e,0xf7,0x9e,0xf7,0xfb,0xde,0x92,0x94,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x92,0x94,0xfb,0xde,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0xdb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x1c,0xe7,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x38,0xc6,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x59,0xce,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x69,0x4a,0x9e,0xf7,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x79,0xce,0x9e,0xf7,0x9e,0xf7,0x69,0x4a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x69,0x4a,0xdb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0x69,0x4a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x1c,0xe7,0x9e,0xf7,0x69,0x4a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x69,0x4a,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xfb,0xde,0x18,0xc6,0x9e,0xf7,0x9e,0xf7,0xb6,0xb5,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xb6,0xb5,0x9e,0xf7,0x9e,0xf7,0xfb,0xde,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x1c,0xe7,0xdb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xf7,0xbd,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x79,0xce,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9a,0xd6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xf7,0xbd,0x30,0x84,0x3c,0xe7,0x55,0xad,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x1c,0xe7,0xba,0xd6,0x3c,0xe7,0x30,0x84,0xf7,0xbd,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0xdb,0xde,0x6d,0x6b,0x34,0xa5,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x34,0xa5,0x6d,0x6b,0xdb,0xde,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9a,0xd6,0x18,0xc6,0xfb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xfb,0xde,0x18,0xc6,0xfb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x5d,0xef,0x9e,0xf7,0x92,0x94,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x92,0x94,0x9e,0xf7,0x5d,0xef,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x18,0xc6,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x1c,0xe7,0xdb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xd7,0xbd,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x18,0xc6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x18,0xc6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0xf7,0xbd,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x51,0x8c,0xeb,0x5a,0xae,0x73,0x75,0xad,0xdb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0x75,0xad,0xae,0x73,0xeb,0x5a,0x51,0x8c,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xd7,0xbd,0xd7,0xbd,0x30,0x84,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0x30,0x84,0xd7,0xbd,0x75,0xad,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x38,0xc6,0xdb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x1c,0xe7,0xba,0xd6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xfb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x38,0xc6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xf7,0xbd,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0xdb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9a,0xd6,0x1c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x0c,0x63,0x0c,0x63,0x0c,0x63,0x0c,0x63,0xeb,0x5a,0xeb,0x5a,0x0c,0x63,0x0c,0x63,0x0c,0x63,0x0c,0x63,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x0c,0x63,0x0c,0x63,0x0c,0x63,0x0c,0x63,0xeb,0x5a,0xeb,0x5a,0x0c,0x63,0x0c,0x63,0x0c,0x63,0x0c,0x63,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0xeb,0x5a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xba,0xd6,0x59,0xce,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xba,0xd6,0xfb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0xb6,0xb5,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x38,0xc6,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x59,0xce,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0xf7,0xbd,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xf7,0xbd,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0x38,0xc6,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0x75,0xad,0xcf,0x7b,0xeb,0x5a,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0xeb,0x5a,0xcf,0x7b,0x75,0xad,0xdb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x51,0x8c,0x30,0x84,0xd7,0xbd,0x1c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x1c,0xe7,0xd7,0xbd,0x30,0x84,0x51,0x8c,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0x3c,0xe7,0x9a,0xd6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0xdb,0xde,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xba,0xd6,0x79,0xce,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0xfb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9a,0xd6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x59,0xce,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x79,0xce,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x3c,0xe7,0x30,0x84,0xb2,0x94,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xb2,0x94,0x30,0x84,0x3c,0xe7,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x34,0xa5,0x6d,0x6b,0xdb,0xde,0xfb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xfb,0xde,0xdb,0xde,0x6d,0x6b,0x34,0xa5,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x18,0xc6,0x3c,0xe7,0x9a,0xd6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0x9a,0xd6,0x18,0xc6,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xf7,0xbd,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xf7,0xbd,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xba,0xd6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xf3,0x9c,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x1c,0xe7,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0xfb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x38,0xc6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9a,0xd6,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0x1c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x1c,0xe7,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xb6,0xb5,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xb6,0xb5,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x5d,0xef,0x9e,0xf7,0x69,0x4a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x69,0x4a,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0x69,0x4a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x69,0x4a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x69,0x4a,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9a,0xd6,0x9e,0xf7,0x9e,0xf7,0x69,0x4a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x14,0xa5,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9a,0xd6,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x1c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9a,0xd6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9a,0xd6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xfb,0xde,0x92,0x94,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x92,0x94,0xfb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x79,0xce,0xd7,0xbd,0xdb,0xde,0x30,0x84,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x30,0x84,0x59,0xce,0xba,0xd6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0x6d,0x6b,0x1c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xfb,0xde,0x3c,0xe7,0x6d,0x6b,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x18,0xc6,0x34,0xa5,0x38,0xc6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0x9e,0xf7,0x34,0xa5,0x18,0xc6,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xf7,0xbd,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x59,0xce,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xeb,0x5a,0xeb,0x5a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xf7,0xbd,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0xf7,0xbd,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x9e,0xf7,0xdb,0xde,0xfb,0xde,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xeb,0x5a,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xeb,0x5a,0xeb,0x5a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0xeb,0x5a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x9e,0xf7,0x9a,0xd6,0xb6,0xb5,0x1c,0xe7,0xcf,0x7b,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xeb,0x5a,0xeb,0x5a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xcf,0x7b,0x79,0xce,0xdb,0xde,0x9e,0xf7,0x3c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x3c,0xe7,0xd7,0xbd,0xb2,0x94,0x1c,0xe7,0x9e,0xf7,0x3c,0xe7,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xeb,0x5a,0xeb,0x5a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x5d,0xef,0x9e,0xf7,0xdb,0xde,0x9a,0xd6,0x75,0xad,0xb6,0xb5,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x30,0x84,0xdb,0xde,0x9e,0xf7,0x9e,0xf7,0xfb,0xde,0x3c,0xe7,0x9e,0xf7,0x59,0xce,0x1c,0xe7,0x9e,0xf7,0xfb,0xde,0x3c,0xe7,0x9e,0xf7,0xeb,0x5a,0xeb,0x5a,0x9e,0xf7,0x1c,0xe7,0x1c,0xe7,0x9e,0xf7,0xdb,0xde,0x9a,0xd6,0x9e,0xf7,0xfb,0xde,0x1c,0xe7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xdb,0xde,0x30,0x84,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x7d,0xef,0x51,0x8c,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x5d,0xef,0x5d,0xef,0x9e,0xf7,0xeb,0x5a,0xeb,0x5a,0x9e,0xf7,0x3c,0xe7,0x5d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x51,0x8c,0x7d,0xef,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xeb,0x5a,0xeb,0x5a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xeb,0x5a,0xeb,0x5a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xeb,0x5a,0xeb,0x5a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0xeb,0x5a,0xeb,0x5a,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + 0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7,0x9e,0xf7, + +}; + +const lv_image_dsc_t img_compass_disc = { + .header = { + .magic = LV_IMAGE_HEADER_MAGIC, + .cf = LV_COLOR_FORMAT_RGB565, + .flags = 0, + .w = 120, + .h = 120, + .stride = 240, + .reserved_2 = 0, + }, + .data_size = sizeof(img_compass_disc_map), + .data = img_compass_disc_map, + .reserved = NULL, +}; + diff --git a/projects/Compass/main/src/ui_compass.cpp b/projects/Compass/main/src/ui_compass.cpp index 7121e0a7..817eb449 100644 --- a/projects/Compass/main/src/ui_compass.cpp +++ b/projects/Compass/main/src/ui_compass.cpp @@ -1,12 +1,16 @@ #include "ui_compass.h" #include "compat/input_keys.h" +#include #include #include +/* LVGL image asset generated from cardputerZero_compass.png */ +extern const lv_image_dsc_t img_compass_disc; + namespace { -constexpr int kScreenWidth = 320; +constexpr int kScreenWidth = 320; constexpr int kScreenHeight = 170; } // namespace @@ -24,84 +28,110 @@ void UiCompass::setActionHandler(std::function handler void UiCompass::buildUi(lv_obj_t* parent) { - /* 顶部状态栏 */ - createStatusBar(parent); - - /* 主内容区 */ - lv_obj_t* content = lv_obj_create(parent); - lv_obj_remove_style_all(content); - lv_obj_set_size(content, kScreenWidth, kScreenHeight - 20); - lv_obj_set_pos(content, 0, 20); - lv_obj_set_flex_flow(content, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(content, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); - lv_obj_set_style_pad_top(content, 4, 0); - lv_obj_set_style_pad_left(content, 4, 0); - lv_obj_set_style_pad_right(content, 4, 0); - lv_obj_set_style_pad_bottom(content, 4, 0); - lv_obj_set_style_pad_row(content, 2, 0); - - /* 航向角大字 */ - lv_obj_t* yawRow = lv_obj_create(content); - lv_obj_remove_style_all(yawRow); - lv_obj_set_size(yawRow, kScreenWidth - 8, 30); - lv_obj_set_flex_flow(yawRow, LV_FLEX_FLOW_ROW); - lv_obj_set_flex_align(yawRow, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - - lblHeading_ = lv_label_create(yawRow); - lv_label_set_text(lblHeading_, "YAW:"); - lv_obj_set_style_text_font(lblHeading_, &lv_font_montserrat_16, 0); - lv_obj_set_style_text_color(lblHeading_, lv_color_hex(0x333333), 0); - - lblYaw_ = lv_label_create(yawRow); + /* ---------- 左侧 Compass 区域 ---------- */ + lblCompassTitle_ = lv_label_create(parent); + lv_label_set_text(lblCompassTitle_, "Compass"); + lv_obj_set_style_text_font(lblCompassTitle_, &lv_font_montserrat_14, 0); + lv_obj_set_style_text_color(lblCompassTitle_, lv_color_hex(0x333333), 0); + lv_obj_set_size(lblCompassTitle_, 160, 16); + lv_obj_set_style_text_align(lblCompassTitle_, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_pos(lblCompassTitle_, 0, 2); + + compassDisc_ = lv_img_create(parent); + lv_img_set_src(compassDisc_, &img_compass_disc); + lv_obj_set_pos(compassDisc_, 20, 22); + lv_img_set_pivot(compassDisc_, kCompassImgSize / 2, kCompassImgSize / 2); + + lblYaw_ = lv_label_create(parent); lv_label_set_text(lblYaw_, "---"); - lv_obj_set_style_text_font(lblYaw_, &lv_font_montserrat_26, 0); - lv_obj_set_style_text_color(lblYaw_, lv_color_hex(0xE74C3C), 0); - - /* Pitch / Roll */ - lblPitchRoll_ = lv_label_create(content); - lv_label_set_text(lblPitchRoll_, "P:--- R:---"); - lv_obj_set_style_text_font(lblPitchRoll_, &lv_font_montserrat_10, 0); - lv_obj_set_style_text_color(lblPitchRoll_, lv_color_hex(0x666666), 0); - - /* 加速度 */ - lblAccel_ = lv_label_create(content); - lv_label_set_text(lblAccel_, "ACC: --- --- ---"); - lv_obj_set_style_text_font(lblAccel_, &lv_font_montserrat_10, 0); - lv_obj_set_style_text_color(lblAccel_, lv_color_hex(0x333333), 0); - - /* 陀螺仪 */ - lblGyro_ = lv_label_create(content); - lv_label_set_text(lblGyro_, "GYR: --- --- ---"); - lv_obj_set_style_text_font(lblGyro_, &lv_font_montserrat_10, 0); - lv_obj_set_style_text_color(lblGyro_, lv_color_hex(0x333333), 0); - - /* 磁力计 */ - lblMag_ = lv_label_create(content); - lv_label_set_text(lblMag_, "MAG: --- --- ---"); - lv_obj_set_style_text_font(lblMag_, &lv_font_montserrat_10, 0); - lv_obj_set_style_text_color(lblMag_, lv_color_hex(0x333333), 0); - - /* 温度 */ - lblTemp_ = lv_label_create(content); - lv_label_set_text(lblTemp_, "T: ---"); - lv_obj_set_style_text_font(lblTemp_, &lv_font_montserrat_10, 0); - lv_obj_set_style_text_color(lblTemp_, lv_color_hex(0x333333), 0); -} + lv_obj_set_style_text_font(lblYaw_, &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblYaw_, lv_color_hex(0x333333), 0); + lv_obj_set_size(lblYaw_, 160, 14); + lv_obj_set_style_text_align(lblYaw_, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_pos(lblYaw_, 0, 128); + + /* ---------- 右侧 IMU 区域 ---------- */ + lblImuTitle_ = lv_label_create(parent); + lv_label_set_text(lblImuTitle_, "IMU"); + lv_obj_set_style_text_font(lblImuTitle_, &lv_font_montserrat_14, 0); + lv_obj_set_style_text_color(lblImuTitle_, lv_color_hex(0x333333), 0); + lv_obj_set_size(lblImuTitle_, 160, 16); + lv_obj_set_style_text_align(lblImuTitle_, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_pos(lblImuTitle_, 160, 2); + + levelDisc_ = lv_obj_create(parent); + lv_obj_remove_style_all(levelDisc_); + lv_obj_set_size(levelDisc_, kDiscDia, kDiscDia); + lv_obj_set_pos(levelDisc_, 190, 22); + lv_obj_set_style_radius(levelDisc_, LV_RADIUS_CIRCLE, 0); + lv_obj_set_style_bg_color(levelDisc_, lv_color_hex(0xDCDCDC), 0); + lv_obj_set_style_bg_opa(levelDisc_, LV_OPA_COVER, 0); + lv_obj_set_style_border_width(levelDisc_, 2, 0); + lv_obj_set_style_border_color(levelDisc_, lv_color_hex(0x999999), 0); + lv_obj_set_style_border_opa(levelDisc_, LV_OPA_COVER, 0); + lv_obj_set_style_pad_all(levelDisc_, 0, 0); + lv_obj_clear_flag(levelDisc_, LV_OBJ_FLAG_SCROLLABLE); + + centerDot_ = lv_obj_create(levelDisc_); + lv_obj_remove_style_all(centerDot_); + lv_obj_set_size(centerDot_, 8, 8); + lv_obj_set_pos(centerDot_, 46, 46); + lv_obj_set_style_radius(centerDot_, LV_RADIUS_CIRCLE, 0); + lv_obj_set_style_bg_color(centerDot_, lv_color_hex(0x777777), 0); + lv_obj_set_style_bg_opa(centerDot_, LV_OPA_COVER, 0); + lv_obj_clear_flag(centerDot_, LV_OBJ_FLAG_SCROLLABLE); + + levelDot_ = lv_obj_create(levelDisc_); + lv_obj_remove_style_all(levelDot_); + lv_obj_set_size(levelDot_, 16, 16); + lv_obj_set_pos(levelDot_, 42, 42); + lv_obj_set_style_radius(levelDot_, LV_RADIUS_CIRCLE, 0); + lv_obj_set_style_bg_color(levelDot_, lv_color_hex(0xE74C3C), 0); + lv_obj_set_style_bg_opa(levelDot_, LV_OPA_COVER, 0); + lv_obj_set_style_border_width(levelDot_, 2, 0); + lv_obj_set_style_border_color(levelDot_, lv_color_hex(0xFFFFFF), 0); + lv_obj_set_style_border_opa(levelDot_, LV_OPA_COVER, 0); + lv_obj_clear_flag(levelDot_, LV_OBJ_FLAG_SCROLLABLE); + + lblAcc_ = lv_label_create(parent); + lv_label_set_text(lblAcc_, "ACC: --- --- ---"); + lv_obj_set_style_text_font(lblAcc_, &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblAcc_, lv_color_hex(0x555555), 0); + lv_obj_set_size(lblAcc_, 160, 12); + lv_obj_set_style_text_align(lblAcc_, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_pos(lblAcc_, 160, 126); + + lblGyr_ = lv_label_create(parent); + lv_label_set_text(lblGyr_, "GYR: --- --- ---"); + lv_obj_set_style_text_font(lblGyr_, &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblGyr_, lv_color_hex(0x555555), 0); + lv_obj_set_size(lblGyr_, 160, 12); + lv_obj_set_style_text_align(lblGyr_, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_pos(lblGyr_, 160, 138); + + /* ---------- 底部栏(五等分,参考 Recorder) ---------- */ + bottomBar_ = lv_obj_create(parent); + lv_obj_remove_style_all(bottomBar_); + lv_obj_set_size(bottomBar_, kScreenW, kBottomH); + lv_obj_set_pos(bottomBar_, 0, kScreenH - kBottomH); + lv_obj_set_style_bg_color(bottomBar_, lv_color_hex(0xD8D8E0), 0); + lv_obj_set_style_bg_opa(bottomBar_, LV_OPA_COVER, 0); + lv_obj_set_style_pad_all(bottomBar_, 0, 0); + lv_obj_clear_flag(bottomBar_, LV_OBJ_FLAG_SCROLLABLE); + + for (int i = 0; i < 5; i++) { + lblBottomBtns_[i] = lv_label_create(bottomBar_); + lv_obj_set_pos(lblBottomBtns_[i], i * kBtnW, 0); + lv_obj_set_size(lblBottomBtns_[i], kBtnW, kBottomH); + lv_obj_set_style_text_font(lblBottomBtns_[i], &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblBottomBtns_[i], lv_color_hex(0x333333), 0); + lv_obj_set_style_text_align(lblBottomBtns_[i], LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_text(lblBottomBtns_[i], "--"); + lv_obj_set_style_pad_top(lblBottomBtns_[i], 4, 0); + } -void UiCompass::createStatusBar(lv_obj_t* parent) -{ - statusBar_ = lv_obj_create(parent); - lv_obj_remove_style_all(statusBar_); - lv_obj_set_size(statusBar_, kScreenWidth, 20); - lv_obj_set_pos(statusBar_, 0, 0); - lv_obj_set_style_bg_color(statusBar_, lv_color_hex(0x555555), 0); - lv_obj_set_style_bg_opa(statusBar_, LV_OPA_COVER, 0); - - lblStatusText_ = lv_label_create(statusBar_); - lv_obj_set_pos(lblStatusText_, 4, 2); - lv_label_set_text(lblStatusText_, "Compass"); - lv_obj_set_style_text_font(lblStatusText_, &lv_font_montserrat_12, 0); - lv_obj_set_style_text_color(lblStatusText_, lv_color_white(), 0); + lv_label_set_text(lblBottomBtns_[0], "Cal"); + lv_label_set_text(lblBottomBtns_[2], "Exit"); } void UiCompass::update(const CompassState& state) @@ -110,38 +140,63 @@ void UiCompass::update(const CompassState& state) char buf[128]; - if (lblStatusText_ && state.statusText != lastState_.statusText) { - lv_label_set_text(lblStatusText_, state.statusText.c_str()); - } - + /* 偏航角 + 方位 */ if (lblYaw_) { - std::snprintf(buf, sizeof(buf), "%.1f\xc2\xb0", state.yaw); + const char* dir = ""; + float y = state.yaw; + if (y >= 337.5f || y < 22.5f) dir = "N"; + else if (y < 67.5f) dir = "NE"; + else if (y < 112.5f) dir = "E"; + else if (y < 157.5f) dir = "SE"; + else if (y < 202.5f) dir = "S"; + else if (y < 247.5f) dir = "SW"; + else if (y < 292.5f) dir = "W"; + else dir = "NW"; + std::snprintf(buf, sizeof(buf), "%.0f° %s", y, dir); lv_label_set_text(lblYaw_, buf); } - if (lblPitchRoll_) { - std::snprintf(buf, sizeof(buf), "P:%+.1f\xc2\xb0 R:%+.1f\xc2\xb0", state.pitch, state.roll); - lv_label_set_text(lblPitchRoll_, buf); - } - - if (lblAccel_) { - std::snprintf(buf, sizeof(buf), "ACC:%+.2f %+.2f %+.2f", state.accX, state.accY, state.accZ); - lv_label_set_text(lblAccel_, buf); + /* ACC / GYR */ + if (lblAcc_) { + std::snprintf(buf, sizeof(buf), "ACC:%6.2f %6.2f %6.2f", + state.accX, state.accY, state.accZ); + lv_label_set_text(lblAcc_, buf); } - - if (lblGyro_) { - std::snprintf(buf, sizeof(buf), "GYR:%+.1f %+.1f %+.1f", state.gyrX, state.gyrY, state.gyrZ); - lv_label_set_text(lblGyro_, buf); + if (lblGyr_) { + std::snprintf(buf, sizeof(buf), "GYR:%6.1f %6.1f %6.1f", + state.gyrX, state.gyrY, state.gyrZ); + lv_label_set_text(lblGyr_, buf); } - if (lblMag_) { - std::snprintf(buf, sizeof(buf), "MAG:%+.1f %+.1f %+.1f", state.magX, state.magY, state.magZ); - lv_label_set_text(lblMag_, buf); + /* 罗盘图片反向旋转,保持真北始终朝上 */ + if (compassDisc_) { + lv_img_set_angle(compassDisc_, -(int16_t)(state.yaw * 10.0f)); } - if (lblTemp_) { - std::snprintf(buf, sizeof(buf), "T:%.1f\xc2\xb0" "C", state.temp); - lv_label_set_text(lblTemp_, buf); + /* 水平仪圆点 */ + if (levelDot_) { + constexpr float maxOff = 30.0f; + /* 传感器 Y → 屏幕左右,传感器 X → 屏幕上下 */ + float dx = state.accY / 9.80665f * maxOff; + float dy = state.accX / 9.80665f * maxOff; + float dist = sqrtf(dx * dx + dy * dy); + if (dist > maxOff) { + dx = dx / dist * maxOff; + dy = dy / dist * maxOff; + } + int dotX = 50 + (int)dx - 8; /* 8 = 16/2 */ + int dotY = 50 + (int)dy - 8; + lv_obj_set_pos(levelDot_, dotX, dotY); + + /* 静止判定:三轴变化量均很小才视为稳定(防抖动,任意姿态均可) */ + bool isStable = false; + if (lastState_.sensorReady) { + isStable = (fabsf(state.accX - lastState_.accX) < 0.2f) && + (fabsf(state.accY - lastState_.accY) < 0.2f) && + (fabsf(state.accZ - lastState_.accZ) < 0.2f); + } + lv_color_t c = isStable ? lv_color_hex(0x2ECC71) : lv_color_hex(0xE74C3C); + lv_obj_set_style_bg_color(levelDot_, c, 0); } lastState_ = state; @@ -154,7 +209,7 @@ void UiCompass::onKeyPressed(uint32_t key_code, bool isRepeat) if (key_code == KEY_F4) { actionHandler_("calibrate"); - } else if (key_code == KEY_F8 || key_code == KEY_ESC) { + } else if (key_code == KEY_F6 || key_code == KEY_ESC) { actionHandler_("quit"); } } From 860c279408ab06ddc109d4b8ba076b0e516e2d9a Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Wed, 3 Jun 2026 17:54:58 +0800 Subject: [PATCH 10/11] Update Recorder app with font support and UI improvements --- projects/Recorder/main/SConstruct | 27 +- projects/Recorder/main/include/app_font.h | 30 ++ projects/Recorder/main/include/ui_recorder.h | 27 +- projects/Recorder/main/src/app_font.cpp | 114 +++++ projects/Recorder/main/src/main.cpp | 3 + projects/Recorder/main/src/recorder_app.cpp | 8 +- projects/Recorder/main/src/ui_recorder.cpp | 464 ++++++++++--------- 7 files changed, 442 insertions(+), 231 deletions(-) create mode 100644 projects/Recorder/main/include/app_font.h create mode 100644 projects/Recorder/main/src/app_font.cpp diff --git a/projects/Recorder/main/SConstruct b/projects/Recorder/main/SConstruct index 9e135fbb..ae9e5421 100644 --- a/projects/Recorder/main/SConstruct +++ b/projects/Recorder/main/SConstruct @@ -12,11 +12,12 @@ if "CONFIG_TOOLCHAIN_PREFIX" in os.environ: SRCS = Glob("src/*.c*") INCLUDE = [ADir("."), ADir("include")] PRIVATE_INCLUDE = [] -REQUIREMENTS = ["lvgl_component", "pthread", "Miniaudio"] +REQUIREMENTS = ["lvgl_component", "pthread", "Miniaudio", "freetype"] if "CONFIG_V9_5_LV_USE_SDL" not in os.environ: REQUIREMENTS += ["input", "xkbcommon", "udev"] STATIC_LIB = [] DYNAMIC_LIB = [] +LDFLAGS = [] DEFINITIONS = [] DEFINITIONS_PRIVATE = [] LDFLAGS = [] @@ -39,6 +40,30 @@ lvgl_component = list(filter(lambda x: x["target"] == "lvgl_component", env["COM if MULTIARCH_INCLUDE: for component in env["COMPONENTS"]: component["INCLUDE"] += [MULTIARCH_INCLUDE] + +# FreeType support +freetype_includes = [] +freetype_link_paths = [] +if "CONFIG_TOOLCHAIN_SYSROOT" in os.environ: + sysroot = os.environ["CONFIG_TOOLCHAIN_SYSROOT"].strip('"') + ft_inc = os.path.join(sysroot, "usr", "include", "freetype2") + ft_lib = os.path.join(sysroot, "usr", "lib", "aarch64-linux-gnu") + if os.path.isdir(ft_inc): + freetype_includes += [ft_inc] + if os.path.isdir(ft_lib): + freetype_link_paths += [ft_lib] +else: + for ft_inc in ["/usr/include/freetype2", "/usr/local/include/freetype2"]: + if os.path.exists(ft_inc): + freetype_includes += [ft_inc] + for ft_lib in ["/usr/lib", "/usr/local/lib"]: + if os.path.exists(ft_lib): + freetype_link_paths += [ft_lib] + +for component in env["COMPONENTS"]: + component["INCLUDE"] += freetype_includes + component["LINK_SEARCH_PATH"] += freetype_link_paths + if "CONFIG_V9_5_LV_USE_SDL" in os.environ: lvgl_component["REQUIREMENTS"] += ["SDL2"] lvgl_component["INCLUDE"] += ["/usr/include/SDL2"] diff --git a/projects/Recorder/main/include/app_font.h b/projects/Recorder/main/include/app_font.h new file mode 100644 index 00000000..6c84a742 --- /dev/null +++ b/projects/Recorder/main/include/app_font.h @@ -0,0 +1,30 @@ +#pragma once + +#include "lvgl/lvgl.h" + +namespace ui::font { + +/* codemap used by font 'assets/svgfont.ttf' (Unicode Private Use Area) */ +/* C++ compiler automatically converts \uXXXX to correct UTF-8 bytes */ +inline constexpr const char* ICON_EXIT = "\uEA01"; // .svgfont-exit +inline constexpr const char* ICON_FAST_FORWARD = "\uEA02"; // .svgfont-fast_forward +inline constexpr const char* ICON_FAST_REWIND = "\uEA03"; // .svgfont-fast_rewind +inline constexpr const char* ICON_LIST = "\uEA04"; // .svgfont-list +inline constexpr const char* ICON_PAUSE = "\uEA05"; // .svgfont-pause +inline constexpr const char* ICON_PLAY = "\uEA06"; // .svgfont-play +inline constexpr const char* ICON_RECORD = "\uEA07"; // .svgfont-record +inline constexpr const char* ICON_SAMPLE_RATE = "\uEA08"; // .svgfont-sample_rate +inline constexpr const char* ICON_SPEED = "\uEA09"; // .svgfont-speed +inline constexpr const char* ICON_STOP = "\uEA0A"; // .svgfont-stop + +class AppFont { +public: + static void init(); + static void deinit(); + static lv_font_t* svgfont(int32_t size); + +private: + AppFont() = delete; +}; + +} // namespace ui::font diff --git a/projects/Recorder/main/include/ui_recorder.h b/projects/Recorder/main/include/ui_recorder.h index 9b27c546..8d966ed3 100644 --- a/projects/Recorder/main/include/ui_recorder.h +++ b/projects/Recorder/main/include/ui_recorder.h @@ -8,8 +8,6 @@ enum class UiPage { Home, FileList, - Recording, - RecPaused, SaveConfirm, Playback, }; @@ -34,8 +32,6 @@ class UiRecorder : public IRecorderView { lv_obj_t* createPageContainer(lv_obj_t* parent); void createPageHome(lv_obj_t* page); void createPageFileList(lv_obj_t* page); - void createPageRecording(lv_obj_t* page); - void createPageRecPaused(lv_obj_t* page); void createPageSaveConfirm(lv_obj_t* page); void createPagePlayback(lv_obj_t* page); @@ -48,42 +44,41 @@ class UiRecorder : public IRecorderView { UiPage currentPage_ = UiPage::Home; // Status bar - lv_obj_t* statusBar_ = nullptr; lv_obj_t* lblStatusText_ = nullptr; // Bottom bar - lv_obj_t* bottomBar_ = nullptr; std::array lblBottomBtns_{}; + std::array lblBottomIndicators_{}; // Pages - std::array pages_{}; - - // Home - lv_obj_t* lblHomeReady_ = nullptr; + std::array pages_{}; // FileList lv_obj_t* lblFileListEmpty_ = nullptr; std::array lblFileListItems_{}; int fileListOffset_ = 0; - // Recording + // Home (shared with recording states) lv_obj_t* lblRecFilename_ = nullptr; lv_obj_t* recWaveContainer_ = nullptr; lv_obj_t* recWaveLine_ = nullptr; std::array recWavePoints_{}; lv_obj_t* lblRecTimer_ = nullptr; - // RecPaused - lv_obj_t* lblPauseFilename_ = nullptr; - lv_obj_t* pauseLine_ = nullptr; - lv_obj_t* lblPauseTimer_ = nullptr; - // SaveConfirm lv_obj_t* taEdit_ = nullptr; std::string editOriginalName_; // Playback lv_obj_t* lblPlayFilename_ = nullptr; + lv_obj_t* lblPlayDuration_ = nullptr; + static constexpr int kPlayBarCount = 40; + std::array playWaveBars_{}; + lv_obj_t* playBaseLine_ = nullptr; + lv_obj_t* playProgressArrow_ = nullptr; + lv_obj_t* playProgressTime_ = nullptr; + + // Legacy (kept but hidden) lv_obj_t* playWaveContainer_ = nullptr; lv_obj_t* playWaveLine_ = nullptr; lv_obj_t* playProgressLine_ = nullptr; diff --git a/projects/Recorder/main/src/app_font.cpp b/projects/Recorder/main/src/app_font.cpp new file mode 100644 index 00000000..e066f270 --- /dev/null +++ b/projects/Recorder/main/src/app_font.cpp @@ -0,0 +1,114 @@ +#include "app_font.h" + +#include +#include +#include +#include + +namespace ui::font { +namespace { + +struct FontSlot { + int32_t size = 0; + lv_font_t* font = nullptr; +}; + +std::array slots{}; +bool initialized = false; + +std::vector candidate_paths() +{ + return { + "assets/svgfont.ttf", + "./assets/svgfont.ttf", + "../assets/svgfont.ttf", + "../../assets/svgfont.ttf", + }; +} + +void log_attempt(const char* path, bool success) +{ + if (success) { + printf("[AppFont] svgfont loaded from: %s\n", path); + } else { + printf("[AppFont] svgfont failed to load from: %s\n", path); + } +} + +lv_font_t* fallback_font() +{ + return const_cast(&lv_font_montserrat_12); +} + +} // namespace + +void AppFont::init() +{ + if (initialized) { + return; + } + slots = {}; + initialized = true; +} + +void AppFont::deinit() +{ +#if LV_USE_FREETYPE + for (auto& s : slots) { + if (s.font) { + lv_freetype_font_delete(s.font); + } + s.font = nullptr; + s.size = 0; + } +#endif + initialized = false; +} + +lv_font_t* AppFont::svgfont(int32_t size) +{ + init(); + if (size <= 0) { + size = 12; + } + + // Cache hit + for (auto& s : slots) { + if (s.font && s.size == size) { + return s.font; + } + } + + // Find empty slot and load + for (auto& s : slots) { + if (!s.font) { +#if LV_USE_FREETYPE + const auto paths = candidate_paths(); + printf("[AppFont] Trying to load svgfont (size=%d), candidates=%zu\n", + size, paths.size()); + for (const auto& p : paths) { + s.font = lv_freetype_font_create( + p.c_str(), + LV_FREETYPE_FONT_RENDER_MODE_BITMAP, + static_cast(size), + LV_FREETYPE_FONT_STYLE_NORMAL); + log_attempt(p.c_str(), s.font != nullptr); + if (s.font) { + s.size = size; + return s.font; + } + } + printf("[AppFont] All svgfont candidates failed, falling back to montserrat_12\n"); +#else + printf("[AppFont] LV_USE_FREETYPE is disabled, falling back to montserrat_12\n"); + (void)candidate_paths; +#endif + return fallback_font(); + } + } + + printf("[AppFont] svgfont cache is full, falling back to montserrat_12\n"); + return fallback_font(); +} + +} // namespace ui::font diff --git a/projects/Recorder/main/src/main.cpp b/projects/Recorder/main/src/main.cpp index d72cce7b..6b155b8e 100644 --- a/projects/Recorder/main/src/main.cpp +++ b/projects/Recorder/main/src/main.cpp @@ -217,6 +217,9 @@ int main() app.init(); lv_init(); +#if LV_USE_FREETYPE + lv_freetype_init(64); +#endif lv_linux_disp_init(); LV_EVENT_KEYBOARD = lv_event_register_id(); lv_linux_indev_init(); diff --git a/projects/Recorder/main/src/recorder_app.cpp b/projects/Recorder/main/src/recorder_app.cpp index 7bdf2153..634828f6 100644 --- a/projects/Recorder/main/src/recorder_app.cpp +++ b/projects/Recorder/main/src/recorder_app.cpp @@ -225,14 +225,10 @@ void RecorderApp::syncStateFromEngine() appState_ = AppState::RecPaused; break; case AudioState::Playing: - if (appState_ != AppState::Playing && appState_ != AppState::PlayPaused) { - appState_ = AppState::Playing; - } + appState_ = AppState::Playing; break; case AudioState::PlayPaused: - if (appState_ != AppState::Playing && appState_ != AppState::PlayPaused) { - appState_ = AppState::PlayPaused; - } + appState_ = AppState::PlayPaused; break; } } diff --git a/projects/Recorder/main/src/ui_recorder.cpp b/projects/Recorder/main/src/ui_recorder.cpp index b3c7d797..b4da4131 100644 --- a/projects/Recorder/main/src/ui_recorder.cpp +++ b/projects/Recorder/main/src/ui_recorder.cpp @@ -1,4 +1,5 @@ #include "ui_recorder.h" +#include "app_font.h" #include "compat/input_keys.h" LV_FONT_DECLARE(lv_font_montserrat_16) @@ -8,19 +9,27 @@ LV_FONT_DECLARE(lv_font_montserrat_26) constexpr int kScreenW = 320; constexpr int kScreenH = 170; constexpr int kStatusH = 30; -constexpr int kBottomH = 30; +constexpr int kBottomH = 25; constexpr int kContentH = kScreenH - kStatusH - kBottomH; // 110 constexpr int kBtnW = kScreenW / 5; // 64 -// Colors (light theme matching reference image) -const lv_color_t kColorBg = lv_color_hex(0xE8E8EC); -const lv_color_t kColorStatusBar = lv_color_hex(0x555555); +// Colors (dark theme) +const lv_color_t kColorBg = lv_color_hex(0x000000); +const lv_color_t kColorStatusBar = lv_color_hex(0x333333); const lv_color_t kColorStatusText = lv_color_hex(0xFFFFFF); -const lv_color_t kColorText = lv_color_hex(0x222222); -const lv_color_t kColorTextGray = lv_color_hex(0x888888); -const lv_color_t kColorBottomBar = lv_color_hex(0xD8D8E0); -const lv_color_t kColorWaveBg = lv_color_hex(0xC8C8D0); -const lv_color_t kColorHighlight = lv_color_hex(0x0066CC); +const lv_color_t kColorText = lv_color_hex(0xFFFFFF); +const lv_color_t kColorTextGray = lv_color_hex(0xAAAAAA); +const lv_color_t kColorBottomBar = lv_color_hex(0x111111); +const lv_color_t kColorWaveBg = lv_color_hex(0x222222); +const lv_color_t kColorHighlight = lv_color_hex(0x00AAFF); +const lv_color_t kColorWaveOrange = lv_color_hex(0xFF8800); +const lv_color_t kColorIconStop = lv_color_hex(0xFF0000); +const lv_color_t kColorIconRecord = lv_color_hex(0xFF3399); +const lv_color_t kColorIconSampleRate = lv_color_hex(0xFFCC00); +const lv_color_t kColorIconList = lv_color_hex(0x33CC33); +const lv_color_t kColorIconPlay = lv_color_hex(0x1F9DFF); +const lv_color_t kColorIconPause = lv_color_hex(0xCC6633); +const lv_color_t kColorIconFast = lv_color_hex(0xCC3366); void UiRecorder::init(lv_obj_t* parent) { @@ -35,39 +44,25 @@ void UiRecorder::buildUi(lv_obj_t* parent) lv_obj_set_style_bg_color(parent, kColorBg, 0); lv_obj_set_style_bg_opa(parent, LV_OPA_COVER, 0); - createStatusBar(parent); - createBottomBar(parent); - pages_[static_cast(UiPage::Home)] = createPageContainer(parent); pages_[static_cast(UiPage::FileList)] = createPageContainer(parent); - pages_[static_cast(UiPage::Recording)] = createPageContainer(parent); - pages_[static_cast(UiPage::RecPaused)] = createPageContainer(parent); pages_[static_cast(UiPage::SaveConfirm)] = createPageContainer(parent); pages_[static_cast(UiPage::Playback)] = createPageContainer(parent); createPageHome(pages_[static_cast(UiPage::Home)]); createPageFileList(pages_[static_cast(UiPage::FileList)]); - createPageRecording(pages_[static_cast(UiPage::Recording)]); - createPageRecPaused(pages_[static_cast(UiPage::RecPaused)]); createPageSaveConfirm(pages_[static_cast(UiPage::SaveConfirm)]); createPagePlayback(pages_[static_cast(UiPage::Playback)]); + createStatusBar(parent); + createBottomBar(parent); + switchPage(UiPage::Home); } void UiRecorder::createStatusBar(lv_obj_t* parent) { - statusBar_ = lv_obj_create(parent); - lv_obj_set_size(statusBar_, kScreenW, kStatusH); - lv_obj_set_pos(statusBar_, 0, 0); - lv_obj_clear_flag(statusBar_, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_set_style_bg_color(statusBar_, kColorStatusBar, 0); - lv_obj_set_style_bg_opa(statusBar_, LV_OPA_COVER, 0); - lv_obj_set_style_border_width(statusBar_, 0, 0); - lv_obj_set_style_pad_all(statusBar_, 0, 0); - lv_obj_set_style_radius(statusBar_, 0, 0); - - lblStatusText_ = lv_label_create(statusBar_); + lblStatusText_ = lv_label_create(parent); lv_obj_set_pos(lblStatusText_, 8, 6); lv_obj_set_style_text_font(lblStatusText_, &lv_font_montserrat_12, 0); lv_obj_set_style_text_color(lblStatusText_, kColorStatusText, 0); @@ -76,25 +71,26 @@ void UiRecorder::createStatusBar(lv_obj_t* parent) void UiRecorder::createBottomBar(lv_obj_t* parent) { - bottomBar_ = lv_obj_create(parent); - lv_obj_set_size(bottomBar_, kScreenW, kBottomH); - lv_obj_set_pos(bottomBar_, 0, kScreenH - kBottomH); - lv_obj_clear_flag(bottomBar_, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_set_style_bg_color(bottomBar_, kColorBottomBar, 0); - lv_obj_set_style_bg_opa(bottomBar_, LV_OPA_COVER, 0); - lv_obj_set_style_border_width(bottomBar_, 0, 0); - lv_obj_set_style_pad_all(bottomBar_, 0, 0); - lv_obj_set_style_radius(bottomBar_, 0, 0); - for (int i = 0; i < 5; i++) { - lblBottomBtns_[i] = lv_label_create(bottomBar_); - lv_obj_set_pos(lblBottomBtns_[i], i * kBtnW, 0); + lblBottomBtns_[i] = lv_label_create(parent); + lv_obj_set_pos(lblBottomBtns_[i], i * kBtnW, kScreenH - kBottomH -4); lv_obj_set_size(lblBottomBtns_[i], kBtnW, kBottomH); lv_obj_set_style_text_font(lblBottomBtns_[i], &lv_font_montserrat_12, 0); lv_obj_set_style_text_color(lblBottomBtns_[i], kColorText, 0); lv_obj_set_style_text_align(lblBottomBtns_[i], LV_TEXT_ALIGN_CENTER, 0); lv_label_set_text(lblBottomBtns_[i], "--"); - lv_obj_set_style_pad_top(lblBottomBtns_[i], 8, 0); + lv_obj_set_style_pad_top(lblBottomBtns_[i], 0, 0); + lv_obj_add_flag(lblBottomBtns_[i], LV_OBJ_FLAG_OVERFLOW_VISIBLE); + } + + for (int i = 0; i < 5; i++) { + lblBottomIndicators_[i] = lv_label_create(parent); + lv_obj_set_pos(lblBottomIndicators_[i], i * kBtnW, kScreenH - 12); + lv_obj_set_size(lblBottomIndicators_[i], kBtnW, 12); + lv_obj_set_style_text_font(lblBottomIndicators_[i], &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblBottomIndicators_[i], kColorText, 0); + lv_obj_set_style_text_align(lblBottomIndicators_[i], LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_text(lblBottomIndicators_[i], "|"); } } @@ -113,38 +109,8 @@ lv_obj_t* UiRecorder::createPageContainer(lv_obj_t* parent) return page; } -// ---------- Home Page ---------- +// ---------- Home Page (merged with Recording/RecPaused) ---------- void UiRecorder::createPageHome(lv_obj_t* page) -{ - lblHomeReady_ = lv_label_create(page); - lv_obj_set_style_text_font(lblHomeReady_, &lv_font_montserrat_26, 0); - lv_obj_set_style_text_color(lblHomeReady_, kColorText, 0); - lv_label_set_text(lblHomeReady_, "Ready"); - lv_obj_center(lblHomeReady_); -} - -// ---------- File List Page ---------- -void UiRecorder::createPageFileList(lv_obj_t* page) -{ - for (int i = 0; i < 5; i++) { - lblFileListItems_[i] = lv_label_create(page); - lv_obj_set_pos(lblFileListItems_[i], 10, 5 + i * 20); - lv_obj_set_width(lblFileListItems_[i], 300); - lv_obj_set_style_text_font(lblFileListItems_[i], &lv_font_montserrat_12, 0); - lv_obj_set_style_text_color(lblFileListItems_[i], kColorText, 0); - lv_label_set_text(lblFileListItems_[i], ""); - lv_obj_add_flag(lblFileListItems_[i], LV_OBJ_FLAG_HIDDEN); - } - - lblFileListEmpty_ = lv_label_create(page); - lv_obj_set_style_text_font(lblFileListEmpty_, &lv_font_montserrat_16, 0); - lv_obj_set_style_text_color(lblFileListEmpty_, kColorTextGray, 0); - lv_label_set_text(lblFileListEmpty_, "Empty"); - lv_obj_center(lblFileListEmpty_); -} - -// ---------- Recording Page ---------- -void UiRecorder::createPageRecording(lv_obj_t* page) { lblRecFilename_ = lv_label_create(page); lv_obj_set_pos(lblRecFilename_, 10, 5); @@ -164,11 +130,12 @@ void UiRecorder::createPageRecording(lv_obj_t* page) lv_obj_set_style_border_width(recWaveContainer_, 1, 0); lv_obj_clear_flag(recWaveContainer_, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_style_radius(recWaveContainer_, 4, 0); + lv_obj_add_flag(recWaveContainer_, LV_OBJ_FLAG_HIDDEN); - recWaveLine_ = lv_line_create(recWaveContainer_); + recWaveLine_ = lv_line_create(page); lv_obj_set_size(recWaveLine_, 280, 50); - lv_obj_set_pos(recWaveLine_, 0, 0); - lv_obj_set_style_line_color(recWaveLine_, kColorHighlight, 0); + lv_obj_set_pos(recWaveLine_, 20, 30); + lv_obj_set_style_line_color(recWaveLine_, kColorWaveOrange, 0); lv_obj_set_style_line_width(recWaveLine_, 2, 0); lv_line_set_y_invert(recWaveLine_, true); lv_line_set_points_mutable(recWaveLine_, recWavePoints_.data(), recWavePoints_.size()); @@ -179,39 +146,27 @@ void UiRecorder::createPageRecording(lv_obj_t* page) lv_obj_set_style_text_font(lblRecTimer_, &lv_font_montserrat_14, 0); lv_obj_set_style_text_color(lblRecTimer_, kColorText, 0); lv_obj_set_style_text_align(lblRecTimer_, LV_TEXT_ALIGN_CENTER, 0); - lv_label_set_text(lblRecTimer_, "00:00"); + lv_label_set_text(lblRecTimer_, ""); } -// ---------- Rec Paused Page ---------- -void UiRecorder::createPageRecPaused(lv_obj_t* page) +// ---------- File List Page ---------- +void UiRecorder::createPageFileList(lv_obj_t* page) { - lblPauseFilename_ = lv_label_create(page); - lv_obj_set_pos(lblPauseFilename_, 10, 5); - lv_obj_set_width(lblPauseFilename_, 300); - lv_obj_set_style_text_font(lblPauseFilename_, &lv_font_montserrat_12, 0); - lv_obj_set_style_text_color(lblPauseFilename_, kColorText, 0); - lv_label_set_long_mode(lblPauseFilename_, LV_LABEL_LONG_SCROLL_CIRCULAR); - lv_obj_set_style_text_align(lblPauseFilename_, LV_TEXT_ALIGN_CENTER, 0); - lv_label_set_text(lblPauseFilename_, ""); - - // Horizontal pause line in the center - pauseLine_ = lv_line_create(page); - static lv_point_precise_t line_points[2]; - line_points[0].x = 20; - line_points[0].y = 55; - line_points[1].x = 300; - line_points[1].y = 55; - lv_line_set_points(pauseLine_, line_points, 2); - lv_obj_set_style_line_width(pauseLine_, 2, 0); - lv_obj_set_style_line_color(pauseLine_, kColorText, 0); - - lblPauseTimer_ = lv_label_create(page); - lv_obj_set_pos(lblPauseTimer_, 0, 88); - lv_obj_set_width(lblPauseTimer_, kScreenW); - lv_obj_set_style_text_font(lblPauseTimer_, &lv_font_montserrat_14, 0); - lv_obj_set_style_text_color(lblPauseTimer_, kColorText, 0); - lv_obj_set_style_text_align(lblPauseTimer_, LV_TEXT_ALIGN_CENTER, 0); - lv_label_set_text(lblPauseTimer_, "00:00"); + for (int i = 0; i < 5; i++) { + lblFileListItems_[i] = lv_label_create(page); + lv_obj_set_pos(lblFileListItems_[i], 10, 5 + i * 20); + lv_obj_set_width(lblFileListItems_[i], 300); + lv_obj_set_style_text_font(lblFileListItems_[i], &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblFileListItems_[i], kColorText, 0); + lv_label_set_text(lblFileListItems_[i], ""); + lv_obj_add_flag(lblFileListItems_[i], LV_OBJ_FLAG_HIDDEN); + } + + lblFileListEmpty_ = lv_label_create(page); + lv_obj_set_style_text_font(lblFileListEmpty_, &lv_font_montserrat_16, 0); + lv_obj_set_style_text_color(lblFileListEmpty_, kColorTextGray, 0); + lv_label_set_text(lblFileListEmpty_, "Empty"); + lv_obj_center(lblFileListEmpty_); } // ---------- Save Confirm Page ---------- @@ -239,24 +194,77 @@ void UiRecorder::createPageSaveConfirm(lv_obj_t* page) // ---------- Playback Page ---------- void UiRecorder::createPagePlayback(lv_obj_t* page) { + // Filename: top-right, scrolling lblPlayFilename_ = lv_label_create(page); lv_obj_set_pos(lblPlayFilename_, 10, 5); lv_obj_set_width(lblPlayFilename_, 300); lv_obj_set_style_text_font(lblPlayFilename_, &lv_font_montserrat_12, 0); lv_obj_set_style_text_color(lblPlayFilename_, kColorText, 0); lv_label_set_long_mode(lblPlayFilename_, LV_LABEL_LONG_SCROLL_CIRCULAR); - lv_obj_set_style_text_align(lblPlayFilename_, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_style_text_align(lblPlayFilename_, LV_TEXT_ALIGN_RIGHT, 0); lv_label_set_text(lblPlayFilename_, ""); + // Duration: below filename, right aligned + lblPlayDuration_ = lv_label_create(page); + lv_obj_set_pos(lblPlayDuration_, 10, 22); + lv_obj_set_width(lblPlayDuration_, 300); + lv_obj_set_style_text_font(lblPlayDuration_, &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblPlayDuration_, kColorTextGray, 0); + lv_obj_set_style_text_align(lblPlayDuration_, LV_TEXT_ALIGN_RIGHT, 0); + lv_label_set_text(lblPlayDuration_, ""); + + // Wave bars: 40 gray bars + constexpr int barW = 5; + constexpr int barGap = 2; + constexpr int startX = 20; + constexpr int baseY = 80; + for (int i = 0; i < kPlayBarCount; i++) { + playWaveBars_[i] = lv_obj_create(page); + lv_obj_set_size(playWaveBars_[i], barW, 1); + lv_obj_set_pos(playWaveBars_[i], startX + i * (barW + barGap), baseY - 1); + lv_obj_set_style_bg_color(playWaveBars_[i], lv_color_hex(0x888888), 0); + lv_obj_set_style_bg_opa(playWaveBars_[i], LV_OPA_COVER, 0); + lv_obj_set_style_border_width(playWaveBars_[i], 0, 0); + lv_obj_set_style_radius(playWaveBars_[i], 0, 0); + lv_obj_clear_flag(playWaveBars_[i], LV_OBJ_FLAG_SCROLLABLE); + } + + // Blue base line + playBaseLine_ = lv_line_create(page); + static lv_point_precise_t baseLinePts[2]; + baseLinePts[0].x = startX; + baseLinePts[0].y = baseY; + baseLinePts[1].x = startX + kPlayBarCount * (barW + barGap) - barGap; + baseLinePts[1].y = baseY; + lv_line_set_points(playBaseLine_, baseLinePts, 2); + lv_obj_set_style_line_color(playBaseLine_, lv_color_hex(0x0088FF), 0); + lv_obj_set_style_line_width(playBaseLine_, 2, 0); + + // Red arrow (below blue line) + playProgressArrow_ = lv_label_create(page); + lv_obj_set_size(playProgressArrow_, 20, 12); + lv_obj_set_style_text_font(playProgressArrow_, &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(playProgressArrow_, lv_color_hex(0xFF0000), 0); + lv_obj_set_style_text_align(playProgressArrow_, LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_text(playProgressArrow_, "▲"); + + // Time below arrow + playProgressTime_ = lv_label_create(page); + lv_obj_set_size(playProgressTime_, 50, 14); + lv_obj_set_style_text_font(playProgressTime_, &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(playProgressTime_, kColorText, 0); + lv_obj_set_style_text_align(playProgressTime_, LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_text(playProgressTime_, "00:00"); + + // Legacy container (hidden) playWaveContainer_ = lv_obj_create(page); lv_obj_set_size(playWaveContainer_, 280, 50); lv_obj_set_pos(playWaveContainer_, 20, 30); - lv_obj_set_style_bg_color(playWaveContainer_, kColorWaveBg, 0); - lv_obj_set_style_bg_opa(playWaveContainer_, LV_OPA_COVER, 0); - lv_obj_set_style_border_color(playWaveContainer_, lv_color_hex(0xAAAAAA), 0); - lv_obj_set_style_border_width(playWaveContainer_, 1, 0); + lv_obj_set_style_bg_opa(playWaveContainer_, LV_OPA_TRANSP, 0); + lv_obj_set_style_border_width(playWaveContainer_, 0, 0); lv_obj_clear_flag(playWaveContainer_, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_set_style_radius(playWaveContainer_, 4, 0); + lv_obj_set_style_radius(playWaveContainer_, 0, 0); + lv_obj_add_flag(playWaveContainer_, LV_OBJ_FLAG_HIDDEN); playWaveLine_ = lv_line_create(playWaveContainer_); lv_obj_set_size(playWaveLine_, 280, 50); @@ -275,12 +283,7 @@ void UiRecorder::createPagePlayback(lv_obj_t* page) lv_line_set_points_mutable(playProgressLine_, playProgressPoints_, 2); lblPlayTimer_ = lv_label_create(page); - lv_obj_set_pos(lblPlayTimer_, 0, 88); - lv_obj_set_width(lblPlayTimer_, kScreenW); - lv_obj_set_style_text_font(lblPlayTimer_, &lv_font_montserrat_14, 0); - lv_obj_set_style_text_color(lblPlayTimer_, kColorText, 0); - lv_obj_set_style_text_align(lblPlayTimer_, LV_TEXT_ALIGN_CENTER, 0); - lv_label_set_text(lblPlayTimer_, "00:00 / 00:00"); + lv_obj_add_flag(lblPlayTimer_, LV_OBJ_FLAG_HIDDEN); } // ---------- Page switching ---------- @@ -299,41 +302,61 @@ void UiRecorder::switchPage(UiPage page) void UiRecorder::updateBottomLabels(UiPage page, bool isPaused) { - struct LabelSet { - const char* b0; - const char* b1; - const char* b2; - const char* b3; - const char* b4; + auto setBtn = [this](int idx, const char* text, bool isIcon, lv_color_t color = kColorText, lv_opa_t opa = LV_OPA_COVER) { + if (isIcon) { + lv_obj_set_style_text_font(lblBottomBtns_[idx], ui::font::AppFont::svgfont(16), 0); + } else { + lv_obj_set_style_text_font(lblBottomBtns_[idx], &lv_font_montserrat_12, 0); + } + lv_obj_set_style_text_color(lblBottomBtns_[idx], color, 0); + lv_obj_set_style_text_opa(lblBottomBtns_[idx], opa, 0); + lv_label_set_text(lblBottomBtns_[idx], text); }; - LabelSet labels = {"--", "--", "--", "--", "--"}; switch (page) { case UiPage::Home: - labels = {"List", lastState_.sampleRate.c_str(), "Rec", "--", "Exit"}; + if (lastState_.state == AppState::Idle) { + setBtn(0, ui::font::ICON_EXIT, true); + setBtn(1, ui::font::ICON_STOP, true, kColorIconStop, LV_OPA_50); + setBtn(2, ui::font::ICON_RECORD, true, kColorIconRecord); + setBtn(3, lastState_.sampleRate.c_str(), false, kColorIconSampleRate); + setBtn(4, ui::font::ICON_LIST, true, kColorIconList); + } else if (lastState_.state == AppState::Recording) { + setBtn(0, ui::font::ICON_EXIT, true); + setBtn(1, ui::font::ICON_STOP, true, kColorIconStop); + setBtn(2, ui::font::ICON_PAUSE, true, kColorIconPause); + setBtn(3, lastState_.sampleRate.c_str(), false, kColorIconSampleRate, LV_OPA_50); + setBtn(4, ui::font::ICON_LIST, true, kColorIconList, LV_OPA_50); + } else if (lastState_.state == AppState::RecPaused) { + setBtn(0, ui::font::ICON_EXIT, true); + setBtn(1, ui::font::ICON_STOP, true, kColorIconStop); + setBtn(2, ui::font::ICON_PLAY, true, kColorIconPlay); + setBtn(3, lastState_.sampleRate.c_str(), false, kColorIconSampleRate, LV_OPA_50); + setBtn(4, ui::font::ICON_LIST, true, kColorIconList, LV_OPA_50); + } break; case UiPage::FileList: - labels = {"Up", "Down", "Play", "Rename", "Exit"}; - break; - case UiPage::Recording: - labels = {"--", "--", "Pause", "Done", "Exit"}; - break; - case UiPage::RecPaused: - labels = {"--", "--", "Continue", "Done", "Exit"}; + setBtn(0, "Up", false); + setBtn(1, "Down", false); + setBtn(2, ui::font::ICON_PLAY, true, kColorIconPlay); + setBtn(3, "Rename", false); + setBtn(4, ui::font::ICON_EXIT, true); break; case UiPage::SaveConfirm: - labels = {"Undo", "<-", "->", "Save", "Exit"}; + setBtn(0, ui::font::ICON_FAST_REWIND, true, kColorIconFast); + setBtn(1, ui::font::ICON_FAST_FORWARD, true, kColorIconFast); + setBtn(2, "Undo", false); + setBtn(3, "Save", false); + setBtn(4, ui::font::ICON_EXIT, true); break; case UiPage::Playback: - labels = {lastState_.playbackSpeed.c_str(), "-10s", isPaused ? "Play" : "Pause", "+10s", "Exit"}; + setBtn(0, ui::font::ICON_EXIT, true); + setBtn(1, lastState_.playbackSpeed.c_str(), false); + setBtn(2, isPaused ? ui::font::ICON_PLAY : ui::font::ICON_PAUSE, true, isPaused ? kColorIconPlay : kColorIconPause); + setBtn(3, ui::font::ICON_FAST_REWIND, true, kColorIconFast); + setBtn(4, ui::font::ICON_FAST_FORWARD, true, kColorIconFast); break; } - - lv_label_set_text(lblBottomBtns_[0], labels.b0); - lv_label_set_text(lblBottomBtns_[1], labels.b1); - lv_label_set_text(lblBottomBtns_[2], labels.b2); - lv_label_set_text(lblBottomBtns_[3], labels.b3); - lv_label_set_text(lblBottomBtns_[4], labels.b4); } // ---------- Content update ---------- @@ -386,12 +409,32 @@ void UiRecorder::updatePageContent(const RecorderState& state) } } - // Recording / RecPaused / SaveConfirm filename & timer - const char* fname = state.currentFileName.empty() ? "" : state.currentFileName.c_str(); - lv_label_set_text(lblRecFilename_, fname); - lv_label_set_text(lblPauseFilename_, fname); - lv_label_set_text(lblRecTimer_, state.timerText.c_str()); - lv_label_set_text(lblPauseTimer_, state.timerText.c_str()); + // Home page content (Idle / Recording / RecPaused) + if (currentPage_ == UiPage::Home) { + if (state.state == AppState::Idle) { + lv_label_set_text(lblRecFilename_, ""); + lv_label_set_text(lblRecTimer_, ""); + } else { + const char* fname = state.currentFileName.empty() ? "" : state.currentFileName.c_str(); + lv_label_set_text(lblRecFilename_, fname); + lv_label_set_text(lblRecTimer_, state.timerText.c_str()); + } + + constexpr int count = 128; + float stepX = 280.0f / (count - 1); + for (int i = 0; i < count; i++) { + recWavePoints_[i].x = i * stepX; + if (state.state == AppState::Idle || state.state == AppState::RecPaused) { + recWavePoints_[i].y = 25.0f; // flat line at center + } else { + float v = state.recWaveform[i]; + if (v < -1.0f) v = -1.0f; + if (v > 1.0f) v = 1.0f; + recWavePoints_[i].y = 25.0f + v * 25.0f; + } + } + lv_obj_invalidate(recWaveLine_); + } if (enteringSaveConfirm && taEdit_) { std::string nameOnly = state.currentFileName; @@ -408,56 +451,60 @@ void UiRecorder::updatePageContent(const RecorderState& state) } // Playback + const char* fname = state.currentFileName.empty() ? "" : state.currentFileName.c_str(); lv_label_set_text(lblPlayFilename_, fname); - lv_label_set_text(lblPlayTimer_, state.timerText.c_str()); - - // Update recording waveform - if (currentPage_ == UiPage::Recording) { - constexpr int count = 128; - float stepX = 280.0f / (count - 1); - for (int i = 0; i < count; i++) { - recWavePoints_[i].x = i * stepX; - float v = state.recWaveform[i]; - // Clamp to [-1, 1] - if (v < -1.0f) v = -1.0f; - if (v > 1.0f) v = 1.0f; - // y_invert enabled: y=0 at bottom, y=50 at top. - // Center line is y=25. Positive values go up, negative go down. - recWavePoints_[i].y = 25.0f + v * 25.0f; - } - lv_obj_invalidate(recWaveLine_); + { + float dur = state.playbackDuration; + int dmin = static_cast(dur) / 60; + int dsec = static_cast(dur) % 60; + char buf[16]; + snprintf(buf, sizeof(buf), "%02d:%02d", dmin, dsec); + lv_label_set_text(lblPlayDuration_, buf); } // Update playback waveform and progress if (currentPage_ == UiPage::Playback) { + // Gray bars if (state.hasPlayWaveform) { - constexpr int count = 256; - float stepX = 280.0f / (count - 1); - for (int i = 0; i < count; i++) { - playWavePoints_[i].x = i * stepX; - float v = state.playWaveform[i]; - if (v < 0.0f) v = 0.0f; - if (v > 1.0f) v = 1.0f; - // y_invert enabled: draw envelope from bottom to top - playWavePoints_[i].y = v * 50.0f; + constexpr int sampleCount = 256; + constexpr int baseY = 80; + constexpr int maxBarH = 40; + for (int i = 0; i < kPlayBarCount; i++) { + int startIdx = i * sampleCount / kPlayBarCount; + int endIdx = (i + 1) * sampleCount / kPlayBarCount; + float maxVal = 0.0f; + for (int j = startIdx; j < endIdx && j < sampleCount; j++) { + float v = state.playWaveform[j]; + if (v < 0.0f) v = 0.0f; + if (v > 1.0f) v = 1.0f; + if (v > maxVal) maxVal = v; + } + int h = static_cast(maxVal * maxBarH); + if (h < 1) h = 1; + lv_obj_set_size(playWaveBars_[i], 5, h); + lv_obj_set_pos(playWaveBars_[i], 20 + i * 7, baseY - h); } - lv_obj_invalidate(playWaveLine_); } - // Progress line + // Arrow and time position float dur = state.playbackDuration; float pos = state.playbackPosition; + int arrowX = 20; if (dur > 0.0f) { float ratio = pos / dur; if (ratio < 0.0f) ratio = 0.0f; if (ratio > 1.0f) ratio = 1.0f; - int px = static_cast(ratio * 280.0f); - playProgressPoints_[0].x = px; - playProgressPoints_[0].y = 0; - playProgressPoints_[1].x = px; - playProgressPoints_[1].y = 50; - lv_obj_invalidate(playProgressLine_); + constexpr int totalW = kPlayBarCount * 7 - 2; // 278 + arrowX = 20 + static_cast(ratio * totalW); } + lv_obj_set_pos(playProgressArrow_, arrowX - 10, 82); + lv_obj_set_pos(playProgressTime_, arrowX - 25, 95); + + int pmin = static_cast(pos) / 60; + int psec = static_cast(pos) % 60; + char timeBuf[16]; + snprintf(timeBuf, sizeof(timeBuf), "%02d:%02d", pmin, psec); + lv_label_set_text(playProgressTime_, timeBuf); } // Update bottom labels in case sample rate or speed changed @@ -468,8 +515,8 @@ void UiRecorder::update(const RecorderState& state) { switch (state.state) { case AppState::Idle: currentPage_ = UiPage::Home; break; - case AppState::Recording: currentPage_ = UiPage::Recording; break; - case AppState::RecPaused: currentPage_ = UiPage::RecPaused; break; + case AppState::Recording: currentPage_ = UiPage::Home; break; + case AppState::RecPaused: currentPage_ = UiPage::Home; break; case AppState::SaveConfirm: currentPage_ = UiPage::SaveConfirm; break; case AppState::FileList: currentPage_ = UiPage::FileList; break; case AppState::Playing: currentPage_ = UiPage::Playback; isPlaybackPaused_ = false; break; @@ -498,24 +545,24 @@ void UiRecorder::onKeyPressed(uint32_t key_code, bool isRepeat) switch (key_code) { case KEY_F4: if (taEdit_) { - lv_textarea_set_text(taEdit_, editOriginalName_.c_str()); - lv_obj_t* label = lv_textarea_get_label(taEdit_); - if (label) { - lv_label_set_text_selection_start(label, 0); - lv_label_set_text_selection_end(label, editOriginalName_.length()); - } + lv_textarea_cursor_left(taEdit_); + lv_textarea_clear_selection(taEdit_); } return; case KEY_F5: if (taEdit_) { - lv_textarea_cursor_left(taEdit_); + lv_textarea_cursor_right(taEdit_); lv_textarea_clear_selection(taEdit_); } return; case KEY_F6: if (taEdit_) { - lv_textarea_cursor_right(taEdit_); - lv_textarea_clear_selection(taEdit_); + lv_textarea_set_text(taEdit_, editOriginalName_.c_str()); + lv_obj_t* label = lv_textarea_get_label(taEdit_); + if (label) { + lv_label_set_text_selection_start(label, 0); + lv_label_set_text_selection_end(label, editOriginalName_.length()); + } } return; case KEY_BACKSPACE: @@ -549,19 +596,22 @@ void UiRecorder::onKeyPressed(uint32_t key_code, bool isRepeat) case KEY_F8: btnIndex = 4; break; case KEY_ENTER: case KEY_KPENTER: - if (currentPage_ == UiPage::Home) btnIndex = 2; // Rec - else if (currentPage_ == UiPage::Recording) btnIndex = 3; // Done - else if (currentPage_ == UiPage::RecPaused) btnIndex = 3; // Done - else if (currentPage_ == UiPage::FileList) btnIndex = 2; // Play - else if (currentPage_ == UiPage::Playback) btnIndex = 2; // Pause/Play - else if (currentPage_ == UiPage::SaveConfirm) btnIndex = 3; // Save + if (currentPage_ == UiPage::Home) btnIndex = 2; + else if (currentPage_ == UiPage::FileList) btnIndex = 2; + else if (currentPage_ == UiPage::Playback) btnIndex = 2; + else if (currentPage_ == UiPage::SaveConfirm) btnIndex = 3; break; case KEY_ESC: - if (currentPage_ == UiPage::Home) { + if (currentPage_ == UiPage::Home && lastState_.state == AppState::Idle) { if (actionHandler_) actionHandler_("quit"); return; + } else if (currentPage_ == UiPage::Home) { + btnIndex = 0; // Exit + } else if (currentPage_ == UiPage::Playback) { + btnIndex = 0; // Exit + } else { + btnIndex = 4; // Exit for FileList / SaveConfirm } - btnIndex = 4; // Exit break; case KEY_LEFT: if (currentPage_ == UiPage::FileList) { btnIndex = 0; break; } @@ -570,7 +620,7 @@ void UiRecorder::onKeyPressed(uint32_t key_code, bool isRepeat) if (currentPage_ == UiPage::FileList) { btnIndex = 1; break; } return; case KEY_P: - if (currentPage_ == UiPage::Recording || currentPage_ == UiPage::RecPaused) btnIndex = 2; + if (currentPage_ == UiPage::Home && (lastState_.state == AppState::Recording || lastState_.state == AppState::RecPaused)) btnIndex = 2; else if (currentPage_ == UiPage::Playback) btnIndex = 2; break; case KEY_SPACE: @@ -612,22 +662,20 @@ void UiRecorder::handleActionForPage(UiPage page, int btn) ActionMap map = {"", "", "", "", ""}; switch (page) { case UiPage::Home: - map = {"list", "cycle_rate", "toggle_record", "", "quit"}; + if (lastState_.state == AppState::Idle) { + map = {"quit", "", "toggle_record", "cycle_rate", "list"}; + } else { + map = {"exit", "done", "toggle_pause", "cycle_rate", "list"}; + } break; case UiPage::FileList: map = {"prev_file", "next_file", "toggle_play", "rename", "exit"}; break; - case UiPage::Recording: - map = {"", "", "toggle_pause", "done", "exit"}; - break; - case UiPage::RecPaused: - map = {"", "", "toggle_pause", "done", "exit"}; - break; case UiPage::SaveConfirm: - map = {"undo", "left", "right", "save", "exit"}; + map = {"left", "right", "undo", "save", "exit"}; break; case UiPage::Playback: - map = {"speed", "seek_back", "toggle_pause", "seek_fwd", "exit"}; + map = {"exit", "speed", "toggle_pause", "seek_back", "seek_fwd"}; break; } From 95d9ff25df0f3a635b0a04dc258314e2a6fee455 Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Wed, 3 Jun 2026 18:32:40 +0800 Subject: [PATCH 11/11] Compass: add custom icon font and update UI --- projects/Compass/config_defaults.mk | 3 + projects/Compass/main/SConstruct | 38 ++++++- projects/Compass/main/include/app_font.h | 30 ++++++ projects/Compass/main/include/ui_compass.h | 11 +- projects/Compass/main/src/app_font.cpp | 114 +++++++++++++++++++++ projects/Compass/main/src/main.cpp | 2 +- projects/Compass/main/src/ui_compass.cpp | 106 +++++++++++++------ 7 files changed, 269 insertions(+), 35 deletions(-) create mode 100644 projects/Compass/main/include/app_font.h create mode 100644 projects/Compass/main/src/app_font.cpp diff --git a/projects/Compass/config_defaults.mk b/projects/Compass/config_defaults.mk index b1de7ee1..cbed6077 100644 --- a/projects/Compass/config_defaults.mk +++ b/projects/Compass/config_defaults.mk @@ -11,3 +11,6 @@ CONFIG_V9_5_LV_FONT_MONTSERRAT_16=y CONFIG_V9_5_LV_FONT_MONTSERRAT_26=y CONFIG_V9_5_LV_FONT_DEFAULT_MONTSERRAT_14=y +CONFIG_V9_5_LV_USE_FREETYPE=y +CONFIG_V9_5_LV_FREETYPE_CACHE_FT_GLYPH_CNT=64 + diff --git a/projects/Compass/main/SConstruct b/projects/Compass/main/SConstruct index c1a3215c..0763925c 100644 --- a/projects/Compass/main/SConstruct +++ b/projects/Compass/main/SConstruct @@ -12,7 +12,7 @@ if "CONFIG_TOOLCHAIN_PREFIX" in os.environ: SRCS = Glob("src/*.c*") INCLUDE = [ADir("."), ADir("include")] PRIVATE_INCLUDE = [] -REQUIREMENTS = ["lvgl_component", "pthread"] +REQUIREMENTS = ["lvgl_component", "pthread", "freetype"] if "CONFIG_V9_5_LV_USE_SDL" not in os.environ: REQUIREMENTS += ["input", "xkbcommon", "udev"] STATIC_LIB = [] @@ -39,6 +39,42 @@ lvgl_component = list(filter(lambda x: x["target"] == "lvgl_component", env["COM if MULTIARCH_INCLUDE: for component in env["COMPONENTS"]: component["INCLUDE"] += [MULTIARCH_INCLUDE] + +# FreeType support +freetype_includes = [] +freetype_link_paths = [] +if "CONFIG_TOOLCHAIN_SYSROOT" in os.environ: + sysroot = os.environ["CONFIG_TOOLCHAIN_SYSROOT"].strip('"') + ft_inc = os.path.join(sysroot, "usr", "include", "freetype2") + ft_lib = os.path.join(sysroot, "usr", "lib", "aarch64-linux-gnu") + if os.path.isdir(ft_inc): + freetype_includes += [ft_inc] + if os.path.isdir(ft_lib): + freetype_link_paths += [ft_lib] + +# Also check project local static_lib (for cross-compilation or local sysroot) +local_static_lib = os.path.join(os.path.dirname(str(Dir('.').srcnode())), "static_lib") +local_ft_inc = os.path.join(local_static_lib, "usr", "include", "freetype2") +local_ft_lib = os.path.join(local_static_lib, "usr", "lib", "aarch64-linux-gnu") +if os.path.isdir(local_ft_inc): + freetype_includes += [local_ft_inc] +if os.path.isdir(local_ft_lib): + freetype_link_paths += [local_ft_lib] + +# Fallback to system paths +if not freetype_includes: + for ft_inc in ["/usr/include/freetype2", "/usr/local/include/freetype2"]: + if os.path.exists(ft_inc): + freetype_includes += [ft_inc] +if not freetype_link_paths: + for ft_lib in ["/usr/lib", "/usr/local/lib"]: + if os.path.exists(ft_lib): + freetype_link_paths += [ft_lib] + +for component in env["COMPONENTS"]: + component["INCLUDE"] += freetype_includes + component["LINK_SEARCH_PATH"] += freetype_link_paths + if "CONFIG_V9_5_LV_USE_SDL" in os.environ: lvgl_component["REQUIREMENTS"] += ["SDL2"] lvgl_component["INCLUDE"] += ["/usr/include/SDL2"] diff --git a/projects/Compass/main/include/app_font.h b/projects/Compass/main/include/app_font.h new file mode 100644 index 00000000..6c84a742 --- /dev/null +++ b/projects/Compass/main/include/app_font.h @@ -0,0 +1,30 @@ +#pragma once + +#include "lvgl/lvgl.h" + +namespace ui::font { + +/* codemap used by font 'assets/svgfont.ttf' (Unicode Private Use Area) */ +/* C++ compiler automatically converts \uXXXX to correct UTF-8 bytes */ +inline constexpr const char* ICON_EXIT = "\uEA01"; // .svgfont-exit +inline constexpr const char* ICON_FAST_FORWARD = "\uEA02"; // .svgfont-fast_forward +inline constexpr const char* ICON_FAST_REWIND = "\uEA03"; // .svgfont-fast_rewind +inline constexpr const char* ICON_LIST = "\uEA04"; // .svgfont-list +inline constexpr const char* ICON_PAUSE = "\uEA05"; // .svgfont-pause +inline constexpr const char* ICON_PLAY = "\uEA06"; // .svgfont-play +inline constexpr const char* ICON_RECORD = "\uEA07"; // .svgfont-record +inline constexpr const char* ICON_SAMPLE_RATE = "\uEA08"; // .svgfont-sample_rate +inline constexpr const char* ICON_SPEED = "\uEA09"; // .svgfont-speed +inline constexpr const char* ICON_STOP = "\uEA0A"; // .svgfont-stop + +class AppFont { +public: + static void init(); + static void deinit(); + static lv_font_t* svgfont(int32_t size); + +private: + AppFont() = delete; +}; + +} // namespace ui::font diff --git a/projects/Compass/main/include/ui_compass.h b/projects/Compass/main/include/ui_compass.h index 536f1cce..2d8c4bee 100644 --- a/projects/Compass/main/include/ui_compass.h +++ b/projects/Compass/main/include/ui_compass.h @@ -1,6 +1,7 @@ #pragma once #include "compass_app.h" +#include "app_font.h" #include "lvgl/lvgl.h" #include #include @@ -19,17 +20,23 @@ class UiCompass : public ICompassView { private: void buildUi(lv_obj_t* parent); + void createStatusBar(lv_obj_t* parent); + void createBottomBar(lv_obj_t* parent); // Layout constants static constexpr int kScreenW = 320; static constexpr int kScreenH = 170; static constexpr int kDiscDia = 100; static constexpr int kCompassImgSize = 120; - static constexpr int kBottomH = 20; + static constexpr int kStatusH = 30; + static constexpr int kBottomH = 25; static constexpr int kBtnW = kScreenW / 5; // 64 lv_obj_t* parent_ = nullptr; + // Status bar + lv_obj_t* lblStatusText_ = nullptr; + // Left panel: Compass lv_obj_t* lblCompassTitle_ = nullptr; lv_obj_t* compassDisc_ = nullptr; @@ -44,8 +51,8 @@ class UiCompass : public ICompassView { lv_obj_t* lblGyr_ = nullptr; // Bottom bar (5-segment like Recorder) - lv_obj_t* bottomBar_ = nullptr; std::array lblBottomBtns_{}; + std::array lblBottomIndicators_{}; // State cache CompassState lastState_{}; diff --git a/projects/Compass/main/src/app_font.cpp b/projects/Compass/main/src/app_font.cpp new file mode 100644 index 00000000..e066f270 --- /dev/null +++ b/projects/Compass/main/src/app_font.cpp @@ -0,0 +1,114 @@ +#include "app_font.h" + +#include +#include +#include +#include + +namespace ui::font { +namespace { + +struct FontSlot { + int32_t size = 0; + lv_font_t* font = nullptr; +}; + +std::array slots{}; +bool initialized = false; + +std::vector candidate_paths() +{ + return { + "assets/svgfont.ttf", + "./assets/svgfont.ttf", + "../assets/svgfont.ttf", + "../../assets/svgfont.ttf", + }; +} + +void log_attempt(const char* path, bool success) +{ + if (success) { + printf("[AppFont] svgfont loaded from: %s\n", path); + } else { + printf("[AppFont] svgfont failed to load from: %s\n", path); + } +} + +lv_font_t* fallback_font() +{ + return const_cast(&lv_font_montserrat_12); +} + +} // namespace + +void AppFont::init() +{ + if (initialized) { + return; + } + slots = {}; + initialized = true; +} + +void AppFont::deinit() +{ +#if LV_USE_FREETYPE + for (auto& s : slots) { + if (s.font) { + lv_freetype_font_delete(s.font); + } + s.font = nullptr; + s.size = 0; + } +#endif + initialized = false; +} + +lv_font_t* AppFont::svgfont(int32_t size) +{ + init(); + if (size <= 0) { + size = 12; + } + + // Cache hit + for (auto& s : slots) { + if (s.font && s.size == size) { + return s.font; + } + } + + // Find empty slot and load + for (auto& s : slots) { + if (!s.font) { +#if LV_USE_FREETYPE + const auto paths = candidate_paths(); + printf("[AppFont] Trying to load svgfont (size=%d), candidates=%zu\n", + size, paths.size()); + for (const auto& p : paths) { + s.font = lv_freetype_font_create( + p.c_str(), + LV_FREETYPE_FONT_RENDER_MODE_BITMAP, + static_cast(size), + LV_FREETYPE_FONT_STYLE_NORMAL); + log_attempt(p.c_str(), s.font != nullptr); + if (s.font) { + s.size = size; + return s.font; + } + } + printf("[AppFont] All svgfont candidates failed, falling back to montserrat_12\n"); +#else + printf("[AppFont] LV_USE_FREETYPE is disabled, falling back to montserrat_12\n"); + (void)candidate_paths; +#endif + return fallback_font(); + } + } + + printf("[AppFont] svgfont cache is full, falling back to montserrat_12\n"); + return fallback_font(); +} + +} // namespace ui::font diff --git a/projects/Compass/main/src/main.cpp b/projects/Compass/main/src/main.cpp index ca064323..f7b0fbdb 100644 --- a/projects/Compass/main/src/main.cpp +++ b/projects/Compass/main/src/main.cpp @@ -225,7 +225,7 @@ int main() g_root = lv_screen_active(); lv_obj_set_size(g_root, kScreenWidth, kScreenHeight); lv_obj_clear_flag(g_root, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_set_style_bg_color(g_root, lv_color_hex(0xE8E8EC), 0); + lv_obj_set_style_bg_color(g_root, lv_color_hex(0x000000), 0); lv_obj_set_style_bg_opa(g_root, LV_OPA_COVER, 0); g_ui = &ui; diff --git a/projects/Compass/main/src/ui_compass.cpp b/projects/Compass/main/src/ui_compass.cpp index 817eb449..7f3f4694 100644 --- a/projects/Compass/main/src/ui_compass.cpp +++ b/projects/Compass/main/src/ui_compass.cpp @@ -13,6 +13,16 @@ namespace { constexpr int kScreenWidth = 320; constexpr int kScreenHeight = 170; +// Colors (dark theme, matching Recorder) +const lv_color_t kColorBg = lv_color_hex(0x000000); +const lv_color_t kColorText = lv_color_hex(0xFFFFFF); +const lv_color_t kColorTextGray = lv_color_hex(0xAAAAAA); +const lv_color_t kColorLevelDisc = lv_color_hex(0x222222); +const lv_color_t kColorLevelBorder= lv_color_hex(0x555555); +const lv_color_t kColorCenterDot = lv_color_hex(0x888888); +const lv_color_t kColorIconList = lv_color_hex(0x33CC33); +const lv_color_t kColorIconExit = lv_color_hex(0xFF0000); + } // namespace void UiCompass::init(lv_obj_t* parent) @@ -28,46 +38,51 @@ void UiCompass::setActionHandler(std::function handler void UiCompass::buildUi(lv_obj_t* parent) { + lv_obj_set_size(parent, kScreenW, kScreenH); + lv_obj_clear_flag(parent, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_bg_color(parent, kColorBg, 0); + lv_obj_set_style_bg_opa(parent, LV_OPA_COVER, 0); + /* ---------- 左侧 Compass 区域 ---------- */ lblCompassTitle_ = lv_label_create(parent); lv_label_set_text(lblCompassTitle_, "Compass"); lv_obj_set_style_text_font(lblCompassTitle_, &lv_font_montserrat_14, 0); - lv_obj_set_style_text_color(lblCompassTitle_, lv_color_hex(0x333333), 0); + lv_obj_set_style_text_color(lblCompassTitle_, kColorText, 0); lv_obj_set_size(lblCompassTitle_, 160, 16); lv_obj_set_style_text_align(lblCompassTitle_, LV_TEXT_ALIGN_CENTER, 0); - lv_obj_set_pos(lblCompassTitle_, 0, 2); + lv_obj_set_pos(lblCompassTitle_, 0, kStatusH - 20); compassDisc_ = lv_img_create(parent); lv_img_set_src(compassDisc_, &img_compass_disc); - lv_obj_set_pos(compassDisc_, 20, 22); + lv_obj_set_pos(compassDisc_, 20, kStatusH + 2); lv_img_set_pivot(compassDisc_, kCompassImgSize / 2, kCompassImgSize / 2); lblYaw_ = lv_label_create(parent); lv_label_set_text(lblYaw_, "---"); lv_obj_set_style_text_font(lblYaw_, &lv_font_montserrat_12, 0); - lv_obj_set_style_text_color(lblYaw_, lv_color_hex(0x333333), 0); + lv_obj_set_style_text_color(lblYaw_, kColorText, 0); lv_obj_set_size(lblYaw_, 160, 14); lv_obj_set_style_text_align(lblYaw_, LV_TEXT_ALIGN_CENTER, 0); - lv_obj_set_pos(lblYaw_, 0, 128); + lv_obj_set_pos(lblYaw_, 0, kScreenH - kBottomH - 24); /* ---------- 右侧 IMU 区域 ---------- */ lblImuTitle_ = lv_label_create(parent); lv_label_set_text(lblImuTitle_, "IMU"); lv_obj_set_style_text_font(lblImuTitle_, &lv_font_montserrat_14, 0); - lv_obj_set_style_text_color(lblImuTitle_, lv_color_hex(0x333333), 0); + lv_obj_set_style_text_color(lblImuTitle_, kColorText, 0); lv_obj_set_size(lblImuTitle_, 160, 16); lv_obj_set_style_text_align(lblImuTitle_, LV_TEXT_ALIGN_CENTER, 0); - lv_obj_set_pos(lblImuTitle_, 160, 2); + lv_obj_set_pos(lblImuTitle_, 160, kStatusH - 20); levelDisc_ = lv_obj_create(parent); lv_obj_remove_style_all(levelDisc_); lv_obj_set_size(levelDisc_, kDiscDia, kDiscDia); - lv_obj_set_pos(levelDisc_, 190, 22); + lv_obj_set_pos(levelDisc_, 190, kStatusH + 2); lv_obj_set_style_radius(levelDisc_, LV_RADIUS_CIRCLE, 0); - lv_obj_set_style_bg_color(levelDisc_, lv_color_hex(0xDCDCDC), 0); + lv_obj_set_style_bg_color(levelDisc_, kColorLevelDisc, 0); lv_obj_set_style_bg_opa(levelDisc_, LV_OPA_COVER, 0); lv_obj_set_style_border_width(levelDisc_, 2, 0); - lv_obj_set_style_border_color(levelDisc_, lv_color_hex(0x999999), 0); + lv_obj_set_style_border_color(levelDisc_, kColorLevelBorder, 0); lv_obj_set_style_border_opa(levelDisc_, LV_OPA_COVER, 0); lv_obj_set_style_pad_all(levelDisc_, 0, 0); lv_obj_clear_flag(levelDisc_, LV_OBJ_FLAG_SCROLLABLE); @@ -77,7 +92,7 @@ void UiCompass::buildUi(lv_obj_t* parent) lv_obj_set_size(centerDot_, 8, 8); lv_obj_set_pos(centerDot_, 46, 46); lv_obj_set_style_radius(centerDot_, LV_RADIUS_CIRCLE, 0); - lv_obj_set_style_bg_color(centerDot_, lv_color_hex(0x777777), 0); + lv_obj_set_style_bg_color(centerDot_, kColorCenterDot, 0); lv_obj_set_style_bg_opa(centerDot_, LV_OPA_COVER, 0); lv_obj_clear_flag(centerDot_, LV_OBJ_FLAG_SCROLLABLE); @@ -96,42 +111,66 @@ void UiCompass::buildUi(lv_obj_t* parent) lblAcc_ = lv_label_create(parent); lv_label_set_text(lblAcc_, "ACC: --- --- ---"); lv_obj_set_style_text_font(lblAcc_, &lv_font_montserrat_12, 0); - lv_obj_set_style_text_color(lblAcc_, lv_color_hex(0x555555), 0); + lv_obj_set_style_text_color(lblAcc_, kColorTextGray, 0); lv_obj_set_size(lblAcc_, 160, 12); lv_obj_set_style_text_align(lblAcc_, LV_TEXT_ALIGN_CENTER, 0); - lv_obj_set_pos(lblAcc_, 160, 126); + lv_obj_set_pos(lblAcc_, 160, kScreenH - kBottomH - 36); lblGyr_ = lv_label_create(parent); lv_label_set_text(lblGyr_, "GYR: --- --- ---"); lv_obj_set_style_text_font(lblGyr_, &lv_font_montserrat_12, 0); - lv_obj_set_style_text_color(lblGyr_, lv_color_hex(0x555555), 0); + lv_obj_set_style_text_color(lblGyr_, kColorTextGray, 0); lv_obj_set_size(lblGyr_, 160, 12); lv_obj_set_style_text_align(lblGyr_, LV_TEXT_ALIGN_CENTER, 0); - lv_obj_set_pos(lblGyr_, 160, 138); - - /* ---------- 底部栏(五等分,参考 Recorder) ---------- */ - bottomBar_ = lv_obj_create(parent); - lv_obj_remove_style_all(bottomBar_); - lv_obj_set_size(bottomBar_, kScreenW, kBottomH); - lv_obj_set_pos(bottomBar_, 0, kScreenH - kBottomH); - lv_obj_set_style_bg_color(bottomBar_, lv_color_hex(0xD8D8E0), 0); - lv_obj_set_style_bg_opa(bottomBar_, LV_OPA_COVER, 0); - lv_obj_set_style_pad_all(bottomBar_, 0, 0); - lv_obj_clear_flag(bottomBar_, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_pos(lblGyr_, 160, kScreenH - kBottomH - 24); + + createStatusBar(parent); + createBottomBar(parent); +} + +void UiCompass::createStatusBar(lv_obj_t* parent) +{ + lblStatusText_ = lv_label_create(parent); + lv_obj_set_pos(lblStatusText_, 0, 6); + lv_obj_set_width(lblStatusText_, kScreenW); + lv_obj_set_style_text_font(lblStatusText_, &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblStatusText_, kColorText, 0); + lv_obj_set_style_text_align(lblStatusText_, LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_text(lblStatusText_, "Compass"); +} +void UiCompass::createBottomBar(lv_obj_t* parent) +{ for (int i = 0; i < 5; i++) { - lblBottomBtns_[i] = lv_label_create(bottomBar_); - lv_obj_set_pos(lblBottomBtns_[i], i * kBtnW, 0); + lblBottomBtns_[i] = lv_label_create(parent); + lv_obj_set_pos(lblBottomBtns_[i], i * kBtnW, kScreenH - kBottomH - 4); lv_obj_set_size(lblBottomBtns_[i], kBtnW, kBottomH); lv_obj_set_style_text_font(lblBottomBtns_[i], &lv_font_montserrat_12, 0); - lv_obj_set_style_text_color(lblBottomBtns_[i], lv_color_hex(0x333333), 0); + lv_obj_set_style_text_color(lblBottomBtns_[i], kColorText, 0); lv_obj_set_style_text_align(lblBottomBtns_[i], LV_TEXT_ALIGN_CENTER, 0); lv_label_set_text(lblBottomBtns_[i], "--"); - lv_obj_set_style_pad_top(lblBottomBtns_[i], 4, 0); + lv_obj_set_style_pad_top(lblBottomBtns_[i], 0, 0); + lv_obj_add_flag(lblBottomBtns_[i], LV_OBJ_FLAG_OVERFLOW_VISIBLE); + } + + for (int i = 0; i < 5; i++) { + lblBottomIndicators_[i] = lv_label_create(parent); + lv_obj_set_pos(lblBottomIndicators_[i], i * kBtnW, kScreenH - 12); + lv_obj_set_size(lblBottomIndicators_[i], kBtnW, 12); + lv_obj_set_style_text_font(lblBottomIndicators_[i], &lv_font_montserrat_12, 0); + lv_obj_set_style_text_color(lblBottomIndicators_[i], kColorText, 0); + lv_obj_set_style_text_align(lblBottomIndicators_[i], LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_text(lblBottomIndicators_[i], "|"); } - lv_label_set_text(lblBottomBtns_[0], "Cal"); - lv_label_set_text(lblBottomBtns_[2], "Exit"); + // Set icons: F4=List (calibrate), F6=Exit + lv_obj_set_style_text_font(lblBottomBtns_[0], ui::font::AppFont::svgfont(16), 0); + lv_obj_set_style_text_color(lblBottomBtns_[0], kColorIconList, 0); + lv_label_set_text(lblBottomBtns_[0], ui::font::ICON_LIST); + + lv_obj_set_style_text_font(lblBottomBtns_[2], ui::font::AppFont::svgfont(16), 0); + lv_obj_set_style_text_color(lblBottomBtns_[2], kColorIconExit, 0); + lv_label_set_text(lblBottomBtns_[2], ui::font::ICON_EXIT); } void UiCompass::update(const CompassState& state) @@ -140,6 +179,11 @@ void UiCompass::update(const CompassState& state) char buf[128]; + /* Status bar */ + if (lblStatusText_) { + lv_label_set_text(lblStatusText_, state.statusText.c_str()); + } + /* 偏航角 + 方位 */ if (lblYaw_) { const char* dir = "";