diff --git a/lambda/airgate.c b/lambda/airgate.c index 7bb5a2d..ec1f9df 100644 --- a/lambda/airgate.c +++ b/lambda/airgate.c @@ -14,20 +14,27 @@ #include "usart.h" -#define MIN_SPEED 150 -#define MAX_SPEED 20 -// 1 = full step, 2 = half step, 3 = 1/4 step, 4 = 8 microsteps, ... -#define SCALE 3 - // TODO pins +/* Direction */ static int8_t dir = 0; +/* Current position */ static uint16_t pos = 0; +/* Target position */ +static int16_t target = 0; +/* Steps remaining */ static uint16_t steps = 0; +/* Steps done */ static uint16_t done = 0; +/* Acceleration profile ramp */ static uint16_t ramp = 0; +/* Speed */ static uint8_t speed = MIN_SPEED; +/** + * Wakes up the driver if sleeping, sets the direction and initial speed and + * starts the motor by starting the timer. + */ static void start(void) { if (bit_is_clear(PORTC, PC5)) { // wake up driver @@ -49,41 +56,66 @@ TCCR2B |= (1 << CS22) | (1 << CS21); } +/** + * Stops the motor by stopping the timer. + */ static void stop(void) { // stop timer2 TCCR2B = 0; // GTCCR |= (1 << PSRASY); } +/** + * Calculates the direction and steps to take to get to the target position, + * the ramp length for the acceleration profile, sets the speed and starts + * the motor. + */ +static void set(void) { + done = 0; + int16_t diff = (target << SCALE) - pos; + if (diff != 0) { + target = -1; + dir = MAX(-1, MIN(diff, 1)); + steps = abs(diff); + ramp = MIN(MIN_SPEED - MAX_SPEED, steps >> 1); + speed = MIN_SPEED; + start(); + } +} + void makeSteps(void) { if (steps > 0) { PORTB ^= (1 << PB7); pos += dir; steps--; done++; - if (done < ramp && speed > MAX_SPEED) --speed; - if (steps < ramp && speed < MIN_SPEED) ++speed; + // accelerate within ramp + if (done < ramp && speed > MAX_SPEED) speed--; + // decelerate within ramp + if (steps < ramp && speed < MIN_SPEED) speed++; OCR2A = speed; } else { - stop(); done = 0; - if (pos == 0) { + stop(); + if (target != -1) { + // move to target position + set(); + } else if (pos == 0) { // driver sleep mode + // TODO when is it best to do this? PORTC &= ~(1 << PC5); } } } void setAirgate(uint8_t const position) { - stop(); - done = 0; - int16_t diff = (((int16_t)position) << SCALE) - pos; - if (diff != 0) { - dir = MAX(-1, MIN(diff, 1)); - steps = abs(diff); - speed = MIN_SPEED; - ramp = MIN(MIN_SPEED - MAX_SPEED, steps >> 1); - start(); + target = position; + if (steps > 0) { + // motor busy - decelerate and move to target position when stopped + steps = MIN(ramp, steps); + } else { + // move to target position + set(); } } diff --git a/lambda/airgate.h b/lambda/airgate.h index 8757c34..6fbf49f 100644 --- a/lambda/airgate.h +++ b/lambda/airgate.h @@ -5,13 +5,35 @@ * Author: dode@luniks.net */ -#ifndef MOTOR_H_ -#define MOTOR_H_ +#ifndef AIRGATE_H_ +#define AIRGATE_H_ +#define MIN_SPEED 150 +#define MAX_SPEED 20 +/** + * 1 = full step, 2 = half step, 3 = 1/4 step, 4 = 8 microsteps, ... + * for 180°. + */ +#define SCALE 3 + +/** + * Called from the timer interrupt ISR and makes one step or stops the + * timer/motor if the target position is reached. + */ void makeSteps(void); -uint8_t getAirgate(void); - +/** + * Sets the airgate position 0 - 100%. The actual number of degrees the motor + * spins depends on the SCALE and the stepping mode. If the motor is currently + * moving to a target position when this function is called, it is first + * decelerated and then moves to the new target position. + */ void setAirgate(uint8_t const position); -#endif /* MOTOR_H_ */ +/** + * Returns the current airgate position, assuming the motor did all the steps + * it was requested to do. + */ +uint8_t getAirgate(void); + +#endif /* AIRGATE_H_ */