Skip to content

Commit

Permalink
Use an ISR to handle bed stepper
Browse files Browse the repository at this point in the history
  • Loading branch information
Lyrkan committed Sep 16, 2023
1 parent c1a0bb6 commit f72d78b
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 139 deletions.
12 changes: 10 additions & 2 deletions include/K40/bed.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@

#include <math.h>

#define BED_STEP_TIMER_ID 0
#define BED_IDLE_UPDATE_INTERVAL 500
#define BED_RUNNING_UPDATE_INTERVAL 100

enum BedDirection {
BED_DIR_DOWN = -1,
BED_DIR_UNKNOWN = 0,
BED_DIR_UP = 1
};

enum BedStepperPin {
PIN_BED_STEP = 16,
Expand All @@ -29,13 +37,13 @@ enum BedCommandType {
typedef struct BedCommand BedCommand;
struct BedCommand {
BedCommandType type;
float_t value;
int32_t value_nm;
};

typedef struct BedPosition BedPosition;
struct BedPosition {
bool is_set;
float_t position;
int32_t position_nm;
};

typedef struct BedStatus BedStatus;
Expand Down
3 changes: 1 addition & 2 deletions include/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ enum SettingsType {

typedef struct BedSettings BedSettings;
struct BedSettings {
float_t screw_pitch;
uint32_t screw_pitch_um;
uint32_t microstep_multiplier;
uint32_t steps_per_revolution;
uint32_t acceleration;
uint32_t moving_speed;
uint32_t homing_speed;
BedPosition origin;
Expand Down
1 change: 0 additions & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ build_flags =
lib_deps =
lvgl/lvgl@^8.3.4
lovyan03/LovyanGFX@^1.1.2
waspinator/AccelStepper@^1.64
ayushsharma82/AsyncElegantOTA@^2.2.7
https://github.com/yubox-node-org/ESPAsyncWebServer.git#1dde9cf0219de662ed0882c0b6208057f70f105d
bblanchon/ArduinoJson@^6.21.0
Expand Down
219 changes: 124 additions & 95 deletions src/K40/bed.cpp
Original file line number Diff line number Diff line change
@@ -1,122 +1,175 @@
#include <AccelStepper.h>
#include <Arduino.h>
#include <math.h>

#include "K40/bed.h"
#include "queues.h"
#include "settings.h"

static AccelStepper stepper(AccelStepper::DRIVER, PIN_BED_STEP, PIN_BED_DIR);

static BedStatus bed_current_status = {
static hw_timer_t *bed_step_interrupt_timer = NULL;
static volatile uint32_t bed_step_interrupt_remaining_steps = 0;
static volatile BedDirection bed_current_direction = BED_DIR_UNKNOWN;
static volatile BedStatus bed_current_status = {
.state = BED_STATE_IDLE,
.target = {.is_set = false, .position = 0},
.current = {.is_set = false, .position = 0},
.origin = {.is_set = false, .position = 0}};
.target = {.is_set = false, .position_nm = 0},
.current = {.is_set = false, .position_nm = 0},
.origin = {.is_set = false, .position_nm = 0}};

void IRAM_ATTR bed_step_interrupt() {
if ((bed_current_direction == BED_DIR_UNKNOWN) || (bed_step_interrupt_remaining_steps == 0) ||
(digitalRead(PIN_BED_LIMIT) == LOW)) {
bed_step_interrupt_remaining_steps = 0;
return;
}

// Do a step
digitalWrite(PIN_BED_STEP, HIGH);
digitalWrite(PIN_BED_STEP, LOW);
bed_step_interrupt_remaining_steps--;

// Update current pos
bed_current_status.current.position_nm +=
(1000 * bed_current_direction * (int32_t)bed_settings.screw_pitch_um) /
(int32_t)(bed_settings.steps_per_revolution * bed_settings.microstep_multiplier);
}

static void bed_update_status_queue() {
BedStatus bed_current_status_copy = {
.state = bed_current_status.state,
.target = {.is_set = bed_current_status.target.is_set, .position_nm = bed_current_status.target.position_nm},
.current = {.is_set = bed_current_status.current.is_set, .position_nm = bed_current_status.current.position_nm},
.origin = {.is_set = bed_current_status.origin.is_set, .position_nm = bed_current_status.origin.position_nm}};

xQueueOverwrite(bed_current_status_queue, &bed_current_status_copy);
}

static void bed_run() {
// Run the stepper
stepper.run();

// Update position and idle if the distance to go is reached
bed_current_status.current.position =
((float_t)stepper.currentPosition() / (bed_settings.steps_per_revolution * bed_settings.microstep_multiplier)) *
bed_settings.screw_pitch;
if (stepper.distanceToGo() == 0) {
// Check if there is still distance to go
if ((bed_current_status.state != BED_STATE_IDLE) && (bed_step_interrupt_remaining_steps == 0)) {
// Disable the interrupt timer
timerAlarmDisable(bed_step_interrupt_timer);

// Check if we reached the limit switch
if (digitalRead(PIN_BED_LIMIT) == LOW) {
Serial.println("Bed: Limit switch triggered");
bed_current_status.current.is_set = true;
if (bed_current_status.origin.is_set) {
bed_current_status.current.position_nm = bed_current_status.origin.position_nm;
} else {
bed_current_status.current.position_nm = 0;
bed_current_status.origin.position_nm = 0;
bed_current_status.origin.is_set = true;
}
}

// Go to idling mode
bed_current_status.state = BED_STATE_IDLE;
bed_current_status.target.is_set = false;
bed_update_status_queue();
return;
}

xQueueOverwrite(bed_current_status_queue, &bed_current_status);
// Notify the UI every BED_RUNNING_UPDATE_INTERVAL milliseconds
unsigned long current_time = millis();
static unsigned long last_update_time = 0;
if ((current_time - last_update_time) >= BED_RUNNING_UPDATE_INTERVAL) {
last_update_time = current_time;
bed_update_status_queue();
}
}

static void bed_stop() {
// Perform an instant stop
stepper.setCurrentPosition(stepper.currentPosition());
static void bed_run_steps(uint32_t steps, BedDirection direction, uint32_t steps_per_second) {
// Update direction
bed_current_direction = direction;
digitalWrite(PIN_BED_DIR, direction > 0 ? HIGH : LOW);

// Set new state
bed_current_status.state = BED_STATE_IDLE;
bed_current_status.target.is_set = false;
// Enable the stepper timer interrupt
unsigned long interval_between_steps_micros = 1000000 / steps_per_second;
bed_step_interrupt_remaining_steps = steps;
timerAlarmWrite(bed_step_interrupt_timer, interval_between_steps_micros, true);
timerAlarmEnable(bed_step_interrupt_timer);
}

xQueueOverwrite(bed_current_status_queue, &bed_current_status);
static void bed_stop() {
// Do not stop the timer here, or state will not be updated.
// The interrupt will be called one last time before
// the state is set to idle by the bed_run function.
bed_step_interrupt_remaining_steps = 0;
}

static void bed_home() {
// Perform an instant stop
stepper.setCurrentPosition(stepper.currentPosition());
// Run the max possible amount of steps down
// hoping for the limit switch to be triggered.
bed_run_steps(UINT32_MAX, BED_DIR_DOWN, bed_settings.homing_speed);

// Set new state
bed_current_status.state = BED_STATE_HOMING;
bed_current_status.current.is_set = false;
bed_current_status.target.is_set = false;

// Move down using an arbitrary large number, the stepper
// should stop once it reaches the limit switch.
stepper.setMaxSpeed(bed_settings.homing_speed * bed_settings.microstep_multiplier);
stepper.move(-1000 * (bed_settings.steps_per_revolution * bed_settings.microstep_multiplier));
bed_update_status_queue();
}

static void bed_move_relative(float_t value) {
// Perform an instant stop
stepper.setCurrentPosition(stepper.currentPosition());
static void bed_move_relative(float_t value_nm) {
// Start the stepper with the amount of steps required
uint32_t steps_to_run =
abs(value_nm / ((1000 * bed_settings.screw_pitch_um) /
(bed_settings.steps_per_revolution * bed_settings.microstep_multiplier)));
bed_run_steps(steps_to_run, value_nm < 0 ? BED_DIR_DOWN : BED_DIR_UP, bed_settings.moving_speed);

// Set new state
bed_current_status.state = value < 0 ? BED_STATE_GOING_DOWN : BED_STATE_GOING_UP;
if (bed_current_status.current.is_set) {
bed_current_status.target.is_set = true;
bed_current_status.target.position = bed_current_status.current.position + value;
}

stepper.setMaxSpeed(bed_settings.moving_speed * bed_settings.microstep_multiplier);
stepper.move(
(value / bed_settings.screw_pitch) * (bed_settings.steps_per_revolution * bed_settings.microstep_multiplier));
bed_current_status.state = value_nm < 0 ? BED_STATE_GOING_DOWN : BED_STATE_GOING_UP;
bed_current_status.target.is_set = true;
bed_current_status.target.position_nm = bed_current_status.current.position_nm + value_nm;
bed_update_status_queue();
}

static void bed_move_absolute(float_t value) {
static void bed_move_absolute(float_t value_nm) {
// If the current position is not set the bed
// can't do absolute moves.
if (!bed_current_status.current.is_set) {
return;
}

// Perform a quick stop
stepper.setCurrentPosition(stepper.currentPosition());
// Start the stepper with the amount of steps required
uint32_t target_absolute_delta_nm = abs(value_nm - bed_current_status.current.position_nm);
uint32_t steps_to_run =
target_absolute_delta_nm / ((1000 * bed_settings.screw_pitch_um) /
(bed_settings.steps_per_revolution * bed_settings.microstep_multiplier));
bed_run_steps(steps_to_run, BED_DIR_DOWN, bed_settings.moving_speed);
bed_update_status_queue();

// Set new state
bed_current_status.state = value < bed_current_status.current.position ? BED_STATE_GOING_DOWN : BED_STATE_GOING_UP;
bed_current_status.state =
value_nm < bed_current_status.current.position_nm ? BED_STATE_GOING_DOWN : BED_STATE_GOING_UP;
bed_current_status.target.is_set = true;
bed_current_status.target.position = bed_current_status.target.position;

stepper.setMaxSpeed(bed_settings.moving_speed * bed_settings.microstep_multiplier);
stepper.moveTo(
(value / bed_settings.screw_pitch) * (bed_settings.steps_per_revolution * bed_settings.microstep_multiplier));
bed_current_status.target.position_nm = value_nm;
}

static void bed_set_current_position_as_origin() {
if (!bed_current_status.current.is_set) {
bed_current_status.current.is_set = true;
bed_current_status.current.position = 0;
}
bed_current_status.current.position_nm = 0;
bed_current_status.current.is_set = true;

bed_current_status.origin.is_set = true;
bed_current_status.origin.position += bed_current_status.current.position;
bed_current_status.current.position = 0;
stepper.setCurrentPosition(0);
bed_current_status.origin.position_nm += bed_current_status.current.position_nm;
bed_current_status.current.position_nm = 0;

xQueueOverwrite(bed_current_status_queue, &bed_current_status);
bed_update_status_queue();

// Save origin into configuration
bed_settings.origin = bed_current_status.origin;
bed_settings.origin = {
.is_set = bed_current_status.origin.is_set,
.position_nm = bed_current_status.origin.position_nm};
settings_schedule_save(SETTINGS_TYPE_BED);
}

void bed_init() {
stepper.setMaxSpeed(bed_settings.moving_speed * bed_settings.microstep_multiplier);
stepper.setAcceleration(bed_settings.acceleration * bed_settings.microstep_multiplier);

// Load origin from configuration
bed_current_status.origin = bed_settings.origin;

xQueueOverwrite(bed_current_status_queue, &bed_current_status);
bed_current_status.origin.is_set = bed_settings.origin.is_set;
bed_current_status.origin.position_nm = bed_settings.origin.position_nm;
bed_update_status_queue();

// Create the stepper interrupt timer
// Its speed will be set by bed_set_speed() and started by bed_run_steps()
bed_step_interrupt_timer = timerBegin(BED_STEP_TIMER_ID, APB_CLK_FREQ / 1000000, true);
timerAttachInterrupt(bed_step_interrupt_timer, &bed_step_interrupt, true);
}

BedState bed_update() {
Expand All @@ -134,11 +187,11 @@ BedState bed_update() {
break;
case BED_COMMAND_MOVE_ABSOLUTE:
Serial.println("Bed: Received MOVE_ABSOLUTE command");
bed_move_absolute(bed_command.value);
bed_move_absolute(bed_command.value_nm);
break;
case BED_COMMAND_MOVE_RELATIVE:
Serial.println("Bed: Received MOVE_RELATIVE command");
bed_move_relative(bed_command.value);
bed_move_relative(bed_command.value_nm);
break;
case BED_COMMAND_SET_CURRENT_POSITION_AS_ORIGIN:
Serial.println("Bed: Received SET_CURRENT_POSITION_AS_ORIGIN command");
Expand All @@ -147,33 +200,9 @@ BedState bed_update() {
}
}

// If bed is idling wait a bit before the next command check
// If bed isn't idling execute its current action
if (bed_current_status.state != BED_STATE_IDLE) {
// Check if the limit switch is triggered.
// If that's the case stop the stepper and start idling.
if (digitalRead(PIN_BED_LIMIT) == LOW) {
// If the bed was homing and the origin is known
// update the current position
Serial.println("Bed: Limit switch triggered");
if (bed_current_status.state == BED_STATE_HOMING) {
bed_current_status.current.is_set = true;
if (bed_current_status.origin.is_set) {
bed_current_status.current.position = bed_current_status.origin.position;
stepper.setCurrentPosition(
bed_current_status.origin.position * bed_settings.screw_pitch *
(bed_settings.steps_per_revolution * bed_settings.microstep_multiplier));
} else {
bed_current_status.current.position = 0;
bed_current_status.origin.position = bed_current_status.current.position;
bed_current_status.origin.is_set = true;
stepper.setCurrentPosition(0);
}
}

bed_stop();
} else {
bed_run();
}
bed_run();
}

return bed_current_status.state;
Expand Down
2 changes: 1 addition & 1 deletion src/K40/cooling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

static CoolingValues cooling_values;

static uint32_t cooling_flow_interrupt_counter = 0;
static volatile uint32_t cooling_flow_interrupt_counter = 0;
void IRAM_ATTR cooling_flow_probe_interrupt() { cooling_flow_interrupt_counter++; }

void cooling_update_status(esp_adc_cal_characteristics_t *adc_chars) {
Expand Down
10 changes: 5 additions & 5 deletions src/UI/screens/bed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ static void ui_bed_button_handler(lv_event_t *e) {
} else if (event_target == ui_bed_go_down_button || event_target == ui_bed_go_up_button) {
const BedCommand bed_command = {
.type = BED_COMMAND_MOVE_RELATIVE,
.value = (event_target == ui_bed_go_down_button ? -1 : 1) *
static_cast<float_t>(atof(lv_textarea_get_text(ui_bed_textarea))),
.value_nm = (event_target == ui_bed_go_down_button ? -1 : 1) * 1000000 *
static_cast<float_t>(atof(lv_textarea_get_text(ui_bed_textarea))),
};
xQueueOverwrite(bed_command_queue, &bed_command);
}
Expand Down Expand Up @@ -337,20 +337,20 @@ void ui_bed_update(bool initialize) {
formatted_current_position,
ARRAY_SIZE(formatted_current_position),
"%.2fmm",
current_bed_status.current.position);
current_bed_status.current.position_nm / 1000000);
lv_label_set_text(ui_bed_current_position_value, formatted_current_position);
} else {
lv_label_set_text(ui_bed_current_position_value, "-");
}

// Update target position
if (current_bed_status.target.is_set) {
if (current_bed_status.current.is_set && current_bed_status.target.is_set) {
char formatted_target_position[10];
snprintf(
formatted_target_position,
ARRAY_SIZE(formatted_target_position),
"%.2fmm",
current_bed_status.target.position);
current_bed_status.target.position_nm / 1000000);
lv_label_set_text(ui_bed_target_position_value, formatted_target_position);
} else {
lv_label_set_text(ui_bed_target_position_value, "-");
Expand Down

0 comments on commit f72d78b

Please sign in to comment.