diff --git a/function-diagram.drawio b/function-diagram.drawio index 496684e..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/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 8dbd96b..87d7ffa 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,11 +46,9 @@ extern "C" //---------------------- //----- variables ------ //---------------------- -static DendoStepper step; static const char *TAG = "stepper"; //tag for logging static bool stepp_direction = true; -static bool dir = true, dirPrev; //TODO local variables in travelSteps? static uint32_t posNow = 0; @@ -58,114 +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){ - - //--- wait if direction changed --- - if (dirPrev != dir){ - ESP_LOGI(TAG, " dir-change detected - waiting for move to finish \n "); - while(step.getState() != 1) vTaskDelay(1); //wait for move to finish - } - - //--- 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 - dirPrev = dir; - dir = 1; - //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 - dirPrev = dir; - dir = 1; - //-- dont wait for move to finish since moves in same direction get merged -- - //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); - dirPrev = dir; - dir = 0; - //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 - dirPrev = dir; - dir = 0; - //-- dont wait for move to finish since moves in same direction get merged -- - //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(); } @@ -174,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); } @@ -185,80 +167,121 @@ void updateSpeedFromAdc() { //---------------------------- void task_stepper_test(void *pvParameter) { - init_stepper(); - home(); + stepper_init(); + int state = 0; + 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"); - } + //cycle through test commands with one button + if (SW_RESET.risingEdge) { + switch (state){ + case 0: + stepper_setTargetPosMm(50); + //stepper_setTargetPosSteps(1000); + state++; + break; + case 1: + stepper_setTargetPosMm(80); + //stepper_setTargetPosSteps(100); + state++; + break; + case 2: + stepper_setTargetPosMm(20); + //stepper_setTargetPosSteps(100); + state++; + break; + case 3: + stepper_setTargetPosMm(60); + //stepper_setTargetPosSteps(2000); + state = 0; + break; + } + } + } + // 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); + // } } + //---------------------------- //----- 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..b08f210 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,17 +94,18 @@ 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_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, 5, NULL); + xTaskCreate(task_control, "task_control", configMINIMAL_STACK_SIZE * 3, NULL, 4, NULL); - //create task for controlling the machine - xTaskCreate(task_stepper_ctl, "task_stepper_ctl", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL); + //create task for controlling the stepper + xTaskCreate(task_stepper_ctl, "task_stepper_ctl", configMINIMAL_STACK_SIZE * 3, NULL, 2, 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..b586324 --- /dev/null +++ b/main/stepper.cpp @@ -0,0 +1,249 @@ +//custom driver for stepper motor +#include "config.hpp" +#include "hal/timer_types.h" +#include +#include + +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 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) + + + +//======================== +//=== global variables === +//======================== +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; + +//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 = 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; + + + +//====================== +//===== DEBUG task ===== +//====================== +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 " + "speedTarget=%u " + "debug=%d ", + + timerIsRunning, + direction, + directionTarget, + posTarget, + posNow, + stepsToGo, + speedNow, + speedTarget, + debug + ); + + vTaskDelay(300 / portTICK_PERIOD_MS); + } +} + + + +//===================== +//===== 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 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; + + // Check if the timer is currently paused + if (!timerIsRunning){ + // If the timer is paused, start it again with the updated targetSteps + 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)); + } +} + + + +//========================= +//=== 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); +} + + + +//======================== +//===== init stepper ===== +//======================== +void stepper_init(){ + ESP_LOGW(TAG, "init - configure struct..."); + + // Configure pulse and direction pins as outputs + ESP_LOGW(TAG, "init - configure gpio pins..."); + 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 = { + .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(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) { + + //----------------- + //--- variables --- + //----------------- + //TODO used (currently global) variables here + + //----------------------------------- + //--- define direction, stepsToGo --- + //----------------------------------- + //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)) { + if (stepsToGo == 0){ //standstill + direction = directionTarget; //switch direction + gpio_set_level(STEPPER_DIR_PIN, direction); + stepsToGo = abs(int64_t(posTarget - posNow)); + } else { + //set to minimun decel steps + stepsToGo = (speedNow - speedMin) / decel_increment; + } + } + //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, increment --- + //------------------------------- + //AT TARGET -> STOP + if (stepsToGo == 0) { + timer_pause(timerGroup, timerIdx); + timerIsRunning = false; + 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)); + + //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 { + //prevent underflow FIXME this case should not happen in the first place? + if (posNow != 0){ + posNow --; + } else { + ESP_LOGE(TAG,"isr: posNow would be negative - ignoring decrement"); + } + } + return 1; +} + + diff --git a/main/stepper.hpp b/main/stepper.hpp new file mode 100644 index 0000000..3c1947a --- /dev/null +++ b/main/stepper.hpp @@ -0,0 +1,13 @@ +#pragma once + +//init stepper pins and timer +void stepper_init(); +//set absolute target position in steps +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); 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