From 25aade1cf13d6d8936859328addf21307b63d91e Mon Sep 17 00:00:00 2001
From: Jason Smith <jason.inet@gmail.com>
Date: Sun, 10 May 2020 23:10:20 -0700
Subject: [PATCH] Improve STM32F4 Flash Behavior (#17946)

---
 Marlin/src/HAL/STM32/Servo.cpp         | 42 ++++++++++++++++++++++----
 Marlin/src/HAL/STM32/Servo.h           | 18 +++++++++--
 Marlin/src/HAL/STM32/eeprom_flash.cpp  | 31 ++++++++++++++++---
 Marlin/src/HAL/STM32/inc/SanityCheck.h |  9 ++++++
 4 files changed, 87 insertions(+), 13 deletions(-)

diff --git a/Marlin/src/HAL/STM32/Servo.cpp b/Marlin/src/HAL/STM32/Servo.cpp
index d192da5d28..5fb8e3cd6a 100644
--- a/Marlin/src/HAL/STM32/Servo.cpp
+++ b/Marlin/src/HAL/STM32/Servo.cpp
@@ -29,32 +29,62 @@
 #include "Servo.h"
 
 static uint_fast8_t servoCount = 0;
+static libServo *servos[NUM_SERVOS] = {0};
 constexpr millis_t servoDelay[] = SERVO_DELAY;
 static_assert(COUNT(servoDelay) == NUM_SERVOS, "SERVO_DELAY must be an array NUM_SERVOS long.");
 
 libServo::libServo()
-: delay(servoDelay[servoCount++])
-{}
+: delay(servoDelay[servoCount]),
+  was_attached_before_pause(false),
+  value_before_pause(0)
+{
+  servos[servoCount++] = this;
+}
 
 int8_t libServo::attach(const int pin) {
   if (servoCount >= MAX_SERVOS) return -1;
   if (pin > 0) servo_pin = pin;
-  return super::attach(servo_pin);
+  return stm32_servo.attach(servo_pin);
 }
 
 int8_t libServo::attach(const int pin, const int min, const int max) {
   if (servoCount >= MAX_SERVOS) return -1;
   if (pin > 0) servo_pin = pin;
-  return super::attach(servo_pin, min, max);
+  return stm32_servo.attach(servo_pin, min, max);
 }
 
 void libServo::move(const int value) {
   if (attach(0) >= 0) {
-    write(value);
+    stm32_servo.write(value);
     safe_delay(delay);
     TERN_(DEACTIVATE_SERVOS_AFTER_MOVE, detach());
   }
 }
-#endif // HAS_SERVOS
 
+void libServo::pause() {
+  was_attached_before_pause = stm32_servo.attached();
+  if (was_attached_before_pause) {
+    value_before_pause = stm32_servo.read();
+    stm32_servo.detach();
+  }
+}
+
+void libServo::resume() {
+  if (was_attached_before_pause) {
+    attach();
+    move(value_before_pause);
+  }
+}
+
+void libServo::pause_all_servos() {
+  for (auto& servo : servos)
+    if (servo) servo->pause();
+}
+
+void libServo::resume_all_servos() {
+  for (auto& servo : servos)
+    if (servo) servo->resume();
+}
+
+#endif // HAS_SERVOS
 #endif // ARDUINO_ARCH_STM32 && !STM32GENERIC
diff --git a/Marlin/src/HAL/STM32/Servo.h b/Marlin/src/HAL/STM32/Servo.h
index e8b3c4b100..50ae1a9b94 100644
--- a/Marlin/src/HAL/STM32/Servo.h
+++ b/Marlin/src/HAL/STM32/Servo.h
@@ -27,15 +27,27 @@
 #include "../../core/millis_t.h"
 
 // Inherit and expand on the official library
-class libServo : public Servo {
+class libServo {
   public:
     libServo();
-    int8_t attach(const int pin);
+    int8_t attach(const int pin = 0); // pin == 0 uses value from previous call
     int8_t attach(const int pin, const int min, const int max);
+    void detach() { stm32_servo.detach(); }
+    int read() { return stm32_servo.read(); }
     void move(const int value);
+
+    void pause();
+    void resume();
+
+    static void pause_all_servos();
+    static void resume_all_servos();
+
   private:
-    typedef Servo super;
+    Servo stm32_servo;
 
     int servo_pin = 0;
     millis_t delay = 0;
+
+    bool was_attached_before_pause;
+    int value_before_pause;
 };
diff --git a/Marlin/src/HAL/STM32/eeprom_flash.cpp b/Marlin/src/HAL/STM32/eeprom_flash.cpp
index 49862957e8..309c5eea9f 100644
--- a/Marlin/src/HAL/STM32/eeprom_flash.cpp
+++ b/Marlin/src/HAL/STM32/eeprom_flash.cpp
@@ -28,10 +28,13 @@
 
 #include "../shared/eeprom_api.h"
 
-
-// Only STM32F4 can support wear leveling at this time
-#ifndef STM32F4xx
-  #undef FLASH_EEPROM_LEVELING
+#if HAS_SERVOS
+  #include "Servo.h"
+  #define PAUSE_SERVO_OUTPUT() libServo::pause_all_servos()
+  #define RESUME_SERVO_OUTPUT() libServo::resume_all_servos()
+#else  
+  #define PAUSE_SERVO_OUTPUT()
+  #define RESUME_SERVO_OUTPUT()
 #endif
 
 /**
@@ -139,6 +142,11 @@ bool PersistentStore::access_start() {
 bool PersistentStore::access_finish() {
 
   if (eeprom_data_written) {
+    #ifdef STM32F4xx
+      // MCU may come up with flash error bits which prevent some flash operations.
+      // Clear flags prior to flash operations to prevent errors.
+      __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
+    #endif    
 
     #if ENABLED(FLASH_EEPROM_LEVELING)
 
@@ -159,7 +167,11 @@ bool PersistentStore::access_finish() {
         current_slot = EEPROM_SLOTS - 1;
         UNLOCK_FLASH();
 
+        PAUSE_SERVO_OUTPUT();
+        DISABLE_ISRS();
         status = HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError);
+        ENABLE_ISRS();
+        RESUME_SERVO_OUTPUT();
         if (status != HAL_OK) {
           DEBUG_ECHOLNPAIR("HAL_FLASHEx_Erase=", status);
           DEBUG_ECHOLNPAIR("GetError=", HAL_FLASH_GetError());
@@ -204,7 +216,18 @@ bool PersistentStore::access_finish() {
       return success;
 
     #else
+      // The following was written for the STM32F4 but may work with other MCUs as well.
+      // Most STM32F4 flash does not allow reading from flash during erase operations.
+      // This takes about a second on a STM32F407 with a 128kB sector used as EEPROM.
+      // Interrupts during this time can have unpredictable results, such as killing Servo
+      // output. Servo output still glitches with interrupts disabled, but recovers after the
+      // erase.
+      PAUSE_SERVO_OUTPUT();
+      DISABLE_ISRS();
       eeprom_buffer_flush();
+      ENABLE_ISRS();
+      RESUME_SERVO_OUTPUT();
+
       eeprom_data_written = false;
     #endif
   }
diff --git a/Marlin/src/HAL/STM32/inc/SanityCheck.h b/Marlin/src/HAL/STM32/inc/SanityCheck.h
index 9cd8db81f4..7734fc0e83 100644
--- a/Marlin/src/HAL/STM32/inc/SanityCheck.h
+++ b/Marlin/src/HAL/STM32/inc/SanityCheck.h
@@ -43,3 +43,12 @@
   #endif
   #error "SDCARD_EEPROM_EMULATION requires SDSUPPORT. Enable SDSUPPORT or choose another EEPROM emulation."
 #endif
+
+#if defined(STM32F4xx) && BOTH(PRINTCOUNTER, FLASH_EEPROM_EMULATION)
+  #warning "FLASH_EEPROM_EMULATION may cause long delays when writing and should not be used while printing."
+  #error "Disable PRINTCOUNTER or choose another EEPROM emulation."
+#endif
+
+#if !defined(STM32F4xx) && ENABLED(FLASH_EEPROM_LEVELING)
+  #error "FLASH_EEPROM_LEVELING is currently only supported on STM32F4 hardware."
+#endif
-- 
GitLab