diff --git a/stm32-modules/flex-stacker/firmware/CMakeLists.txt b/stm32-modules/flex-stacker/firmware/CMakeLists.txt index c2e2d3bb2..0a8069588 100644 --- a/stm32-modules/flex-stacker/firmware/CMakeLists.txt +++ b/stm32-modules/flex-stacker/firmware/CMakeLists.txt @@ -22,7 +22,9 @@ set(${TARGET_MODULE_NAME}_FW_LINTABLE_SRCS ${SYSTEM_DIR}/freertos_idle_timer_task.cpp ${SYSTEM_DIR}/freertos_system_task.cpp ${SYSTEM_DIR}/system_policy.cpp + ${SYSTEM_DIR}/i2c_comms.cpp ${UI_DIR}/freertos_ui_task.cpp + ${UI_DIR}/ui_policy.cpp ${MOTOR_CONTROL_DIR}/freertos_motor_task.cpp ${MOTOR_CONTROL_DIR}/freertos_motor_driver_task.cpp ${MOTOR_CONTROL_DIR}/motor_driver_policy.cpp @@ -41,6 +43,7 @@ set(${TARGET_MODULE_NAME}_FW_NONLINTABLE_SRCS ${SYSTEM_DIR}/hal_tick.c ${SYSTEM_DIR}/system_serial_number.c ${SYSTEM_DIR}/system_hardware.c + ${SYSTEM_DIR}/i2c_hardware.c ${UI_DIR}/ui_hardware.c ${COMMS_DIR}/usbd_conf.c ${COMMS_DIR}/usbd_desc.c diff --git a/stm32-modules/flex-stacker/firmware/host_comms_task/usbd_desc.c b/stm32-modules/flex-stacker/firmware/host_comms_task/usbd_desc.c index 9bdd36b08..9ea4e0e58 100644 --- a/stm32-modules/flex-stacker/firmware/host_comms_task/usbd_desc.c +++ b/stm32-modules/flex-stacker/firmware/host_comms_task/usbd_desc.c @@ -233,18 +233,18 @@ uint8_t *USBD_Class_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *le */ static void Get_SerialNum(void) { - uint32_t deviceserial0, deviceserial1, deviceserial2; + uint32_t sg_0, sg_1; + uint32_t pointer = USB_STRING_SERIAL_BASE; + // Serial number is stored in 6 32-bit words + for (uint8_t idx = 0; idx < 4; idx++) { + sg_0 = *(uint32_t *)(pointer + 4); + sg_1 = *(uint32_t *)(pointer); - deviceserial0 = *(uint32_t *)(UID_BASE+4); - deviceserial1 = *(uint32_t *)(UID_BASE+8); - deviceserial2 = *(uint32_t *)(UID_BASE+12); + if (sg_0 == 0U) break; - deviceserial0 += deviceserial2; - - if (deviceserial0 != 0U) - { - IntToUnicode(deviceserial0, &USBD_StringSerial[2], 8U); - IntToUnicode(deviceserial1, &USBD_StringSerial[18], 4U); + IntToUnicode(sg_0, &USBD_StringSerial[2 + 16*idx], 4U); + IntToUnicode(sg_1, &USBD_StringSerial[10 + 16*idx], 4U); + pointer += 8; } } @@ -259,20 +259,15 @@ static void IntToUnicode(uint32_t value, uint8_t *pbuf, uint8_t len) { uint8_t idx = 0U; - for (idx = 0U ; idx < len ; idx ++) + for (idx = 0U; idx < len; idx++) { - if (((value >> 28)) < 0xAU) - { - pbuf[ 2U * idx] = (value >> 28) + '0'; - } - else - { - pbuf[2U * idx] = (value >> 28) + 'A' - 10U; - } - - value = value << 4; - + // Extract the most significant byte + pbuf[2U * idx] = (value >> 24) & 0xFF; + // Add a null byte for Unicode (UTF-16) pbuf[2U * idx + 1] = 0U; + + // Shift the value to process the next byte + value = value << 8; } } diff --git a/stm32-modules/flex-stacker/firmware/host_comms_task/usbd_desc.h b/stm32-modules/flex-stacker/firmware/host_comms_task/usbd_desc.h index bdedd43bc..0f160086a 100644 --- a/stm32-modules/flex-stacker/firmware/host_comms_task/usbd_desc.h +++ b/stm32-modules/flex-stacker/firmware/host_comms_task/usbd_desc.h @@ -26,8 +26,8 @@ /* Exported types ------------------------------------------------------------*/ /* Exported constants --------------------------------------------------------*/ - -#define USB_SIZ_STRING_SERIAL 0x1A +#define USB_SIZ_STRING_SERIAL 0x32 +#define USB_STRING_SERIAL_BASE 0x0807F800 /* Exported macro ------------------------------------------------------------*/ /* Exported functions ------------------------------------------------------- */ diff --git a/stm32-modules/flex-stacker/firmware/main.cpp b/stm32-modules/flex-stacker/firmware/main.cpp index 89ed70849..49221ac2e 100644 --- a/stm32-modules/flex-stacker/firmware/main.cpp +++ b/stm32-modules/flex-stacker/firmware/main.cpp @@ -1,15 +1,14 @@ #include "FreeRTOS.h" -#include "task.h" - -#include "ot_utils/freertos/freertos_task.hpp" -#include "firmware/freertos_tasks.hpp" #include "firmware/firmware_tasks.hpp" +#include "firmware/freertos_tasks.hpp" #include "firmware/i2c_comms.hpp" +#include "firmware/i2c_hardware.h" #include "firmware/motor_hardware.h" -#include "firmware/tof_sensor_hardware.h" #include "firmware/system_stm32g4xx.h" #include "flex-stacker/messages.hpp" +#include "ot_utils/freertos/freertos_task.hpp" #include "systemwide.h" +#include "task.h" #pragma GCC diagnostic push // NOLINTNEXTLINE(clang-diagnostic-unknown-warning-option) @@ -17,53 +16,51 @@ #include "stm32g4xx_hal.h" #pragma GCC diagnostic pop -// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) - -// Task EntryPoint using EntryPoint = std::function; -using EntryPointTOFDriver = std::function; +using EntryPointUI = std::function; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static auto motor_driver_task_entry = EntryPoint(motor_driver_task::run); +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static auto motor_task_entry = EntryPoint(motor_control_task::run); -static auto ui_task_entry = EntryPoint(ui_control_task::run); +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static auto ui_task_entry = EntryPointUI(ui_control_task::run); +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static auto host_comms_entry = EntryPoint(host_comms_control_task::run); +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static auto system_task_entry = EntryPoint(system_control_task::run); -static auto tof_driver_task_entry = EntryPointTOFDriver(tof_driver_task::run); -static auto tof_sensor_task_entry = EntryPoint(tof_sensor_task::run); -// Tasks +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static auto driver_task = ot_utils::freertos_task::FreeRTOSTask(motor_driver_task_entry); - +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static auto motor_task = ot_utils::freertos_task::FreeRTOSTask( motor_task_entry); - +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static auto host_comms_task = ot_utils::freertos_task::FreeRTOSTask( host_comms_entry); - +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static auto ui_task = - ot_utils::freertos_task::FreeRTOSTask( + ot_utils::freertos_task::FreeRTOSTask( ui_task_entry); - +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static auto system_task = ot_utils::freertos_task::FreeRTOSTask( system_task_entry); -static auto tof_d_task = ot_utils::freertos_task::FreeRTOSTask< - tasks::TOF_DRIVER_STACK_SIZE, EntryPointTOFDriver>(tof_driver_task_entry); - -static auto tof_s_task = - ot_utils::freertos_task::FreeRTOSTask(tof_sensor_task_entry); - -// Agregator +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static auto aggregator = tasks::FirmwareTasks::QueueAggregator(); -// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static auto i2c2_comms = i2c::hardware::I2C(); +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static auto i2c3_comms = i2c::hardware::I2C(); +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static auto i2c_handles = I2CHandlerStruct{}; extern "C" void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { switch (GPIO_Pin) { @@ -77,27 +74,20 @@ extern "C" void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { } } -static auto i2c2_comms = i2c::hardware::I2C(); -static auto i2c3_comms = i2c::hardware::I2C(); -static auto i2c_handles = I2CHandlerStruct{}; - auto main() -> int { HardwareInit(); i2c_hardware_init(&i2c_handles); - i2c2_comms.set_handle(i2c_handles.i2c2); - i2c3_comms.set_handle(i2c_handles.i2c3); + + i2c2_comms.set_handle(i2c_handles.i2c2, I2C_BUS_2); + i2c3_comms.set_handle(i2c_handles.i2c3, I2C_BUS_3); system_task.start(tasks::SYSTEM_TASK_PRIORITY, "System", &aggregator); driver_task.start(tasks::MOTOR_DRIVER_TASK_PRIORITY, "Motor Driver", &aggregator); motor_task.start(tasks::MOTOR_TASK_PRIORITY, "Motor", &aggregator); - tof_d_task.start(tasks::TOF_DRIVER_TASK_PRIORITY, "TOF Driver", &aggregator, - &i2c3_comms); - tof_s_task.start(tasks::TOF_SENSOR_TASK_PRIORITY, "TOF Sensor", - &aggregator); host_comms_task.start(tasks::COMMS_TASK_PRIORITY, "Comms", &aggregator); - ui_task.start(tasks::UI_TASK_PRIORITY, "UI", &aggregator); + ui_task.start(tasks::UI_TASK_PRIORITY, "UI", &aggregator, &i2c2_comms); vTaskStartScheduler(); return 0; diff --git a/stm32-modules/flex-stacker/firmware/system/i2c_comms.cpp b/stm32-modules/flex-stacker/firmware/system/i2c_comms.cpp new file mode 100644 index 000000000..00791e875 --- /dev/null +++ b/stm32-modules/flex-stacker/firmware/system/i2c_comms.cpp @@ -0,0 +1,27 @@ +#include "firmware/i2c_comms.hpp" + +#include + +#include "firmware/i2c_hardware.h" +#include "systemwide.h" + +using namespace i2c::hardware; + +auto I2C::set_handle(HAL_I2C_HANDLE i2c_handle, I2C_BUS i2c_bus) -> void { + this->bus = i2c_bus; + i2c_register_handle(i2c_handle, i2c_bus); +} + +auto I2C::i2c_write(uint16_t dev_addr, uint16_t reg, uint8_t* data, + uint16_t size) -> RxTxReturn { + MessageT resp{0}; + auto ret = hal_i2c_write(bus, dev_addr, reg, data, size); + return RxTxReturn(ret, resp); +} + +auto I2C::i2c_read(uint16_t dev_addr, uint16_t reg, uint16_t size) + -> RxTxReturn { + MessageT resp{0}; + auto ret = hal_i2c_read(bus, dev_addr, reg, resp.data(), size); + return RxTxReturn(ret, resp); +} diff --git a/stm32-modules/flex-stacker/firmware/system/i2c_hardware.c b/stm32-modules/flex-stacker/firmware/system/i2c_hardware.c new file mode 100644 index 000000000..82f2751f0 --- /dev/null +++ b/stm32-modules/flex-stacker/firmware/system/i2c_hardware.c @@ -0,0 +1,275 @@ +#include + +#include "stm32g4xx_hal.h" +#include "stm32g4xx_hal_conf.h" +#include "stm32g4xx_hal_gpio.h" +#include "stm32g4xx_hal_def.h" +#include "stm32g4xx_it.h" +#include "FreeRTOS.h" +#include "task.h" + +#include "firmware/i2c_hardware.h" + +#define MAX_TIMEOUT (pdMS_TO_TICKS(100)) +#define MAX_RETRIES (3) +#define MAX_I2C_HANDLES (3) +#define NO_HANDLE_ERROR (255) +#define REGISTER_ADDR_LEN (1) // 1 byte + +static I2C_HandleTypeDef hi2c2; +static I2C_HandleTypeDef hi2c3; + +typedef struct { + I2C_BUS bus; + I2C_HandleTypeDef *i2c_handle; + TaskHandle_t task_to_notify; + bool should_retry; +} NotificationHandle_t; + +static NotificationHandle_t _notification_handles[MAX_I2C_HANDLES]; + +static bool _initialized = false; + +HAL_I2C_HANDLE MX_I2C2_Init() +{ + hi2c2.State = HAL_I2C_STATE_RESET; + hi2c2.Instance = I2C2; + hi2c2.Init.Timing = 0x10C0ECFF; + hi2c2.Init.OwnAddress1 = 0; + hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; + hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; + hi2c2.Init.OwnAddress2 = 0; + hi2c2.Init.OwnAddress2Masks = I2C_OA2_NOMASK; + hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; + hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; + if (HAL_I2C_Init(&hi2c2) != HAL_OK) + { + Error_Handler(); + } + /** Configure Analogue filter + */ + if (HAL_I2CEx_ConfigAnalogFilter(&hi2c2, I2C_ANALOGFILTER_ENABLE) != HAL_OK) + { + Error_Handler(); + } + /** Configure Digital filter + */ + if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0) != HAL_OK) + { + Error_Handler(); + } + + /** I2C Fast mode Plus enable */ + __HAL_SYSCFG_FASTMODEPLUS_ENABLE(I2C_FASTMODEPLUS_I2C2); + + return &hi2c2; +} + +HAL_I2C_HANDLE MX_I2C3_Init() { + hi2c3.Instance = I2C3; + hi2c3.Init.Timing = 0x10C0ECFF; + hi2c3.Init.OwnAddress1 = 0; + hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; + hi2c3.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; + hi2c3.Init.OwnAddress2 = 0; + hi2c3.Init.OwnAddress2Masks = I2C_OA2_NOMASK; + hi2c3.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; + hi2c3.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; + if (HAL_I2C_Init(&hi2c3) != HAL_OK) { + Error_Handler(); + } + /** Configure Analogue filter + */ + if (HAL_I2CEx_ConfigAnalogFilter(&hi2c3, I2C_ANALOGFILTER_ENABLE) != + HAL_OK) { + Error_Handler(); + } + /** Configure Digital filter + */ + if (HAL_I2CEx_ConfigDigitalFilter(&hi2c3, 0) != HAL_OK) { + Error_Handler(); + } + + /** I2C Fast mode Plus enable */ + __HAL_SYSCFG_FASTMODEPLUS_ENABLE(I2C_FASTMODEPLUS_I2C3); + + return &hi2c3; +} + +void i2c_hardware_init(I2CHandlerStruct* i2c_handles) { + HAL_I2C_HANDLE i2c2 = MX_I2C2_Init(); + HAL_I2C_HANDLE i2c3 = MX_I2C3_Init(); + i2c_handles->i2c2 = i2c2; + i2c_handles->i2c3 = i2c3; +} + +/** + * @brief Get the notification handle based on the I2C Bus. + * Returns NULL if the handle is not found. + */ +static NotificationHandle_t* lookup_handle(I2C_BUS bus) { + for(size_t i = 0; i < MAX_I2C_HANDLES; ++i) { + I2C_BUS notif_bus = _notification_handles[i].bus; + if(notif_bus != NO_BUS && notif_bus == bus) { + return &_notification_handles[i]; + } + } + return NULL; +} + +/** + * @brief Get the notification handle based on the HAL I2C struct. + * Returns NULL if the handle is not found. + */ +static NotificationHandle_t* get_handle(I2C_HandleTypeDef *i2c_handle) { + for(size_t i = 0; i < MAX_I2C_HANDLES; ++i) { + if(_notification_handles[i].i2c_handle == i2c_handle) { + return &_notification_handles[i]; + } + } + return NULL; +} + +static void initialize_notification_handles() { + if(!_initialized) { + for(size_t i = 0; i < MAX_I2C_HANDLES; ++i) { + _notification_handles[i].i2c_handle = NULL; + _notification_handles[i].task_to_notify = NULL; + _notification_handles[i].should_retry = false; + _notification_handles[i].bus = NO_BUS; + } + _initialized = true; + } +} + +/** + * @brief Common handler for all I2C callbacks. + */ +static void handle_i2c_callback(I2C_HandleTypeDef *i2c_handle, bool error) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + NotificationHandle_t *instance = get_handle(i2c_handle); + if(instance == NULL) return; + if(instance->task_to_notify == NULL) return; + + vTaskNotifyGiveFromISR(instance->task_to_notify, + &xHigherPriorityTaskWoken); + instance->task_to_notify = NULL; + instance->should_retry = error; + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); +} + +bool i2c_register_handle(HAL_I2C_HANDLE handle, I2C_BUS bus) { + initialize_notification_handles(); + + I2C_HandleTypeDef* i2c_handle = (I2C_HandleTypeDef*)handle; + NotificationHandle_t *notif_handle = lookup_handle(bus); + if(notif_handle != NULL) return true; // Already Registered + + // Now find an empty slot + notif_handle = get_handle(NULL); + if(notif_handle == NULL) return false; // No empty slots + + notif_handle->i2c_handle = i2c_handle; + notif_handle->bus = bus; + return true; +} + +/** + * Wrapper around HAL_I2C_Mem_Write + */ +uint8_t hal_i2c_write(I2C_BUS bus, uint16_t DevAddress, uint8_t reg, uint8_t *data, uint16_t size) { + uint32_t notification_val = 0; + NotificationHandle_t *notification_handle = lookup_handle(bus); + I2C_HandleTypeDef* i2c_handle = (I2C_HandleTypeDef*)notification_handle->i2c_handle; + + // Bus was not registered + if(notification_handle == NULL) return false; + + uint8_t retries = 0; + HAL_StatusTypeDef tx_result = HAL_OK; + do { + // Setup task handle and send message + notification_handle->task_to_notify = xTaskGetCurrentTaskHandle(); + tx_result = HAL_I2C_Mem_Write_IT(i2c_handle, + DevAddress, reg, REGISTER_ADDR_LEN, data, size); + + // Wait for callback and check result + notification_val = ulTaskNotifyTake(pdTRUE, MAX_TIMEOUT); + if (tx_result == HAL_OK) break; + if (notification_handle->should_retry) tx_result = HAL_BUSY; + if (notification_val != 1) tx_result = HAL_TIMEOUT; + + retries += 1; + } while(retries < MAX_RETRIES); + return tx_result; +} + +/** + * Wrapper around HAL_I2C_Mem_Read + */ +uint8_t hal_i2c_read(I2C_BUS bus, uint16_t DevAddress, uint16_t reg, uint8_t *data, uint16_t size) { + uint32_t notification_val = 0; + NotificationHandle_t *notification_handle = lookup_handle(bus); + I2C_HandleTypeDef* i2c_handle = (I2C_HandleTypeDef*)notification_handle->i2c_handle; + + // Bus was not registered + if(notification_handle == NULL) return NO_HANDLE_ERROR; + + uint8_t retries = 0; + HAL_StatusTypeDef rx_result = HAL_OK; + do { + // Setup task handle and send message + notification_handle->task_to_notify = xTaskGetCurrentTaskHandle(); + rx_result = HAL_I2C_Mem_Read_IT(i2c_handle, + DevAddress, reg, REGISTER_ADDR_LEN, data, size); + + // Wait for callback and check result. + notification_val = ulTaskNotifyTake(pdTRUE, MAX_TIMEOUT); + if (rx_result == HAL_OK) break; + if (notification_handle->should_retry) rx_result = HAL_BUSY; + if (notification_val != 1) rx_result = HAL_TIMEOUT; + + retries += 1; + } while (retries < MAX_RETRIES); + return rx_result; +} + +void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *i2c_handle) { + handle_i2c_callback(i2c_handle, false); +} + +void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *i2c_handle) { + handle_i2c_callback(i2c_handle, false); +} + +void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *i2c_handle) { + handle_i2c_callback(i2c_handle, false); +} + +void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *i2c_handle) { + handle_i2c_callback(i2c_handle, false); +} + +void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *i2c_handle) { + handle_i2c_callback(i2c_handle, true); +} + +void I2C2_EV_IRQHandler(void) +{ + HAL_I2C_EV_IRQHandler(&hi2c2); +} + +void I2C2_ER_IRQHandler(void) +{ + HAL_I2C_ER_IRQHandler(&hi2c2); +} + +void I2C3_EV_IRQHandler(void) +{ + HAL_I2C_EV_IRQHandler(&hi2c3); +} + +void I2C3_ER_IRQHandler(void) +{ + HAL_I2C_ER_IRQHandler(&hi2c3); +} diff --git a/stm32-modules/flex-stacker/firmware/system/stm32g4xx_hal_msp.c b/stm32-modules/flex-stacker/firmware/system/stm32g4xx_hal_msp.c index 1333aa156..5f3a45390 100644 --- a/stm32-modules/flex-stacker/firmware/system/stm32g4xx_hal_msp.c +++ b/stm32-modules/flex-stacker/firmware/system/stm32g4xx_hal_msp.c @@ -83,7 +83,7 @@ void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c) GPIO_InitStruct.Pin = EEPROM_I2C2_SDA_Pin|EEPOM_I2C2_SCL_Pin; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); @@ -105,7 +105,7 @@ void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c) GPIO_InitStruct.Pin = TOF_I2C3_SCL_Pin|TOF_I2C3_SDA_Pin; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF8_I2C3; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); diff --git a/stm32-modules/flex-stacker/firmware/system/system_hardware.c b/stm32-modules/flex-stacker/firmware/system/system_hardware.c index 233642fc1..0405f8a64 100644 --- a/stm32-modules/flex-stacker/firmware/system/system_hardware.c +++ b/stm32-modules/flex-stacker/firmware/system/system_hardware.c @@ -13,6 +13,106 @@ // address 4 in the bootable region is the address of the first instruction that // should run, aka the data that should be loaded into $pc. const uint32_t *const sysmem_boot_loc = (uint32_t*)SYSMEM_BOOT; +uint16_t reset_reason; + +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 +}; + +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_HSIRDY)) { + 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_LSERDY)) { + 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; + } +} + +uint16_t system_hardware_reset_reason() { + return reset_reason; +} /** PUBLIC FUNCTION IMPLEMENTATION */ @@ -60,6 +160,7 @@ void system_hardware_enter_bootloader(void) { } void system_hardware_gpio_init(void) { + save_reset_reason(); GPIO_InitTypeDef init = {0}; /* GPIO Ports Clock Enable */ diff --git a/stm32-modules/flex-stacker/firmware/system/system_policy.cpp b/stm32-modules/flex-stacker/firmware/system/system_policy.cpp index e6382f49a..33062a93e 100644 --- a/stm32-modules/flex-stacker/firmware/system/system_policy.cpp +++ b/stm32-modules/flex-stacker/firmware/system/system_policy.cpp @@ -65,3 +65,8 @@ auto SystemPolicy::initialize() -> void { system_hardware_gpio_init(); } auto SystemPolicy::get_door_closed() -> bool { return system_hardware_read_door_closed(); } + +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) +auto SystemPolicy::last_reset_reason() const -> uint16_t { + return system_hardware_reset_reason(); +} diff --git a/stm32-modules/flex-stacker/firmware/ui/freertos_ui_task.cpp b/stm32-modules/flex-stacker/firmware/ui/freertos_ui_task.cpp index a69376669..7a11b41c6 100644 --- a/stm32-modules/flex-stacker/firmware/ui/freertos_ui_task.cpp +++ b/stm32-modules/flex-stacker/firmware/ui/freertos_ui_task.cpp @@ -1,17 +1,35 @@ +#include "FreeRTOS.h" #include "firmware/freertos_tasks.hpp" +#include "firmware/i2c_comms.hpp" #include "firmware/ui_hardware.h" - -static constexpr uint32_t HALF_SECOND = 500; +#include "firmware/ui_policy.hpp" +#include "flex-stacker/ui_task.hpp" namespace ui_control_task { +using namespace ui_policy; + +enum class Notifications : uint8_t { + INCOMING_MESSAGE = 1, +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static tasks::FirmwareTasks::UIQueue + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + _queue(static_cast(Notifications::INCOMING_MESSAGE), "UI Queue"); + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static auto _top_task = ui_task::UITask(_queue, nullptr, nullptr); + +auto run(tasks::FirmwareTasks::QueueAggregator* aggregator, + i2c::hardware::I2C* i2c_comms) -> void { + auto* handle = xTaskGetCurrentTaskHandle(); + _queue.provide_handle(handle); + aggregator->register_queue(_queue); + _top_task.provide_aggregator(aggregator); -auto run(tasks::FirmwareTasks::QueueAggregator* aggregator) -> void { - std::ignore = aggregator; + auto policy = UIPolicy(i2c_comms); while (true) { - ui_hardware_set_heartbeat_led(true); - vTaskDelay(HALF_SECOND); - ui_hardware_set_heartbeat_led(false); - vTaskDelay(HALF_SECOND); + _top_task.run_once(policy); } } diff --git a/stm32-modules/flex-stacker/firmware/ui/ui_hardware.c b/stm32-modules/flex-stacker/firmware/ui/ui_hardware.c index cf57bde44..316a55822 100644 --- a/stm32-modules/flex-stacker/firmware/ui/ui_hardware.c +++ b/stm32-modules/flex-stacker/firmware/ui/ui_hardware.c @@ -1,18 +1,10 @@ -#include "main.h" - #include #include "stm32g4xx_hal.h" - - -#define nSTATUS_LED_Pin GPIO_PIN_10 -#define nSTATUS_LED_GPIO_Port GPIOC +#include "main.h" void ui_hardware_set_heartbeat_led(bool setting) { //NOLINTNEXTLINE(performance-no-int-to-ptr) HAL_GPIO_WritePin(nSTATUS_LED_GPIO_Port, nSTATUS_LED_Pin, setting ? GPIO_PIN_SET : GPIO_PIN_RESET); - -// HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, -// setting ? GPIO_PIN_SET : GPIO_PIN_RESET); } diff --git a/stm32-modules/flex-stacker/firmware/ui/ui_policy.cpp b/stm32-modules/flex-stacker/firmware/ui/ui_policy.cpp new file mode 100644 index 000000000..3f24ab6fb --- /dev/null +++ b/stm32-modules/flex-stacker/firmware/ui/ui_policy.cpp @@ -0,0 +1,11 @@ +#include "firmware/ui_policy.hpp" + +#include "firmware/ui_hardware.h" + +namespace ui_policy { + +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) +auto UIPolicy::set_heartbeat_led(bool value) -> void { + ui_hardware_set_heartbeat_led(value); +} +}; // namespace ui_policy diff --git a/stm32-modules/flex-stacker/src/errors.cpp b/stm32-modules/flex-stacker/src/errors.cpp index b1e23d767..79b0f169e 100644 --- a/stm32-modules/flex-stacker/src/errors.cpp +++ b/stm32-modules/flex-stacker/src/errors.cpp @@ -16,6 +16,8 @@ const char* const SYSTEM_SERIAL_NUMBER_HAL_ERROR = "ERR302:system:HAL error, busy, or timeout\n"; const char* const SYSTEM_EEPROM_ERROR = "ERR303:system:EEPROM communication error\n"; +const char* const SYSTEM_SET_STATUSBAR_COLOR_ERROR = + "ERR304:system:STATUSBAR communication error\n"; const char* const TMC2160_READ_ERROR = "ERR901:TMC2160 driver read error\n"; const char* const TMC2160_WRITE_ERROR = "ERR902:TMC2160 driver write error\n"; const char* const TMC2160_INVALID_ADDRESS = "ERR903:TMC2160 invalid address\n"; @@ -49,6 +51,7 @@ auto errors::errorstring(ErrorCode code) -> const char* { HANDLE_CASE(SYSTEM_SERIAL_NUMBER_INVALID); HANDLE_CASE(SYSTEM_SERIAL_NUMBER_HAL_ERROR); HANDLE_CASE(SYSTEM_EEPROM_ERROR); + HANDLE_CASE(SYSTEM_SET_STATUSBAR_COLOR_ERROR); HANDLE_CASE(TMC2160_READ_ERROR); HANDLE_CASE(TMC2160_WRITE_ERROR); HANDLE_CASE(TMC2160_INVALID_ADDRESS); diff --git a/stm32-modules/heater-shaker/firmware/motor_task/motor_hardware.c b/stm32-modules/heater-shaker/firmware/motor_task/motor_hardware.c index 957b82885..f43dd4c01 100644 --- a/stm32-modules/heater-shaker/firmware/motor_task/motor_hardware.c +++ b/stm32-modules/heater-shaker/firmware/motor_task/motor_hardware.c @@ -19,6 +19,39 @@ motor_hardware_handles *MOTOR_HW_HANDLE = NULL; static _Atomic int16_t motor_speed_buffer[MOTOR_SPEED_BUFFER_SIZE]; static _Atomic size_t motor_speed_buffer_itr = 0; +uint16_t reset_reason; + +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 +}; static void MX_NVIC_Init(void) { @@ -491,6 +524,69 @@ static void DAC_Init(DAC_HandleTypeDef* dac) { HAL_DAC_SetValue(dac, SOLENOID_DAC_CHANNEL, DAC_ALIGN_8B_R, 0); } +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_HSIRDY)) { + 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_LSERDY)) { + 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_PORRST)) { + 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; + } +} + /** * Initializes the Global MSP. */ @@ -814,6 +910,7 @@ void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base) } void motor_hardware_setup(motor_hardware_handles* handles) { + save_reset_reason(); MOTOR_HW_HANDLE = handles; memset(motor_speed_buffer, 0, sizeof(motor_speed_buffer)); MX_GPIO_Init(); @@ -840,6 +937,10 @@ void motor_hardware_solenoid_release(DAC_HandleTypeDef* dac1) { HAL_DAC_SetValue(dac1, SOLENOID_DAC_CHANNEL, DAC_ALIGN_8B_R, 0); } +uint16_t motor_hardware_reset_reason() { + return reset_reason; +} + void motor_hardware_plate_lock_on(TIM_HandleTypeDef* tim3, float power) { float power_scale = fabs(power); if (power_scale > 1.f) { diff --git a/stm32-modules/heater-shaker/firmware/motor_task/motor_hardware.h b/stm32-modules/heater-shaker/firmware/motor_task/motor_hardware.h index 6ed795616..32a6ad935 100644 --- a/stm32-modules/heater-shaker/firmware/motor_task/motor_hardware.h +++ b/stm32-modules/heater-shaker/firmware/motor_task/motor_hardware.h @@ -37,6 +37,13 @@ bool motor_hardware_plate_lock_sensor_read(uint16_t GPIO_Pin); void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim); +/** + * @brief Returns the reason saved upon startup for the last + * reset of the software. + * @return a flag containing the reason for reset. + * */ +uint16_t motor_hardware_reset_reason(); + /** * @brief To be called every time the motor control library updates its speed * measurement. This function adds new speed values to a circular buffer, diff --git a/stm32-modules/heater-shaker/firmware/motor_task/motor_policy.cpp b/stm32-modules/heater-shaker/firmware/motor_task/motor_policy.cpp index d03ce32d6..6b509ac5c 100644 --- a/stm32-modules/heater-shaker/firmware/motor_task/motor_policy.cpp +++ b/stm32-modules/heater-shaker/firmware/motor_task/motor_policy.cpp @@ -131,3 +131,7 @@ auto MotorPolicy::get_serial_number(void) -> std::array { return _serial.get_serial_number(); } + +auto MotorPolicy::last_reset_reason() const -> uint16_t { + return motor_hardware_reset_reason(); +} \ No newline at end of file diff --git a/stm32-modules/heater-shaker/firmware/motor_task/motor_policy.hpp b/stm32-modules/heater-shaker/firmware/motor_task/motor_policy.hpp index c86e2b0f7..c6d145950 100644 --- a/stm32-modules/heater-shaker/firmware/motor_task/motor_policy.hpp +++ b/stm32-modules/heater-shaker/firmware/motor_task/motor_policy.hpp @@ -46,6 +46,7 @@ class MotorPolicy { auto plate_lock_closed_sensor_read() -> bool; auto get_serial_number(void) -> std::array; + auto last_reset_reason() const -> uint16_t; private: static constexpr uint16_t MAX_SOLENOID_CURRENT_MA = 330; diff --git a/stm32-modules/heater-shaker/simulator/motor_thread.cpp b/stm32-modules/heater-shaker/simulator/motor_thread.cpp index bd7d3cc9f..ac3e1fcf6 100644 --- a/stm32-modules/heater-shaker/simulator/motor_thread.cpp +++ b/stm32-modules/heater-shaker/simulator/motor_thread.cpp @@ -109,6 +109,12 @@ struct SimMotorPolicy { } } + auto last_reset_reason() -> uint16_t { return reset_reason; } + + auto set_last_reset_reason(uint16_t sim_reason) -> void { + reset_reason = sim_reason; + } + private: int16_t rpm_setpoint = 0; int16_t rpm_current = 0; @@ -116,6 +122,8 @@ struct SimMotorPolicy { float sim_plate_lock_power = 0; bool sim_plate_lock_enabled = false; bool sim_plate_lock_braked = false; + // Simulated reset reason from HAL RCC flag + uint16_t reset_reason = 0; }; struct motor_thread::TaskControlBlock { diff --git a/stm32-modules/include/common/core/is31fl_driver.hpp b/stm32-modules/include/common/core/is31fl_driver.hpp index 061703c70..34dbf990c 100644 --- a/stm32-modules/include/common/core/is31fl_driver.hpp +++ b/stm32-modules/include/common/core/is31fl_driver.hpp @@ -51,14 +51,12 @@ class IS31FL { // Send updated values to the driver template auto send_update(Policy& policy) -> bool { - if (!policy.i2c_write(Address, PWM_REGISTER_BASE, _pwm_settings)) { - return false; - } - if (!policy.i2c_write(Address, LED_CONTROL_REGISTER_BASE, - _current_settings)) { - return false; - } - return write_single_reg(UPDATE_REGISTER, TRIGGER_UPDATE_VALUE, policy); + auto res1 = policy.i2c_write(Address, PWM_REGISTER_BASE, _pwm_settings); + auto res2 = policy.i2c_write(Address, LED_CONTROL_REGISTER_BASE, + _current_settings); + auto res3 = + write_single_reg(UPDATE_REGISTER, TRIGGER_UPDATE_VALUE, policy); + return res1 && res2 && res3; } // Update current setting for a channel @@ -152,4 +150,4 @@ class IS31FL { std::array _single_reg_buffer{}; }; -}; // namespace is31fl \ No newline at end of file +}; // namespace is31fl diff --git a/stm32-modules/include/flex-stacker/firmware/freertos_tasks.hpp b/stm32-modules/include/flex-stacker/firmware/freertos_tasks.hpp index c0fb41e7a..8f6706a32 100644 --- a/stm32-modules/include/flex-stacker/firmware/freertos_tasks.hpp +++ b/stm32-modules/include/flex-stacker/firmware/freertos_tasks.hpp @@ -1,11 +1,11 @@ #pragma once #include "FreeRTOS.h" -#include "task.h" #include "firmware/firmware_tasks.hpp" #include "firmware/freertos_message_queue.hpp" #include "firmware/i2c_comms.hpp" +#include "task.h" namespace motor_driver_task { @@ -24,7 +24,8 @@ auto run(tasks::FirmwareTasks::QueueAggregator* aggregator) -> void; namespace ui_control_task { // Actual function that runs in the task -auto run(tasks::FirmwareTasks::QueueAggregator* aggregator) -> void; +auto run(tasks::FirmwareTasks::QueueAggregator* aggregator, + i2c::hardware::I2C* i2c_comms) -> void; } // namespace ui_control_task namespace host_comms_control_task { diff --git a/stm32-modules/include/flex-stacker/firmware/hardware_iface.hpp b/stm32-modules/include/flex-stacker/firmware/hardware_iface.hpp index 52ccc8f29..98de0a53c 100644 --- a/stm32-modules/include/flex-stacker/firmware/hardware_iface.hpp +++ b/stm32-modules/include/flex-stacker/firmware/hardware_iface.hpp @@ -1,9 +1,7 @@ #pragma once #include - #include -#include #include using std::size_t; diff --git a/stm32-modules/include/flex-stacker/firmware/i2c_comms.hpp b/stm32-modules/include/flex-stacker/firmware/i2c_comms.hpp index bae4e17e7..49c1c73fd 100644 --- a/stm32-modules/include/flex-stacker/firmware/i2c_comms.hpp +++ b/stm32-modules/include/flex-stacker/firmware/i2c_comms.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -20,16 +19,15 @@ class I2C : public I2CBase { auto operator=(const I2C &) = delete; auto operator=(const I2C &&) = delete; - auto i2c_read(uint16_t dev_addr, uint16_t reg, uint16_t size) -> RxTxReturn; + auto i2c_read(uint16_t dev_addr, uint16_t reg, uint16_t size) + -> RxTxReturn final; auto i2c_write(uint16_t dev_addr, uint16_t reg, uint8_t *data, - uint16_t size) -> RxTxReturn; - auto set_handle(HAL_I2C_HANDLE i2c_handle) -> void; - auto enable_tof_sensor(TOFSensorID sensor_id, bool enable) -> void; + uint16_t size) -> RxTxReturn final; + auto set_handle(HAL_I2C_HANDLE i2c_handle, I2C_BUS bus) -> void; private: + I2C_BUS bus = NO_BUS; HAL_I2C_HANDLE handle = nullptr; - // Timeout in ms - static constexpr auto TIMEOUT = 1000; }; }; // namespace hardware }; // namespace i2c diff --git a/stm32-modules/include/flex-stacker/firmware/i2c_hardware.h b/stm32-modules/include/flex-stacker/firmware/i2c_hardware.h index cda3019ce..d8dc814ba 100644 --- a/stm32-modules/include/flex-stacker/firmware/i2c_hardware.h +++ b/stm32-modules/include/flex-stacker/firmware/i2c_hardware.h @@ -3,29 +3,31 @@ #include #include +#include "systemwide.h" + #ifdef __cplusplus extern "C" { #endif // __cplusplus +typedef enum I2C_BUS { + I2C_BUS_2, + I2C_BUS_3, + NO_BUS, +} I2C_BUS; + typedef void *HAL_I2C_HANDLE; -/** - * @brief Before using an I2C struct, it should be "registered" so that - * the callbacks can be associated with the HAL I2C handles. - */ -bool i2c_register_handle(HAL_I2C_HANDLE handle); - -/** - * Wrapper around HAL_I2C_Mem_Write - */ -uint8_t hal_i2c_write(HAL_I2C_HANDLE handle, uint16_t DevAddress, uint16_t reg, - uint8_t *data, uint16_t size, uint32_t timeout); - -/** - * Wrapper around HAL_I2C_Mem_Read - */ -uint8_t hal_i2c_read(HAL_I2C_HANDLE handle, uint16_t DevAddress, uint16_t reg, - uint8_t *data, uint16_t size, uint32_t timeout); +typedef struct HandlerStruct { + HAL_I2C_HANDLE i2c2; + HAL_I2C_HANDLE i2c3; +} I2CHandlerStruct; + +void i2c_hardware_init(I2CHandlerStruct *i2c_handles); +bool i2c_register_handle(HAL_I2C_HANDLE handle, I2C_BUS bus); +uint8_t hal_i2c_write(I2C_BUS bus, uint16_t DevAddress, uint8_t reg, + uint8_t *data, uint16_t size); +uint8_t hal_i2c_read(I2C_BUS bus, uint16_t DevAddress, uint16_t reg, + uint8_t *data, uint16_t size); #ifdef __cplusplus } // extern "C" diff --git a/stm32-modules/include/flex-stacker/firmware/motor_interrupt.hpp b/stm32-modules/include/flex-stacker/firmware/motor_interrupt.hpp index c879363ed..080ea2592 100644 --- a/stm32-modules/include/flex-stacker/firmware/motor_interrupt.hpp +++ b/stm32-modules/include/flex-stacker/firmware/motor_interrupt.hpp @@ -67,6 +67,7 @@ class MotorInterruptController { motor_util::MovementType::FixedDistance, steps); _policy->enable_motor(_id); _response_id = move_id; + _policy->start_motor_timer(_id); } auto start_move(Move move) -> void { motor_util::MotorState* state = move.motor_state; diff --git a/stm32-modules/include/flex-stacker/firmware/system_hardware.h b/stm32-modules/include/flex-stacker/firmware/system_hardware.h index 51741b2ce..83ae9d0af 100644 --- a/stm32-modules/include/flex-stacker/firmware/system_hardware.h +++ b/stm32-modules/include/flex-stacker/firmware/system_hardware.h @@ -5,6 +5,7 @@ extern "C" { #endif // __cplusplus #include +#include #define HOPPER_DOR_CLOSED_PIN GPIO_PIN_13 #define HOPPER_DOR_CLOSED_GPIO_PORT GPIOC @@ -15,6 +16,7 @@ extern "C" { void system_hardware_enter_bootloader(void); void system_hardware_gpio_init(void); bool system_hardware_read_door_closed(void); +uint16_t system_hardware_reset_reason(void); #ifdef __cplusplus } // extern "C" diff --git a/stm32-modules/include/flex-stacker/firmware/system_policy.hpp b/stm32-modules/include/flex-stacker/firmware/system_policy.hpp index 87b7745fa..dfdede638 100644 --- a/stm32-modules/include/flex-stacker/firmware/system_policy.hpp +++ b/stm32-modules/include/flex-stacker/firmware/system_policy.hpp @@ -23,4 +23,5 @@ class SystemPolicy { -> errors::ErrorCode; auto get_serial_number() -> std::array; auto get_door_closed() -> bool; + [[nodiscard]] auto last_reset_reason() const -> uint16_t; }; diff --git a/stm32-modules/include/flex-stacker/firmware/ui_policy.hpp b/stm32-modules/include/flex-stacker/firmware/ui_policy.hpp new file mode 100644 index 000000000..eee926963 --- /dev/null +++ b/stm32-modules/include/flex-stacker/firmware/ui_policy.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include "i2c_comms.hpp" + +namespace ui_policy { +using namespace i2c::hardware; + +class UIPolicy { + public: + UIPolicy(I2C *i2c) : i2c_comms{i2c} {} + auto set_heartbeat_led(bool value) -> void; + template + auto i2c_write(uint8_t device_address, uint8_t register_address, + std::array &data) -> bool { + auto [ret, _] = i2c_comms->i2c_write(device_address, register_address, + data.data(), Len); + return ret == 0; + } + + private: + I2C *i2c_comms; +}; +}; // namespace ui_policy diff --git a/stm32-modules/include/flex-stacker/flex-stacker/errors.hpp b/stm32-modules/include/flex-stacker/flex-stacker/errors.hpp index b47a146c1..8a463e8d1 100644 --- a/stm32-modules/include/flex-stacker/flex-stacker/errors.hpp +++ b/stm32-modules/include/flex-stacker/flex-stacker/errors.hpp @@ -19,7 +19,7 @@ enum class ErrorCode { SYSTEM_SERIAL_NUMBER_INVALID = 301, SYSTEM_SERIAL_NUMBER_HAL_ERROR = 302, SYSTEM_EEPROM_ERROR = 303, - // TODO: Change TMC2160 Suffix so its generic + SYSTEM_SET_STATUSBAR_COLOR_ERROR = 304, // 9xx - TMC2160 TMC2160_READ_ERROR = 901, TMC2160_WRITE_ERROR = 902, diff --git a/stm32-modules/include/flex-stacker/flex-stacker/gcodes.hpp b/stm32-modules/include/flex-stacker/flex-stacker/gcodes.hpp index a002b1f09..d2d33051d 100644 --- a/stm32-modules/include/flex-stacker/flex-stacker/gcodes.hpp +++ b/stm32-modules/include/flex-stacker/flex-stacker/gcodes.hpp @@ -23,10 +23,6 @@ namespace gcode { -auto inline sensor_id_to_char(TOFSensorID sensor_id) -> char { - return static_cast(sensor_id == TOFSensorID::TOF_X ? 'X' : 'Z'); -} - struct EnterBootloader { /** * EnterBootloader uses the command string "dfu" instead of a gcode to be @@ -146,6 +142,43 @@ 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; + static constexpr auto prefix = std::array{'M', '1', '1', '4'}; + + template + requires std::forward_iterator && + std::sized_sentinel_for + 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 R:%X OK\n", reason); + if (res <= 0) { + return buf; + } + return buf + res; + } + 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); + } + if (working != limit && !std::isspace(*working)) { + return std::make_pair(ParseResult(), input); + } + return std::make_pair(ParseResult(GetResetReason()), working); + } +}; + struct SetSerialNumber { using ParseResult = std::optional; static constexpr auto prefix = std::array{'M', '9', '9', '6'}; @@ -207,7 +240,7 @@ struct GetDoorClosed { static auto write_response_into(InputIt buf, InLimit limit, int door_closed) -> InputIt { int res = 0; - res = snprintf(&*buf, (limit - buf), "M122 %i OK\n", door_closed); + res = snprintf(&*buf, (limit - buf), "M122 D:%i OK\n", door_closed); if (res <= 0) { return buf; } @@ -215,6 +248,57 @@ struct GetDoorClosed { } }; +struct SetStatusBarColor { + std::optional bar_id; + std::optional color; + float power; + + using ParseResult = std::optional; + static constexpr auto prefix = std::array{'M', '2', '0', '0', ' '}; + static constexpr const char* response = "M200 OK\n"; + + using PowerArg = Arg; + using ColorArg = Arg; + using KArg = Arg; + + template + requires std::forward_iterator && + std::sized_sentinel_for + static auto parse(const InputIt& input, Limit limit) + -> std::pair { + auto res = gcode::SingleParser::parse_gcode( + input, limit, prefix); + if (!res.first.has_value()) { + return std::make_pair(ParseResult(), input); + } + + auto ret = SetStatusBarColor{ + .bar_id = std::nullopt, .color = std::nullopt, .power = 0}; + + auto arguments = res.first.value(); + if (std::get<0>(arguments).present) { + ret.power = static_cast(std::get<0>(arguments).value); + } + + if (std::get<1>(arguments).present) { + ret.color = + static_cast(std::get<1>(arguments).value); + } + + if (std::get<2>(arguments).present) { + ret.bar_id = static_cast(std::get<2>(arguments).value); + } + return std::make_pair(ret, res.second); + } + + template + requires std::forward_iterator && + std::sized_sentinel_for + static auto write_response_into(InputIt buf, InLimit limit) -> InputIt { + return write_string_to_iterpair(buf, limit, response); + } +}; + struct GetTOFSensorStatus { TOFSensorID sensor_id; using ParseResult = std::optional; 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 3bbd97c8a..d76cddcea 100644 --- a/stm32-modules/include/flex-stacker/flex-stacker/gcodes_motor.hpp +++ b/stm32-modules/include/flex-stacker/flex-stacker/gcodes_motor.hpp @@ -97,7 +97,7 @@ struct GetTMCRegister { static auto write_response_into(InputIt buf, InLimit limit, MotorID motor_id, uint8_t reg, uint32_t data) -> InputIt { - auto res = snprintf(&*buf, (limit - buf), "M920 %s%u %lu OK\n", + auto res = snprintf(&*buf, (limit - buf), "M920 %s:%u V:%lu OK\n", motor_id_to_char(motor_id), reg, data); if (res <= 0) { return buf; @@ -817,9 +817,9 @@ struct GetMoveParams { : motor_id == MotorID::MOTOR_Z ? 'Z' : 'L'; int res = 0; - res = - snprintf(&*buf, (limit - buf), "M120 %c V:%.3f A:%.3f D:%.3f OK\n", - motor_char, velocity, accel, velocity_discont); + res = snprintf(&*buf, (limit - buf), + "M120 M:%c V:%.3f A:%.3f D:%.3f OK\n", motor_char, + velocity, accel, velocity_discont); if (res <= 0) { return buf; } @@ -931,7 +931,7 @@ struct GetMotorStallGuard { : motor_id == MotorID::MOTOR_Z ? 'Z' : 'L'; int res = 0; - res = snprintf(&*buf, (limit - buf), "M911 %c%d T:%d OK\n", motor_char, + res = snprintf(&*buf, (limit - buf), "M911 %c:%d T:%d OK\n", motor_char, int(enabled), threshold); if (res <= 0) { return buf; @@ -994,7 +994,7 @@ struct GetEstopStatus { static auto write_response_into(InputIt buf, InLimit limit, int triggered) -> InputIt { int res = 0; - res = snprintf(&*buf, (limit - buf), "M112 %i OK\n", triggered); + res = snprintf(&*buf, (limit - buf), "M112 E:%i OK\n", triggered); if (res <= 0) { return buf; } 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 f0377ab9d..a0996c038 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 @@ -15,6 +15,7 @@ #include "flex-stacker/errors.hpp" #include "flex-stacker/gcodes.hpp" #include "flex-stacker/messages.hpp" +#include "gcodes.hpp" #include "gcodes_motor.hpp" #include "hal/message_queue.hpp" #include "messages.hpp" @@ -55,8 +56,9 @@ class HostCommsTask { gcode::SetTMCRegister, gcode::SetRunCurrent, gcode::SetHoldCurrent, gcode::EnableMotor, gcode::DisableMotor, gcode::MoveMotorInSteps, gcode::MoveToLimitSwitch, gcode::MoveMotorInMm, gcode::SetMicrosteps, - gcode::SetMotorStallGuard, gcode::HomeMotor, gcode::SetTOFRegister, - gcode::StopMotor, gcode::EnableTOFSensor>; + gcode::SetMotorStallGuard, gcode::HomeMotor, gcode::StopMotor, + gcode::GetResetReason, gcode::SetStatusBarColor, gcode::SetTOFRegister, + gcode::EnableTOFSensor>; using GetSystemInfoCache = AckCache<8, gcode::GetSystemInfo>; using GetTMCRegisterCache = AckCache<8, gcode::GetTMCRegister>; using GetLimitSwitchesCache = AckCache<8, gcode::GetLimitSwitches>; @@ -65,6 +67,7 @@ class HostCommsTask { using GetDoorClosedCache = AckCache<8, gcode::GetDoorClosed>; using GetPlatformSensorsCache = AckCache<8, gcode::GetPlatformSensors>; using GetEstopCache = AckCache<8, gcode::GetEstopStatus>; + using GetResetReasonCache = AckCache<8, gcode::GetResetReason>; using GetTOFSensorStatusCache = AckCache<8, gcode::GetTOFSensorStatus>; using GetTOFRegisterCache = AckCache<8, gcode::GetTOFRegister>; @@ -86,6 +89,7 @@ class HostCommsTask { get_door_closed_cache(), get_platform_sensors_cache(), get_estop_cache(), + get_reset_reason_cache(), get_tof_sensor_status_cache(), get_tof_register_cache() {} // NOLINTEND(readability-redundant-member-init) @@ -293,6 +297,50 @@ class HostCommsTask { cache_entry); } + template + requires std::forward_iterator && + std::sized_sentinel_for + 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; + if constexpr (std::is_same_v) { + 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 + requires std::forward_iterator && + std::sized_sentinel_for + auto visit_gcode(const gcode::GetResetReason& gcode, InputIt tx_into, + InputLimit tx_limit) -> std::pair { + 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->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 requires std::forward_iterator && std::sized_sentinel_for @@ -997,6 +1045,32 @@ class HostCommsTask { return std::make_pair(true, tx_into); } + template + requires std::forward_iterator && + std::sized_sentinel_for + auto visit_gcode(const gcode::SetStatusBarColor& gcode, InputIt tx_into, + InputLimit tx_limit) -> std::pair { + auto id = ack_only_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::SetStatusBarColorMessage{ + .id = id, + .bar_id = gcode.bar_id, + .power = gcode.power, + .color = gcode.color, + }; + 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); + ack_only_cache.remove_if_present(id); + return std::make_pair(false, wrote_to); + } + return std::make_pair(true, tx_into); + } + template requires std::forward_iterator && std::sized_sentinel_for @@ -1092,13 +1166,6 @@ class HostCommsTask { requires std::forward_iterator && std::sized_sentinel_for auto visit_gcode(const gcode::SetTOFRegister& gcode, InputIt tx_into, - InputLimit tx_limit) -> std::pair { - auto id = ack_only_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::SetTOFRegisterMessage{.id = id, .sensor_id = gcode.sensor_id, @@ -1158,6 +1225,7 @@ class HostCommsTask { GetDoorClosedCache get_door_closed_cache; GetPlatformSensorsCache get_platform_sensors_cache; GetEstopCache get_estop_cache; + GetResetReasonCache get_reset_reason_cache; GetTOFSensorStatusCache get_tof_sensor_status_cache; GetTOFRegisterCache get_tof_register_cache; bool may_connect_latch = true; diff --git a/stm32-modules/include/flex-stacker/flex-stacker/messages.hpp b/stm32-modules/include/flex-stacker/flex-stacker/messages.hpp index 4534827c6..51491697f 100644 --- a/stm32-modules/include/flex-stacker/flex-stacker/messages.hpp +++ b/stm32-modules/include/flex-stacker/flex-stacker/messages.hpp @@ -74,6 +74,15 @@ struct GetSystemInfoResponse { const char* hw_version; }; +struct GetResetReasonMessage { + uint32_t id; +}; + +struct GetResetReasonResponse { + uint32_t responding_to_id; + uint16_t reason; +}; + struct SetSerialNumberMessage { uint32_t id; static constexpr std::size_t SERIAL_NUMBER_LENGTH = @@ -272,6 +281,13 @@ struct GetEstopResponse { bool triggered; }; +struct SetStatusBarColorMessage { + uint32_t id = 0; + std::optional bar_id = std::nullopt; + std::optional power = std::nullopt; + std::optional color = std::nullopt; +}; + struct GetTOFSensorStatusMessage { uint32_t id; TOFSensorID sensor_id; @@ -318,13 +334,15 @@ using HostCommsMessage = GetTMCRegisterResponse, GetLimitSwitchesResponses, GetMoveParamsResponse, GetMotorStallGuardResponse, GetDoorClosedResponse, GetPlatformSensorsResponse, - GetEstopResponse, GetTOFSensorStatusResponse, + GetEstopResponse, GetResetReasonResponse, GetTOFSensorStatusResponse, GetTOFRegisterResponse>; using SystemMessage = ::std::variant; + GetDoorClosedMessage, GetResetReasonMessage>; + +using UIMessage = ::std::variant; using MotorDriverMessage = ::std::variant void { + _z_controller.stop_movement(id, true); + _x_controller.stop_movement(id, false); + _l_controller.stop_movement(id, false); + _move_queue.reset(); + } + auto make_move(uint32_t id, MotorID motor_id, bool direction, float distance, bool has_next_move = false) -> Move { return Move{.motor_id = motor_id, @@ -312,9 +319,9 @@ class MotorTask { template auto visit_message(const messages::StopMotorMessage& m, Policy& policy) -> void { - static_cast(m); static_cast(policy); - controller_from_id(m.motor_id).stop_movement(m.id, true); + stop_motors(m.id); + send_ack_message(m.id); } template @@ -419,9 +426,14 @@ class MotorTask { // don't care about other interrupts return; } - _z_controller.stop_movement(0, true); - _x_controller.stop_movement(0, false); - _l_controller.stop_movement(0, false); + stop_motors(); + + // Set status bars to RED + // TODO: Enable this after SLAS + // auto message = + // messages::SetStatusBarColorMessage{.color = StatusBarColor::Red}; + // static_cast( + // _task_registry->send_to_address(message, Queues::UIAddress)); } /** diff --git a/stm32-modules/include/flex-stacker/flex-stacker/system_task.hpp b/stm32-modules/include/flex-stacker/flex-stacker/system_task.hpp index 33efd3c10..e89e8f891 100644 --- a/stm32-modules/include/flex-stacker/flex-stacker/system_task.hpp +++ b/stm32-modules/include/flex-stacker/flex-stacker/system_task.hpp @@ -86,6 +86,17 @@ class SystemTask { static_cast(_task_registry->send(response)); } + template + 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( + _task_registry->send(response, Queues::HostCommsAddress)); + } + template auto visit_message(const messages::SetSerialNumberMessage& message, Policy& policy) { diff --git a/stm32-modules/include/flex-stacker/flex-stacker/tasks.hpp b/stm32-modules/include/flex-stacker/flex-stacker/tasks.hpp index 7f72c9e90..e49e293f6 100644 --- a/stm32-modules/include/flex-stacker/flex-stacker/tasks.hpp +++ b/stm32-modules/include/flex-stacker/flex-stacker/tasks.hpp @@ -19,6 +19,8 @@ struct Tasks { using HostCommsQueue = QueueImpl; // Message queue for system task using SystemQueue = QueueImpl; + // Message queue for UI task + using UIQueue = QueueImpl; // Message queue for tof driver task using TOFDriverQueue = QueueImpl; // Message queue for tof sensor task @@ -27,7 +29,7 @@ struct Tasks { // Central aggregator using QueueAggregator = queue_aggregator::QueueAggregator; // Addresses @@ -39,6 +41,8 @@ struct Tasks { QueueAggregator::template get_queue_idx(); static constexpr size_t SystemAddress = QueueAggregator::template get_queue_idx(); + static constexpr size_t UIAddress = + QueueAggregator::template get_queue_idx(); static constexpr size_t TOFDriverAddress = QueueAggregator::template get_queue_idx(); static constexpr size_t TOFSensorAddress = diff --git a/stm32-modules/include/flex-stacker/flex-stacker/tmc2160_registers.hpp b/stm32-modules/include/flex-stacker/flex-stacker/tmc2160_registers.hpp index f7bbf885c..a78b85d98 100644 --- a/stm32-modules/include/flex-stacker/flex-stacker/tmc2160_registers.hpp +++ b/stm32-modules/include/flex-stacker/flex-stacker/tmc2160_registers.hpp @@ -261,11 +261,11 @@ struct __attribute__((packed, __may_alias__)) PowerDownDelay { static constexpr uint32_t max_val = 0xFF; static constexpr uint32_t reset = 10; - [[nodiscard]] static auto reg_to_seconds(uint8_t reg) -> double { + [[nodiscard]] static constexpr auto reg_to_seconds(uint8_t reg) -> double { return (static_cast(reg) / static_cast(max_val)) * max_time; } - static auto seconds_to_reg(double seconds) -> uint8_t { + static constexpr auto seconds_to_reg(double seconds) -> uint8_t { if (seconds > max_time) { return max_val; } diff --git a/stm32-modules/include/flex-stacker/flex-stacker/ui_task.hpp b/stm32-modules/include/flex-stacker/flex-stacker/ui_task.hpp new file mode 100644 index 000000000..8b5f627ec --- /dev/null +++ b/stm32-modules/include/flex-stacker/flex-stacker/ui_task.hpp @@ -0,0 +1,241 @@ +#pragma once + +#include +#include + +#include "core/is31fl_driver.hpp" +#include "errors.hpp" +#include "hal/message_queue.hpp" +#include "messages.hpp" +#include "ot_utils/freertos/freertos_timer.hpp" +#include "systemwide.h" +#include "ui_policy.hpp" + +namespace ui_task { +using namespace ot_utils::freertos_timer; +using namespace ui_policy; + +template +concept UIPolicyIface = requires(Policy& p) { + // A function to set the heartbeat LED on or off + {p.set_heartbeat_led(true)}; +} +&&is31fl::IS31FL_Policy; + +// There are 3 channels per color +using ChannelMapping = std::array; + +static constexpr ChannelMapping white_channels{2, 3, 4, 5}; +static constexpr ChannelMapping red_channels{6, 9, 12, 15}; +static constexpr ChannelMapping green_channels{7, 10, 13, 16}; +static constexpr ChannelMapping blue_channels{8, 11, 14, 17}; + +static auto color_to_channels(StatusBarColor color) -> const ChannelMapping& { + switch (color) { + case StatusBarColor::White: + return white_channels; + case StatusBarColor::Red: + return red_channels; + case StatusBarColor::Green: + return green_channels; + case StatusBarColor::Blue: + return blue_channels; + default: + return white_channels; + } +} + +// The timer driving LED update frequency should run at this period +static constexpr uint32_t UPDATE_PERIOD_MS = 1000; +static constexpr uint8_t LED_DRIVER0_I2C_ADDRESS = 0x6C << 1; // Internal +static constexpr uint8_t LED_DRIVER1_I2C_ADDRESS = 0x6F << 1; // External +static constexpr auto DEFAULT_COLOR = StatusBarColor::Green; +static constexpr auto DEFAULT_POWER = 0.5F; + +struct StatusBarState { + StatusBarID kind; + StatusBarColor color; + float power; +}; + +const StatusBarState led_bar_internal = { + .kind = StatusBarID::Internal, + .color = DEFAULT_COLOR, + .power = DEFAULT_POWER, +}; + +const StatusBarState led_bar_external = { + .kind = StatusBarID::External, + .color = DEFAULT_COLOR, + .power = DEFAULT_POWER, +}; + +using Message = messages::UIMessage; + +template