jonny 1dd8388769 Add task shutDownDetection, save last pos to nvs, optimize auto-home
shutdown.cpp/hpp:
    - repeatedly check supply voltage
    - save value to nvs when dropping below threshold
    - provide function to read lastPos from nvs

guide-stepper:
    - auto-home considers stored last position in nvs

main:
    - add new task shutDownDetection

config:
    - slightly increase stepper speed
2024-03-12 23:58:15 +01:00

305 lines
8.6 KiB
C++

//custom driver for stepper motor
#include "config.h"
#include "global.hpp"
#include "hal/timer_types.h"
#include <cstdint>
#include <inttypes.h>
extern "C" {
#include "driver/timer.h"
#include "driver/gpio.h"
#include "esp_log.h"
}
//=====================
//=== configuration ===
//=====================
//used macros from config.h:
//#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 (steps-per-rot / slope)
//#define STEPPER_SPEED_DEFAULT 20 //mm/s
//#define STEPPER_SPEED_MIN 4 //mm/s - speed threshold at which stepper immediately starts/stops
//#define STEPPER_ACCEL_INC 3 //steps/s increment per cycle
//#define STEPPER_DECEL_INC 8 //steps/s decrement 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-driver"; //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_LOGI(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_LOGI(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_LOGI(TAG, "starting timer");
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_LOGI(TAG, "set target position to %u mm", posMm);
stepper_setTargetPosSteps(posMm * STEPPER_STEPS_PER_MM);
}
//=======================
//===== waitForStop =====
//=======================
//delay until stepper is stopped, optional timeout in ms, 0 = no limit
void stepper_waitForStop(uint32_t timeoutMs){
ESP_LOGI(TAG, "waiting for stepper to stop...");
uint32_t timestampStart = esp_log_timestamp();
while (timerIsRunning) {
if ( (esp_log_timestamp() - timestampStart) >= timeoutMs && timeoutMs != 0){
ESP_LOGE(TAG, "timeout waiting for stepper to stop");
return;
}
vTaskDelay(100 / portTICK_PERIOD_MS);
}
ESP_LOGI(TAG, "finished waiting stepper to stop");
return;
}
//======================
//======== home ========
//======================
//define zero/start position
//run to limit and define zero/start position.
//Currently simply runs stepper for travelMm and bumps into hardware limit
void stepper_home(uint32_t travelMm){
//TODO add timeout, limitswitch...
ESP_LOGW(TAG, "initiate auto-home, moving %d mm...", travelMm);
posNow = travelMm * STEPPER_STEPS_PER_MM;
while (posNow != 0){
//reactivate just in case stopped by other call to prevent deadlock
if (!timerIsRunning) {
stepper_setTargetPosSteps(0);
}
vTaskDelay(100 / portTICK_PERIOD_MS);
}
ESP_LOGW(TAG, "finished auto-home");
return;
}
//========================
//===== init stepper =====
//========================
void stepper_init(){
ESP_LOGI(TAG, "init - configure struct...");
// Configure pulse and direction pins as outputs
ESP_LOGI(TAG, "init - configure gpio pins...");
gpio_set_direction(STEPPER_DIR_PIN, GPIO_MODE_OUTPUT);
gpio_set_direction(STEPPER_STEP_PIN, GPIO_MODE_OUTPUT);
ESP_LOGI(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
//prevent hard stop (faster stop than decel ramp)
//Idea: when target gets lowered while decelerating,
// move further than target to not exceed decel ramp (overshoot),
// then change dir and move back to actual target pos
if (stepsToGo < stepsDecelRemaining/2){ //significantly less steps planned to comply with decel ramp
stepsToGo = stepsDecelRemaining; //set to required steps
}
if (stepsToGo <= stepsDecelRemaining) {
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;
}