diff --git a/doc/logging_components.md b/doc/logging_components.md index 5b05935a32..51a7a8f229 100644 --- a/doc/logging_components.md +++ b/doc/logging_components.md @@ -21,7 +21,6 @@ This file is generated automatically so don't edit it directly - FSensor: LOG_SEVERITY_INFO, src/common/filament_sensors_handler.cpp - FileSystem: LOG_SEVERITY_INFO, src/buddy/filesystem.cpp - GUI: LOG_SEVERITY_DEBUG, src/gui/logger.cpp -- I2C: LOG_SEVERITY_INFO, src/device/stm32f4/peripherals.cpp - Led: LOG_SEVERITY_INFO, src/gui/led_animations/printer_animation_state.cpp - Loadcell: LOG_SEVERITY_INFO, src/common/loadcell.cpp - MMU2: LOG_SEVERITY_INFO, src/common/appmain.cpp diff --git a/include/device/stm32f4/device/peripherals.h b/include/device/stm32f4/device/peripherals.h index d4d50ab05b..bc59b7321a 100644 --- a/include/device/stm32f4/device/peripherals.h +++ b/include/device/stm32f4/device/peripherals.h @@ -310,18 +310,17 @@ void hw_uart8_init(); #if HAS_I2CN(1) void hw_i2c1_init(); +void hw_i2c1_pins_init(); #endif #if HAS_I2CN(2) void hw_i2c2_init(); +void hw_i2c2_pins_init(); #endif #if HAS_I2CN(3) void hw_i2c3_init(); +void hw_i2c3_pins_init(); #endif -size_t hw_i2c1_get_busy_clear_count(); -size_t hw_i2c2_get_busy_clear_count(); -size_t hw_i2c3_get_busy_clear_count(); - void hw_spi2_init(); void hw_spi3_init(); void hw_spi4_init(); diff --git a/src/common/PCA9557.hpp b/src/common/PCA9557.hpp index 7f02e5cc26..b33c578c73 100644 --- a/src/common/PCA9557.hpp +++ b/src/common/PCA9557.hpp @@ -1,6 +1,6 @@ #pragma once #include "Pin.hpp" -#include "i2c.h" +#include "i2c.hpp" namespace buddy::hw { @@ -53,7 +53,7 @@ class PCA9557 { void write_reg(Register_t reg, uint8_t value) { uint8_t data[2] = { (uint8_t)reg, value }; - I2C_Transmit(&i2c, write_address, data, sizeof(data), TIMEOUT); + (void)i2c::Transmit(i2c, write_address, data, sizeof(data), TIMEOUT); } }; diff --git a/src/common/gcode_info.cpp b/src/common/gcode_info.cpp index b8d4d4276e..147d14fa6b 100644 --- a/src/common/gcode_info.cpp +++ b/src/common/gcode_info.cpp @@ -119,8 +119,11 @@ void GCodeInfo::EvaluateToolsValid() { #endif // nozzle diameter of this tool in gcode is different then printer has - if (per_extruder_info[e].nozzle_diameter.has_value() && per_extruder_info[e].nozzle_diameter != config_store().get_nozzle_diameter(e)) { - valid_printer_settings.wrong_nozzle_diameter.fail(); + if (per_extruder_info[e].nozzle_diameter.has_value()) { + float nozzle_diameter_distance = per_extruder_info[e].nozzle_diameter.value() - config_store().get_nozzle_diameter(e); + if (nozzle_diameter_distance > 0.001f || nozzle_diameter_distance < -0.001f) { + valid_printer_settings.wrong_nozzle_diameter.fail(); + } } } } diff --git a/src/common/i2c.cpp b/src/common/i2c.cpp index 3d7a40994e..2167e94ace 100644 --- a/src/common/i2c.cpp +++ b/src/common/i2c.cpp @@ -1,130 +1,316 @@ -#include "i2c.h" +#include "i2c.hpp" +#include "stm32f4xx_hal.h" #include "bsod.h" #include "log.h" #include "cmsis_os.h" #include "bsod_gui.hpp" +#include -#define EEPROM_MAX_RETRIES 20 +#define MAX_RETRIES 20 LOG_COMPONENT_REF(EEPROM); -volatile std::atomic I2C_TRANSMIT_RESULTS_HAL_OK = 0; -volatile std::atomic I2C_TRANSMIT_RESULTS_HAL_BUSY = 0; +namespace i2c { -osMutexId i2c_mutex = 0; // mutex handle +namespace statistics { -static void I2C_lock(void) { - if (i2c_mutex == 0) { - osMutexDef(i2c_mutex); - i2c_mutex = osMutexCreate(osMutex(i2c_mutex)); + struct Results { + std::atomic HAL_OK = 0; + std::atomic HAL_BUSY = 0; + std::atomic HAL_ERROR = 0; + std::atomic HAL_TIMEOUT = 0; + }; + +#if HAS_I2CN(1) + Results ch1; +#endif +#if HAS_I2CN(2) + Results ch2; +#endif +#if HAS_I2CN(3) + Results ch3; +#endif + + uint32_t get_hal_ok(uint8_t channel) { + switch (channel) { +#if HAS_I2CN(1) + case 1: + return ch1.HAL_OK; +#endif +#if HAS_I2CN(2) + case 2: + return ch2.HAL_OK; +#endif +#if HAS_I2CN(3) + case 3: + return ch3.HAL_OK; +#endif + } + return 0; } - osMutexWait(i2c_mutex, osWaitForever); -} -static void I2C_unlock(void) { - osMutexRelease(i2c_mutex); + uint32_t get_hal_busy(uint8_t channel) { + switch (channel) { +#if HAS_I2CN(1) + case 1: + return ch1.HAL_BUSY; +#endif +#if HAS_I2CN(2) + case 2: + return ch2.HAL_BUSY; +#endif +#if HAS_I2CN(3) + case 3: + return ch3.HAL_BUSY; +#endif + } + return 0; + } + uint32_t get_hal_error(uint8_t channel) { + switch (channel) { +#if HAS_I2CN(1) + case 1: + return ch1.HAL_ERROR; +#endif +#if HAS_I2CN(2) + case 2: + return ch2.HAL_ERROR; +#endif +#if HAS_I2CN(3) + case 3: + return ch3.HAL_ERROR; +#endif + } + return 0; + } + uint32_t get_hal_timeout(uint8_t channel) { + switch (channel) { +#if HAS_I2CN(1) + case 1: + return ch1.HAL_TIMEOUT; +#endif +#if HAS_I2CN(2) + case 2: + return ch2.HAL_TIMEOUT; +#endif +#if HAS_I2CN(3) + case 3: + return ch3.HAL_TIMEOUT; +#endif + } + return 0; + } +} // namespace statistics + +// mutex handles +static osMutexId i2c_mutex_1 = nullptr; +static osMutexId i2c_mutex_2 = nullptr; +static osMutexId i2c_mutex_3 = nullptr; + +static int get_i2c_no(I2C_HandleTypeDef &hi2c) { + uintptr_t offset = I2C2_BASE - I2C1_BASE; + uintptr_t current = reinterpret_cast(hi2c.Instance); + current -= I2C1_BASE; + return (current / offset) + 1; // +1 .. i2c numbered from 1 not from 0 } -extern "C" HAL_StatusTypeDef I2C_Transmit_ext(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) { - int retries = EEPROM_MAX_RETRIES; - HAL_StatusTypeDef result = HAL_ERROR; - while (--retries) { - I2C_lock(); - result = HAL_I2C_Master_Transmit(hi2c, DevAddress, pData, Size, Timeout); - I2C_unlock(); - if (result != HAL_BUSY) - break; - ++I2C_TRANSMIT_RESULTS_HAL_BUSY; - log_error(EEPROM, "%s: was BUSY at %d. try of %d, total retries %d", __FUNCTION__, EEPROM_MAX_RETRIES - retries, retries, I2C_TRANSMIT_RESULTS_HAL_BUSY); +static_assert(std::is_same_v, "rewrite lock declaration"); + +osMutexId ChannelMutex::get_handle(I2C_HandleTypeDef &hi2c) { + switch (get_i2c_no(hi2c)) { + case 1: + return i2c_mutex_1; + case 2: + return i2c_mutex_2; + case 3: + return i2c_mutex_3; } + return nullptr; +} - return result; +void ChannelMutex::init_mutexes() { + if (i2c_mutex_1 == nullptr) { + osMutexDef(i2c_mutex_1); + i2c_mutex_1 = osMutexCreate(osMutex(i2c_mutex_1)); + if (i2c_mutex_1 == nullptr) { + fatal_error(ErrCode::ERR_ELECTRO_I2C_TX_UNDEFINED); // TODO change to ERR_I2C_MUTEX_CREATE_FAILED + } + } + if (i2c_mutex_2 == nullptr) { + osMutexDef(i2c_mutex_2); + i2c_mutex_2 = osMutexCreate(osMutex(i2c_mutex_2)); + if (i2c_mutex_2 == nullptr) { + fatal_error(ErrCode::ERR_ELECTRO_I2C_TX_UNDEFINED); // TODO change to ERR_I2C_MUTEX_CREATE_FAILED + } + } + if (i2c_mutex_3 == nullptr) { + osMutexDef(i2c_mutex_3); + i2c_mutex_3 = osMutexCreate(osMutex(i2c_mutex_3)); + if (i2c_mutex_3 == nullptr) { + fatal_error(ErrCode::ERR_ELECTRO_I2C_TX_UNDEFINED); // TODO change to ERR_I2C_MUTEX_CREATE_FAILED + } + } } -extern "C" void I2C_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) { - auto result = I2C_Transmit_ext(hi2c, DevAddress, pData, Size, Timeout); +ChannelMutex::ChannelMutex(I2C_HandleTypeDef &hi2c) + : mutex_handle(get_handle(hi2c)) { + + init_mutexes(); // this will be done only once - if (result == HAL_OK) { - // print entire status only at log severity debug - log_debug(EEPROM, "%s: OK %u, BUSY %u", __FUNCTION__, - I2C_TRANSMIT_RESULTS_HAL_OK.load(), I2C_TRANSMIT_RESULTS_HAL_BUSY.load()); - } else { - // some kind of error print entire status - log_info(EEPROM, "%s: OK %u, BUSY %u", __FUNCTION__, - I2C_TRANSMIT_RESULTS_HAL_OK.load(), I2C_TRANSMIT_RESULTS_HAL_BUSY.load()); + // lock + if (mutex_handle) { + osMutexWait(mutex_handle, osWaitForever); } +} +ChannelMutex::~ChannelMutex() { + // unlock + if (mutex_handle) { + osMutexRelease(mutex_handle); + } +} + +static Result process_result_n(HAL_StatusTypeDef result, statistics::Results &result_counters) { switch (result) { case HAL_OK: - ++I2C_TRANSMIT_RESULTS_HAL_OK; + ++result_counters.HAL_OK; log_debug(EEPROM, "%s: OK", __FUNCTION__); - break; + return Result::ok; case HAL_ERROR: + ++result_counters.HAL_ERROR; log_error(EEPROM, "%s: ERROR", __FUNCTION__); - fatal_error(ErrCode::ERR_ELECTRO_I2C_TX_ERROR); - break; + return Result::error; case HAL_BUSY: + ++result_counters.HAL_BUSY; log_error(EEPROM, "%s: BUSY", __FUNCTION__); - fatal_error(ErrCode::ERR_ELECTRO_I2C_TX_BUSY); - break; + return Result::busy_after_retries; case HAL_TIMEOUT: + ++result_counters.HAL_TIMEOUT; log_error(EEPROM, "%s: TIMEOUT", __FUNCTION__); - fatal_error(ErrCode::ERR_ELECTRO_I2C_TX_TIMEOUT); - break; + return Result::timeout; default: log_critical(EEPROM, "%s: UNDEFINED", __FUNCTION__); - fatal_error(ErrCode::ERR_ELECTRO_I2C_TX_UNDEFINED); + fatal_error(ErrCode::ERR_ELECTRO_I2C_TX_UNDEFINED); // TODO change to ERR_ELECTRO_I2C_UNDEFINED break; } + return Result::error; // will not get here, just prevent warning } -volatile std::atomic I2C_RECEIVE_RESULTS_HAL_OK = 0; -volatile std::atomic I2C_RECEIVE_RESULTS_HAL_BUSY = 0; +static Result process_result(I2C_HandleTypeDef &hi2c, HAL_StatusTypeDef result) { + int i2c_no = get_i2c_no(hi2c); -extern "C" void I2C_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) { + switch (i2c_no) { +#if HAS_I2CN(1) + case 1: + return process_result_n(result, statistics::ch1); +#endif +#if HAS_I2CN(2) + case 2: + return process_result_n(result, statistics::ch2); +#endif +#if HAS_I2CN(3) + case 3: + return process_result_n(result, statistics::ch3); +#endif + } - int retries = EEPROM_MAX_RETRIES; + fatal_error(ErrCode::ERR_ELECTRO_I2C_TX_UNDEFINED); // TODO change to access to unused i2c + return Result::error; // will not get here, just prevent warning +} + +Result Transmit(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) { + int retries = MAX_RETRIES; HAL_StatusTypeDef result = HAL_ERROR; - while (--retries) { - I2C_lock(); - result = HAL_I2C_Master_Receive(hi2c, DevAddress, pData, Size, Timeout); - I2C_unlock(); + Result res = Result::error; + while (retries--) { + { + ChannelMutex M(hi2c); + result = HAL_I2C_Master_Transmit(&hi2c, DevAddress, pData, Size, Timeout); + } + res = process_result(hi2c, result); if (result != HAL_BUSY) break; - ++I2C_RECEIVE_RESULTS_HAL_BUSY; - log_error(EEPROM, "%s: was BUSY at %d. try of %d, total retries %d", __FUNCTION__, EEPROM_MAX_RETRIES - retries, retries, I2C_TRANSMIT_RESULTS_HAL_BUSY); } - if (result == HAL_OK) { - // print entire status only at log severity debug - log_debug(EEPROM, "%s: OK %d, BUSY %d", __FUNCTION__, - I2C_RECEIVE_RESULTS_HAL_OK.load(), I2C_RECEIVE_RESULTS_HAL_BUSY.load()); - } else { - // some kind of error print entire status - log_info(EEPROM, "%s: OK %d, BUSY %d", __FUNCTION__, - I2C_RECEIVE_RESULTS_HAL_OK.load(), I2C_RECEIVE_RESULTS_HAL_BUSY.load()); + return res; +} + +Result Receive(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) { + + int retries = MAX_RETRIES; + HAL_StatusTypeDef result = HAL_ERROR; + Result res = Result::error; + while (retries--) { + { + ChannelMutex M(hi2c); + result = HAL_I2C_Master_Receive(&hi2c, DevAddress, pData, Size, Timeout); + } + res = process_result(hi2c, result); + if (result != HAL_BUSY) + break; } - switch (result) { - case HAL_OK: - ++I2C_RECEIVE_RESULTS_HAL_OK; - log_debug(EEPROM, "%s: OK", __FUNCTION__); - break; - case HAL_ERROR: - log_error(EEPROM, "%s: ERROR", __FUNCTION__); - fatal_error(ErrCode::ERR_ELECTRO_I2C_RX_ERROR); - break; - case HAL_BUSY: - log_error(EEPROM, "%s: BUSY", __FUNCTION__); - fatal_error(ErrCode::ERR_ELECTRO_I2C_RX_BUSY); - break; - case HAL_TIMEOUT: - log_error(EEPROM, "%s: TIMEOUT", __FUNCTION__); - fatal_error(ErrCode::ERR_ELECTRO_I2C_RX_TIMEOUT); - break; - default: - log_critical(EEPROM, "%s: UNDEFINED", __FUNCTION__); - fatal_error(ErrCode::ERR_ELECTRO_I2C_RX_UNDEFINED); - break; + return res; +} + +static Result Mem_Write(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout) { + int retries = MAX_RETRIES; // prevent int underflow + HAL_StatusTypeDef result = HAL_ERROR; + Result res = Result::error; + while (retries--) { + { + ChannelMutex M(hi2c); + result = HAL_I2C_Mem_Write(&hi2c, DevAddress, MemAddress, MemAddSize, pData, Size, Timeout); + } + res = process_result(hi2c, result); + if (res == Result::ok) + break; + } + + return res; +} + +Result Mem_Write_8bit_Addr(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint8_t MemAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) { + return Mem_Write(hi2c, DevAddress, MemAddress, I2C_MEMADD_SIZE_8BIT, pData, Size, Timeout); +} + +Result Mem_Write_16bit_Addr(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) { + return Mem_Write(hi2c, DevAddress, MemAddress, I2C_MEMADD_SIZE_16BIT, pData, Size, Timeout); +} + +[[nodiscard]] static Result Mem_Read(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout) { + int retries = MAX_RETRIES; // prevent int underflow + HAL_StatusTypeDef result = HAL_ERROR; + Result res = Result::error; + while (--retries) { + { + ChannelMutex M(hi2c); + result = HAL_I2C_Mem_Read(&hi2c, DevAddress, MemAddress, MemAddSize, pData, Size, Timeout); + } + res = process_result(hi2c, result); + if (res != Result::ok) + break; } + + return res; } + +Result Mem_Read_8bit_Addr(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint8_t MemAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) { + return Mem_Read(hi2c, DevAddress, MemAddress, I2C_MEMADD_SIZE_8BIT, pData, Size, Timeout); +} + +Result Mem_Read_16bit_Addr(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) { + return Mem_Read(hi2c, DevAddress, MemAddress, I2C_MEMADD_SIZE_16BIT, pData, Size, Timeout); +} + +Result IsDeviceReady(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout) { + HAL_StatusTypeDef result; + { + ChannelMutex M(hi2c); + result = HAL_I2C_IsDeviceReady(&hi2c, DevAddress, Trials, Timeout); + } + + return process_result(hi2c, result); +} + +} // namespace i2c diff --git a/src/common/i2c.h b/src/common/i2c.h deleted file mode 100644 index 038d7ba60e..0000000000 --- a/src/common/i2c.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "inttypes.h" -#include "stm32f4xx_hal.h" - -#ifdef __cplusplus - #include - -extern volatile std::atomic I2C_TRANSMIT_RESULTS_HAL_OK; -extern volatile std::atomic I2C_TRANSMIT_RESULTS_HAL_BUSY; - -extern volatile std::atomic I2C_RECEIVE_RESULTS_HAL_OK; -extern volatile std::atomic I2C_RECEIVE_RESULTS_HAL_BUSY; - -extern "C" { -#endif // __cplusplus - -/** - * @brief Transmit data on I2C, in case of error, throw redscreen - */ -extern void I2C_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout); - -/** - * @brief Transmit data on I2C, in case of error, just return it - */ -extern HAL_StatusTypeDef I2C_Transmit_ext(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout); - -extern void I2C_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout); - -#ifdef __cplusplus -} -#endif // __cplusplus diff --git a/src/common/i2c.hpp b/src/common/i2c.hpp new file mode 100644 index 0000000000..100c8f3cf6 --- /dev/null +++ b/src/common/i2c.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include "inttypes.h" +#include +#include + +namespace i2c { +namespace statistics { + uint32_t get_hal_ok(uint8_t channel); + uint32_t get_hal_busy(uint8_t channel); + uint32_t get_hal_error(uint8_t channel); + uint32_t get_hal_timeout(uint8_t channel); +} // namespace statistics + +// numbered because of touch read return value +enum class Result { + ok = 0, + error = 1, + busy_after_retries = 2, + timeout = 3 +}; + +class ChannelMutex { + void *mutex_handle; // osMutexId is void*, dont use osMutexId in header - lower dependency + + static osMutexId get_handle(I2C_HandleTypeDef &hi2c); + static void init_mutexes(); + +public: + [[nodiscard]] ChannelMutex(I2C_HandleTypeDef &hi2c); + ~ChannelMutex(); +}; + +[[nodiscard]] Result Transmit(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout); +[[nodiscard]] Result Receive(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout); + +[[nodiscard]] Result Mem_Write_8bit_Addr(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint8_t MemAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout); +[[nodiscard]] Result Mem_Read_8bit_Addr(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint8_t MemAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout); +[[nodiscard]] Result Mem_Write_16bit_Addr(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout); +[[nodiscard]] Result Mem_Read_16bit_Addr(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout); + +[[nodiscard]] Result IsDeviceReady(I2C_HandleTypeDef &hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout); + +} diff --git a/src/common/marlin_server.cpp b/src/common/marlin_server.cpp index 2162fab81e..df14c3cb22 100644 --- a/src/common/marlin_server.cpp +++ b/src/common/marlin_server.cpp @@ -1065,7 +1065,8 @@ bool heatbreak_fan_check() { ) { // Allow fan check only if fan had time to build up RPM (after CFanClt::rpm_stabilization) // CFanClt error states are checked in the end of each _server_print_loop() - if (Fans::heat_break(active_extruder).getState() == CFanCtl::running && !Fans::heat_break(active_extruder).getRPMIsOk()) { + auto hb_state = Fans::heat_break(active_extruder).getState(); + if ((hb_state == CFanCtl::running || hb_state == CFanCtl::error_running || hb_state == CFanCtl::error_starting) && !Fans::heat_break(active_extruder).getRPMIsOk()) { log_error(MarlinServer, "HeatBreak FAN RPM is not OK - Actual: %d rpm, PWM: %d", (int)Fans::heat_break(active_extruder).getActualRPM(), Fans::heat_break(active_extruder).getPWM()); diff --git a/src/common/marlin_server.hpp b/src/common/marlin_server.hpp index d795204d7f..a2644cf581 100644 --- a/src/common/marlin_server.hpp +++ b/src/common/marlin_server.hpp @@ -121,7 +121,7 @@ typedef struct float nozzle_temp[EXTRUDERS]; // resume nozzle temperature bool nozzle_temp_paused; // True if nozzle_temp is valid and hotend cools down uint8_t fan_speed; // resume fan speed - uint8_t print_speed; // resume printing speed + uint16_t print_speed; // resume printing speed } resume_state_t; // diff --git a/src/common/power_panic.cpp b/src/common/power_panic.cpp index f50390e45d..b6137c5fa7 100644 --- a/src/common/power_panic.cpp +++ b/src/common/power_panic.cpp @@ -107,18 +107,17 @@ struct flash_planner_t { int16_t extrude_min_temp; #if ENABLED(MODULAR_HEATBED) uint16_t enabled_bedlets_mask; + uint8_t _padding_heat[2]; // padding to 2 or 4 bytes? #endif + uint16_t print_speed; uint8_t was_paused; uint8_t was_crashed; uint8_t fan_speed; - uint8_t print_speed; uint8_t axis_relative; uint8_t allow_cold_extrude; -#if DISABLED(MODULAR_HEATBED) - uint8_t _padding[2]; -#endif + uint8_t _padding[1]; }; // fully independent state that persist across panics until the end of the print diff --git a/src/common/selftest/selftest_gears.cpp b/src/common/selftest/selftest_gears.cpp index ac281291c1..4d28882c91 100644 --- a/src/common/selftest/selftest_gears.cpp +++ b/src/common/selftest/selftest_gears.cpp @@ -158,6 +158,7 @@ LoopResult SelftestGears::state_filament_unload_wait_finished() { PreheatStatus::Result res = PreheatStatus::ConsumeResult(); if (res == PreheatStatus::Result::DoneNoFilament) { queue.enqueue_one_now("M104 S0"); // cool down the nozzle + queue.enqueue_one_now("M140 S0"); // cool down the heatbed return LoopResult::RunNext; } return LoopResult::GoToMark; diff --git a/src/common/sound.cpp b/src/common/sound.cpp index 2ea09acbbd..16033d320f 100644 --- a/src/common/sound.cpp +++ b/src/common/sound.cpp @@ -2,6 +2,160 @@ #include "hwio.h" #include +struct SoundPattern { + int8_t repeat; /// signals repeats - how many times will sound signals repeat (-1 is infinite) + int16_t delay; /// delays for repeat sounds (ms) + int16_t duration; /// durations for sound modes +}; + +static constexpr SoundPattern SILENCE = { 0, 0, 0 }; + +struct SoundSettings { + SoundPattern once; + SoundPattern loud; + SoundPattern silent; + SoundPattern assist; + float frequency; /// frequency of signals in ms + float volume; /// volumes of signals in ms + bool forced; /// forced types of sounds - mainly for ERROR sounds. Ignores volume settings. + + const SoundPattern &pattern(eSOUND_MODE mode) const { + switch (mode) { + case eSOUND_MODE::ONCE: + return once; + case eSOUND_MODE::LOUD: + return loud; + case eSOUND_MODE::SILENT: + return silent; + case eSOUND_MODE::ASSIST: + return assist; + default: + bsod("sound pattern"); + } + } +}; + +struct AllSoundSettings { + SoundSettings button_echo; + SoundSettings standard_prompt; + SoundSettings standard_alert; + SoundSettings critical_alert; + SoundSettings encoder_move; + SoundSettings blind_alert; + SoundSettings start; + SoundSettings single_beep; + SoundSettings waiting_beep; + + const SoundSettings &settings(eSOUND_TYPE type) const { + switch (type) { + case eSOUND_TYPE::ButtonEcho: + return button_echo; + case eSOUND_TYPE::StandardPrompt: + return standard_prompt; + case eSOUND_TYPE::StandardAlert: + return standard_alert; + case eSOUND_TYPE::CriticalAlert: + return critical_alert; + case eSOUND_TYPE::EncoderMove: + return encoder_move; + case eSOUND_TYPE::BlindAlert: + return blind_alert; + case eSOUND_TYPE::Start: + return start; + case eSOUND_TYPE::SingleBeep: + return single_beep; + case eSOUND_TYPE::WaitingBeep: + return waiting_beep; + default: + bsod("sound pattern"); + } + } +}; + +static constexpr AllSoundSettings all_sound_settings = { + .button_echo = { + .once = { 1, 1, 100 }, + .loud = { 1, 1, 100 }, + .silent = SILENCE, + .assist = { 1, 1, 100 }, + .frequency = 900.F, + .volume = Sound::volumeInit, + .forced = false, + }, + .standard_prompt = { + .once = { 1, 1, 500 }, + .loud = { -1, 1, 500 }, + .silent = SILENCE, + .assist = { -1, 1, 500 }, + .frequency = 600.F, + .volume = Sound::volumeInit, + .forced = false, + }, + .standard_alert = { + .once = SILENCE, + .loud = { 3, 1, 200 }, + .silent = { 1, 1, 200 }, + .assist = { 3, 1, 200 }, + .frequency = 950.F, + .volume = Sound::volumeInit, + .forced = false, + }, + .critical_alert = { + .once = { -1, 250, 500 }, + .loud = { -1, 250, 500 }, + .silent = { -1, 250, 500 }, + .assist = { -1, 250, 500 }, + .frequency = 999.F, + .volume = Sound::volumeInit, + .forced = true, + }, + .encoder_move = { + .once = SILENCE, + .loud = SILENCE, + .silent = SILENCE, + .assist = { 1, 1, 10 }, + .frequency = 800.F, + .volume = 0.175F, + .forced = false, + }, + .blind_alert = { + .once = SILENCE, + .loud = SILENCE, + .silent = SILENCE, + .assist = { 1, 1, 50 }, + .frequency = 500.F, + .volume = 0.175F, + .forced = false, + }, + .start = { + .once = { 1, 1, 100 }, + .loud = { 1, 1, 100 }, + .silent = { 1, 1, 100 }, + .assist = { 1, 1, 100 }, + .frequency = 999.F, + .volume = Sound::volumeInit, + .forced = false, + }, + .single_beep = { + .once = { 1, 1, 800 }, + .loud = { 1, 1, 800 }, + .silent = SILENCE, + .assist = { 1, 1, 800 }, + .frequency = 950.F, + .volume = Sound::volumeInit, + .forced = false, + }, + .waiting_beep = { + .once = { 1, 1, 800 }, + .loud = { -1, 2000, 100 }, + .silent = SILENCE, + .assist = { -1, 2000, 100 }, + .frequency = 800.F, + .volume = Sound::volumeInit, + .forced = false, + }, +}; + eSOUND_MODE Sound_GetMode() { return Sound::getInstance().getMode(); } int Sound_GetVolume() { return Sound::getInstance().getVolume(); } void Sound_SetMode(eSOUND_MODE eSMode) { Sound::getInstance().setMode(eSMode); } @@ -62,15 +216,11 @@ void Sound::stop() { delay_active = 0; } -void Sound::_playSound(eSOUND_TYPE sound, const eSOUND_TYPE types[], - const int8_t repeats[], const int16_t durations[], const int16_t delays[], unsigned size) { - for (unsigned i = 0; i < size; i++) { - eSOUND_TYPE type = types[i]; - if (type == sound) { - _sound(repeats[i], frequencies[(size_t)type], - durations[i], delays[i], volumes[(size_t)type], forced[(size_t)type]); - break; - } +void Sound::_playSound(eSOUND_TYPE type, eSOUND_MODE mode) { + const SoundSettings &settings = all_sound_settings.settings(type); + const SoundPattern &pattern = settings.pattern(mode); + if (pattern.duration) { + _sound(pattern.repeat, settings.frequency, pattern.duration, pattern.delay, settings.volume, settings.forced); } } @@ -79,7 +229,6 @@ void Sound::_playSound(eSOUND_TYPE sound, const eSOUND_TYPE types[], * Every mode handle just his own signal types. */ void Sound::play(eSOUND_TYPE eSoundType) { - int t_size = 0; eSOUND_MODE mode = eSoundMode; if (eSoundType == eSOUND_TYPE::CriticalAlert) @@ -87,27 +236,19 @@ void Sound::play(eSOUND_TYPE eSoundType) { switch (mode) { case eSOUND_MODE::ONCE: - t_size = sizeof(onceTypes) / sizeof(onceTypes[0]); - _playSound(eSoundType, onceTypes, onceRepeats, onceDurations, onceDelays, t_size); - break; case eSOUND_MODE::SILENT: - t_size = sizeof(silentTypes) / sizeof(silentTypes[0]); - _playSound(eSoundType, silentTypes, silentRepeats, silentDurations, silentDelays, t_size); - break; case eSOUND_MODE::ASSIST: - t_size = sizeof(assistTypes) / sizeof(assistTypes[0]); - _playSound(eSoundType, assistTypes, assistRepeats, assistDurations, assistDelays, t_size); - break; case eSOUND_MODE::LOUD: + _playSound(eSoundType, mode); + break; default: - t_size = sizeof(loudTypes) / sizeof(loudTypes[0]); - _playSound(eSoundType, loudTypes, loudRepeats, loudDurations, loudDelays, t_size); + _playSound(eSoundType, eSOUND_MODE::LOUD); break; } } /// Generic [_sound] method with setting values and repeating logic -void Sound::_sound(int rep, float frq, int16_t dur, int16_t del, [[maybe_unused]] float vol, [[maybe_unused]] bool f) { +void Sound::_sound(int rep, float frq, int16_t dur, int16_t del, [[maybe_unused]] float vol, bool f) { /// forced non-repeat sounds - can be played when another /// repeating sound is playing float tmpVol; @@ -119,7 +260,7 @@ void Sound::_sound(int rep, float frq, int16_t dur, int16_t del, [[maybe_unused] tmpVol = f ? 0.3F : (vol * varVolume) * 0.3F; } #else - tmpVol = varVolume; + tmpVol = f ? volumeInit : varVolume; #endif if (rep == 1) { singleSound(frq, dur, tmpVol); diff --git a/src/common/sound.hpp b/src/common/sound.hpp index 42ef21591b..906b00e2f2 100644 --- a/src/common/sound.hpp +++ b/src/common/sound.hpp @@ -52,7 +52,7 @@ class Sound { void saveMode(); void saveVolume(); // + one louder void _sound(int rep, float frq, int16_t dur, int16_t del, float vol, bool f); - void _playSound(eSOUND_TYPE sound, const eSOUND_TYPE types[], const int8_t repeats[], const int16_t durations[], const int16_t delays[], unsigned size); + void _playSound(eSOUND_TYPE type, eSOUND_MODE mode); void nextRepeat(); float real_volume(int displayed_volume); ///< converts displayed / saved volume to volume used by beeper uint8_t displayed_volume(float real_volume); ///< converts beeper volume to displayed / saved one @@ -67,86 +67,7 @@ class Sound { int16_t delay_set = 100; ///< added variable for delay between beeps eSOUND_MODE eSoundMode = eSOUND_MODE::DEFAULT_SOUND; ///< current mode +public: /// main constant of main volume which is maximal volume that we allow static constexpr float volumeInit = 0.35F; - - /// frequency of signals in ms - static constexpr float frequencies[eSOUND_TYPE::count] = { 900.F, 600.F, 950.F, 999.F, 800.F, 500.F, 999.F, 950.F, 800.F }; - - /// volumes of signals in ms - static constexpr float volumes[eSOUND_TYPE::count] = { - Sound::volumeInit, - Sound::volumeInit, - Sound::volumeInit, - Sound::volumeInit, - 0.175F, - 0.175F, - Sound::volumeInit, - Sound::volumeInit, - Sound::volumeInit - }; - - /// forced types of sounds - mainly for ERROR sounds. Ignores volume settings. - static constexpr bool forced[eSOUND_TYPE::count] = { false, false, false, true, false, false, false, false, false }; - - /// array of usable types (eSOUND_TYPE) of every sound modes (eSOUND_MODE) - static constexpr eSOUND_TYPE onceTypes[] = { - eSOUND_TYPE::Start, - eSOUND_TYPE::ButtonEcho, - eSOUND_TYPE::StandardPrompt, - eSOUND_TYPE::CriticalAlert, - eSOUND_TYPE::SingleBeep, - eSOUND_TYPE::WaitingBeep - }; - static constexpr eSOUND_TYPE loudTypes[] = { - eSOUND_TYPE::Start, - eSOUND_TYPE::ButtonEcho, - eSOUND_TYPE::StandardPrompt, - eSOUND_TYPE::StandardAlert, - eSOUND_TYPE::CriticalAlert, - eSOUND_TYPE::SingleBeep, - eSOUND_TYPE::WaitingBeep - }; - static constexpr eSOUND_TYPE silentTypes[] = { - eSOUND_TYPE::Start, - eSOUND_TYPE::StandardAlert, - eSOUND_TYPE::CriticalAlert - }; - static constexpr eSOUND_TYPE assistTypes[] = { - eSOUND_TYPE::Start, - eSOUND_TYPE::ButtonEcho, - eSOUND_TYPE::StandardPrompt, - eSOUND_TYPE::StandardAlert, - eSOUND_TYPE::EncoderMove, - eSOUND_TYPE::BlindAlert, - eSOUND_TYPE::CriticalAlert, - eSOUND_TYPE::SingleBeep, - eSOUND_TYPE::WaitingBeep - }; - - /// signals repeats - how many times will sound signals repeat (-1 is infinite) - static constexpr int8_t onceRepeats[] = { 1, 1, 1, -1, 1, 1 }; - static constexpr int8_t loudRepeats[] = { 1, 1, -1, 3, -1, 1, -1 }; - static constexpr int8_t silentRepeats[] = { 1, 1, -1 }; - static constexpr int8_t assistRepeats[] = { 1, 1, -1, 3, 1, 1, -1, 1, -1 }; - - /// delays for repeat sounds (ms) - static constexpr int16_t onceDelays[] = { 1, 1, 1, 250, 1 }; - static constexpr int16_t loudDelays[] = { 1, 1, 1, 1, 250, 1, 2000 }; - static constexpr int16_t silentDelays[] = { 1, 1, 250 }; - static constexpr int16_t assistDelays[] = { 1, 1, 1, 1, 1, 1, 250, 1, 2000 }; - - /// durations for sound modes - static constexpr int16_t onceDurations[] = { - 100, 100, 500, 500, 800, 800 - }; - static constexpr int16_t loudDurations[] = { - 100, 100, 500, 200, 500, 800, 100 - }; - static constexpr int16_t silentDurations[] = { - 100, 200, 500 - }; - static constexpr int16_t assistDurations[] = { - 100, 100, 500, 200, 10, 50, 500, 800, 100 - }; }; diff --git a/src/common/sound_enum.h b/src/common/sound_enum.h index abc591845f..f56cad8b8e 100644 --- a/src/common/sound_enum.h +++ b/src/common/sound_enum.h @@ -11,7 +11,7 @@ enum class eSOUND_MODE : uint8_t { DEFAULT_SOUND = LOUD }; -enum eSOUND_TYPE : uint8_t { +enum class eSOUND_TYPE : uint8_t { ButtonEcho, StandardPrompt, StandardAlert, @@ -21,5 +21,4 @@ enum eSOUND_TYPE : uint8_t { Start, SingleBeep, WaitingBeep, - count }; diff --git a/src/common/st25dv64k.cpp b/src/common/st25dv64k.cpp index e40a8cb057..ef8bb95413 100644 --- a/src/common/st25dv64k.cpp +++ b/src/common/st25dv64k.cpp @@ -1,26 +1,30 @@ // st25dv64k.c #include "st25dv64k.h" -#include "i2c.h" +#include "i2c.hpp" #include #include "cmsis_os.h" #include "main.h" #include "metric.h" #include "SEGGER_SYSVIEW.h" - +#include "bsod_gui.hpp" +#include "utility_extensions.hpp" +#include #define ST25DV64K_RTOS +using namespace i2c; + // system config address registr // static const uint16_t REG_GPO = 0x0000; // static const uint16_t REG_IT_TIME = 0x0001; // static const uint16_t REG_EH_MODE = 0x0002; // static const uint16_t REG_RF_MNGT = 0x0003; // static const uint16_t REG_RFA1SS = 0x0004; -static const uint16_t REG_ENDA1 = 0x0005; +static constexpr uint16_t REG_ENDA1 = 0x0005; // static const uint16_t REG_RFA2SS = 0x0006; -static const uint16_t REG_ENDA2 = 0x0007; +static constexpr uint16_t REG_ENDA2 = 0x0007; // static const uint16_t REG_RFA3SS = 0x0008; -static const uint16_t REG_ENDA3 = 0x0009; +static constexpr uint16_t REG_ENDA3 = 0x0009; // static const uint16_t REG_RFA4SS = 0x000A; // static const uint16_t REG_I2CSS = 0x000B; // static const uint16_t REG_LOCK_CCFILE = 0x000C; @@ -29,15 +33,39 @@ static const uint16_t REG_ENDA3 = 0x0009; // static const uint16_t REG_LOCK_CFG = 0x000F; // EEPROM I2C addresses -static const uint16_t ADDR_WRITE = 0xA6; -static const uint16_t ADDR_READ = 0xA7; -static const uint16_t ADDR_WRITE_SYS = 0xAE; -static const uint16_t ADDR_READ_SYS = 0xAF; +enum class EepromCommand : bool { + memory, + registers +}; + +enum class EepromCommandWrite : uint16_t { + addr_memory = 0xA6, + addr_registers = 0xAE +}; + +enum class EepromCommandRead : uint16_t { + addr_memory = 0xA7, + addr_registers = 0xAF +}; + +static constexpr EepromCommandWrite eeprom_get_write_address(EepromCommand cmd) { + if (cmd == EepromCommand::memory) { + return EepromCommandWrite::addr_memory; + } + return EepromCommandWrite::addr_registers; +} -static const uint8_t BLOCK_DELAY = 5; // block delay [ms] -static const uint8_t BLOCK_BYTES = 4; // bytes per block +static constexpr EepromCommandRead eeprom_get_read_address(EepromCommand cmd) { + if (cmd == EepromCommand::memory) { + return EepromCommandRead::addr_memory; + } + return EepromCommandRead::addr_registers; +} + +static constexpr uint8_t BLOCK_DELAY = 5; // block delay [ms] +static constexpr uint8_t BLOCK_BYTES = 4; // bytes per block -static const uint8_t WRITE_RETRIES = 3; +static constexpr uint32_t RETRIES = 3; #define DELAY HAL_Delay @@ -92,115 +120,185 @@ void st25dv64k_init(void) { } } -uint8_t st25dv64k_user_read(uint16_t address) { - uint8_t _out[2] = { static_cast(address >> 8), static_cast(address & 0xff) }; - uint8_t data; - st25dv64k_lock(); - I2C_Transmit(&I2C_HANDLE_FOR(eeprom), ADDR_WRITE, _out, 2, HAL_MAX_DELAY); - I2C_Receive(&I2C_HANDLE_FOR(eeprom), ADDR_READ, &data, 1, HAL_MAX_DELAY); - st25dv64k_unlock(); - return data; +static void rise_error_if_needed(Result result) { + switch (result) { + case i2c::Result::ok: + break; + case i2c::Result::busy_after_retries: + fatal_error(ErrCode::ERR_ELECTRO_I2C_TX_BUSY); + break; + case i2c::Result::error: + fatal_error(ErrCode::ERR_ELECTRO_I2C_TX_ERROR); + break; + case i2c::Result::timeout: + fatal_error(ErrCode::ERR_ELECTRO_I2C_TX_TIMEOUT); + break; + } } -static void st25dv64k_user_write_unchecked(uint16_t address, uint8_t data) { - uint8_t _out[3] = { static_cast(address >> 8), static_cast(address & 0xff), data }; - st25dv64k_lock(); - I2C_Transmit(&I2C_HANDLE_FOR(eeprom), ADDR_WRITE, _out, 3, HAL_MAX_DELAY); - DELAY(BLOCK_DELAY); - st25dv64k_unlock(); // unlock must be here because other threads cannot access eeprom while writing/waiting +static void try_fix_if_needed(Result result) { + switch (result) { + case i2c::Result::busy_after_retries: + case i2c::Result::error: + I2C_INIT(eeprom); + [[fallthrough]]; + case i2c::Result::timeout: + case i2c::Result::ok: + break; + } } -void st25dv64k_user_write(uint16_t address, uint8_t data) { - for (uint8_t i = 0; i < WRITE_RETRIES; ++i) { - st25dv64k_user_write_unchecked(address, data); - const uint8_t data_read = st25dv64k_user_read(address); - if (data_read == data) { - break; - } - } +[[nodiscard]] static Result eeprom_transmit(EepromCommandWrite cmd, uint8_t *pData, uint16_t size) { + return Transmit(I2C_HANDLE_FOR(eeprom), ftrstd::to_underlying(cmd), pData, size, HAL_MAX_DELAY); } -void st25dv64k_user_read_bytes(uint16_t address, void *pdata, uint16_t size) { - if (size == 0) { - return; - } - uint8_t _out[2] = { static_cast(address >> 8), static_cast(address & 0xff) }; - st25dv64k_lock(); - I2C_Transmit(&I2C_HANDLE_FOR(eeprom), ADDR_WRITE, _out, 2, HAL_MAX_DELAY); - I2C_Receive(&I2C_HANDLE_FOR(eeprom), ADDR_READ, static_cast(pdata), size, HAL_MAX_DELAY); - st25dv64k_unlock(); +[[nodiscard]] static Result user_write_address_without_lock(EepromCommandWrite cmd, uint16_t address) { + uint8_t _out[sizeof(address)]; + _out[0] = address >> 8; + _out[1] = address & 0xff; + + Result result = eeprom_transmit(cmd, _out, sizeof(address)); + DELAY(BLOCK_DELAY); + + return result; } -static void st25dv64k_user_write_bytes_unchecked(uint16_t address, void const *pdata, uint16_t size) { +[[nodiscard]] static Result user_write_bytes_without_lock(EepromCommandWrite cmd, uint16_t address, void const *pdata, uint16_t size) { + if (size == 0 || pdata == nullptr) { + return user_write_address_without_lock(cmd, address); + } + static metric_t metric_eeprom_write = METRIC("eeprom_write", METRIC_VALUE_EVENT, 0, METRIC_HANDLER_ENABLE_ALL); metric_record_event(&metric_eeprom_write); + uint8_t const *p = (uint8_t const *)pdata; - uint8_t _out[6]; - uint8_t block_size; - st25dv64k_lock(); + uint8_t _out[sizeof(address) + BLOCK_BYTES]; while (size) { - block_size = BLOCK_BYTES - (address % BLOCK_BYTES); + uint8_t block_size = BLOCK_BYTES - (address % BLOCK_BYTES); if (block_size > size) block_size = size; _out[0] = address >> 8; _out[1] = address & 0xff; - memcpy(_out + 2, p, block_size); - I2C_Transmit(&I2C_HANDLE_FOR(eeprom), ADDR_WRITE, _out, 2 + block_size, HAL_MAX_DELAY); + memcpy(_out + sizeof(address), p, block_size); + + Result result = eeprom_transmit(cmd, _out, sizeof(address) + block_size); + if (result != Result::ok) { + return result; + } + DELAY(BLOCK_DELAY); + size -= block_size; address += block_size; p += block_size; } - st25dv64k_unlock(); // unlock must be here because other threads cannot access eeprom while writing/waiting + return Result::ok; } -static inline uint16_t min(const uint16_t a, const uint16_t b) { - return a < b ? a : b; +[[nodiscard]] static Result user_read_bytes_without_lock(EepromCommand cmd, uint16_t address, void *pdata, uint16_t size) { + if (size == 0) { + return Result::ok; + } + + Result result = user_write_address_without_lock(eeprom_get_write_address(cmd), address); + if (result == Result::ok) { + result = Receive(I2C_HANDLE_FOR(eeprom), ftrstd::to_underlying(eeprom_get_read_address(cmd)), static_cast(pdata), size, HAL_MAX_DELAY); + } + + return result; } -void st25dv64k_user_write_bytes(uint16_t address, void const *pdata, uint16_t size) { +static void user_read_bytes(EepromCommand cmd, uint16_t address, void *pdata, uint16_t size) { if (size == 0) { return; } - for (uint8_t i = 0; i < WRITE_RETRIES; ++i) { - st25dv64k_user_write_bytes_unchecked(address, pdata, size); - - // Verify the data being written correctly - uint8_t chunk_read[32]; - uint16_t read_pos = 0; - bool match = true; - while (read_pos < size) { - uint16_t to_read = min(sizeof(chunk_read), size - read_pos); - st25dv64k_user_read_bytes(address + read_pos, chunk_read, to_read); - if (memcmp(chunk_read, ((uint8_t *)pdata) + read_pos, to_read)) { - match = false; + + i2c::Result result = i2c::Result::error; + + // receive retry requires new transmit + for (uint32_t try_no = 0; (result != Result::ok) && (try_no < RETRIES); ++try_no) { + st25dv64k_lock(); + + result = user_read_bytes_without_lock(cmd, address, pdata, size); + + st25dv64k_unlock(); + + try_fix_if_needed(result); + } + + rise_error_if_needed(result); +} + +void st25dv64k_user_read_bytes(uint16_t address, void *pdata, uint16_t size) { + user_read_bytes(EepromCommand::memory, address, pdata, size); +} + +uint8_t st25dv64k_user_read(uint16_t address) { + uint8_t data; + st25dv64k_user_read_bytes(address, &data, sizeof(data)); + return data; +} + +static void user_write_bytes(EepromCommand cmd, uint16_t address, void const *pdata, uint16_t size) { + if (size == 0) { + return; + } + + i2c::Result result = i2c::Result::error; + bool match = false; + + for (uint32_t try_no = 0; try_no < RETRIES; ++try_no) { + + st25dv64k_lock(); + result = user_write_bytes_without_lock(EepromCommandWrite::addr_memory, address, pdata, size); + if (result == Result::ok) { + // Verify the data being written correctly + uint8_t chunk_read[32]; + uint16_t read_pos = 0; + match = true; + while (read_pos < size) { + uint16_t to_read = std::min(sizeof(chunk_read), size - read_pos); + result = user_read_bytes_without_lock(cmd, address + read_pos, chunk_read, to_read); + if (result != Result::ok) { + break; + } + if (memcmp(chunk_read, ((uint8_t *)pdata) + read_pos, to_read)) { + match = false; + break; + } + read_pos += to_read; } - read_pos += to_read; } + st25dv64k_unlock(); + + try_fix_if_needed(result); + match = match && result == Result::ok; // Stop retrying on match if (match) { break; } } + + rise_error_if_needed(result); +} + +void st25dv64k_user_write_bytes(uint16_t address, void const *pdata, uint16_t size) { + user_write_bytes(EepromCommand::memory, address, pdata, size); +} + +void st25dv64k_user_write(uint16_t address, uint8_t data) { + st25dv64k_user_write_bytes(address, &data, sizeof(data)); } uint8_t st25dv64k_rd_cfg(uint16_t address) { - uint8_t _out[2] = { static_cast(address >> 8), static_cast(address & 0xff) }; uint8_t data; - st25dv64k_lock(); - I2C_Transmit(&I2C_HANDLE_FOR(eeprom), ADDR_WRITE_SYS, _out, 2, HAL_MAX_DELAY); - I2C_Receive(&I2C_HANDLE_FOR(eeprom), ADDR_READ_SYS, &data, 1, HAL_MAX_DELAY); - st25dv64k_unlock(); + user_read_bytes(EepromCommand::registers, address, &data, sizeof(data)); return data; } void st25dv64k_wr_cfg(uint16_t address, uint8_t data) { - uint8_t _out[3] = { static_cast(address >> 8), static_cast(address & 0xff), data }; - st25dv64k_lock(); - I2C_Transmit(&I2C_HANDLE_FOR(eeprom), ADDR_WRITE_SYS, _out, 3, HAL_MAX_DELAY); - DELAY(BLOCK_DELAY); - st25dv64k_unlock(); // unlock must be here because other threads cannot access eeprom while writing/waiting + user_write_bytes(EepromCommand::registers, address, &data, sizeof(data)); } void st25dv64k_present_pwd(uint8_t *pwd) { @@ -209,7 +307,15 @@ void st25dv64k_present_pwd(uint8_t *pwd) { memcpy(_out + 2, pwd, 8); memcpy(_out + 11, pwd, 8); } - st25dv64k_lock(); - I2C_Transmit(&I2C_HANDLE_FOR(eeprom), ADDR_WRITE_SYS, _out, 19, HAL_MAX_DELAY); - st25dv64k_unlock(); + + i2c::Result result = i2c::Result::error; + + for (uint32_t try_no = 0; (result != Result::ok) && (try_no < RETRIES); ++try_no) { + st25dv64k_lock(); + result = eeprom_transmit(EepromCommandWrite::addr_registers, _out, sizeof(_out)); + st25dv64k_unlock(); + try_fix_if_needed(result); + } + + rise_error_if_needed(result); } diff --git a/src/device/stm32f4/hal_msp.cpp b/src/device/stm32f4/hal_msp.cpp index 88cec85b61..53f4d0b64b 100644 --- a/src/device/stm32f4/hal_msp.cpp +++ b/src/device/stm32f4/hal_msp.cpp @@ -194,68 +194,25 @@ void HAL_ADC_MspDeInit(ADC_HandleTypeDef *hadc) { /** * @brief I2C MSP Initialization * This function configures the hardware resources used in this example + * Currently replaced by hw_i2cX_pins_init functions in peripherals.cpp + * It calls those functions to preserve functionality of HAL_I2C_Init * @param hi2c: I2C handle pointer * @retval None */ void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c) { - - GPIO_InitTypeDef GPIO_InitStruct {}; - -#if (BOARD_IS_XBUDDY || BOARD_IS_XLBUDDY) +#if HAS_I2CN(1) + if (hi2c->Instance == I2C1) { + hw_i2c1_pins_init(); + } +#endif +#if HAS_I2CN(2) if (hi2c->Instance == I2C2) { - static_assert(i2c2_SDA_PORT_BASE == GPIOF_BASE, "fix i2c2 sda port"); - static_assert(i2c2_SCL_PORT_BASE == GPIOF_BASE, "fix i2c2 scl port"); - __HAL_RCC_GPIOF_CLK_ENABLE(); - - GPIO_InitStruct.Pin = i2c2_SDA_PIN | i2c2_SCL_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; - GPIO_InitStruct.Pull = GPIO_PULLUP; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF4_I2C2; - HAL_GPIO_Init(i2c2_SDA_PORT, &GPIO_InitStruct); - - /* Peripheral clock enable */ - __HAL_RCC_I2C2_CLK_ENABLE(); - } else if (hi2c->Instance == I2C3) { - static_assert(i2c3_SDA_PORT_BASE == GPIOC_BASE, "fix i2c3 sda port"); - static_assert(i2c3_SCL_PORT_BASE == GPIOA_BASE, "fix i2c3 scl port"); - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOA_CLK_ENABLE(); - - GPIO_InitStruct.Pin = i2c3_SDA_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; - GPIO_InitStruct.Pull = GPIO_PULLUP; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF4_I2C3; - HAL_GPIO_Init(i2c3_SDA_PORT, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = i2c3_SCL_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; - GPIO_InitStruct.Pull = GPIO_PULLUP; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF4_I2C3; - HAL_GPIO_Init(i2c3_SCL_PORT, &GPIO_InitStruct); - - /* Peripheral clock enable */ - __HAL_RCC_I2C3_CLK_ENABLE(); + hw_i2c2_pins_init(); } #endif - -#if (BOARD_IS_BUDDY || BOARD_IS_XLBUDDY) - if (hi2c->Instance == I2C1) { - static_assert(i2c1_SDA_PORT_BASE == GPIOB_BASE, "fix i2c3 sda port"); - static_assert(i2c1_SCL_PORT_BASE == GPIOB_BASE, "fix i2c3 scl port"); - __HAL_RCC_GPIOB_CLK_ENABLE(); - - GPIO_InitStruct.Pin = i2c1_SDA_PIN | i2c1_SCL_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; - GPIO_InitStruct.Pull = GPIO_PULLUP; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; - HAL_GPIO_Init(i2c1_SDA_PORT, &GPIO_InitStruct); - - /* Peripheral clock enable */ - __HAL_RCC_I2C1_CLK_ENABLE(); +#if HAS_I2CN(3) + if (hi2c->Instance == I2C3) { + hw_i2c3_pins_init(); } #endif } diff --git a/src/device/stm32f4/peripherals.cpp b/src/device/stm32f4/peripherals.cpp index 9e391fa070..261e4933a4 100644 --- a/src/device/stm32f4/peripherals.cpp +++ b/src/device/stm32f4/peripherals.cpp @@ -9,6 +9,11 @@ #include "timer_defaults.h" #include "PCA9557.hpp" #include "log.h" +#include "timing_precise.hpp" + +// breakpoint +#include "FreeRTOS.h" +#include "task.h" // // I2C @@ -414,155 +419,137 @@ void hw_uart8_init() { } #endif -LOG_COMPONENT_DEF(I2C, LOG_SEVERITY_INFO); - -static void wait_for_pin(int &workaround_step, GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState pin_state, uint32_t max_wait_us = 128) { - volatile uint32_t start_us = ticks_us(); - while (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) != pin_state) { - volatile uint32_t now = ticks_us(); - if (((now - start_us) > max_wait_us) && HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) != pin_state) { - log_error(I2C, "not operational, busy reset step %d", workaround_step++); - return; - } - } -} -#if HAS_I2CN(1) -static void hw_i2c1_base_init(); -#endif -#if HAS_I2CN(2) -static void hw_i2c2_base_init(); -#endif -#if HAS_I2CN(3) -static void hw_i2c3_base_init(); -#endif +struct hw_pin { + GPIO_TypeDef *port; + uint16_t no; +}; /** - * @brief I2C is busy and does not communicate - * I have found similar issue in STM32F1 error datasheet (but not in STM32F4) - * Description - * The I2C analog filters embedded in the I2C I/Os may be tied to low level, whereas SCL and SDA lines are kept at - * high level. This can occur after an MCU power-on reset, or during ESD stress. Consequently, the I2C BUSY flag - * is set, and the I2C cannot enter master mode (START condition cannot be sent). The I2C BUSY flag cannot be - * cleared by the SWRST control bit, nor by a peripheral or a system reset. BUSY bit is cleared under reset, but it - * is set high again as soon as the reset is released, because the analog filter output is still at low level. This issue - * occurs randomly. - * - * Note: Under the same conditions, the I2C analog filters may also provide a high level, whereas SCL and SDA lines are - * kept to low level. This should not create issues as the filters output is correct after next SCL and SDA transition. - * - * - * Workaround - * The SCL and SDA analog filter output is updated after a transition occurs on the SCL and SDA line respectively. - * The SCL and SDA transition can be forced by software configuring the I2C I/Os in output mode. Then, once the - * analog filters are unlocked and output the SCL and SDA lines level, the BUSY flag can be reset with a software - * reset, and the I2C can enter master mode. Therefore, the following sequence must be applied: + * @brief Set the pin to open-drain */ -static void i2c_busy_flag_error_workaround(I2C_HandleTypeDef *hi2c, GPIO_TypeDef *SDA_PORT, uint32_t SDA_PIN, GPIO_TypeDef *SCL_PORT, uint32_t SCL_PIN, void (*init_fn)()) { - int workaround_step = 0; - - GPIO_InitTypeDef GPIO_InitStruct; - - // 1. Disable the I2C peripheral by clearing the PE bit in I2Cx_CR1 register. - __HAL_I2C_DISABLE(hi2c); - - // 2. Configure the SCL and SDA I/Os as General Purpose Output Open-Drain, High level (Write 1 to GPIOx_ODR). - GPIO_InitStruct.Pin = SDA_PIN; +static void set_pin_od(hw_pin pin) { + GPIO_InitTypeDef GPIO_InitStruct {}; + GPIO_InitStruct.Pin = pin.no; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FAST; - HAL_GPIO_Init(SDA_PORT, &GPIO_InitStruct); + GPIO_InitStruct.Alternate = 0; + HAL_GPIO_Init(pin.port, &GPIO_InitStruct); +} - GPIO_InitStruct.Pin = SCL_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; +/** + * @brief Set the pin to input + */ +static void set_pin_in(hw_pin pin) { + GPIO_InitTypeDef GPIO_InitStruct {}; + GPIO_InitStruct.Pin = pin.no; + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FAST; - HAL_GPIO_Init(SCL_PORT, &GPIO_InitStruct); - - HAL_GPIO_WritePin(SDA_PORT, SDA_PIN, GPIO_PIN_SET); - HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, GPIO_PIN_SET); - - // 3. Check SCL and SDA High level in GPIOx_IDR. - wait_for_pin(workaround_step, SDA_PORT, SDA_PIN, GPIO_PIN_SET); - wait_for_pin(workaround_step, SCL_PORT, SCL_PIN, GPIO_PIN_SET); - - // 4. Configure the SDA I/O as General Purpose Output Open-Drain, Low level (Write 0 to GPIOx_ODR). - GPIO_InitStruct.Pin = SDA_PIN; - HAL_GPIO_Init(SDA_PORT, &GPIO_InitStruct); - - HAL_GPIO_TogglePin(SDA_PORT, SDA_PIN); - - // 5. Check SDA Low level in GPIOx_IDR. - wait_for_pin(workaround_step, SDA_PORT, SDA_PIN, GPIO_PIN_RESET); + GPIO_InitStruct.Alternate = 0; + HAL_GPIO_Init(pin.port, &GPIO_InitStruct); +} - // 6. Configure the SCL I/O as General Purpose Output Open-Drain, Low level (Write 0 to GPIOx_ODR). - GPIO_InitStruct.Pin = SCL_PIN; - HAL_GPIO_Init(SCL_PORT, &GPIO_InitStruct); +/** + * @brief calculate edge timing + * edges timing is of half period + * round up + * + * @param clk frequency [Hz] + * @return constexpr uint32_t half of period [us] + */ +static constexpr uint32_t i2c_get_edge_us(uint32_t clk) { + // clk + 1 .. round up + // / 2 .. need half of period + return (1'000'000 % clk ? (1'000'000 / clk + 1) : (1'000'000 / clk)) / 2; +} - HAL_GPIO_TogglePin(SCL_PORT, SCL_PIN); +/** + * @brief unblock i2c data pin + * apply up to 9 clock pulses and check SDA logical level + * make sure it is in '1', so master can manipulate with it + * in case HW is OK it is ensured that 9 pulses is enough + * because in the worse case scenario 9th pulse would be expected to be ACK from master + * + * @param clk frequency [Hz] + * @param sda pin of data + * @param scl pin of clock + */ +static void i2c_unblock_sda(uint32_t clk, hw_pin sda, hw_pin scl) { + delay_us_precise(i2c_get_edge_us(clk)); // half period - ensure first edge is not too short + for (size_t i = 0; i < 9; ++i) { // 9 pulses, there is no point to try it more times - 9th bit is ACK (will be NACK) + HAL_GPIO_WritePin(scl.port, scl.no, GPIO_PIN_SET); // set clock to '1' + delay_us_precise(i2c_get_edge_us(clk)); // wait half period + if (HAL_GPIO_ReadPin(sda.port, sda.no) == GPIO_PIN_SET) { // check if slave does not pull SDA to '0' while SCL == 1 + return; // sda is not pulled by a slave, it is done + } - // 7. Check SCL Low level in GPIOx_IDR. - wait_for_pin(workaround_step, SCL_PORT, SCL_PIN, GPIO_PIN_RESET); + HAL_GPIO_WritePin(scl.port, scl.no, GPIO_PIN_RESET); // set clock to '0' + delay_us_precise(i2c_get_edge_us(clk)); // wait half period + } - // 8. Configure the SCL I/O as General Purpose Output Open-Drain, High level (Write 1 to GPIOx_ODR). - GPIO_InitStruct.Pin = SDA_PIN; - HAL_GPIO_Init(SDA_PORT, &GPIO_InitStruct); + // in case code reaches this, there is some HW issue + // but we cannot log it or rise red screen, it is too early +#ifdef _DEBUG + // Breakpoint if debugger is connected + if (CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) { + __BKPT(0); + } +#endif + HAL_GPIO_WritePin(scl.port, scl.no, GPIO_PIN_SET); // this code should never be reached, just in case it was set clock to '1' +} - HAL_GPIO_WritePin(SDA_PORT, SDA_PIN, GPIO_PIN_SET); +/** + * @brief free I2C in case of slave deadlock + * in case printer is resetted during I2C transmit, slave can deadlock + * it has not been resetted and is expecting clock to finish its command + * problem is that it can hold SDA in '0' - it blocks the bus so master cannot do start / stop condition + * this code generates a clock until SDA is in '1' and than master sdoes start + stop to end any slave communication + * + * @param clk frequency [Hz] + * @param sda pin of data + * @param scl pin of clock + */ +static void i2c_free_bus_in_case_of_slave_deadlock(uint32_t clk, hw_pin sda, hw_pin scl) { + set_pin_in(sda); // configure SDA to input + if (HAL_GPIO_ReadPin(sda.port, sda.no) == GPIO_PIN_RESET) { // check if slave pulls SDA to '0' while SCL == 1 + set_pin_od(scl); // configure SCL to open-drain + i2c_unblock_sda(clk, sda, scl); // get SDA pin in state pin can be "moved" + } + + set_pin_od(sda); // reconfigure SDA to open-drain, to be able to move it + HAL_GPIO_WritePin(sda.port, sda.no, GPIO_PIN_RESET); // set SDA to '0' while SCL == '1' - start condition + delay_us_precise(i2c_get_edge_us(clk)); // wait half period + HAL_GPIO_WritePin(sda.port, sda.no, GPIO_PIN_RESET); // set SDA to '1' while SCL == '1' - stop condition + delay_us_precise(i2c_get_edge_us(clk)); // wait half period +} - // 9. Check SCL High level in GPIOx_IDR. - wait_for_pin(workaround_step, SDA_PORT, SDA_PIN, GPIO_PIN_SET); +#if HAS_I2CN(1) - // 10. Configure the SDA I/O as General Purpose Output Open-Drain , High level (Write 1 to GPIOx_ODR). - GPIO_InitStruct.Pin = SCL_PIN; - HAL_GPIO_Init(SCL_PORT, &GPIO_InitStruct); +static constexpr uint32_t i2c1_speed = 400'000; - HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, GPIO_PIN_SET); +void hw_i2c1_pins_init() { + GPIO_InitTypeDef GPIO_InitStruct {}; - // 11. Check SDA High level in GPIOx_IDR. - wait_for_pin(workaround_step, SCL_PORT, SCL_PIN, GPIO_PIN_SET); + __HAL_RCC_GPIOB_CLK_ENABLE(); - // 12. Configure the SCL and SDA I/Os as Alternate function Open-Drain. - GPIO_InitStruct.Pin = SDA_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; - GPIO_InitStruct.Alternate = 0x04; // 4 == I2C - HAL_GPIO_Init(SDA_PORT, &GPIO_InitStruct); + i2c_free_bus_in_case_of_slave_deadlock(i2c1_speed, { i2c1_SDA_PORT, i2c1_SDA_PIN }, { i2c1_SCL_PORT, i2c1_SCL_PIN }); - GPIO_InitStruct.Pin = SCL_PIN; + // GPIO I2C mode + GPIO_InitStruct.Pin = i2c1_SDA_PIN | i2c1_SCL_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; - GPIO_InitStruct.Alternate = 0x04; // 4 == I2C - HAL_GPIO_Init(SCL_PORT, &GPIO_InitStruct); - - // 13. Set SWRST bit in I2Cx_CR1 register. - hi2c->Instance->CR1 |= I2C_CR1_SWRST; - - // 14. Clear SWRST bit in I2Cx_CR1 register. - hi2c->Instance->CR1 ^= I2C_CR1_SWRST; - - // need extra step - call init fnc - init_fn(); + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; + HAL_GPIO_Init(i2c1_SDA_PORT, &GPIO_InitStruct); - // 15. Enable the I2C peripheral by setting the PE bit in I2Cx_CR1 register. - // this step is done during I2C init function - __HAL_I2C_ENABLE(hi2c); + // Peripheral clock enable + __HAL_RCC_I2C1_CLK_ENABLE(); } -/// call busy flag reset function -#define I2C_BUSY_FLAG_ERROR_WORKAROUND(i2c_num) i2c_busy_flag_error_workaround(&hi2c##i2c_num, \ - i2c##i2c_num##_SDA_PORT, i2c##i2c_num##_SDA_PIN, \ - i2c##i2c_num##_SCL_PORT, i2c##i2c_num##_SCL_PIN, hw_i2c##i2c_num##_base_init) - -static std::atomic i2c1_busy_clear_count = 0; -static std::atomic i2c2_busy_clear_count = 0; -static std::atomic i2c3_busy_clear_count = 0; - -size_t hw_i2c1_get_busy_clear_count() { return i2c1_busy_clear_count.load(); } -size_t hw_i2c2_get_busy_clear_count() { return i2c2_busy_clear_count.load(); } -size_t hw_i2c3_get_busy_clear_count() { return i2c3_busy_clear_count.load(); } - -#if HAS_I2CN(1) -void hw_i2c1_base_init() { +void hw_i2c1_init() { hi2c1.Instance = I2C1; - hi2c1.Init.ClockSpeed = 400000; + hi2c1.Init.ClockSpeed = i2c1_speed; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; @@ -586,21 +573,34 @@ void hw_i2c1_base_init() { } #endif } +#endif // HAS_I2CN(1) -void hw_i2c1_init() { - hw_i2c1_base_init(); +#if HAS_I2CN(2) - if (__HAL_I2C_GET_FLAG(&hi2c1, I2C_FLAG_BUSY) == SET) { - I2C_BUSY_FLAG_ERROR_WORKAROUND(1); - ++i2c1_busy_clear_count; - } +static constexpr uint32_t i2c2_speed = 100'000; + +void hw_i2c2_pins_init() { + GPIO_InitTypeDef GPIO_InitStruct {}; + + __HAL_RCC_GPIOF_CLK_ENABLE(); + + i2c_free_bus_in_case_of_slave_deadlock(i2c2_speed, { i2c2_SDA_PORT, i2c2_SDA_PIN }, { i2c2_SCL_PORT, i2c2_SCL_PIN }); + + // GPIO I2C mode + GPIO_InitStruct.Pin = i2c2_SDA_PIN | i2c2_SCL_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF4_I2C2; + HAL_GPIO_Init(i2c2_SDA_PORT, &GPIO_InitStruct); + + // Peripheral clock enable + __HAL_RCC_I2C2_CLK_ENABLE(); } -#endif // HAS_I2CN(1) -#if HAS_I2CN(2) -static void hw_i2c2_base_init() { +void hw_i2c2_init() { hi2c2.Instance = I2C2; - hi2c2.Init.ClockSpeed = 100000; + hi2c2.Init.ClockSpeed = i2c2_speed; hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c2.Init.OwnAddress1 = 0; @@ -618,27 +618,49 @@ static void hw_i2c2_base_init() { if (HAL_I2CEx_ConfigAnalogFilter(&hi2c2, I2C_ANALOGFILTER_ENABLE) != HAL_OK) { Error_Handler(); } - // Configure Digital filter - if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0) != HAL_OK) { + // Configure Digital filter to maximum (tHD:STA 0.357us delay) + if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0x0F) != HAL_OK) { Error_Handler(); } #endif } +#endif // HAS_I2CN(2) -void hw_i2c2_init() { - hw_i2c2_base_init(); +#if HAS_I2CN(3) - if (__HAL_I2C_GET_FLAG(&hi2c2, I2C_FLAG_BUSY) == SET) { - I2C_BUSY_FLAG_ERROR_WORKAROUND(2); - ++i2c2_busy_clear_count; - } +static constexpr uint32_t i2c3_speed = 100'000; + +void hw_i2c3_pins_init() { + + GPIO_InitTypeDef GPIO_InitStruct {}; + + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOA_CLK_ENABLE(); + + i2c_free_bus_in_case_of_slave_deadlock(i2c3_speed, { i2c3_SDA_PORT, i2c3_SDA_PIN }, { i2c3_SCL_PORT, i2c3_SCL_PIN }); + + // GPIO I2C mode + GPIO_InitStruct.Pin = i2c3_SDA_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF4_I2C3; + HAL_GPIO_Init(i2c3_SDA_PORT, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = i2c3_SCL_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF4_I2C3; + HAL_GPIO_Init(i2c3_SCL_PORT, &GPIO_InitStruct); + + // Peripheral clock enable + __HAL_RCC_I2C3_CLK_ENABLE(); } -#endif // HAS_I2CN(2) -#if HAS_I2CN(3) -static void hw_i2c3_base_init() { +void hw_i2c3_init() { hi2c3.Instance = I2C3; - hi2c3.Init.ClockSpeed = 100000; + hi2c3.Init.ClockSpeed = i2c3_speed; hi2c3.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c3.Init.OwnAddress1 = 0; hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; @@ -661,15 +683,6 @@ static void hw_i2c3_base_init() { } #endif } - -void hw_i2c3_init() { - hw_i2c3_base_init(); - - if (__HAL_I2C_GET_FLAG(&hi2c3, I2C_FLAG_BUSY) == SET) { - I2C_BUSY_FLAG_ERROR_WORKAROUND(3); - ++i2c3_busy_clear_count; - } -} #endif // HAS_I2CN(3) void hw_spi2_init() { diff --git a/src/gui/MItem_eeprom.cpp b/src/gui/MItem_eeprom.cpp index 87c8166ad9..cc7735922c 100644 --- a/src/gui/MItem_eeprom.cpp +++ b/src/gui/MItem_eeprom.cpp @@ -4,7 +4,7 @@ * @date 2021-09-22 */ #include "MItem_eeprom.hpp" -#include "i2c.h" +#include "i2c.hpp" #include "eeprom_loadsave.h" #include "GuiDefaults.hpp" #include "i18n.h" @@ -14,45 +14,70 @@ static constexpr bool use_long_text = GuiDefaults::infoDefaultLen >= 16; constexpr static const char *label_CRC_ERR = (use_long_text) ? "INIT_CRC_ERROR" : "I_CRC"; constexpr static const char *label_UPGRADED = (use_long_text) ? "INIT_UPGRADED" : "I_UPG"; constexpr static const char *label_UPGRADE_FAILED = (use_long_text) ? "INIT_UPG_FAILED" : "I_UPF"; -constexpr static const char *label_TRANSMIT_OK = (use_long_text) ? "TRANSMIT_OK" : "T_OK"; -constexpr static const char *label_TRANSMIT_ERROR = (use_long_text) ? "TRANSMIT_ERROR" : "T_ERROR"; -constexpr static const char *label_TRANSMIT_BUSY = (use_long_text) ? "TRANSMIT_BUSY" : "T_BUSY"; -constexpr static const char *label_TRANSMIT_TIMEOUT = (use_long_text) ? "TRANSMIT_TIMEOUT" : "R_TIMEOUT"; -constexpr static const char *label_TRANSMIT_UNDEF = (use_long_text) ? "TRANSMIT_UNDEF" : "T_UNDEF"; -constexpr static const char *label_RECEIVE_OK = (use_long_text) ? "RECEIVE_OK" : "R_OK"; -constexpr static const char *label_RECEIVE_ERROR = (use_long_text) ? "RECEIVE_ERROR" : "R_ERROR"; -constexpr static const char *label_RECEIVE_BUSY = (use_long_text) ? "RECEIVE_BUSY" : "R_BUSY"; -constexpr static const char *label_RECEIVE_TIMEOUT = (use_long_text) ? "RECEIVE_TIMEOUT" : "R_TIMEOUT"; -constexpr static const char *label_RECEIVE_UNDEF = (use_long_text) ? "RECEIVE_UNDEF" : "R_UNDEF"; - -// accessing extern variables from other thread -// uint32_t will be most likely atomic, if it is not it will not break anything because it is "read only" -// meant to be used in dev mode -MI_EEPROM_INIT_CRC_ERROR::MI_EEPROM_INIT_CRC_ERROR() - : WI_INFO_DEV_t(0, string_view_utf8::MakeCPUFLASH((const uint8_t *)label_CRC_ERR)) { // TODO(ConfigStore) : Not migrated +static constexpr uint8_t i2c_channels = 3; + +constexpr static const char *labels_ok[i2c_channels] = { + (use_long_text) ? "I2C1_OK" : "I1_OK", + (use_long_text) ? "I2C2_OK" : "I2_OK", + (use_long_text) ? "I2C3_OK" : "I3_OK" +}; +constexpr static const char *labels_error[i2c_channels] = { + (use_long_text) ? "I2C1_ERROR" : "I1_ERROR", + (use_long_text) ? "I2C2_ERROR" : "I2_ERROR", + (use_long_text) ? "I2C3_ERROR" : "I3_ERROR" +}; +constexpr static const char *labels_busy[i2c_channels] = { + (use_long_text) ? "I2C1_BUSY" : "I1_BUSY", + (use_long_text) ? "I2C2_BUSY" : "I2_BUSY", + (use_long_text) ? "I2C3_BUSY" : "I3_BUSY" +}; +constexpr static const char *labels_timeout[i2c_channels] = { + (use_long_text) ? "I2C1_TIMEOUT" : "I1_TIMEOUT", + (use_long_text) ? "I2C2_TIMEOUT" : "I2_TIMEOUT", + (use_long_text) ? "I2C3_TIMEOUT" : "I3_TIMEOUT" +}; + +const uint8_t *get_label_ok(uint8_t channel) { + --channel; + if (channel < i2c_channels) { + return (const uint8_t *)labels_ok[channel]; + } + return nullptr; } -MI_EEPROM_INIT_UPGRADED::MI_EEPROM_INIT_UPGRADED() - : WI_INFO_DEV_t(0, string_view_utf8::MakeCPUFLASH((const uint8_t *)label_UPGRADED)) { // TODO(ConfigStore) : Not migrated +const uint8_t *get_label_error(uint8_t channel) { + --channel; + if (channel < i2c_channels) { + return (const uint8_t *)labels_error[channel]; + } + return nullptr; } -MI_EEPROM_INIT_UPGRADE_FAILED::MI_EEPROM_INIT_UPGRADE_FAILED() - : WI_INFO_DEV_t(0, string_view_utf8::MakeCPUFLASH((const uint8_t *)label_UPGRADE_FAILED)) { // TODO(ConfigStore) : Not migrated +const uint8_t *get_label_busy(uint8_t channel) { + --channel; + if (channel < i2c_channels) { + return (const uint8_t *)labels_busy[channel]; + } + return nullptr; } -MI_I2C_TRANSMIT_RESULTS_HAL_OK::MI_I2C_TRANSMIT_RESULTS_HAL_OK() - : WI_INFO_DEV_t(I2C_TRANSMIT_RESULTS_HAL_OK, string_view_utf8::MakeCPUFLASH((const uint8_t *)label_TRANSMIT_OK)) { +const uint8_t *get_label_timeout(uint8_t channel) { + --channel; + if (channel < i2c_channels) { + return (const uint8_t *)labels_timeout[channel]; + } + return nullptr; } -MI_I2C_TRANSMIT_RESULTS_HAL_BUSY::MI_I2C_TRANSMIT_RESULTS_HAL_BUSY() - : WI_INFO_DEV_t(I2C_TRANSMIT_RESULTS_HAL_BUSY, string_view_utf8::MakeCPUFLASH((const uint8_t *)label_TRANSMIT_BUSY)) { +MI_EEPROM_INIT_CRC_ERROR::MI_EEPROM_INIT_CRC_ERROR() + : WI_INFO_DEV_t(0, string_view_utf8::MakeCPUFLASH((const uint8_t *)label_CRC_ERR)) { // TODO(ConfigStore) : Not migrated } -MI_I2C_RECEIVE_RESULTS_HAL_OK::MI_I2C_RECEIVE_RESULTS_HAL_OK() - : WI_INFO_DEV_t(I2C_RECEIVE_RESULTS_HAL_OK, string_view_utf8::MakeCPUFLASH((const uint8_t *)label_RECEIVE_OK)) { +MI_EEPROM_INIT_UPGRADED::MI_EEPROM_INIT_UPGRADED() + : WI_INFO_DEV_t(0, string_view_utf8::MakeCPUFLASH((const uint8_t *)label_UPGRADED)) { // TODO(ConfigStore) : Not migrated } -MI_I2C_RECEIVE_RESULTS_HAL_BUSY::MI_I2C_RECEIVE_RESULTS_HAL_BUSY() - : WI_INFO_DEV_t(I2C_RECEIVE_RESULTS_HAL_BUSY, string_view_utf8::MakeCPUFLASH((const uint8_t *)label_RECEIVE_BUSY)) { +MI_EEPROM_INIT_UPGRADE_FAILED::MI_EEPROM_INIT_UPGRADE_FAILED() + : WI_INFO_DEV_t(0, string_view_utf8::MakeCPUFLASH((const uint8_t *)label_UPGRADE_FAILED)) { // TODO(ConfigStore) : Not migrated } diff --git a/src/gui/MItem_eeprom.hpp b/src/gui/MItem_eeprom.hpp index 8ce799ad6f..3769841f8a 100644 --- a/src/gui/MItem_eeprom.hpp +++ b/src/gui/MItem_eeprom.hpp @@ -6,7 +6,7 @@ */ #pragma once #include "WindowMenuInfo.hpp" - +#include "i2c.hpp" class MI_EEPROM_INIT_CRC_ERROR : public WI_INFO_DEV_t { public: MI_EEPROM_INIT_CRC_ERROR(); @@ -22,22 +22,39 @@ class MI_EEPROM_INIT_UPGRADE_FAILED : public WI_INFO_DEV_t { MI_EEPROM_INIT_UPGRADE_FAILED(); }; -class MI_I2C_TRANSMIT_RESULTS_HAL_OK : public WI_INFO_DEV_t { +const uint8_t *get_label_ok(uint8_t channel); +const uint8_t *get_label_error(uint8_t channel); +const uint8_t *get_label_busy(uint8_t channel); +const uint8_t *get_label_timeout(uint8_t channel); + +template +class MI_I2C_RESULTS_HAL_OK : public WI_INFO_DEV_t { public: - MI_I2C_TRANSMIT_RESULTS_HAL_OK(); + MI_I2C_RESULTS_HAL_OK() + : WI_INFO_DEV_t(i2c::statistics::get_hal_ok(CHAN), string_view_utf8::MakeCPUFLASH(get_label_ok(CHAN))) { + } }; -class MI_I2C_TRANSMIT_RESULTS_HAL_BUSY : public WI_INFO_DEV_t { +template +class MI_I2C_RESULTS_HAL_ERROR : public WI_INFO_DEV_t { public: - MI_I2C_TRANSMIT_RESULTS_HAL_BUSY(); + MI_I2C_RESULTS_HAL_ERROR() + : WI_INFO_DEV_t(i2c::statistics::get_hal_error(CHAN), string_view_utf8::MakeCPUFLASH(get_label_error(CHAN))) { + } }; -class MI_I2C_RECEIVE_RESULTS_HAL_OK : public WI_INFO_DEV_t { +template +class MI_I2C_RESULTS_HAL_BUSY : public WI_INFO_DEV_t { public: - MI_I2C_RECEIVE_RESULTS_HAL_OK(); + MI_I2C_RESULTS_HAL_BUSY() + : WI_INFO_DEV_t(i2c::statistics::get_hal_busy(CHAN), string_view_utf8::MakeCPUFLASH(get_label_busy(CHAN))) { + } }; -class MI_I2C_RECEIVE_RESULTS_HAL_BUSY : public WI_INFO_DEV_t { +template +class MI_I2C_RESULTS_HAL_TIMEOUT : public WI_INFO_DEV_t { public: - MI_I2C_RECEIVE_RESULTS_HAL_BUSY(); + MI_I2C_RESULTS_HAL_TIMEOUT() + : WI_INFO_DEV_t(i2c::statistics::get_hal_timeout(CHAN), string_view_utf8::MakeCPUFLASH(get_label_timeout(CHAN))) { + } }; diff --git a/src/gui/MItem_hardware.cpp b/src/gui/MItem_hardware.cpp index cdcabbcf52..e8f874b118 100644 --- a/src/gui/MItem_hardware.cpp +++ b/src/gui/MItem_hardware.cpp @@ -1,6 +1,7 @@ #include "MItem_hardware.hpp" #include "ScreenHandler.hpp" #include