From 1d96b3e68085a5f1e6a3191b81a4500e83aad7df Mon Sep 17 00:00:00 2001 From: yanke Date: Fri, 19 Jul 2024 12:49:36 +0800 Subject: [PATCH] feat: support DRV10987 bldc motor driver in I2C control mode --- .gitlab/ci/build.yml | 18 + .gitlab/ci/rules.yml | 34 ++ components/motor/drv10987/CHANGELOG.md | 7 + components/motor/drv10987/CMakeLists.txt | 4 + components/motor/drv10987/README.md | 114 +++++++ components/motor/drv10987/drv10987.c | 318 ++++++++++++++++++ components/motor/drv10987/idf_component.yml | 11 + components/motor/drv10987/include/drv10987.h | 216 ++++++++++++ .../motor/drv10987/include/drv10987_config.h | 214 ++++++++++++ .../motor/drv10987/include/drv10987_fault.h | 30 ++ components/motor/drv10987/license.txt | 202 +++++++++++ .../drv10987/priv_include/drv10987_reg.h | 21 ++ .../motor/drv10987/test_apps/CMakeLists.txt | 9 + .../drv10987/test_apps/main/CMakeLists.txt | 3 + .../drv10987/test_apps/main/drv10987_test.c | 100 ++++++ .../drv10987/test_apps/sdkconfig.defaults | 4 + examples/motor/drv10987/CMakeLists.txt | 6 + examples/motor/drv10987/README.md | 118 +++++++ examples/motor/drv10987/main/CMakeLists.txt | 2 + .../motor/drv10987/main/Kconfig.projbuild | 9 + .../motor/drv10987/main/idf_component.yml | 4 + examples/motor/drv10987/main/main.c | 94 ++++++ examples/motor/drv10987/sdkconfig.defaults | 3 + 23 files changed, 1541 insertions(+) create mode 100644 components/motor/drv10987/CHANGELOG.md create mode 100644 components/motor/drv10987/CMakeLists.txt create mode 100644 components/motor/drv10987/README.md create mode 100644 components/motor/drv10987/drv10987.c create mode 100644 components/motor/drv10987/idf_component.yml create mode 100644 components/motor/drv10987/include/drv10987.h create mode 100644 components/motor/drv10987/include/drv10987_config.h create mode 100644 components/motor/drv10987/include/drv10987_fault.h create mode 100644 components/motor/drv10987/license.txt create mode 100644 components/motor/drv10987/priv_include/drv10987_reg.h create mode 100644 components/motor/drv10987/test_apps/CMakeLists.txt create mode 100644 components/motor/drv10987/test_apps/main/CMakeLists.txt create mode 100644 components/motor/drv10987/test_apps/main/drv10987_test.c create mode 100644 components/motor/drv10987/test_apps/sdkconfig.defaults create mode 100644 examples/motor/drv10987/CMakeLists.txt create mode 100644 examples/motor/drv10987/README.md create mode 100644 examples/motor/drv10987/main/CMakeLists.txt create mode 100644 examples/motor/drv10987/main/Kconfig.projbuild create mode 100644 examples/motor/drv10987/main/idf_component.yml create mode 100644 examples/motor/drv10987/main/main.c create mode 100644 examples/motor/drv10987/sdkconfig.defaults diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index b498f3734..82ee2e6bb 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -515,6 +515,14 @@ build_example_motor_bldc_fan_rainmaker: EXAMPLE_DIR: examples/motor/bldc_fan/rainmaker IMAGE: espressif/idf:release-v5.0 +build_example_motor_drv10987: + extends: + - .build_examples_template + - .rules:build:example_motor_drv10987 + variables: + EXAMPLE_DIR: examples/motor/drv10987 + IMAGE: espressif/idf:release-v5.0 + build_example_motor_foc_openloop_control: extends: - .build_examples_template @@ -1030,6 +1038,16 @@ build_components_led_led_indicator_test_apps: variables: EXAMPLE_DIR: components/led/led_indicator/test_apps +build_components_motor_drv10987_test_apps: + extends: + - .build_examples_template + - .rules:build:components_motor_drv10987_test_apps + parallel: + matrix: + - IMAGE: espressif/idf:release-v5.0 + variables: + EXAMPLE_DIR: components/motor/drv10987/test_apps + build_components_motor_esp_sensorless_bldc_control_test_apps: extends: - .build_examples_template diff --git a/.gitlab/ci/rules.yml b/.gitlab/ci/rules.yml index ae4888a62..bdf3eca86 100644 --- a/.gitlab/ci/rules.yml +++ b/.gitlab/ci/rules.yml @@ -166,6 +166,10 @@ - "components/led/lightbulb_driver/**/*" - "components/tools/cmake_utilities/package_manager.cmake" +.patterns-components_motor_drv10987: &patterns-components_motor_drv10987 + - "components/motor/drv10987/**/*" + - "components/tools/cmake_utilities/package_manager.cmake" + .patterns-components_motor_esp_simplefoc: &patterns-components_motor_esp_simplefoc - "components/motor/esp_simplefoc/**/*" - "components/tools/cmake_utilities/package_manager.cmake" @@ -441,6 +445,9 @@ .patterns-example_motor_bldc_fan_rainmaker: &patterns-example_motor_bldc_fan_rainmaker - "examples/motor/bldc_fan_rainmaker/**/*" +.patterns-example_motor_drv10987: &patterns-example_motor_drv10987 + - "examples/motor/drv10987/**/*" + .patterns-example_motor_foc_openloop_control: &patterns-example_motor_foc_openloop_control - "examples/motor/foc_openloop_control/**/*" @@ -973,6 +980,20 @@ - <<: *if-dev-push changes: *patterns-example_motor_bldc_fan_rainmaker +.rules:build:example_motor_drv10987: + rules: + - <<: *if-protected + - <<: *if-label-build + - <<: *if-trigger-job + - <<: *if-dev-push + changes: *patterns-build_system + - <<: *if-dev-push + changes: *patterns-components_motor_drv10987 + - <<: *if-dev-push + changes: *patterns-example_motor_drv10987 + - <<: *if-dev-push + changes: *patterns-components_i2c_bus + .rules:build:example_motor_foc_openloop_control: rules: - <<: *if-protected @@ -1732,6 +1753,19 @@ - <<: *if-dev-push changes: *patterns-components_led_lightbulb_driver +.rules:build:components_motor_drv10987_test_apps: + rules: + - <<: *if-protected + - <<: *if-label-build + - <<: *if-label-target_test + - <<: *if-trigger-job + - <<: *if-dev-push + changes: *patterns-build_system + - <<: *if-dev-push + changes: *patterns-components_motor_drv10987 + - <<: *if-dev-push + changes: *patterns-components_i2c_bus + .rules:build:components_motor_esp_simplefoc_test_apps: rules: - <<: *if-protected diff --git a/components/motor/drv10987/CHANGELOG.md b/components/motor/drv10987/CHANGELOG.md new file mode 100644 index 000000000..aa779eff7 --- /dev/null +++ b/components/motor/drv10987/CHANGELOG.md @@ -0,0 +1,7 @@ +# ChangeLog + +## v0.1.0 - 2024-7-23 + +### Enhancements: + +* Initial version diff --git a/components/motor/drv10987/CMakeLists.txt b/components/motor/drv10987/CMakeLists.txt new file mode 100644 index 000000000..4ae776647 --- /dev/null +++ b/components/motor/drv10987/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "drv10987.c" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "priv_include" + REQUIRES driver i2c_bus) diff --git a/components/motor/drv10987/README.md b/components/motor/drv10987/README.md new file mode 100644 index 000000000..413a11c84 --- /dev/null +++ b/components/motor/drv10987/README.md @@ -0,0 +1,114 @@ +# Component: DRV10987 + +The DRV10987 device is a 3-phase sensorless 180° sinusodial motor driver with integrated power MOSFETs, which can provide continuous drive current up to 2 A. + +* This component will show you how to read and write DRV10987. +* Pin assignment: +* master: + * GPIO23 is assigned as the data signal of i2c master port. + * GPIO22 is assigned as the clock signal of i2c master port. + * GPIO21 is assigned as rotation direction control. +* connection: + * Connect sda of drv10987 with GPIO23. + * Connect scl of drv10987 with GPIO22. + * Connect dir of drv10987 with GPIO21. + + +## Startup process + +It is recommended to program the EEPROM with the motor off and read back the EEPROM to verify that the drive is valid. + +1. Power up with any voltage within operating voltage range (6.2 V to 28 V). +2. Wait 10 ms. +3. Write register 0x60 to set MTR_DIS = 1; this disables the motor driver. +4. Write register 0x31 with 0x0000 to clear the EEPROM access code. +5. Write register 0x31 with 0xC0DE to enable access to EEPROM. +7. Read register 0x32 for eeReadyStatus = 1. +8. Case-A: Mass Write: + 1. Write all individual shadow registers. + 1. Write register 0x90 (CONFIG1) with CONFIG1 data. + 2. ... + 3. Write register 0x96 (CONFIG7) with CONFIG7 data. + 2. Write the following to register 0x35. + 1. ShadowRegEn = 0 + 2. eeRefresh = 0 + 3. eeWRnEn = 1 + 4. EEPROM Access Mode = 10 + 3. Wait for register 0x32 eeReadyStatus = 1 – EEPROM is now updated with the contents of the shadow registers. +9. Case-B: Mass Read: + 1. Write the following to register 0x35. + 1. ShadowRegEn = 0 + 2. eeRefresh = 0 + 3. eeWRnEn =0 + 4. eeAccMode = 10 + 2. Internally, the device starts reading the EEPROM and storing it in the shadow registers. + 3. Wait for register 0x32 eeReadyStatus = 1 – shadow registers now contain the EEPROM values. +10. Write register 0x60 to set MTR_DIS = 0; this re-enables the motor driver. + +More detailed data can be found in the [DRV10987 Datasheet](https://www.ti.com/lit/gpn/drv10987). + +## Example of DRV10987 usage + +The `drv10987` component provides a simplified encapsulation of the above process, enabling users to drive drv10987 faster. + +It is worth noting that the `drv10987` component provides default configuration entries for the configuration registers, but for different motors it is necessary to **modify the rm and kt** of the default configuration entries for config1 and config2. + +```c +static i2c_bus_handle_t i2c_bus = NULL; +static drv10987_handle_t drv10987 = NULL; + +//Step1:Init I2C bus +i2c_config_t conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = I2C_MASTER_SDA_IO, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_io_num = I2C_MASTER_SCL_IO, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = I2C_MASTER_FREQ_HZ, +}; +i2c_bus = i2c_bus_create(I2C_MASTER_NUM, &conf); + +//Step2:Init drv10987 +drv10987_config_t drv10987_cfg = { + .bus = i2c_bus, + .dev_addr = DRV10987_I2C_ADDRESS_DEFAULT, + .dir_pin = DIRECTION_IO, +}; +esp_err_t err = drv10987_create(&drv10987, &drv10987_cfg); + +//Step3:Set the direction of motor rotation +drv10987_set_direction_uvw(drv10987); + +//Step4:Enable access to EEPROM and check if EEPROM is ready for read/write access. +drv10987_write_access_to_eeprom(drv10987, 5); + +//Step5:Write configuration to EEPROM +//The component provides a default configuration, for different brushless motors it is necessary to configure Rm and Kt in config1 and config2. +drv10987_config1_t config1 = DEFAULT_DRV10987_CONFIG1; +drv10987_write_config_register(drv10987, CONFIG1_REGISTER_ADDR, &config1); +drv10987_config2_t config2 = DEFAULT_DRV10987_CONFIG2; +drv10987_write_config_register(drv10987, CONFIG2_REGISTER_ADDR, &config2); +drv10987_config3_t config3 = DEFAULT_DRV10987_CONFIG3; +drv10987_write_config_register(drv10987, CONFIG3_REGISTER_ADDR, &config3); +drv10987_config4_t config4 = DEFAULT_DRV10987_CONFIG4; +drv10987_write_config_register(drv10987, CONFIG4_REGISTER_ADDR, &config4); +drv10987_config5_t config5 = DEFAULT_DRV10987_CONFIG5; +drv10987_write_config_register(drv10987, CONFIG5_REGISTER_ADDR, &config5); +drv10987_config6_t config6 = DEFAULT_DRV10987_CONFIG6; +drv10987_write_config_register(drv10987, CONFIG6_REGISTER_ADDR, &config6); +drv10987_config7_t config7 = DEFAULT_DRV10987_CONFIG7; +drv10987_write_config_register(drv10987, CONFIG7_REGISTER_ADDR, &config7); +drv10987_mass_write_acess_to_eeprom(drv10987); + +//Step6:Waiting for eeprom status to be ready +while (1) { + if (drv10987_read_eeprom_status(drv10987) == EEPROM_READY) { + break; + } +} + +//Step7 Write motor speed and start drv10987 +drv10987_enable_control(drv10987); +drv10987_write_speed(drv10987, CONFIG_EXAMPLE_MOROT_SPEED); +drv10987_enable_control(drv10987); +``` \ No newline at end of file diff --git a/components/motor/drv10987/drv10987.c b/components/motor/drv10987/drv10987.c new file mode 100644 index 000000000..9d954c4fc --- /dev/null +++ b/components/motor/drv10987/drv10987.c @@ -0,0 +1,318 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "drv10987.h" +#include "driver/gpio.h" +#include "drv10987_reg.h" +#include "esp_log.h" +#include "esp_check.h" + +static const char *TAG = "DRV10987"; + +typedef struct { + i2c_bus_device_handle_t i2c_dev; + uint8_t i2c_addr; + drv10987_config_reg_t drv10987_config_reg; + gpio_num_t dir_pin; +} drv10987_t; + +static esp_err_t drv10987_read_reg(drv10987_t *drv10987, uint8_t reg, uint16_t *data) +{ + uint8_t drv10987_data[2] = {0}; + esp_err_t ret = i2c_bus_read_bytes(drv10987->i2c_dev, reg, 2, &drv10987_data[0]); + *data = ((drv10987_data[0] << 8 | drv10987_data[1])); + return ret; +} + +static esp_err_t drv10987_write_reg(drv10987_t *drv10987, uint8_t reg, uint16_t data) +{ + uint8_t buffer[2] = {0}; + buffer[0] = data >> 8; + buffer[1] = data; + esp_err_t ret = i2c_bus_write_bytes(drv10987->i2c_dev, reg, 2, buffer); + return ret; +} + +esp_err_t drv10987_create(drv10987_handle_t *handle, drv10987_config_t *config) +{ + esp_err_t err = ESP_OK; + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Handle is NULL"); + ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, TAG, "Config is NULL"); + + drv10987_t *drv10987 = (drv10987_t *)calloc(1, sizeof(drv10987_t)); + ESP_RETURN_ON_FALSE(drv10987, ESP_ERR_NO_MEM, TAG, "Failed to allocate memory for drv10987"); + + drv10987->i2c_addr = config->dev_addr; + drv10987->i2c_dev = i2c_bus_device_create(config->bus, drv10987->i2c_addr, i2c_bus_get_current_clk_speed(config->bus)); + + drv10987->dir_pin = config->dir_pin; + gpio_config_t io_cfg = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = (1ull << drv10987->dir_pin), + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + }; + err = gpio_config(&io_cfg); + + if (drv10987->i2c_dev == NULL) { + free(drv10987); + return ESP_FAIL; + } + + *handle = (drv10987_handle_t)drv10987; + return err; +} + +esp_err_t drv10987_delete(drv10987_handle_t handle) +{ + if (handle == NULL) { + return ESP_OK; + } + + drv10987_t *drv10987 = (drv10987_t *)handle; + i2c_bus_device_delete(&drv10987->i2c_dev); + free(drv10987); + return ESP_OK; +} + +esp_err_t drv10987_disable_control(drv10987_handle_t handle) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + + drv10987_t *drv10987 = (drv10987_t *)handle; + return drv10987_write_reg(drv10987, DRV10987_EECTRL_REGISTER_ADDR, EECTRL_REGISTER_VALUE(0X01)); /*!< Set the highest bit of the electrl register to 1 to turn off the motor */ +} + +esp_err_t drv10987_enable_control(drv10987_handle_t handle) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + + drv10987_t *drv10987 = (drv10987_t *)handle; + return drv10987_write_reg(drv10987, DRV10987_EECTRL_REGISTER_ADDR, EECTRL_REGISTER_VALUE(0X00)); /*!< Set the highest bit of the electrl register to 0 to turn on the motor */ +} + +esp_err_t drv10987_write_config_register(drv10987_handle_t handle, drv10987_config_register_addr_t config_register_addr, void *config) +{ + if (handle == NULL || config == NULL) { + return ESP_ERR_INVALID_ARG; + } + + drv10987_t *drv10987 = (drv10987_t *)handle; + uint16_t value = *(uint16_t *)config; /*!< Get the value of config without saving it separately to drv10987. */ + return drv10987_write_reg(drv10987, config_register_addr, value); +} + +drv10987_eeprom_status_t drv10987_read_eeprom_status(drv10987_handle_t handle) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + + drv10987_t *drv10987 = (drv10987_t *)handle; + uint16_t result = 0; + esp_err_t ret; + ret = drv10987_read_reg(drv10987, DRV10987_EEPROM_PROGRAMMING2_REGISTER_ADDR, &result); /*!< Get EEPROM status bits */ + + if (ret != ESP_OK) { + return EEPROM_NO_READY; + } + + if (result == 0x0001) { + return EEPROM_READY; /*!< 1: EEPROM ready for read/write access */ + } + return EEPROM_NO_READY; /*!< 0: EEPROM not ready for read/write access */ +} + +esp_err_t drv10987_write_access_to_eeprom(drv10987_handle_t handle, uint8_t maximum_failure_count) +{ + if (handle == NULL || maximum_failure_count < 1) { + return ESP_ERR_INVALID_ARG; + } + + drv10987_t *drv10987 = (drv10987_t *)handle; + esp_err_t ret; + drv10987_disable_control(handle); + ret = drv10987_write_reg(drv10987, DRV10987_EEPROM_PROGRAMMING1_REGISTER_ADDR, 0x0000); + ret = drv10987_write_reg(drv10987, DRV10987_EEPROM_PROGRAMMING1_REGISTER_ADDR, 0XC0DE); + + if (ret != ESP_OK) { + return ESP_FAIL; + } + + while (maximum_failure_count--) { + if (drv10987_read_eeprom_status(handle) == EEPROM_READY) { /*!< Within the maximum number of failures, an attempt is made to determine if the eeprom can allow reads and writes. */ + return ESP_OK; + } + vTaskDelay(10 / portTICK_PERIOD_MS); + } + ESP_LOGE(TAG, "EEPROM not ready for read/write access"); + return ESP_FAIL; +} + +esp_err_t drv10987_mass_write_acess_to_eeprom(drv10987_handle_t handle) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + + drv10987_t *drv10987 = (drv10987_t *)handle; + return drv10987_write_reg(drv10987, DRV10987_EEPROM_PROGRAMMING5_REGISTER_ADDR, EEPROM_PROGRAMMING5_REGISTER_VALUE(0x02, true, 0x00, true)); /*!< Config EEPROM_PROGRAMMING5_REGISTER */ +} + +esp_err_t drv10987_write_speed(drv10987_handle_t handle, uint16_t speed) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + + drv10987_t *drv10987 = (drv10987_t *)handle; + if (speed >= 511) { + speed = 511; /*!< In I2C mode, the speed is 9 bits, so the maximum value is 511 */ + } + + return drv10987_write_reg(drv10987, DRV10987_SPEEDCTRL_REGISTER_ADDR, 0x8000 | (speed & 0x1FF)); +} + +esp_err_t drv10987_read_driver_status(drv10987_handle_t handle, drv10987_fault_t *fault) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + + drv10987_t *drv10987 = (drv10987_t *)handle; + return drv10987_read_reg(drv10987, DRV10987_FAULT_ADDR, &fault->code); /*!< Get Fail Code */ +} + +drv10987_config_reg_t drv10987_read_config_register(drv10987_handle_t handle) +{ + drv10987_config_reg_t invalid_config; + memset(&invalid_config, 0, sizeof(drv10987_config_reg_t)); + + if (handle == NULL) { + return invalid_config; + } + + drv10987_t *drv10987 = (drv10987_t *)handle; + drv10987_read_reg(drv10987, DRV10987_CONFIG1_ADDR, &drv10987->drv10987_config_reg.config1.result); + drv10987_read_reg(drv10987, DRV10987_CONFIG2_ADDR, &drv10987->drv10987_config_reg.config2.result); + drv10987_read_reg(drv10987, DRV10987_CONFIG3_ADDR, &drv10987->drv10987_config_reg.config3.result); + drv10987_read_reg(drv10987, DRV10987_CONFIG4_ADDR, &drv10987->drv10987_config_reg.config4.result); + drv10987_read_reg(drv10987, DRV10987_CONFIG5_ADDR, &drv10987->drv10987_config_reg.config5.result); + drv10987_read_reg(drv10987, DRV10987_CONFIG6_ADDR, &drv10987->drv10987_config_reg.config6.result); + drv10987_read_reg(drv10987, DRV10987_CONFIG7_ADDR, &drv10987->drv10987_config_reg.config7.result); + + return drv10987->drv10987_config_reg; +} + +esp_err_t drv10987_read_phase_resistance_and_kt(drv10987_handle_t handle, float *phase_res, float *kt) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + + drv10987_t *drv10987 = (drv10987_t *)handle; + esp_err_t ret; + + ret = drv10987_read_reg(drv10987, DRV10987_CONFIG1_ADDR, &drv10987->drv10987_config_reg.config1.result); + if (ret != ESP_OK) { + return ESP_FAIL; + } + + ret = drv10987_read_reg(drv10987, DRV10987_CONFIG2_ADDR, &drv10987->drv10987_config_reg.config2.result); + if (ret != ESP_OK) { + return ESP_FAIL; + } + + *phase_res = 1.0f * (drv10987->drv10987_config_reg.config1.rm_value << drv10987->drv10987_config_reg.config1.rm_shift) * 0.00967f; + *kt = 1.0f * (drv10987->drv10987_config_reg.config2.kt_value << drv10987->drv10987_config_reg.config2.kt_shift) / 1090.0f * 1000.0f; + + return ESP_OK; + +} + +esp_err_t drv10987_clear_driver_fault(drv10987_handle_t handle) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + + drv10987_t *drv10987 = (drv10987_t *)handle; + return drv10987_write_reg(drv10987, DRV10987_FAULT_ADDR, 0xFFFF); /*!< Clear all faults. It is important to note that all faults should be set to 1 to clear them. */ +} + +esp_err_t drv10987_set_direction_uvw(drv10987_handle_t handle) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + + drv10987_t *drv10987 = (drv10987_t *)handle; + return gpio_set_level(drv10987->dir_pin, 0); /*!< When low, phase driving sequence is U → V → W. */ +} + +esp_err_t drv10987_set_direction_uwv(drv10987_handle_t handle) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + + drv10987_t *drv10987 = (drv10987_t *)handle; + return gpio_set_level(drv10987->dir_pin, 1); /*!< When low, phase driving sequence is U → V → W. */ +} + +esp_err_t drv10987_eeprom_test(drv10987_handle_t handle, drv10987_config1_t config1, drv10987_config2_t config2) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t ret = ESP_OK; + drv10987_t *drv10987 = (drv10987_t *)handle; + ret = drv10987_write_access_to_eeprom(handle, 5); /*!< Enable access to EEPROM and check if EEPROM is ready for read/write access. */ + if (ret != ESP_OK) { + ESP_LOGE(TAG, "EEPROM not ready for read/write access"); + return ESP_FAIL; + } + + ret = drv10987_write_config_register(handle, DRV10987_CONFIG1_ADDR, &config1); /*!< Write data to the CONFIG1 register. */ + ret = drv10987_write_config_register(handle, DRV10987_CONFIG2_ADDR, &config2); + ret = drv10987_mass_write_acess_to_eeprom(handle); + + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to write to EEPROM"); + return ESP_FAIL; + } + + while (1) { + if (drv10987_read_eeprom_status(handle) == EEPROM_READY) { + ESP_LOGI(TAG, "EEPROM is now updated with the contents of the shadow registers."); + break; + } + } + + drv10987->drv10987_config_reg = drv10987_read_config_register(handle); + ESP_LOGI(TAG, "CONFIG1:0x%04x, Read:0x%04x", config1.result, drv10987->drv10987_config_reg.config1.result); + ESP_LOGI(TAG, "CONFIG1 RMValue:%02x", drv10987->drv10987_config_reg.config1.rm_value); + ESP_LOGI(TAG, "CONFIG1 RMShift:%02x", drv10987->drv10987_config_reg.config1.rm_shift); + ESP_LOGI(TAG, "CONFIG1 ClkCycleAdjust:%02x", drv10987->drv10987_config_reg.config1.clk_cycle_adjust); + ESP_LOGI(TAG, "CONFIG1 FGCycle:%02x", drv10987->drv10987_config_reg.config1.fg_cycle); + ESP_LOGI(TAG, "CONFIG1 FGOLSel:%02x", drv10987->drv10987_config_reg.config1.fg_open_loop_sel); + ESP_LOGI(TAG, "CONFIG1 SSMConfig:%02x", drv10987->drv10987_config_reg.config1.ssm_config); + ESP_LOGI(TAG, "--------------------------"); + ESP_LOGI(TAG, "CONFIG2:0x%04x, Read:0x%04x", config2.result, drv10987->drv10987_config_reg.config2.result); + ESP_LOGI(TAG, "CONFIG2 TCtrlAdvValue:%02x", drv10987->drv10987_config_reg.config2.tctrladv); + ESP_LOGI(TAG, "CONFIG2 TCtrlAdvShift:%02x", drv10987->drv10987_config_reg.config2.tctrladv_shift); + ESP_LOGI(TAG, "CONFIG2 CommAdvMode:%02x", drv10987->drv10987_config_reg.config2.commadv_mode); + ESP_LOGI(TAG, "CONFIG2 KtValue:%02x", drv10987->drv10987_config_reg.config2.kt_value); + ESP_LOGI(TAG, "CONFIG2 KtShift:%02x", drv10987->drv10987_config_reg.config2.kt_shift); + return ESP_OK; +} diff --git a/components/motor/drv10987/idf_component.yml b/components/motor/drv10987/idf_component.yml new file mode 100644 index 000000000..655be28ce --- /dev/null +++ b/components/motor/drv10987/idf_component.yml @@ -0,0 +1,11 @@ +version: "0.1.0" +description: DRV10987 motor drive component +url: https://github.com/espressif/esp-iot-solution/tree/master/components/motor/drv10987 +repository: https://github.com/espressif/esp-iot-solution.git +issues: https://github.com/espressif/esp-iot-solution/issues +dependencies: + idf: + version: '>=5.0' + i2c_bus: + version: "*" + override_path: "../../i2c_bus" diff --git a/components/motor/drv10987/include/drv10987.h b/components/motor/drv10987/include/drv10987.h new file mode 100644 index 000000000..84ab3de45 --- /dev/null +++ b/components/motor/drv10987/include/drv10987.h @@ -0,0 +1,216 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include +#include "driver/gpio.h" +#include "drv10987_config.h" +#include "drv10987_fault.h" +#include "esp_err.h" +#include "i2c_bus.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + i2c_bus_handle_t bus; + uint8_t dev_addr; + gpio_num_t dir_pin; +} drv10987_config_t; + +typedef struct drv10987_t *drv10987_handle_t; + +#define DRV10987_I2C_ADDRESS_DEFAULT (0x52) + +/** + * @brief Create and init object and return a handle + * + * @param handle Pointer to the DRV10987 handle + * @param config Pointer to configuration + * + * @return + * - ESP_OK Success + * - Others Fail + */ +esp_err_t drv10987_create(drv10987_handle_t *handle, drv10987_config_t *config); + +/** + * @brief Deinit object and free memory + * + * @param handle Pointer to the DRV10987 handle + * + * @return + * - ESP_OK Success + * - Others Fail + */ +esp_err_t drv10987_delete(drv10987_handle_t handle); + +/** + * @brief Disable control for the DRV10987 motor driver + * + * @param handle Pointer to the DRV10987 handle + * + * @return + * - ESP_OK Success + * - Others Fail + */ +esp_err_t drv10987_disable_control(drv10987_handle_t handle); + +/** + * @brief Enable control for the DRV10987 motor driver + * + * @param handle Pointer to the DRV10987 handle + * + * @return + * - ESP_OK Success + * - Others Fail + */ +esp_err_t drv10987_enable_control(drv10987_handle_t handle); + +/** + * @brief Write to a configuration register of the DRV10987 motor driver + * + * @param handle Pointer to the DRV10987 handle + * @param drv10987_config_register_addr_t Address of the configuration register to write to + * @param config Pointer to the configuration data to be written + * + * @return + * - ESP_OK Success + * - Others Fail + */ +esp_err_t drv10987_write_config_register(drv10987_handle_t handle, drv10987_config_register_addr_t config_register_addr, void *config); + +/** + * @brief Write access to EEPROM of the DRV10987 motor driver + * + * @param handle Pointer to the DRV10987 handle + * @param maximum_failure_count Maximum allowable failure count for EEPROM read attempts + * + * @return + * - ESP_OK Success + * - Others Fail + */ +esp_err_t drv10987_write_access_to_eeprom(drv10987_handle_t handle, uint8_t maximum_failure_count); + +/** + * @brief Enable mass write access to EEPROM of the DRV10987 motor driver + * + * @param handle Pointer to the DRV10987 handle + * + * @return + * - ESP_OK Success + * - Others Fail + */ +esp_err_t drv10987_mass_write_acess_to_eeprom(drv10987_handle_t handle); + +/** + * @brief Write the target speed to the DRV10987 motor driver + * + * @param handle Pointer to the DRV10987 handle + * @param speed target speed value to be written (It is worth noting that this speed value is a 9bit piece of data, so the maximum value is 511) + * + * @return + * - ESP_OK Success + * - Others Fail + */ +esp_err_t drv10987_write_speed(drv10987_handle_t handle, uint16_t speed); + +/** + * @brief Read the EEPROM status from the DRV10987 motor driver + * + * @param handle Pointer to the DRV10987 handle + * + * @return + * - EEPROM_NO_READY EEPROM not ready for read/write access + * - EEPROM_READY EEPROM ready for read/write access + */ +drv10987_eeprom_status_t drv10987_read_eeprom_status(drv10987_handle_t handle); + +/** + * @brief Read the driver status from the DRV10987 motor driver + * + * @param handle Pointer to the DRV10987 handle + * @param fault Pointer to fault + * + * @return + * - ESP_OK Success + * - Others Fail + */ +esp_err_t drv10987_read_driver_status(drv10987_handle_t handle, drv10987_fault_t *fault); + +/** + * @brief Read all configuration registers from the DRV10987 motor driver + * + * @param handle Pointer to the DRV10987 handle + * + * @return + * - drv10987_config_reg_t All values of the configuration registers + */ +drv10987_config_reg_t drv10987_read_config_register(drv10987_handle_t handle); + +/** + * @brief Read motor phase resistance and kt from the DRV10987 motor driver + * + * @param handle Pointer to the DRV10987 handle + * @param phase_res Pointer to phase resistance + * @param kt Pointer to kt + * + * @return + * - ESP_OK Success + * - Others Fail + */ +esp_err_t drv10987_read_phase_resistance_and_kt(drv10987_handle_t handle, float *phase_res, float *kt); + +/** + * @brief Clear any driver faults in the DRV10987 motor driver + * + * @param handle Pointer to the DRV10987 handle + * + * @return + * - ESP_OK Success + * - Others Fail + */ +esp_err_t drv10987_clear_driver_fault(drv10987_handle_t handle); + +/** + * @brief Set the motor direction uvw for the DRV10987 motor driver + * + * @param handle Pointer to the DRV10987 handle + * + * @return + * - ESP_OK Success + * - Others Fail + */ +esp_err_t drv10987_set_direction_uvw(drv10987_handle_t handle); + +/** + * @brief Set the motor direction uwv for the DRV10987 motor driver + * + * @param handle Pointer to the DRV10987 handle + * + * @return + * - ESP_OK Success + * - Others Fail + */ +esp_err_t drv10987_set_direction_uwv(drv10987_handle_t handle); + +/** + * @brief Perform an EEPROM test on the DRV10987 motor driver + * + * @param handle Pointer to the DRV10987 handle + * @param config Configuration data to be used for the EEPROM test + * + * @return + * - ESP_OK Success + * - Others Fail + */ +esp_err_t drv10987_eeprom_test(drv10987_handle_t handle, drv10987_config1_t config1, drv10987_config2_t config2); + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/drv10987/include/drv10987_config.h b/components/motor/drv10987/include/drv10987_config.h new file mode 100644 index 000000000..363c66c43 --- /dev/null +++ b/components/motor/drv10987/include/drv10987_config.h @@ -0,0 +1,214 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#define SET_BITS(VALUE, MASK, SHIFT) ((VALUE & MASK) << SHIFT) + +typedef enum { + CONFIG1_REGISTER_ADDR = 0x90, + CONFIG2_REGISTER_ADDR, + CONFIG3_REGISTER_ADDR, + CONFIG4_REGISTER_ADDR, + CONFIG5_REGISTER_ADDR, + CONFIG6_REGISTER_ADDR, + CONFIG7_REGISTER_ADDR, +} drv10987_config_register_addr_t; + +typedef enum { + EEPROM_NO_READY = 0, + EEPROM_READY, +} drv10987_eeprom_status_t; + +typedef union { + uint16_t result; + struct { + uint16_t rm_value : 4; + uint16_t rm_shift : 3; + uint16_t clk_cycle_adjust : 1; + uint16_t fg_cycle : 4; + uint16_t fg_open_loop_sel : 2; + uint16_t ssm_config : 2; + }; +} drv10987_config1_t; + +typedef union { + uint16_t result; + struct { + uint16_t tctrladv : 4; + uint16_t tctrladv_shift : 3; + uint16_t commadv_mode : 1; + uint16_t kt_value : 4; + uint16_t kt_shift : 4; + }; +} drv10987_config2_t; + +typedef union { + uint16_t result; + struct { + uint16_t brkdone_thr : 3; + uint16_t oplcurr_rt : 3; + uint16_t openl_curr : 2; + uint16_t rvsd_thr : 2; + uint16_t rvsd_en : 1; + uint16_t isd_en : 1; + uint16_t bemf_hys : 1; + uint16_t brkcur_thrsel : 1; + uint16_t isd_thr : 2; + }; +} drv10987_config3_t; + +typedef union { + uint16_t result; + struct { + uint16_t align_time : 3; + uint16_t op2cls_thr : 5; + uint16_t st_accel : 3; + uint16_t st_accel2 : 3; + uint16_t accel_range_sel : 2; + }; +} drv10987_config4_t; + +typedef union { + uint16_t result; + struct { + uint16_t ipdashwi_limit : 1; + uint16_t hwi_limit_thr : 3; + uint16_t swi_limit_thr : 4; + uint16_t lock_en0 : 1; + uint16_t lock_en1 : 1; + uint16_t lock_en2 : 1; + uint16_t lock_en3 : 1; + uint16_t lock_en4 : 1; + uint16_t lock_en5 : 1; + uint16_t ot_warning_limit : 2; + }; +} drv10987_config5_t; + +typedef union { + uint16_t result; + struct { + uint16_t slew_rate : 2; + uint16_t duty_cycle_limit : 2; + uint16_t cl_slp_accel : 3; + uint16_t cl_op_dis : 1; + uint16_t ipd_rl_smd : 1; + uint16_t avs_mmd : 1; + uint16_t avs_en : 1; + uint16_t avs_ind_en : 1; + uint16_t kt_lck_thr : 2; + uint16_t pwm_freq : 1; + uint16_t spd_ctrl_md : 1; + }; +} drv10987_config6_t; + +typedef union { + uint16_t result; + struct { + uint16_t dead_time : 5; + uint16_t ctrl_coef : 3; + uint16_t ipd_clk : 2; + uint16_t ipd_curr_thr : 4; + uint16_t ipd_advc_ag : 2; + }; +} drv10987_config7_t; + +typedef struct { + drv10987_config1_t config1; + drv10987_config2_t config2; + drv10987_config3_t config3; + drv10987_config4_t config4; + drv10987_config5_t config5; + drv10987_config6_t config6; + drv10987_config7_t config7; +} drv10987_config_reg_t; + +#define DEFAULT_DRV10987_CONFIG1 \ + { \ + .rm_value = 0x0D, \ + .rm_shift = 0x03, \ + .clk_cycle_adjust = 0x00, \ + .fg_cycle = 0x00, \ + .fg_open_loop_sel = 0x00, \ + .ssm_config = 0x00} + +#define DEFAULT_DRV10987_CONFIG2 \ + { \ + .tctrladv = 0x00, \ + .tctrladv_shift = 0x00, \ + .commadv_mode = 0x00, \ + .kt_value = 0x09, \ + .kt_shift = 0x03} + +#define DEFAULT_DRV10987_CONFIG3 \ + { \ + .brkdone_thr = 0x00, \ + .oplcurr_rt = 0x00, \ + .openl_curr = 0x03, \ + .rvsd_thr = 0X00, \ + .rvsd_en = 0X00, \ + .isd_en = 0X00, \ + .bemf_hys = 0x00, \ + .brkcur_thrsel = 0x00, \ + .isd_thr = 0x00} + +#define DEFAULT_DRV10987_CONFIG4 \ + { \ + .align_time = 0X02, \ + .op2cls_thr = 0x11, \ + .st_accel = 0x07, \ + .st_accel2 = 0x06, \ + .accel_range_sel = 0X00} + +#define DEFAULT_DRV10987_CONFIG5 \ + { \ + .ipdashwi_limit = 0x01, \ + .hwi_limit_thr = 0X07, \ + .swi_limit_thr = 0X00, \ + .lock_en0 = 0x01, \ + .lock_en1 = 0X01, \ + .lock_en2 = 0x00, \ + .lock_en3 = 0x01, \ + .lock_en4 = 0x01, \ + .lock_en5 = 0x01, \ + .ot_warning_limit = 0x00} + +#define DEFAULT_DRV10987_CONFIG6 \ + { \ + .slew_rate = 0x00, \ + .duty_cycle_limit = 0x00, \ + .cl_slp_accel = 0x04, \ + .cl_op_dis = 0x00, \ + .ipd_rl_smd = 0x00, \ + .avs_mmd = 0x00, \ + .avs_en = 0x00, \ + .avs_ind_en = 0x01, \ + .kt_lck_thr = 0x03, \ + .pwm_freq = 0x01, \ + .spd_ctrl_md = 0x00} + +#define DEFAULT_DRV10987_CONFIG7 \ + { \ + .dead_time = 0x1A, \ + .ctrl_coef = 0x03, \ + .ipd_clk = 0x00, \ + .ipd_curr_thr = 0x00, \ + .ipd_advc_ag = 0x00} + +#define MTR_DIS_MASK 0x01 +#define EEACCMODE_MASK 0x03 +#define EEWRNEN_MASK 0x01 +#define EEREFRESH_MASK 0x01 +#define SHADOWREGEN_MASK 0x01 + +#define EECTRL_REGISTER_VALUE(MTR_DIS) \ + (SET_BITS(MTR_DIS, MTR_DIS_MASK, 15)) + +#define EEPROM_PROGRAMMING5_REGISTER_VALUE(EEACCMODE, EEWRNEN, EEREFRESH, SHADOWREGEN) \ + (SET_BITS(EEACCMODE, EEACCMODE_MASK, 0) | \ + SET_BITS(EEWRNEN, EEWRNEN_MASK, 2) | \ + SET_BITS(EEREFRESH, EEREFRESH_MASK, 8) | \ + SET_BITS(SHADOWREGEN, SHADOWREGEN_MASK, 12)) diff --git a/components/motor/drv10987/include/drv10987_fault.h b/components/motor/drv10987/include/drv10987_fault.h new file mode 100644 index 000000000..b17a3b8f2 --- /dev/null +++ b/components/motor/drv10987/include/drv10987_fault.h @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +typedef union { + uint16_t code; + struct { + uint16_t hardware_current_limit : 1; + uint16_t speed_abnormal : 1; + uint16_t kt_abnormal : 1; + uint16_t no_motor : 1; + uint16_t stuck_in_open_loop : 1; + uint16_t stuck_in_closed_loop : 1; + uint16_t reserved : 1; + uint16_t ldo_3v3_undervoltage : 1; + uint16_t vcc_undervoltage : 1; + uint16_t vreg_undervoltage : 1; + uint16_t charge_pump_undervoltage : 1; + uint16_t overcurrent : 1; + uint16_t vreg_overcurrent : 1; + uint16_t vcc_upper_limit : 1; + uint16_t temperature_over_warning_limit : 1; + uint16_t temperature_over_limit : 1; + }; +} drv10987_fault_t; diff --git a/components/motor/drv10987/license.txt b/components/motor/drv10987/license.txt new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/components/motor/drv10987/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/motor/drv10987/priv_include/drv10987_reg.h b/components/motor/drv10987/priv_include/drv10987_reg.h new file mode 100644 index 000000000..b84436c45 --- /dev/null +++ b/components/motor/drv10987/priv_include/drv10987_reg.h @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#define DRV10987_CONFIG1_ADDR 0X90 +#define DRV10987_CONFIG2_ADDR 0X91 +#define DRV10987_CONFIG3_ADDR 0X92 +#define DRV10987_CONFIG4_ADDR 0X93 +#define DRV10987_CONFIG5_ADDR 0X94 +#define DRV10987_CONFIG6_ADDR 0X95 +#define DRV10987_CONFIG7_ADDR 0X96 + +#define DRV10987_FAULT_ADDR 0X00 +#define DRV10987_SPEEDCTRL_REGISTER_ADDR 0X30 +#define DRV10987_EEPROM_PROGRAMMING1_REGISTER_ADDR 0X31 +#define DRV10987_EEPROM_PROGRAMMING2_REGISTER_ADDR 0X32 +#define DRV10987_EEPROM_PROGRAMMING5_REGISTER_ADDR 0X35 +#define DRV10987_EECTRL_REGISTER_ADDR 0X60 diff --git a/components/motor/drv10987/test_apps/CMakeLists.txt b/components/motor/drv10987/test_apps/CMakeLists.txt new file mode 100644 index 000000000..8f0119eba --- /dev/null +++ b/components/motor/drv10987/test_apps/CMakeLists.txt @@ -0,0 +1,9 @@ +# 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" + "../../drv10987") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(drv10987_test) diff --git a/components/motor/drv10987/test_apps/main/CMakeLists.txt b/components/motor/drv10987/test_apps/main/CMakeLists.txt new file mode 100644 index 000000000..7d4815b20 --- /dev/null +++ b/components/motor/drv10987/test_apps/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRC_DIRS "." + PRIV_INCLUDE_DIRS "." + PRIV_REQUIRES unity drv10987) diff --git a/components/motor/drv10987/test_apps/main/drv10987_test.c b/components/motor/drv10987/test_apps/main/drv10987_test.c new file mode 100644 index 000000000..37e45795e --- /dev/null +++ b/components/motor/drv10987/test_apps/main/drv10987_test.c @@ -0,0 +1,100 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "unity.h" +#include "drv10987.h" +#include "esp_check.h" + +#define TEST_MEMORY_LEAK_THRESHOLD (-460) + +#define DIRECTION_IO 21 +#define I2C_MASTER_SCL_IO 22 +#define I2C_MASTER_SDA_IO 23 +#define I2C_MASTER_NUM I2C_NUM_0 +#define I2C_MASTER_FREQ_HZ 100000 + +static size_t before_free_8bit; +static size_t before_free_32bit; + +static i2c_bus_handle_t i2c_bus = NULL; +static drv10987_handle_t drv10987 = NULL; + +static void drv10987_test_init(void) +{ + i2c_config_t conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = I2C_MASTER_SDA_IO, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_io_num = I2C_MASTER_SCL_IO, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = I2C_MASTER_FREQ_HZ, + }; + i2c_bus = i2c_bus_create(I2C_MASTER_NUM, &conf); + TEST_ASSERT_NOT_NULL(i2c_bus); + + drv10987_config_t drv10987_cfg = { + .bus = i2c_bus, + .dev_addr = DRV10987_I2C_ADDRESS_DEFAULT, + .dir_pin = DIRECTION_IO, + }; + + esp_err_t err = drv10987_create(&drv10987, &drv10987_cfg); + TEST_ASSERT(err == ESP_OK); +} + +static void drv10987_test_deinit(void) +{ + drv10987_delete(drv10987); + drv10987 = NULL; + i2c_bus_delete(&i2c_bus); +} + +TEST_CASE("drv10987 eeprom test", "[eeprom]") +{ + drv10987_test_init(); + drv10987_config1_t config1 = DEFAULT_DRV10987_CONFIG1; + drv10987_config2_t config2 = DEFAULT_DRV10987_CONFIG2; + TEST_ASSERT_EQUAL(ESP_OK, drv10987_eeprom_test(drv10987, config1, config2)); + drv10987_test_deinit(); +} + +TEST_CASE("drv10987 read motor phase resistance and kt", "[motor info][eeprom]") +{ + drv10987_test_init(); + float phase_resistance = 0.0f, kt = 0.0f; + TEST_ASSERT_EQUAL(ESP_OK, drv10987_read_phase_resistance_and_kt(drv10987, &phase_resistance, &kt)); + printf("Phase Resistance: %.2f Ohm, kt: %.2f mV/Hz \n", phase_resistance, kt); + drv10987_test_deinit(); +} + +static void check_leak(size_t before_free, size_t after_free, const char *type) +{ + ssize_t delta = after_free - before_free; + printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); + TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); +} + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + check_leak(before_free_8bit, after_free_8bit, "8BIT"); + check_leak(before_free_32bit, after_free_32bit, "32BIT"); +} + +void app_main(void) +{ + printf("DRV10987 TEST\n"); + unity_run_menu(); +} diff --git a/components/motor/drv10987/test_apps/sdkconfig.defaults b/components/motor/drv10987/test_apps/sdkconfig.defaults new file mode 100644 index 000000000..62e98b0aa --- /dev/null +++ b/components/motor/drv10987/test_apps/sdkconfig.defaults @@ -0,0 +1,4 @@ +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/examples/motor/drv10987/CMakeLists.txt b/examples/motor/drv10987/CMakeLists.txt new file mode 100644 index 000000000..cf0f2f9f8 --- /dev/null +++ b/examples/motor/drv10987/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) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(drv10987) diff --git a/examples/motor/drv10987/README.md b/examples/motor/drv10987/README.md new file mode 100644 index 000000000..b7b090dfe --- /dev/null +++ b/examples/motor/drv10987/README.md @@ -0,0 +1,118 @@ +# Application example of DRV10987 + +This example demonstrates how to use the `drv10987` component to develop motor control programs on ESP32 series chips. The specific functions demonstrated are as follows: + +- Motor parameter writing and speed control. +- Detection of motor operating status. + +## How to use the example + +### Hardware requirement + +You need to prepare a three-phase brushless motor and a DRV10987 motor driver board. For this example, the parameters of the brushless motor used are as follows: +* Phase to center Tap Resistance (Ω): 1.0088 +* BEMF Constant (Kt) (mV/Hz): 66.24 + +For motor parameter measurements, refer to the `Device Functional Modes` section of the DRV10987 documentation. Also, for the calculated results, you can verify them against the Look-UP Table. + +As an example, suppose the current phase resistance of the motor is 2.328, and the corresponding HEX is 0X4F according to the Motor Phase Resistance Look-Up Table query, then config1 needs to be modified: + +```c +drv10987_config1_t config1 = DEFAULT_DRV10987_CONFIG1; +config1.rm_value = 0x0F; +config1.rm_shift = 0x04; +``` + +In addition, it is important to note that the measured results must be converted to calculated values and displacement numbers as required by the TI documentation. + +### Compile and write + +1. Enter the `drv10987` directory: + + ```linux + cd ./esp-iot-solution/examples/motor/drv10987 + ``` + +2. Use the `idf.py` tool to set the compiler chip, and then compile and download it. The instructions are: + + ```linux + # Set compiler chip + idf.py idf.py set-target esp32s3 + + # Compile and download + idf.py -p PORT build flash + ``` + + Please replace `PORT` with the port number currently in use + +### Example output + +The log of the motor speed control is as follows, we blocked the motor after it had been running for some time to test if the error flag could be detected. + +When the driver detects an error, we clear the error flag bit and wait drv10987 reboot, at which point the fault flag bit is cleared. + +```log +I (31) boot: ESP-IDF v5.4-dev-1592-g18f9050cb1-dirty 2nd stage bootloader +I (31) boot: compile time Jul 30 2024 11:42:45 +I (33) boot: Multicore bootloader +I (38) boot: chip revision: v3.0 +I (41) boot.esp32: SPI Speed : 40MHz +I (46) boot.esp32: SPI Mode : DIO +I (51) boot.esp32: SPI Flash Size : 2MB +I (55) boot: Enabling RNG early entropy source... +I (61) boot: Partition Table: +I (64) boot: ## Label Usage Type ST Offset Length +I (71) boot: 0 nvs WiFi data 01 02 00009000 00006000 +I (79) boot: 1 phy_init RF data 01 01 0000f000 00001000 +I (86) boot: 2 factory factory app 00 00 00010000 00100000 +I (94) boot: End of partition table +I (98) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=0b35ch ( 45916) map +I (122) esp_image: segment 1: paddr=0001b384 vaddr=3ffb0000 size=02394h ( 9108) load +I (126) esp_image: segment 2: paddr=0001d720 vaddr=40080000 size=028f8h ( 10488) load +I (132) esp_image: segment 3: paddr=00020020 vaddr=400d0020 size=176dch ( 95964) map +I (169) esp_image: segment 4: paddr=00037704 vaddr=400828f8 size=0b744h ( 46916) load +I (195) boot: Loaded app from partition at offset 0x10000 +I (195) boot: Disabling RNG early entropy source... +I (207) cpu_start: Multicore app +I (215) cpu_start: Pro cpu start user code +I (215) cpu_start: cpu freq: 240000000 Hz +I (216) app_init: Application information: +I (218) app_init: Project name: drv10987 +I (223) app_init: App version: 7a207e10-dirty +I (229) app_init: Compile time: Aug 1 2024 10:56:51 +I (235) app_init: ELF file SHA256: 6df6df1d4... +I (240) app_init: ESP-IDF: v5.4-dev-1592-g18f9050cb1-dirty +I (247) efuse_init: Min chip rev: v0.0 +I (252) efuse_init: Max chip rev: v3.99 +I (257) efuse_init: Chip rev: v3.0 +I (262) heap_init: Initializing. RAM available for dynamic allocation: +I (269) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM +I (275) heap_init: At 3FFB2CD0 len 0002D330 (180 KiB): DRAM +I (281) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM +I (287) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM +I (294) heap_init: At 4008E03C len 00011FC4 (71 KiB): IRAM +I (301) spi_flash: detected chip: generic +I (305) spi_flash: flash io: dio +W (309) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k). Using the size in the binary image header. +W (322) i2c: This driver is an old driver, please migrate your application code to adapt `driver/i2c_master.h` +I (333) main_task: Started on CPU0 +I (337) main_task: Calling app_main() +I (341) i2c_bus: i2c0 bus inited +I (344) i2c_bus: I2C Bus Config Succeed, Version: 0.1.0 +I (350) gpio: GPIO[21]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +E (12447) DRV10987: Fault detected:1 +E (13448) DRV10987: Fault detected:1 +E (14449) DRV10987: Fault detected:1 +E (15450) DRV10987: Fault detected:1 +E (16451) DRV10987: Fault detected:1 +E (17452) DRV10987: Fault detected:1 +E (34453) DRV10987: Fault detected:1 +E (35454) DRV10987: Fault detected:1 +E (36455) DRV10987: Fault detected:1 +E (37456) DRV10987: Fault detected:1 +E (38457) DRV10987: Fault detected:1 +E (39458) DRV10987: Fault detected:1 + +``` + +![](https://dl.espressif.com/AE/esp-iot-solution/drv10987_motor.gif) \ No newline at end of file diff --git a/examples/motor/drv10987/main/CMakeLists.txt b/examples/motor/drv10987/main/CMakeLists.txt new file mode 100644 index 000000000..a12097b98 --- /dev/null +++ b/examples/motor/drv10987/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + ) diff --git a/examples/motor/drv10987/main/Kconfig.projbuild b/examples/motor/drv10987/main/Kconfig.projbuild new file mode 100644 index 000000000..6f0a336dc --- /dev/null +++ b/examples/motor/drv10987/main/Kconfig.projbuild @@ -0,0 +1,9 @@ +menu "Example Configuration" + + config EXAMPLE_MOROT_SPEED + int "MOTOR SPEED" + range 0 511 + default 300 + help + Configure the speed value. It must be an integer between 0 and 511. +endmenu diff --git a/examples/motor/drv10987/main/idf_component.yml b/examples/motor/drv10987/main/idf_component.yml new file mode 100644 index 000000000..353559182 --- /dev/null +++ b/examples/motor/drv10987/main/idf_component.yml @@ -0,0 +1,4 @@ +dependencies: + idf: ">=5.0" + drv10987: + override_path: "../../../../components/motor/drv10987" diff --git a/examples/motor/drv10987/main/main.c b/examples/motor/drv10987/main/main.c new file mode 100644 index 000000000..f8731857f --- /dev/null +++ b/examples/motor/drv10987/main/main.c @@ -0,0 +1,94 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "stdio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "drv10987.h" +#include "esp_log.h" + +#define DIRECTION_IO 21 +#define I2C_MASTER_SCL_IO 22 +#define I2C_MASTER_SDA_IO 23 +#define I2C_MASTER_NUM I2C_NUM_0 +#define I2C_MASTER_FREQ_HZ 100000 + +static i2c_bus_handle_t i2c_bus = NULL; +static drv10987_handle_t drv10987 = NULL; +static char *TAG = "DRV10987 Demo"; + +void app_main() +{ + drv10987_fault_t fault = {0}; + i2c_config_t conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = I2C_MASTER_SDA_IO, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_io_num = I2C_MASTER_SCL_IO, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = I2C_MASTER_FREQ_HZ, + }; + i2c_bus = i2c_bus_create(I2C_MASTER_NUM, &conf); + + if (i2c_bus == NULL) { + ESP_LOGE(TAG, "I2C bus create failed"); + return; + } + + drv10987_config_t drv10987_cfg = { + .bus = i2c_bus, + .dev_addr = DRV10987_I2C_ADDRESS_DEFAULT, + .dir_pin = DIRECTION_IO, + }; + + esp_err_t err = drv10987_create(&drv10987, &drv10987_cfg); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "DRV10987 create failed"); + return; + } + + drv10987_set_direction_uvw(drv10987); /*!< Set the direction of motor rotation to V->V->W */ + drv10987_clear_driver_fault(drv10987); /*!< Clear all faults. */ + drv10987_write_access_to_eeprom(drv10987, 5); /*!< Enable access to EEPROM and check if EEPROM is ready for read/write access. */ + + drv10987_config1_t config1 = DEFAULT_DRV10987_CONFIG1; + drv10987_write_config_register(drv10987, CONFIG1_REGISTER_ADDR, &config1); /*!< Mainly written for motor phase resistance, FG and Spread spectrum modulation control. */ + drv10987_config2_t config2 = DEFAULT_DRV10987_CONFIG2; + drv10987_write_config_register(drv10987, CONFIG2_REGISTER_ADDR, &config2); /*!< Mainly written for communtation advance value and kt value. */ + drv10987_config3_t config3 = DEFAULT_DRV10987_CONFIG3; + drv10987_write_config_register(drv10987, CONFIG3_REGISTER_ADDR, &config3); /*!< Mainly writes braking modes, open-loop parameters, etc. */ + drv10987_config4_t config4 = DEFAULT_DRV10987_CONFIG4; + drv10987_write_config_register(drv10987, CONFIG4_REGISTER_ADDR, &config4); /*!< Mainly write the alignment time, open to closed loop threshold, and open loop startup acceleration. */ + drv10987_config5_t config5 = DEFAULT_DRV10987_CONFIG5; + drv10987_write_config_register(drv10987, CONFIG5_REGISTER_ADDR, &config5); /*!< Mainly writes hardware overcurrent thresholds, software overcurrent thresholds and some other exceptions. */ + drv10987_config6_t config6 = DEFAULT_DRV10987_CONFIG6; + drv10987_write_config_register(drv10987, CONFIG6_REGISTER_ADDR, &config6); /*!< Mainly writes Slew-rate control for phase node, Minimum duty-cycle limit, Minimum duty-cycle limit, etc. */ + drv10987_config7_t config7 = DEFAULT_DRV10987_CONFIG7; + drv10987_write_config_register(drv10987, CONFIG7_REGISTER_ADDR, &config7); /*!< Mainly writes driver dead time, SCORE control constant, IPD current thtrshold, etc. */ + + drv10987_mass_write_acess_to_eeprom(drv10987); + + while (1) { + if (drv10987_read_eeprom_status(drv10987) == EEPROM_READY) { /*!< EEPROM is now updated with the contents of the shadow sregisters */ + break; + } + } + + drv10987_enable_control(drv10987); + drv10987_write_speed(drv10987, CONFIG_EXAMPLE_MOROT_SPEED); /*!< Write speed control via I2C */ + drv10987_enable_control(drv10987); + + while (1) { + drv10987_read_driver_status(drv10987, &fault); + if (fault.code != 0) { + ESP_LOGE(TAG, "Fault detected:%d", fault.code); + drv10987_clear_driver_fault(drv10987); /*!< Once the device detects a fault, the motor will stop rotating for 5 seconds. After 5 seconds, the motor will start rotating again. The motor will not start rotating again after 5 seconds unless the fault is cleared within this time period. */ + } + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + drv10987_delete(drv10987); +} diff --git a/examples/motor/drv10987/sdkconfig.defaults b/examples/motor/drv10987/sdkconfig.defaults new file mode 100644 index 000000000..67a21c41e --- /dev/null +++ b/examples/motor/drv10987/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096