diff --git a/stm32-modules/flex-stacker/firmware/motor_control/freertos_motor_driver_task.cpp b/stm32-modules/flex-stacker/firmware/motor_control/freertos_motor_driver_task.cpp index cbd2ada8..97708862 100644 --- a/stm32-modules/flex-stacker/firmware/motor_control/freertos_motor_driver_task.cpp +++ b/stm32-modules/flex-stacker/firmware/motor_control/freertos_motor_driver_task.cpp @@ -1,8 +1,10 @@ #include "FreeRTOS.h" +#include "climits" #include "firmware/freertos_tasks.hpp" #include "firmware/motor_driver_policy.hpp" #include "firmware/motor_hardware.h" #include "flex-stacker/motor_driver_task.hpp" +#include "flex-stacker/tmc2160_interface.hpp" #include "ot_utils/freertos/freertos_timer.hpp" namespace motor_driver_task { @@ -20,6 +22,48 @@ static tasks::FirmwareTasks::MotorDriverQueue // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static auto _top_task = motor_driver_task::MotorDriverTask(_queue, nullptr); +static constexpr uint32_t STREAM_TASK_DEPTH = 200; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static StaticTask_t stream_task_buffer; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static std::array stream_task_stack; +static constexpr uint32_t FREQ_MS = 10; + +static void run_stallguard_task(void* arg) { + auto* interface = static_cast< + tmc2160::TMC2160Interface*>( + arg); + + uint32_t ulNotifiedValue = 0; + MotorID motor_id = MotorID::MOTOR_X; + + while (true) { + // wait for a new notification + xTaskNotifyWait(0, ULONG_MAX, &ulNotifiedValue, portMAX_DELAY); + if (ulNotifiedValue > 0) { + motor_id = (ulNotifiedValue == 1) ? MotorID::MOTOR_X + : (ulNotifiedValue == 2) ? MotorID::MOTOR_Z + : MotorID::MOTOR_L; + static_cast(interface->read_stallguard(motor_id)); + for (;;) { + if (xTaskNotifyWait(ULONG_MAX, ULONG_MAX, nullptr, 0) == + pdPASS) { + // Received a notification! This notification should only be + // used to break the for loop and the value would not be + // read. This, together with the task suspend issued by the + // top task, makes sure that we can't just switch from the + // current motor to another one immediately + break; + } + auto result = interface->read_stallguard(motor_id); + auto msg = messages::StallGuardResultMessage{.data = result}; + static_cast(_queue.try_send_from_isr(msg)); + vTaskDelay(pdMS_TO_TICKS(FREQ_MS)); + } + } + } +} + auto run(tasks::FirmwareTasks::QueueAggregator* aggregator) -> void { auto* handle = xTaskGetCurrentTaskHandle(); _queue.provide_handle(handle); @@ -29,8 +73,17 @@ auto run(tasks::FirmwareTasks::QueueAggregator* aggregator) -> void { spi_hardware_init(); auto policy = motor_driver_policy::MotorDriverPolicy(); + auto tmc2160_interface = tmc2160::TMC2160Interface(policy); + + auto* stream_handle = xTaskCreateStatic( + run_stallguard_task, "Stallguard Task", STREAM_TASK_DEPTH, + &tmc2160_interface, 1, stream_task_stack.data(), &stream_task_buffer); + vTaskSuspend(stream_handle); + + _top_task.provide_stallguard_handle(stream_handle); + while (true) { - _top_task.run_once(policy); + _top_task.run_once(tmc2160_interface); } } diff --git a/stm32-modules/flex-stacker/firmware/motor_control/motor_spi_hardware.c b/stm32-modules/flex-stacker/firmware/motor_control/motor_spi_hardware.c index a4cfe6a0..7a4b95c8 100644 --- a/stm32-modules/flex-stacker/firmware/motor_control/motor_spi_hardware.c +++ b/stm32-modules/flex-stacker/firmware/motor_control/motor_spi_hardware.c @@ -279,7 +279,7 @@ void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) bool motor_spi_sendreceive( MotorID motor_id, uint8_t *txData, uint8_t *rxData, uint16_t size ) { - const TickType_t max_block_time = pdMS_TO_TICKS(100); + const TickType_t max_block_time = pdMS_TO_TICKS(10); uint32_t notification_val = 0; if (!_spi.initialized || (_spi.task_to_notify != NULL) || (size > MOTOR_MAX_SPI_LEN)) { diff --git a/stm32-modules/include/flex-stacker/firmware/firmware_tasks.hpp b/stm32-modules/include/flex-stacker/firmware/firmware_tasks.hpp index 03cf498e..9f1baa38 100644 --- a/stm32-modules/include/flex-stacker/firmware/firmware_tasks.hpp +++ b/stm32-modules/include/flex-stacker/firmware/firmware_tasks.hpp @@ -21,7 +21,7 @@ constexpr uint8_t SYSTEM_TASK_PRIORITY = 1; constexpr size_t MOTOR_STACK_SIZE = 256; constexpr uint8_t MOTOR_TASK_PRIORITY = 1; -constexpr size_t MOTOR_DRIVER_STACK_SIZE = 256; +constexpr size_t MOTOR_DRIVER_STACK_SIZE = 512; constexpr uint8_t MOTOR_DRIVER_TASK_PRIORITY = 1; constexpr size_t UI_STACK_SIZE = 256; diff --git a/stm32-modules/include/flex-stacker/flex-stacker/gcodes_motor.hpp b/stm32-modules/include/flex-stacker/flex-stacker/gcodes_motor.hpp index 9f5ad84e..48bd90fa 100644 --- a/stm32-modules/include/flex-stacker/flex-stacker/gcodes_motor.hpp +++ b/stm32-modules/include/flex-stacker/flex-stacker/gcodes_motor.hpp @@ -49,6 +49,37 @@ struct ArgNoPrefix { ValueType value = ValueType{}; }; +struct StallGuardResult { + uint32_t data; + + using ParseResult = std::optional; + static constexpr auto prefix = std::array{'M', '9', '0', '0', ' '}; + + template + requires std::forward_iterator && + std::sized_sentinel_for + static auto parse(const InputIt& input, Limit limit) + -> std::pair { + auto working = prefix_matches(input, limit, prefix); + if (working == input) { + return std::make_pair(ParseResult(), input); + } + return std::make_pair(ParseResult(StallGuardResult()), working); + } + + template + requires std::forward_iterator && + std::sized_sentinel_for + static auto write_response_into(InputIt buf, InLimit limit, uint32_t data) + -> InputIt { + auto res = snprintf(&*buf, (limit - buf), "M900 %lu OK\n", data); + if (res <= 0) { + return buf; + } + return buf + res; + } +}; + struct GetTMCRegister { MotorID motor_id; uint8_t reg; @@ -409,6 +440,7 @@ struct MoveMotorInSteps { int32_t steps; uint32_t steps_per_second; uint32_t steps_per_second_sq; + bool stream_stallguard; using ParseResult = std::optional; static constexpr auto prefix = std::array{'G', '0', '.', 'S', ' '}; @@ -419,6 +451,7 @@ struct MoveMotorInSteps { using LArg = Arg; using FreqArg = Arg; using RampArg = Arg; + using StreamArg = ArgNoVal<'S'>; template requires std::forward_iterator && @@ -426,8 +459,8 @@ struct MoveMotorInSteps { static auto parse(const InputIt& input, Limit limit) -> std::pair { auto res = - gcode::SingleParser::parse_gcode(input, limit, prefix); + gcode::SingleParser::parse_gcode(input, limit, prefix); if (!res.first.has_value()) { return std::make_pair(ParseResult(), input); } @@ -436,6 +469,7 @@ struct MoveMotorInSteps { .steps = 0, .steps_per_second = 0, .steps_per_second_sq = 0, + .stream_stallguard = false, }; auto arguments = res.first.value(); @@ -460,6 +494,12 @@ struct MoveMotorInSteps { if (std::get<4>(arguments).present) { ret.steps_per_second_sq = std::get<4>(arguments).value; } + + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) + if (std::get<5>(arguments).present) { + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) + ret.stream_stallguard = true; + } return std::make_pair(ret, res.second); } @@ -474,6 +514,7 @@ struct MoveMotorInSteps { struct MoveMotorInMm { MotorID motor_id; float mm; + bool stream_stallguard; std::optional mm_per_second, mm_per_second_sq, mm_per_second_discont; using ParseResult = std::optional; @@ -486,6 +527,7 @@ struct MoveMotorInMm { using VelArg = Arg; using AccelArg = Arg; using DiscontArg = Arg; + using StreamArg = ArgNoVal<'S'>; template requires std::forward_iterator && @@ -493,14 +535,15 @@ struct MoveMotorInMm { static auto parse(const InputIt& input, Limit limit) -> std::pair { auto res = - gcode::SingleParser::parse_gcode(input, limit, prefix); + gcode::SingleParser::parse_gcode(input, limit, prefix); if (!res.first.has_value()) { return std::make_pair(ParseResult(), input); } auto ret = MoveMotorInMm{ .motor_id = MotorID::MOTOR_X, .mm = 0.0, + .stream_stallguard = false, .mm_per_second = std::nullopt, .mm_per_second_sq = std::nullopt, .mm_per_second_discont = std::nullopt, @@ -531,6 +574,11 @@ struct MoveMotorInMm { // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) ret.mm_per_second_discont = std::get<5>(arguments).value; } + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) + if (std::get<6>(arguments).present) { + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) + ret.stream_stallguard = true; + } return std::make_pair(ret, res.second); } @@ -545,6 +593,7 @@ struct MoveMotorInMm { struct MoveToLimitSwitch { MotorID motor_id; bool direction; + bool stream_stallguard; std::optional mm_per_second, mm_per_second_sq, mm_per_second_discont; using ParseResult = std::optional; @@ -557,6 +606,7 @@ struct MoveToLimitSwitch { using VelArg = Arg; using AccelArg = Arg; using DiscontArg = Arg; + using StreamArg = ArgNoVal<'S'>; template requires std::forward_iterator && @@ -564,14 +614,15 @@ struct MoveToLimitSwitch { static auto parse(const InputIt& input, Limit limit) -> std::pair { auto res = - gcode::SingleParser::parse_gcode(input, limit, prefix); + gcode::SingleParser::parse_gcode(input, limit, prefix); if (!res.first.has_value()) { return std::make_pair(ParseResult(), input); } auto ret = MoveToLimitSwitch{ .motor_id = MotorID::MOTOR_X, .direction = false, + .stream_stallguard = false, .mm_per_second = std::nullopt, .mm_per_second_sq = std::nullopt, .mm_per_second_discont = std::nullopt, @@ -604,6 +655,12 @@ struct MoveToLimitSwitch { // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) ret.mm_per_second_discont = std::get<5>(arguments).value; } + + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) + if (std::get<6>(arguments).present) { + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) + ret.stream_stallguard = true; + } return std::make_pair(ret, res.second); } @@ -619,6 +676,7 @@ struct MoveMotor { MotorID motor_id; bool direction; uint32_t frequency; + bool stream_stallguard; using ParseResult = std::optional; static constexpr auto prefix = std::array{'G', '5', ' '}; @@ -628,6 +686,7 @@ struct MoveMotor { using ZArg = Arg; using LArg = Arg; using FreqArg = Arg; + using StreamArg = ArgNoVal<'S'>; template requires std::forward_iterator && @@ -643,6 +702,7 @@ struct MoveMotor { .motor_id = MotorID::MOTOR_X, .direction = false, .frequency = 0, + .stream_stallguard = false, }; auto arguments = res.first.value(); @@ -661,6 +721,9 @@ struct MoveMotor { if (std::get<3>(arguments).present) { ret.frequency = std::get<3>(arguments).value; } + if (std::get<4>(arguments).present) { + ret.stream_stallguard = true; + } return std::make_pair(ret, res.second); } diff --git a/stm32-modules/include/flex-stacker/flex-stacker/host_comms_task.hpp b/stm32-modules/include/flex-stacker/flex-stacker/host_comms_task.hpp index 0209dabb..86256c92 100644 --- a/stm32-modules/include/flex-stacker/flex-stacker/host_comms_task.hpp +++ b/stm32-modules/include/flex-stacker/flex-stacker/host_comms_task.hpp @@ -246,6 +246,15 @@ class HostCommsTask { cache_entry); } + template + requires std::forward_iterator && + std::sized_sentinel_for + auto visit_message(const messages::StallGuardResultMessage& response, + InputIt tx_into, InputLimit tx_limit) -> InputIt { + return gcode::StallGuardResult::write_response_into(tx_into, tx_limit, + response.data); + } + template requires std::forward_iterator && std::sized_sentinel_for @@ -536,7 +545,8 @@ class HostCommsTask { .motor_id = gcode.motor_id, .steps = gcode.steps, .steps_per_second = gcode.steps_per_second, - .steps_per_second_sq = gcode.steps_per_second_sq}; + .steps_per_second_sq = gcode.steps_per_second_sq, + .stream_stallguard = gcode.stream_stallguard}; if (!task_registry->send(message, TICKS_TO_WAIT_ON_SEND)) { auto wrote_to = errors::write_into( tx_into, tx_limit, errors::ErrorCode::INTERNAL_QUEUE_FULL); @@ -561,6 +571,7 @@ class HostCommsTask { .id = id, .motor_id = gcode.motor_id, .mm = gcode.mm, + .stream_stallguard = gcode.stream_stallguard, .mm_per_second = gcode.mm_per_second, .mm_per_second_sq = gcode.mm_per_second_sq, .mm_per_second_discont = gcode.mm_per_second_discont}; @@ -588,6 +599,7 @@ class HostCommsTask { .id = id, .motor_id = gcode.motor_id, .direction = gcode.direction, + .stream_stallguard = gcode.stream_stallguard, .mm_per_second = gcode.mm_per_second, .mm_per_second_sq = gcode.mm_per_second_sq, .mm_per_second_discont = gcode.mm_per_second_discont}; @@ -611,10 +623,12 @@ class HostCommsTask { false, errors::write_into(tx_into, tx_limit, errors::ErrorCode::GCODE_CACHE_FULL)); } - auto message = messages::MoveMotorMessage{.id = id, - .motor_id = gcode.motor_id, - .direction = gcode.direction, - .frequency = gcode.frequency}; + auto message = messages::MoveMotorMessage{ + .id = id, + .motor_id = gcode.motor_id, + .direction = gcode.direction, + .frequency = gcode.frequency, + .stream_stallguard = gcode.stream_stallguard}; if (!task_registry->send(message, TICKS_TO_WAIT_ON_SEND)) { auto wrote_to = errors::write_into( tx_into, tx_limit, errors::ErrorCode::INTERNAL_QUEUE_FULL); diff --git a/stm32-modules/include/flex-stacker/flex-stacker/messages.hpp b/stm32-modules/include/flex-stacker/flex-stacker/messages.hpp index 7936d953..ce76ee06 100644 --- a/stm32-modules/include/flex-stacker/flex-stacker/messages.hpp +++ b/stm32-modules/include/flex-stacker/flex-stacker/messages.hpp @@ -115,16 +115,17 @@ struct GetTMCRegisterMessage { uint8_t reg; }; -struct PollTMCRegisterMessage { +struct PollStallGuardMessage { uint32_t id; MotorID motor_id; - uint8_t reg; }; -struct StopPollTMCRegisterMessage { - uint32_t id; +struct StallGuardResultMessage { + uint32_t data; }; +struct StopPollStallGuardMessage {}; + struct GetTMCRegisterResponse { uint32_t responding_to_id; MotorID motor_id; @@ -145,12 +146,14 @@ struct MoveMotorInStepsMessage { int32_t steps; uint32_t steps_per_second; uint32_t steps_per_second_sq; + bool stream_stallguard; }; struct MoveMotorInMmMessage { uint32_t id = 0; MotorID motor_id = MotorID::MOTOR_X; float mm = 0; + bool stream_stallguard = false; std::optional mm_per_second = std::nullopt; std::optional mm_per_second_sq = std::nullopt; std::optional mm_per_second_discont = std::nullopt; @@ -160,6 +163,7 @@ struct MoveToLimitSwitchMessage { uint32_t id = 0; MotorID motor_id = MotorID::MOTOR_X; bool direction = false; + bool stream_stallguard = false; std::optional mm_per_second = std::nullopt; std::optional mm_per_second_sq = std::nullopt; std::optional mm_per_second_discont = std::nullopt; @@ -188,6 +192,7 @@ struct MoveMotorMessage { MotorID motor_id; bool direction; uint32_t frequency; + bool stream_stallguard; }; struct StopMotorMessage { @@ -211,7 +216,7 @@ using HostCommsMessage = ::std::variant; + GetMoveParamsResponse, StallGuardResultMessage>; using SystemMessage = ::std::variant; + PollStallGuardMessage, StopPollStallGuardMessage, + SetMotorCurrentMessage, SetMicrostepsMessage, + StallGuardResultMessage>; using MotorMessage = ::std::variant tmc2160::TMC2160RegisterMap& { switch (motor_id) { case MotorID::MOTOR_X: @@ -141,12 +145,25 @@ class MotorDriverTask { } } + auto int_from_id(MotorID motor_id) -> uint8_t { + switch (motor_id) { + case MotorID::MOTOR_X: + return 1; + case MotorID::MOTOR_Z: + return 2; + case MotorID::MOTOR_L: + return 3; + default: + return 0; + } + } + template - auto run_once(Policy& policy) -> void { + auto run_once(tmc2160::TMC2160Interface& tmc2160_interface) + -> void { if (!_task_registry) { return; } - auto tmc2160_interface = tmc2160::TMC2160Interface(policy); if (!_initialized) { if (!_tmc2160.initialize_config(motor_x_config, tmc2160_interface, MotorID::MOTOR_X)) { @@ -224,19 +241,36 @@ class MotorDriverTask { } template - auto visit_message(const messages::PollTMCRegisterMessage& m, + auto visit_message(const messages::PollStallGuardMessage& m, tmc2160::TMC2160Interface& tmc2160_interface) -> void { - static_cast(m); static_cast(tmc2160_interface); + if (_stream_task_handle != nullptr) { + vTaskResume(_stream_task_handle); + xTaskNotify(_stream_task_handle, int_from_id(m.motor_id), + eSetValueWithOverwrite); + } } template - auto visit_message(const messages::StopPollTMCRegisterMessage& m, + auto visit_message(const messages::StopPollStallGuardMessage& m, tmc2160::TMC2160Interface& tmc2160_interface) -> void { static_cast(m); static_cast(tmc2160_interface); + if (_stream_task_handle != nullptr) { + xTaskNotify(_stream_task_handle, 0, eSetValueWithOverwrite); + vTaskSuspend(_stream_task_handle); + } + } + + template + auto visit_message(const messages::StallGuardResultMessage& m, + tmc2160::TMC2160Interface& tmc2160_interface) + -> void { + static_cast(tmc2160_interface); + static_cast( + _task_registry->send_to_address(m, Queues::HostCommsAddress)); } template @@ -297,5 +331,7 @@ class MotorDriverTask { tmc2160::TMC2160RegisterMap _x_config = motor_x_config; tmc2160::TMC2160RegisterMap _z_config = motor_z_config; tmc2160::TMC2160RegisterMap _l_config = motor_l_config; + + TaskHandle_t _stream_task_handle = nullptr; }; }; // namespace motor_driver_task diff --git a/stm32-modules/include/flex-stacker/flex-stacker/motor_task.hpp b/stm32-modules/include/flex-stacker/flex-stacker/motor_task.hpp index cfc7159d..09cbd9b3 100644 --- a/stm32-modules/include/flex-stacker/flex-stacker/motor_task.hpp +++ b/stm32-modules/include/flex-stacker/flex-stacker/motor_task.hpp @@ -202,6 +202,12 @@ class MotorTask { controller_from_id(m.motor_id) .start_fixed_movement(m.id, direction, std::abs(m.steps), 0, m.steps_per_second, m.steps_per_second_sq); + if (m.stream_stallguard) { + auto driver_message = messages::PollStallGuardMessage{ + .id = m.id, .motor_id = m.motor_id}; + static_cast(_task_registry->send_to_address( + driver_message, Queues::MotorDriverAddress)); + } } template @@ -227,6 +233,13 @@ class MotorTask { motor_state(m.motor_id).get_speed_discont(), motor_state(m.motor_id).get_speed(), motor_state(m.motor_id).get_accel()); + + if (m.stream_stallguard) { + auto driver_message = messages::PollStallGuardMessage{ + .id = m.id, .motor_id = m.motor_id}; + static_cast(_task_registry->send_to_address( + driver_message, Queues::MotorDriverAddress)); + } } template @@ -249,6 +262,12 @@ class MotorTask { motor_state(m.motor_id).get_speed_discont(), motor_state(m.motor_id).get_speed(), motor_state(m.motor_id).get_accel()); + if (m.stream_stallguard) { + auto driver_message = messages::PollStallGuardMessage{ + .id = m.id, .motor_id = m.motor_id}; + static_cast(_task_registry->send_to_address( + driver_message, Queues::MotorDriverAddress)); + } } template @@ -256,6 +275,10 @@ class MotorTask { -> void { static_cast(m); static_cast(policy); + // stops stallguard streaming task if it's running + auto driver_message = messages::StopPollStallGuardMessage{}; + static_cast(_task_registry->send_to_address( + driver_message, Queues::MotorDriverAddress)); } template @@ -289,7 +312,10 @@ class MotorTask { controller_from_id(m.motor_id).get_response_id()}; static_cast(_task_registry->send_to_address( response, Queues::HostCommsAddress)); - } + auto driver_message = messages::StopPollStallGuardMessage{}; + static_cast(_task_registry->send_to_address( + driver_message, Queues::MotorDriverAddress)); + }; template auto visit_message(const messages::SetMicrostepsMessage& m, Policy& policy) diff --git a/stm32-modules/include/flex-stacker/flex-stacker/tmc2160_interface.hpp b/stm32-modules/include/flex-stacker/flex-stacker/tmc2160_interface.hpp index 6a328e2c..d6b917bc 100644 --- a/stm32-modules/include/flex-stacker/flex-stacker/tmc2160_interface.hpp +++ b/stm32-modules/include/flex-stacker/flex-stacker/tmc2160_interface.hpp @@ -114,6 +114,22 @@ class TMC2160Interface { return RT(retval); } + auto read_stallguard(MotorID motor_id) -> uint32_t { + MessageT stallguard_msg = {static_cast(Registers::DRVSTATUS), + 0x00, 0x00, 0x00, 0x00}; + auto ret = _policy.tmc2160_transmit_receive(motor_id, stallguard_msg); + if (!ret.has_value()) { + return 0; + } + auto* iter = ret.value().begin(); + std::advance(iter, 1); + + RegisterSerializedType retval = 0; + iter = bit_utils::bytes_to_int(iter, ret.value().end(), retval); + DriveStatus drv_status{retval}; + return drv_status.sg_result; + } + private: Policy& _policy; };