From 1ded9abdb795fb47a23467d4f3fbf546d8286caf Mon Sep 17 00:00:00 2001 From: Pavel Borcin Date: Mon, 19 Aug 2024 10:05:34 +0200 Subject: [PATCH] add qma6100p accelerometer --- .github/ci/update_readme_dependencies.py | 2 +- .github/workflows/upload_component.yml | 2 +- bsp/esp32_s3_eye/README.md | 2 +- bsp/esp32_s3_eye/idf_component.yml | 5 + bsp/esp32_s3_eye/include/bsp/esp32_s3_eye.h | 2 +- components/qma6100p/CMakeLists.txt | 8 + components/qma6100p/README.md | 34 ++ components/qma6100p/idf_component.yml | 6 + components/qma6100p/include/qma6100p.h | 339 +++++++++++++ components/qma6100p/license.txt | 202 ++++++++ components/qma6100p/qma6100p.c | 471 ++++++++++++++++++ components/qma6100p/test_apps/CMakeLists.txt | 6 + .../qma6100p/test_apps/main/CMakeLists.txt | 1 + .../qma6100p/test_apps/main/idf_component.yml | 6 + .../test_apps/main/test_esp_acc_qma6100p.c | 114 +++++ .../qma6100p/test_apps/sdkconfig.defaults | 12 + test_apps/components/CMakeLists.txt | 2 +- 17 files changed, 1209 insertions(+), 5 deletions(-) create mode 100644 components/qma6100p/CMakeLists.txt create mode 100644 components/qma6100p/README.md create mode 100644 components/qma6100p/idf_component.yml create mode 100644 components/qma6100p/include/qma6100p.h create mode 100644 components/qma6100p/license.txt create mode 100644 components/qma6100p/qma6100p.c create mode 100644 components/qma6100p/test_apps/CMakeLists.txt create mode 100644 components/qma6100p/test_apps/main/CMakeLists.txt create mode 100644 components/qma6100p/test_apps/main/idf_component.yml create mode 100644 components/qma6100p/test_apps/main/test_esp_acc_qma6100p.c create mode 100644 components/qma6100p/test_apps/sdkconfig.defaults diff --git a/.github/ci/update_readme_dependencies.py b/.github/ci/update_readme_dependencies.py index 8d7d349f..5e7ca983 100755 --- a/.github/ci/update_readme_dependencies.py +++ b/.github/ci/update_readme_dependencies.py @@ -36,7 +36,7 @@ # 'AUDIO_SPEAKER' : r'', # 'AUDIO_MIC' : r'', 'SDCARD': r'(^idf$)', # SD card driver is native to ESP-IDF - 'IMU': r'(icm42670$|mpu6050$)', + 'IMU': r'(icm42670$|mpu6050$|qma6100p$)', 'LED': r'(led_indicator$|^idf$)', # Provided by led_indicator or idf 'BAT': r'(^idf$)', # Battery (ADC driver) is native to ESP-IDF 'CAMERA': r'(esp32-camera$)', # esp32-camera component diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml index 924661a2..a5aaaab4 100644 --- a/.github/workflows/upload_component.yml +++ b/.github/workflows/upload_component.yml @@ -18,7 +18,7 @@ jobs: with: directories: > bsp/esp32_azure_iot_kit;bsp/esp32_s2_kaluga_kit;bsp/esp_wrover_kit;bsp/esp-box;bsp/esp32_s3_usb_otg;bsp/esp32_s3_eye;bsp/esp32_s3_lcd_ev_board;bsp/esp32_s3_korvo_2;bsp/esp-box-lite;bsp/esp32_lyrat;bsp/esp32_c3_lcdkit;bsp/esp-box-3;bsp/esp_bsp_generic;bsp/esp32_s3_korvo_1;bsp/esp32_p4_function_ev_board;bsp/m5stack_core_s3;bsp/m5dial;bsp/m5stack_core_2;bsp/esp_bsp_devkit; - components/bh1750;components/ds18b20;components/es8311;components/es7210;components/fbm320;components/hts221;components/mag3110;components/mpu6050;components/esp_lvgl_port;components/icm42670; + components/bh1750;components/ds18b20;components/es8311;components/es7210;components/fbm320;components/hts221;components/mag3110;components/mpu6050;components/esp_lvgl_port;components/icm42670;components/qma6100p; components/lcd_touch/esp_lcd_touch;components/lcd_touch/esp_lcd_touch_ft5x06;components/lcd_touch/esp_lcd_touch_gt911;components/lcd_touch/esp_lcd_touch_tt21100;components/lcd_touch/esp_lcd_touch_gt1151;components/lcd_touch/esp_lcd_touch_cst816s; components/lcd/esp_lcd_gc9a01;components/lcd/esp_lcd_ili9341;components/lcd/esp_lcd_ra8875;components/lcd_touch/esp_lcd_touch_stmpe610;components/lcd/esp_lcd_sh1107;components/lcd/esp_lcd_st7796;components/lcd/esp_lcd_gc9503;components/lcd/esp_lcd_ssd1681;components/lcd/esp_lcd_ili9881c; components/io_expander/esp_io_expander;components/io_expander/esp_io_expander_tca9554;components/io_expander/esp_io_expander_tca95xx_16bit;components/io_expander/esp_io_expander_ht8574; diff --git a/bsp/esp32_s3_eye/README.md b/bsp/esp32_s3_eye/README.md index 3b141f8c..df2a13da 100644 --- a/bsp/esp32_s3_eye/README.md +++ b/bsp/esp32_s3_eye/README.md @@ -30,6 +30,6 @@ The ESP32-S3-EYE board consists of two parts: the main board (ESP32-S3-EYE-MB) t |AUDIO_SPEAKER| :x: | | | | AUDIO_MIC |:heavy_check_mark:| | | | SDCARD |:heavy_check_mark:| idf | >=4.4.5 | -| IMU | :x: | | | +| IMU |:heavy_check_mark:| [qma6100p](https://components.espressif.com/components/qma6100p) | 1.* | | CAMERA |:heavy_check_mark:| [espressif/esp32-camera](https://components.espressif.com/components/espressif/esp32-camera) | ^2.0.2 | diff --git a/bsp/esp32_s3_eye/idf_component.yml b/bsp/esp32_s3_eye/idf_component.yml index 19778829..efea657b 100644 --- a/bsp/esp32_s3_eye/idf_component.yml +++ b/bsp/esp32_s3_eye/idf_component.yml @@ -27,3 +27,8 @@ dependencies: esp_codec_dev: version: "^1,<1.2" public: true + + qma6100p: + version: "1.*" + override_path: "../../components/qma6100p" + public: true diff --git a/bsp/esp32_s3_eye/include/bsp/esp32_s3_eye.h b/bsp/esp32_s3_eye/include/bsp/esp32_s3_eye.h index ce8eea8f..df4e55ea 100644 --- a/bsp/esp32_s3_eye/include/bsp/esp32_s3_eye.h +++ b/bsp/esp32_s3_eye/include/bsp/esp32_s3_eye.h @@ -41,7 +41,7 @@ #define BSP_CAPS_AUDIO_SPEAKER 0 #define BSP_CAPS_AUDIO_MIC 1 #define BSP_CAPS_SDCARD 1 -#define BSP_CAPS_IMU 0 // There is an IMU, but not supported in this BSP +#define BSP_CAPS_IMU 1 #define BSP_CAPS_CAMERA 1 /************************************************************************************************** diff --git a/components/qma6100p/CMakeLists.txt b/components/qma6100p/CMakeLists.txt new file mode 100644 index 00000000..11c3feea --- /dev/null +++ b/components/qma6100p/CMakeLists.txt @@ -0,0 +1,8 @@ +idf_component_register( + SRCS "qma6100p.c" + INCLUDE_DIRS "include" + REQUIRES "driver" +) + +include(package_manager) +cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) \ No newline at end of file diff --git a/components/qma6100p/README.md b/components/qma6100p/README.md new file mode 100644 index 00000000..9718df4a --- /dev/null +++ b/components/qma6100p/README.md @@ -0,0 +1,34 @@ +# QMA6100P Driver Component + +[![Component Registry](https://components.espressif.com/components/espressif/qma6100p/badge.svg)](https://components.espressif.com/components/espressif/qma6100p) + +C driver for QST QMA6100P 3-axis accelerometer based on I2C communication. + +## Features + +- Get 3-axis accelerometer data, either raw or as floating point values. +- Configure accelerometer sensitivity. +- Support for QMA6100P interrupt generation when data ready (occurs each time a write to all sensor data registers has been completed). + +## Important Notes + +- Keep in mind that QMA6100P I2C address depends on the level of its AD0 pin (1) (0x12 when low, 0x13 when high). +- In order to receive QMA6100P interrupts, its INT pins (5, 6) must be connected to a GPIO on the ESP32. + +## Limitations + +- Only I2C communication is supported. +- If QMA6100P interrupts are used, it is recommended to not read data using I2C directly from the ISR. + +## Get Started + +This driver, along with many other components from this repository, can be used as a package from [Espressif's IDF Component Registry](https://components.espressif.com). To include this driver in your project, run the following idf.py from the project's root directory: + +``` + idf.py add-dependency "espressif/qma6100p" +``` + +Another option is to manually create a `idf_component.yml` file. You can find more about using .yml files for components from [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). + +## See Also +* [QMA6100P datasheet](https://www.qstcorp.com/upload/pdf/202203/13-52-20%20QMA6100P%20Preliminary%20Datasheet%20Rev.%20A1_SPImode03.pdf) diff --git a/components/qma6100p/idf_component.yml b/components/qma6100p/idf_component.yml new file mode 100644 index 00000000..c2949f95 --- /dev/null +++ b/components/qma6100p/idf_component.yml @@ -0,0 +1,6 @@ +version: "1.0.0" +description: I2C driver for QMA6100P accelerometer +url: https://github.com/espressif/esp-bsp/tree/master/components/qma6100p +dependencies: + idf : ">=4.0" + cmake_utilities: "0.*" diff --git a/components/qma6100p/include/qma6100p.h b/components/qma6100p/include/qma6100p.h new file mode 100644 index 00000000..5d18b642 --- /dev/null +++ b/components/qma6100p/include/qma6100p.h @@ -0,0 +1,339 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief qma6100p driver + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "driver/i2c.h" +#include "driver/gpio.h" + +#define QMA6100P_I2C_ADDRESS 0x12u /*!< I2C address with AD0 pin low */ +#define QMA6100P_I2C_ADDRESS_1 0x13u /*!< I2C address with AD0 pin high */ +#define QMA6100P_WHO_AM_I_VAL 0x90u + +typedef enum { + ACCE_FS_2G = 0b0001, /*!< Accelerometer full scale range is +/- 2g */ + ACCE_FS_4G = 0b0010, /*!< Accelerometer full scale range is +/- 4g */ + ACCE_FS_8G = 0b0100, /*!< Accelerometer full scale range is +/- 8g */ + ACCE_FS_16G = 0b1000, /*!< Accelerometer full scale range is +/- 16g */ + ACCE_FS_32G = 0b1111, /*!< Accelerometer full scale range is +/- 32g */ +} qma6100p_acce_fs_t; + +typedef enum { + INTERRUPT_PIN_ACTIVE_HIGH = 0, /*!< The qma6100p sets its INT pin HIGH on interrupt */ + INTERRUPT_PIN_ACTIVE_LOW = 1 /*!< The qma6100p sets its INT pin LOW on interrupt */ +} qma6100p_int_pin_active_level_t; + +typedef enum { + INTERRUPT_PIN_PUSH_PULL = 0, /*!< The qma6100p configures its INT pin as push-pull */ + INTERRUPT_PIN_OPEN_DRAIN = 1 /*!< The qma6100p configures its INT pin as open drain */ +} qma6100p_int_pin_mode_t; + +typedef enum { + INTERRUPT_NON_LATCH_MODE = 0, /*!< The qma6100p configures the INT to non-latch mode */ + INTERRUPT_LATCH_MODE = 1 /*!< The qma6100p configures the INT to latch mode */ +} qma6100p_int_latch_t; + +typedef enum { + INTERRUPT_CLEAR_ALL_INTERRUPTS = 0, /*!< INT_STATUS register bits are cleared */ + INTERRUPT_CLEAR_LATCHED = 1 /*!< INT_STATUS register bits are cleared only if latched */ +} qma6100p_int_clear_t; + +typedef gpio_isr_t qma6100p_isr_t; + +typedef struct { + gpio_num_t interrupt_pin; /*!< GPIO connected to qma6100p INT pin */ + qma6100p_int_pin_active_level_t active_level_int; /*!< Active level of qma6100p INT pin */ + qma6100p_int_pin_mode_t pin_mode_int; /*!< Push-pull or open drain mode for INT pin */ + qma6100p_int_latch_t interrupt_latch; /*!< The interrupt pulse behavior of INT pin */ + qma6100p_int_clear_t interrupt_clear_behavior; /*!< Interrupt status clear behavior */ + qma6100p_isr_t isr; /*!< Interrupt handler */ + uint8_t interrupt_sources; /*!< Sources; use QMA6100P_*_INT_BIT macros */ +} qma6100p_int_config_t; + +typedef enum { + FIFO_BYPASS_MODE = 0, + FIFO_FIFO_MODE = 1, + FIFO_STREAM_MODE = 2 +} qma6100p_fifo_mode_t; + +extern const uint8_t QMA6100P_DATA_RDY_INT_BIT; /*!< DATA READY interrupt bit */ +extern const uint8_t QMA6100P_FIFO_FULL_INT_BIT; /*!< FIFO Full interrupt bit */ +extern const uint8_t QMA6100P_FIFO_WM_INT_BIT; /*!< FIFO Watermark interrupt bit */ +extern const uint8_t QMA6100P_FIFO_OF_INT_BIT; /*!< FIFO Overflow interrupt bit */ +extern const uint8_t QMA6100P_ALL_INTERRUPTS; /*!< All interrupts supported by qma6100p */ + +typedef struct { + int16_t raw_acce_x; + int16_t raw_acce_y; + int16_t raw_acce_z; +} qma6100p_raw_acce_value_t; + +typedef struct { + float acce_x; + float acce_y; + float acce_z; +} qma6100p_acce_value_t; + +typedef void *qma6100p_handle_t; + +/** + * @brief Create and init sensor object and return a sensor handle + * + * @param port I2C port number + * @param dev_addr I2C device address of sensor + * + * @return + * - NULL Fail + * - Others Success + */ +qma6100p_handle_t qma6100p_create(i2c_port_t port, const uint16_t dev_addr); + +/** + * @brief Delete and release a sensor object + * + * @param sensor object handle of qma6100p + */ +void qma6100p_delete(qma6100p_handle_t sensor); + +/** + * @brief Get device identification of qma6100p + * + * @param sensor object handle of qma6100p + * @param deviceid a pointer of device ID + * + * @return + * - ESP_OK Success + * - ESP_FAIL Fail + */ +esp_err_t qma6100p_get_deviceid(qma6100p_handle_t sensor, uint8_t *const deviceid); + +/** + * @brief Load non volatile memory + * + * @param sensor object handle of qma6100p + * + * @return + * - ESP_OK Success + * - ESP_FAIL Fail + */ +esp_err_t qma6100p_nvm_load(qma6100p_handle_t sensor); + +/** + * @brief Wake up qma6100p + * + * @param sensor object handle of qma6100p + * + * @return + * - ESP_OK Success + * - ESP_FAIL Fail + */ +esp_err_t qma6100p_wake_up(qma6100p_handle_t sensor); + +/** + * @brief Enter sleep mode + * + * @param sensor object handle of qma6100p + * + * @return + * - ESP_OK Success + * - ESP_FAIL Fail + */ +esp_err_t qma6100p_sleep(qma6100p_handle_t sensor); + +/** + * @brief Set accelerometer full scale range + * + * @param sensor object handle of qma6100p + * @param acce_fs accelerometer full scale range + * + * @return + * - ESP_OK Success + * - ESP_FAIL Fail + */ +esp_err_t qma6100p_config(qma6100p_handle_t sensor, const qma6100p_acce_fs_t acce_fs); + +/** + * @brief Get accelerometer sensitivity + * + * @param sensor object handle of qma6100p + * @param acce_sensitivity accelerometer sensitivity + * + * @return + * - ESP_OK Success + * - ESP_FAIL Fail + */ +esp_err_t qma6100p_get_acce_sensitivity(qma6100p_handle_t sensor, float *const acce_sensitivity); + +/** + * @brief Configure INT pin behavior and setup target GPIO. + * + * @warning Not tested, implemented according to datasheet. + * + * @param sensor object handle of qma6100p + * @param interrupt_configuration qma6100p INT pin configuration parameters + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG A parameter is NULL or incorrect + * - ESP_FAIL Failed to configure INT pin on qma6100p + */ +esp_err_t qma6100p_config_interrupt(qma6100p_handle_t sensor, int int_num, const qma6100p_int_config_t *const interrupt_configuration); + +/** + * @brief Enable specific interrupts from qma6100p + * + * @param sensor object handle of qma6100p + * @param interrupt_sources bit mask with interrupt sources to enable + * + * This function does not disable interrupts not set in interrupt_sources. To disable + * specific qma6100p interrupts, use qma6100p_disable_interrupts(). + * + * To enable all qma6100p interrupts, pass qma6100p_ALL_INTERRUPTS as the argument + * for interrupt_sources. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG A parameter is NULL or not valid + * - ESP_FAIL Failed to enable interrupt sources on qma6100p + */ +esp_err_t qma6100p_enable_interrupts(qma6100p_handle_t sensor, uint8_t interrupt_sources); + +/** + * @brief Disable specific interrupts from qma6100p + * + * @param sensor object handle of qma6100p + * @param interrupt_sources bit mask with interrupt sources to disable + * + * This function does not enable interrupts not set in interrupt_sources. To enable + * specific qma6100p interrupts, use qma6100p_enable_interrupts(). + * + * To disable all qma6100p interrupts, pass qma6100p_ALL_INTERRUPTS as the + * argument for interrupt_sources. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG A parameter is NULL or not valid + * - ESP_FAIL Failed to enable interrupt sources on qma6100p + */ +esp_err_t qma6100p_disable_interrupts(qma6100p_handle_t sensor, uint8_t interrupt_sources); + +/** + * @brief Get the interrupt status of qma6100p + * + * @param sensor object handle of qma6100p + * @param out_intr_status[out] bit mask that is assigned a value representing the interrupts triggered by the qma6100p + * + * This function can be used by the qma6100p ISR to determine the source of + * the qma6100p interrupt that it is handling. + * + * After this function returns, the bits set in out_intr_status are + * the sources of the latest interrupt triggered by the qma6100p. For example, + * if QMA6100P_DATA_RDY_INT_BIT is set in out_intr_status, the last interrupt + * from the qma6100p was a DATA READY interrupt. + * + * The behavior of the INT_STATUS register of the qma6100p may change depending on + * the value of qma6100p_int_clear_t used on interrupt configuration. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG A parameter is NULL or not valid + * - ESP_FAIL Failed to retrieve interrupt status from qma6100p + */ +esp_err_t qma6100p_get_interrupt_status(qma6100p_handle_t sensor, uint8_t *const out_intr_status); + +/** + * @brief Determine if the last qma6100p interrupt was due to data ready. + * + * @param interrupt_status qma6100p interrupt status, obtained by invoking qma6100p_get_interrupt_status() + * + * @return + * - 0: The interrupt was not produced due to data ready + * - Any other positive integer: Interrupt was a DATA_READY interrupt + */ +extern uint8_t qma6100p_is_data_ready_interrupt(uint8_t interrupt_status); + +/** + * @brief Determine if the last qma6100p interrupt was triggered by a fifo overflow. + * + * @param interrupt_status qma6100p interrupt status, obtained by invoking qma6100p_get_interrupt_status() + * + * @return + * - 0: The interrupt is not a fifo overflow interrupt + * - Any other positive integer: Interrupt was triggered by a fifo overflow + */ +extern uint8_t qma6100p_is_fifo_overflow_interrupt(uint8_t interrupt_status); + +/** + * @brief Determine if the last qma6100p interrupt was triggered due to fifo full. + * + * @param interrupt_status qma6100p interrupt status, obtained by invoking qma6100p_get_interrupt_status() + * + * @return + * - 0: The interrupt is not a fifo full interrupt + * - Any other positive integer: Interrupt was triggered due to fifo full + */ +extern uint8_t qma6100p_is_fifo_full_interrupt(uint8_t interrupt_status); + +/** + * @brief Read raw accelerometer measurements + * + * @param sensor object handle of qma6100p + * @param raw_acce_value raw accelerometer measurements + * + * @return + * - ESP_OK Success + * - ESP_FAIL Fail + */ +esp_err_t qma6100p_get_raw_acce(qma6100p_handle_t sensor, qma6100p_raw_acce_value_t *const raw_acce_value); + +/** + * @brief Read accelerometer measurements + * + * @param sensor object handle of qma6100p + * @param acce_value accelerometer measurements + * + * @return + * - ESP_OK Success + * - ESP_FAIL Fail + */ +esp_err_t qma6100p_get_acce(qma6100p_handle_t sensor, qma6100p_acce_value_t *const acce_value); + +/** + * @brief Get fifo frame counter, 64 is full + * + * @param sensor object handle of qma6100p + * @param counter frame counter + * + * @return + * - ESP_OK Success + * - ESP_FAIL Fail + */ +esp_err_t qma6100p_get_fifo_frame_counter(qma6100p_handle_t sensor, uint8_t *counter); + +/** + * @brief Get fifo data pointer + * + * @param sensor object handle of qma6100p + * @param counter frame counter + * + * @return + * - ESP_OK Success + * - ESP_FAIL Fail + */ +esp_err_t qma6100p_get_fifo_data(qma6100p_handle_t sensor, uint8_t *data); + +#ifdef __cplusplus +} +#endif diff --git a/components/qma6100p/license.txt b/components/qma6100p/license.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/components/qma6100p/license.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/components/qma6100p/qma6100p.c b/components/qma6100p/qma6100p.c new file mode 100644 index 00000000..7cd5d607 --- /dev/null +++ b/components/qma6100p/qma6100p.c @@ -0,0 +1,471 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "esp_system.h" +#include "driver/i2c.h" +#include "qma6100p.h" + +#define QMA6100P_WHO_AM_I 0x00u +#define QMA6100P_ACCEL_CONFIG 0x0Fu +#define QMA6100P_ACCEL_XOUT_H 0x01u +#define QMA6100P_PWR_MGMT_1 0x11u +#define QMA6100P_NVM_LOAD 0x33u + +#define QMA6100P_INTR_PIN_CFG 0x20u +#define QMA6100P_INTR_CFG 0X21u +#define QMA6100P_INTR_ENABLE 0x16u +#define QMA6100P_INTR_TAP_EN 0x16u +#define QMA6100P_INTR_FIFO_EN 0x17u +#define QMA6100P_INTR_MOT_EN 0x18u + +// status 0x09 <-> 0x0D +#define QMA6100P_INTR_STATUS 0x09u +#define QMA6100P_INTR_FIFO_ST 0x0Bu + +// map 0x19 <-> 0x1C +#define QMA6100P_INT1_MAP_TAP 0x19u +#define QMA6100P_INT1_MAP_FIFO 0x1Au +#define QMA6100P_INT2_MAP_TAP 0x1Bu +#define QMA6100P_INT2_MAP_FIFO 0x1Cu + +#define QMA6100P_FIFO_FRAME_CTR 0x0Eu +#define QMA6100P_FIFO_DATA 0x3Fu +#define QMA6100P_FIFO_MODE 0x3Eu + + +const uint8_t QMA6100P_DATA_RDY_INT_BIT = (uint8_t) BIT4; +// FIFO full interrupt +const uint8_t QMA6100P_FIFO_FULL_INT_BIT = (uint8_t) BIT5; +// FIFO watermark interrupt +const uint8_t QMA6100P_FIFO_WM_INT_BIT = (uint8_t) BIT6; +const uint8_t QMA6100P_FIFO_OF_INT_BIT = (uint8_t) BIT7; +const uint8_t QMA6100P_ALL_INTERRUPTS = (QMA6100P_DATA_RDY_INT_BIT | QMA6100P_FIFO_FULL_INT_BIT | QMA6100P_FIFO_WM_INT_BIT | QMA6100P_FIFO_OF_INT_BIT); + + +typedef struct { + i2c_port_t bus; + gpio_num_t int_pin; + uint16_t dev_addr; + uint32_t counter; + float dt; /*!< delay time between two measurements, dt should be small (ms level) */ + struct timeval *timer; +} qma6100p_dev_t; + +static esp_err_t qma6100p_write(qma6100p_handle_t sensor, const uint8_t reg_start_addr, const uint8_t data_buf) +{ + qma6100p_dev_t *sens = (qma6100p_dev_t *) sensor; + esp_err_t ret; + + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + ret = i2c_master_start(cmd); + assert(ESP_OK == ret); + ret = i2c_master_write_byte(cmd, sens->dev_addr | I2C_MASTER_WRITE, true); + assert(ESP_OK == ret); + ret = i2c_master_write_byte(cmd, reg_start_addr, true); + assert(ESP_OK == ret); + ret = i2c_master_write_byte(cmd, data_buf, true); + assert(ESP_OK == ret); + ret = i2c_master_stop(cmd); + assert(ESP_OK == ret); + ret = i2c_master_cmd_begin(sens->bus, cmd, 1000 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + + return ret; +} + +static esp_err_t qma6100p_read(qma6100p_handle_t sensor, const uint8_t reg_start_addr, uint8_t *const data_buf, const uint8_t data_len) +{ + qma6100p_dev_t *sens = (qma6100p_dev_t *) sensor; + esp_err_t ret; + + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + ret = i2c_master_start(cmd); + assert(ESP_OK == ret); + ret = i2c_master_write_byte(cmd, sens->dev_addr | I2C_MASTER_WRITE, true); + assert(ESP_OK == ret); + ret = i2c_master_write_byte(cmd, reg_start_addr, true); + assert(ESP_OK == ret); + ret = i2c_master_start(cmd); + assert(ESP_OK == ret); + ret = i2c_master_write_byte(cmd, sens->dev_addr | I2C_MASTER_READ, true); + assert(ESP_OK == ret); + ret = i2c_master_read(cmd, data_buf, data_len, I2C_MASTER_LAST_NACK); + assert(ESP_OK == ret); + ret = i2c_master_stop(cmd); + assert(ESP_OK == ret); + ret = i2c_master_cmd_begin(sens->bus, cmd, 1000 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + + return ret; +} + +qma6100p_handle_t qma6100p_create(i2c_port_t port, const uint16_t dev_addr) +{ + qma6100p_dev_t *sensor = (qma6100p_dev_t *) calloc(1, sizeof(qma6100p_dev_t)); + sensor->bus = port; + sensor->dev_addr = dev_addr << 1; + sensor->counter = 0; + sensor->dt = 0; + sensor->timer = (struct timeval *) calloc(1, sizeof(struct timeval)); + return (qma6100p_handle_t) sensor; +} + +void qma6100p_delete(qma6100p_handle_t sensor) +{ + qma6100p_dev_t *sens = (qma6100p_dev_t *) sensor; + free(sens); +} + +esp_err_t qma6100p_get_deviceid(qma6100p_handle_t sensor, uint8_t *const deviceid) +{ + return qma6100p_read(sensor, QMA6100P_WHO_AM_I, deviceid, 1); +} + +esp_err_t qma6100p_nvm_load(qma6100p_handle_t sensor) +{ + esp_err_t ret; + uint8_t tmp; + ret = qma6100p_read(sensor, QMA6100P_NVM_LOAD, &tmp, 1); + if (ESP_OK != ret) { + return ret; + } + tmp |= BIT3; + ret = qma6100p_write(sensor, QMA6100P_NVM_LOAD, tmp); + return ret; +} + +esp_err_t qma6100p_wake_up(qma6100p_handle_t sensor) +{ + esp_err_t ret; + uint8_t tmp; + ret = qma6100p_read(sensor, QMA6100P_PWR_MGMT_1, &tmp, 1); + if (ESP_OK != ret) { + return ret; + } + tmp |= BIT7; + ret = qma6100p_write(sensor, QMA6100P_PWR_MGMT_1, tmp); + + ret = qma6100p_read(sensor, QMA6100P_PWR_MGMT_1, &tmp, 1); + return ret; +} + +esp_err_t qma6100p_config(qma6100p_handle_t sensor, const qma6100p_acce_fs_t acce_fs) +{ + uint8_t config_reg; + esp_err_t ret; + ret = qma6100p_read(sensor, QMA6100P_ACCEL_CONFIG, &config_reg, 1); + if (ESP_OK != ret) { + return ret; + } + config_reg = (config_reg & 0xf0) | (acce_fs & 0x0f); + return qma6100p_write(sensor, QMA6100P_ACCEL_CONFIG, config_reg); +} + +esp_err_t qma6100p_get_acce_sensitivity(qma6100p_handle_t sensor, float *const acce_sensitivity) +{ + esp_err_t ret; + uint8_t acce_fs; + ret = qma6100p_read(sensor, QMA6100P_ACCEL_CONFIG, &acce_fs, 1); + acce_fs &= 0b1111; + switch (acce_fs) { + case ACCE_FS_2G: + *acce_sensitivity = 4096; + break; + + case ACCE_FS_4G: + *acce_sensitivity = 2048; + break; + + case ACCE_FS_8G: + *acce_sensitivity = 1024; + break; + + case ACCE_FS_16G: + *acce_sensitivity = 512; + break; + + case ACCE_FS_32G: + *acce_sensitivity = 256; + break; + + default: + *acce_sensitivity = 4096; + break; + } + return ret; +} + +esp_err_t qma6100p_register_isr(qma6100p_handle_t sensor, const qma6100p_isr_t isr) +{ + esp_err_t ret; + qma6100p_dev_t *sensor_device = (qma6100p_dev_t *) sensor; + + if (NULL == sensor_device) { + return ESP_ERR_INVALID_ARG; + } + + gpio_install_isr_service(ESP_INTR_FLAG_EDGE); + + ret = gpio_isr_handler_add( + sensor_device->int_pin, + ((gpio_isr_t) * (isr)), + ((void *) sensor) + ); + + if (ESP_OK != ret) { + return ret; + } + + ret = gpio_intr_enable(sensor_device->int_pin); + + return ret; +} + +esp_err_t qma6100p_enable_interrupts(qma6100p_handle_t sensor, uint8_t interrupt_sources) +{ + esp_err_t ret; + uint8_t enabled_interrupts = 0x00; + + ret = qma6100p_read(sensor, QMA6100P_INTR_FIFO_EN, &enabled_interrupts, 1); + + if (ESP_OK != ret) { + return ret; + } + + if (enabled_interrupts != interrupt_sources) { + enabled_interrupts |= interrupt_sources; + ret = qma6100p_write(sensor, QMA6100P_INTR_FIFO_EN, enabled_interrupts); + } + + return ret; +} + +esp_err_t qma6100p_map_interrupts(qma6100p_handle_t sensor, int int_num, uint8_t interrupt_sources) +{ + esp_err_t ret; + int int_map_reg = 0; + + if (int_num == 0) { + int_map_reg = QMA6100P_INT1_MAP_FIFO; + } else if (int_num == 1) { + int_map_reg = QMA6100P_INT2_MAP_FIFO; + } else { + return ESP_ERR_INVALID_ARG; + } + + uint8_t int_map = 0; + ret = qma6100p_read(sensor, int_map_reg, &int_map, 1); + + if (ret != ESP_OK) { + return ret; + } + + int_map |= interrupt_sources; + + ret = qma6100p_write(sensor, int_map_reg, int_map); + + return ret; +} + +esp_err_t qma6100p_config_interrupt(qma6100p_handle_t sensor, int int_num, const qma6100p_int_config_t *const interrupt_configuration) +{ + esp_err_t ret = ESP_OK; + + if (NULL == interrupt_configuration) { + return ESP_ERR_INVALID_ARG; + } + + qma6100p_dev_t *sensor_device = (qma6100p_dev_t *) sensor; + + if (GPIO_IS_VALID_GPIO(interrupt_configuration->interrupt_pin)) { + // Set GPIO connected to qma6100p INT pin only when user configures interrupts. + sensor_device->int_pin = interrupt_configuration->interrupt_pin; + } else { + return ESP_ERR_INVALID_ARG; + } + + uint8_t int_pin_cfg = 0x00; + uint8_t int_cfg = 0x00; + + ret = qma6100p_read(sensor, QMA6100P_INTR_PIN_CFG, &int_pin_cfg, 1); + + if (ESP_OK != ret) { + return ret; + } + + ret = qma6100p_read(sensor, QMA6100P_INTR_CFG, &int_cfg, 1); + + if (ESP_OK != ret) { + return ret; + } + + if (int_num == 0) { + if (INTERRUPT_PIN_ACTIVE_LOW == interrupt_configuration->active_level_int) { + int_pin_cfg |= BIT0; + } + if (INTERRUPT_PIN_OPEN_DRAIN == interrupt_configuration->pin_mode_int) { + int_pin_cfg |= BIT1; + } + } else if (int_num == 1) { + if (INTERRUPT_PIN_ACTIVE_LOW == interrupt_configuration->active_level_int) { + int_pin_cfg |= BIT2; + } + if (INTERRUPT_PIN_OPEN_DRAIN == interrupt_configuration->pin_mode_int) { + int_pin_cfg |= BIT3; + } + } else { + return ESP_ERR_INVALID_ARG; + } + + if (INTERRUPT_LATCH_MODE == interrupt_configuration->interrupt_latch) { + int_cfg |= BIT0; + } + if (INTERRUPT_CLEAR_LATCHED == interrupt_configuration->interrupt_clear_behavior) { + int_cfg |= BIT7; + } + + ret = qma6100p_write(sensor, QMA6100P_INTR_PIN_CFG, int_pin_cfg); + + if (ESP_OK != ret) { + return ret; + } + + ret = qma6100p_write(sensor, QMA6100P_INTR_CFG, int_cfg); + + if (ESP_OK != ret) { + return ret; + } + + gpio_int_type_t gpio_intr_type; + + if (INTERRUPT_PIN_ACTIVE_LOW == interrupt_configuration->active_level_int) { + gpio_intr_type = GPIO_INTR_NEGEDGE; + } else { + gpio_intr_type = GPIO_INTR_POSEDGE; + } + + gpio_config_t int_gpio_config = { + .mode = GPIO_MODE_INPUT, + .intr_type = gpio_intr_type, + .pin_bit_mask = (BIT0 << interrupt_configuration->interrupt_pin) + }; + + ret = gpio_config(&int_gpio_config); + + if (ret != ESP_OK) { + return ret; + } + + ret = qma6100p_register_isr(sensor_device, interrupt_configuration->isr); + + if (ret != ESP_OK) { + return ret; + } + + ret = qma6100p_map_interrupts(sensor, int_num, interrupt_configuration->interrupt_sources); + + if (ret != ESP_OK) { + return ret; + } + + ret = qma6100p_enable_interrupts(sensor, interrupt_configuration->interrupt_sources); + + return ret; +} + +esp_err_t qma6100p_disable_interrupts(qma6100p_handle_t sensor, uint8_t interrupt_sources) +{ + esp_err_t ret; + uint8_t enabled_interrupts = 0x00; + + ret = qma6100p_read(sensor, QMA6100P_INTR_ENABLE, &enabled_interrupts, 1); + + if (ESP_OK != ret) { + return ret; + } + + if (0 != (enabled_interrupts & interrupt_sources)) { + enabled_interrupts &= (~interrupt_sources); + + ret = qma6100p_write(sensor, QMA6100P_INTR_ENABLE, enabled_interrupts); + } + + return ret; +} + +esp_err_t qma6100p_get_interrupt_status(qma6100p_handle_t sensor, uint8_t *const out_intr_status) +{ + esp_err_t ret; + + if (NULL == out_intr_status) { + ret = ESP_ERR_INVALID_ARG; + return ret; + } + + ret = qma6100p_read(sensor, QMA6100P_INTR_FIFO_ST, out_intr_status, 1); + + return ret; +} + +inline uint8_t qma6100p_is_data_ready_interrupt(uint8_t interrupt_status) +{ + return (QMA6100P_DATA_RDY_INT_BIT == (QMA6100P_DATA_RDY_INT_BIT & interrupt_status)); +} + +inline uint8_t qma6100p_is_fifo_overflow_interrupt(uint8_t interrupt_status) +{ + return (QMA6100P_FIFO_OF_INT_BIT == (QMA6100P_FIFO_OF_INT_BIT & interrupt_status)); +} + +inline uint8_t qma6100p_is_fifo_full_interrupt(uint8_t interrupt_status) +{ + return (QMA6100P_FIFO_FULL_INT_BIT == (QMA6100P_FIFO_FULL_INT_BIT & interrupt_status)); +} + +esp_err_t qma6100p_get_raw_acce(qma6100p_handle_t sensor, qma6100p_raw_acce_value_t *const raw_acce_value) +{ + uint8_t data_rd[6]; + esp_err_t ret = qma6100p_read(sensor, QMA6100P_ACCEL_XOUT_H, data_rd, sizeof(data_rd)); + + raw_acce_value->raw_acce_x = (int16_t)((data_rd[1] << 8) + (data_rd[0])) / 4; + raw_acce_value->raw_acce_y = (int16_t)((data_rd[3] << 8) + (data_rd[2])) / 4; + raw_acce_value->raw_acce_z = (int16_t)((data_rd[5] << 8) + (data_rd[4])) / 4; + return ret; +} + +esp_err_t qma6100p_get_acce(qma6100p_handle_t sensor, qma6100p_acce_value_t *const acce_value) +{ + esp_err_t ret; + float acce_sensitivity; + qma6100p_raw_acce_value_t raw_acce; + + ret = qma6100p_get_acce_sensitivity(sensor, &acce_sensitivity); + if (ret != ESP_OK) { + return ret; + } + ret = qma6100p_get_raw_acce(sensor, &raw_acce); + if (ret != ESP_OK) { + return ret; + } + + acce_value->acce_x = raw_acce.raw_acce_x / acce_sensitivity; + acce_value->acce_y = raw_acce.raw_acce_y / acce_sensitivity; + acce_value->acce_z = raw_acce.raw_acce_z / acce_sensitivity; + return ESP_OK; +} + +esp_err_t qma6100p_get_fifo_frame_counter(qma6100p_handle_t sensor, uint8_t *counter) +{ + return qma6100p_read(sensor, QMA6100P_FIFO_FRAME_CTR, counter, 1); +} + +esp_err_t qma6100p_get_fifo_data(qma6100p_handle_t sensor, uint8_t *data) +{ + return qma6100p_read(sensor, QMA6100P_FIFO_DATA, data, 1); +} diff --git a/components/qma6100p/test_apps/CMakeLists.txt b/components/qma6100p/test_apps/CMakeLists.txt new file mode 100644 index 00000000..498cfb42 --- /dev/null +++ b/components/qma6100p/test_apps/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) +set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_esp_acc_qma6100p) \ No newline at end of file diff --git a/components/qma6100p/test_apps/main/CMakeLists.txt b/components/qma6100p/test_apps/main/CMakeLists.txt new file mode 100644 index 00000000..3d134a4b --- /dev/null +++ b/components/qma6100p/test_apps/main/CMakeLists.txt @@ -0,0 +1 @@ +idf_component_register(SRCS "test_esp_acc_qma6100p.c") diff --git a/components/qma6100p/test_apps/main/idf_component.yml b/components/qma6100p/test_apps/main/idf_component.yml new file mode 100644 index 00000000..2502e5c0 --- /dev/null +++ b/components/qma6100p/test_apps/main/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + idf: ">=4.0" + qma6100p: + version: "*" + override_path: "../../../qma6100p" diff --git a/components/qma6100p/test_apps/main/test_esp_acc_qma6100p.c b/components/qma6100p/test_apps/main/test_esp_acc_qma6100p.c new file mode 100644 index 00000000..47f4f4f8 --- /dev/null +++ b/components/qma6100p/test_apps/main/test_esp_acc_qma6100p.c @@ -0,0 +1,114 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "unity.h" +#include "driver/i2c.h" +#include "qma6100p.h" +#include "esp_system.h" +#include "esp_log.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" + +#define I2C_MASTER_SCL_IO 5 /*!< gpio number for I2C master clock */ +#define I2C_MASTER_SDA_IO 4 /*!< gpio number for I2C master data */ +#define I2C_MASTER_NUM I2C_NUM_0 /*!< I2C port number for master dev */ +#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */ + +static const char *TAG = "qma6100p test"; +static qma6100p_handle_t qma6100p = NULL; + +/** + * @brief i2c master initialization + */ +static void i2c_bus_init(void) +{ + i2c_config_t conf; + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = (gpio_num_t)I2C_MASTER_SDA_IO; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf.scl_io_num = (gpio_num_t)I2C_MASTER_SCL_IO; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.master.clk_speed = I2C_MASTER_FREQ_HZ; + conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL; + + esp_err_t ret = i2c_param_config(I2C_MASTER_NUM, &conf); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C config returned error"); + + ret = i2c_driver_install(I2C_MASTER_NUM, conf.mode, 0, 0, 0); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C install returned error"); +} + +/** + * @brief i2c master initialization + */ +static void i2c_sensor_qma6100p_init(void) +{ + esp_err_t ret; + + i2c_bus_init(); + qma6100p = qma6100p_create(I2C_MASTER_NUM, QMA6100P_I2C_ADDRESS); + TEST_ASSERT_NOT_NULL_MESSAGE(qma6100p, "QMA6100P create returned NULL"); + + ret = qma6100p_wake_up(qma6100p); + TEST_ASSERT_EQUAL(ESP_OK, ret); +} + +TEST_CASE("Sensor qma6100p test", "[qma6100p][iot][sensor]") +{ + esp_err_t ret; + uint8_t qma6100p_deviceid; + qma6100p_acce_value_t acce; + + i2c_sensor_qma6100p_init(); + + ret = qma6100p_get_deviceid(qma6100p, &qma6100p_deviceid); + TEST_ASSERT_EQUAL(ESP_OK, ret); + TEST_ASSERT_EQUAL(QMA6100P_WHO_AM_I_VAL, qma6100p_deviceid); + + ret = qma6100p_get_acce(qma6100p, &acce); + TEST_ASSERT_EQUAL(ESP_OK, ret); + ESP_LOGI(TAG, "acce_x:%.2f, acce_y:%.2f, acce_z:%.2f\n", acce.acce_x, acce.acce_y, acce.acce_z); + + qma6100p_delete(qma6100p); + ret = i2c_driver_delete(I2C_MASTER_NUM); + TEST_ASSERT_EQUAL(ESP_OK, ret); +} + +#define TEST_MEMORY_LEAK_THRESHOLD (300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +void setUp(void) +{ + unity_utils_set_leak_level(TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_record_free_mem(); +} + +void tearDown(void) +{ + unity_utils_evaluate_leaks(); +} + +void app_main(void) +{ + /** + * ___ __ __ _ __ _ ___ ___ ____ + * / _ \| \/ | / \ / /_ / |/ _ \ / _ \| _ \ + * | | | | |\/| | / _ \| '_ \| | | | | | | | |_) | + * | |_| | | | |/ ___ \ (_) | | |_| | |_| | __/ + * \__\_\_| |_/_/ \_\___/|_|\___/ \___/|_| + */ + + printf(" ___ __ __ _ __ _ ___ ___ ____ \r\n"); + printf(" / _ \\| \\/ | / \\ / /_ / |/ _ \\ / _ \\| _ \\ \r\n"); + printf("| | | | |\\/| | / _ \\| '_ \\| | | | | | | | |_) |\r\n"); + printf("| |_| | | | |/ ___ \\ (_) | | |_| | |_| | __/ \r\n"); + printf(" \\__\\_\\_| |_/_/ \\_\\___/|_|\\___/ \\___/|_| \r\n"); + unity_run_menu(); +} diff --git a/components/qma6100p/test_apps/sdkconfig.defaults b/components/qma6100p/test_apps/sdkconfig.defaults new file mode 100644 index 00000000..b5b36e5c --- /dev/null +++ b/components/qma6100p/test_apps/sdkconfig.defaults @@ -0,0 +1,12 @@ +CONFIG_IDF_TARGET="esp32s3" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_RODATA=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/test_apps/components/CMakeLists.txt b/test_apps/components/CMakeLists.txt index ca1ec53e..df2271a3 100644 --- a/test_apps/components/CMakeLists.txt +++ b/test_apps/components/CMakeLists.txt @@ -35,6 +35,6 @@ if(NOT "${IDF_TARGET}" STREQUAL "esp32s2" AND NOT "${IDF_TARGET}" STREQUAL "esp3 endif() # Set the components to include the tests for. -set(TEST_COMPONENTS bh1750 mpu6050 mag3110 hts221 fbm320 icm42670 CACHE STRING "List of components to test") +set(TEST_COMPONENTS bh1750 mpu6050 mag3110 hts221 fbm320 icm42670 qma6100p CACHE STRING "List of components to test") include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(esp_bsp_test_app)