PulseGen: introduce CurPosition() for testing
CurPosition() returns the live axis position, which in this implementation is inherently expensive to compute. This shouldn't be required for the MMU, but it /will/ come in handy to check for the axis position/s in Motion tests.pull/47/head
parent
a5a91cbaa8
commit
16e7f62aee
|
|
@ -115,21 +115,35 @@ bool PulseGen::PlanMoveTo(pos_t target, steps_t feed_rate) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pos_t PulseGen::CurPosition() const {
|
||||||
|
pos_t cur_pos = position;
|
||||||
|
circular_index_t iter = block_index;
|
||||||
|
|
||||||
|
// if we have a live block remove the partial offset
|
||||||
|
if (current_block) {
|
||||||
|
cur_pos -= CurBlockShift();
|
||||||
|
iter.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// rollback remaining blocks
|
||||||
|
while (!iter.empty()) {
|
||||||
|
cur_pos -= BlockShift(&block_buffer[iter.front()]);
|
||||||
|
iter.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return cur_pos;
|
||||||
|
}
|
||||||
|
|
||||||
void PulseGen::AbortPlannedMoves() {
|
void PulseGen::AbortPlannedMoves() {
|
||||||
if (!current_block)
|
// always update to effective position
|
||||||
return;
|
position = CurPosition();
|
||||||
|
|
||||||
// update position
|
// destroy the current block
|
||||||
steps_t steps_missing = (current_block->steps - steps_completed);
|
if (current_block) {
|
||||||
if (current_block->direction)
|
|
||||||
position -= steps_missing;
|
|
||||||
else
|
|
||||||
position += steps_missing;
|
|
||||||
|
|
||||||
// destroy the block
|
|
||||||
current_block = nullptr;
|
current_block = nullptr;
|
||||||
block_index.pop();
|
block_index.pop();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace motor
|
} // namespace motor
|
||||||
} // namespace modules
|
} // namespace modules
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ using hal::tmc2130::TMC2130;
|
||||||
using math::mulU24X24toH16;
|
using math::mulU24X24toH16;
|
||||||
using speed_table::calc_timer;
|
using speed_table::calc_timer;
|
||||||
using speed_table::st_timer_t;
|
using speed_table::st_timer_t;
|
||||||
|
typedef CircularIndex<uint8_t, blockBufferSize> circular_index_t;
|
||||||
typedef uint32_t steps_t; ///< Absolute step units
|
typedef uint32_t steps_t; ///< Absolute step units
|
||||||
typedef uint32_t rate_t; ///< Type for step rates
|
typedef uint32_t rate_t; ///< Type for step rates
|
||||||
typedef int32_t pos_t; ///< Axis position (signed)
|
typedef int32_t pos_t; ///< Axis position (signed)
|
||||||
|
|
@ -35,13 +36,20 @@ public:
|
||||||
/// @returns true if the move has been enqueued
|
/// @returns true if the move has been enqueued
|
||||||
bool PlanMoveTo(pos_t pos, steps_t feedrate);
|
bool PlanMoveTo(pos_t pos, steps_t feedrate);
|
||||||
|
|
||||||
/// stop whatever moves are being done
|
/// Stop whatever moves are being done
|
||||||
void AbortPlannedMoves();
|
void AbortPlannedMoves();
|
||||||
|
|
||||||
/// @returns the current position of the axis
|
/// @returns the position of the axis at the end of all moves
|
||||||
pos_t Position() const { return position; }
|
pos_t Position() const { return position; }
|
||||||
|
|
||||||
|
/// Fetch the current position of the axis while stepping. This function is expensive!
|
||||||
|
/// It's necessary only in exceptional cases. For regular usage, Position() should
|
||||||
|
/// probably be used instead.
|
||||||
|
/// @returns the current position of the axis
|
||||||
|
pos_t CurPosition() const;
|
||||||
|
|
||||||
/// Set the position of the axis
|
/// Set the position of the axis
|
||||||
|
/// Should only be called when the queue is empty.
|
||||||
void SetPosition(pos_t x) { position = x; }
|
void SetPosition(pos_t x) { position = x; }
|
||||||
|
|
||||||
/// @returns true if all planned moves have been finished
|
/// @returns true if all planned moves have been finished
|
||||||
|
|
@ -150,7 +158,7 @@ private:
|
||||||
|
|
||||||
// Block buffer parameters
|
// Block buffer parameters
|
||||||
block_t block_buffer[blockBufferSize];
|
block_t block_buffer[blockBufferSize];
|
||||||
CircularIndex<uint8_t, blockBufferSize> block_index;
|
circular_index_t block_index;
|
||||||
block_t *current_block;
|
block_t *current_block;
|
||||||
|
|
||||||
// Axis data
|
// Axis data
|
||||||
|
|
@ -168,6 +176,17 @@ private:
|
||||||
|
|
||||||
/// Calculate the trapezoid parameters for the block
|
/// Calculate the trapezoid parameters for the block
|
||||||
void CalculateTrapezoid(block_t *block, steps_t entry_speed, steps_t exit_speed);
|
void CalculateTrapezoid(block_t *block, steps_t entry_speed, steps_t exit_speed);
|
||||||
|
|
||||||
|
/// Return the axis shift introduced by the current (live) block
|
||||||
|
inline pos_t CurBlockShift() const {
|
||||||
|
steps_t steps_missing = (current_block->steps - steps_completed);
|
||||||
|
return current_block->direction ? steps_missing : -steps_missing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the axis shift introduced by the specified full block
|
||||||
|
static inline pos_t BlockShift(const block_t *block) {
|
||||||
|
return block->direction ? block->steps : -block->steps;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pulse_gen
|
} // namespace pulse_gen
|
||||||
|
|
|
||||||
|
|
@ -112,10 +112,14 @@ TEST_CASE("pulse_gen::step_count", "[pulse_gen]") {
|
||||||
pg.PlanMoveTo(10, 10);
|
pg.PlanMoveTo(10, 10);
|
||||||
bool st = ReadPin(IDLER_STEP_PIN) == Level::high;
|
bool st = ReadPin(IDLER_STEP_PIN) == Level::high;
|
||||||
for (size_t i = 0; i != 10; ++i) {
|
for (size_t i = 0; i != 10; ++i) {
|
||||||
|
// check current axis position
|
||||||
|
REQUIRE((pos_t)i == pg.CurPosition());
|
||||||
|
|
||||||
|
// perform the step
|
||||||
REQUIRE(pg.Step(mp) > 0);
|
REQUIRE(pg.Step(mp) > 0);
|
||||||
bool newSt = ReadPin(IDLER_STEP_PIN) == Level::high;
|
bool newSt = ReadPin(IDLER_STEP_PIN) == Level::high;
|
||||||
|
|
||||||
// Assuming DEDGE each step should toggle the pin
|
// assuming DEDGE each step should toggle the pin
|
||||||
REQUIRE(newSt != st);
|
REQUIRE(newSt != st);
|
||||||
st = newSt;
|
st = newSt;
|
||||||
}
|
}
|
||||||
|
|
@ -129,6 +133,42 @@ TEST_CASE("pulse_gen::step_count", "[pulse_gen]") {
|
||||||
REQUIRE(pg.Position() == 10);
|
REQUIRE(pg.Position() == 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pulse_gen::queue_position", "[pulse_gen]") {
|
||||||
|
MotorParams mp = {
|
||||||
|
.idx = 0,
|
||||||
|
.dirOn = config::idler.dirOn,
|
||||||
|
.csPin = IDLER_CS_PIN,
|
||||||
|
.stepPin = IDLER_STEP_PIN,
|
||||||
|
.sgPin = IDLER_SG_PIN,
|
||||||
|
.uSteps = config::idler.uSteps
|
||||||
|
};
|
||||||
|
|
||||||
|
PulseGen pg(10, 100);
|
||||||
|
|
||||||
|
// enqueue two moves, observing Position and CurPosition.
|
||||||
|
REQUIRE(pg.Position() == 0);
|
||||||
|
REQUIRE(pg.CurPosition() == 0);
|
||||||
|
|
||||||
|
// while enqueuing Position should move but CurPosition should not
|
||||||
|
pg.PlanMoveTo(10, 10);
|
||||||
|
REQUIRE(pg.Position() == 10);
|
||||||
|
REQUIRE(pg.CurPosition() == 0);
|
||||||
|
|
||||||
|
pg.PlanMoveTo(15, 10);
|
||||||
|
REQUIRE(pg.Position() == 15);
|
||||||
|
REQUIRE(pg.CurPosition() == 0);
|
||||||
|
|
||||||
|
// step through the moves manually, cycling through two blocks
|
||||||
|
for (size_t i = 0; i != 15; ++i) {
|
||||||
|
REQUIRE((pos_t)i == pg.CurPosition());
|
||||||
|
REQUIRE(pg.Position() == 15);
|
||||||
|
pg.Step(mp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the final positions should match
|
||||||
|
REQUIRE(pg.CurPosition() == pg.Position());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("pulse_gen::queue_size", "[pulse_gen]") {
|
TEST_CASE("pulse_gen::queue_size", "[pulse_gen]") {
|
||||||
PulseGen pg(10, 100);
|
PulseGen pg(10, 100);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue