Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grab reset flags on startup #485

Draft
wants to merge 2 commits into
base: edge
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ void motor_hardware_seal_switch_set_disarmed();
*/
void motor_hardware_seal_switch_interrupt();

/**
* @brief Returns the reason saved upon startup for the last
* reset of the software.
* @return a string describing the reason for reset.
* */
uint16_t motor_hardware_reset_reason();

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,13 @@ class MotorPolicy {
*/
[[nodiscard]] auto seal_switches_are_shared() const -> bool;

/**
* @brief Returns the reason saved upon startup for the last
* reset of the software.
* @return a string describing the reason for reset.
* */
[[nodiscard]] auto last_reset_reason() const -> uint16_t;

/**
* @brief Call the seal callback function
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,44 @@ struct GetSystemInfo {
}
};

struct GetResetReason {
/*
* M114- GetResetReason retrieves the value of the RCC reset flag
* that was captured at the beginning of the hardware setup
* */
using ParseResult = std::optional<GetResetReason>;
static constexpr auto prefix = std::array{'M', '1', '1', '4'};

template <typename InputIt, typename InLimit>
requires std::forward_iterator<InputIt> &&
std::sized_sentinel_for<InputIt, InLimit>
static auto write_response_into(InputIt buf, InLimit limit, uint16_t reason)
-> InputIt {
int res = 0;
// print a hexadecimal representation of the reset flags
res = snprintf(&*buf, (limit - buf), "M114 Last Reset Reason: %X OK\n",
reason);
if (res <= 0) {
return buf;
}
return buf + res;
}
template <typename InputIt, typename Limit>
requires std::forward_iterator<InputIt> &&
std::sized_sentinel_for<Limit, InputIt>
static auto parse(const InputIt& input, Limit limit)
-> std::pair<ParseResult, InputIt> {
auto working = prefix_matches(input, limit, prefix);
if (working == input) {
return std::make_pair(ParseResult(), input);
}
if (working != limit && !std::isspace(*working)) {
return std::make_pair(ParseResult(), input);
}
return std::make_pair(ParseResult(GetResetReason()), working);
}
};

struct SetSerialNumber {
/*
** Set Serial Number uses a random gcode, M996, adjacent to the firmware
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class HostCommsTask {
gcode::GetOffsetConstants, gcode::OpenLid, gcode::CloseLid,
gcode::LiftPlate, gcode::DeactivateAll, gcode::GetBoardRevision,
gcode::GetLidSwitches, gcode::GetFrontButton, gcode::SetLidFans,
gcode::SetLightsDebug>;
gcode::SetLightsDebug, gcode::GetResetReason>;
using AckOnlyCache =
AckCache<8, gcode::EnterBootloader, gcode::SetSerialNumber,
gcode::ActuateSolenoid, gcode::ActuateLidStepperDebug,
Expand All @@ -72,6 +72,7 @@ class HostCommsTask {
using GetLidStatusCache = AckCache<8, gcode::GetLidStatus>;
using GetOffsetConstantsCache = AckCache<8, gcode::GetOffsetConstants>;
using SealStepperDebugCache = AckCache<8, gcode::ActuateSealStepperDebug>;
using GetResetReasonCache = AckCache<8, gcode::GetResetReason>;
// This is a two-stage message since both the Plate and Lid tasks have
// to respond.
using GetThermalPowerCache = AckCache<8, gcode::GetThermalPowerDebug,
Expand Down Expand Up @@ -115,6 +116,8 @@ class HostCommsTask {
// NOLINTNEXTLINE(readability-redundant-member-init)
deactivate_all_cache(),
// NOLINTNEXTLINE(readability-redundant-member-init)
get_reset_reason_cache(),
// NOLINTNEXTLINE(readability-redundant-member-init)
get_switch_cache() {}
HostCommsTask(const HostCommsTask& other) = delete;
auto operator=(const HostCommsTask& other) -> HostCommsTask& = delete;
Expand Down Expand Up @@ -460,6 +463,28 @@ class HostCommsTask {
cache_entry);
}

template <typename InputIt, typename InputLimit>
requires std::forward_iterator<InputIt> &&
std::sized_sentinel_for<InputLimit, InputIt>
auto visit_message(const messages::GetResetReasonResponse& response,
InputIt tx_into, InputLimit tx_limit) -> InputIt {
auto cache_entry =
get_reset_reason_cache.remove_if_present(response.responding_to_id);
return std::visit(
[tx_into, tx_limit, response](auto cache_element) {
using T = std::decay_t<decltype(cache_element)>;
if constexpr (std::is_same_v<std::monostate, T>) {
return errors::write_into(
tx_into, tx_limit,
errors::ErrorCode::BAD_MESSAGE_ACKNOWLEDGEMENT);
} else {
return cache_element.write_response_into(tx_into, tx_limit,
response.reason);
}
},
cache_entry);
}

template <typename InputIt, typename InputLimit>
requires std::forward_iterator<InputIt> &&
std::sized_sentinel_for<InputLimit, InputIt>
Expand Down Expand Up @@ -1232,6 +1257,28 @@ class HostCommsTask {
return std::make_pair(true, tx_into);
}

template <typename InputIt, typename InputLimit>
requires std::forward_iterator<InputIt> &&
std::sized_sentinel_for<InputLimit, InputIt>
auto visit_gcode(const gcode::GetResetReason& gcode, InputIt tx_into,
InputLimit tx_limit) -> std::pair<bool, InputIt> {
auto id = get_reset_reason_cache.add(gcode);
if (id == 0) {
return std::make_pair(
false, errors::write_into(tx_into, tx_limit,
errors::ErrorCode::GCODE_CACHE_FULL));
}
auto message = messages::GetResetReasonMessage{.id = id};
if (!task_registry->motor->get_message_queue().try_send(
message, TICKS_TO_WAIT_ON_SEND)) {
auto wrote_to = errors::write_into(
tx_into, tx_limit, errors::ErrorCode::INTERNAL_QUEUE_FULL);
get_reset_reason_cache.remove_if_present(id);
return std::make_pair(false, wrote_to);
}
return std::make_pair(true, tx_into);
}

template <typename InputIt, typename InputLimit>
requires std::forward_iterator<InputIt> &&
std::sized_sentinel_for<InputLimit, InputIt>
Expand Down Expand Up @@ -1524,6 +1571,7 @@ class HostCommsTask {
SealStepperDebugCache seal_stepper_debug_cache;
GetThermalPowerCache get_thermal_power_cache;
DeactivateAllCache deactivate_all_cache;
GetResetReasonCache get_reset_reason_cache;
GetSwitchCache get_switch_cache;
bool may_connect_latch = true;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,15 @@ struct GetLidStatusResponse {
motor_util::SealStepper::Status seal;
};

struct GetResetReasonMessage {
uint32_t id;
};

struct GetResetReasonResponse {
uint32_t responding_to_id;
uint16_t reason;
};

struct OpenLidMessage {
uint32_t id;
};
Expand Down Expand Up @@ -421,9 +430,9 @@ using HostCommsMessage = ::std::variant<
ForceUSBDisconnectMessage, GetSystemInfoResponse,
GetLidTemperatureDebugResponse, GetPlateTemperatureDebugResponse,
GetPlateTempResponse, GetLidTempResponse, GetSealDriveStatusResponse,
GetLidStatusResponse, GetPlatePowerResponse, GetLidPowerResponse,
GetOffsetConstantsResponse, SealStepperDebugResponse, DeactivateAllResponse,
GetLidSwitchesResponse, GetFrontButtonResponse>;
GetLidStatusResponse, GetResetReasonResponse, GetPlatePowerResponse,
GetLidPowerResponse, GetOffsetConstantsResponse, SealStepperDebugResponse,
DeactivateAllResponse, GetLidSwitchesResponse, GetFrontButtonResponse>;
using ThermalPlateMessage =
::std::variant<std::monostate, ThermalPlateTempReadComplete,
GetPlateTemperatureDebugMessage, SetPeltierDebugMessage,
Expand All @@ -441,6 +450,6 @@ using MotorMessage = ::std::variant<
std::monostate, ActuateSolenoidMessage, LidStepperDebugMessage,
LidStepperComplete, SealStepperDebugMessage, SealStepperComplete,
GetSealDriveStatusMessage, SetSealParameterMessage, GetLidStatusMessage,
OpenLidMessage, CloseLidMessage, PlateLiftMessage, FrontButtonPressMessage,
GetLidSwitchesMessage>;
GetResetReasonMessage, OpenLidMessage, CloseLidMessage, PlateLiftMessage,
FrontButtonPressMessage, GetLidSwitchesMessage>;
}; // namespace messages
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,17 @@ class MotorTask {
messages::HostCommsMessage(response)));
}

template <MotorExecutionPolicy Policy>
auto visit_message(const messages::GetResetReasonMessage& msg,
Policy& policy) -> void {
auto reason = policy.last_reset_reason();

auto response = messages::GetResetReasonResponse{
.responding_to_id = msg.id, .reason = reason};
static_cast<void>(_task_registry->comms->get_message_queue().try_send(
messages::HostCommsMessage(response)));
}

template <MotorExecutionPolicy Policy>
auto visit_message(const messages::OpenLidMessage& msg, Policy& policy)
-> void {
Expand Down
103 changes: 103 additions & 0 deletions stm32-modules/thermocycler-gen2/firmware/motor_task/motor_hardware.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,38 @@ static motor_hardware_t _motor_hardware = {
}
};

enum RCC_FLAGS {
NONE,
// high speed internal clock ready
HSIRDY, // = 1
// high speed external clock ready
HSERDY, // = 2
// main phase-locked loop clock ready
PLLRDY, // = 3
// hsi48 clock ready
HSI48RDY, // = 4
// low-speed external clock ready
LSERDY, // = 5
// lse clock security system failure
LSECSSD, // = 6
// low-speed internal clock ready
LSIRDY, // = 7
// brown out
BORRST, // = 8
// option byte-loader reset
OBLRST, // = 9
// pin reset
PINRST, // = 10
// software reset
SFTRST, // = 11
// independent watchdog
IWDGRST, // = 12
// window watchdog
WWDGRST, // = 13
// low power reset
LPWRRST, // = 14
};

// ----------------------------------------------------------------------------
// Local function declaration

Expand All @@ -193,11 +225,15 @@ static void init_dac1(DAC_HandleTypeDef* hdac);
static void init_tim2(TIM_HandleTypeDef* htim);
static void init_tim6(TIM_HandleTypeDef* htim);
static bool lid_active();
static void save_reset_reason();
//static char* reset_reason;
uint16_t reset_reason;

// ----------------------------------------------------------------------------
// Public function implementation

void motor_hardware_setup(const motor_hardware_callbacks* callbacks) {
save_reset_reason();
configASSERT(callbacks != NULL);
configASSERT(callbacks->lid_stepper_complete != NULL);
configASSERT(callbacks->seal_stepper_tick != NULL);
Expand Down Expand Up @@ -407,6 +443,10 @@ void motor_hardware_seal_switch_set_disarmed() {
_motor_hardware.seal.retraction_switch_armed = false;
}

uint16_t motor_hardware_reset_reason() {
return reset_reason;
}

// ----------------------------------------------------------------------------
// Local function implementation

Expand Down Expand Up @@ -623,6 +663,69 @@ static bool lid_active() {
!= HAL_TIM_CHANNEL_STATE_READY;
}

static void save_reset_reason() {
// check various reset flags to see if the HAL RCC
// reset flag matches any of them
reset_reason = 0;

// high speed internal clock ready
if (__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY)) {
reset_reason |= HSIRDY;
}
// high speed external clock ready
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY)) {
reset_reason |= HSERDY;
}
// main phase-locked loop clock ready
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY)) {
reset_reason |= PLLRDY;
}
// hsi48 clock ready
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_HSI48RDY)) {
reset_reason |= HSI48RDY;
}
// low-speed external clock ready
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY)) {
reset_reason |= LSERDY;
}
// lse clock security system failure
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSECSSD)) {
reset_reason |= LSECSSD;
}
// low-speed internal clock ready
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY)) {
reset_reason |= LSIRDY;
}
// brown out
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) {
reset_reason |= BORRST;
}
// option byte-loader reset
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_OBLRST)) {
reset_reason |= OBLRST;
}
// pin reset
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST)) {
reset_reason |= PINRST;
}
// software reset
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST)) {
reset_reason |= SFTRST;
}
// independent watchdog
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST)) {
reset_reason |= IWDGRST;
}
// window watchdog
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST)) {
reset_reason |= WWDGRST;
}
// low power reset
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST)) {
reset_reason |= LPWRRST;
}
}

// ----------------------------------------------------------------------------
// Overwritten HAL functions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,9 @@ auto MotorPolicy::seal_switch_set_disarmed() -> void {
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
[[nodiscard]] auto MotorPolicy::seal_switches_are_shared() const -> bool {
return _shared_seal_switch_lines;
}

// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
[[nodiscard]] auto MotorPolicy::last_reset_reason() const -> uint16_t {
return motor_hardware_reset_reason();
}
Loading