diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml index 1ee61d825..be2c7e88d 100644 --- a/.github/workflows/upload_component.yml +++ b/.github/workflows/upload_component.yml @@ -34,6 +34,7 @@ jobs: components/knob; components/led/led_indicator; components/led/lightbulb_driver; + components/motor/esp_sensorless_bldc_control; components/motor/esp_simplefoc; components/openai; components/sensors/humiture/aht20; diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index 6d7bb1918..c9e626dbe 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -511,200 +511,211 @@ build_components_button_test_apps: variables: EXAMPLE_DIR: components/button/test_apps -build_components_ir_ir_learn_test_apps: +build_components_display_lcd_esp_lcd_gc9b71_test_apps: extends: - .build_examples_template - - .rules:build:components_ir_ir_learn_test_apps + - .rules:build:components_display_lcd_esp_lcd_gc9b71_test_apps parallel: matrix: - - IMAGE: espressif/idf:release-v5.0 + - IMAGE: espressif/idf:latest variables: - EXAMPLE_DIR: components/ir/ir_learn/test_apps + EXAMPLE_DIR: components/display/lcd/esp_lcd_gc9b71/test_apps -build_components_knob_test_apps: +build_components_display_lcd_esp_lcd_panel_io_additions_test_apps: extends: - .build_examples_template - - .rules:build:components_knob_test + - .rules:build:components_display_lcd_esp_lcd_panel_io_additions_test_apps parallel: matrix: - IMAGE: espressif/idf:release-v4.4 - IMAGE: espressif/idf:release-v5.0 variables: - EXAMPLE_DIR: components/knob/test_apps + EXAMPLE_DIR: components/display/lcd/esp_lcd_panel_io_additions/test_apps -build_components_motor_esp_simplefoc_test_apps: +build_components_display_lcd_esp_lcd_sh8601_test_apps: extends: - .build_examples_template - - .rules:build:components_motor_esp_simplefoc_test_apps + - .rules:build:components_display_lcd_esp_lcd_sh8601_test_apps parallel: matrix: - - IMAGE: espressif/idf:release-v5.0 + - IMAGE: espressif/idf:latest variables: - EXAMPLE_DIR: components/motor/esp_simplefoc/test_apps + EXAMPLE_DIR: components/display/lcd/esp_lcd_sh8601/test_apps -build_components_openai_test_apps: +build_components_display_lcd_esp_lcd_spd2010_test_apps: extends: - .build_examples_template - - .rules:build:components_openai_test_apps + - .rules:build:components_display_lcd_esp_lcd_spd2010_test_apps parallel: matrix: - - IMAGE: espressif/idf:release-v4.4 - - IMAGE: espressif/idf:release-v5.0 + - IMAGE: espressif/idf:latest variables: - EXAMPLE_DIR: components/openai/test_apps + EXAMPLE_DIR: components/display/lcd/esp_lcd_spd2010/test_apps -build_components_usb_esp_msc_ota_test_apps: +build_components_display_lcd_esp_lcd_st7701_test_apps: extends: - .build_examples_template - - .rules:build:components_usb_esp_msc_ota_test_apps + - .rules:build:components_display_lcd_esp_lcd_st7701_test_apps + parallel: + matrix: + - IMAGE: espressif/idf:latest variables: - IMAGE: espressif/idf:release-v5.1 - EXAMPLE_DIR: components/usb/esp_msc_ota/test_apps + EXAMPLE_DIR: components/display/lcd/esp_lcd_st7701/test_apps -build_components_usb_usb_stream_test_apps: +build_components_display_lcd_esp_lcd_st77903_test_apps: extends: - .build_examples_template - - .rules:build:components_usb_usb_stream_test + - .rules:build:components_display_lcd_esp_lcd_st77903_test_apps parallel: matrix: - - IMAGE: espressif/idf:release-v4.4 - - IMAGE: espressif/idf:release-v5.0 + - IMAGE: espressif/idf:latest + before_script: + - export PATCH_PATH=${IOT_SOLUTION_PATH}/examples/display/lcd/qspi_without_ram/patch/support_qspi_without_ram_a9349f4ad4.patch + - cd "$(dirname $(whereis idf.py | awk -F ' ' '{print $2}'))/.." + - git checkout a9349f4ad4 + - git submodule update --init --recursive + - git apply ${PATCH_PATH} + - cd ${IOT_SOLUTION_PATH} variables: - EXAMPLE_DIR: components/usb/usb_stream/test_apps + EXAMPLE_DIR: components/display/lcd/esp_lcd_st77903/test_apps -build_components_usb_iot_usbh_test_apps: +build_components_display_lcd_touch_esp_lcd_touch_spd2010_test_apps: extends: - .build_examples_template - - .rules:build:components_usb_iot_usbh_test + - .rules:build:components_display_lcd_touch_esp_lcd_touch_spd2010_test_apps parallel: matrix: - IMAGE: espressif/idf:release-v4.4 - IMAGE: espressif/idf:release-v5.0 variables: - EXAMPLE_DIR: components/usb/iot_usbh/test_apps + EXAMPLE_DIR: components/display/lcd_touch/esp_lcd_touch_spd2010/test_apps -build_components_usb_iot_usbh_cdc_test_apps: +build_components_ir_ir_learn_test_apps: extends: - .build_examples_template - - .rules:build:components_usb_iot_usbh_cdc_test + - .rules:build:components_ir_ir_learn_test_apps parallel: matrix: - - IMAGE: espressif/idf:release-v4.4 - IMAGE: espressif/idf:release-v5.0 variables: - EXAMPLE_DIR: components/usb/iot_usbh_cdc/test_apps + EXAMPLE_DIR: components/ir/ir_learn/test_apps -build_components_display_lcd_esp_lcd_gc9b71_test_apps: +build_components_knob_test_apps: extends: - .build_examples_template - - .rules:build:components_display_lcd_esp_lcd_gc9b71_test_apps + - .rules:build:components_knob_test parallel: matrix: - - IMAGE: espressif/idf:latest + - IMAGE: espressif/idf:release-v4.4 + - IMAGE: espressif/idf:release-v5.0 variables: - EXAMPLE_DIR: components/display/lcd/esp_lcd_gc9b71/test_apps + EXAMPLE_DIR: components/knob/test_apps -build_components_display_lcd_esp_lcd_panel_io_additions_test_apps: +build_components_led_led_indicator_test_apps: extends: - .build_examples_template - - .rules:build:components_display_lcd_esp_lcd_panel_io_additions_test_apps + - .rules:build:components_led_led_indicator_test_apps parallel: matrix: - IMAGE: espressif/idf:release-v4.4 - IMAGE: espressif/idf:release-v5.0 variables: - EXAMPLE_DIR: components/display/lcd/esp_lcd_panel_io_additions/test_apps + EXAMPLE_DIR: components/led/led_indicator/test_apps -build_components_display_lcd_esp_lcd_sh8601_test_apps: +build_components_motor_esp_sensorless_bldc_control_test_apps: extends: - .build_examples_template - - .rules:build:components_display_lcd_esp_lcd_sh8601_test_apps + - .rules:build:components_motor_esp_sensorless_bldc_control_test_apps parallel: matrix: - - IMAGE: espressif/idf:latest + - IMAGE: espressif/idf:release-v5.0 variables: - EXAMPLE_DIR: components/display/lcd/esp_lcd_sh8601/test_apps + EXAMPLE_DIR: components/motor/esp_sensorless_bldc_control/test_apps -build_components_display_lcd_esp_lcd_spd2010_test_apps: +build_components_motor_esp_simplefoc_test_apps: extends: - .build_examples_template - - .rules:build:components_display_lcd_esp_lcd_spd2010_test_apps + - .rules:build:components_motor_esp_simplefoc_test_apps parallel: matrix: - - IMAGE: espressif/idf:latest + - IMAGE: espressif/idf:release-v5.0 variables: - EXAMPLE_DIR: components/display/lcd/esp_lcd_spd2010/test_apps + EXAMPLE_DIR: components/motor/esp_simplefoc/test_apps -build_components_display_lcd_esp_lcd_st7701_test_apps: +build_components_openai_test_apps: extends: - .build_examples_template - - .rules:build:components_display_lcd_esp_lcd_st7701_test_apps + - .rules:build:components_openai_test_apps parallel: matrix: - - IMAGE: espressif/idf:latest + - IMAGE: espressif/idf:release-v4.4 + - IMAGE: espressif/idf:release-v5.0 variables: - EXAMPLE_DIR: components/display/lcd/esp_lcd_st7701/test_apps + EXAMPLE_DIR: components/openai/test_apps -build_components_display_lcd_esp_lcd_st77903_test_apps: +build_components_sensors_humiture_aht20_test_apps: extends: - .build_examples_template - - .rules:build:components_display_lcd_esp_lcd_st77903_test_apps + - .rules:build:components_sensors_humiture_aht20_test_apps parallel: matrix: - - IMAGE: espressif/idf:latest - before_script: - - export PATCH_PATH=${IOT_SOLUTION_PATH}/examples/display/lcd/qspi_without_ram/patch/support_qspi_without_ram_a9349f4ad4.patch - - cd "$(dirname $(whereis idf.py | awk -F ' ' '{print $2}'))/.." - - git checkout a9349f4ad4 - - git submodule update --init --recursive - - git apply ${PATCH_PATH} - - cd ${IOT_SOLUTION_PATH} + - IMAGE: espressif/idf:release-v4.4 + - IMAGE: espressif/idf:release-v5.0 variables: - EXAMPLE_DIR: components/display/lcd/esp_lcd_st77903/test_apps + EXAMPLE_DIR: components/sensors/humiture/aht20/test_apps -build_components_display_lcd_touch_esp_lcd_touch_spd2010_test_apps: +build_components_sensors_radar_at581x_test_apps: extends: - .build_examples_template - - .rules:build:components_display_lcd_touch_esp_lcd_touch_spd2010_test_apps + - .rules:build:components_sensors_radar_at581x_test_apps parallel: matrix: - IMAGE: espressif/idf:release-v4.4 - IMAGE: espressif/idf:release-v5.0 variables: - EXAMPLE_DIR: components/display/lcd_touch/esp_lcd_touch_spd2010/test_apps + EXAMPLE_DIR: components/sensors/radar/at581x/test_apps -build_components_led_led_indicator_test_apps: +build_components_usb_esp_msc_ota_test_apps: extends: - .build_examples_template - - .rules:build:components_led_led_indicator_test_apps + - .rules:build:components_usb_esp_msc_ota_test_apps + variables: + IMAGE: espressif/idf:release-v5.1 + EXAMPLE_DIR: components/usb/esp_msc_ota/test_apps + +build_components_usb_usb_stream_test_apps: + extends: + - .build_examples_template + - .rules:build:components_usb_usb_stream_test parallel: matrix: - IMAGE: espressif/idf:release-v4.4 - IMAGE: espressif/idf:release-v5.0 variables: - EXAMPLE_DIR: components/led/led_indicator/test_apps + EXAMPLE_DIR: components/usb/usb_stream/test_apps -build_components_sensors_humiture_aht20_test_apps: +build_components_usb_iot_usbh_test_apps: extends: - .build_examples_template - - .rules:build:components_sensors_humiture_aht20_test_apps + - .rules:build:components_usb_iot_usbh_test parallel: matrix: - IMAGE: espressif/idf:release-v4.4 - IMAGE: espressif/idf:release-v5.0 variables: - EXAMPLE_DIR: components/sensors/humiture/aht20/test_apps + EXAMPLE_DIR: components/usb/iot_usbh/test_apps -build_components_sensors_radar_at581x_test_apps: +build_components_usb_iot_usbh_cdc_test_apps: extends: - .build_examples_template - - .rules:build:components_sensors_radar_at581x_test_apps + - .rules:build:components_usb_iot_usbh_cdc_test parallel: matrix: - IMAGE: espressif/idf:release-v4.4 - IMAGE: espressif/idf:release-v5.0 variables: - EXAMPLE_DIR: components/sensors/radar/at581x/test_apps + EXAMPLE_DIR: components/usb/iot_usbh_cdc/test_apps + # # tools build jobs # diff --git a/.gitlab/ci/rules.yml b/.gitlab/ci/rules.yml index f8bd4e17c..cde008cb9 100644 --- a/.gitlab/ci/rules.yml +++ b/.gitlab/ci/rules.yml @@ -101,10 +101,14 @@ - "components/led/lightbulb_driver/**/*" - "components/tools/cmake_utilities/package_manager.cmake" -.patterns-components_esp_simplefoc: &patterns-components_esp_simplefoc +.patterns-components_motor_esp_simplefoc: &patterns-components_motor_esp_simplefoc - "components/motor/esp_simplefoc/**/*" - "components/tools/cmake_utilities/package_manager.cmake" +.patterns-components_motor_esp_sensorless_bldc_control: &patterns-components_motor_esp_sensorless_bldc_control + - "components/motor/esp_sensorless_bldc_control/**/*" + - "components/tools/cmake_utilities/package_manager.cmake" + .patterns-components_motor_servo: &patterns-components_motor_servo - "components/motor/servo/**/*" @@ -205,20 +209,25 @@ .patterns-docs_inc: &patterns-docs_inc - "components/audio/dac_audio/include/dac_audio.h" - "components/audio/pwm_audio/include/pwm_audio.h" + - "components/bluetooth/ble_conn_mgr/include/esp_ble_conn_mgr.h" - "components/bus/include/i2c_bus.h" - "components/bus/include/spi_bus.h" - - "components/led/led_indicator/include/led_indicator.h" - - "components/display/screen/screen_driver.h" - - "components/display/screen/interface_driver/scr_interface_driver.h" - "components/button/include/iot_button.h" - - "components/knob/iot_knob.h" + - "components/display/screen/interface_driver/scr_interface_driver.h" + - "components/display/screen/screen_driver.h" - "components/display/touch_panel/touch_panel.h" + - "components/knob/iot_knob.h" + - "components/led/led_indicator/include/led_indicator.h" + - "components/motor/esp_sensorless_bldc_control/control/include/bldc_control_param.h" + - "components/motor/esp_sensorless_bldc_control/control/include/bldc_control.h" + - "components/motor/esp_sensorless_bldc_control/user_cfg/bldc_user_cfg.h" - "components/motor/servo/include/iot_servo.h" + - "components/openai/include/OpenAI.h" - "components/sensors/sensor_hub/include/hal/humiture_hal.h" - "components/sensors/sensor_hub/include/hal/imu_hal.h" - "components/sensors/sensor_hub/include/hal/light_sensor_hal.h" - - "components/sensors/sensor_hub/include/sensor_type.h" - "components/sensors/sensor_hub/include/iot_sensor_hub.h" + - "components/sensors/sensor_hub/include/sensor_type.h" - "components/usb/usb_stream/include/usb_stream.h" - "components/bluetooth/ble_conn_mgr/include/esp_ble_conn_mgr.h" - "components/zero_detection/include/zero_detection.h" @@ -641,7 +650,7 @@ - <<: *if-dev-push changes: *patterns-build_system - <<: *if-dev-push - changes: *patterns-components_esp_simplefoc + changes: *patterns-components_motor_esp_simplefoc - <<: *if-dev-push changes: *patterns-example_motor_foc_openloop_control @@ -653,7 +662,7 @@ - <<: *if-dev-push changes: *patterns-build_system - <<: *if-dev-push - changes: *patterns-components_esp_simplefoc + changes: *patterns-components_motor_esp_simplefoc - <<: *if-dev-push changes: *patterns-example_motor_foc_velocity_control @@ -1167,7 +1176,18 @@ - <<: *if-dev-push changes: *patterns-build_system - <<: *if-dev-push - changes: *patterns-components_esp_simplefoc + changes: *patterns-components_motor_esp_simplefoc + +.rules:build:components_motor_esp_sensorless_bldc_control_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_esp_sensorless_bldc_control .rules:build:components_motor_servo_test: rules: diff --git a/README.md b/README.md index 001b890ca..88e1e802e 100755 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ The registered components in ESP-IoT-Solution are listed below: | [esp_lcd_st7701](https://components.espressif.com/components/espressif/esp_lcd_st7701) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_st7701/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_st7701) | | [esp_lcd_touch_spd2010](https://components.espressif.com/components/espressif/esp_lcd_touch_spd2010) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_touch_spd2010/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_touch_spd2010) | | [esp_simplefoc](https://components.espressif.com/components/espressif/esp_simplefoc) | [![Component Registry](https://components.espressif.com/components/espressif/esp_simplefoc/badge.svg)](https://components.espressif.com/components/espressif/esp_simplefoc) | +| [esp_sensorless_bldc_control](https://components.espressif.com/components/espressif/esp_sensorless_bldc_control) | [![Component Registry](https://components.espressif.com/components/espressif/esp_sensorless_bldc_control/badge.svg)](https://components.espressif.com/components/espressif/esp_sensorless_bldc_control) | [esp_tinyuf2](https://components.espressif.com/components/espressif/esp_tinyuf2) | [![Component Registry](https://components.espressif.com/components/espressif/esp_tinyuf2/badge.svg)](https://components.espressif.com/components/espressif/esp_tinyuf2) | | [extended_vfs](https://components.espressif.com/components/espressif/extended_vfs) | [![Component Registry](https://components.espressif.com/components/espressif/extended_vfs/badge.svg)](https://components.espressif.com/components/espressif/extended_vfs) | | [gprof](https://components.espressif.com/components/espressif/gprof) | [![Component Registry](https://components.espressif.com/components/espressif/gprof/badge.svg)](https://components.espressif.com/components/espressif/gprof) | diff --git a/README_CN.md b/README_CN.md index df0bf18e6..8414df2b9 100755 --- a/README_CN.md +++ b/README_CN.md @@ -64,6 +64,7 @@ ESP-IoT-Solution 中注册的组件如下: | [esp_lcd_st7701](https://components.espressif.com/components/espressif/esp_lcd_st7701) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_st7701/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_st7701) | | [esp_lcd_touch_spd2010](https://components.espressif.com/components/espressif/esp_lcd_touch_spd2010) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_touch_spd2010/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_touch_spd2010) | | [esp_simplefoc](https://components.espressif.com/components/espressif/esp_simplefoc) | [![Component Registry](https://components.espressif.com/components/espressif/esp_simplefoc/badge.svg)](https://components.espressif.com/components/espressif/esp_simplefoc) | +| [esp_sensorless_bldc_control](https://components.espressif.com/components/espressif/esp_sensorless_bldc_control) | [![Component Registry](https://components.espressif.com/components/espressif/esp_sensorless_bldc_control/badge.svg)](https://components.espressif.com/components/espressif/esp_sensorless_bldc_control) | [esp_tinyuf2](https://components.espressif.com/components/espressif/esp_tinyuf2) | [![Component Registry](https://components.espressif.com/components/espressif/esp_tinyuf2/badge.svg)](https://components.espressif.com/components/espressif/esp_tinyuf2) | | [extended_vfs](https://components.espressif.com/components/espressif/extended_vfs) | [![Component Registry](https://components.espressif.com/components/espressif/extended_vfs/badge.svg)](https://components.espressif.com/components/espressif/extended_vfs) | | [gprof](https://components.espressif.com/components/espressif/gprof) | [![Component Registry](https://components.espressif.com/components/espressif/gprof/badge.svg)](https://components.espressif.com/components/espressif/gprof) | diff --git a/components/.build-rules.yml b/components/.build-rules.yml index 52c55871c..03bab047f 100644 --- a/components/.build-rules.yml +++ b/components/.build-rules.yml @@ -22,6 +22,14 @@ components/led/led_indicator/test_apps: enable: - if: INCLUDE_DEFAULT == 1 +components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc: + enable: + - if: SOC_MCPWM_SUPPORTED == 1 + +components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer: + disable: + - if: IDF_TARGET in ["esp32c2"] + components/motor/esp_simplefoc/test_apps: enable: - if: IDF_TARGET in ["esp32s3"] @@ -34,10 +42,6 @@ components/sensors/radar/at581x/test_apps: enable: - if: INCLUDE_DEFAULT == 1 -components/usb/esp_msc_ota/test_apps: - enable: - - if: SOC_USB_OTG_SUPPORTED == 1 - components/display/lcd/esp_lcd_gc9b71/test_apps: enable: - if: INCLUDE_DEFAULT == 1 @@ -53,6 +57,8 @@ components/display/lcd/esp_lcd_sh8601/test_apps: components/display/lcd/esp_lcd_spd2010/test_apps: enable: - if: INCLUDE_DEFAULT == 1 + disable: + - if: IDF_TARGET in ["esp32c2"] components/display/lcd/esp_lcd_st7701/test_apps: enable: diff --git a/components/motor/esp_sensorless_bldc_control/CHANGELOG.md b/components/motor/esp_sensorless_bldc_control/CHANGELOG.md new file mode 100644 index 000000000..08022c9c3 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/CHANGELOG.md @@ -0,0 +1,7 @@ +# ChangeLog + +## v0.1.0 - 2023-11-14 + +### Enhancements + +* Init version \ No newline at end of file diff --git a/components/motor/esp_sensorless_bldc_control/CMakeLists.txt b/components/motor/esp_sensorless_bldc_control/CMakeLists.txt new file mode 100644 index 000000000..21da69266 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/CMakeLists.txt @@ -0,0 +1,9 @@ +if(NOT CONFIG_SOC_MCPWM_SUPPORTED) + set(exclude_srcs "hardware_driver/bldc_mcpwm.c" "control/bldc_zero_cross_adc.c") +endif() + + +idf_component_register(SRC_DIRS "." "common" "control" "hardware_driver" + INCLUDE_DIRS "." "include" "common/include" "control/include" "hardware_driver/include" + EXCLUDE_SRCS ${exclude_srcs} + REQUIRES driver esp_timer esp_event esp_adc) \ No newline at end of file diff --git a/components/motor/esp_sensorless_bldc_control/LICENSE b/components/motor/esp_sensorless_bldc_control/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/LICENSE @@ -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/esp_sensorless_bldc_control/README.md b/components/motor/esp_sensorless_bldc_control/README.md new file mode 100644 index 000000000..fef154cb4 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/README.md @@ -0,0 +1,96 @@ +[![Component Registry](https://components.espressif.com/components/espressif/esp_sensorless_bldc_control/badge.svg)](https://components.espressif.com/components/espressif/esp_sensorless_bldc_control) + +## ESP Sensorless bldc control 组件介绍 + +``esp_sensorless_bldc_control`` 是基于 ESP32 系列芯片的 BLDC 无感方波控制库,支持以下功能: + +* 支持基于 ADC 采样检测过零点 +* 支持基于比较器检测过零点 +* 支持基于脉冲法实现转子初始相位检测 +* 支持堵转保护 +* 支持过流,过欠压保护[feature] +* 支持缺相保护[feature] + +### ESP Sensorless bldc control 用户指南 + +请参考:https://docs.espressif.com/projects/esp-iot-solution/zh_CN/latest/motor/index.html + +### 添加组件到工程 + +1. 请使用组件管理器指令 `add-dependency` 将 `esp_sensorless_bldc_control` 添加到项目的依赖项, 在 `CMake` 执行期间该组件将被自动下载到工程目录。 + + ``` + idf.py add-dependency "espressif/esp_sensorless_bldc_control=*" + ``` + +2. 将 `esp_sensorless_bldc_control/user_cfg/bldc_user_cfg.h.tpl` 文件复制到工程目录下,并更名为 `bldc_user_cfg.h`。并在 `main/CMakeLists.txt` 文件中加入: + + ``` + idf_component_get_property(bldc_lib espressif__esp_sensorless_bldc_control COMPONENT_LIB) + cmake_policy(SET CMP0079 NEW) + target_link_libraries(${bldc_lib} PUBLIC ${COMPONENT_LIB}) + ``` + + Note: 该文件用于设置电机控制相关参数,一定要包含在工程中。 + +## 使用示例 + +```C + esp_event_loop_create_default(); + esp_event_handler_register(BLDC_CONTROL_EVENT, ESP_EVENT_ANY_ID, &bldc_control_event_handler, NULL); + switch_config_t_t upper_switch_config = { + .control_type = CONTROL_TYPE_MCPWM, + .bldc_mcpwm = { + .group_id = 0, + .gpio_num = {17,16,15}, + }, + }; + + switch_config_t_t lower_switch_config = { + .control_type = CONTROL_TYPE_GPIO, + .bldc_gpio[0] = { + .gpio_num = 12, + .gpio_mode = GPIO_MODE_OUTPUT, + }, + .bldc_gpio[1] = { + .gpio_num = 11, + .gpio_mode = GPIO_MODE_OUTPUT, + }, + .bldc_gpio[2] = { + .gpio_num = 10, + .gpio_mode = GPIO_MODE_OUTPUT, + }, + }; + + bldc_zero_cross_adc_config_t zero_cross_adc_config = { + .adc_handle = NULL, + .adc_unit = ADC_UNIT_1, + .chan_cfg = { + .bitwidth = ADC_BITWIDTH_12, + .atten = ADC_ATTEN_DB_0, + }, + .adc_channel = {ADC_CHANNEL_3, ADC_CHANNEL_4, ADC_CHANNEL_5, ADC_CHANNEL_0, ADC_CHANNEL_1}, + }; + + bldc_control_config_t config = { + .speed_mode = SPEED_CLOSED_LOOP, + .control_mode = BLDC_SIX_STEP, + .alignment_mode = ALIGNMENT_ADC, + .six_step_config = { + .lower_switch_active_level = 0, + .upper_switch_config = upper_switch_config, + .lower_switch_config = lower_switch_config, + .mos_en_config.has_enable = false, + }, + .zero_cross_adc_config = zero_cross_adc_config, + }; + + /*!< Init hardware driver */ + bldc_control_init(&bldc_control_handle, &config); + + /*!< Setting motor direction */ + bldc_control_set_dir(bldc_control_handle, CCW); + + /*!< Start motor control */ + bldc_control_start(bldc_control_handle, 300); +``` \ No newline at end of file diff --git a/components/motor/esp_sensorless_bldc_control/bldc_control.c b/components/motor/esp_sensorless_bldc_control/bldc_control.c new file mode 100644 index 000000000..1c33418f7 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/bldc_control.c @@ -0,0 +1,455 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "bldc_control.h" +#include "bldc_control_param.h" +#include "bldc_config.h" +#include "bldc_common.h" + +static const char *TAG = "bldc_control"; + +ESP_EVENT_DEFINE_BASE(BLDC_CONTROL_EVENT); + +static portMUX_TYPE s_control_lock = portMUX_INITIALIZER_UNLOCKED; +#define BLDC_CONTROL_ENTER_CRITICAL() portENTER_CRITICAL(&s_control_lock) +#define BLDC_CONTROL_EXIT_CRITICAL() portEXIT_CRITICAL(&s_control_lock) + +typedef struct { + gptimer_handle_t gptimer; + SemaphoreHandle_t xSemaphore; /*!< semaphore handle */ + control_param_t control_param; + control_mode_t control_mode; /*!< control mode */ + alignment_mode_t alignment_mode; /*!< alignment mode */ + speed_mode_t speed_mode; /*!< speed mode */ + pid_ctrl_block_handle_t pid; + uint8_t (*change_phase)(void *handle); + void *change_phase_handle; + uint8_t (*zero_cross)(void *handle); + void *zero_cross_handle; + esp_err_t (*control_operation)(void *handle); + int delayCnt; /*!< Delay count, each plus one is an interrupt function time */ +} bldc_control_t; + +// Table to lookup bldc control event name +static const char *bldc_control_event_name_table[] = { + "BLDC_CONTROL_START", + "BLDC_CONTROL_STOP", + "BLDC_CONTROL_ALIGNMENT", + "BLDC_CONTROL_CLOSED_LOOP", + "BLDC_CONTROL_DRAG", + "BLDC_CONTROL_BLOCKED", +}; + +static esp_err_t bldc_control_operation(void *handle); + +static void bldc_control_dispatch_event(int32_t event_id, const void *event_data, size_t event_data_size) +{ + if (esp_event_post(BLDC_CONTROL_EVENT, event_id, event_data, event_data_size, portMAX_DELAY) != ESP_OK) + { + // please call esp_event_loop_create_default() to create event loop to receive event if this happens + ESP_LOGE(TAG, "Failed to post bldc_control event: %s", bldc_control_event_name_table[event_id]); + } +} + +static bool IRAM_ATTR bldc_control_gptimer_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx) +{ + bldc_control_t *control = (bldc_control_t *)user_ctx; + /*!< Determine if xSemaphore exists */ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + /*!< Floating-point numbers cannot be computed in an interrupt, informing the task to start an arithmetic control */ + xSemaphoreGiveFromISR(control->xSemaphore, &xHigherPriorityTaskWoken); + return xHigherPriorityTaskWoken == pdTRUE ? true : false; +} + +static void bldc_control_task(void *args) +{ + bldc_control_t *control = (bldc_control_t *)args; + while (1) { + if (xSemaphoreTake(control->xSemaphore, portMAX_DELAY) == pdTRUE) + { + control->control_operation(args); + } + } +} + +esp_err_t bldc_control_init(bldc_control_handle_t *handle, bldc_control_config_t *config) +{ + BLDC_CHECK(config != NULL, "config is NULL", ESP_ERR_INVALID_ARG); + bldc_control_t *control = (bldc_control_t *)calloc(1, sizeof(bldc_control_t)); + BLDC_CHECK(control, "calloc failed", ESP_ERR_NO_MEM); + esp_err_t ret = ESP_OK; + + // Init gptimer + bldc_gptimer_config_t gptimer_config = { + .gptimer = &control->gptimer, + .cbs.on_alarm = &bldc_control_gptimer_cb, + .user_data = (void *)control, + .alarm_count_us = ALARM_COUNT_US, + }; + ret = bldc_gptimer_init(&gptimer_config); + BLDC_CHECK_GOTO(ret == ESP_OK, "Failed to init gptimer", deinit); + + control->xSemaphore = xSemaphoreCreateBinary(); + BLDC_CHECK_GOTO(control->xSemaphore != NULL, "xSemaphoreCreateBinary failed", deinit); + + if (config->debug_config.if_debug) { + control->control_operation = config->debug_config.debug_operation; + } else { + control->control_operation = &bldc_control_operation; + } + + xTaskCreate(bldc_control_task, "bldc_control_task", 1024 * 4, control, 10, NULL); + + switch (config->alignment_mode) + { + case ALIGNMENT_COMPARER: + ret = bldc_zero_cross_comparer_init(&control->zero_cross_handle, &config->zero_cross_comparer_config, &control->control_param); + BLDC_CHECK_GOTO(ret == ESP_OK, "bldc_zero_cross_comparer_init failed", deinit); + control->zero_cross = &bldc_zero_cross_comparer_operation; + break; +#if CONFIG_SOC_MCPWM_SUPPORTED + case ALIGNMENT_ADC: + ret = bldc_zero_cross_adc_init(&control->zero_cross_handle, &config->zero_cross_adc_config, &control->control_param); + BLDC_CHECK_GOTO(ret == ESP_OK, "bldc_zero_cross_comparer_init failed", deinit); + control->zero_cross = &bldc_zero_cross_adc_operation; + if (config->six_step_config.upper_switch_config.control_type != CONTROL_TYPE_MCPWM) { + ESP_LOGE(TAG, "error: control type must be mcpwm"); + goto deinit; + } + mcpwm_timer_event_callbacks_t cbs = { + .on_full = &read_adc_on_full, + }; + config->six_step_config.upper_switch_config.bldc_mcpwm.cbs = &cbs; + config->six_step_config.upper_switch_config.bldc_mcpwm.timer_cb_user_data = (void *)control->zero_cross_handle; + break; +#endif + default: + ESP_LOGE(TAG, "control_mode error"); + goto deinit; + break; + } + control->alignment_mode = config->alignment_mode; + + switch (config->control_mode) { + case BLDC_SIX_STEP: + ret = bldc_six_step_init(&control->change_phase_handle, &config->six_step_config, &control->control_param); + BLDC_CHECK_GOTO(ret == ESP_OK, "bldc_six_step_init failed", deinit); + control->change_phase = &bldc_six_step_operation; + break; + default: + ESP_LOGE(TAG, "control_mode error"); + goto deinit; + break; + } + control->control_mode = config->control_mode; + + if (config->speed_mode == SPEED_CLOSED_LOOP) { + pid_ctrl_config_t pid_ctrl_config = { + .init_param = PID_CTRL_PARAMETER_DEFAULT(), + }; + pid_new_control_block(&pid_ctrl_config, &control->pid); + } + + control->speed_mode = config->speed_mode; + + *handle = (bldc_control_handle_t)control; + + return ESP_OK; +deinit: + if (control->change_phase_handle) + { + // todo change_phase_deinit + free(control->change_phase_handle); + } + if (control->xSemaphore) + { + vSemaphoreDelete(control->xSemaphore); + } + if (control->gptimer) + { + bldc_gptimer_deinit(control->gptimer); + } + if (control) + { + free(control); + } + return ret; +} + +esp_err_t bldc_control_deinit(bldc_control_handle_t *handle) +{ + bldc_control_t *control = (bldc_control_t *)handle; + BLDC_CHECK(control != NULL, "control is NULL", ESP_ERR_INVALID_ARG); + esp_err_t err = ESP_OK; + + switch (control->alignment_mode) { + case ALIGNMENT_COMPARER: + err = bldc_zero_cross_comparer_deinit(control->zero_cross_handle); + BLDC_CHECK(err == ESP_OK, "bldc_zero_cross_comparer_deinit failed", ESP_FAIL); + break; + default: + break; + } + + switch (control->control_mode) { + case BLDC_SIX_STEP: + err = bldc_six_step_deinit(control->change_phase_handle); + BLDC_CHECK(err == ESP_OK, "bldc_six_step_deinit failed", ESP_FAIL); + break; + default: + break; + } + + if (control->xSemaphore) + { + vSemaphoreDelete(control->xSemaphore); + } + if (control->gptimer) + { + bldc_gptimer_deinit(control->gptimer); + } + + free(control); + return ESP_OK; +} + +esp_err_t bldc_control_start(bldc_control_handle_t *handle, uint32_t expect_Speed_rpm) +{ + bldc_control_t *control = (bldc_control_t *)handle; + switch (control->control_mode) { + case BLDC_SIX_STEP: + bldc_six_step_start(control->change_phase_handle); + break; + default: + break; + } + + switch (control->alignment_mode) { + case ALIGNMENT_COMPARER: + bldc_zero_cross_comparer_start(control->zero_cross_handle); + break; +#ifdef CONFIG_SOC_MCPWM_SUPPORTED + case ALIGNMENT_ADC: + bldc_zero_cross_adc_start(control->zero_cross_handle); + break; +#endif + default: + break; + } + + control->control_param.expect_speed_rpm = expect_Speed_rpm; + control->control_param.charge_time = 0; + control->control_param.inject_adc_read = 0; + control->control_param.inject_count = 0; + control->control_param.adc_bemf_phase = 0; + + pid_reset_ctrl_block(control->pid); + + bldc_control_dispatch_event(BLDC_CONTROL_START, NULL, 0); +#if INJECT_ENABLE + control->control_param.status = INJECT; +#else + control->control_param.status = ALIGNMENT; +#endif // INJECT_ENABLE + bldc_gptimer_start(control->gptimer); + + return ESP_OK; +} + +esp_err_t bldc_control_stop(bldc_control_handle_t *handle) +{ + bldc_control_t *control = (bldc_control_t *)handle; + esp_err_t err = gptimer_stop(control->gptimer); + BLDC_CHECK(err == ESP_OK, "gptimer_stop failed", ESP_FAIL); + + switch (control->control_mode) { + case BLDC_SIX_STEP: + err = bldc_six_step_stop(control->change_phase_handle); + BLDC_CHECK(err == ESP_OK, "bldc_six_step_stop failed", ESP_FAIL); + break; + default: + break; + } + + switch (control->alignment_mode) { + case ALIGNMENT_COMPARER: + break; +#if CONFIG_SOC_MCPWM_SUPPORTED + case ALIGNMENT_ADC: + break; +#endif + default: + break; + } + return ESP_OK; +} + +int bldc_control_get_speed_rpm(bldc_control_handle_t *handle) +{ + bldc_control_t *control = (bldc_control_t *)handle; + BLDC_CHECK(control != NULL, "control is NULL", 1); + BLDC_CONTROL_ENTER_CRITICAL(); + int speed_rpm = control->control_param.speed_rpm; + BLDC_CONTROL_EXIT_CRITICAL(); + return speed_rpm; +} + +esp_err_t bldc_control_set_speed_rpm(bldc_control_handle_t *handle, int speed_rpm) +{ + bldc_control_t *control = (bldc_control_t *)handle; + BLDC_CHECK(control != NULL, "control is NULL", ESP_ERR_INVALID_STATE); + BLDC_CONTROL_ENTER_CRITICAL(); + control->control_param.expect_speed_rpm = speed_rpm; + BLDC_CONTROL_EXIT_CRITICAL(); + return ESP_OK; +} + +dir_enum_t bldc_control_get_dir(bldc_control_handle_t *handle) +{ + bldc_control_t *control = (bldc_control_t *)handle; + BLDC_CHECK(control != NULL, "control is NULL", -1); + BLDC_CONTROL_ENTER_CRITICAL(); + dir_enum_t dir = control->control_param.dir; + BLDC_CONTROL_EXIT_CRITICAL(); + return dir; +} + +esp_err_t bldc_control_set_dir(bldc_control_handle_t *handle, dir_enum_t dir) +{ + bldc_control_t *control = (bldc_control_t *)handle; + BLDC_CHECK(control != NULL, "control is NULL", ESP_ERR_INVALID_STATE); + BLDC_CONTROL_ENTER_CRITICAL(); + control->control_param.dir = dir; + BLDC_CONTROL_EXIT_CRITICAL(); + return ESP_OK; +} + +int bldc_control_get_duty(bldc_control_handle_t *handle) +{ + bldc_control_t *control = (bldc_control_t *)handle; + BLDC_CHECK(control != NULL, "control is NULL", -1); + BLDC_CONTROL_ENTER_CRITICAL(); + uint16_t duty = control->control_param.duty; + BLDC_CONTROL_EXIT_CRITICAL(); + return duty; +} + +esp_err_t bldc_control_set_duty(bldc_control_handle_t *handle, uint16_t duty) +{ + bldc_control_t *control = (bldc_control_t *)handle; + BLDC_CHECK(control != NULL, "control is NULL", -1); + BLDC_CONTROL_ENTER_CRITICAL(); + control->control_param.duty = duty; + BLDC_CONTROL_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t bldc_control_update_pid(bldc_control_handle_t *handle, const pid_ctrl_parameter_t *params) +{ + bldc_control_t *control = (bldc_control_t *)handle; + BLDC_CHECK(control != NULL, "control is NULL", -1); + esp_err_t ret = ESP_OK; + BLDC_CONTROL_ENTER_CRITICAL(); + ret = pid_update_parameters(control->pid, params); + BLDC_CONTROL_EXIT_CRITICAL(); + BLDC_CHECK(ret == ESP_OK, "pid_update_parameters failed", ESP_FAIL); + return pid_reset_ctrl_block(control->pid); +} + +static esp_err_t bldc_control_operation(void *handle) +{ + bldc_control_t *control = (bldc_control_t *)handle; + + switch (control->control_param.status) + { + case INJECT: + if (--control->delayCnt <= 0) + { + if (control->change_phase(control->change_phase_handle) == 1) { + control->control_param.status = ALIGNMENT; + ESP_LOGI(TAG, "INJECT OK\n"); + } + control->delayCnt = control->control_param.charge_time; + } + break; + case ALIGNMENT: + bldc_control_dispatch_event(BLDC_CONTROL_ALIGNMENT, NULL, 0); + control->change_phase(control->change_phase_handle); + control->control_param.status = DRAG; + bldc_control_dispatch_event(BLDC_CONTROL_DRAG, NULL, 0); + /*!< To fix strong dragging to a certain phase, a delay of ALIGNMENTNMS is needed. */ + control->delayCnt = ALIGNMENTNMS; + break; + case DRAG: + if (--control->delayCnt <= 0) + { + control->change_phase(control->change_phase_handle); + control->delayCnt = control->control_param.drag_time; + } + + /*!< Determining whether to stabilize the zero crossing point */ + if (control->zero_cross(control->zero_cross_handle) == 1) { + ESP_LOGI(TAG, "CLOSED_LOOP"); + control->control_param.status = CLOSED_LOOP; + bldc_control_dispatch_event(BLDC_CONTROL_CLOSED_LOOP, NULL, 0); + control->delayCnt = control->control_param.filter_delay; + } + + if (control->control_param.filter_failed_count > 15000) + { + control->control_param.filter_failed_count = 0; + control->control_param.speed_rpm = 0; + control->control_param.duty = 0; + control->control_param.status = BLOCKED; + bldc_control_dispatch_event(BLDC_CONTROL_BLOCKED, NULL, 0); + } + break; + case CLOSED_LOOP: + if (control->control_param.phase_cnt != control->control_param.phase_cnt_prev) { + control->delayCnt--; + } + control->zero_cross(control->zero_cross_handle); + + if (control->control_param.filter_failed_count > 15000) { + control->control_param.filter_failed_count = 0; + control->control_param.speed_rpm = 0; + control->control_param.duty = 0; + + control->control_param.status = BLOCKED; + bldc_control_dispatch_event(BLDC_CONTROL_BLOCKED, NULL, 0); + } else if (control->delayCnt <= 0) { + control->change_phase(control->change_phase_handle); + control->delayCnt = control->control_param.filter_delay; + } + + if (control->speed_mode == SPEED_CLOSED_LOOP) { + float duty = 0; + int32_t a = control->control_param.expect_speed_rpm - control->control_param.speed_rpm; + pid_compute(control->pid, (float)(a), &duty); + if (duty < PWM_DUTYCYCLE_20 && duty >= 0) { + duty = PWM_DUTYCYCLE_20; + } else if (duty > -PWM_DUTYCYCLE_20 && duty < 0) { + duty = -PWM_DUTYCYCLE_20; + } + control->control_param.duty = abs((int)duty); + } + + break; + case BLOCKED: + ESP_LOGE(TAG, "BLOCKED"); + control->change_phase(control->change_phase_handle); + control->control_param.status = FAULT; + break; + case STOP: + bldc_control_dispatch_event(BLDC_CONTROL_STOP, NULL, 0); + break; + case FAULT: + break; + } + return ESP_OK; +} diff --git a/components/motor/esp_sensorless_bldc_control/common/include/bldc_common.h b/components/motor/esp_sensorless_bldc_control/common/include/bldc_common.h new file mode 100644 index 000000000..5fc3d77af --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/common/include/bldc_common.h @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bldc_helper.h" +#include "bldc_config.h" +#include "bldc_pid.h" + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/common/include/bldc_config.h b/components/motor/esp_sensorless_bldc_control/common/include/bldc_config.h new file mode 100644 index 000000000..6ff854a52 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/common/include/bldc_config.h @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#if __has_include("bldc_user_cfg.h") +#include "bldc_user_cfg.h" +#else +#error "bldc_user_cfg.h not found, must provide by user" +#endif // BLDC_USER_CFG_H + +#define COMPARER_RPM_CALCULATION_COEFFICIENT (((1000000 / ALARM_COUNT_US) / (2.0 * POLE_PAIR)) * 60) +#define ADC_RPM_CALCULATION_COEFFICIENT (((1000000 / ALARM_COUNT_US) / (1.0 * POLE_PAIR)) * 60) + +#define SKIP_INVALID_SPEED_CALCULATION ((uint32_t)(COMPARER_RPM_CALCULATION_COEFFICIENT / (SPEED_MAX_RPM * MAX_SPEED_MEASUREMENT_FACTOR))) + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/common/include/bldc_helper.h b/components/motor/esp_sensorless_bldc_control/common/include/bldc_helper.h new file mode 100644 index 000000000..bb15a6188 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/common/include/bldc_helper.h @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_log.h" + +#define BLDC_CHECK(a, str, ret, ...) if(!(a)) { \ + ESP_LOGE(TAG,"%s:%d (%s):" str, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \ + return (ret); \ + } + +#define BLDC_CHECK_ABORT(a, str, ...) if(!(a)) { \ + ESP_LOGE(TAG,"%s:%d (%s):" str, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \ + abort(); \ + } + +#define BLDC_CHECK_RETURN_VOID(a, str, ...) if(!(a)) { \ + ESP_LOGE(TAG,"%s:%d (%s):" str, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \ + return; \ + } + +#define BLDC_CHECK_CONTINUE(a, str, ...) if(!(a)) { \ + ESP_LOGE(TAG,"%s:%d (%s):" str, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \ + } + +#define BLDC_CHECK_GOTO(a, str, label, ...) if(!(a)) { \ + ESP_LOGE(TAG,"%s:%d (%s):" str, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \ + goto label; \ + } + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/common/include/bldc_pid.h b/components/motor/esp_sensorless_bldc_control/common/include/bldc_pid.h new file mode 100644 index 000000000..cf80ce6b3 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/common/include/bldc_pid.h @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "pid_ctrl.h" + +#define BLDC_LPF(valuePrev, value, alpha) ((valuePrev) = (valuePrev) * (alpha) + (value) * (1 - (alpha))) + +#define PID_CTRL_PARAMETER_DEFAULT() \ +{ \ + .kp = SPEED_KP, \ + .ki = SPEED_KI, \ + .kd = SPEED_KD, \ + .max_output = SPEED_MAX_OUTPUT, \ + .min_output = SPEED_MIN_OUTPUT, \ + .max_integral = SPEED_MAX_INTEGRAL, \ + .min_integral = SPEED_MIN_INTEGRAL, \ + .cal_type = SPEED_CAL_TYPE, \ +} + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/control/bldc_six_step.c b/components/motor/esp_sensorless_bldc_control/control/bldc_six_step.c new file mode 100644 index 000000000..967556bed --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/control/bldc_six_step.c @@ -0,0 +1,567 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bldc_helper.h" +#include "bldc_six_step.h" +#include "bldc_driver.h" +#include "bldc_snls_lib.h" +#include "bldc_config.h" + +static const char *TAG = "bldc_six_step"; + +typedef struct bldc_six_step { + bool _can_be_enable; + bool _if_enable; + bool enable_level; + bool lower_active_level; + control_param_t *control_param; + control_type_t upper_switch_type; + control_type_t lower_switch_type; + void *phase_upper_pin[PHASE_MAX]; + void *phase_lower_pin[PHASE_MAX]; + void *phase_enable_pin[PHASE_MAX]; + esp_err_t (*set_upper_pwm)(void *phase, uint32_t duty); + esp_err_t (*set_lower_pwm)(void *phase, uint32_t duty); + esp_err_t (*set_gpio)(void *phase, uint32_t duty); + void (*set_phase_duty)(struct bldc_six_step *six_step, phase_enum_t phase, uint32_t H_duty, uint32_t L_duty); + uint8_t pos_check_stage; +} bldc_six_step_t; + +static void bldc_six_step_set_Hpwm_Lgpio(bldc_six_step_t *six_step, phase_enum_t phase, uint32_t H_duty, uint32_t L_duty) +{ + six_step->set_upper_pwm(six_step->phase_upper_pin[phase], H_duty); + six_step->set_gpio(six_step->phase_lower_pin[phase], L_duty); +} + +static void bldc_six_step_set_Hpwm_Lpwm(bldc_six_step_t *six_step, phase_enum_t phase, uint32_t H_duty, uint32_t L_duty) +{ + six_step->set_upper_pwm(six_step->phase_upper_pin[phase], H_duty); + six_step->set_lower_pwm(six_step->phase_lower_pin[phase], L_duty); +} + +static void bldc_six_step_UVW_stop(bldc_six_step_t *six_step, uint32_t Lduty) +{ + six_step->set_phase_duty(six_step, PHASE_U, 0, Lduty); + six_step->set_phase_duty(six_step, PHASE_V, 0, Lduty); + six_step->set_phase_duty(six_step, PHASE_W, 0, Lduty); +} + +static void bldc_six_step_UphaseH_VphaseL(bldc_six_step_t *six_step, uint32_t Hduty, uint32_t Lduty) +{ + six_step->set_phase_duty(six_step, PHASE_U, Hduty, !Lduty); + six_step->set_phase_duty(six_step, PHASE_V, 0, Lduty); + six_step->set_phase_duty(six_step, PHASE_W, 0, !Lduty); +} + +static void bldc_six_step_VphaseH_UphaseL(bldc_six_step_t *six_step, uint32_t Hduty, uint32_t Lduty) +{ + six_step->set_phase_duty(six_step, PHASE_U, 0, Lduty); + six_step->set_phase_duty(six_step, PHASE_V, Hduty, !Lduty); + six_step->set_phase_duty(six_step, PHASE_W, 0, !Lduty); +} + +static void bldc_six_step_WphaseH_UphaseL(bldc_six_step_t *six_step, uint32_t Hduty, uint32_t Lduty) +{ + six_step->set_phase_duty(six_step, PHASE_U, 0, Lduty); + six_step->set_phase_duty(six_step, PHASE_V, 0, !Lduty); + six_step->set_phase_duty(six_step, PHASE_W, Hduty, !Lduty); +} + +static void bldc_six_step_WphaseH_VphaseL(bldc_six_step_t *six_step, uint32_t Hduty, uint32_t Lduty) +{ + six_step->set_phase_duty(six_step, PHASE_U, 0, !Lduty); + six_step->set_phase_duty(six_step, PHASE_V, 0, Lduty); + six_step->set_phase_duty(six_step, PHASE_W, Hduty, !Lduty); +} + +static void bldc_six_step_UphaseH_WphaseL(bldc_six_step_t *six_step, uint32_t Hduty, uint32_t Lduty) +{ + six_step->set_phase_duty(six_step, PHASE_U, Hduty, !Lduty); + six_step->set_phase_duty(six_step, PHASE_V, 0, !Lduty); + six_step->set_phase_duty(six_step, PHASE_W, 0, Lduty); +} + +static void bldc_six_step_VphaseH_WphaseL(bldc_six_step_t *six_step, uint32_t Hduty, uint32_t Lduty) +{ + six_step->set_phase_duty(six_step, PHASE_U, 0, !Lduty); + six_step->set_phase_duty(six_step, PHASE_V, Hduty, !Lduty); + six_step->set_phase_duty(six_step, PHASE_W, 0, Lduty); +} + +static void bldc_six_step_UVphaseH_WphaseL(bldc_six_step_t *six_step, uint32_t Hduty, uint32_t Lduty) +{ + six_step->set_phase_duty(six_step, PHASE_U, Hduty, !Lduty); + six_step->set_phase_duty(six_step, PHASE_V, Hduty, !Lduty); + six_step->set_phase_duty(six_step, PHASE_W, 0, Lduty); +} + +static void bldc_six_step_WphaseH_UVphaseL(bldc_six_step_t *six_step, uint32_t Hduty, uint32_t Lduty) +{ + six_step->set_phase_duty(six_step, PHASE_U, 0, Lduty); + six_step->set_phase_duty(six_step, PHASE_V, 0, Lduty); + six_step->set_phase_duty(six_step, PHASE_W, Hduty, !Lduty); +} + +static void bldc_six_step_UWphaseH_VphaseL(bldc_six_step_t *six_step, uint32_t Hduty, uint32_t Lduty) +{ + six_step->set_phase_duty(six_step, PHASE_U, Hduty, !Lduty); + six_step->set_phase_duty(six_step, PHASE_V, 0, Lduty); + six_step->set_phase_duty(six_step, PHASE_W, Hduty, !Lduty); +} + +static void bldc_six_step_VphaseH_UWphaseL(bldc_six_step_t *six_step, uint32_t Hduty, uint32_t Lduty) +{ + six_step->set_phase_duty(six_step, PHASE_U, 0, Lduty); + six_step->set_phase_duty(six_step, PHASE_V, Hduty, !Lduty); + six_step->set_phase_duty(six_step, PHASE_W, 0, Lduty); +} + +static void bldc_six_step_VWphaseH_UphaseL(bldc_six_step_t *six_step, uint32_t Hduty, uint32_t Lduty) +{ + six_step->set_phase_duty(six_step, PHASE_U, 0, Lduty); + six_step->set_phase_duty(six_step, PHASE_V, Hduty, !Lduty); + six_step->set_phase_duty(six_step, PHASE_W, Hduty, !Lduty); +} + +static void bldc_six_step_UphaseH_VWphaseL(bldc_six_step_t *six_step, uint32_t Hduty, uint32_t Lduty) +{ + six_step->set_phase_duty(six_step, PHASE_U, Hduty, !Lduty); + six_step->set_phase_duty(six_step, PHASE_V, 0, Lduty); + six_step->set_phase_duty(six_step, PHASE_W, 0, Lduty); +} + +/** + * @brief Pulse injection sequence + * + */ +static void (*injectArray[6])(bldc_six_step_t *six_step, uint32_t Hduty, uint32_t Lduty) = { + &bldc_six_step_UVphaseH_WphaseL, + &bldc_six_step_WphaseH_UVphaseL, + &bldc_six_step_UWphaseH_VphaseL, + &bldc_six_step_VphaseH_UWphaseL, + &bldc_six_step_VWphaseH_UphaseL, + &bldc_six_step_UphaseH_VWphaseL, +}; + +uint8_t bldc_six_step_inject(bldc_six_step_handle_t *handle) +{ + bldc_six_step_t *six_step = (bldc_six_step_t *)handle; + + six_step->control_param->charge_time = 0; + /*!< The value of adc is being read. Skip processing. */ + if (six_step->control_param->inject_adc_read) { + six_step->control_param->inject_adc_read = false; + return 0; + } + + switch (six_step->pos_check_stage) + { + case 0: + six_step->pos_check_stage = 10; + goto charge; + break; + case 10: + six_step->pos_check_stage = 1; + goto inject; + break; + case 1: + six_step->pos_check_stage = 20; + goto charge; + break; + case 20: + six_step->pos_check_stage = 2; + goto inject; + break; + case 2: + six_step->pos_check_stage = 30; + goto charge; + break; + case 30: + six_step->pos_check_stage = 3; + goto inject; + break; + case 3: + six_step->pos_check_stage = 40; + goto charge; + break; + case 40: + six_step->pos_check_stage = 4; + goto inject; + break; + case 4: + six_step->pos_check_stage = 50; + goto charge; + break; + case 50: + six_step->pos_check_stage = 5; + goto inject; + break; + case 5: + six_step->pos_check_stage = 60; + goto charge; + break; + case 60: + six_step->pos_check_stage = 6; + goto inject; + break; + case 6: + bldc_six_step_UVW_stop(six_step, !six_step->lower_active_level); + six_step->control_param->phase_cnt = inject_get_phase(six_step->control_param->inject_adc_value); + ESP_LOGD(TAG, "inject detect phase: %d", six_step->control_param->phase_cnt); + ESP_LOGD(TAG, "inject_adc_value: %"PRId32" %"PRId32" %"PRId32" %"PRId32" %"PRId32" %"PRId32"", + six_step->control_param->inject_adc_value[0], six_step->control_param->inject_adc_value[1], + six_step->control_param->inject_adc_value[2], six_step->control_param->inject_adc_value[3], + six_step->control_param->inject_adc_value[4], six_step->control_param->inject_adc_value[5]); + if (six_step->control_param->phase_cnt < 1 || six_step->control_param->phase_cnt > 6) + { + //TODO: error handle make again + six_step->control_param->phase_cnt = 1; + } + return 1; + break; + } + return 0; + +charge: + bldc_six_step_UVW_stop(six_step, !six_step->lower_active_level); + six_step->control_param->charge_time = CHARGE_TIME; + return 0; +inject: + six_step->control_param->inject_count++; + injectArray[six_step->control_param->inject_count -1](six_step, DUTY_MAX * 0.95, six_step->lower_active_level); + six_step->control_param->inject_adc_read = true; + six_step->control_param->charge_time = CHARGE_TIME; + return 0; +} + +void bldc_six_step_turn(bldc_six_step_handle_t *handle) +{ + bldc_six_step_t *six_step = (bldc_six_step_t *)handle; + if (six_step->control_param->phase_cnt > 6) { + six_step->control_param->phase_cnt = 1; + } else if (six_step->control_param->phase_cnt < 1) { + six_step->control_param->phase_cnt = 6; + } + + if (six_step->control_param->dir == CCW) + { + switch (six_step->control_param->phase_cnt) + { + case 1: + bldc_six_step_UphaseH_VphaseL(six_step, six_step->control_param->duty, six_step->lower_active_level); + six_step->control_param->adc_bemf_phase = PHASE_W; + break; + case 2: + bldc_six_step_UphaseH_WphaseL(six_step, six_step->control_param->duty, six_step->lower_active_level); + six_step->control_param->adc_bemf_phase = PHASE_V; + break; + case 3: + bldc_six_step_VphaseH_WphaseL(six_step, six_step->control_param->duty, six_step->lower_active_level); + six_step->control_param->adc_bemf_phase = PHASE_U; + break; + case 4: + bldc_six_step_VphaseH_UphaseL(six_step, six_step->control_param->duty, six_step->lower_active_level); + six_step->control_param->adc_bemf_phase = PHASE_W; + break; + case 5: + bldc_six_step_WphaseH_UphaseL(six_step, six_step->control_param->duty, six_step->lower_active_level); + six_step->control_param->adc_bemf_phase = PHASE_V; + break; + case 6: + bldc_six_step_WphaseH_VphaseL(six_step, six_step->control_param->duty, six_step->lower_active_level); + six_step->control_param->adc_bemf_phase = PHASE_U; + break; + default: + break; + } + } + else if (six_step->control_param->dir == CW) + { + switch (six_step->control_param->phase_cnt) + { + case 1: + bldc_six_step_UphaseH_VphaseL(six_step, six_step->control_param->duty, six_step->lower_active_level); + six_step->control_param->adc_bemf_phase = PHASE_W; + break; + case 2: + bldc_six_step_WphaseH_VphaseL(six_step, six_step->control_param->duty, six_step->lower_active_level); + six_step->control_param->adc_bemf_phase = PHASE_U; + break; + case 3: + bldc_six_step_WphaseH_UphaseL(six_step, six_step->control_param->duty, six_step->lower_active_level); + six_step->control_param->adc_bemf_phase = PHASE_V; + break; + case 4: + bldc_six_step_VphaseH_UphaseL(six_step, six_step->control_param->duty, six_step->lower_active_level); + six_step->control_param->adc_bemf_phase = PHASE_W; + break; + case 5: + bldc_six_step_VphaseH_WphaseL(six_step, six_step->control_param->duty, six_step->lower_active_level); + six_step->control_param->adc_bemf_phase = PHASE_U; + break; + case 6: + bldc_six_step_UphaseH_WphaseL(six_step, six_step->control_param->duty, six_step->lower_active_level); + six_step->control_param->adc_bemf_phase = PHASE_V; + break; + default: + break; + } + } + six_step->control_param->phase_cnt_prev = six_step->control_param->phase_cnt; + six_step->control_param->phase_change_done = true; +} + +esp_err_t bldc_six_step_init(bldc_six_step_handle_t *handle, bldc_six_step_config_t *config, control_param_t *control_param) +{ + BLDC_CHECK(config != NULL, "six_step_config can not be NULL", ESP_ERR_INVALID_ARG); + BLDC_CHECK(config->upper_switch_config.control_type != CONTROL_TYPE_GPIO, "upper_switch_config can not be GPIO", ESP_ERR_INVALID_ARG); + BLDC_CHECK(control_param != NULL, "control_param can not be NULL", ESP_ERR_INVALID_ARG); + bldc_six_step_t *six_step = (bldc_six_step_t *)calloc(1, sizeof(bldc_six_step_t)); + BLDC_CHECK(six_step != NULL, "calloc error", ESP_ERR_NO_MEM); + + esp_err_t err = ESP_OK; + six_step->control_param = control_param; + /*upper_switch_config.control_type) + { + case CONTROL_TYPE_LEDC: + { + err = bldc_ledc_init(&config->upper_switch_config.bldc_ledc); + BLDC_CHECK_GOTO(err == ESP_OK, "ledc init error", deinit); + for (int i = 0; i < PHASE_MAX; i++) + { + six_step->phase_upper_pin[i] = (void *)config->upper_switch_config.bldc_ledc.ledc_channel[i]; + } + six_step->set_upper_pwm = &bldc_ledc_set_duty; + six_step->control_param->duty_max = DUTY_MAX; + } + break; +#if CONFIG_SOC_MCPWM_SUPPORTED + case CONTROL_TYPE_MCPWM: + err = bldc_mcpwm_init(&config->upper_switch_config.bldc_mcpwm, six_step->phase_upper_pin); + BLDC_CHECK_GOTO(err == ESP_OK, "mcpwm init error", deinit); + six_step->set_upper_pwm = &bldc_mcpwm_set_duty; + six_step->control_param->duty_max = DUTY_MAX; + break; +#endif + default: + return ESP_ERR_INVALID_STATE; + } + six_step->upper_switch_type = config->upper_switch_config.control_type; + + /*lower_switch_config.control_type) + { + case CONTROL_TYPE_LEDC: + { + err = bldc_ledc_init(&config->lower_switch_config.bldc_ledc); + BLDC_CHECK_GOTO(err == ESP_OK, "gpio init error", deinit); + for (int i = 0; i < PHASE_MAX; i++) + { + six_step->phase_lower_pin[i] = (void *)config->lower_switch_config.bldc_ledc.ledc_channel[i]; + } + six_step->set_lower_pwm = &bldc_ledc_set_duty; + six_step->set_phase_duty = &bldc_six_step_set_Hpwm_Lpwm; + } + break; +#if CONFIG_SOC_MCPWM_SUPPORTED + case CONTROL_TYPE_MCPWM: + err = bldc_mcpwm_init(&config->lower_switch_config.bldc_mcpwm, six_step->phase_lower_pin); + BLDC_CHECK_GOTO(err == ESP_OK, "mcpwm init error", deinit); + six_step->set_lower_pwm = &bldc_mcpwm_set_duty; + six_step->set_phase_duty = &bldc_six_step_set_Hpwm_Lpwm; + break; +#endif + case CONTROL_TYPE_GPIO: + { + for (int i = 0; i < PHASE_MAX; i++) + { + err = bldc_gpio_init(&config->lower_switch_config.bldc_gpio[i]); + BLDC_CHECK_GOTO(err == ESP_OK, "gpio init error", deinit); + six_step->phase_lower_pin[i] = (void *)config->lower_switch_config.bldc_gpio[i].gpio_num; + } + six_step->set_gpio = &bldc_gpio_set_level; + six_step->set_phase_duty = &bldc_six_step_set_Hpwm_Lgpio; + } + break; + default: + return ESP_ERR_INVALID_STATE; + } + + six_step->lower_switch_type = config->lower_switch_config.control_type; + six_step->lower_active_level = config->lower_switch_active_level; + + if (config->mos_en_config.has_enable) + { + for (int i = 0; i < PHASE_MAX; i++) + { + err = bldc_gpio_init(&config->mos_en_config.en_gpio[i]); + BLDC_CHECK_GOTO(err == ESP_OK, "gpio init error", deinit); + six_step->phase_enable_pin[i] = (void *)config->mos_en_config.en_gpio[i].gpio_num; + } + six_step->enable_level = config->mos_en_config.en_level; + six_step->_can_be_enable = true; + } else { + six_step->_can_be_enable = false; + } + + *handle = (bldc_six_step_handle_t)six_step; + + return ESP_OK; +deinit: + if (six_step) + { + free(six_step); + } + return ESP_FAIL; +} + +esp_err_t bldc_six_step_deinit(bldc_six_step_handle_t handle) +{ + bldc_six_step_t *six_step = (bldc_six_step_t *)handle; + BLDC_CHECK(six_step != NULL, "six_step has not been init", ESP_ERR_INVALID_ARG); + esp_err_t err = ESP_OK; + switch (six_step->upper_switch_type) { +#if CONFIG_SOC_MCPWM_SUPPORTED + case CONTROL_TYPE_MCPWM: + err = bldc_mcpwm_deinit(six_step->phase_upper_pin); + BLDC_CHECK(err == ESP_OK, "mcpwm deinit error", ESP_FAIL); + break; +#endif + default: + break; + } + + switch (six_step->lower_switch_type) { +#if CONFIG_SOC_MCPWM_SUPPORTED + case CONTROL_TYPE_MCPWM: + err = bldc_mcpwm_deinit(six_step->phase_lower_pin); + BLDC_CHECK(err == ESP_OK, "mcpwm deinit error", ESP_FAIL); + break; +#endif + case CONTROL_TYPE_GPIO: + if (six_step->_can_be_enable) + { + for (int i = 0; i < PHASE_MAX; i++) + { + err = bldc_gpio_deinit((uint32_t)six_step->phase_enable_pin[i]); + BLDC_CHECK(err == ESP_OK, "gpio deinit error", ESP_FAIL); + } + } + default: + break; + } + + free(six_step); + return ESP_OK; +} + +static esp_err_t bldc_six_step_enable(bldc_six_step_handle_t handle) +{ + bldc_six_step_t *six_step = (bldc_six_step_t *)handle; + BLDC_CHECK(six_step != NULL, "six_step has not been init", ESP_ERR_INVALID_STATE); + if (six_step->_can_be_enable) + { + six_step->set_gpio(six_step->phase_enable_pin[PHASE_U], six_step->enable_level); + six_step->set_gpio(six_step->phase_enable_pin[PHASE_V], six_step->enable_level); + six_step->set_gpio(six_step->phase_enable_pin[PHASE_W], six_step->enable_level); + } + six_step->_if_enable = true; + return ESP_OK; +} + +static esp_err_t bldc_six_step_disable(bldc_six_step_handle_t handle) +{ + bldc_six_step_t *six_step = (bldc_six_step_t *)handle; + BLDC_CHECK(six_step != NULL, "six_step has not been init", ESP_ERR_INVALID_STATE); + if (six_step->_can_be_enable) + { + six_step->set_gpio(six_step->phase_enable_pin[PHASE_U], six_step->enable_level); + six_step->set_gpio(six_step->phase_enable_pin[PHASE_V], six_step->enable_level); + six_step->set_gpio(six_step->phase_enable_pin[PHASE_W], six_step->enable_level); + } + six_step->_if_enable = false; + return ESP_OK; +} + +esp_err_t bldc_six_step_start(bldc_six_step_handle_t handle) +{ + bldc_six_step_t *six_step = (bldc_six_step_t *)handle; + BLDC_CHECK(six_step != NULL, "six_step has not been init", ESP_ERR_INVALID_ARG); + BLDC_CHECK(six_step->_if_enable == false, "six_step has been enable", ESP_ERR_INVALID_ARG); + + bldc_six_step_enable(handle); + bldc_six_step_UVW_stop(six_step, !six_step->lower_active_level); + six_step->control_param->phase_cnt = 0; + six_step->control_param->phase_cnt_prev = 0; + six_step->control_param->duty = RAMP_DUTY_STA; + six_step->control_param->filter_delay = RAMP_TIM_STA; + six_step->pos_check_stage = 0; + return ESP_OK; +} + +esp_err_t bldc_six_step_stop(bldc_six_step_handle_t handle) +{ + bldc_six_step_t *six_step = (bldc_six_step_t *)handle; + BLDC_CHECK(six_step != NULL, "six_step has not been init", ESP_ERR_INVALID_ARG); + + bldc_six_step_UVW_stop(six_step, !six_step->lower_active_level); + bldc_six_step_disable(handle); + return ESP_OK; +} + +uint8_t bldc_six_step_operation(void *handle) +{ + bldc_six_step_t *six_step = (bldc_six_step_t *)handle; + if (six_step->control_param->status == CLOSED_LOOP) { + if (six_step->control_param->phase_cnt <= 0 || six_step->control_param->phase_cnt > 6) + { + return 0; + } + + if (six_step->control_param->phase_cnt_prev != six_step->control_param->phase_cnt) { + bldc_six_step_turn(handle); + } + } else if (six_step->control_param->status == DRAG) { + six_step->control_param->duty += RAMP_DUTY_INC; + six_step->control_param->drag_time -= (six_step->control_param->drag_time / RAMP_TIM_STEP) + 1; + if (six_step->control_param->drag_time < RAMP_TIM_END) + { + /*!< Zero crossing time interval */ + six_step->control_param->drag_time = RAMP_TIM_END; + } + if (six_step->control_param->duty < RAMP_DUTY_STA) + { + six_step->control_param->duty = RAMP_DUTY_STA; + } + else if (six_step->control_param->duty > RAMP_DUTY_END) + { + six_step->control_param->duty = RAMP_DUTY_END; + } + + /*!< Starting strong drag phase change */ + bldc_six_step_turn(handle); + if (++six_step->control_param->phase_cnt >= 7) { + six_step->control_param->phase_cnt = 1; + } + + } else if (six_step->control_param->status == ALIGNMENT) { + six_step->control_param->duty = ALIGNMENTDUTY; + /*!< Aligning the initial phase */ + bldc_six_step_turn(handle); + if (++six_step->control_param->phase_cnt >= 7) { + six_step->control_param->phase_cnt = 1; + } + six_step->control_param->duty = RAMP_DUTY_STA; + six_step->control_param->drag_time = RAMP_TIM_STA; + }else if (six_step->control_param->status == INJECT) { + /*!< Returns 1 if the initial phase is obtained after six injections, 0 otherwise. */ + return bldc_six_step_inject(handle); + } else if (six_step->control_param->status == BLOCKED || six_step->control_param->status == STOP) { + bldc_six_step_UVW_stop(six_step, !six_step->lower_active_level); + } + return 0; +} diff --git a/components/motor/esp_sensorless_bldc_control/control/bldc_zero_cross_adc.c b/components/motor/esp_sensorless_bldc_control/control/bldc_zero_cross_adc.c new file mode 100644 index 000000000..347099b89 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/control/bldc_zero_cross_adc.c @@ -0,0 +1,145 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bldc_zero_cross_adc.h" +#include "bldc_snls_lib.h" +#include "bldc_config.h" + +static const char *TAG = "bldc_zero_cross_adc"; + +typedef struct { + control_param_t *control_param; + adc_oneshot_unit_handle_t adc_handle; + adc_channel_t adc_channel[5]; + uint16_t avoid_continue_current; /*!< Avoid continuation current counting */ + uint32_t speed_cnt; +} bldc_zero_cross_adc_t; + +bool read_adc_on_full(mcpwm_timer_handle_t timer, const mcpwm_timer_event_data_t *edata, void *user_data) +{ + bldc_zero_cross_adc_t *zero_cross = (bldc_zero_cross_adc_t *)user_data; + if (zero_cross->control_param->status == INJECT) { + /*!< Read the injected ADC value */ + if (zero_cross->control_param->inject_adc_read) { + zero_cross->control_param->inject_adc_value[zero_cross->control_param->inject_count -1] = bldc_adc_read_isr(zero_cross->adc_handle, zero_cross->adc_channel[BUS_CHANNEL]); + /*!< ADC read complete. */ + } + } else { + /*!< Obtain the current phase voltage and power supply voltage. */ + zero_cross->control_param->adc_value[1] = bldc_adc_read_isr(zero_cross->adc_handle, zero_cross->adc_channel[POW_CHANNEL]); + zero_cross->control_param->adc_value[0] = bldc_adc_read_isr(zero_cross->adc_handle, zero_cross->adc_channel[zero_cross->control_param->adc_bemf_phase]); + } + + return false; +} + +esp_err_t bldc_zero_cross_adc_init(bldc_zero_cross_adc_handle_t *handle, bldc_zero_cross_adc_config_t *config, control_param_t *control_param) +{ + BLDC_CHECK(config != NULL, "bldc zero cross adc config is NULL", ESP_ERR_INVALID_ARG); + bldc_zero_cross_adc_t *zero_cross = (bldc_zero_cross_adc_t *)calloc(1, sizeof(bldc_zero_cross_adc_t)); + BLDC_CHECK(zero_cross != NULL, "calloc error", ESP_ERR_NO_MEM); + + zero_cross->control_param = control_param; + esp_err_t err = ESP_OK; + + bldc_adc_config_t adc_cfg = { + .adc_handle = config->adc_handle, + .adc_unit = config->adc_unit, + .chan_cfg = config->chan_cfg, + .adc_channel = config->adc_channel, + .adc_channel_num = 5, + }; + + /*!< Get adc handle */ + err = bldc_adc_init(&adc_cfg, &zero_cross->adc_handle); + BLDC_CHECK_GOTO(err == ESP_OK, "adc init error", deinit); + + for (int i = 0; i < 5; i++) { + zero_cross->adc_channel[i] = config->adc_channel[i]; + } + + *handle = (bldc_zero_cross_adc_handle_t)zero_cross; + return ESP_OK; + +deinit: + if (zero_cross != NULL) + { + free(zero_cross); + } + return ESP_FAIL; +} + +esp_err_t bldc_zero_cross_adc_deinit(bldc_zero_cross_adc_handle_t handle) +{ + BLDC_CHECK(handle != NULL, "bldc zero cross adc handle is NULL", ESP_ERR_INVALID_STATE); + bldc_zero_cross_adc_t *zero_cross = (bldc_zero_cross_adc_t *)handle; + esp_err_t err = ESP_OK; + err = bldc_adc_deinit(zero_cross->adc_handle); + BLDC_CHECK(err == ESP_OK, "adc deinit error", ESP_FAIL); + return ESP_OK; +} + +uint8_t bldc_zero_cross_adc_operation(void *handle) +{ + BLDC_CHECK(handle != NULL, "bldc zero cross adc handle is NULL", 0); + bldc_zero_cross_adc_t *zero_cross = (bldc_zero_cross_adc_t *)handle; + /*!< ADC injection phase */ + zero_cross->control_param->speed_count++; + if (zero_cross->control_param->status == DRAG) { + if (zero_cross->control_param->speed_count >= ENTER_CLOSE_TIME) { + zero_cross->control_param->speed_count = 0; + return 1; + } + } else { + zero_cross->speed_cnt++; + if (zero_cross->control_param->phase_change_done == true) { + + if (++zero_cross->avoid_continue_current >= 4) { + zero_cross->control_param->phase_cnt = zero_cross_adc_get_phase(zero_cross->control_param->phase_cnt, ZERO_REPEAT_TIME, zero_cross->control_param->adc_value); + } + + if (zero_cross->control_param->phase_cnt == zero_cross->control_param->phase_cnt_prev) { + zero_cross->control_param->filter_failed_count++; + } else { + zero_cross->control_param->filter_delay = zero_cross->control_param->speed_count * 3.0 / ZERO_CROSS_ADVANCE; /*!< Thirty-degree time delay. At some point compensation is required */ + zero_cross->control_param->phase_change_done = false; + zero_cross->control_param->speed_count = 0; + zero_cross->control_param->filter_failed_count=0; + zero_cross->avoid_continue_current=0; + } + + static bool speed_clac_flag = false; + /*!< Motor changed six phases */ + if (zero_cross->control_param->phase_cnt == 1 && !speed_clac_flag) { + speed_clac_flag = true; + if (zero_cross->speed_cnt < SKIP_INVALID_SPEED_CALCULATION) { + } else { + uint32_t speed_rpm = 0; + if (zero_cross->control_param->dir == CCW) { + /*!< ft/(n*c) * 60; where ft is the counting frequency, that is, the frequency of the cycle interrupt, n is the polar logarithm, c is the number of times to remember */ + speed_rpm = (uint32_t)(ADC_RPM_CALCULATION_COEFFICIENT / zero_cross->speed_cnt); + } else { + speed_rpm = -(uint32_t)(ADC_RPM_CALCULATION_COEFFICIENT / zero_cross->speed_cnt); + } + BLDC_LPF(zero_cross->control_param->speed_rpm, speed_rpm, 0.2); + } + zero_cross->speed_cnt = 0; + } else if (zero_cross->control_param->phase_cnt != 1){ + speed_clac_flag = false; + } + } + } + return 0; +} + +esp_err_t bldc_zero_cross_adc_start(bldc_zero_cross_adc_handle_t *handle) +{ + BLDC_CHECK(handle != NULL, "bldc zero cross adc handle is NULL", ESP_ERR_INVALID_STATE); + bldc_zero_cross_adc_t *zero_cross = (bldc_zero_cross_adc_t *)handle; + zero_cross->avoid_continue_current = 0; + zero_cross->speed_cnt = 0; + return ESP_OK; +} diff --git a/components/motor/esp_sensorless_bldc_control/control/bldc_zero_cross_comparer.c b/components/motor/esp_sensorless_bldc_control/control/bldc_zero_cross_comparer.c new file mode 100644 index 000000000..3d0573c6e --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/control/bldc_zero_cross_comparer.c @@ -0,0 +1,186 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_log.h" +#include "bldc_zero_cross_comparer.h" +#include "bldc_common.h" +#include "bldc_snls_lib.h" + +static const char *TAG = "bldc_zero_cross"; + +typedef struct { + control_param_t *control_param; + int alignment_pin[PHASE_MAX]; + int zero_stable_flag; /*!< Over-zero stabilization flag bit */ + uint32_t alignment_queue_value[PHASE_MAX]; + uint16_t queue_filter_state[PHASE_MAX]; /*!< State after three-phase filtering */ +} bldc_zero_cross_comparer_t; + +static void alignment_comparer_get_value(bldc_zero_cross_comparer_t *zero_cross) +{ + for (int i = 0; i < PHASE_MAX; i++) + { + zero_cross->alignment_queue_value[i] = zero_cross->alignment_queue_value[i] << 1; + zero_cross->alignment_queue_value[i] |= gpio_get_level(zero_cross->alignment_pin[i]); + } +} + +/** + * @brief emf edge monitoring + * @param {uint8_t} val + * @return {*} + */ +static uint8_t bldc_umef_edge(uint8_t val) +{ + static uint8_t oldval = 0; + if (oldval != val) + { + oldval = val; + + if (val == 0) + { + return 0; + } + else + { + return 1; + } + } + + return 2; +} + +esp_err_t bldc_zero_cross_comparer_init(bldc_zero_cross_comparer_handle_t *handle, bldc_zero_cross_comparer_config_t *config, control_param_t *control_param) +{ + BLDC_CHECK(config != NULL, "bldc zero cross config is NULL", ESP_ERR_INVALID_ARG); + bldc_zero_cross_comparer_t *zero_cross = (bldc_zero_cross_comparer_t *)calloc(1, sizeof(bldc_zero_cross_comparer_t)); + BLDC_CHECK(zero_cross != NULL, "calloc error", ESP_ERR_NO_MEM); + + zero_cross->control_param = control_param; + esp_err_t err = ESP_OK; + + for (int i = 0; i < PHASE_MAX; i++) + { + err = bldc_gpio_init(&config->comparer_gpio[i]); + BLDC_CHECK_GOTO(err == ESP_OK, "gpio init error", deinit); + zero_cross->alignment_pin[i] = config->comparer_gpio[i].gpio_num; + } + + *handle = (bldc_zero_cross_comparer_handle_t)zero_cross; + return ESP_OK; + +deinit: + if (zero_cross != NULL) + { + free(zero_cross); + } + return ESP_FAIL; +} + +esp_err_t bldc_zero_cross_comparer_deinit(bldc_zero_cross_comparer_handle_t handle) +{ + bldc_zero_cross_comparer_t *zero_cross = (bldc_zero_cross_comparer_t *)handle; + BLDC_CHECK(zero_cross != NULL, "bldc zero cross handle is NULL", ESP_ERR_INVALID_ARG); + + for (int i = 0; i < PHASE_MAX; i++) + { + gpio_reset_pin(zero_cross->alignment_pin[i]); + } + + free(zero_cross); + return ESP_OK; +} + +uint8_t bldc_zero_cross_comparer_operation(void *handle) +{ + BLDC_CHECK(handle != NULL, "bldc zero cross handle comparer is NULL", 0); + + bldc_zero_cross_comparer_t *zero_cross = (bldc_zero_cross_comparer_t *)handle; + uint16_t filterEdge; /*!< Edge detection after filtering */ + alignment_comparer_get_value(zero_cross); + zero_cross->control_param->speed_count++; + + for (int i = 0; i < PHASE_MAX; i++) + { + if ((zero_cross->alignment_queue_value[i] & ZERO_CROSS_DETECTION_ACCURACY) == ZERO_CROSS_DETECTION_ACCURACY) + { + zero_cross->queue_filter_state[i] = 1; + } + else if ((zero_cross->alignment_queue_value[i] & ZERO_CROSS_DETECTION_ACCURACY) == 0) + { + zero_cross->queue_filter_state[i] = 0; + } + else + { + zero_cross->control_param->filter_failed_count++; + /*!< Direct return */ + return 0; + } + } + + /*!< Speed measurement */ + filterEdge = bldc_umef_edge(zero_cross->queue_filter_state[0]); + if (filterEdge == 0) { + /*!< Starts counting the time past zero from 1->0. */ + if (zero_cross->zero_stable_flag >= ZERO_STABLE_FLAG_CNT) { + uint32_t speed_rpm = 0; + if (zero_cross->control_param->speed_count < SKIP_INVALID_SPEED_CALCULATION) { + /*!< A speed count of less than SKIP_INVALID_SPEED_CALCULATION means that the speed is too fast or unstable and is not counted */ + } else { + if (zero_cross->control_param->dir == CCW) { + /*!< ft/(2*n*c) * 60; where ft is the counting frequency, that is, the frequency of the cycle interrupt, n is the polar logarithm, and c is the number of times it is memorized. */ + speed_rpm = (uint32_t)(COMPARER_RPM_CALCULATION_COEFFICIENT / zero_cross->control_param->speed_count); + } else { + speed_rpm = -(uint32_t)(COMPARER_RPM_CALCULATION_COEFFICIENT / zero_cross->control_param->speed_count); + } + ESP_LOGD(TAG, "speed_rpm: %"PRIu32", speed_count: %"PRIu32"\n", speed_rpm, zero_cross->control_param->speed_count); + + BLDC_LPF(zero_cross->control_param->speed_rpm, speed_rpm, 0.2); + } + } + /*!< The high time is noted as 180 degrees, with 30 degrees of hysteresis, divided by 6 to make the delay time shorter and avoid hardware problems. */ + zero_cross->control_param->filter_delay = zero_cross->control_param->speed_count / ZERO_CROSS_ADVANCE; + zero_cross->control_param->speed_count = 0; + zero_cross->control_param->filter_failed_count = 0; + zero_cross->zero_stable_flag++; + } + + if (filterEdge == 1) { + zero_cross->control_param->speed_count = 0; + zero_cross->control_param->filter_failed_count = 0; + } + + if (filterEdge == 2) { + zero_cross->control_param->filter_failed_count++; + } + + /*!< return CLOSED LOOP after two stabilized revolutions. */ + if (zero_cross->zero_stable_flag >= ZERO_STABLE_FLAG_CNT + 2) { + zero_cross->zero_stable_flag = ZERO_STABLE_FLAG_CNT + 2; + uint8_t phase = zero_cross_comparer_get_phase(zero_cross->queue_filter_state, zero_cross->control_param->dir); + if (phase <= 0 || phase > 6) + { + zero_cross->control_param->error_hall_count++; + return 0; + } else { + zero_cross->control_param->phase_cnt = phase; + } + return 1; + } + + return 0; +} + +esp_err_t bldc_zero_cross_comparer_start(bldc_zero_cross_comparer_handle_t handle) +{ + BLDC_CHECK(handle != NULL, "bldc zero cross handle is NULL", ESP_ERR_INVALID_ARG); + bldc_zero_cross_comparer_t *zero_cross = (bldc_zero_cross_comparer_t *)handle; + zero_cross->zero_stable_flag = 0; + zero_cross->alignment_queue_value[0] = 0; + zero_cross->alignment_queue_value[1] = 0; + zero_cross->alignment_queue_value[2] = 0; + return ESP_OK; +} diff --git a/components/motor/esp_sensorless_bldc_control/control/include/bldc_control_param.h b/components/motor/esp_sensorless_bldc_control/control/include/bldc_control_param.h new file mode 100644 index 000000000..db31afaed --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/control/include/bldc_control_param.h @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef enum +{ + CW = 0, /*!< Clockwise */ + CCW, /*!< counterclockwise */ +} dir_enum_t; + +typedef enum { + INJECT = 0, /*!< Pulse injection for confirming initial phase */ + ALIGNMENT, /*!< Alignment phase for fixing the motor to the initial phase */ + DRAG, /*!< Strong drag gives the motor some initial velocity */ + CLOSED_LOOP, /*!< Closed-loop sensorless control */ + BLOCKED, /*!< Motor blocked */ + STOP, /*!< Motor stalls */ + FAULT, /*!< Motor failure */ +} control_status_enum_t; + +typedef enum { + PHASE_U = 0, + PHASE_V, + PHASE_W, + PHASE_MAX, +} phase_enum_t; + +/** + * @brief Some parameters in the control process are akin to global variables. + * + */ +typedef struct { + /* motor control */ + dir_enum_t dir; /*!< directional */ + control_status_enum_t status; /*!< Motor status */ + uint8_t phase_cnt; /*!< Motor current phase */ + uint8_t phase_cnt_prev; /*!< Motor previous phase */ + uint8_t phase_change_done; /*!< Motor phase change successful */ + uint16_t duty; /*!< duty cycle */ + uint16_t duty_max; /*!< duty cycle max */ + uint16_t drag_time; /*!< drag time */ + /* speed */ + uint32_t expect_speed_rpm; /*!< Expected speed */ + uint32_t speed_rpm; /*!< Current speed */ + uint32_t speed_count; /*!< Speed count */ + /* data */ + uint16_t filter_delay; /*!< delayed count */ + uint16_t filter_failed_count; /*!< Filter Instability Count */ + int error_hall_count; /*!< Incorrect phase counting */ + /* inject */ + uint16_t charge_time; /*!< Capacitor charge time */ + uint8_t inject_adc_read; /*!< Read adc flag on injection */ + uint8_t inject_count; /*!< Injection count, 0: not yet started 1-6: injection in progress 7: injection complete */ + uint32_t inject_adc_value[6]; /*!< The adc value at the time of injection */ + /* adc */ + uint32_t adc_value[3]; /*!< adc value for closed-loop control */ + phase_enum_t adc_bemf_phase; /*!< Current phase current that should be detected */ +} control_param_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/control/include/bldc_six_step.h b/components/motor/esp_sensorless_bldc_control/control/include/bldc_six_step.h new file mode 100644 index 000000000..8224dd60c --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/control/include/bldc_six_step.h @@ -0,0 +1,114 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bldc_control_param.h" +#include "bldc_driver.h" + +typedef enum +{ + CONTROL_TYPE_LEDC = 0, /*!< LEDC control */ +#if CONFIG_SOC_MCPWM_SUPPORTED + CONTROL_TYPE_MCPWM, /*!< MCPWM control */ +#endif + CONTROL_TYPE_GPIO, /*!< GPIO control */ +} control_type_t; + +typedef struct +{ + control_type_t control_type; /*!< Control type */ + union + { + bldc_ledc_config_t bldc_ledc; /*!< LEDC control config */ +#if CONFIG_SOC_MCPWM_SUPPORTED + bldc_mcpwm_config_t bldc_mcpwm; /*!< MCPWM control config */ +#endif + bldc_gpio_config_t bldc_gpio[PHASE_MAX]; /*!< GPIO control config */ + }; +} switch_config_t_t; + +typedef struct { + bool en_level; /*!< Enable level */ + bool has_enable; /*!< Enable pin or not */ + bldc_gpio_config_t en_gpio[PHASE_MAX]; /*!< If three enable pins share a gpio, all three need to be set to the same */ +} mos_en_config_t; + +typedef struct { + bool lower_switch_active_level; /*!< Lower mos tube active level */ + switch_config_t_t upper_switch_config; /*!< Upper mos tube configuration */ + switch_config_t_t lower_switch_config; /*!< Lower mos tube configuration */ + mos_en_config_t mos_en_config; /*!< mos tube enable configuration */ +} bldc_six_step_config_t; + +/** + * @brief Six step handle + * + */ +typedef void *bldc_six_step_handle_t; + +/** + * @brief Initialize the six-step phase change + * + * @param handle Pointer to the handle + * @param config Pointer to the configuration + * @param control_param Pointer to the control parameter + * @return + * ESP_OK on success + * ESP_ERR_INVALID_ARG if the parameter is invalid + * ESP_ERR_NO_MEM if memory allocation failed + * ESP_ERR_INVALID_STATE Unsupported control types + * ESP_FAIL on other errors + */ +esp_err_t bldc_six_step_init(bldc_six_step_handle_t *handle, bldc_six_step_config_t *config, control_param_t *control_param); + +/** + * @brief Deinitialize the six-step phase change + * + * @param handle Pointer to the handle + * @return + * ESP_OK on success + * ESP_ERR_INVALID_ARG if the parameter is invalid + * ESP_FAIL on other errors + */ +esp_err_t bldc_six_step_deinit(bldc_six_step_handle_t handle); + +/** + * @brief Six-step phase change key functions that the user doesn't have to call themselves + * + * @param handle Pointer to the handle + * @return + * 0 normal + * 1 inject success + */ +uint8_t bldc_six_step_operation(void *handle); + +/** + * @brief Reinitialize the relevant parameters, which need to be called every time the motor is started, and the user does not need to care about + * + * @param handle Pointer to the handle + * @return + * ESP_OK on success + * ESP_ERR_INVALID_ARG if the parameter is invalid + */ +esp_err_t bldc_six_step_start(bldc_six_step_handle_t handle); + +/** + * @brief To stop the motor, the user does not need to call + * + * @param handle Pointer to the handle + * @return + * ESP_OK on success + * ESP_ERR_INVALID_ARG if the parameter is invalid + */ +esp_err_t bldc_six_step_stop(bldc_six_step_handle_t handle); + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/control/include/bldc_zero_cross_adc.h b/components/motor/esp_sensorless_bldc_control/control/include/bldc_zero_cross_adc.h new file mode 100644 index 000000000..cabde10eb --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/control/include/bldc_zero_cross_adc.h @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bldc_common.h" +#include "esp_attr.h" +#include "esp_adc/adc_oneshot.h" +#include "esp_adc/adc_cali.h" +#include "esp_adc/adc_cali_scheme.h" +#include "bldc_driver.h" +#include "bldc_control_param.h" + +typedef enum { + U_PHASE_CHANNEL = 0, /*!< U phase adc channel */ + V_PHASE_CHANNEL, /*!< V phase adc channel */ + W_PHASE_CHANNEL, /*!< W phase adc channel */ + BUS_CHANNEL, /*!< bus adc channel */ + POW_CHANNEL, /*!< power adc channel */ + BLDC_ADC_CHANNEL_MAX, +} bldc_adc_channel_enum_t; + +typedef struct +{ + adc_oneshot_unit_handle_t *adc_handle; /*!< ADC handle */ + adc_unit_t adc_unit; /*!< ADC unit */ + adc_oneshot_chan_cfg_t chan_cfg; /*!< ADC channel configuration */ + adc_channel_t adc_channel[BLDC_ADC_CHANNEL_MAX]; /*!< ADC channel */ +} bldc_zero_cross_adc_config_t; + +/** + * @brief bldc zero cross adc handle + * + */ +typedef void *bldc_zero_cross_adc_handle_t; + +/** + * @brief Used to detect the value of adc when the top tube and bottom tube are open + * + * @param[in] timer MCPWM timer handle + * @param[in] edata MCPWM timer event data, fed by driver + * @param[in] user_ctx User data, set in `mcpwm_timer_register_event_callbacks()` + * @return Whether a high priority task has been waken up by this function + */ +bool read_adc_on_full(mcpwm_timer_handle_t timer, const mcpwm_timer_event_data_t *edata, void *user_data); + +/** + * @brief bldc zero cross adc init + * + * @param handle Pointer to the bldc zero cross adc handle + * @param config Pointer to the bldc zero cross adc config + * @param control_param Pointer to the control parameter + * @return + * ESP_OK on success + * ESP_ERR_INVALID_ARG if the parameter is invalid + * ESP_ERR_NO_MEM if memory allocation failed + * ESP_FAIL on other errors + */ +esp_err_t bldc_zero_cross_adc_init(bldc_zero_cross_adc_handle_t *handle, bldc_zero_cross_adc_config_t *config, control_param_t *control_param); + +/** + * @brief The adc detects the zero crossing key function without the user having to call it himself. + * + * @param handle Pointer to the bldc zero cross adc handle + * @return + * 1 Access to closed-loop sensorless control + * 0 Nominal operation + */ +uint8_t bldc_zero_cross_adc_operation(void *handle); + +/** + * @brief Initialization of the parameters before motor rotation, the user does not need to call them himself + * + * @param handle Pointer to the bldc zero cross adc handle + * @return + * ESP_OK on success + * ESP_ERR_INVALID_ARG if the parameter is invalid + */ +esp_err_t bldc_zero_cross_adc_start(bldc_zero_cross_adc_handle_t *handle); + +/** + * @brief Deinitialization of the parameters after motor rotation, the user does not need to call them himself + * + * @param handle Pointer to the bldc zero cross adc handle + * @return + * ESP_OK on success + * ESP_ERR_INVALID_ARG if the parameter is invalid + */ +esp_err_t bldc_zero_cross_adc_deinit(bldc_zero_cross_adc_handle_t handle); + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/control/include/bldc_zero_cross_comparer.h b/components/motor/esp_sensorless_bldc_control/control/include/bldc_zero_cross_comparer.h new file mode 100644 index 000000000..37bb4fd08 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/control/include/bldc_zero_cross_comparer.h @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bldc_driver.h" +#include "bldc_control_param.h" + +typedef struct +{ + bldc_gpio_config_t comparer_gpio[PHASE_MAX]; /*!< Read IO for hardware comparators */ +} bldc_zero_cross_comparer_config_t; + +/** + * @brief zero cross comparer handle + * + */ +typedef void *bldc_zero_cross_comparer_handle_t; + +/** + * @brief Initialize zero cross comparer + * + * @param handle pointer to zero cross comparer handle + * @param config pointer to zero cross comparer config + * @param control_param Pointer to the control parameter + * @return + * ESP_OK on success + * ESP_ERR_INVALID_ARG if the parameter is invalid + * ESP_ERR_NO_MEM if memory allocation failed + * ESP_FAIL on other errors + */ +esp_err_t bldc_zero_cross_comparer_init(bldc_zero_cross_comparer_handle_t *handle, bldc_zero_cross_comparer_config_t *config, control_param_t *control_param); + +/** + * @brief deinitialize zero cross comparer + * + * @param handle pointer to zero cross comparer handle + * ESP_OK on success + * ESP_ERR_INVALID_ARG if the parameter is invalid + * ESP_FAIL on other errors + */ +esp_err_t bldc_zero_cross_comparer_deinit(bldc_zero_cross_comparer_handle_t handle); + +/** + * @brief Comparator detects over-zero key functions without the user having to call them themselves + * + * @param handle pointer to zero cross comparer handle + * @return + * 0 normal + * 1 Access to sensorless closed loop control + */ +uint8_t bldc_zero_cross_comparer_operation(void *handle); + +/** + * @brief Called before the motor starts to rotate, used to initialize the parameters, the user does not need to call their own + * + * @param handle pointer to zero cross comparer handle + * @return + * ESP_OK on success + * ESP_ERR_INVALID_ARG if the parameter is invalid + */ +esp_err_t bldc_zero_cross_comparer_start(bldc_zero_cross_comparer_handle_t handle); + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_adc.c b/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_adc.c new file mode 100644 index 000000000..fbeb9b918 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_adc.c @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bldc_adc.h" +#include "bldc_helper.h" +#include "esp_log.h" +#include "esp_err.h" + +static const char *TAG = "bldc adc"; + +static bool if_init_adc_unit = false; + +esp_err_t bldc_adc_init(const bldc_adc_config_t *config, adc_oneshot_unit_handle_t *adc_handle) +{ + BLDC_CHECK(config, "ADC config is NULL", ESP_ERR_INVALID_ARG); + esp_err_t err = ESP_OK; + + if (config->adc_handle != NULL) { + adc_handle = config->adc_handle; + } else { + adc_oneshot_unit_init_cfg_t init_config = { + .unit_id = config->adc_unit, + }; + + err = adc_oneshot_new_unit(&init_config, adc_handle); + BLDC_CHECK(err == ESP_OK, "ADC unit initialization failed", ESP_ERR_INVALID_STATE); + if_init_adc_unit = true; + } + + for (int i=0; iadc_channel_num; i++) { + err = adc_oneshot_config_channel(*adc_handle, config->adc_channel[i], &config->chan_cfg); + BLDC_CHECK(err == ESP_OK, "ADC channel configuration failed", ESP_ERR_INVALID_STATE); + } + + return ESP_OK; +} + +esp_err_t bldc_adc_deinit(adc_oneshot_unit_handle_t adc_handle) +{ + BLDC_CHECK(adc_handle, "ADC handle is NULL", ESP_ERR_INVALID_ARG); + esp_err_t err = ESP_OK; + if (if_init_adc_unit) { + adc_oneshot_del_unit(adc_handle); + } + return err; +} + +int bldc_adc_read(adc_oneshot_unit_handle_t adc_handle, adc_channel_t adc_channel) +{ + int adc_raw_value = 0; + adc_oneshot_read(adc_handle, adc_channel, &adc_raw_value); + return adc_raw_value; +} + +int bldc_adc_read_isr(adc_oneshot_unit_handle_t adc_handle, adc_channel_t adc_channel) +{ + int adc_raw_value = 0; + adc_oneshot_read_isr(adc_handle, adc_channel, &adc_raw_value); + return adc_raw_value; +} diff --git a/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_gpio.c b/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_gpio.c new file mode 100644 index 000000000..4600372f8 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_gpio.c @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bldc_gpio.h" + +static const char *TAG = "bldc_gpio"; + +esp_err_t bldc_gpio_init(const bldc_gpio_config_t *config) +{ + BLDC_CHECK(NULL != config, "Pointer of config is invalid", ESP_ERR_INVALID_ARG); + BLDC_CHECK(GPIO_IS_VALID_GPIO(config->gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + gpio_config_t gpio_conf = GPIO_CONFIG_DEFAULT(config->gpio_mode, config->gpio_num); + if (config->gpio_mode == GPIO_MODE_INPUT) + { + if (config->active_level) { + gpio_conf.pull_down_en = GPIO_PULLDOWN_ENABLE; + } else { + gpio_conf.pull_up_en = GPIO_PULLUP_ENABLE; + } + } + gpio_config(&gpio_conf); + + return ESP_OK; +} + +esp_err_t bldc_gpio_deinit(int gpio_num) +{ + gpio_reset_pin(gpio_num); + return ESP_OK; +} + +esp_err_t bldc_gpio_set_level(void *gpio_num, uint32_t level) +{ + return gpio_set_level((uint32_t)gpio_num, level); +} + +int bldc_gpio_get_level(uint32_t gpio_num) +{ + return gpio_get_level(gpio_num); +} diff --git a/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_gptimer.c b/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_gptimer.c new file mode 100644 index 000000000..dbfb17c1c --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_gptimer.c @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bldc_gptimer.h" + +static const char *TAG = "bldc_gptimer"; + +esp_err_t bldc_gptimer_init(bldc_gptimer_config_t *config) +{ + BLDC_CHECK(config != NULL, "Pointer of config is NULL", ESP_ERR_INVALID_ARG); + BLDC_CHECK(config->gptimer != NULL, "Pointer of gptimer is NULL", ESP_ERR_INVALID_ARG); + gptimer_config_t timer_config = GPTIMER_CONFIG_DEFAULT(); + esp_err_t err = gptimer_new_timer(&timer_config, config->gptimer); + BLDC_CHECK(err == ESP_OK, "Failed to create gptimer", err); + + err = gptimer_register_event_callbacks(*config->gptimer, &config->cbs, config->user_data); + BLDC_CHECK(err == ESP_OK, "Failed to register gptimer callbacks", err); + + gptimer_alarm_config_t alarm_config = GPTIMER_ALARM_CONFIG_DEFAULT(config->alarm_count_us); + err = gptimer_set_alarm_action(*config->gptimer, &alarm_config); + BLDC_CHECK(err == ESP_OK, "Failed to set gptimer alarm action", err); + err = gptimer_enable(*config->gptimer); + BLDC_CHECK(err == ESP_OK, "Failed to enable gptimer", err); + return ESP_OK; +} + +esp_err_t bldc_gptimer_start(gptimer_handle_t gptimer) +{ + BLDC_CHECK(gptimer != NULL, "Pointer of gptimer is NULL", ESP_ERR_INVALID_ARG); + esp_err_t err = gptimer_start(gptimer); + BLDC_CHECK(err == ESP_OK, "Failed to start gptimer", err); + return ESP_OK; +} + +esp_err_t bldc_gptimer_stop(gptimer_handle_t gptimer) +{ + BLDC_CHECK(gptimer != NULL, "Pointer of gptimer is NULL", ESP_ERR_INVALID_ARG); + esp_err_t err = gptimer_stop(gptimer); + BLDC_CHECK(err == ESP_OK, "Failed to stop gptimer", err); + return ESP_OK; +} + +esp_err_t bldc_gptimer_deinit(gptimer_handle_t gptimer) +{ + BLDC_CHECK(gptimer != NULL, "Pointer of gptimer is NULL", ESP_ERR_INVALID_ARG); + esp_err_t err = gptimer_stop(gptimer); + BLDC_CHECK(err == ESP_OK, "Failed to stop gptimer", err); + err = gptimer_disable(gptimer); + BLDC_CHECK(err == ESP_OK, "Failed to delete gptimer", err); + err = gptimer_del_timer(gptimer); + BLDC_CHECK(err == ESP_OK, "Failed to delete gptimer", err); + return ESP_OK; +} diff --git a/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_ledc.c b/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_ledc.c new file mode 100644 index 000000000..c0769925a --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_ledc.c @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bldc_helper.h" +#include "bldc_ledc.h" +#include "esp_log.h" + +static const char *TAG = "bldc_ledc"; + +esp_err_t bldc_ledc_init(bldc_ledc_config_t *ledc_config) +{ + BLDC_CHECK(ledc_config != NULL, "Invalid ledc configuration", ESP_ERR_INVALID_STATE); + ledc_timer_config_t ledc_timer = LEDC_TIMER_CONFIG_DEFAULT(); + ledc_channel_config_t ledc_channel_cfg = LEDC_CHANNEL_CONFIG_DEFAULT(); + esp_err_t err; + err = ledc_timer_config(&ledc_timer); + BLDC_CHECK(err == ESP_OK, "Invalid ledc timer configuration", ESP_FAIL); + for (int i = 0; i < 3; i++) { + ledc_channel_cfg.channel = ledc_config->ledc_channel[i]; + ledc_channel_cfg.gpio_num = ledc_config->gpio_num[i]; + err = ledc_channel_config(&ledc_channel_cfg); + BLDC_CHECK(err == ESP_OK, "Invalid ledc channel configuration", ESP_FAIL); + } + + return ESP_OK; +} + +esp_err_t bldc_ledc_deinit() +{ + return ESP_OK; +} + +esp_err_t bldc_ledc_set_duty(void *channel, uint32_t duty) +{ + uint32_t channel_num = (uint32_t)channel; + ledc_set_duty(BLDC_LEDC_MODE, channel_num, duty); + ledc_update_duty(BLDC_LEDC_MODE, channel_num); + return ESP_OK; +} diff --git a/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_mcpwm.c b/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_mcpwm.c new file mode 100644 index 000000000..a79ffcc7b --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/hardware_driver/bldc_mcpwm.c @@ -0,0 +1,110 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bldc_common.h" +#include "bldc_mcpwm.h" + +static const char *TAG = "bldc_mcpwm"; + +typedef struct { + mcpwm_timer_handle_t timer; + mcpwm_oper_handle_t operators[MCPWM_MAX_COMPARATOR]; + mcpwm_cmpr_handle_t comparators[MCPWM_MAX_COMPARATOR]; + mcpwm_gen_handle_t generators[MCPWM_MAX_COMPARATOR]; +} bldc_mcpwm_ctx_t; + +esp_err_t bldc_mcpwm_init(bldc_mcpwm_config_t *mcpwm_config, void **cmprs) +{ + BLDC_CHECK(mcpwm_config, "mcpwm_config is NULL", ESP_ERR_INVALID_ARG); + esp_err_t err = ESP_OK; + bldc_mcpwm_ctx_t *bldc_mcpwm_ctx = calloc(1, sizeof(bldc_mcpwm_ctx_t)); + BLDC_CHECK(bldc_mcpwm_ctx, "calloc failed", ESP_ERR_NO_MEM); + + mcpwm_timer_config_t timer_config = MCPWM_TIMER_CONFIG_DEFAULT(mcpwm_config->group_id); + err = mcpwm_new_timer(&timer_config, &bldc_mcpwm_ctx->timer); + BLDC_CHECK(err == ESP_OK, "mcpwm_new_timer failed: %s", err, esp_err_to_name(err)); + + mcpwm_operator_config_t operator_config = { + .group_id = mcpwm_config->group_id, + }; + + for (int i = 0; i < 3; i++) { + err = mcpwm_new_operator(&operator_config, &bldc_mcpwm_ctx->operators[i]); + BLDC_CHECK(err == ESP_OK, "mcpwm_new_operator failed: %s", err, esp_err_to_name(err)); + err = mcpwm_operator_connect_timer(bldc_mcpwm_ctx->operators[i], bldc_mcpwm_ctx->timer); + BLDC_CHECK(err == ESP_OK, "mcpwm_operator_connect_timer failed: %s", err, esp_err_to_name(err)); + } + + mcpwm_comparator_config_t compare_config = { + .flags.update_cmp_on_tez = true, + }; + for (int i = 0; i < 3; i++) { + err = mcpwm_new_comparator(bldc_mcpwm_ctx->operators[i], &compare_config, &bldc_mcpwm_ctx->comparators[i]); + BLDC_CHECK(err == ESP_OK, "mcpwm_new_comparator failed: %s", err, esp_err_to_name(err)); + // set compare value to 0, we will adjust the speed in a period timer callback + err = mcpwm_comparator_set_compare_value(bldc_mcpwm_ctx->comparators[i], 0); + BLDC_CHECK(err == ESP_OK, "mcpwm_comparator_set_compare_value failed: %s", err, esp_err_to_name(err)); + } + + mcpwm_generator_config_t gen_config = {}; + for (int i = 0; i < 3; i++) { + gen_config.gen_gpio_num = mcpwm_config->gpio_num[i]; + err = mcpwm_new_generator(bldc_mcpwm_ctx->operators[i], &gen_config, &bldc_mcpwm_ctx->generators[i]); + BLDC_CHECK(err == ESP_OK, "mcpwm_new_generator failed: %s", err, esp_err_to_name(err)); + } + + for (int i = 0; i < 3; i++) { + err = mcpwm_generator_set_actions_on_compare_event(bldc_mcpwm_ctx->generators[i], + MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, bldc_mcpwm_ctx->comparators[i], MCPWM_GEN_ACTION_LOW), + MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_DOWN, bldc_mcpwm_ctx->comparators[i], MCPWM_GEN_ACTION_HIGH), + MCPWM_GEN_COMPARE_EVENT_ACTION_END()); + BLDC_CHECK(err == ESP_OK, "mcpwm_generator_set_action_on_compare_event failed: %s", err, esp_err_to_name(err)); + } + + if (mcpwm_config->cbs) { + err = mcpwm_timer_register_event_callbacks(bldc_mcpwm_ctx->timer, mcpwm_config->cbs, mcpwm_config->timer_cb_user_data); + BLDC_CHECK(err == ESP_OK, "mcpwm_timer_register_event_callbacks failed: %s", err, esp_err_to_name(err)); + } + + err = mcpwm_timer_enable(bldc_mcpwm_ctx->timer); + BLDC_CHECK(err == ESP_OK, "mcpwm_timer_enable failed: %s", err, esp_err_to_name(err)); + + err = mcpwm_timer_start_stop(bldc_mcpwm_ctx->timer, MCPWM_TIMER_START_NO_STOP); + BLDC_CHECK(err == ESP_OK, "mcpwm_timer_start_stop failed: %s", err, esp_err_to_name(err)); + + cmprs[0] = bldc_mcpwm_ctx->comparators[0]; + cmprs[1] = bldc_mcpwm_ctx->comparators[1]; + cmprs[2] = bldc_mcpwm_ctx->comparators[2]; + + return ESP_OK; +} + +esp_err_t bldc_mcpwm_deinit(void *cmprs) +{ + BLDC_CHECK(cmprs, "cmprs is NULL", ESP_ERR_INVALID_ARG); + bldc_mcpwm_ctx_t *mcpwm_ctx = __containerof(cmprs, bldc_mcpwm_ctx_t, comparators); + esp_err_t err = ESP_OK; + err = mcpwm_timer_disable(mcpwm_ctx->timer); + BLDC_CHECK(err == ESP_OK, "mcpwm_timer_disable failed: %s", err, esp_err_to_name(err)); + for (int i = 0; i < 3; i++) { + err = mcpwm_del_generator(mcpwm_ctx->generators[i]); + BLDC_CHECK(err == ESP_OK, "mcpwm_del_generator failed: %s", err, esp_err_to_name(err)); + err = mcpwm_del_comparator(mcpwm_ctx->comparators[i]); + BLDC_CHECK(err == ESP_OK, "mcpwm_del_comparator failed: %s", err, esp_err_to_name(err)); + err = mcpwm_del_operator(mcpwm_ctx->operators[i]); + BLDC_CHECK(err == ESP_OK, "mcpwm_del_operator failed: %s", err, esp_err_to_name(err)); + } + err = mcpwm_del_timer(mcpwm_ctx->timer); + BLDC_CHECK(err == ESP_OK, "mcpwm_del_timer failed: %s", err, esp_err_to_name(err)); + return ESP_OK; +} + +esp_err_t bldc_mcpwm_set_duty(void *cmprs, uint32_t duty) +{ + esp_err_t err = ESP_OK; + err = mcpwm_comparator_set_compare_value(cmprs, duty); + return err; +} diff --git a/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_adc.h b/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_adc.h new file mode 100644 index 000000000..74141a341 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_adc.h @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef BLDC_ADC_H +#define BLDC_ADC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_adc/adc_oneshot.h" +#include "esp_adc/adc_cali.h" +#include "esp_adc/adc_cali_scheme.h" +#include "esp_private/adc_private.h" +#include "esp_attr.h" + +#define GPTIMER_CONFIG_DEFAULT() \ +{ \ + .clk_src = GPTIMER_CLK_SRC_DEFAULT, \ + .direction = GPTIMER_COUNT_UP, \ + .resolution_hz = 1 * 1000 * 1000, \ +} + +/** + * @brief ADC configuration structure + */ +typedef struct { + adc_oneshot_unit_handle_t *adc_handle; /**< ADC handle */ + adc_unit_t adc_unit; /**< ADC unit */ + adc_oneshot_chan_cfg_t chan_cfg; /**< ADC channel configuration */ + adc_channel_t *adc_channel; /**< ADC channel */ + size_t adc_channel_num; /**< ADC channel number */ +} bldc_adc_config_t; + +/** + * @brief Initialize ADC + * + * @param config Pointer to the configuration structure + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if arguments are NULL + * - ESP_ERR_NOT_SUPPORTED if arguments are out of range + * - ESP_ERR_INVALID_STATE if there is an error in the state + */ +esp_err_t bldc_adc_init(const bldc_adc_config_t *config, adc_oneshot_unit_handle_t *adc_handle); + +/** + * @brief Deinitialize ADC + * + * @param adc_handle ADC handle + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if arguments are NULL + */ +esp_err_t bldc_adc_deinit(adc_oneshot_unit_handle_t adc_handle); + +/** + * @brief Get the raw ADC value + * + * @param adc_handle ADC handle + * @param adc_channel ADC channel + * @note The runtime of this function is about 29us/run on esp32s3 + * @return Raw ADC value + */ +int bldc_adc_read(adc_oneshot_unit_handle_t adc_handle, adc_channel_t adc_channel); + +/** + * @brief Get the raw ADC value in ISR + * + * @param adc_handle ADC handle + * @param adc_channel ADC channel + * @note This function takes about 26us/run on s3. + * @return Raw ADC value + */ +IRAM_ATTR int bldc_adc_read_isr(adc_oneshot_unit_handle_t adc_handle, adc_channel_t adc_channel); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_driver.h b/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_driver.h new file mode 100644 index 000000000..28200225b --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_driver.h @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bldc_common.h" +#include "bldc_adc.h" +#include "bldc_gptimer.h" +#include "bldc_gpio.h" +#include "bldc_ledc.h" +#if CONFIG_SOC_MCPWM_SUPPORTED +#include "bldc_mcpwm.h" +#endif + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_gpio.h b/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_gpio.h new file mode 100644 index 000000000..bf13d9b4b --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_gpio.h @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bldc_helper.h" +#include "driver/gpio.h" + +#define GPIO_CONFIG_DEFAULT(gpio_mode, gpio_num) \ +{ \ + .intr_type = GPIO_INTR_DISABLE, \ + .mode = gpio_mode, \ + .pull_down_en = 0, \ + .pull_up_en = 0, \ + .pin_bit_mask = (1ULL << gpio_num),\ +} + +/** + * + * @brief gpio button configuration + * + */ +typedef struct { + int32_t gpio_num; /*!< num of gpio */ + gpio_mode_t gpio_mode; /*!< gpio mode input or output */ + uint8_t active_level; /*!< INPUT_MODE: gpio level when press down */ +} bldc_gpio_config_t; + +/** + * @brief gpio init + * + * @param config gpio config + * @return + * ESP_ERR_INVALID_ARG if parameter is invalid + * ESP_OK if success + */ +esp_err_t bldc_gpio_init(const bldc_gpio_config_t *config); + +/** + * @brief gpio deinit + * + * @param gpio_num gpio num + * @return always return ESP_OK + */ +esp_err_t bldc_gpio_deinit(int gpio_num); + +/** + * @brief gpio set level + * + * @param gpio_num gpio num + * @param level gpio level + * @return ESP_OK if success + */ +esp_err_t bldc_gpio_set_level(void *gpio_num, uint32_t level); + +/** + * @brief gpio get level + * + * @param gpio_num gpio num + * @return int[out] gpio level + */ +int bldc_gpio_get_level(uint32_t gpio_num); + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_gptimer.h b/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_gptimer.h new file mode 100644 index 000000000..3aabbf16b --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_gptimer.h @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bldc_common.h" +#include "driver/gptimer.h" + +// 1MHz, 1 tick = 1us +#define GPTIMER_CONFIG_DEFAULT() \ +{ \ + .clk_src = GPTIMER_CLK_SRC_DEFAULT, \ + .direction = GPTIMER_COUNT_UP, \ + .resolution_hz = 1 * 1000 * 1000, \ +} + +#define GPTIMER_ALARM_CONFIG_DEFAULT(count) \ +{ \ + .reload_count = 0, \ + .flags.auto_reload_on_alarm = true, \ + .alarm_count = count, \ +} + +typedef struct +{ + gptimer_handle_t *gptimer; /*!< Pointer to gptimer handle */ + gptimer_event_callbacks_t cbs; /*!< gptimer event callbacks */ + void *user_data; /*!< User data */ + uint32_t alarm_count_us; /*!< Timer interrupt period */ +} bldc_gptimer_config_t; + +/** + * @brief Initialize gptime + * + * @param config Configuration for the gptimer + * @return + * ESP_OK on success + * ESP_ERR_INVALID_ARG if the parameter is invalid + */ +esp_err_t bldc_gptimer_init(bldc_gptimer_config_t *config); + +/** + * @brief deinitialize gptimer + * + * @param gptimer gptimer handle + * @return + * ESP_ERR_INVALID_ARG if parameter is invalid + * ESP_OK if success + */ +esp_err_t bldc_gptimer_deinit(gptimer_handle_t gptimer); + +/** + * @brief Stop the gptimer. + * + * @param gptimer gptimer handle + * @return ESP_OK if success + */ +esp_err_t bldc_gptimer_stop(gptimer_handle_t gptimer); + +/** + * @brief Start the gptimer. + * + * @param gptimer gptimer handle + * @return ESP_OK if success + */ +esp_err_t bldc_gptimer_start(gptimer_handle_t gptimer); + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_ledc.h b/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_ledc.h new file mode 100644 index 000000000..635583df2 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_ledc.h @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bldc_common.h" +#include "driver/ledc.h" + +#define BLDC_LEDC_TIMER LEDC_TIMER_0 +#define BLDC_LEDC_MODE LEDC_LOW_SPEED_MODE +#define BLDC_LEDC_DUTY_RES LEDC_TIMER_11_BIT + +#define LEDC_TIMER_CONFIG_DEFAULT() \ +{ \ + .speed_mode = BLDC_LEDC_MODE, \ + .duty_resolution = BLDC_LEDC_DUTY_RES, \ + .timer_num = LEDC_TIMER_0, \ + .freq_hz = FREQ_HZ, \ + .clk_cfg = LEDC_USE_APB_CLK, \ +} + +#define LEDC_CHANNEL_CONFIG_DEFAULT() \ +{ \ + .speed_mode = BLDC_LEDC_MODE, \ + .timer_sel = BLDC_LEDC_TIMER, \ + .hpoint = 0, \ + .duty = 0, \ + .intr_type = LEDC_INTR_DISABLE, \ +} + +typedef struct { + ledc_channel_t ledc_channel[3]; + int gpio_num[3]; +} bldc_ledc_config_t; + +esp_err_t bldc_ledc_init(bldc_ledc_config_t *ledc_config); + +esp_err_t bldc_ledc_set_duty(void *channel, uint32_t duty); + +esp_err_t bldc_ledc_deinit(); + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_mcpwm.h b/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_mcpwm.h new file mode 100644 index 000000000..f9f3ce8cc --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/hardware_driver/include/bldc_mcpwm.h @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bldc_common.h" +#include "driver/mcpwm_prelude.h" + +// 10MHz, 1 tick = 0.1us + +#define MCPWM_MAX_COMPARATOR (3) + +#define MCPWM_TIMER_CONFIG_DEFAULT(mc_group_id) \ +{ \ + .group_id = mc_group_id, \ + .clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT, \ + .resolution_hz = MCPWM_CLK_SRC, \ + .count_mode = MCPWM_TIMER_COUNT_MODE_UP_DOWN, \ + .period_ticks = (uint32_t)(MCPWM_PERIOD) \ +}; + +typedef struct { + /*!< TODO: Add deadtime config */ + int group_id; + int gpio_num[3]; + mcpwm_timer_event_callbacks_t *cbs; + void *timer_cb_user_data; +} bldc_mcpwm_config_t; + +esp_err_t bldc_mcpwm_init(bldc_mcpwm_config_t *mcpwm_config, void **cmprs); + +esp_err_t bldc_mcpwm_deinit(void *cmprs); + +esp_err_t bldc_mcpwm_set_duty(void *cmprs, uint32_t duty); + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/idf_component.yml b/components/motor/esp_sensorless_bldc_control/idf_component.yml new file mode 100644 index 000000000..a8d9eb0f0 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/idf_component.yml @@ -0,0 +1,10 @@ +version: 0.1.0 +description: ESP32 Sensorless BLDC Control Component +url: https://github.com/espressif/esp-iot-solution/tree/master/components/motor/esp_sensorless_bldc_control +repository: https://github.com/espressif/esp-iot-solution.git +issues: https://github.com/espressif/esp-iot-solution/issues +dependencies: + espressif/pid_ctrl: "^0.2.0" + esp_sensorless_bldc_control_lib: "^0.1.0~2" + idf: + version: '>=5.0' \ No newline at end of file diff --git a/components/motor/esp_sensorless_bldc_control/include/bldc_control.h b/components/motor/esp_sensorless_bldc_control/include/bldc_control.h new file mode 100644 index 000000000..f04596766 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/include/bldc_control.h @@ -0,0 +1,187 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bldc_control_param.h" +#include "bldc_driver.h" +#include "bldc_six_step.h" +#include "bldc_zero_cross_comparer.h" +#if CONFIG_SOC_MCPWM_SUPPORTED +#include "bldc_zero_cross_adc.h" +#endif +#include "esp_event.h" + +/** + * @brief using esp_event_handler_register() to register BLDC_CONTROL_EVENT + * + */ +ESP_EVENT_DECLARE_BASE(BLDC_CONTROL_EVENT); /*!< esp event name */ + +typedef enum { + BLDC_CONTROL_START = 0, /*!< BLDC control start event */ + BLDC_CONTROL_ALIGNMENT, /*!< BLDC control alignment event */ + BLDC_CONTROL_DRAG, /*!< BLDC control drag event */ + BLDC_CONTROL_STOP, /*!< BLDC control stop event */ + BLDC_CONTROL_CLOSED_LOOP, /*!< BLDC control closed loop event */ + BLDC_CONTROL_BLOCKED, /*!< BLDC control blocked event */ +} bldc_control_event_t; + +typedef enum{ + SPEED_OPEN_LOOP = 0, /*!< Open-loop speed control, speed control by setting pwm duty, poor load carrying capacity */ + SPEED_CLOSED_LOOP, /*!< Closed-loop speed control, rotational speed control via PID, high load carrying capacity */ +} speed_mode_t; + +typedef enum +{ + BLDC_SIX_STEP = 0, /*!< six-step phase change */ + BLDC_FOC, /*!< foc phase change, not supported yet */ +} control_mode_t; + +typedef enum +{ + ALIGNMENT_COMPARER, /*!< Comparator detects zero crossing */ +#if CONFIG_SOC_MCPWM_SUPPORTED + ALIGNMENT_ADC, /*!< ADC detects zero crossing */ +#endif +} alignment_mode_t; + +/** + * @brief Debug configuration, when activated, will periodically invoke the debug_operation. + * + */ +typedef struct { + uint8_t if_debug; /*!< set 1 to open debug mode */ + esp_err_t (*debug_operation)(void *handle); /*!< debug operation */ +} bldc_debug_config_t; + +/** + * @brief BLDC Control Configuration + * + */ +typedef struct { + speed_mode_t speed_mode; /*!< Speed Mode */ + control_mode_t control_mode; /*!< Control Mode */ + alignment_mode_t alignment_mode; /*!< Alignment Mode */ + union + { + bldc_six_step_config_t six_step_config; /*!< six-step phase change config */ + }; + union + { + bldc_zero_cross_comparer_config_t zero_cross_comparer_config; /*!< Comparator detects zero crossing config */ +#if CONFIG_SOC_MCPWM_SUPPORTED + bldc_zero_cross_adc_config_t zero_cross_adc_config; /*!< ADC detects zero crossing config */ +#endif + }; + bldc_debug_config_t debug_config; /*!< debug config */ +} bldc_control_config_t; + +/** + * @brief bldc control handle + * + */ +typedef void *bldc_control_handle_t; + +/** + * @brief init bldc control + * + * @param handle pointer to bldc control handle + * @param config pointer to bldc control config + * @return + * ESP_ERR_INVALID_ARG if handle or config is NULL + * ESP_ERR_NO_MEM if memory allocation failed + * ESP_OK on success + * ESP_FAIL on other errors + */ +esp_err_t bldc_control_init(bldc_control_handle_t *handle, bldc_control_config_t *config); + +/** + * @brief deinit bldc control + * + * @param handle pointer to bldc control handle + * ESP_ERR_INVALID_ARG if handle or config is NULL + * ESP_OK on success + * ESP_FAIL on other errors + */ +esp_err_t bldc_control_deinit(bldc_control_handle_t *handle); + +/** + * @brief motor start + * + * @param handle pointer to bldc control handle + * @param expect_speed_rpm expect speed in rpm. This parameter does not work in case of open-loop control + * @return ESP_OK on success + */ +esp_err_t bldc_control_start(bldc_control_handle_t *handle, uint32_t expect_speed_rpm); + +/** + * @brief motor stop + * + * @param handle pointer to bldc control handle + * @return + * ESP_FAIL if motor stop failed + * ESP_OK on success + */ +esp_err_t bldc_control_stop(bldc_control_handle_t *handle); + +/** + * @brief get current motor direction + * + * @param handle pointer to bldc control handle + * @return dir_enum_t current motor direction + */ +dir_enum_t bldc_control_get_dir(bldc_control_handle_t *handle); + +/** + * @brief set motor direction + * + * @param handle pointer to bldc control handle + * @param dir motor direction + * @return ESP_OK on success + */ +esp_err_t bldc_control_set_dir(bldc_control_handle_t *handle, dir_enum_t dir); + +/** + * @brief get current motor pwm duty + * + * @param handle pointer to bldc control handle + * @return int current motor pwm duty + */ +int bldc_control_get_duty(bldc_control_handle_t *handle); + +/** + * @brief set motor pwm duty, Closed-loop speed control without calls + * + * @param handle pointer to bldc control handle + * @param duty motor pwm duty + * @return ESP_OK on success + */ +esp_err_t bldc_control_set_duty(bldc_control_handle_t *handle, uint16_t duty); + +/** + * @brief get current RPM + * + * @param handle pointer to bldc control handle + * @return int current RPM + */ +int bldc_control_get_speed_rpm(bldc_control_handle_t *handle); + +/** + * @brief set motor RPM + * + * @param handle pointer to bldc control handle + * @param speed_rpm motor RPM + * @return ESP_OK on success + */ +esp_err_t bldc_control_set_speed_rpm(bldc_control_handle_t *handle, int speed_rpm); + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/CMakeLists.txt b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/CMakeLists.txt new file mode 100644 index 000000000..7187a53cb --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/CMakeLists.txt @@ -0,0 +1,8 @@ +# 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" + "../../../esp_sensorless_bldc_control") +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(bldc_adc) \ No newline at end of file diff --git a/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/CMakeLists.txt b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/CMakeLists.txt new file mode 100644 index 000000000..6bd9caabc --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/CMakeLists.txt @@ -0,0 +1,7 @@ +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + WHOLE_ARCHIVE) + +idf_component_get_property(bldc_lib esp_sensorless_bldc_control COMPONENT_LIB) +cmake_policy(SET CMP0079 NEW) +target_link_libraries(${bldc_lib} PUBLIC ${COMPONENT_LIB}) \ No newline at end of file diff --git a/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/bldc_user_cfg.h b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/bldc_user_cfg.h new file mode 100644 index 000000000..e89a7757d --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/bldc_user_cfg.h @@ -0,0 +1,143 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLDC_LEDC 0 +#define BLDC_MCPWM 1 + +/** + * @brief Configure the generation of PWM. + * + */ +#define PWM_MODE (BLDC_MCPWM) /*!< Configure the generation of PWM. */ + +/** + * @brief MCPWM Settings + * + */ +#define MCPWM_CLK_SRC (20 * 1000 * 1000) /*!< Number of count ticks within a period time_us = 1,000,000 / MCPWM_CLK_SRC */ +#define MCPWM_PERIOD (2000) /*!< pwm_cycle_us = 1,000,000 / MCPWM_CLK_SRC * MCPWM_PERIOD */ + +/** + * @brief LEDC Settings + * + */ +#define FREQ_HZ (10*1000) /*= 6 */ + +/** + * @brief Motor parameter settings. + * + */ +#define POLE_PAIR (4) /*!< Number of pole pairs in the motor. */ +#define BASE_VOLTAGE (12) /*!< Rated voltage. */ +#define BASE_SPEED (1000) /*!< Rated speed unit: rpm. */ + +/** + * @brief Closed-loop PID parameters for speed. + * + */ +#define SPEED_KP (0.0032) /*!< P */ +#define SPEED_KI (0.0002) /*!< I */ +#define SPEED_KD (0) /*!< D */ +#define SPEED_MIN_INTEGRAL (- DUTY_MAX/SPEED_KI) /*!< Minimum integral saturation limit. */ +#define SPEED_MAX_INTEGRAL (DUTY_MAX/SPEED_KI) /*!< Maximum integral saturation limit. */ +#define SPEED_MIN_OUTPUT (0) /*!< Minimum PWM duty cycle output. */ +#define SPEED_MAX_OUTPUT (DUTY_MAX) /*!< Maximum PWM duty cycle output. */ +#define SPEED_CAL_TYPE (0) /*!< 0 Incremental 1 Positional */ + +/** + * @brief Speed parameter settings. + * + */ +#define SPEED_MAX_RPM 1000 /*!< Maximum speed. */ +#define SPEED_MIN_RPM 0 /*!< Minimum speed. */ +#define MAX_SPEED_MEASUREMENT_FACTOR 1.2 /*!< Supports a measured speed range from 0 to 1.2 times SpeedMAX. Large values could prevent proper filtering of incorrect data. */ + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/idf_component.yml b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/idf_component.yml new file mode 100644 index 000000000..875b1b7c1 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/idf_component.yml @@ -0,0 +1,8 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/button: "~3.0.0" + ## Required IDF version + idf: + version: ">=5.0.0" + esp_sensorless_bldc_control: + override_path: ../../../../esp_sensorless_bldc_control/ diff --git a/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/test_adc.c b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/test_adc.c new file mode 100644 index 000000000..6394e263c --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/test_adc.c @@ -0,0 +1,142 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "test_utils.h" +#include "unity.h" +#include "bldc_control.h" +#include "iot_button.h" + +#define CLOSED_LOOP 1 + +static const char *TAG = "test_adc"; + +bldc_control_handle_t bldc_control_handle = NULL; + +static void bldc_control_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + switch (event_id) { + case BLDC_CONTROL_START: + ESP_LOGI(TAG, "BLDC_CONTROL_START"); + break; + case BLDC_CONTROL_STOP: + ESP_LOGI(TAG, "BLDC_CONTROL_STOP"); + break; + case BLDC_CONTROL_ALIGNMENT: + ESP_LOGI(TAG, "BLDC_CONTROL_ALIGNMENT"); + break; + case BLDC_CONTROL_CLOSED_LOOP: + ESP_LOGI(TAG, "BLDC_CONTROL_CLOSED_LOOP"); + break; + case BLDC_CONTROL_DRAG: + ESP_LOGI(TAG, "BLDC_CONTROL_DRAG"); + break; + case BLDC_CONTROL_BLOCKED: + ESP_LOGI(TAG, "BLDC_CONTROL_BLOCKED"); + break; + } +} + +static void button_press_down_cb(void *arg, void *data) +{ + bldc_control_handle_t bldc_control_handle = (bldc_control_handle_t) data; +#if CLOSED_LOOP == 1 + static int speed_rpm = 200; + bldc_control_set_speed_rpm(bldc_control_handle,speed_rpm); + speed_rpm += 50; + if (speed_rpm > 1000) { + speed_rpm = 200; + } +#else + static int duty = 400; + + duty += 30; + if (duty > 1000) { + duty = 400; + } + bldc_control_set_duty(bldc_control_handle, duty); +#endif +} + +void button_init(void *user_data) +{ + button_config_t cfg = { + .type = BUTTON_TYPE_GPIO, + .gpio_button_config = { + .gpio_num = 0, + .active_level = 0, + }, + }; + button_handle_t btn = iot_button_create(&cfg); + iot_button_register_cb(btn, BUTTON_PRESS_DOWN, button_press_down_cb, user_data); +} + +TEST_CASE("bldc adc test mcpwm","[bldc comparer][mcpwm]") +{ + esp_event_loop_create_default(); + ESP_ERROR_CHECK(esp_event_handler_register(BLDC_CONTROL_EVENT, ESP_EVENT_ANY_ID, &bldc_control_event_handler, NULL)); + switch_config_t_t upper_switch_config = { + .control_type = CONTROL_TYPE_MCPWM, + .bldc_mcpwm = { + .group_id = 0, + .gpio_num = {17,16,15}, + }, + }; + + switch_config_t_t lower_switch_config = { + .control_type = CONTROL_TYPE_GPIO, + .bldc_gpio[0] = { + .gpio_num = 12, + .gpio_mode = GPIO_MODE_OUTPUT, + }, + .bldc_gpio[1] = { + .gpio_num = 11, + .gpio_mode = GPIO_MODE_OUTPUT, + }, + .bldc_gpio[2] = { + .gpio_num = 10, + .gpio_mode = GPIO_MODE_OUTPUT, + }, + }; + + bldc_zero_cross_adc_config_t zero_cross_adc_config = { + .adc_handle = NULL, + .adc_unit = ADC_UNIT_1, + .chan_cfg = { + .bitwidth = ADC_BITWIDTH_12, + .atten = ADC_ATTEN_DB_0, + }, + .adc_channel = {ADC_CHANNEL_3, ADC_CHANNEL_4, ADC_CHANNEL_5, ADC_CHANNEL_0, ADC_CHANNEL_1}, + }; + + bldc_control_config_t config = { + .speed_mode = SPEED_CLOSED_LOOP, + .control_mode = BLDC_SIX_STEP, + .alignment_mode = ALIGNMENT_ADC, + .six_step_config = { + .lower_switch_active_level = 0, + .upper_switch_config = upper_switch_config, + .lower_switch_config = lower_switch_config, + .mos_en_config.has_enable = false, + }, + .zero_cross_adc_config = zero_cross_adc_config, + }; + + TEST_ASSERT(ESP_OK == bldc_control_init(&bldc_control_handle, &config)); + + TEST_ASSERT(ESP_OK == bldc_control_set_dir(bldc_control_handle, CCW)); + + TEST_ASSERT(ESP_OK == bldc_control_start(bldc_control_handle, 300)); + + button_init(bldc_control_handle); + + while (1) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} diff --git a/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/test_apps.c b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/test_apps.c new file mode 100644 index 000000000..8f5a63743 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/main/test_apps.c @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "esp_heap_caps.h" + +// Some resources are lazy allocated in GPTimer driver, the threshold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (-300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +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); + unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + + // ___ _ ___ ___ _____ ___ ___ _____ + // | _ ) | | \ / __| |_ _| __/ __|_ _| + // | _ \ |__| |) | (__ | | | _|\__ \ | | + // |___/____|___/ \___| |_| |___|___/ |_| + // + printf(" ___ _ ___ ___ _____ ___ ___ _____ \n"); + printf(" | _ ) | | \\ / __| |_ _| __/ __|_ _|\n"); + printf(" | _ \\ |__| |) | (__ | | | _|\\__ \\ | | \n"); + printf(" |___/____|___/ \\___| |_| |___|___/ |_| \n"); + unity_run_menu(); +} diff --git a/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/sdkconfig.defaults b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/sdkconfig.defaults new file mode 100644 index 000000000..3778471bc --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc/sdkconfig.defaults @@ -0,0 +1,9 @@ +# For IDF 5.0 +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT_EN=n + +# For IDF4.4 +CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP_TASK_WDT=n diff --git a/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/CMakeLists.txt b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/CMakeLists.txt new file mode 100644 index 000000000..767188b67 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/CMakeLists.txt @@ -0,0 +1,8 @@ +# 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" + "../../../esp_sensorless_bldc_control") +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(bldc_comparer) \ No newline at end of file diff --git a/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/CMakeLists.txt b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/CMakeLists.txt new file mode 100644 index 000000000..6bd9caabc --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/CMakeLists.txt @@ -0,0 +1,7 @@ +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + WHOLE_ARCHIVE) + +idf_component_get_property(bldc_lib esp_sensorless_bldc_control COMPONENT_LIB) +cmake_policy(SET CMP0079 NEW) +target_link_libraries(${bldc_lib} PUBLIC ${COMPONENT_LIB}) \ No newline at end of file diff --git a/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/bldc_user_cfg.h b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/bldc_user_cfg.h new file mode 100644 index 000000000..d25f28844 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/bldc_user_cfg.h @@ -0,0 +1,143 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLDC_LEDC 0 +#define BLDC_MCPWM 1 + +/** + * @brief Configure the generation of PWM. + * + */ +#define PWM_MODE (BLDC_MCPWM) /*!< Configure the generation of PWM. */ + +/** + * @brief MCPWM Settings + * + */ +#define MCPWM_CLK_SRC (20 * 1000 * 1000) /*!< Number of count ticks within a period time_us = 1,000,000 / MCPWM_CLK_SRC */ +#define MCPWM_PERIOD (2000) /*!< pwm_cycle_us = 1,000,000 / MCPWM_CLK_SRC * MCPWM_PERIOD */ + +/** + * @brief LEDC Settings + * + */ +#define FREQ_HZ (20*1000) /*= 6 */ + +/** + * @brief Motor parameter settings. + * + */ +#define POLE_PAIR (5) /*!< Number of pole pairs in the motor. */ +#define BASE_VOLTAGE (12) /*!< Rated voltage. */ +#define BASE_SPEED (1000) /*!< Rated speed unit: rpm. */ + +/** + * @brief Closed-loop PID parameters for speed. + * + */ +#define SPEED_KP (0.003) /*!< P */ +#define SPEED_KI (0.0001) /*!< I */ +#define SPEED_KD (0) /*!< D */ +#define SPEED_MIN_INTEGRAL (- DUTY_MAX/SPEED_KI) /*!< Minimum integral saturation limit. */ +#define SPEED_MAX_INTEGRAL (DUTY_MAX/SPEED_KI) /*!< Maximum integral saturation limit. */ +#define SPEED_MIN_OUTPUT (0) /*!< Minimum PWM duty cycle output. */ +#define SPEED_MAX_OUTPUT (DUTY_MAX) /*!< Maximum PWM duty cycle output. */ +#define SPEED_CAL_TYPE (0) /*!< 0 Incremental 1 Positional */ + +/** + * @brief Speed parameter settings. + * + */ +#define SPEED_MAX_RPM 1000 /*!< Maximum speed. */ +#define SPEED_MIN_RPM 0 /*!< Minimum speed. */ +#define MAX_SPEED_MEASUREMENT_FACTOR 1.2 /*!< Supports a measured speed range from 0 to 1.2 times SpeedMAX. Large values could prevent proper filtering of incorrect data. */ + +#ifdef __cplusplus +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/idf_component.yml b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/idf_component.yml new file mode 100644 index 000000000..79af53f65 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/idf_component.yml @@ -0,0 +1,8 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/button: "~3.1.3" + ## Required IDF version + idf: + version: ">=5.0.0" + esp_sensorless_bldc_control: + override_path: ../../../../esp_sensorless_bldc_control/ diff --git a/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/test_apps.c b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/test_apps.c new file mode 100644 index 000000000..edb595f27 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/test_apps.c @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "esp_heap_caps.h" + +// Some resources are lazy allocated in GPTimer driver, the threshold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (-300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +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); + unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + + // ___ _ ___ ___ _____ ___ ___ _____ + // | _ ) | | \ / __| |_ _| __/ __|_ _| + // | _ \ |__| |) | (__ | | | _|\__ \ | | + // |___/____|___/ \___| |_| |___|___/ |_| + // + printf(" ___ _ ___ ___ _____ ___ ___ _____ \n"); + printf(" | _ ) | | \\ / __| |_ _| __/ __|_ _|\n"); + printf(" | _ \\ |__| |) | (__ | | | _|\\__ \\ | | \n"); + printf(" |___/____|___/ \\___| |_| |___|___/ |_| \n"); + unity_run_menu(); +} diff --git a/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/test_comparer.c b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/test_comparer.c new file mode 100644 index 000000000..a7e52d291 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/main/test_comparer.c @@ -0,0 +1,229 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "test_utils.h" +#include "unity.h" +#include "bldc_control.h" +#include "iot_button.h" + +#define CLOSED_LOOP 1 + +static const char *TAG = "test_comparer"; + + +static void bldc_control_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + switch (event_id) { + case BLDC_CONTROL_START: + ESP_LOGI(TAG, "BLDC_CONTROL_START"); + break; + case BLDC_CONTROL_STOP: + ESP_LOGI(TAG, "BLDC_CONTROL_STOP"); + break; + case BLDC_CONTROL_ALIGNMENT: + ESP_LOGI(TAG, "BLDC_CONTROL_ALIGNMENT"); + break; + case BLDC_CONTROL_CLOSED_LOOP: + ESP_LOGI(TAG, "BLDC_CONTROL_CLOSED_LOOP"); + break; + case BLDC_CONTROL_DRAG: + ESP_LOGI(TAG, "BLDC_CONTROL_DRAG"); + break; + case BLDC_CONTROL_BLOCKED: + ESP_LOGI(TAG, "BLDC_CONTROL_BLOCKED"); + break; + } +} + +static void button_press_down_cb(void *arg, void *data) +{ + bldc_control_handle_t bldc_control_handle = (bldc_control_handle_t) data; +#if CLOSED_LOOP == 1 + static int speed_rpm = 200; + ESP_LOGI(TAG, "speed_rpm:%d", speed_rpm); + bldc_control_set_speed_rpm(bldc_control_handle,speed_rpm); + speed_rpm += 50; + if (speed_rpm > 1000) { + speed_rpm = 200; + } +#else + static int duty = 400; + + duty += 30; + if (duty > 1000) { + duty = 400; + } + bldc_control_set_duty(bldc_control_handle, duty); +#endif +} + +void button_init(void *user_data) +{ + button_config_t cfg = { + .type = BUTTON_TYPE_GPIO, + .gpio_button_config = { + .gpio_num = 0, + .active_level = 0, + }, + }; + button_handle_t btn = iot_button_create(&cfg); + iot_button_register_cb(btn, BUTTON_PRESS_DOWN, button_press_down_cb, user_data); +} + +TEST_CASE("bldc comparer ledc test","[bldc comparer][ledc]") +{ + esp_event_loop_create_default(); + ESP_ERROR_CHECK(esp_event_handler_register(BLDC_CONTROL_EVENT, ESP_EVENT_ANY_ID, &bldc_control_event_handler, NULL)); + + switch_config_t_t upper_switch_config = { + .control_type = CONTROL_TYPE_LEDC, + .bldc_ledc = { + .ledc_channel = {0,1,2}, + .gpio_num = {17,16,15}, + }, + }; + + switch_config_t_t lower_switch_config = { + .control_type = CONTROL_TYPE_GPIO, + .bldc_gpio[0] = { + .gpio_num = 12, + .gpio_mode = GPIO_MODE_OUTPUT, + }, + .bldc_gpio[1] = { + .gpio_num = 11, + .gpio_mode = GPIO_MODE_OUTPUT, + }, + .bldc_gpio[2] = { + .gpio_num = 10, + .gpio_mode = GPIO_MODE_OUTPUT, + }, + }; + + bldc_zero_cross_comparer_config_t zero_cross_comparer_config = { + .comparer_gpio[0] = { + .gpio_num = 3, + .gpio_mode = GPIO_MODE_INPUT, + .active_level = 0, + }, + .comparer_gpio[1] = { + .gpio_num = 46, + .gpio_mode = GPIO_MODE_INPUT, + .active_level = 0, + }, + .comparer_gpio[2] = { + .gpio_num = 9, + .gpio_mode = GPIO_MODE_INPUT, + .active_level = 0, + }, + }; + + bldc_control_config_t config = { + .speed_mode = SPEED_CLOSED_LOOP, + .control_mode = BLDC_SIX_STEP, + .alignment_mode = ALIGNMENT_COMPARER, + .six_step_config = { + .lower_switch_active_level = 0, + .upper_switch_config = upper_switch_config, + .lower_switch_config = lower_switch_config, + .mos_en_config.has_enable = false, + }, + .zero_cross_comparer_config = zero_cross_comparer_config, + }; + + bldc_control_handle_t bldc_control_handle = NULL; + + TEST_ASSERT(ESP_OK == bldc_control_init(&bldc_control_handle, &config)); + + TEST_ASSERT(ESP_OK == bldc_control_set_dir(bldc_control_handle, CCW)); + + TEST_ASSERT(ESP_OK == bldc_control_start(bldc_control_handle, 300)); + + button_init(bldc_control_handle); + + while (1) { + vTaskDelay(10 / portTICK_PERIOD_MS); + } +} + +#if CONFIG_SOC_MCPWM_SUPPORTED +TEST_CASE("bldc comparer test mcpwm","[bldc comparer][mcpwm]") +{ + esp_event_loop_create_default(); + ESP_ERROR_CHECK(esp_event_handler_register(BLDC_CONTROL_EVENT, ESP_EVENT_ANY_ID, &bldc_control_event_handler, NULL)); + switch_config_t_t upper_switch_config = { + .control_type = CONTROL_TYPE_MCPWM, + .bldc_mcpwm = { + .group_id = 0, + .gpio_num = {17,16,15}, + }, + }; + + switch_config_t_t lower_switch_config = { + .control_type = CONTROL_TYPE_GPIO, + .bldc_gpio[0] = { + .gpio_num = 12, + .gpio_mode = GPIO_MODE_OUTPUT, + }, + .bldc_gpio[1] = { + .gpio_num = 11, + .gpio_mode = GPIO_MODE_OUTPUT, + }, + .bldc_gpio[2] = { + .gpio_num = 10, + .gpio_mode = GPIO_MODE_OUTPUT, + }, + }; + + bldc_zero_cross_comparer_config_t zero_cross_comparer_config = { + .comparer_gpio[0] = { + .gpio_num = 3, + .gpio_mode = GPIO_MODE_INPUT, + .active_level = 0, + }, + .comparer_gpio[1] = { + .gpio_num = 46, + .gpio_mode = GPIO_MODE_INPUT, + .active_level = 0, + }, + .comparer_gpio[2] = { + .gpio_num = 9, + .gpio_mode = GPIO_MODE_INPUT, + .active_level = 0, + }, + }; + + bldc_control_config_t config = { + .speed_mode = SPEED_CLOSED_LOOP, + .control_mode = BLDC_SIX_STEP, + .alignment_mode = ALIGNMENT_COMPARER, + .six_step_config = { + .lower_switch_active_level = 0, + .upper_switch_config = upper_switch_config, + .lower_switch_config = lower_switch_config, + .mos_en_config.has_enable = false, + }, + .zero_cross_comparer_config = zero_cross_comparer_config, + }; + + bldc_control_handle_t bldc_control_handle = NULL; + + TEST_ASSERT(ESP_OK == bldc_control_init(&bldc_control_handle, &config)); + + TEST_ASSERT(ESP_OK == bldc_control_set_dir(bldc_control_handle, CCW)); + + TEST_ASSERT(ESP_OK == bldc_control_start(bldc_control_handle, 300)); + + button_init(bldc_control_handle); + + while (1) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} +#endif diff --git a/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/sdkconfig.defaults b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/sdkconfig.defaults new file mode 100644 index 000000000..3778471bc --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/sdkconfig.defaults @@ -0,0 +1,9 @@ +# For IDF 5.0 +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT_EN=n + +# For IDF4.4 +CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP_TASK_WDT=n diff --git a/components/motor/esp_sensorless_bldc_control/user_cfg/README.md b/components/motor/esp_sensorless_bldc_control/user_cfg/README.md new file mode 100644 index 000000000..5c67378aa --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/user_cfg/README.md @@ -0,0 +1,5 @@ +## About bldc_user_cfg.h + +* Users should create a `bldc_user_cfg.h` file within their project and replicate all macro definitions from the `bldc_user_cfg.h.tpl` file. Please avoid deleting any macros in the process. + +* The `bldc_user_cfg.h` file is utilized for defining various parameters related to motor control. Please refer to the comments for detailed information on its specific functionalities. \ No newline at end of file diff --git a/components/motor/esp_sensorless_bldc_control/user_cfg/bldc_user_cfg.h b/components/motor/esp_sensorless_bldc_control/user_cfg/bldc_user_cfg.h new file mode 100644 index 000000000..705220d50 --- /dev/null +++ b/components/motor/esp_sensorless_bldc_control/user_cfg/bldc_user_cfg.h @@ -0,0 +1,143 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLDC_LEDC 0 +#define BLDC_MCPWM 1 + +/** + * @brief Configure the generation of PWM. + * + */ +#define PWM_MODE (BLDC_MCPWM) /*!< Configure the generation of PWM. */ + +/** + * @brief MCPWM Settings + * + */ +#define MCPWM_CLK_SRC (20 * 1000 * 1000) /*!< Number of count ticks within a period time_us = 1,000,000 / MCPWM_CLK_SRC */ +#define MCPWM_PERIOD (2000) /*!< pwm_cycle_us = 1,000,000 / MCPWM_CLK_SRC * MCPWM_PERIOD */ + +/** + * @brief LEDC Settings + * + */ +#define FREQ_HZ (10*1000) /*= 6 */ + +/** + * @brief Motor parameter settings. + * + */ +#define POLE_PAIR (5) /*!< Number of pole pairs in the motor. */ +#define BASE_VOLTAGE (12) /*!< Rated voltage. */ +#define BASE_SPEED (1000) /*!< Rated speed unit: rpm. */ + +/** + * @brief Closed-loop PID parameters for speed. + * + */ +#define SPEED_KP (0.004) /*!< P */ +#define SPEED_KI (0.00015) /*!< I */ +#define SPEED_KD (0) /*!< D */ +#define SPEED_MIN_INTEGRAL (- DUTY_MAX/SPEED_KI) /*!< Minimum integral saturation limit. */ +#define SPEED_MAX_INTEGRAL (DUTY_MAX/SPEED_KI) /*!< Maximum integral saturation limit. */ +#define SPEED_MIN_OUTPUT (0) /*!< Minimum PWM duty cycle output. */ +#define SPEED_MAX_OUTPUT (DUTY_MAX) /*!< Maximum PWM duty cycle output. */ +#define SPEED_CAL_TYPE (0) /*!< 0 Incremental 1 Positional */ + +/** + * @brief Speed parameter settings. + * + */ +#define SPEED_MAX_RPM 1000 /*!< Maximum speed. */ +#define SPEED_MIN_RPM 0 /*!< Minimum speed. */ +#define MAX_SPEED_MEASUREMENT_FACTOR 1.2 /*!< Supports a measured speed range from 0 to 1.2 times SpeedMAX. Large values could prevent proper filtering of incorrect data. */ + +#ifdef __cplusplus +} +#endif diff --git a/docs/Doxyfile b/docs/Doxyfile index 0170c76f3..cbbb6d99f 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -21,25 +21,28 @@ PROJECT_NAME = "ESP IoT Solution Guide" ## and used to include in API reference documentation INPUT = \ + $(PROJECT_PATH)/components/audio/dac_audio/include/dac_audio.h \ + $(PROJECT_PATH)/components/audio/pwm_audio/include/pwm_audio.h \ $(PROJECT_PATH)/components/bus/include/i2c_bus.h \ - $(PROJECT_PATH)/components/bus/include/spi_bus.h \ $(PROJECT_PATH)/components/bus/include/i2s_lcd_driver.h \ - $(PROJECT_PATH)/components/sensors/sensor_hub/include/sensor_type.h \ - $(PROJECT_PATH)/components/sensors/sensor_hub/include/iot_sensor_hub.h \ - $(PROJECT_PATH)/components/sensors/sensor_hub/include/hal/humiture_hal.h \ - $(PROJECT_PATH)/components/sensors/sensor_hub/include/hal/imu_hal.h \ - $(PROJECT_PATH)/components/sensors/sensor_hub/include/hal/light_sensor_hal.h \ - $(PROJECT_PATH)/components/motor/servo/include/iot_servo.h \ - $(PROJECT_PATH)/components/audio/pwm_audio/include/pwm_audio.h \ - $(PROJECT_PATH)/components/audio/dac_audio/include/dac_audio.h \ - $(PROJECT_PATH)/components/display/screen/screen_driver.h \ + $(PROJECT_PATH)/components/bus/include/spi_bus.h \ + $(PROJECT_PATH)/components/button/include/iot_button.h \ $(PROJECT_PATH)/components/display/screen/interface_driver/scr_interface_driver.h \ + $(PROJECT_PATH)/components/display/screen/screen_driver.h \ $(PROJECT_PATH)/components/display/touch_panel/touch_panel.h \ + $(PROJECT_PATH)/components/knob/iot_knob.h \ $(PROJECT_PATH)/components/led/led_indicator/include/led_indicator.h \ - $(PROJECT_PATH)/components/button/include/iot_button.h \ + $(PROJECT_PATH)/components/motor/esp_sensorless_bldc_control/control/include/bldc_control_param.h \ + $(PROJECT_PATH)/components/motor/esp_sensorless_bldc_control/include/bldc_control.h \ + $(PROJECT_PATH)/components/motor/esp_sensorless_bldc_control/user_cfg/bldc_user_cfg.h \ + $(PROJECT_PATH)/components/motor/servo/include/iot_servo.h \ $(PROJECT_PATH)/components/openai/include/OpenAI.h \ + $(PROJECT_PATH)/components/sensors/sensor_hub/include/hal/humiture_hal.h \ + $(PROJECT_PATH)/components/sensors/sensor_hub/include/hal/imu_hal.h \ + $(PROJECT_PATH)/components/sensors/sensor_hub/include/hal/light_sensor_hal.h \ + $(PROJECT_PATH)/components/sensors/sensor_hub/include/iot_sensor_hub.h \ + $(PROJECT_PATH)/components/sensors/sensor_hub/include/sensor_type.h \ $(PROJECT_PATH)/components/usb/usb_stream/include/usb_stream.h \ - $(PROJECT_PATH)/components/knob/iot_knob.h \ ## Get warnings for functions that have no documentation for their parameters or return value ## diff --git a/docs/_static/motor/bldc/bldc_adc_hardware.png b/docs/_static/motor/bldc/bldc_adc_hardware.png new file mode 100644 index 000000000..50b35cdbf Binary files /dev/null and b/docs/_static/motor/bldc/bldc_adc_hardware.png differ diff --git a/docs/_static/motor/bldc/bldc_comparator.svg b/docs/_static/motor/bldc/bldc_comparator.svg new file mode 100644 index 000000000..b6dffbf98 --- /dev/null +++ b/docs/_static/motor/bldc/bldc_comparator.svg @@ -0,0 +1,4 @@ + + + +
BUS
BUS
GND
GND
虚拟中性点
虚拟中性点
比较器
比较器
反电势
反电势
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docs/_static/motor/bldc/bldc_comparator_hardware.png b/docs/_static/motor/bldc/bldc_comparator_hardware.png new file mode 100644 index 000000000..a1735ca1c Binary files /dev/null and b/docs/_static/motor/bldc/bldc_comparator_hardware.png differ diff --git a/docs/_static/motor/bldc/bldc_electrical_degrees.svg b/docs/_static/motor/bldc/bldc_electrical_degrees.svg new file mode 100644 index 000000000..ce490ac58 --- /dev/null +++ b/docs/_static/motor/bldc/bldc_electrical_degrees.svg @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 页-1 + + + 工作表.1 + + + + 工作表.2 + + + + 工作表.3 + + + + 工作表.4 + + + + 工作表.5 + + + + 工作表.6 + + + + 工作表.7 + + + + 工作表.8 + + + + 工作表.9 + 60 + + + + 60 + + 工作表.11 + 120 + + + + 120 + + 工作表.12 + 180 + + + + 180 + + 工作表.13 + 240 + + + + 240 + + 工作表.14 + 300 + + + + 300 + + 工作表.15 + 360 + + + + 360 + + 工作表.16 + 0 + + + + 0 + + 工作表.17 + + + + 工作表.18 + + + + 工作表.19 + + + + 工作表.20 + + + + 工作表.21 + + + + 工作表.22 + + + + 工作表.23 + + + + 工作表.24 + + + + 工作表.25 + + + + 工作表.26 + + + + 工作表.27 + + + + 工作表.28 + + + + 工作表.29 + + + + 工作表.30 + + + + 工作表.31 + + + + 工作表.33 + + + + 工作表.34 + + + + 工作表.35 + + + + 工作表.36 + + + + 工作表.37 + + + + 工作表.38 + + + + 工作表.39 + + + + 工作表.41 + + + + 工作表.43 + + + + 工作表.44 + + + + 工作表.45 + + + + 工作表.46 + + + + 工作表.47 + + + + 工作表.48 + + + + 工作表.49 + + + + 工作表.50 + + + + 工作表.51 + U相 + + + + U + + 工作表.53 + V相 + + + + V + + 工作表.54 + W相 + + + + W + + 工作表.62 + + 工作表.61 + + + + 工作表.55 + + + + 工作表.56 + + + + 工作表.57 + 电流 + + + + 电流 + + 工作表.59 + 反电势 + + + + 反电势 + + + diff --git a/docs/_static/motor/bldc/bldc_hall.png b/docs/_static/motor/bldc/bldc_hall.png new file mode 100644 index 000000000..d4045a58f Binary files /dev/null and b/docs/_static/motor/bldc/bldc_hall.png differ diff --git a/docs/_static/motor/bldc/bldc_hall_status.png b/docs/_static/motor/bldc/bldc_hall_status.png new file mode 100644 index 000000000..5c50ad17c Binary files /dev/null and b/docs/_static/motor/bldc/bldc_hall_status.png differ diff --git a/docs/_static/motor/bldc/bldc_inject.png b/docs/_static/motor/bldc/bldc_inject.png new file mode 100644 index 000000000..b2587d745 Binary files /dev/null and b/docs/_static/motor/bldc/bldc_inject.png differ diff --git a/docs/_static/motor/bldc/bldc_motor.png b/docs/_static/motor/bldc/bldc_motor.png new file mode 100644 index 000000000..3cb596c28 Binary files /dev/null and b/docs/_static/motor/bldc/bldc_motor.png differ diff --git a/docs/_static/motor/bldc/bldc_nverter_circuit.png b/docs/_static/motor/bldc/bldc_nverter_circuit.png new file mode 100644 index 000000000..1388f12f0 Binary files /dev/null and b/docs/_static/motor/bldc/bldc_nverter_circuit.png differ diff --git a/docs/_static/motor/bldc/bldc_run.gif b/docs/_static/motor/bldc/bldc_run.gif new file mode 100644 index 000000000..f00cb9fd0 Binary files /dev/null and b/docs/_static/motor/bldc/bldc_run.gif differ diff --git a/docs/en/motor/bldc/bldc_overview.rst b/docs/en/motor/bldc/bldc_overview.rst new file mode 100644 index 000000000..36b5200aa --- /dev/null +++ b/docs/en/motor/bldc/bldc_overview.rst @@ -0,0 +1 @@ +.. include:: ../../../zh_CN/motor/bldc/bldc_overview.rst \ No newline at end of file diff --git a/docs/en/motor/bldc/bldc_snls_adc.rst b/docs/en/motor/bldc/bldc_snls_adc.rst new file mode 100644 index 000000000..97f71a309 --- /dev/null +++ b/docs/en/motor/bldc/bldc_snls_adc.rst @@ -0,0 +1 @@ +.. include:: ../../../zh_CN/motor/bldc/bldc_snls_adc.rst \ No newline at end of file diff --git a/docs/en/motor/bldc/bldc_snls_comparer.rst b/docs/en/motor/bldc/bldc_snls_comparer.rst new file mode 100644 index 000000000..25fd643c2 --- /dev/null +++ b/docs/en/motor/bldc/bldc_snls_comparer.rst @@ -0,0 +1 @@ +.. include:: ../../../zh_CN/motor/bldc/bldc_snls_comparer.rst \ No newline at end of file diff --git a/docs/en/motor/bldc/esp_sensorless_bldc_control.rst b/docs/en/motor/bldc/esp_sensorless_bldc_control.rst new file mode 100644 index 000000000..57f9d2dd8 --- /dev/null +++ b/docs/en/motor/bldc/esp_sensorless_bldc_control.rst @@ -0,0 +1 @@ +.. include:: ../../../zh_CN/motor/bldc/esp_sensorless_bldc_control.rst \ No newline at end of file diff --git a/docs/en/motor/bldc/index.rst b/docs/en/motor/bldc/index.rst new file mode 100644 index 000000000..1cfaa9c0c --- /dev/null +++ b/docs/en/motor/bldc/index.rst @@ -0,0 +1,12 @@ +BLDC Motor +=========== + +:link_to_translation:`zh_CN:[中文]` + +.. toctree:: + :maxdepth: 2 + + BLDC Overview + BLDC ADC Detection Over Zero Program + BLDC Comparator Detection Over Zero Program + ESP Sensorless BLDC Control \ No newline at end of file diff --git a/docs/en/motor/index.rst b/docs/en/motor/index.rst index c322b725a..3e8f328d0 100644 --- a/docs/en/motor/index.rst +++ b/docs/en/motor/index.rst @@ -5,4 +5,5 @@ Motor .. toctree:: :maxdepth: 1 + BLDC Servo diff --git a/docs/zh_CN/motor/bldc/bldc_overview.rst b/docs/zh_CN/motor/bldc/bldc_overview.rst new file mode 100644 index 000000000..1f3a32bc5 --- /dev/null +++ b/docs/zh_CN/motor/bldc/bldc_overview.rst @@ -0,0 +1,193 @@ +无刷电机控制概述 +================= + +:link_to_translation:`en:[English]` + +本指南包含以下内容: + +.. list:: + + - `无刷电机概述`_:无刷电机优势 + - `驱动方式`_: 无刷电机的驱动硬件方式,逆变电路 + - `控制方式`_:控制无刷电机的几种方式 + +无刷电机概述 +-------------- + +无刷直流(Brushless Direct Current, BLDC)电机属于同步电机的一种,可配置为单相,两相,三相。此文讨论的都是三相无刷电机。 + +无刷电机不使用电刷进行换向,而是使用电子换向,具有以下优点: + + - 更好的转速-转矩特性 + - 快速的动态响应 + - 高效率 + - 使用寿命长 + - 运转无噪音 + - 较高的转速范围 + +.. figure:: ../../../_static/motor/bldc/bldc_motor.png + :align: center + :width: 50% + :alt: BLDC 无刷电机 + + 无刷电机 + +无刷电机的组成分为定子和转子两部分: + + - 定子是线圈绕组电枢,具有三个星型连接的定子绕组,沿着定子圆周分布这些绕组,以构成均匀分布的磁极。 + + - 转子用永磁体制成,永磁体的磁极数目大多为 2 到 8 磁极。南磁极和北磁极交替。 + +.. figure:: ../../../_static/motor/bldc/bldc_run.gif + :align: center + :width: 50% + :alt: BLDC 无刷电机运转方式 + +如果只给电机通固定的直流电流,电机只会产生不变的磁场。无法转动起来。只有通过适当的顺序来为定子相位供电,在定子上产生一个旋转磁场。转子的固有磁极跟随定子的旋转磁场有序旋转,才能达到转动的目的。 + +.. note:: 理想状态下,转矩峰值出现在两个磁场正交时候,而在两磁场平行时最弱。 + +重要参数: +^^^^^^^^^^ + +`KV 值`:可以直观表示无刷电机在具体工作电压下的具体转速。 + +.. math:: + 实际转速 = KV * 工作电压 + +`转矩`:电机中转子产生的可以用来带动机械负载的驱动力矩。 + +`转速`:电机每分钟的转速 + +`最大电流`:可以承受并安全工作的最大电流 + +`极对数 Pp`:转子上磁钢的数量除以 2,可以通过给任意两相通过小电压,手动旋转电机一周,感受阻力的次数就是极对数。如感到 6 次阻力,极对数就是 6。 + +`相电感 LS`:电机静止时的定子绕组两端的电感为 LL, 相电感为其一半 + +.. math:: + LS = LL / 2 + +`相电阻 RS`:万用表测电机两项电阻 RL,相电阻为其一半 + +.. math:: + RS = RL / 2 + +驱动方式 +--------- + +无刷电机一般通过 6 MOS 管组成的逆变电路进行驱动,通过上臂和下臂开关器件的组合,可以在定子上产生一个旋转磁场。 + +.. figure:: ../../../_static/motor/bldc/bldc_nverter_circuit.png + :align: center + :width: 70% + :alt: BLDC 逆变电路 + +通过图上的逆变电路,按照顺序依次导通,转子磁铁就能循环转动,每经过 6 次切换电流,转子转动一圈。这里展示的是导通两个桥臂的方式。 + +.. note:: 上下桥臂不能同时导通,否则会短路,所以我们需要引入死区控制,来规避掉同一相的上下桥臂同时导通的情况。 + +.. list-table:: + :widths: 15 15 15 15 15 + :header-rows: 1 + + * - **导通上臂** + - **导通下臂** + - **相电流 A** + - **相电流 B** + - **相电流 C** + * - UH + - WL + - DC+ + - 悬空 + - DC- + * - UH + - VL + - DC+ + - DC- + - 悬空 + * - WH + - VL + - 悬空 + - DC- + - DC+ + * - WH + - UL + - DC- + - 悬空 + - DC+ + * - VH + - UL + - DC- + - DC+ + - 悬空 + * - VH + - WL + - 悬空 + - DC+ + - DC- +.. + +为了让电机旋转的速度可控,我们可以让施加在上臂的控制信号为 PWM 信号,通过控制 PWM 的占空比来达到控制转速的作用。 + +控制方式 +--------- + +在实际的电机控制中,我们并不能准确的知道电机的位置,所以我们需要能过获取到电机的实时位置,并计算出下一步导通的桥臂,这样才能让电机旋转起来。 + +获取转子位置一般有两种方式,有感霍尔和无感检测。 + +有感霍尔 +^^^^^^^^^ + +在无刷电机中,一般用 3 个开关型霍尔器件检测转子位置,安装位置一般相隔 120°。如下图所示 + +.. figure:: ../../../_static/motor/bldc/bldc_hall.png + :align: center + :width: 60% + :alt: BLDC 霍尔传感器安装位置 + +当 N 极靠近霍尔 a 时,a 输出高电平 1,当 N 极原理 a 时,a 输出低电平。其他同理。那么当转子转动一圈,会产生下面的波形。 + +.. figure:: ../../../_static/motor/bldc/bldc_hall_status.png + :align: center + :width: 70% + :alt: BLDC 霍尔传感器波形 + +这样我们就能通过霍尔传感器的输出,确定转子的当前位置。并使用“二二导通”法让电机旋转起来 + +无感检测 +^^^^^^^^^ + +在一些小、微电机系统中,安装位置传感器对电机的体积和成本有不利影响。因此无传感器的位置检测也非常重要。当转子磁铁在转动时,每个绕组都会产生反电动势,而观测反电动势的变化,确认转子位置就是无感检测中的一种。 + +反电动势 +"""""""""" + +反电动势根据楞次定律,方向与提供绕组的主电压相反。反电动势的极性与励磁电压相反。反电动势主要取决于三个方向。 + + - 转子角速度 + - 转子磁铁产生的磁场 + - 定子绕组的匝数 + +.. math:: + BEMF = NlrB\omega + +对于电机来说,转子磁场和定子绕组的匝数都是固定的,那么在实际运转中,唯一决定反电动势的因素就是角速度,或者说转子转速。在每次换向时,都有一个绕组得正电,第二个得负电,第三个保持开路状态。 + +通过检测各相绕组的反电动势过零点,就能在一个电周期内得到转子的六个位置。非导通想反电动势过零点延迟 30° 电角度就是换线点。 + +.. note:: 在电机转速极慢的时候,反电动势的幅值很低,很难检测到过零点。 + +基于反电动势检测过零点有两种方式 + +.. list:: + + - :doc:`./bldc_snls_adc` ADC 采样检测过零点 + - :doc:`./bldc_snls_comparer` 比较器检测过零点 + +此外还有基于相电流采集的无感 FOC 方案 + +.. list:: + + - 双电阻无感 FOC 方案(待更新) \ No newline at end of file diff --git a/docs/zh_CN/motor/bldc/bldc_snls_adc.rst b/docs/zh_CN/motor/bldc/bldc_snls_adc.rst new file mode 100644 index 000000000..07ae587d7 --- /dev/null +++ b/docs/zh_CN/motor/bldc/bldc_snls_adc.rst @@ -0,0 +1,70 @@ +基于 ADC 采样的无感方波电机控制 +=============================== + +:link_to_translation:`en:[English]` + +本指南包含以下内容: + +.. contents:: 目录 + :local: + :depth: 2 + +初始位置检测 +------------- + +利用无刷电机磁阻在圆周表面不均匀特性,通过向BLDC电机注入高频电压,检测各相的感应电路并比较大小,以确定转子初始位置。 + +.. note:: + 1. 在静止状态下,对BLDC电机分别注入特定电压矢量,每个电压矢量作用固定时间 :math:`T_{s}`,保障注入电流大小。 + 2. 电压矢量注入结束时刻,对母线电流采样。 + 3. 依次注入剩余电压矢量,比较各电压矢量作用下的电流值大小,确定最大电流标识,得到转子初始位置。 + + ++----------+-----+-----+-----+ +| 注入顺序 | U相 | V相 | W相 | ++==========+=====+=====+=====+ +| 1 | Udc | Udc | GND | ++----------+-----+-----+-----+ +| 2 | GND | GND | Udc | ++----------+-----+-----+-----+ +| 3 | Udc | GND | Udc | ++----------+-----+-----+-----+ +| 4 | GND | Udc | GND | ++----------+-----+-----+-----+ +| 5 | GND | Udc | Udc | ++----------+-----+-----+-----+ +| 6 | Udc | GND | GND | ++----------+-----+-----+-----+ + +基于ADC方案的BLDC无感控制 +------------------------- + +反电势定义 +^^^^^^^^^^^^ + +当无刷电机转动时,每个绕组都会产生反电动势电压,根据楞次定律,反电势极性与主电压相反。反电势计算公式: + +.. math:: + BEMF = NlrB\omega + +其中,N为绕组匝数,l为转子长度,r为转子内半径,B为转子磁场,:math:`\omega` 为角速度。 + +当电机做定后,电机绕组与转子参数固定。电机反电势只与角速度成正比。 + +下图为电机旋转一个电周期中电流与反电势波形。 + +.. figure:: ../../../_static/motor/bldc/bldc_electrical_degrees.svg + :align: center + :width: 70% + +ADC方案的过零点采样原理 +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +当BLDC电机转动时,反电势过零点发生在浮空相。通过检测各相各相对地电压,并与直流母线电压对比。当端电压等于直流母线电压一半时,即发生过零事件。在基于ADC的过零点检测方案中,同时测量端电压与直流母线电压并进行对比,获得过零信号。 + +ADC方案的过零点采样硬件 +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. figure:: ../../../_static/motor/bldc/bldc_adc_hardware.png + :align: center + :width: 70% + +为简化计算流程,端电压与直流母线电压采用相同的分压系数。在12V电机控制方案中,采用 :math:`1/21` 的分压方案,控制直流母线电压与端电压范围在ESP32系列芯片的 :math:`V_{ref}` 范围内。 \ No newline at end of file diff --git a/docs/zh_CN/motor/bldc/bldc_snls_comparer.rst b/docs/zh_CN/motor/bldc/bldc_snls_comparer.rst new file mode 100644 index 000000000..dee2f0fd9 --- /dev/null +++ b/docs/zh_CN/motor/bldc/bldc_snls_comparer.rst @@ -0,0 +1,50 @@ +基于比较器检测的无感方波电机控制 +================================ + +:link_to_translation:`en:[English]` + +本指南包含以下内容: + +.. contents:: 目录 + :local: + :depth: 2 + +基于比较器方案的BLDC无感控制 +----------------------------- + +反电势定义 +^^^^^^^^^^^^ +当无刷电机转动时,每个绕组都会产生反电动势电压,根据楞次定律,反电势极性与主电压相反。反电势计算公式: + +.. math:: + BEMF = NlrB\omega + +其中,N为绕组匝数,l为转子长度,r为转子内半径,B为转子磁场,:math:`\omega` 为角速度。 + +当电机做定后,电机绕组与转子参数固定。电机反电势只与角速度成正比。 + +下图为电机旋转一个电周期中电流与反电势波形。 + +.. figure:: ../../../_static/motor/bldc/bldc_electrical_degrees.svg + :align: center + :width: 60% + + +比较器方案的过零点采样原理 +^^^^^^^^^^^^^^^^^^^^^^^^^^ +当BLDC电机转动时,反电势过零点发生在浮空项。通过检测各相对地电压,并与中性点电压比较。当端电压从大于中性点电压变成小于中性点电压,或端电压从小于中性点电压变成大于中性点电压时,即为过零点。但一般BLDC电机并未引出中性点,导致无法直接测量中性点电压。在基于比较器的过零点检测方案中,将三相绕组通过等阻值电阻连接到公共点,以此重构中性点,并将中性点与端电压通过比较器获得过零信号。 + +.. figure:: ../../../_static/motor/bldc/bldc_comparator.svg + :align: center + :width: 50% + :alt: 比较器过零点原理 + +比较器方案的过零点采样硬件 +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. figure:: ../../../_static/motor/bldc/bldc_comparator_hardware.png + :align: center + :width: 100% + :alt: 比较器过零点硬件 + +使用相同阻值的电阻连接各相构建虚拟中性点。以U相为例,U相反电势与中性点经过比较器输出过零信号。 \ No newline at end of file diff --git a/docs/zh_CN/motor/bldc/esp_sensorless_bldc_control.rst b/docs/zh_CN/motor/bldc/esp_sensorless_bldc_control.rst new file mode 100644 index 000000000..a4dbd0de1 --- /dev/null +++ b/docs/zh_CN/motor/bldc/esp_sensorless_bldc_control.rst @@ -0,0 +1,163 @@ +ESP Sensorless BLDC Control Components +======================================= + +:link_to_translation:`en:[English]` + +本指南包含以下内容: + +.. contents:: 目录 + :local: + :depth: 2 + +`esp_sensorless_bldc_control`` 组件是基于 ESP32 系列芯片的 BLDC 无感方波控制库。目前以及支持以下功能: + + - 基于ADC 采样检测过零点 + - 基于支持比较器检测过零点 + - 基于脉冲法实现转子初始相位检测 + - 堵转保护 + +本文主要讲解如何使用 `esp_sensorless_bldc_control`` 组件进行无刷电机开发,不涉及原理讲解,如需了解更多原理请参考 + + - :doc:`./bldc_overview` 无刷电机控制概述 + - :doc:`./bldc_snls_adc` ADC 采样检测过零点 + - :doc:`./bldc_snls_comparer` 比较器检测过零点 + +无感方波控制流程主要可以分为以下部分 + + - INJECT:通过脉振高频电压注入得到初始相位 :cpp:enumerator:`INJECT` + - ALIGNMENT:将转子固定到初始相位 :cpp:enumerator:`ALIGNMENT` + - DRAG:通过六步换向将转子转动起来 :cpp:enumerator:`DRAG` + - CLOSED_LOOP:无感闭环控制,通过检测反电动势过零点进行换向 :cpp:enumerator:`CLOSED_LOOP` + - BLOCKED:电机堵转 :cpp:enumerator:`BLOCKED` + - STOP: 电机停止 :cpp:enumerator:`STOP` + - FAULT: 电机故障 :cpp:enumerator:`FAULT` + +接下来会分别介绍各个部分的具体流程以及需要注意的参数 + +INJECT +-------- + +通过脉振注入法获取电机初始相位,需要在逆变电路的低端采集母线电流。如下图所示: + +.. figure:: ../../../_static/motor/bldc/bldc_inject.png + :align: center + :width: 100% + :alt: BLDC 母线电流采集 + +.. note:: + 由于电流不能直接被采集到,我们通过一个采样电阻,可以将电流转化为电压。注意,电压需要转化到 ESP32 ADC 能够采集的范围。请参考:`ESP32 ADC `__ + +由于电流只存在于上下管均导通的情况,所以我们需要在上管导通的时候进行 ADC 采样。将 MCPWM 配置为上升下降模式,并在计数器达到顶峰的时候进行采样,可以采集到准确的母线电压。 + +.. note:: + LEDC 驱动不支持在高电平时候触发回调,因此使用 LEDC 方式驱动的方案`无法使用` INJECT 模式。 + +:c:macro:`INJECT_ENABLE` 为 1 时,开启 INJECT 模式,否则关闭。默认为 0。PWM 的生成模式必须为 MCPWM + +:c:macro:`INJECT_DUTY` 注入的电压大小,一般都是采用高占空比注入 + +:c:macro:`CHARGE_TIME` 电感充电时间和脉冲注入时间,该值影响到初始相位检测的精准性。这个值太小会导致采集到的 ADC 值为 0,太大会导致 ADC 值过大。以手动旋转电机,在一圈中可以获得稳定的相位 1-6,不出现错误相位 0 和 7 为佳。 + +ALIGNMENT +----------- + +将转子固定到固定相位,为后面的强拖做准备。 + +:c:macro:`ALIGNMENTNMS` 对齐时间,时间太长会过流。时间太短可能会导致转子没有对齐到正确的相位。 + +:c:macro:`ALIGNMENTDUTY` 对齐的力度。 + +DRAG +------ + +通过六步换向将转子拖动起来,转子拖动采用升压升频的方式。逐渐的加大电压和换向频率,使电机具有初始速度,有明显的反电动势。以电机拖动过程中无异响,丝滑,无卡顿为佳。拖动时间无需太长。 + +:c:macro:`RAMP_TIM_STA` 拖动的初始延迟时间 + +:c:macro:`RAMP_TIM_END` 拖动的最小延迟时间 + +:c:macro:`RAMP_TIM_STEP` 拖动时间的步进 + +:c:macro:`RAMP_DUTY_STA` 拖动的初始占空比 + +:c:macro:`RAMP_DUTY_END` 拖动的最大占空比 + +:c:macro:`RAMP_DUTY_INC` 拖动占空比的步进 + +.. note:: + 强拖需要在电机工作环境下进行调参,电机空载参数不一定适用于带载情况 + +CLOSED_LOOP +------------ + +ADC 采样检测过零点 +^^^^^^^^^^^^^^^^^^^ + +ADC 采样检测过零点需要采集悬空相电压和电机电源电压,且需要在上管导通的时候进行采集。 + +.. note:: + 采用 ADC 检测过零点,必须使用 MCPWM 作为驱动 + +:c:macro:`ENTER_CLOSE_TIME` 设置进入闭环的时间,默认强拖一段时间后即可进行闭环控制。 + +:c:macro:`ZERO_REPEAT_TIME` 连续 N 次检测到过零点才认为是过零点。 + +:c:macro:`AVOID_CONTINUE_CURRENT_TIME` 在换向后,会存在续电流影响,通过延迟检测规避掉续电流 + +比较器检测过零点 +^^^^^^^^^^^^^^^^^ + +比较器检测过零点是通过硬件比较器比较悬空相反电动势和母线电压,通过 GPIO 检测比较器信号翻转来判断过零点,因为在实际过程中会有很多噪点,所以我们需要多次检测来确认过零点。 + +:c:macro:`ZERO_STABLE_FLAG_CNT` 多次检测到稳定过零点信号后,进入无感控制 + +:c:macro:`ZERO_CROSS_DETECTION_ACCURACY` 连续 N 次检测到相同信号视为稳定信号 0xFF 为 8次,0XFFFF 为 16 次 + +提前换向 +^^^^^^^^^ + +过零点信号一般在换向前 30° 到来,当我们检测到过零点信号后,只需要延迟 30° 的时间即可。但是因为存在软件滤波和时延等原因,我们需要稍微补偿一下换向时间。 + +:c:macro:`ZERO_CROSS_ADVANCE` 提前换向时间,提前角度为 180 / ZERO_CROSS_ADVANCE, 默认为 6 + +.. note:: + 换向角度并不是越提前约好,可以搭配示波器观测计算的换向角度与实际的换向角度是否一致。 + +堵转保护 +---------- + +电机长时间不换相即可视为堵转,此时会停止电机运行,进入堵转保护状态。 + +速度控制 +--------- + +通过 PID 控制速度,使电机达到设定的速度。 + +:c:macro:`SPEED_KP` 速度控制的 P 值 + +:c:macro:`SPEED_KI` 速度控制的 I 值 + +:c:macro:`SPEED_KD` 速度控制的 D 值 + +:c:macro:`SPEED_MIN_INTEGRAL` 速度控制的积分最小值 + +:c:macro:`SPEED_MAX_INTEGRAL` 速度控制的积分最大值 + +:c:macro:`SPEED_MIN_OUTPUT` 速度控制的输出最小值 + +:c:macro:`SPEED_MAX_OUTPUT` 速度控制的输出最大值,不超过最大占空比 + +:c:macro:`SPEED_CAL_TYPE` 位置式 PID 还是增量式 PID + +:c:macro:`SPEED_MAX_RPM` 最大转速 RPM + +:c:macro:`SPEED_MIN_RPM` 最小转速 RPM + +:c:macro:`MAX_SPEED_MEASUREMENT_FACTOR` 为了避免错误的速度检测,如果检测到的速度大于此设定系数,则视为错误速度检测。 + +API 参考 +------------ + +.. include-build-file:: inc/bldc_control.inc + +.. include-build-file:: inc/bldc_user_cfg.inc diff --git a/docs/zh_CN/motor/bldc/index.rst b/docs/zh_CN/motor/bldc/index.rst new file mode 100644 index 000000000..fe75effec --- /dev/null +++ b/docs/zh_CN/motor/bldc/index.rst @@ -0,0 +1,12 @@ +无刷电机 +=========== + +:link_to_translation:`en:[English]` + +.. toctree:: + :maxdepth: 2 + + BLDC 概述 + BLDC ADC 采样过零点方案 + BLDC 比较器检测过零点方案 + ESP Sensorless BLDC Control \ No newline at end of file diff --git a/docs/zh_CN/motor/index.rst b/docs/zh_CN/motor/index.rst index 7eb4cef94..b7c641514 100644 --- a/docs/zh_CN/motor/index.rst +++ b/docs/zh_CN/motor/index.rst @@ -5,4 +5,5 @@ .. toctree:: :maxdepth: 1 + 无刷电机 舵机