diff --git a/.github/workflows/build_examples_esp_idf_project.yml b/.github/workflows/build_examples_esp_idf_project.yml new file mode 100644 index 0000000..4d7d257 --- /dev/null +++ b/.github/workflows/build_examples_esp_idf_project.yml @@ -0,0 +1,21 @@ +name: Build ESP-IDF example project + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: esp-idf build + uses: espressif/esp-idf-ci-action@v1 + with: + esp_idf_version: v5.3.2 + target: esp32 + path: examples/ESPIDFExample diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9dd7575 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRC_DIRS "src/TMC2209" + INCLUDE_DIRS "src" + REQUIRES arduino-esp32 +) diff --git a/examples/ESPIDFExample/.gitignore b/examples/ESPIDFExample/.gitignore new file mode 100644 index 0000000..bbab53b --- /dev/null +++ b/examples/ESPIDFExample/.gitignore @@ -0,0 +1,5 @@ +/build +/.vscode +/managed_components +/sdkconfig +/dependencies.lock diff --git a/examples/ESPIDFExample/CMakeLists.txt b/examples/ESPIDFExample/CMakeLists.txt new file mode 100644 index 0000000..e0254bf --- /dev/null +++ b/examples/ESPIDFExample/CMakeLists.txt @@ -0,0 +1,10 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five 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.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(idf_project_example) diff --git a/examples/ESPIDFExample/README.md b/examples/ESPIDFExample/README.md new file mode 100644 index 0000000..7152e9b --- /dev/null +++ b/examples/ESPIDFExample/README.md @@ -0,0 +1,8 @@ +This folder contains a bare bones functional ESP-IDF project. + +You can create one using the **ESP-IDF: New Project** command in +Visual Studio code, or use this one as a basis. If you do use this one +as a basis for your own project, review the `.gitignore` file as it +excludes files you would not want to exclude in a normal project. +Normally only the `managed_components` and `build` folders would be +excluded from version control. diff --git a/examples/ESPIDFExample/main/CMakeLists.txt b/examples/ESPIDFExample/main/CMakeLists.txt new file mode 100644 index 0000000..0125dc2 --- /dev/null +++ b/examples/ESPIDFExample/main/CMakeLists.txt @@ -0,0 +1,6 @@ +FILE(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) + +idf_component_register( + SRCS ${SOURCES} + INCLUDE_DIRS "." +) diff --git a/examples/ESPIDFExample/main/idf_component.yml b/examples/ESPIDFExample/main/idf_component.yml new file mode 100644 index 0000000..8358e91 --- /dev/null +++ b/examples/ESPIDFExample/main/idf_component.yml @@ -0,0 +1,5 @@ +dependencies: + TMC2209: + path: ../../.. + # Alternatively you can point this directly at the GitHub repository: + # git: https://github.com/janelia-arduino/TMC2209.git diff --git a/examples/ESPIDFExample/main/main.cpp b/examples/ESPIDFExample/main/main.cpp new file mode 100644 index 0000000..e3b7275 --- /dev/null +++ b/examples/ESPIDFExample/main/main.cpp @@ -0,0 +1,129 @@ +#include + +// This example will not work on Arduino boards without HardwareSerial ports, +// such as the Uno, Nano, and Mini. +// +// See this reference for more details: +// https://www.arduino.cc/reference/en/language/functions/communication/serial/ + +HardwareSerial & serial_stream = Serial1; + +const long SERIAL_BAUD_RATE = 115200; +const int DELAY = 2000; + +// Instantiate TMC2209 +TMC2209 stepper_driver; + +extern "C" void app_main() +{ + Serial.begin(SERIAL_BAUD_RATE); + + stepper_driver.setup(serial_stream); + + Serial.println("*************************"); + Serial.println("getSettings()"); + TMC2209::Settings settings = stepper_driver.getSettings(); + Serial.print("settings.is_communicating = "); + Serial.println(settings.is_communicating); + Serial.print("settings.is_setup = "); + Serial.println(settings.is_setup); + Serial.print("settings.software_enabled = "); + Serial.println(settings.software_enabled); + Serial.print("settings.microsteps_per_step = "); + Serial.println(settings.microsteps_per_step); + Serial.print("settings.inverse_motor_direction_enabled = "); + Serial.println(settings.inverse_motor_direction_enabled); + Serial.print("settings.stealth_chop_enabled = "); + Serial.println(settings.stealth_chop_enabled); + Serial.print("settings.standstill_mode = "); + switch (settings.standstill_mode) + { + case TMC2209::NORMAL: + Serial.println("normal"); + break; + case TMC2209::FREEWHEELING: + Serial.println("freewheeling"); + break; + case TMC2209::STRONG_BRAKING: + Serial.println("strong_braking"); + break; + case TMC2209::BRAKING: + Serial.println("braking"); + break; + } + Serial.print("settings.irun_percent = "); + Serial.println(settings.irun_percent); + Serial.print("settings.irun_register_value = "); + Serial.println(settings.irun_register_value); + Serial.print("settings.ihold_percent = "); + Serial.println(settings.ihold_percent); + Serial.print("settings.ihold_register_value = "); + Serial.println(settings.ihold_register_value); + Serial.print("settings.iholddelay_percent = "); + Serial.println(settings.iholddelay_percent); + Serial.print("settings.iholddelay_register_value = "); + Serial.println(settings.iholddelay_register_value); + Serial.print("settings.automatic_current_scaling_enabled = "); + Serial.println(settings.automatic_current_scaling_enabled); + Serial.print("settings.automatic_gradient_adaptation_enabled = "); + Serial.println(settings.automatic_gradient_adaptation_enabled); + Serial.print("settings.pwm_offset = "); + Serial.println(settings.pwm_offset); + Serial.print("settings.pwm_gradient = "); + Serial.println(settings.pwm_gradient); + Serial.print("settings.cool_step_enabled = "); + Serial.println(settings.cool_step_enabled); + Serial.print("settings.analog_current_scaling_enabled = "); + Serial.println(settings.analog_current_scaling_enabled); + Serial.print("settings.internal_sense_resistors_enabled = "); + Serial.println(settings.internal_sense_resistors_enabled); + Serial.println("*************************"); + Serial.println(); + + Serial.println("*************************"); + Serial.println("hardwareDisabled()"); + bool hardware_disabled = stepper_driver.hardwareDisabled(); + Serial.print("hardware_disabled = "); + Serial.println(hardware_disabled); + Serial.println("*************************"); + Serial.println(); + + Serial.println("*************************"); + Serial.println("getStatus()"); + TMC2209::Status status = stepper_driver.getStatus(); + Serial.print("status.over_temperature_warning = "); + Serial.println(status.over_temperature_warning); + Serial.print("status.over_temperature_shutdown = "); + Serial.println(status.over_temperature_shutdown); + Serial.print("status.short_to_ground_a = "); + Serial.println(status.short_to_ground_a); + Serial.print("status.short_to_ground_b = "); + Serial.println(status.short_to_ground_b); + Serial.print("status.low_side_short_a = "); + Serial.println(status.low_side_short_a); + Serial.print("status.low_side_short_b = "); + Serial.println(status.low_side_short_b); + Serial.print("status.open_load_a = "); + Serial.println(status.open_load_a); + Serial.print("status.open_load_b = "); + Serial.println(status.open_load_b); + Serial.print("status.over_temperature_120c = "); + Serial.println(status.over_temperature_120c); + Serial.print("status.over_temperature_143c = "); + Serial.println(status.over_temperature_143c); + Serial.print("status.over_temperature_150c = "); + Serial.println(status.over_temperature_150c); + Serial.print("status.over_temperature_157c = "); + Serial.println(status.over_temperature_157c); + Serial.print("status.current_scaling = "); + Serial.println(status.current_scaling); + Serial.print("status.stealth_chop_mode = "); + Serial.println(status.stealth_chop_mode); + Serial.print("status.standstill = "); + Serial.println(status.standstill); + Serial.println("*************************"); + Serial.println(); + + Serial.println(); + delay(DELAY); +} diff --git a/examples/ESPIDFExample/sdkconfig.defaults b/examples/ESPIDFExample/sdkconfig.defaults new file mode 100644 index 0000000..f1e9f31 --- /dev/null +++ b/examples/ESPIDFExample/sdkconfig.defaults @@ -0,0 +1,2 @@ +# Required by the android-esp32 component. +CONFIG_FREERTOS_HZ=1000 diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 0000000..ca47266 --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,5 @@ +dependencies: + idf: + version: ">=5.3.0" + arduino-esp32: + version: "*" diff --git a/src/TMC2209.h b/src/TMC2209.h index 1fd38e1..2742073 100644 --- a/src/TMC2209.h +++ b/src/TMC2209.h @@ -86,10 +86,16 @@ class TMC2209 void setAllCurrentValues(uint8_t run_current_percent, uint8_t hold_current_percent, uint8_t hold_delay_percent); + void setRMSCurrent(uint16_t mA, + float rSense, + float holdMultiplier = 0.5f); void enableDoubleEdge(); void disableDoubleEdge(); + void enableVSense(); + void disableVSense(); + void enableInverseMotorDirection(); void disableInverseMotorDirection(); @@ -279,6 +285,9 @@ class TMC2209 const static uint8_t STEPPER_DRIVER_FEATURE_OFF = 0; const static uint8_t STEPPER_DRIVER_FEATURE_ON = 1; + const static uint8_t MAX_READ_RETRIES = 5; + const static uint32_t READ_RETRY_DELAY_MS = 20; + // Datagrams const static uint8_t WRITE_READ_REPLY_DATAGRAM_SIZE = 8; const static uint8_t DATA_SIZE = 4; @@ -504,6 +513,8 @@ class TMC2209 const static uint8_t MRES_001 = 0b1000; const static uint8_t DOUBLE_EDGE_DISABLE = 0; const static uint8_t DOUBLE_EDGE_ENABLE = 1; + const static uint8_t VSENSE_DISABLE = 0; + const static uint8_t VSENSE_ENABLE = 1; const static size_t MICROSTEPS_PER_STEP_MIN = 1; const static size_t MICROSTEPS_PER_STEP_MAX = 256; diff --git a/src/TMC2209/TMC2209.cpp b/src/TMC2209/TMC2209.cpp index 804f184..895cf49 100644 --- a/src/TMC2209/TMC2209.cpp +++ b/src/TMC2209/TMC2209.cpp @@ -223,6 +223,30 @@ void TMC2209::setAllCurrentValues(uint8_t run_current_percent, writeStoredDriverCurrent(); } +void TMC2209::setRMSCurrent(uint16_t mA, + float rSense, + float holdMultiplier) +{ + // Taken from https://github.com/teemuatlut/TMCStepper/blob/74e8e6881adc9241c2e626071e7328d7652f361a/src/source/TMCStepper.cpp#L41. + + uint8_t CS = 32.0*1.41421*mA/1000.0*(rSense+0.02)/0.325 - 1; + // If Current Scale is too low, turn on high sensitivity R_sense and calculate again + if (CS < 16) { + enableVSense(); + CS = 32.0*1.41421*mA/1000.0*(rSense+0.02)/0.180 - 1; + } else { // If CS >= 16, turn off high_sense_r + disableVSense(); + } + + if (CS > 31) { + CS = 31; + } + + driver_current_.irun = CS; + driver_current_.ihold = CS*holdMultiplier; + writeStoredDriverCurrent(); +} + void TMC2209::enableDoubleEdge() { chopper_config_.double_edge = DOUBLE_EDGE_ENABLE; @@ -235,6 +259,18 @@ void TMC2209::disableDoubleEdge() writeStoredChopperConfig(); } +void TMC2209::enableVSense() +{ + chopper_config_.vsense = VSENSE_ENABLE; + writeStoredChopperConfig(); +} + +void TMC2209::disableVSense() +{ + chopper_config_.vsense = VSENSE_DISABLE; + writeStoredChopperConfig(); +} + void TMC2209::enableInverseMotorDirection() { global_config_.shaft = 1; @@ -880,32 +916,43 @@ uint32_t TMC2209::read(uint8_t register_address) read_request_datagram.rw = RW_READ; read_request_datagram.crc = calculateCrc(read_request_datagram, READ_REQUEST_DATAGRAM_SIZE); - sendDatagramBidirectional(read_request_datagram, READ_REQUEST_DATAGRAM_SIZE); - - uint32_t reply_delay = 0; - while ((serialAvailable() < WRITE_READ_REPLY_DATAGRAM_SIZE) and - (reply_delay < REPLY_DELAY_MAX_MICROSECONDS)) + for (uint8_t retry = 0; retry < MAX_READ_RETRIES; retry++) { - delayMicroseconds(REPLY_DELAY_INC_MICROSECONDS); - reply_delay += REPLY_DELAY_INC_MICROSECONDS; - } + sendDatagramBidirectional(read_request_datagram, READ_REQUEST_DATAGRAM_SIZE); - if (reply_delay >= REPLY_DELAY_MAX_MICROSECONDS) - { - return 0; - } + uint32_t reply_delay = 0; + while ((serialAvailable() < WRITE_READ_REPLY_DATAGRAM_SIZE) and + (reply_delay < REPLY_DELAY_MAX_MICROSECONDS)) + { + delayMicroseconds(REPLY_DELAY_INC_MICROSECONDS); + reply_delay += REPLY_DELAY_INC_MICROSECONDS; + } - uint64_t byte; - uint8_t byte_count = 0; - WriteReadReplyDatagram read_reply_datagram; - read_reply_datagram.bytes = 0; - for (uint8_t i=0; i= REPLY_DELAY_MAX_MICROSECONDS) + { + return 0; + } + + uint64_t byte; + uint8_t byte_count = 0; + WriteReadReplyDatagram read_reply_datagram; + read_reply_datagram.bytes = 0; + for (uint8_t i=0; i