From a0032ea07f2cddeab35816ab306d6140ec761e95 Mon Sep 17 00:00:00 2001 From: jonny_ji7 Date: Thu, 2 Mar 2023 23:35:24 +0100 Subject: [PATCH 01/10] Add C-project for simulating axis in commandline before adding the travelMm function the algorithm for moving the axis between left and right was designed and evaluated via commandline first. -> Add this test-code in this commit --- testing/cnc-guide/main.c | 104 +++++++++++++++++++++++++++++++++++++ testing/cnc-guide/makefile | 7 +++ 2 files changed, 111 insertions(+) create mode 100644 testing/cnc-guide/main.c create mode 100644 testing/cnc-guide/makefile diff --git a/testing/cnc-guide/main.c b/testing/cnc-guide/main.c new file mode 100644 index 0000000..07c1dc3 --- /dev/null +++ b/testing/cnc-guide/main.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include + +#define MAX 100 +#define MIN 10 +#define TRAVEL MAX-MIN + + + +//==== VARIABLES ==== +bool direction = true; +uint16_t posNow = 10; + + + +//==== FUNCTIONS ==== + +//print position +//print line that illustrates the position pos between 0 and MAX +//e.g. "|----<=>--------|" +void printPos(int pos){ + int width = 50; + printf("(%d)|", pos); + for (int i = 0; i<(pos) * width/MAX; i++) printf("-"); + printf("<=>"); + for (int i = 0; i<(MAX-pos) * width/MAX; i++) printf("-"); + printf("|\n"); + return; +} + + +//move "virtual axis" to absolute coordinate in mm +void moveToAbs(int d){ + int posOld = posNow; + printf ("moving from %d %s to %d (%s)\n", posNow, direction ? "=>" : "<=", d, direction ? "RIGHT" : "LEFT"); + posNow = d; + //illustrate movement (print position for each coordinate change) + for (int i=posOld; i!=posNow; i+=posNow>posOld?1:-1) printPos(i); + return; +} + + +//travel back and forth between MIN and MAX coordinate for a certain distance +//negative values are processed reversed +void travel(int length){ + int d, remaining; + d = abs(length); + if(length < 0) direction = !direction; //invert direction in reverse mode + + while (d != 0){ + //--- currently moving right --- + if (direction == true){ //currently moving right + remaining = MAX - posNow; //calc remaining distance fom current position to limit + if (d > remaining){ //new distance will exceed limit + moveToAbs (MAX); //move to limit + direction = false; //change current direction for next iteration + d = d - remaining; //decrease target length by already traveled distance + printf(" --- changed direction (L) --- \n "); + } + else { //target distance does not reach the limit + moveToAbs (posNow + d); //move by (remaining) distance to reach target length + d = 0; //finished, reset target length (could as well exit loop/break) + } + } + + //--- currently moving left --- + else { + remaining = posNow - MIN; + if (d > remaining){ + moveToAbs (MIN); + direction = true; + d = d - remaining; + printf(" --- changed direction (R) --- \n "); + } + else { + moveToAbs (posNow - d); //when moving left the coordinate has to be decreased + d = 0; + } + } + } + + if(length < 0) direction = !direction; //undo inversion of direction after reverse mode is finished + return; +} + + + +//==== MAIN ==== +int main (void) +{ + int input; + printf("**cable-length-cutter testing cnc-guide**\n"); + while(1){ + printf("enter mm to travel:"); + scanf("%d", &input); + travel(input); + printf("\n"); + } + return 0; +} + diff --git a/testing/cnc-guide/makefile b/testing/cnc-guide/makefile new file mode 100644 index 0000000..9eabd10 --- /dev/null +++ b/testing/cnc-guide/makefile @@ -0,0 +1,7 @@ +default: program + +program: + gcc main.c -o a.out -lm + +clean: + -rm -f a.out From 1d53d3467c85cc2b23a011b07430567a45268dc4 Mon Sep 17 00:00:00 2001 From: jonny_l480 Date: Mon, 10 Apr 2023 22:28:18 +0200 Subject: [PATCH 02/10] Stepper driver from scratch, test with buttons - WIP - Add stepper driver code from scratch - Remove / comment out code that used DendoStepper library - Test custom driver with buttons (guide-stepper.cpp) untested limited features --- main/CMakeLists.txt | 1 + main/config.hpp | 2 +- main/guide-stepper.cpp | 326 ++++++++++++++++++++++------------------- main/main.cpp | 4 +- main/stepper.cpp | 163 +++++++++++++++++++++ main/stepper.hpp | 40 +++++ 6 files changed, 380 insertions(+), 156 deletions(-) create mode 100644 main/stepper.cpp create mode 100644 main/stepper.hpp diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index d2b02cb..1fd9e2c 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -8,6 +8,7 @@ idf_component_register( "display.cpp" "cutter.cpp" "switchesAnalog.cpp" + "stepper.cpp" "guide-stepper.cpp" "encoder.cpp" INCLUDE_DIRS diff --git a/main/config.hpp b/main/config.hpp index dcd474b..95ca28b 100644 --- a/main/config.hpp +++ b/main/config.hpp @@ -85,7 +85,7 @@ extern "C" { //----- stepper config ----- //-------------------------- //enable stepper test mode (dont start control and encoder task) -//#define STEPPER_TEST +#define STEPPER_TEST #define STEPPER_STEP_PIN GPIO_NUM_18 //mos1 #define STEPPER_DIR_PIN GPIO_NUM_16 //ST3 #define STEPPER_EN_PIN GPIO_NUM_0 //not connected (-> stepper always on) diff --git a/main/guide-stepper.cpp b/main/guide-stepper.cpp index 37cd5de..25da8ea 100644 --- a/main/guide-stepper.cpp +++ b/main/guide-stepper.cpp @@ -7,7 +7,7 @@ extern "C" #include "driver/adc.h" } -#include "DendoStepper.h" +#include "stepper.hpp" #include "config.hpp" #include "guide-stepper.hpp" #include "encoder.hpp" @@ -46,7 +46,6 @@ extern "C" //---------------------- //----- variables ------ //---------------------- -static DendoStepper step; static const char *TAG = "stepper"; //tag for logging static bool stepp_direction = true; @@ -57,96 +56,98 @@ static uint32_t posNow = 0; //---------------------- //----- functions ------ //---------------------- -//move axis certain Steps (relative) between left and right or reverse when negative -void travelSteps(int stepsTarget){ - //posNow = step.getPositionMm(); //not otherwise controlled, so no update necessary - int stepsToGo, remaining; - - stepsToGo = abs(stepsTarget); - if(stepsTarget < 0) stepp_direction = !stepp_direction; //invert direction in reverse mode - - while (stepsToGo != 0){ - //--- currently moving right --- - if (stepp_direction == true){ //currently moving right - remaining = POS_MAX_STEPS - posNow; //calc remaining distance fom current position to limit - if (stepsToGo > remaining){ //new distance will exceed limit - step.runAbs (POS_MAX_STEPS); //move to limit - while(step.getState() != 1) vTaskDelay(1); //wait for move to finish - posNow = POS_MAX_STEPS; - stepp_direction = false; //change current direction for next iteration - stepsToGo = stepsToGo - remaining; //decrease target length by already traveled distance - ESP_LOGI(TAG, " --- moved to max -> change direction (L) --- \n "); - } - else { //target distance does not reach the limit - step.runAbs (posNow + stepsToGo); //move by (remaining) distance to reach target length - while(step.getState() != 1) vTaskDelay(1); //wait for move to finish - ESP_LOGD(TAG, "moving to %d\n", posNow+stepsToGo); - posNow += stepsToGo; - stepsToGo = 0; //finished, reset target length (could as well exit loop/break) - } - } - - //--- currently moving left --- - else { - remaining = posNow - POS_MIN_STEPS; - if (stepsToGo > remaining){ - step.runAbs (POS_MIN_STEPS); - while(step.getState() != 1) vTaskDelay(2); //wait for move to finish - posNow = POS_MIN_STEPS; - stepp_direction = true; - stepsToGo = stepsToGo - remaining; - ESP_LOGI(TAG, " --- moved to min -> change direction (R) --- \n "); - } - else { - step.runAbs (posNow - stepsToGo); //when moving left the coordinate has to be decreased - while(step.getState() != 1) vTaskDelay(2); //wait for move to finish - ESP_LOGD(TAG, "moving to %d\n", posNow - stepsToGo); - posNow -= stepsToGo; - stepsToGo = 0; - } - } - } - if(stepsTarget < 0) stepp_direction = !stepp_direction; //undo inversion of stepp_direction after reverse mode is finished - return; -} - - -//move axis certain Mm (relative) between left and right or reverse when negative -void travelMm(int length){ - travelSteps(length * STEPPER_STEPS_PER_MM); -} - - -//define zero/start position -//currently crashes into hardware limitation for certain time -//TODO: limit switch -void home() { - ESP_LOGW(TAG, "auto-home..."); - step.setSpeedMm(100, 500, 10); - step.runInf(1); - vTaskDelay(1500 / portTICK_PERIOD_MS); - step.stop(); - step.resetAbsolute(); - ESP_LOGW(TAG, "auto-home finished"); -} +////move axis certain Steps (relative) between left and right or reverse when negative +//void travelSteps(int stepsTarget){ +// //posNow = step.getPositionMm(); //not otherwise controlled, so no update necessary +// int stepsToGo, remaining; +// +// stepsToGo = abs(stepsTarget); +// if(stepsTarget < 0) stepp_direction = !stepp_direction; //invert direction in reverse mode +// +// while (stepsToGo != 0){ +// //--- currently moving right --- +// if (stepp_direction == true){ //currently moving right +// remaining = POS_MAX_STEPS - posNow; //calc remaining distance fom current position to limit +// if (stepsToGo > remaining){ //new distance will exceed limit +// //....step.runAbs (POS_MAX_STEPS); //move to limit +// //....while(step.getState() != 1) vTaskDelay(1); //wait for move to finish +// posNow = POS_MAX_STEPS; +// stepp_direction = false; //change current direction for next iteration +// stepsToGo = stepsToGo - remaining; //decrease target length by already traveled distance +// ESP_LOGI(TAG, " --- moved to max -> change direction (L) --- \n "); +// } +// else { //target distance does not reach the limit +// //....step.runAbs (posNow + stepsToGo); //move by (remaining) distance to reach target length +// //....while(step.getState() != 1) vTaskDelay(1); //wait for move to finish +// ESP_LOGD(TAG, "moving to %d\n", posNow+stepsToGo); +// posNow += stepsToGo; +// stepsToGo = 0; //finished, reset target length (could as well exit loop/break) +// } +// } +// +// //--- currently moving left --- +// else { +// remaining = posNow - POS_MIN_STEPS; +// if (stepsToGo > remaining){ +// //....step.runAbs (POS_MIN_STEPS); +// //....while(step.getState() != 1) vTaskDelay(2); //wait for move to finish +// posNow = POS_MIN_STEPS; +// stepp_direction = true; +// stepsToGo = stepsToGo - remaining; +// ESP_LOGI(TAG, " --- moved to min -> change direction (R) --- \n "); +// } +// else { +// //....step.runAbs (posNow - stepsToGo); //when moving left the coordinate has to be decreased +// while(step.getState() != 1) vTaskDelay(2); //wait for move to finish +// ESP_LOGD(TAG, "moving to %d\n", posNow - stepsToGo); +// posNow -= stepsToGo; +// stepsToGo = 0; +// } +// } +// } +// if(stepsTarget < 0) stepp_direction = !stepp_direction; //undo inversion of stepp_direction after reverse mode is finished +// return; +//} +// +// +////move axis certain Mm (relative) between left and right or reverse when negative +//void travelMm(int length){ +// travelSteps(length * STEPPER_STEPS_PER_MM); +//} +// +// +////define zero/start position +////currently crashes into hardware limitation for certain time +////TODO: limit switch +//void home() { +// ESP_LOGW(TAG, "auto-home..."); +// //....step.setSpeedMm(100, 500, 10); +// //....step.runInf(1); +// vTaskDelay(1500 / portTICK_PERIOD_MS); +// //....step.stop(); +// //....step.resetAbsolute(); +// ESP_LOGW(TAG, "auto-home finished"); +//} //initialize/configure stepper instance void init_stepper() { - ESP_LOGW(TAG, "initializing stepper..."); - DendoStepper_config_t step_cfg = { - .stepPin = STEPPER_STEP_PIN, - .dirPin = STEPPER_DIR_PIN, - .enPin = STEPPER_EN_PIN, - .timer_group = TIMER_GROUP_0, - .timer_idx = TIMER_0, - .miStep = MICROSTEP_32, - .stepAngle = 1.8}; - step.config(&step_cfg); - step.init(); +// ESP_LOGW(TAG, "initializing stepper..."); +// DendoStepper_config_t step_cfg = { +// .stepPin = STEPPER_STEP_PIN, +// .dirPin = STEPPER_DIR_PIN, +// .enPin = STEPPER_EN_PIN, +// .timer_group = TIMER_GROUP_0, +// .timer_idx = TIMER_0, +// .miStep = MICROSTEP_32, +// .stepAngle = 1.8}; +// //....step.config(&step_cfg); +// //....step.init(); +// +// //....step.setSpeed(1000, 1000, 1000); //random default speed +// //....step.setStepsPerMm(STEPPER_STEPS_PER_MM); //guide: 4mm/rot - step.setSpeed(1000, 1000, 1000); //random default speed - step.setStepsPerMm(STEPPER_STEPS_PER_MM); //guide: 4mm/rot + stepper_init(); } @@ -155,7 +156,7 @@ void updateSpeedFromAdc() { int potiRead = gpio_readAdc(ADC_CHANNEL_POTI); //0-4095 GPIO34 double poti = potiRead/4095.0; int speed = poti*(SPEED_MAX-SPEED_MIN) + SPEED_MIN; - step.setSpeedMm(speed, ACCEL_MS, DECEL_MS); + //....step.setSpeedMm(speed, ACCEL_MS, DECEL_MS); ESP_LOGW(TAG, "poti: %d (%.2lf%%), set speed to: %d", potiRead, poti*100, speed); } @@ -166,80 +167,99 @@ void updateSpeedFromAdc() { //---------------------------- void task_stepper_test(void *pvParameter) { - init_stepper(); - home(); + stepper_init(); + while(1){ + vTaskDelay(20 / portTICK_PERIOD_MS); - while (1) { - updateSpeedFromAdc(); - step.runPosMm(STEPPER_TEST_TRAVEL); - while(step.getState() != 1) vTaskDelay(2); - ESP_LOGI(TAG, "finished moving right => moving left"); + //------ handle switches ------ + //run handle functions for all switches + SW_START.handle(); + SW_RESET.handle(); + SW_SET.handle(); + SW_PRESET1.handle(); + SW_PRESET2.handle(); + SW_PRESET3.handle(); + SW_CUT.handle(); + SW_AUTO_CUT.handle(); - updateSpeedFromAdc(); - step.runPosMm(-STEPPER_TEST_TRAVEL); - while(step.getState() != 1) vTaskDelay(2); //1=idle - ESP_LOGI(TAG, "finished moving left => moving right"); - } + if (SW_RESET.risingEdge) { + stepper_toggleDirection(); + buzzer.beep(1, 1000, 100); + } + if (SW_PRESET1.risingEdge) { + buzzer.beep(2, 300, 100); + stepper_setTargetSteps(100); + } + if (SW_PRESET2.risingEdge) { + buzzer.beep(1, 500, 100); + stepper_setTargetSteps(1000); + } + if (SW_PRESET3.risingEdge) { + buzzer.beep(1, 100, 100); + stepper_setTargetSteps(6000); + } + } } + //---------------------------- //----- TASK stepper-ctl ----- //---------------------------- void task_stepper_ctl(void *pvParameter) { - //variables - int encStepsNow = 0; //get curret steps of encoder - int encStepsPrev = 0; //steps at last check - int encStepsDelta = 0; //steps changed since last iteration - - double cableLen = 0; - double travelStepsExact = 0; //steps axis has to travel - double travelStepsPartial = 0; - int travelStepsFull = 0; - double travelMm = 0; - double turns = 0; - - float potiModifier; - - init_stepper(); - home(); - - while(1){ - //get current length - encStepsNow = encoder_getSteps(); - - //calculate change - encStepsDelta = encStepsNow - encStepsPrev; //FIXME MAJOR BUG: when resetting encoder/length in control task, diff will be huge! - - //read potentiometer and normalize (0-1) to get a variable for testing - potiModifier = (float) gpio_readAdc(ADC_CHANNEL_POTI) / 4095; //0-4095 -> 0-1 - //ESP_LOGI(TAG, "current poti-modifier = %f", potiModifier); - - //calculate steps to move - cableLen = (double)encStepsDelta * 1000 / ENCODER_STEPS_PER_METER; - turns = cableLen / (PI * D_REEL); - travelMm = turns * D_CABLE; - travelStepsExact = travelMm * STEPPER_STEPS_PER_MM + travelStepsPartial; //convert mm to steps and add not moved partial steps - travelStepsPartial = 0; - travelStepsFull = (int)travelStepsExact; - - //move axis when ready to move at least 1 step - if (abs(travelStepsFull) > 1){ - travelStepsPartial = fmod(travelStepsExact, 1); //save remaining partial steps to be added in the next iteration - ESP_LOGD(TAG, "cablelen=%.2lf, turns=%.2lf, travelMm=%.3lf, travelStepsExact: %.3lf, travelStepsFull=%d, partialStep=%.3lf", cableLen, turns, travelMm, travelStepsExact, travelStepsFull, travelStepsPartial); - ESP_LOGI(TAG, "MOVING %d steps", travelStepsFull); - //TODO: calculate variable speed for smoother movement? for example intentionally lag behind and calculate speed according to buffered data - step.setSpeedMm(35, 100, 50); - //testing: get speed from poti - //step.setSpeedMm(35, 1000*potiModifier+1, 1000*potiModifier+1); - travelSteps(travelStepsExact); - encStepsPrev = encStepsNow; //update previous length - } - else { - //TODO use encoder queue to only run this check at encoder event? - vTaskDelay(2); - } - } +// //variables +// int encStepsNow = 0; //get curret steps of encoder +// int encStepsPrev = 0; //steps at last check +// int encStepsDelta = 0; //steps changed since last iteration +// +// double cableLen = 0; +// double travelStepsExact = 0; //steps axis has to travel +// double travelStepsPartial = 0; +// int travelStepsFull = 0; +// double travelMm = 0; +// double turns = 0; +// +// float potiModifier; +// +// init_stepper(); +// home(); +// +// while(1){ +// //get current length +// encStepsNow = encoder_getSteps(); +// +// //calculate change +// encStepsDelta = encStepsNow - encStepsPrev; //FIXME MAJOR BUG: when resetting encoder/length in control task, diff will be huge! +// +// //read potentiometer and normalize (0-1) to get a variable for testing +// potiModifier = (float) gpio_readAdc(ADC_CHANNEL_POTI) / 4095; //0-4095 -> 0-1 +// //ESP_LOGI(TAG, "current poti-modifier = %f", potiModifier); +// +// //calculate steps to move +// cableLen = (double)encStepsDelta * 1000 / ENCODER_STEPS_PER_METER; +// turns = cableLen / (PI * D_REEL); +// travelMm = turns * D_CABLE; +// travelStepsExact = travelMm * STEPPER_STEPS_PER_MM + travelStepsPartial; //convert mm to steps and add not moved partial steps +// travelStepsPartial = 0; +// travelStepsFull = (int)travelStepsExact; +// +// //move axis when ready to move at least 1 step +// if (abs(travelStepsFull) > 1){ +// travelStepsPartial = fmod(travelStepsExact, 1); //save remaining partial steps to be added in the next iteration +// ESP_LOGD(TAG, "cablelen=%.2lf, turns=%.2lf, travelMm=%.3lf, travelStepsExact: %.3lf, travelStepsFull=%d, partialStep=%.3lf", cableLen, turns, travelMm, travelStepsExact, travelStepsFull, travelStepsPartial); +// ESP_LOGI(TAG, "MOVING %d steps", travelStepsFull); +// //TODO: calculate variable speed for smoother movement? for example intentionally lag behind and calculate speed according to buffered data +// //....step.setSpeedMm(35, 100, 50); +// //testing: get speed from poti +// //step.setSpeedMm(35, 1000*potiModifier+1, 1000*potiModifier+1); +// travelSteps(travelStepsExact); +// encStepsPrev = encStepsNow; //update previous length +// } +// else { +// //TODO use encoder queue to only run this check at encoder event? +// vTaskDelay(2); +// } +// } } diff --git a/main/main.cpp b/main/main.cpp index 7872214..0533bd5 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -97,12 +97,12 @@ extern "C" void app_main() //create task for controlling the machine xTaskCreate(task_control, "task_control", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL); - //create task for controlling the machine + //create task for controlling the stepper xTaskCreate(task_stepper_ctl, "task_stepper_ctl", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL); +#endif //create task for handling the buzzer xTaskCreate(&task_buzzer, "task_buzzer", 2048, NULL, 2, NULL); -#endif //beep at startup buzzer.beep(3, 70, 50); diff --git a/main/stepper.cpp b/main/stepper.cpp new file mode 100644 index 0000000..9c4b54a --- /dev/null +++ b/main/stepper.cpp @@ -0,0 +1,163 @@ +//custom driver for stepper motor + +#include "config.hpp" + + +//config from config.hpp +//#define STEPPER_STEP_PIN GPIO_NUM_18 //mos1 +//#define STEPPER_DIR_PIN GPIO_NUM_16 //ST3 +// + +extern "C" { +#include "driver/timer.h" +#include "driver/gpio.h" +} + +#define TIMER_F 1000000ULL +#define TICK_PER_S TIMER_S +#define NS_TO_T_TICKS(x) (x) + +static const char *TAG = "stepper-ctl"; //tag for logging +bool direction = 0; + +bool timer_isr(void *arg); + + +typedef struct { + int targetSteps; // Target step count + int currentSteps; // Current step count + int acceleration; // Acceleration (in steps per second^2) + int deceleration; // Deceleration (in steps per second^2) + gpio_num_t pulsePin; // Pin for pulse signal + gpio_num_t directionPin; // Pin for direction signal + timer_group_t timerGroup; // Timer group + timer_idx_t timerIdx; // Timer index + bool isAccelerating; // Flag to indicate if accelerating + bool isDecelerating; // Flag to indicate if decelerating + int initialSpeed; // Initial speed (in steps per second) + int targetSpeed; // Target speed (in steps per second) + int currentSpeed; // Current speed (in steps per second) +} StepperControl; + +StepperControl ctrl; // Create an instance of StepperControl struct + + + +void stepper_setTargetSteps(int steps){ + ESP_LOGW(TAG, "set target steps to %d", steps); + //TODO switch dir pin in isr? not in sync with count + if(steps < 0){ + gpio_set_level(ctrl.directionPin, 1); + } else { + gpio_set_level(ctrl.directionPin, 0); + } + + ctrl.targetSteps = abs(steps); +} + +void stepper_toggleDirection(){ + direction = !direction; + gpio_set_level(ctrl.directionPin, 1); + ESP_LOGW(TAG, "toggle direction -> %d", direction); +} + + + + + + + +void stepper_init(){ + ESP_LOGW(TAG, "init - configure struct..."); + // Set values in StepperControl struct + ctrl.targetSteps = 0; + ctrl.currentSteps = 0; + ctrl.acceleration = 50; + ctrl.deceleration = 50; + ctrl.pulsePin = STEPPER_STEP_PIN; + ctrl.directionPin = STEPPER_DIR_PIN; + ctrl.timerGroup = TIMER_GROUP_0; + ctrl.timerIdx = TIMER_0; + ctrl.isAccelerating = true; + ctrl.isDecelerating = false; + ctrl.initialSpeed = 0; // Set initial speed as needed + ctrl.targetSpeed = 500; // Set target speed as needed + ctrl.currentSpeed = ctrl.initialSpeed; + + // Configure pulse and direction pins as outputs + ESP_LOGW(TAG, "init - configure gpio pins..."); + gpio_set_direction(ctrl.pulsePin, GPIO_MODE_OUTPUT); + gpio_set_direction(ctrl.directionPin, GPIO_MODE_OUTPUT); + + + ESP_LOGW(TAG, "init - initialize/configure timer..."); + timer_config_t timer_conf = { + .alarm_en = TIMER_ALARM_EN, // we need alarm + .counter_en = TIMER_PAUSE, // dont start now lol + .intr_type = TIMER_INTR_LEVEL, // interrupt + .counter_dir = TIMER_COUNT_UP, // count up duh + .auto_reload = TIMER_AUTORELOAD_EN, // reload pls + .divider = 80000000ULL / TIMER_F, // ns resolution + }; + + + ESP_ERROR_CHECK(timer_init(ctrl.timerGroup, ctrl.timerIdx, &timer_conf)); // init the timer + ESP_ERROR_CHECK(timer_set_counter_value(ctrl.timerGroup, ctrl.timerIdx, 0)); // set it to 0 + //ESP_ERROR_CHECK(timer_isr_callback_add(ctrl.timerGroup, ctrl.timerIdx, timer_isr, )); // add callback fn to run when alarm is triggrd + ESP_ERROR_CHECK(timer_isr_callback_add(ctrl.timerGroup, ctrl.timerIdx, timer_isr, (void *)ctrl.timerIdx, 0)); +} + + + + +bool timer_isr(void *arg) { + // Cast arg to an integer type that has the same size as timer_idx_t + uintptr_t arg_val = (uintptr_t)arg; + // Cast arg_val to timer_idx_t + timer_idx_t timer_idx = (timer_idx_t)arg_val; + int32_t step_diff = ctrl.targetSteps - ctrl.currentSteps; + + if (timer_idx == ctrl.timerIdx) { + if (ctrl.currentSteps < ctrl.targetSteps) { + // Check if accelerating + if (ctrl.isAccelerating) { + if (ctrl.currentSpeed < ctrl.targetSpeed) { + // Increase speed if not yet at target speed + ctrl.currentSpeed += ctrl.acceleration; + } else { + // Reached target speed, clear accelerating flag + ctrl.isAccelerating = false; + } + } + + // Check if decelerating + if (ctrl.isDecelerating) { + if (ctrl.currentSpeed > ctrl.targetSpeed) { + // Decrease speed if not yet at target speed + ctrl.currentSpeed -= ctrl.deceleration; + } else { + // Reached target speed, clear decelerating flag + ctrl.isDecelerating = false; + } + } + + // Generate pulse for stepper motor + gpio_set_level(ctrl.pulsePin, 1); + ets_delay_us(500); // Adjust delay as needed + gpio_set_level(ctrl.pulsePin, 0); + + // Update current step count + ctrl.currentSteps++; + + // Update timer period based on current speed + timer_set_alarm_value(ctrl.timerGroup, ctrl.timerIdx, TIMER_BASE_CLK / ctrl.currentSpeed); + } else { + // Reached target step count, stop timer + timer_pause(ctrl.timerGroup, ctrl.timerIdx); + ESP_LOGW(TAG,"finished, pausing timer"); + } + } + return 1; +} + + diff --git a/main/stepper.hpp b/main/stepper.hpp new file mode 100644 index 0000000..4e9292d --- /dev/null +++ b/main/stepper.hpp @@ -0,0 +1,40 @@ + + +void stepper_init(); + +void stepper_setTargetSteps(int steps); + +void stepper_toggleDirection(); + +//typedef struct +//{ +// uint8_t stepPin; /** step signal pin */ +// uint8_t dirPin; /** dir signal pin */ +// timer_group_t timer_group; /** timer group, useful if we are controlling more than 2 steppers */ +// timer_idx_t timer_idx; /** timer index, useful if we are controlling 2steppers */ +// float stepAngle; /** one step angle in degrees (usually 1.8deg), used in steps per rotation calculation */ +//} stepper_config_t; +// +//typedef struct +//{ +// uint32_t stepInterval = 40000; // step interval in ns/25 +// double targetSpeed = 0; // target speed in steps/s +// double currentSpeed = 0; +// double accInc = 0; +// double decInc = 0; +// uint32_t stepCnt = 0; // step counter +// uint32_t accEnd; // when to end acc and start coast +// uint32_t coastEnd; // when to end coast and start decel +// uint32_t stepsToGo = 0; // steps we need to take +// float speed = 100; // speed in rad/s +// float acc = 100; // acceleration in rad*second^-2 +// float dec = 100; // decceleration in rad*second^-2 +// uint32_t accSteps = 0; +// uint32_t decSteps = 0; +// uint8_t status = DISABLED; +// bool dir = CW; +// bool runInfinite = false; +// uint16_t stepsPerRot; // steps per one revolution, 360/stepAngle * microstep +// uint16_t stepsPerMm = 0; /** Steps per one milimiter, if the motor is performing linear movement */ +//} ctrl_var_t; + From 7bde75806c6fea7beedc67b6fd5b9a3878690cb8 Mon Sep 17 00:00:00 2001 From: jonny_l480 Date: Tue, 11 Apr 2023 10:26:21 +0200 Subject: [PATCH 03/10] Fixes, Works but crashes when finished accelerating Some adjustments to make it work - fix direction change (temporary) - fix isr initial start See notes in stepper.cpp Todo: - fix crash after accelerating - add initial speed (takes long to accelerate from 0 - --- main/guide-stepper.cpp | 6 +++--- main/stepper.cpp | 48 ++++++++++++++++++++++++++++++------------ 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/main/guide-stepper.cpp b/main/guide-stepper.cpp index 25da8ea..4542668 100644 --- a/main/guide-stepper.cpp +++ b/main/guide-stepper.cpp @@ -188,15 +188,15 @@ void task_stepper_test(void *pvParameter) } if (SW_PRESET1.risingEdge) { buzzer.beep(2, 300, 100); - stepper_setTargetSteps(100); + stepper_setTargetSteps(1000); } if (SW_PRESET2.risingEdge) { buzzer.beep(1, 500, 100); - stepper_setTargetSteps(1000); + stepper_setTargetSteps(10000); } if (SW_PRESET3.risingEdge) { buzzer.beep(1, 100, 100); - stepper_setTargetSteps(6000); + stepper_setTargetSteps(60000); } } } diff --git a/main/stepper.cpp b/main/stepper.cpp index 9c4b54a..2305ccb 100644 --- a/main/stepper.cpp +++ b/main/stepper.cpp @@ -19,6 +19,7 @@ extern "C" { static const char *TAG = "stepper-ctl"; //tag for logging bool direction = 0; +bool timerIsRunning = false; bool timer_isr(void *arg); @@ -43,21 +44,36 @@ StepperControl ctrl; // Create an instance of StepperControl struct -void stepper_setTargetSteps(int steps){ - ESP_LOGW(TAG, "set target steps to %d", steps); +void stepper_setTargetSteps(int target_steps) { + ESP_LOGW(TAG, "set target steps to %d", target_steps); //TODO switch dir pin in isr? not in sync with count - if(steps < 0){ - gpio_set_level(ctrl.directionPin, 1); - } else { - gpio_set_level(ctrl.directionPin, 0); - } + //TODO switch direction using negative values as below +// if(target_steps < 0){ +// gpio_set_level(ctrl.directionPin, 1); +// } else { +// gpio_set_level(ctrl.directionPin, 0); +// } + ESP_LOGW(TAG, "toggle direction -> %d", direction); + - ctrl.targetSteps = abs(steps); + // Update the targetSteps value + ctrl.targetSteps = abs(target_steps); + + // Check if the timer is currently paused + if (!timerIsRunning){ + // If the timer is paused, start it again with the updated targetSteps + timer_set_alarm_value(ctrl.timerGroup, ctrl.timerIdx, 1000); + timer_set_counter_value(ctrl.timerGroup, ctrl.timerIdx, 1000); + timer_start(ctrl.timerGroup, ctrl.timerIdx); + ESP_LOGW(TAG, "STARTED TIMER"); + } } + + void stepper_toggleDirection(){ direction = !direction; - gpio_set_level(ctrl.directionPin, 1); + gpio_set_level(ctrl.directionPin, direction); ESP_LOGW(TAG, "toggle direction -> %d", direction); } @@ -72,8 +88,8 @@ void stepper_init(){ // Set values in StepperControl struct ctrl.targetSteps = 0; ctrl.currentSteps = 0; - ctrl.acceleration = 50; - ctrl.deceleration = 50; + ctrl.acceleration = 100; + ctrl.deceleration = 100; ctrl.pulsePin = STEPPER_STEP_PIN; ctrl.directionPin = STEPPER_DIR_PIN; ctrl.timerGroup = TIMER_GROUP_0; @@ -81,7 +97,7 @@ void stepper_init(){ ctrl.isAccelerating = true; ctrl.isDecelerating = false; ctrl.initialSpeed = 0; // Set initial speed as needed - ctrl.targetSpeed = 500; // Set target speed as needed + ctrl.targetSpeed = 500000; // Set target speed as needed ctrl.currentSpeed = ctrl.initialSpeed; // Configure pulse and direction pins as outputs @@ -116,6 +132,7 @@ bool timer_isr(void *arg) { // Cast arg_val to timer_idx_t timer_idx_t timer_idx = (timer_idx_t)arg_val; int32_t step_diff = ctrl.targetSteps - ctrl.currentSteps; + timerIsRunning = true; if (timer_idx == ctrl.timerIdx) { if (ctrl.currentSteps < ctrl.targetSteps) { @@ -129,9 +146,11 @@ bool timer_isr(void *arg) { ctrl.isAccelerating = false; } } + //FIXME controller crashes when finished accelerating + //Guru Meditation Error: Core 0 panic'ed (Interrupt wdt timeout on CPU0). // Check if decelerating - if (ctrl.isDecelerating) { + if (ctrl.isDecelerating) { //FIXME isDecelerating is never set??? if (ctrl.currentSpeed > ctrl.targetSpeed) { // Decrease speed if not yet at target speed ctrl.currentSpeed -= ctrl.deceleration; @@ -154,7 +173,8 @@ bool timer_isr(void *arg) { } else { // Reached target step count, stop timer timer_pause(ctrl.timerGroup, ctrl.timerIdx); - ESP_LOGW(TAG,"finished, pausing timer"); + timerIsRunning = false; + //ESP_LOGW(TAG,"finished, pausing timer"); } } return 1; From 1e2fa1db8f6763347bf77f4ab4f0c0dd3788909e Mon Sep 17 00:00:00 2001 From: jonny_l480 Date: Sat, 22 Apr 2023 20:47:39 +0200 Subject: [PATCH 04/10] Add basic control task without timer (works, but dropped) basic task for controlling the stepper (no accel / decel etc) works at current speed settings but long moves trigger watchdog so dropped this idea and using timer as before --- main/guide-stepper.cpp | 6 ++--- main/main.cpp | 9 ++++--- main/stepper.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++ main/stepper.hpp | 4 +++ 4 files changed, 69 insertions(+), 6 deletions(-) diff --git a/main/guide-stepper.cpp b/main/guide-stepper.cpp index 4542668..47f2e1b 100644 --- a/main/guide-stepper.cpp +++ b/main/guide-stepper.cpp @@ -188,15 +188,15 @@ void task_stepper_test(void *pvParameter) } if (SW_PRESET1.risingEdge) { buzzer.beep(2, 300, 100); - stepper_setTargetSteps(1000); + stepperSw_setTargetSteps(1000); } if (SW_PRESET2.risingEdge) { buzzer.beep(1, 500, 100); - stepper_setTargetSteps(10000); + stepperSw_setTargetSteps(10000); } if (SW_PRESET3.risingEdge) { buzzer.beep(1, 100, 100); - stepper_setTargetSteps(60000); + stepperSw_setTargetSteps(30000); } } } diff --git a/main/main.cpp b/main/main.cpp index 0533bd5..3b424fc 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -17,6 +17,8 @@ extern "C" #include "guide-stepper.hpp" #include "encoder.hpp" +#include "stepper.hpp" + //================================= //=========== functions =========== @@ -92,13 +94,14 @@ extern "C" void app_main() #ifdef STEPPER_TEST //create task for stepper testing - xTaskCreate(task_stepper_test, "task_stepper_test", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL); + xTaskCreate(task_stepperCtrlSw, "task stepper control software", configMINIMAL_STACK_SIZE * 3, NULL, configMAX_PRIORITIES - 1, NULL); + xTaskCreate(task_stepper_test, "task_stepper_test", configMINIMAL_STACK_SIZE * 3, NULL, 2, NULL); #else //create task for controlling the machine - xTaskCreate(task_control, "task_control", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL); + xTaskCreate(task_control, "task_control", configMINIMAL_STACK_SIZE * 3, NULL, 4, NULL); //create task for controlling the stepper - xTaskCreate(task_stepper_ctl, "task_stepper_ctl", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL); + xTaskCreate(task_stepper_ctl, "task_stepper_ctl", configMINIMAL_STACK_SIZE * 3, NULL, 2, NULL); #endif //create task for handling the buzzer diff --git a/main/stepper.cpp b/main/stepper.cpp index 2305ccb..be7b0d2 100644 --- a/main/stepper.cpp +++ b/main/stepper.cpp @@ -2,6 +2,7 @@ #include "config.hpp" +#include //config from config.hpp //#define STEPPER_STEP_PIN GPIO_NUM_18 //mos1 @@ -11,6 +12,7 @@ extern "C" { #include "driver/timer.h" #include "driver/gpio.h" +#include "esp_log.h" } #define TIMER_F 1000000ULL @@ -24,6 +26,60 @@ bool timerIsRunning = false; bool timer_isr(void *arg); + + + +//stepper driver in software for testing (no timer/interrupt): +uint64_t stepsTarget = 0; +void task_stepperCtrlSw(void *pvParameter) +{ + uint64_t stepsNow = 0; + int increment = 1; + + while(1){ + int64_t delta = stepsTarget - stepsNow; + + //at position, nothing to do + if (delta == 0){ + ESP_LOGW(TAG, "delta =0 waiting for change"); + vTaskDelay(300 / portTICK_PERIOD_MS); + continue; + } + + //define direction + if (delta > 0){ + gpio_set_level(STEPPER_DIR_PIN, 0); + increment = 1; + } else { + gpio_set_level(STEPPER_DIR_PIN, 1); + increment = -1; + } + + //do one step + //note: triggers watchdog at long movements + stepsNow += increment; + gpio_set_level(STEPPER_STEP_PIN, 1); + ets_delay_us(30); + gpio_set_level(STEPPER_STEP_PIN, 0); + ets_delay_us(90); //speed + } +} + +void stepperSw_setTargetSteps(uint64_t target){ + stepsTarget = target; + char buffer[20]; + snprintf(buffer, sizeof(buffer), "%" PRIu64, target); + ESP_LOGW(TAG, "set target steps to %s", buffer); +} + + + + + + + + + typedef struct { int targetSteps; // Target step count int currentSteps; // Current step count diff --git a/main/stepper.hpp b/main/stepper.hpp index 4e9292d..545dce3 100644 --- a/main/stepper.hpp +++ b/main/stepper.hpp @@ -6,6 +6,10 @@ void stepper_setTargetSteps(int steps); void stepper_toggleDirection(); +//control stepper without timer (software) +void task_stepperCtrlSw(void *pvParameter); +void stepperSw_setTargetSteps(uint64_t target); + //typedef struct //{ // uint8_t stepPin; /** step signal pin */ From b6a7ee65edd727936c824605b9a28b39b7e6345a Mon Sep 17 00:00:00 2001 From: jonny_l480 Date: Sun, 23 Apr 2023 11:08:55 +0200 Subject: [PATCH 05/10] Add stepper ISR func to function-diagram (WIP) add diagram page with current idea for custom stepper driver that handles acceleration, deceleration and dynamic changes --- function-diagram.drawio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/function-diagram.drawio b/function-diagram.drawio index 496684e..3930368 100644 --- a/function-diagram.drawio +++ b/function-diagram.drawio @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file From 61deaf9eada2472c700a5f3b99659ca35b9c0b39 Mon Sep 17 00:00:00 2001 From: jonny_ji7 Date: Mon, 24 Apr 2023 23:14:39 +0200 Subject: [PATCH 06/10] New implementation with timer, partial functional (WIP) Partially implemented stepper driver as planned in diagram works partially (reset button toggles distances) major issues though - wip needs debugging and testing --- function-diagram.drawio | 1056 ++++++++++++++++++++++++++++++++++++++- main/guide-stepper.cpp | 36 +- main/main.cpp | 2 +- main/stepper.cpp | 215 +++++--- 4 files changed, 1237 insertions(+), 72 deletions(-) diff --git a/function-diagram.drawio b/function-diagram.drawio index 3930368..2612b8a 100644 --- a/function-diagram.drawio +++ b/function-diagram.drawio @@ -1 +1,1055 @@ -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 \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main/guide-stepper.cpp b/main/guide-stepper.cpp index 47f2e1b..5bc694c 100644 --- a/main/guide-stepper.cpp +++ b/main/guide-stepper.cpp @@ -168,6 +168,7 @@ void updateSpeedFromAdc() { void task_stepper_test(void *pvParameter) { stepper_init(); + int state = 0; while(1){ vTaskDelay(20 / portTICK_PERIOD_MS); @@ -183,21 +184,28 @@ void task_stepper_test(void *pvParameter) SW_AUTO_CUT.handle(); if (SW_RESET.risingEdge) { - stepper_toggleDirection(); - buzzer.beep(1, 1000, 100); - } - if (SW_PRESET1.risingEdge) { - buzzer.beep(2, 300, 100); - stepperSw_setTargetSteps(1000); - } - if (SW_PRESET2.risingEdge) { - buzzer.beep(1, 500, 100); - stepperSw_setTargetSteps(10000); - } - if (SW_PRESET3.risingEdge) { - buzzer.beep(1, 100, 100); - stepperSw_setTargetSteps(30000); + //stepper_toggleDirection(); + //buzzer.beep(1, 1000, 100); + if (state) { + stepper_setTargetSteps(1000); + state = 0; + } else { + stepper_setTargetSteps(600); + state = 1; + } } + // if (SW_PRESET1.risingEdge) { + // buzzer.beep(2, 300, 100); + // stepperSw_setTargetSteps(1000); + // } + // if (SW_PRESET2.risingEdge) { + // buzzer.beep(1, 500, 100); + // stepperSw_setTargetSteps(10000); + // } + // if (SW_PRESET3.risingEdge) { + // buzzer.beep(1, 100, 100); + // stepperSw_setTargetSteps(30000); + // } } } diff --git a/main/main.cpp b/main/main.cpp index 3b424fc..c8fc04f 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -94,7 +94,7 @@ extern "C" void app_main() #ifdef STEPPER_TEST //create task for stepper testing - xTaskCreate(task_stepperCtrlSw, "task stepper control software", configMINIMAL_STACK_SIZE * 3, NULL, configMAX_PRIORITIES - 1, NULL); + //xTaskCreate(task_stepperCtrlSw, "task stepper control software", configMINIMAL_STACK_SIZE * 3, NULL, configMAX_PRIORITIES - 1, NULL); xTaskCreate(task_stepper_test, "task_stepper_test", configMINIMAL_STACK_SIZE * 3, NULL, 2, NULL); #else //create task for controlling the machine diff --git a/main/stepper.cpp b/main/stepper.cpp index be7b0d2..52cb0a6 100644 --- a/main/stepper.cpp +++ b/main/stepper.cpp @@ -1,7 +1,9 @@ //custom driver for stepper motor #include "config.hpp" +#include "hal/timer_types.h" +#include #include //config from config.hpp @@ -20,13 +22,16 @@ extern "C" { #define NS_TO_T_TICKS(x) (x) static const char *TAG = "stepper-ctl"; //tag for logging -bool direction = 0; +bool direction = 1; bool timerIsRunning = false; bool timer_isr(void *arg); - + static timer_group_t timerGroup = TIMER_GROUP_0; + static timer_idx_t timerIdx = TIMER_0; + static uint64_t posTarget = 0; + static uint64_t posNow = 0; //stepper driver in software for testing (no timer/interrupt): @@ -101,26 +106,23 @@ StepperControl ctrl; // Create an instance of StepperControl struct void stepper_setTargetSteps(int target_steps) { - ESP_LOGW(TAG, "set target steps to %d", target_steps); + ESP_LOGW(TAG, "set target steps from %lld to %d (stepsNow: %llu", (long long int)posTarget, target_steps, (long long int)posNow); + posTarget = target_steps; //TODO switch dir pin in isr? not in sync with count //TODO switch direction using negative values as below -// if(target_steps < 0){ -// gpio_set_level(ctrl.directionPin, 1); -// } else { -// gpio_set_level(ctrl.directionPin, 0); -// } - ESP_LOGW(TAG, "toggle direction -> %d", direction); - // Update the targetSteps value - ctrl.targetSteps = abs(target_steps); +//ctrl.targetSteps = abs(target_steps); // Check if the timer is currently paused + ESP_LOGW(TAG, "check if timer is running %d", timerIsRunning); if (!timerIsRunning){ // If the timer is paused, start it again with the updated targetSteps - timer_set_alarm_value(ctrl.timerGroup, ctrl.timerIdx, 1000); - timer_set_counter_value(ctrl.timerGroup, ctrl.timerIdx, 1000); - timer_start(ctrl.timerGroup, ctrl.timerIdx); + timerIsRunning = true; + ESP_LOGW(TAG, "starting timer because did not run before"); + ESP_ERROR_CHECK(timer_set_alarm_value(timerGroup, timerIdx, 1000)); + //timer_set_counter_value(timerGroup, timerIdx, 1000); + ESP_ERROR_CHECK(timer_start(timerGroup, timerIdx)); ESP_LOGW(TAG, "STARTED TIMER"); } } @@ -183,56 +185,157 @@ void stepper_init(){ bool timer_isr(void *arg) { - // Cast arg to an integer type that has the same size as timer_idx_t - uintptr_t arg_val = (uintptr_t)arg; - // Cast arg_val to timer_idx_t - timer_idx_t timer_idx = (timer_idx_t)arg_val; - int32_t step_diff = ctrl.targetSteps - ctrl.currentSteps; - timerIsRunning = true; + //turn pin on (fast) + GPIO.out_w1ts = (1ULL << STEPPER_STEP_PIN); - if (timer_idx == ctrl.timerIdx) { - if (ctrl.currentSteps < ctrl.targetSteps) { - // Check if accelerating - if (ctrl.isAccelerating) { - if (ctrl.currentSpeed < ctrl.targetSpeed) { - // Increase speed if not yet at target speed - ctrl.currentSpeed += ctrl.acceleration; - } else { - // Reached target speed, clear accelerating flag - ctrl.isAccelerating = false; - } - } - //FIXME controller crashes when finished accelerating - //Guru Meditation Error: Core 0 panic'ed (Interrupt wdt timeout on CPU0). - // Check if decelerating - if (ctrl.isDecelerating) { //FIXME isDecelerating is never set??? - if (ctrl.currentSpeed > ctrl.targetSpeed) { - // Decrease speed if not yet at target speed - ctrl.currentSpeed -= ctrl.deceleration; - } else { - // Reached target speed, clear decelerating flag - ctrl.isDecelerating = false; - } - } + uint32_t speedTarget = 100000; + uint32_t speedMin = 20000; + //FIXME increment actually has to be re-calculated every run to have linear accel (because also gets called faster/slower) + uint32_t decel_increment = 200; + uint32_t accel_increment = 150; - // Generate pulse for stepper motor - gpio_set_level(ctrl.pulsePin, 1); - ets_delay_us(500); // Adjust delay as needed - gpio_set_level(ctrl.pulsePin, 0); + static uint64_t stepsToGo = 0; + static uint32_t speedNow = speedMin; - // Update current step count - ctrl.currentSteps++; - // Update timer period based on current speed - timer_set_alarm_value(ctrl.timerGroup, ctrl.timerIdx, TIMER_BASE_CLK / ctrl.currentSpeed); + //--- define direction, stepsToGo --- + //int64_t delta = (int)posTarget - (int)posNow; + //bool directionTarget = delta >= 0 ? 1 : 0; + bool directionTarget; + if (posTarget > posNow) { + directionTarget = 1; + } else { + directionTarget = 0; + } + + //directionTarget = 1; + //direction = 1; + //gpio_set_level(STEPPER_DIR_PIN, direction); + + if (direction != directionTarget) { + //ESP_LOGW(TAG, "direction differs! new: %d", direction); + if (stepsToGo == 0){ + direction = directionTarget; //switch direction if almost idle + gpio_set_level(STEPPER_DIR_PIN, direction); + //stepsToGo = abs((int64_t)posTarget - (int64_t)posNow); + stepsToGo = 2; } else { - // Reached target step count, stop timer - timer_pause(ctrl.timerGroup, ctrl.timerIdx); - timerIsRunning = false; - //ESP_LOGW(TAG,"finished, pausing timer"); + //stepsToGo = speedNow / decel_increment; //set steps to decel to min speed + stepsToGo = speedNow / decel_increment; + } + } else if (direction == 1) { + stepsToGo = posTarget - posNow; + //stepsToGo = abs((int64_t)posTarget - (int64_t)posNow); + } else { + stepsToGo = posNow - posTarget; + //stepsToGo = abs((int64_t)posTarget - (int64_t)posNow); + } + + //TODO fix direction code above currently ony works with the below line instead + //stepsToGo = abs((int64_t)posTarget - (int64_t)posNow); + + + //--- define speed --- + //uint64_t stepsAccelRemaining = (speedTarget - speedNow) / accel_increment; + uint64_t stepsDecelRemaining = (speedNow - speedMin) / decel_increment; + + if (stepsToGo <= stepsDecelRemaining) { + if ((speedNow - speedMin) > decel_increment) { + speedNow -= decel_increment; + } else { + speedNow = speedMin; //PAUSE HERE??? / irrelevant? } } + else if (speedNow < speedTarget) { + speedNow += accel_increment; + if (speedNow > speedTarget) speedNow = speedTarget; + } + else { //not relevant? + speedNow = speedTarget; + } + + + //--- update timer --- + if (stepsToGo == 0) { + timer_pause(timerGroup, timerIdx); + timerIsRunning = false; + } + else { + ESP_ERROR_CHECK(timer_set_alarm_value(timerGroup, timerIdx, TIMER_BASE_CLK / speedNow)); + } + + + //--- increment position --- + if (stepsToGo > 0){ + stepsToGo --; //TODO increment at start, check at start?? + } + if (direction == 1){ + posNow ++; + } else { + posNow --; //FIXME posNow gets extremely big after second start (underflow?) + } + + + // Generate pulse for stepper motor + //gpio_set_level(STEPPER_STEP_PIN, 1); + //ets_delay_us(500); // Adjust delay as needed + //gpio_set_level(STEPPER_STEP_PIN, 0); + + //turn pin off (fast) + GPIO.out_w1tc = (1ULL << STEPPER_STEP_PIN); + + +// // Cast arg to an integer type that has the same size as timer_idx_t +// uintptr_t arg_val = (uintptr_t)arg; +// // Cast arg_val to timer_idx_t +// timer_idx_t timer_idx = (timer_idx_t)arg_val; +// int32_t step_diff = ctrl.targetSteps - ctrl.currentSteps; +// timerIsRunning = true; +// +// if (timer_idx == ctrl.timerIdx) { +// if (ctrl.currentSteps < ctrl.targetSteps) { +// // Check if accelerating +// if (ctrl.isAccelerating) { +// if (ctrl.currentSpeed < ctrl.targetSpeed) { +// // Increase speed if not yet at target speed +// ctrl.currentSpeed += ctrl.acceleration; +// } else { +// // Reached target speed, clear accelerating flag +// ctrl.isAccelerating = false; +// } +// } +// //FIXME controller crashes when finished accelerating +// //Guru Meditation Error: Core 0 panic'ed (Interrupt wdt timeout on CPU0). +// +// // Check if decelerating +// if (ctrl.isDecelerating) { //FIXME isDecelerating is never set??? +// if (ctrl.currentSpeed > ctrl.targetSpeed) { +// // Decrease speed if not yet at target speed +// ctrl.currentSpeed -= ctrl.deceleration; +// } else { +// // Reached target speed, clear decelerating flag +// ctrl.isDecelerating = false; +// } +// } +// +// // Generate pulse for stepper motor +// gpio_set_level(ctrl.pulsePin, 1); +// ets_delay_us(500); // Adjust delay as needed +// gpio_set_level(ctrl.pulsePin, 0); +// +// // Update current step count +// ctrl.currentSteps++; +// +// // Update timer period based on current speed +// timer_set_alarm_value(ctrl.timerGroup, ctrl.timerIdx, TIMER_BASE_CLK / ctrl.currentSpeed); +// } else { +// // Reached target step count, stop timer +// timer_pause(ctrl.timerGroup, ctrl.timerIdx); +// timerIsRunning = false; +// //ESP_LOGW(TAG,"finished, pausing timer"); +// } +// } return 1; } From c99e71846c7def028b821b240f023a58b4bc9d14 Mon Sep 17 00:00:00 2001 From: jonny_l480 Date: Tue, 25 Apr 2023 11:39:59 +0200 Subject: [PATCH 07/10] Remove unused code, potential bugfix remove commented out code and unused code from previous approach addition al check that might fix issue with underflow --- main/guide-stepper.cpp | 1 - main/stepper.cpp | 180 +++++++++++------------------------------ main/stepper.hpp | 39 +-------- 3 files changed, 49 insertions(+), 171 deletions(-) diff --git a/main/guide-stepper.cpp b/main/guide-stepper.cpp index 5bc694c..867cf15 100644 --- a/main/guide-stepper.cpp +++ b/main/guide-stepper.cpp @@ -184,7 +184,6 @@ void task_stepper_test(void *pvParameter) SW_AUTO_CUT.handle(); if (SW_RESET.risingEdge) { - //stepper_toggleDirection(); //buzzer.beep(1, 1000, 100); if (state) { stepper_setTargetSteps(1000); diff --git a/main/stepper.cpp b/main/stepper.cpp index 52cb0a6..eccc845 100644 --- a/main/stepper.cpp +++ b/main/stepper.cpp @@ -1,39 +1,44 @@ //custom driver for stepper motor - #include "config.hpp" #include "hal/timer_types.h" - #include #include -//config from config.hpp -//#define STEPPER_STEP_PIN GPIO_NUM_18 //mos1 -//#define STEPPER_DIR_PIN GPIO_NUM_16 //ST3 -// - extern "C" { #include "driver/timer.h" #include "driver/gpio.h" #include "esp_log.h" } +//config from config.hpp +//#define STEPPER_STEP_PIN GPIO_NUM_18 //mos1 +//#define STEPPER_DIR_PIN GPIO_NUM_16 //ST3 + #define TIMER_F 1000000ULL #define TICK_PER_S TIMER_S #define NS_TO_T_TICKS(x) (x) + + +//======================== +//=== global variables === +//======================== static const char *TAG = "stepper-ctl"; //tag for logging + bool direction = 1; bool timerIsRunning = false; - bool timer_isr(void *arg); - - static timer_group_t timerGroup = TIMER_GROUP_0; - static timer_idx_t timerIdx = TIMER_0; - static uint64_t posTarget = 0; - static uint64_t posNow = 0; +static timer_group_t timerGroup = TIMER_GROUP_0; +static timer_idx_t timerIdx = TIMER_0; +static uint64_t posTarget = 0; +static uint64_t posNow = 0; + +//=============================== +//=== software - control task === +//=============================== //stepper driver in software for testing (no timer/interrupt): uint64_t stepsTarget = 0; void task_stepperCtrlSw(void *pvParameter) @@ -70,6 +75,9 @@ void task_stepperCtrlSw(void *pvParameter) } } +//================================= +//=== software - set target pos === +//================================= void stepperSw_setTargetSteps(uint64_t target){ stepsTarget = target; char buffer[20]; @@ -83,28 +91,9 @@ void stepperSw_setTargetSteps(uint64_t target){ - - -typedef struct { - int targetSteps; // Target step count - int currentSteps; // Current step count - int acceleration; // Acceleration (in steps per second^2) - int deceleration; // Deceleration (in steps per second^2) - gpio_num_t pulsePin; // Pin for pulse signal - gpio_num_t directionPin; // Pin for direction signal - timer_group_t timerGroup; // Timer group - timer_idx_t timerIdx; // Timer index - bool isAccelerating; // Flag to indicate if accelerating - bool isDecelerating; // Flag to indicate if decelerating - int initialSpeed; // Initial speed (in steps per second) - int targetSpeed; // Target speed (in steps per second) - int currentSpeed; // Current speed (in steps per second) -} StepperControl; - -StepperControl ctrl; // Create an instance of StepperControl struct - - - +//======================== +//==== set target pos ==== +//======================== void stepper_setTargetSteps(int target_steps) { ESP_LOGW(TAG, "set target steps from %lld to %d (stepsNow: %llu", (long long int)posTarget, target_steps, (long long int)posNow); posTarget = target_steps; @@ -129,40 +118,18 @@ void stepper_setTargetSteps(int target_steps) { -void stepper_toggleDirection(){ - direction = !direction; - gpio_set_level(ctrl.directionPin, direction); - ESP_LOGW(TAG, "toggle direction -> %d", direction); -} - - - - - +//======================== +//===== init stepper ===== +//======================== void stepper_init(){ ESP_LOGW(TAG, "init - configure struct..."); - // Set values in StepperControl struct - ctrl.targetSteps = 0; - ctrl.currentSteps = 0; - ctrl.acceleration = 100; - ctrl.deceleration = 100; - ctrl.pulsePin = STEPPER_STEP_PIN; - ctrl.directionPin = STEPPER_DIR_PIN; - ctrl.timerGroup = TIMER_GROUP_0; - ctrl.timerIdx = TIMER_0; - ctrl.isAccelerating = true; - ctrl.isDecelerating = false; - ctrl.initialSpeed = 0; // Set initial speed as needed - ctrl.targetSpeed = 500000; // Set target speed as needed - ctrl.currentSpeed = ctrl.initialSpeed; // Configure pulse and direction pins as outputs ESP_LOGW(TAG, "init - configure gpio pins..."); - gpio_set_direction(ctrl.pulsePin, GPIO_MODE_OUTPUT); - gpio_set_direction(ctrl.directionPin, GPIO_MODE_OUTPUT); - + gpio_set_direction(STEPPER_DIR_PIN, GPIO_MODE_OUTPUT); + gpio_set_direction(STEPPER_STEP_PIN, GPIO_MODE_OUTPUT); ESP_LOGW(TAG, "init - initialize/configure timer..."); timer_config_t timer_conf = { @@ -173,27 +140,28 @@ void stepper_init(){ .auto_reload = TIMER_AUTORELOAD_EN, // reload pls .divider = 80000000ULL / TIMER_F, // ns resolution }; - - - ESP_ERROR_CHECK(timer_init(ctrl.timerGroup, ctrl.timerIdx, &timer_conf)); // init the timer - ESP_ERROR_CHECK(timer_set_counter_value(ctrl.timerGroup, ctrl.timerIdx, 0)); // set it to 0 - //ESP_ERROR_CHECK(timer_isr_callback_add(ctrl.timerGroup, ctrl.timerIdx, timer_isr, )); // add callback fn to run when alarm is triggrd - ESP_ERROR_CHECK(timer_isr_callback_add(ctrl.timerGroup, ctrl.timerIdx, timer_isr, (void *)ctrl.timerIdx, 0)); + ESP_ERROR_CHECK(timer_init(timerGroup, timerIdx, &timer_conf)); // init the timer + ESP_ERROR_CHECK(timer_set_counter_value(timerGroup, timerIdx, 0)); // set it to 0 + ESP_ERROR_CHECK(timer_isr_callback_add(timerGroup, timerIdx, timer_isr, (void *)timerIdx, 0)); } - +//================================ +//=== timer interrupt function === +//================================ bool timer_isr(void *arg) { + // Generate pulse for stepper motor //turn pin on (fast) GPIO.out_w1ts = (1ULL << STEPPER_STEP_PIN); - uint32_t speedTarget = 100000; - uint32_t speedMin = 20000; + //--- variables --- + static uint32_t speedTarget = 100000; + static uint32_t speedMin = 20000; //FIXME increment actually has to be re-calculated every run to have linear accel (because also gets called faster/slower) - uint32_t decel_increment = 200; - uint32_t accel_increment = 150; + static uint32_t decel_increment = 200; + static uint32_t accel_increment = 150; static uint64_t stepsToGo = 0; static uint32_t speedNow = speedMin; @@ -208,11 +176,9 @@ bool timer_isr(void *arg) { } else { directionTarget = 0; } - //directionTarget = 1; //direction = 1; //gpio_set_level(STEPPER_DIR_PIN, direction); - if (direction != directionTarget) { //ESP_LOGW(TAG, "direction differs! new: %d", direction); if (stepsToGo == 0){ @@ -231,13 +197,11 @@ bool timer_isr(void *arg) { stepsToGo = posNow - posTarget; //stepsToGo = abs((int64_t)posTarget - (int64_t)posNow); } - //TODO fix direction code above currently ony works with the below line instead //stepsToGo = abs((int64_t)posTarget - (int64_t)posNow); //--- define speed --- - //uint64_t stepsAccelRemaining = (speedTarget - speedNow) / accel_increment; uint64_t stepsDecelRemaining = (speedNow - speedMin) / decel_increment; if (stepsToGo <= stepsDecelRemaining) { @@ -273,69 +237,19 @@ bool timer_isr(void *arg) { if (direction == 1){ posNow ++; } else { - posNow --; //FIXME posNow gets extremely big after second start (underflow?) + //prevent underflow FIXME this case should not happen in the first place? + if (posNow != 0){ + posNow --; + } else { + //ERR posNow would be negative? + } } // Generate pulse for stepper motor - //gpio_set_level(STEPPER_STEP_PIN, 1); - //ets_delay_us(500); // Adjust delay as needed - //gpio_set_level(STEPPER_STEP_PIN, 0); - //turn pin off (fast) GPIO.out_w1tc = (1ULL << STEPPER_STEP_PIN); - -// // Cast arg to an integer type that has the same size as timer_idx_t -// uintptr_t arg_val = (uintptr_t)arg; -// // Cast arg_val to timer_idx_t -// timer_idx_t timer_idx = (timer_idx_t)arg_val; -// int32_t step_diff = ctrl.targetSteps - ctrl.currentSteps; -// timerIsRunning = true; -// -// if (timer_idx == ctrl.timerIdx) { -// if (ctrl.currentSteps < ctrl.targetSteps) { -// // Check if accelerating -// if (ctrl.isAccelerating) { -// if (ctrl.currentSpeed < ctrl.targetSpeed) { -// // Increase speed if not yet at target speed -// ctrl.currentSpeed += ctrl.acceleration; -// } else { -// // Reached target speed, clear accelerating flag -// ctrl.isAccelerating = false; -// } -// } -// //FIXME controller crashes when finished accelerating -// //Guru Meditation Error: Core 0 panic'ed (Interrupt wdt timeout on CPU0). -// -// // Check if decelerating -// if (ctrl.isDecelerating) { //FIXME isDecelerating is never set??? -// if (ctrl.currentSpeed > ctrl.targetSpeed) { -// // Decrease speed if not yet at target speed -// ctrl.currentSpeed -= ctrl.deceleration; -// } else { -// // Reached target speed, clear decelerating flag -// ctrl.isDecelerating = false; -// } -// } -// -// // Generate pulse for stepper motor -// gpio_set_level(ctrl.pulsePin, 1); -// ets_delay_us(500); // Adjust delay as needed -// gpio_set_level(ctrl.pulsePin, 0); -// -// // Update current step count -// ctrl.currentSteps++; -// -// // Update timer period based on current speed -// timer_set_alarm_value(ctrl.timerGroup, ctrl.timerIdx, TIMER_BASE_CLK / ctrl.currentSpeed); -// } else { -// // Reached target step count, stop timer -// timer_pause(ctrl.timerGroup, ctrl.timerIdx); -// timerIsRunning = false; -// //ESP_LOGW(TAG,"finished, pausing timer"); -// } -// } return 1; } diff --git a/main/stepper.hpp b/main/stepper.hpp index 545dce3..7b7d165 100644 --- a/main/stepper.hpp +++ b/main/stepper.hpp @@ -1,44 +1,9 @@ +#pragma once - +//init stepper pins and timer void stepper_init(); - void stepper_setTargetSteps(int steps); -void stepper_toggleDirection(); - //control stepper without timer (software) void task_stepperCtrlSw(void *pvParameter); void stepperSw_setTargetSteps(uint64_t target); - -//typedef struct -//{ -// uint8_t stepPin; /** step signal pin */ -// uint8_t dirPin; /** dir signal pin */ -// timer_group_t timer_group; /** timer group, useful if we are controlling more than 2 steppers */ -// timer_idx_t timer_idx; /** timer index, useful if we are controlling 2steppers */ -// float stepAngle; /** one step angle in degrees (usually 1.8deg), used in steps per rotation calculation */ -//} stepper_config_t; -// -//typedef struct -//{ -// uint32_t stepInterval = 40000; // step interval in ns/25 -// double targetSpeed = 0; // target speed in steps/s -// double currentSpeed = 0; -// double accInc = 0; -// double decInc = 0; -// uint32_t stepCnt = 0; // step counter -// uint32_t accEnd; // when to end acc and start coast -// uint32_t coastEnd; // when to end coast and start decel -// uint32_t stepsToGo = 0; // steps we need to take -// float speed = 100; // speed in rad/s -// float acc = 100; // acceleration in rad*second^-2 -// float dec = 100; // decceleration in rad*second^-2 -// uint32_t accSteps = 0; -// uint32_t decSteps = 0; -// uint8_t status = DISABLED; -// bool dir = CW; -// bool runInfinite = false; -// uint16_t stepsPerRot; // steps per one revolution, 360/stepAngle * microstep -// uint16_t stepsPerMm = 0; /** Steps per one milimiter, if the motor is performing linear movement */ -//} ctrl_var_t; - From 63f0da25f13582a7fc5c387448f787c381a28c76 Mon Sep 17 00:00:00 2001 From: jonny_ji7 Date: Tue, 25 Apr 2023 14:35:07 +0200 Subject: [PATCH 08/10] Fix isr-function (fully functional now), Add task for debug output new driver works well while testing with one button with debug output - small changes to isr function that made it fully functional! - add stepper debug task - minor optimizations necessary --- main/guide-stepper.cpp | 23 ++++++++++++++-------- main/main.cpp | 1 + main/stepper.cpp | 44 +++++++++++++++++++++++++++++++++++------- main/stepper.hpp | 3 +++ 4 files changed, 56 insertions(+), 15 deletions(-) diff --git a/main/guide-stepper.cpp b/main/guide-stepper.cpp index 867cf15..16b3e44 100644 --- a/main/guide-stepper.cpp +++ b/main/guide-stepper.cpp @@ -183,16 +183,24 @@ void task_stepper_test(void *pvParameter) SW_CUT.handle(); SW_AUTO_CUT.handle(); + //cycle through test commands with one button if (SW_RESET.risingEdge) { - //buzzer.beep(1, 1000, 100); - if (state) { - stepper_setTargetSteps(1000); - state = 0; - } else { - stepper_setTargetSteps(600); - state = 1; + switch (state){ + case 0: + stepper_setTargetSteps(1000); + state++; + break; + case 1: + stepper_setTargetSteps(100); + state++; + break; + case 2: + stepper_setTargetSteps(2000); + state = 0; + break; } } + } // if (SW_PRESET1.risingEdge) { // buzzer.beep(2, 300, 100); // stepperSw_setTargetSteps(1000); @@ -205,7 +213,6 @@ void task_stepper_test(void *pvParameter) // buzzer.beep(1, 100, 100); // stepperSw_setTargetSteps(30000); // } - } } diff --git a/main/main.cpp b/main/main.cpp index c8fc04f..7c00440 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -96,6 +96,7 @@ extern "C" void app_main() //create task for stepper testing //xTaskCreate(task_stepperCtrlSw, "task stepper control software", configMINIMAL_STACK_SIZE * 3, NULL, configMAX_PRIORITIES - 1, NULL); xTaskCreate(task_stepper_test, "task_stepper_test", configMINIMAL_STACK_SIZE * 3, NULL, 2, NULL); + xTaskCreate(task_stepper_debug, "task_stepper_test", configMINIMAL_STACK_SIZE * 3, NULL, 2, NULL); #else //create task for controlling the machine xTaskCreate(task_control, "task_control", configMINIMAL_STACK_SIZE * 3, NULL, 4, NULL); diff --git a/main/stepper.cpp b/main/stepper.cpp index eccc845..ae12286 100644 --- a/main/stepper.cpp +++ b/main/stepper.cpp @@ -26,13 +26,20 @@ extern "C" { static const char *TAG = "stepper-ctl"; //tag for logging bool direction = 1; +bool directionTarget = 1; bool timerIsRunning = false; bool timer_isr(void *arg); static timer_group_t timerGroup = TIMER_GROUP_0; static timer_idx_t timerIdx = TIMER_0; + +//move to isr static uint64_t posTarget = 0; static uint64_t posNow = 0; +static uint64_t stepsToGo = 0; +static uint32_t speedMin = 20000; +static uint32_t speedNow = speedMin; +static int debug = 0; @@ -88,6 +95,32 @@ void stepperSw_setTargetSteps(uint64_t target){ +void task_stepper_debug(void *pvParameter){ + while (1){ + ESP_LOGI("stepper-DEBUG", + "timer=%d " + "dir=%d " + "dirTarget=%d " + "posTarget=%llu " + "posNow=%llu " + "stepsToGo=%llu " + "speedNow=%u " + "debug=%d ", + + timerIsRunning, + direction, + directionTarget, + posTarget, + posNow, + stepsToGo, + speedNow, + debug + ); + + vTaskDelay(300 / portTICK_PERIOD_MS); + } +} + @@ -158,20 +191,16 @@ bool timer_isr(void *arg) { //--- variables --- static uint32_t speedTarget = 100000; - static uint32_t speedMin = 20000; //FIXME increment actually has to be re-calculated every run to have linear accel (because also gets called faster/slower) static uint32_t decel_increment = 200; static uint32_t accel_increment = 150; - static uint64_t stepsToGo = 0; - static uint32_t speedNow = speedMin; //--- define direction, stepsToGo --- //int64_t delta = (int)posTarget - (int)posNow; //bool directionTarget = delta >= 0 ? 1 : 0; - bool directionTarget; - if (posTarget > posNow) { + if (posTarget >= posNow) { directionTarget = 1; } else { directionTarget = 0; @@ -179,7 +208,7 @@ bool timer_isr(void *arg) { //directionTarget = 1; //direction = 1; //gpio_set_level(STEPPER_DIR_PIN, direction); - if (direction != directionTarget) { + if ( (direction != directionTarget) && (posTarget != posNow)) { //ESP_LOGW(TAG, "direction differs! new: %d", direction); if (stepsToGo == 0){ direction = directionTarget; //switch direction if almost idle @@ -188,7 +217,8 @@ bool timer_isr(void *arg) { stepsToGo = 2; } else { //stepsToGo = speedNow / decel_increment; //set steps to decel to min speed - stepsToGo = speedNow / decel_increment; + //set to minimun decel steps + stepsToGo = (speedNow - speedMin) / decel_increment; } } else if (direction == 1) { stepsToGo = posTarget - posNow; diff --git a/main/stepper.hpp b/main/stepper.hpp index 7b7d165..3474661 100644 --- a/main/stepper.hpp +++ b/main/stepper.hpp @@ -4,6 +4,9 @@ void stepper_init(); void stepper_setTargetSteps(int steps); +//task that periodically logs variables for debugging stepper driver +void task_stepper_debug(void *pvParameter); + //control stepper without timer (software) void task_stepperCtrlSw(void *pvParameter); void stepperSw_setTargetSteps(uint64_t target); From e8e1070bd1ef20af6659b0b2fd56e3dcf8a94d38 Mon Sep 17 00:00:00 2001 From: jonny_ji7 Date: Tue, 25 Apr 2023 17:08:19 +0200 Subject: [PATCH 09/10] Simplify code, Add actual units: TargetPosMm, SpeedMm - add functions setSpeed (mm/s) setTargetPosMm - add macros and function to define and convert variables to actual units speed in mm/s instead of unknown abs pos in mm instead of steps - simplyfy isr code --- main/guide-stepper.cpp | 14 +++- main/stepper.cpp | 145 ++++++++++++++++++++++------------------- main/stepper.hpp | 5 +- 3 files changed, 93 insertions(+), 71 deletions(-) diff --git a/main/guide-stepper.cpp b/main/guide-stepper.cpp index 16b3e44..87d7ffa 100644 --- a/main/guide-stepper.cpp +++ b/main/guide-stepper.cpp @@ -187,15 +187,23 @@ void task_stepper_test(void *pvParameter) if (SW_RESET.risingEdge) { switch (state){ case 0: - stepper_setTargetSteps(1000); + stepper_setTargetPosMm(50); + //stepper_setTargetPosSteps(1000); state++; break; case 1: - stepper_setTargetSteps(100); + stepper_setTargetPosMm(80); + //stepper_setTargetPosSteps(100); state++; break; case 2: - stepper_setTargetSteps(2000); + stepper_setTargetPosMm(20); + //stepper_setTargetPosSteps(100); + state++; + break; + case 3: + stepper_setTargetPosMm(60); + //stepper_setTargetPosSteps(2000); state = 0; break; } diff --git a/main/stepper.cpp b/main/stepper.cpp index ae12286..51232e2 100644 --- a/main/stepper.cpp +++ b/main/stepper.cpp @@ -14,6 +14,13 @@ extern "C" { //#define STEPPER_STEP_PIN GPIO_NUM_18 //mos1 //#define STEPPER_DIR_PIN GPIO_NUM_16 //ST3 +#define STEPPER_STEPS_PER_MM 200/2 //steps/mm +#define STEPPER_SPEED_DEFAULT 20 //mm/s +#define STEPPER_SPEED_MIN 4 //mm/s - speed at which stepper immediately starts/stops +#define STEPPER_ACCEL_INC 3 //steps/s per cycle +#define STEPPER_DECEL_INC 8 //steps/s per cycle + + #define TIMER_F 1000000ULL #define TICK_PER_S TIMER_S #define NS_TO_T_TICKS(x) (x) @@ -33,13 +40,17 @@ bool timer_isr(void *arg); static timer_group_t timerGroup = TIMER_GROUP_0; static timer_idx_t timerIdx = TIMER_0; -//move to isr +//TODO the below variables can be moved to isr function once debug output is no longer needed static uint64_t posTarget = 0; static uint64_t posNow = 0; static uint64_t stepsToGo = 0; -static uint32_t speedMin = 20000; +static uint32_t speedMin = STEPPER_SPEED_MIN * STEPPER_STEPS_PER_MM; static uint32_t speedNow = speedMin; static int debug = 0; +static uint32_t speedTarget = STEPPER_SPEED_DEFAULT * STEPPER_STEPS_PER_MM; +//TODO/NOTE increment actually has to be re-calculated every run to have linear accel (because also gets called faster/slower) +static uint32_t decel_increment = STEPPER_DECEL_INC; +static uint32_t accel_increment = STEPPER_ACCEL_INC; @@ -94,7 +105,9 @@ void stepperSw_setTargetSteps(uint64_t target){ - +//====================== +//===== DEBUG task ===== +//====================== void task_stepper_debug(void *pvParameter){ while (1){ ESP_LOGI("stepper-DEBUG", @@ -105,6 +118,7 @@ void task_stepper_debug(void *pvParameter){ "posNow=%llu " "stepsToGo=%llu " "speedNow=%u " + "speedTarget=%u " "debug=%d ", timerIsRunning, @@ -114,6 +128,7 @@ void task_stepper_debug(void *pvParameter){ posNow, stepsToGo, speedNow, + speedTarget, debug ); @@ -122,22 +137,24 @@ void task_stepper_debug(void *pvParameter){ } +//===================== +//===== set speed ===== +//===================== +void stepper_setSpeed(uint32_t speedMmPerS) { + ESP_LOGW(TAG, "set target speed from %u to %u mm/s (%u steps/s)", + speedTarget, speedMmPerS, speedMmPerS * STEPPER_STEPS_PER_MM); + speedTarget = speedMmPerS * STEPPER_STEPS_PER_MM; +} -//======================== -//==== set target pos ==== -//======================== -void stepper_setTargetSteps(int target_steps) { - ESP_LOGW(TAG, "set target steps from %lld to %d (stepsNow: %llu", (long long int)posTarget, target_steps, (long long int)posNow); +//========================== +//== set target pos STEPS == +//========================== +void stepper_setTargetPosSteps(uint64_t target_steps) { + ESP_LOGW(TAG, "update target position from %llu to %llu steps (stepsNow: %llu", posTarget, target_steps, posNow); posTarget = target_steps; - //TODO switch dir pin in isr? not in sync with count - //TODO switch direction using negative values as below - - // Update the targetSteps value -//ctrl.targetSteps = abs(target_steps); // Check if the timer is currently paused - ESP_LOGW(TAG, "check if timer is running %d", timerIsRunning); if (!timerIsRunning){ // If the timer is paused, start it again with the updated targetSteps timerIsRunning = true; @@ -145,11 +162,17 @@ void stepper_setTargetSteps(int target_steps) { ESP_ERROR_CHECK(timer_set_alarm_value(timerGroup, timerIdx, 1000)); //timer_set_counter_value(timerGroup, timerIdx, 1000); ESP_ERROR_CHECK(timer_start(timerGroup, timerIdx)); - ESP_LOGW(TAG, "STARTED TIMER"); } } +//========================= +//=== set target pos MM === +//========================= +void stepper_setTargetPosMm(uint32_t posMm){ + ESP_LOGW(TAG, "set target position to %u mm", posMm); + stepper_setTargetPosSteps(posMm * STEPPER_STEPS_PER_MM); +} @@ -184,86 +207,80 @@ void stepper_init(){ //=== timer interrupt function === //================================ bool timer_isr(void *arg) { - // Generate pulse for stepper motor - //turn pin on (fast) - GPIO.out_w1ts = (1ULL << STEPPER_STEP_PIN); - + //----------------- //--- variables --- - static uint32_t speedTarget = 100000; - //FIXME increment actually has to be re-calculated every run to have linear accel (because also gets called faster/slower) - static uint32_t decel_increment = 200; - static uint32_t accel_increment = 150; - - + //----------------- + //TODO used (currently global) variables here + //----------------------------------- //--- define direction, stepsToGo --- - //int64_t delta = (int)posTarget - (int)posNow; - //bool directionTarget = delta >= 0 ? 1 : 0; - if (posTarget >= posNow) { - directionTarget = 1; - } else { - directionTarget = 0; - } - //directionTarget = 1; - //direction = 1; - //gpio_set_level(STEPPER_DIR_PIN, direction); + //----------------------------------- + //Note: the idea is that the stepper has to decelerate to min speed first before changeing the direction + //define target direction depending on position difference + bool directionTarget = posTarget > posNow ? 1 : 0; + //DIRECTION DIFFERS (change) if ( (direction != directionTarget) && (posTarget != posNow)) { - //ESP_LOGW(TAG, "direction differs! new: %d", direction); - if (stepsToGo == 0){ - direction = directionTarget; //switch direction if almost idle + if (stepsToGo == 0){ //standstill + direction = directionTarget; //switch direction gpio_set_level(STEPPER_DIR_PIN, direction); - //stepsToGo = abs((int64_t)posTarget - (int64_t)posNow); - stepsToGo = 2; + stepsToGo = abs(int64_t(posTarget - posNow)); } else { - //stepsToGo = speedNow / decel_increment; //set steps to decel to min speed //set to minimun decel steps stepsToGo = (speedNow - speedMin) / decel_increment; } - } else if (direction == 1) { - stepsToGo = posTarget - posNow; - //stepsToGo = abs((int64_t)posTarget - (int64_t)posNow); - } else { - stepsToGo = posNow - posTarget; - //stepsToGo = abs((int64_t)posTarget - (int64_t)posNow); } - //TODO fix direction code above currently ony works with the below line instead - //stepsToGo = abs((int64_t)posTarget - (int64_t)posNow); - + //NORMAL (any direction 0/1) + else { + stepsToGo = abs(int64_t(posTarget - posNow)); + } + //-------------------- //--- define speed --- + //-------------------- + //FIXME noticed crash: division by 0 when min speed > target speed uint64_t stepsDecelRemaining = (speedNow - speedMin) / decel_increment; - + //DECELERATE if (stepsToGo <= stepsDecelRemaining) { + //FIXME if stepsToGo gets updated (lowered) close to target while close to target, the stepper may stop too fast -> implement possibility to 'overshoot and reverse'? if ((speedNow - speedMin) > decel_increment) { speedNow -= decel_increment; } else { speedNow = speedMin; //PAUSE HERE??? / irrelevant? } } + //ACCELERATE else if (speedNow < speedTarget) { speedNow += accel_increment; if (speedNow > speedTarget) speedNow = speedTarget; } + //COASTING else { //not relevant? speedNow = speedTarget; } - - //--- update timer --- + //------------------------------- + //--- update timer, increment --- + //------------------------------- + //AT TARGET -> STOP if (stepsToGo == 0) { timer_pause(timerGroup, timerIdx); timerIsRunning = false; - } - else { - ESP_ERROR_CHECK(timer_set_alarm_value(timerGroup, timerIdx, TIMER_BASE_CLK / speedNow)); + speedNow = speedMin; + return 1; } + //STEPS REMAINING -> NEXT STEP + //update timer with new speed + ESP_ERROR_CHECK(timer_set_alarm_value(timerGroup, timerIdx, TIMER_F / speedNow)); - //--- increment position --- - if (stepsToGo > 0){ - stepsToGo --; //TODO increment at start, check at start?? - } + //generate pulse + GPIO.out_w1ts = (1ULL << STEPPER_STEP_PIN); //turn on (fast) + ets_delay_us(10); + GPIO.out_w1tc = (1ULL << STEPPER_STEP_PIN); //turn off (fast) + + //increment pos + stepsToGo --; if (direction == 1){ posNow ++; } else { @@ -271,15 +288,9 @@ bool timer_isr(void *arg) { if (posNow != 0){ posNow --; } else { - //ERR posNow would be negative? + ESP_LOGE(TAG,"isr: posNow would be negative - ignoring decrement"); } } - - - // Generate pulse for stepper motor - //turn pin off (fast) - GPIO.out_w1tc = (1ULL << STEPPER_STEP_PIN); - return 1; } diff --git a/main/stepper.hpp b/main/stepper.hpp index 3474661..a189cbf 100644 --- a/main/stepper.hpp +++ b/main/stepper.hpp @@ -2,7 +2,10 @@ //init stepper pins and timer void stepper_init(); -void stepper_setTargetSteps(int steps); +//set absolute target position in steps +void stepper_setTargetPosSteps(uint64_t steps); +//set absolute target position in millimeters +void stepper_setTargetPosMm(uint32_t posMm); //task that periodically logs variables for debugging stepper driver void task_stepper_debug(void *pvParameter); From 34882815024fc460bea336a627bc1a79673a5377 Mon Sep 17 00:00:00 2001 From: jonny_ji7 Date: Tue, 25 Apr 2023 19:04:36 +0200 Subject: [PATCH 10/10] Disable debug output, Remove impl. without timer disable debug task remove software implementation of stepper control --- main/main.cpp | 3 +-- main/stepper.cpp | 54 +++--------------------------------------------- main/stepper.hpp | 6 ++---- 3 files changed, 6 insertions(+), 57 deletions(-) diff --git a/main/main.cpp b/main/main.cpp index 7c00440..b08f210 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -94,9 +94,8 @@ extern "C" void app_main() #ifdef STEPPER_TEST //create task for stepper testing - //xTaskCreate(task_stepperCtrlSw, "task stepper control software", configMINIMAL_STACK_SIZE * 3, NULL, configMAX_PRIORITIES - 1, NULL); xTaskCreate(task_stepper_test, "task_stepper_test", configMINIMAL_STACK_SIZE * 3, NULL, 2, NULL); - xTaskCreate(task_stepper_debug, "task_stepper_test", configMINIMAL_STACK_SIZE * 3, NULL, 2, NULL); + //xTaskCreate(task_stepper_debug, "task_stepper_test", configMINIMAL_STACK_SIZE * 3, NULL, 2, NULL); #else //create task for controlling the machine xTaskCreate(task_control, "task_control", configMINIMAL_STACK_SIZE * 3, NULL, 4, NULL); diff --git a/main/stepper.cpp b/main/stepper.cpp index 51232e2..b586324 100644 --- a/main/stepper.cpp +++ b/main/stepper.cpp @@ -54,57 +54,6 @@ static uint32_t accel_increment = STEPPER_ACCEL_INC; -//=============================== -//=== software - control task === -//=============================== -//stepper driver in software for testing (no timer/interrupt): -uint64_t stepsTarget = 0; -void task_stepperCtrlSw(void *pvParameter) -{ - uint64_t stepsNow = 0; - int increment = 1; - - while(1){ - int64_t delta = stepsTarget - stepsNow; - - //at position, nothing to do - if (delta == 0){ - ESP_LOGW(TAG, "delta =0 waiting for change"); - vTaskDelay(300 / portTICK_PERIOD_MS); - continue; - } - - //define direction - if (delta > 0){ - gpio_set_level(STEPPER_DIR_PIN, 0); - increment = 1; - } else { - gpio_set_level(STEPPER_DIR_PIN, 1); - increment = -1; - } - - //do one step - //note: triggers watchdog at long movements - stepsNow += increment; - gpio_set_level(STEPPER_STEP_PIN, 1); - ets_delay_us(30); - gpio_set_level(STEPPER_STEP_PIN, 0); - ets_delay_us(90); //speed - } -} - -//================================= -//=== software - set target pos === -//================================= -void stepperSw_setTargetSteps(uint64_t target){ - stepsTarget = target; - char buffer[20]; - snprintf(buffer, sizeof(buffer), "%" PRIu64, target); - ESP_LOGW(TAG, "set target steps to %s", buffer); -} - - - //====================== //===== DEBUG task ===== //====================== @@ -137,6 +86,7 @@ void task_stepper_debug(void *pvParameter){ } + //===================== //===== set speed ===== //===================== @@ -147,6 +97,7 @@ void stepper_setSpeed(uint32_t speedMmPerS) { } + //========================== //== set target pos STEPS == //========================== @@ -166,6 +117,7 @@ void stepper_setTargetPosSteps(uint64_t target_steps) { } + //========================= //=== set target pos MM === //========================= diff --git a/main/stepper.hpp b/main/stepper.hpp index a189cbf..3c1947a 100644 --- a/main/stepper.hpp +++ b/main/stepper.hpp @@ -6,10 +6,8 @@ void stepper_init(); void stepper_setTargetPosSteps(uint64_t steps); //set absolute target position in millimeters void stepper_setTargetPosMm(uint32_t posMm); +//set target speed in millimeters per second +void stepper_setSpeed(uint32_t speedMmPerS); //task that periodically logs variables for debugging stepper driver void task_stepper_debug(void *pvParameter); - -//control stepper without timer (software) -void task_stepperCtrlSw(void *pvParameter); -void stepperSw_setTargetSteps(uint64_t target);