From 21993b75f4e32bd687edadedb2306ebfebc0e43d Mon Sep 17 00:00:00 2001
From: Giuliano Zaro <3684609+GMagician@users.noreply.github.com>
Date: Fri, 2 Aug 2019 14:37:41 +0200
Subject: [PATCH] SAMD51 Servo class (#14781)

---
 Marlin/src/HAL/HAL_AVR/ServoTimers.h          |   4 +-
 Marlin/src/HAL/HAL_DUE/ServoTimers.h          |   4 +-
 Marlin/src/HAL/HAL_SAMD51/HAL.cpp             |  58 ++---
 Marlin/src/HAL/HAL_SAMD51/HAL.h               |   3 +-
 .../src/HAL/HAL_SAMD51/HAL_timers_SAMD51.cpp  |   4 +-
 Marlin/src/HAL/HAL_SAMD51/HAL_timers_SAMD51.h |   9 +-
 Marlin/src/HAL/HAL_SAMD51/SAMD51.h            |   4 +-
 Marlin/src/HAL/HAL_SAMD51/ServoTimers.h       |  39 +++
 Marlin/src/HAL/HAL_SAMD51/Servo_SAMD51.cpp    | 226 ++++++++++++++++++
 Marlin/src/HAL/HAL_SAMD51/Tone.cpp            |  59 -----
 Marlin/src/HAL/HAL_STM32/HAL_timers_STM32.cpp |   2 -
 .../STM32F4/HAL_timers_STM32F4.cpp            |   2 -
 Marlin/src/HAL/shared/servo.cpp               |   5 +-
 Marlin/src/HAL/shared/servo.h                 |   4 +-
 Marlin/src/HAL/shared/servo_private.h         |  12 +-
 15 files changed, 318 insertions(+), 117 deletions(-)
 create mode 100644 Marlin/src/HAL/HAL_SAMD51/ServoTimers.h
 create mode 100644 Marlin/src/HAL/HAL_SAMD51/Servo_SAMD51.cpp
 delete mode 100644 Marlin/src/HAL/HAL_SAMD51/Tone.cpp

diff --git a/Marlin/src/HAL/HAL_AVR/ServoTimers.h b/Marlin/src/HAL/HAL_AVR/ServoTimers.h
index 07d3070297..4991caefe6 100644
--- a/Marlin/src/HAL/HAL_AVR/ServoTimers.h
+++ b/Marlin/src/HAL/HAL_AVR/ServoTimers.h
@@ -53,8 +53,8 @@
  * --------------------
  */
 
-#define TRIM_DURATION       2   // compensation ticks to trim adjust for digitalWrite delays
-#define PRESCALER           8   // timer prescaler
+#define TRIM_DURATION           2   // compensation ticks to trim adjust for digitalWrite delays
+#define SERVO_TIMER_PRESCALER   8   // timer prescaler
 
 // Say which 16 bit timers can be used and in what order
 #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
diff --git a/Marlin/src/HAL/HAL_DUE/ServoTimers.h b/Marlin/src/HAL/HAL_DUE/ServoTimers.h
index 84129ef2b6..c32c938253 100644
--- a/Marlin/src/HAL/HAL_DUE/ServoTimers.h
+++ b/Marlin/src/HAL/HAL_DUE/ServoTimers.h
@@ -36,8 +36,8 @@
 //!#define _useTimer4
 #define _useTimer5
 
-#define TRIM_DURATION       2    // compensation ticks to trim adjust for digitalWrite delays
-#define PRESCALER           32   // timer prescaler
+#define TRIM_DURATION             2   // compensation ticks to trim adjust for digitalWrite delays
+#define SERVO_TIMER_PRESCALER     32  // timer prescaler
 
 /*
   TC0, chan 0 => TC0_Handler
diff --git a/Marlin/src/HAL/HAL_SAMD51/HAL.cpp b/Marlin/src/HAL/HAL_SAMD51/HAL.cpp
index 0e35610a42..e9f378b666 100644
--- a/Marlin/src/HAL/HAL_SAMD51/HAL.cpp
+++ b/Marlin/src/HAL/HAL_SAMD51/HAL.cpp
@@ -193,8 +193,8 @@ uint16_t HAL_adc_result;
   uint16_t HAL_adc_results[COUNT(adc_pins)];
 
   #if ADC0_IS_REQUIRED
-    Adafruit_ZeroDMA adc0ProgramDMA,
-                     adc0ReadDMA;
+    Adafruit_ZeroDMA adc0DMAProgram,
+                     adc0DMARead;
 
     const HAL_DMA_DAC_Registers adc0_dma_regs_list[] = {
       #if GET_TEMP_0_ADC() == 0
@@ -233,8 +233,8 @@ uint16_t HAL_adc_result;
   #endif // ADC0_IS_REQUIRED
 
   #if ADC1_IS_REQUIRED
-    Adafruit_ZeroDMA adc1ProgramDMA,
-                     adc1ReadDMA;
+    Adafruit_ZeroDMA adc1DMAProgram,
+                     adc1DMARead;
 
     const HAL_DMA_DAC_Registers adc1_dma_regs_list[] = {
       #if GET_TEMP_0_ADC() == 1
@@ -284,11 +284,11 @@ uint16_t HAL_adc_result;
     DmacDescriptor *descriptor;
 
     #if ADC0_IS_REQUIRED
-      adc0ProgramDMA.setTrigger(ADC0_DMAC_ID_SEQ);
-      adc0ProgramDMA.setAction(DMA_TRIGGER_ACTON_BEAT);
-      adc0ProgramDMA.loop(true);
-      if (adc0ProgramDMA.allocate() == DMA_STATUS_OK) {
-        descriptor = adc0ProgramDMA.addDescriptor(
+      adc0DMAProgram.setTrigger(ADC0_DMAC_ID_SEQ);
+      adc0DMAProgram.setAction(DMA_TRIGGER_ACTON_BEAT);
+      adc0DMAProgram.loop(true);
+      if (adc0DMAProgram.allocate() == DMA_STATUS_OK) {
+        descriptor = adc0DMAProgram.addDescriptor(
           (void *)adc0_dma_regs_list,         // SRC
           (void *)&ADC0->DSEQDATA.reg,        // DEST
           sizeof(adc0_dma_regs_list) / 4,     // CNT
@@ -300,14 +300,14 @@ uint16_t HAL_adc_result;
         );
         if (descriptor != nullptr)
           descriptor->BTCTRL.bit.EVOSEL = DMA_EVENT_OUTPUT_BEAT;
-        adc0ProgramDMA.startJob();
+        adc0DMAProgram.startJob();
       }
 
-      adc0ReadDMA.setTrigger(ADC0_DMAC_ID_RESRDY);
-      adc0ReadDMA.setAction(DMA_TRIGGER_ACTON_BEAT);
-      adc0ReadDMA.loop(true);
-      if (adc0ReadDMA.allocate() == DMA_STATUS_OK) {
-        adc0ReadDMA.addDescriptor(
+      adc0DMARead.setTrigger(ADC0_DMAC_ID_RESRDY);
+      adc0DMARead.setAction(DMA_TRIGGER_ACTON_BEAT);
+      adc0DMARead.loop(true);
+      if (adc0DMARead.allocate() == DMA_STATUS_OK) {
+        adc0DMARead.addDescriptor(
           (void *)&ADC0->RESULT.reg,          // SRC
           &HAL_adc_results,                   // DEST
           ADC0_AINCOUNT,                      // CNT
@@ -317,15 +317,15 @@ uint16_t HAL_adc_result;
           DMA_ADDRESS_INCREMENT_STEP_SIZE_1,  // STEPSIZE
           DMA_STEPSEL_DST                     // STEPSEL
         );
-        adc0ReadDMA.startJob();
+        adc0DMARead.startJob();
       }
     #endif
     #if ADC1_IS_REQUIRED
-      adc1ProgramDMA.setTrigger(ADC1_DMAC_ID_SEQ);
-      adc1ProgramDMA.setAction(DMA_TRIGGER_ACTON_BEAT);
-      adc1ProgramDMA.loop(true);
-      if (adc1ProgramDMA.allocate() == DMA_STATUS_OK) {
-        descriptor = adc1ProgramDMA.addDescriptor(
+      adc1DMAProgram.setTrigger(ADC1_DMAC_ID_SEQ);
+      adc1DMAProgram.setAction(DMA_TRIGGER_ACTON_BEAT);
+      adc1DMAProgram.loop(true);
+      if (adc1DMAProgram.allocate() == DMA_STATUS_OK) {
+        descriptor = adc1DMAProgram.addDescriptor(
           (void *)adc1_dma_regs_list,         // SRC
           (void *)&ADC1->DSEQDATA.reg,        // DEST
           sizeof(adc1_dma_regs_list) / 4,     // CNT
@@ -337,14 +337,14 @@ uint16_t HAL_adc_result;
         );
         if (descriptor != nullptr)
           descriptor->BTCTRL.bit.EVOSEL = DMA_EVENT_OUTPUT_BEAT;
-        adc1ProgramDMA.startJob();
+        adc1DMAProgram.startJob();
       }
 
-      adc1ReadDMA.setTrigger(ADC1_DMAC_ID_RESRDY);
-      adc1ReadDMA.setAction(DMA_TRIGGER_ACTON_BEAT);
-      adc1ReadDMA.loop(true);
-      if (adc1ReadDMA.allocate() == DMA_STATUS_OK) {
-        adc1ReadDMA.addDescriptor(
+      adc1DMARead.setTrigger(ADC1_DMAC_ID_RESRDY);
+      adc1DMARead.setAction(DMA_TRIGGER_ACTON_BEAT);
+      adc1DMARead.loop(true);
+      if (adc1DMARead.allocate() == DMA_STATUS_OK) {
+        adc1DMARead.addDescriptor(
           (void *)&ADC1->RESULT.reg,          // SRC
           &HAL_adc_results[ADC0_AINCOUNT],    // DEST
           ADC1_AINCOUNT,                      // CNT
@@ -354,11 +354,11 @@ uint16_t HAL_adc_result;
           DMA_ADDRESS_INCREMENT_STEP_SIZE_1,  // STEPSIZE
           DMA_STEPSEL_DST                     // STEPSEL
         );
-        adc1ReadDMA.startJob();
+        adc1DMARead.startJob();
       }
     #endif
 
-    DMAC->PRICTRL0.bit.RRLVLEN0 = true;                         // Activate round robin for DMA channels used by ADCs
+    DMAC->PRICTRL0.bit.RRLVLEN0 = true;                         // Activate round robin for DMA channels required by ADCs
   }
 
 #endif // DMA_IS_REQUIRED
diff --git a/Marlin/src/HAL/HAL_SAMD51/HAL.h b/Marlin/src/HAL/HAL_SAMD51/HAL.h
index 3dd1313462..7c6d7e37ee 100644
--- a/Marlin/src/HAL/HAL_SAMD51/HAL.h
+++ b/Marlin/src/HAL/HAL_SAMD51/HAL.h
@@ -76,7 +76,8 @@
 
 typedef int8_t pin_t;
 
-//#define HAL_SERVO_LIB Servo
+#define SHARED_SERVOS HAS_SERVOS
+#define HAL_SERVO_LIB Servo
 
 //
 // Interrupts
diff --git a/Marlin/src/HAL/HAL_SAMD51/HAL_timers_SAMD51.cpp b/Marlin/src/HAL/HAL_SAMD51/HAL_timers_SAMD51.cpp
index 70733e2415..8868acc874 100644
--- a/Marlin/src/HAL/HAL_SAMD51/HAL_timers_SAMD51.cpp
+++ b/Marlin/src/HAL/HAL_SAMD51/HAL_timers_SAMD51.cpp
@@ -40,8 +40,8 @@
 const tTimerConfig TimerConfig[NUM_HARDWARE_TIMERS] = {
   { TC0, TC0_IRQn, TC_PRIORITY(0) },
   { TC1, TC1_IRQn, TC_PRIORITY(1) },
-  { TC2, TC2_IRQn, TC_PRIORITY(2) },
-  { TC3, TC3_IRQn, TC_PRIORITY(3) },
+  { TC2, TC2_IRQn, TC_PRIORITY(2) },  // Reserved by framework tone function
+  { TC3, TC3_IRQn, TC_PRIORITY(3) },  // Reserved by servo library
   { TC4, TC4_IRQn, TC_PRIORITY(4) },
   { TC5, TC5_IRQn, TC_PRIORITY(5) },
   { TC6, TC6_IRQn, TC_PRIORITY(6) },
diff --git a/Marlin/src/HAL/HAL_SAMD51/HAL_timers_SAMD51.h b/Marlin/src/HAL/HAL_SAMD51/HAL_timers_SAMD51.h
index 60d2b32962..37cdf7a9bd 100644
--- a/Marlin/src/HAL/HAL_SAMD51/HAL_timers_SAMD51.h
+++ b/Marlin/src/HAL/HAL_SAMD51/HAL_timers_SAMD51.h
@@ -33,7 +33,6 @@ typedef uint32_t hal_timer_t;
 
 #define STEP_TIMER_NUM      0  // index of timer to use for stepper (also +1 for 32bits counter)
 #define PULSE_TIMER_NUM     STEP_TIMER_NUM
-#define TONE_TIMER_NUM      2  // index of timer to use for beeper tones (also +1 for 32bits counter)
 #define TEMP_TIMER_NUM      4  // index of timer to use for temperature (also +1 for 32bits counter)
 
 #define TEMP_TIMER_FREQUENCY   1000 // temperature interrupt frequency
@@ -53,9 +52,10 @@ typedef uint32_t hal_timer_t;
 #define ENABLE_TEMPERATURE_INTERRUPT()  HAL_timer_enable_interrupt(TEMP_TIMER_NUM)
 #define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(TEMP_TIMER_NUM)
 
-#define TC_PRIORITY(t)        (t == STEP_TIMER_NUM || t == PULSE_TIMER_NUM) ? 2     \
-                               : (t == TEMP_TIMER_NUM) ? 6                          \
-                               : (t == TONE_TIMER_NUM) ? 5 : 7
+#define TC_PRIORITY(t)        (t == STEP_TIMER_NUM || t == PULSE_TIMER_NUM) ? 2   \
+                               : (t == TEMP_TIMER_NUM) ? 6                        \
+                               : 7
+
 #define _TC_HANDLER(t)        void TC##t##_Handler()
 #define TC_HANDLER(t)         _TC_HANDLER(t)
 #define HAL_STEP_TIMER_ISR()  TC_HANDLER(STEP_TIMER_NUM)
@@ -63,7 +63,6 @@ typedef uint32_t hal_timer_t;
   #define HAL_PULSE_TIMER_ISR()  TC_HANDLER(PULSE_TIMER_NUM)
 #endif
 #define HAL_TEMP_TIMER_ISR()  TC_HANDLER(TEMP_TIMER_NUM)
-#define HAL_TONE_TIMER_ISR()  TC_HANDLER(TONE_TIMER_NUM)
 
 // --------------------------------------------------------------------------
 // Types
diff --git a/Marlin/src/HAL/HAL_SAMD51/SAMD51.h b/Marlin/src/HAL/HAL_SAMD51/SAMD51.h
index 63d53a9600..242423f881 100644
--- a/Marlin/src/HAL/HAL_SAMD51/SAMD51.h
+++ b/Marlin/src/HAL/HAL_SAMD51/SAMD51.h
@@ -20,8 +20,8 @@
  */
 #pragma once
 
-#define SYNC(sc)    while (sc) {   \
-                      asm("");     \
+#define SYNC(sc)    while (sc) {  \
+                      asm("");    \
                     }
 
 // Get SAMD port/pin from specified arduino pin
diff --git a/Marlin/src/HAL/HAL_SAMD51/ServoTimers.h b/Marlin/src/HAL/HAL_SAMD51/ServoTimers.h
new file mode 100644
index 0000000000..b1e18729c6
--- /dev/null
+++ b/Marlin/src/HAL/HAL_SAMD51/ServoTimers.h
@@ -0,0 +1,39 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (C) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * SAMD51 HAL developed by Giuliano Zaro (AKA GMagician)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#define _useTimer1
+#define _useTimer2
+
+#define TRIM_DURATION           5   // compensation ticks to trim adjust for digitalWrite delays
+#define SERVO_TIMER_PRESCALER   64  // timer prescaler factor to 64 (avoid overflowing 16-bit clock counter, at 120MHz this is 1831 ticks per millisecond
+
+#define SERVO_TC                3
+
+typedef enum {
+  #ifdef _useTimer1
+    _timer1,
+  #endif
+  #ifdef _useTimer2
+    _timer2,
+  #endif
+  _Nbr_16timers
+} timer16_Sequence_t;
diff --git a/Marlin/src/HAL/HAL_SAMD51/Servo_SAMD51.cpp b/Marlin/src/HAL/HAL_SAMD51/Servo_SAMD51.cpp
new file mode 100644
index 0000000000..1e3c4f0161
--- /dev/null
+++ b/Marlin/src/HAL/HAL_SAMD51/Servo_SAMD51.cpp
@@ -0,0 +1,226 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (C) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * SAMD51 HAL developed by Giuliano Zaro (AKA GMagician)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/**
+ * This comes from Arduino library which at the moment is buggy and uncompilable
+ */
+
+#ifdef __SAMD51__
+
+#include "../../inc/MarlinConfig.h"
+
+#if HAS_SERVOS
+
+#include "../shared/Marduino.h"
+#include "../shared/servo.h"
+#include "../shared/servo_private.h"
+#include "SAMD51.h"
+#include "HAL_timers_SAMD51.h"
+
+#define __TC_GCLK_ID(t)         TC##t##_GCLK_ID
+#define _TC_GCLK_ID(t)          __TC_GCLK_ID(t)
+#define TC_GCLK_ID              _TC_GCLK_ID(SERVO_TC)
+
+#define _TC_PRESCALER(d)        TC_CTRLA_PRESCALER_DIV##d##_Val
+#define TC_PRESCALER(d)         _TC_PRESCALER(d)
+
+#define __SERVO_IRQn(t)         TC##t##_IRQn
+#define _SERVO_IRQn(t)          __SERVO_IRQn(t)
+#define SERVO_IRQn              _SERVO_IRQn(SERVO_TC)
+
+#define HAL_SERVO_TIMER_ISR()   TC_HANDLER(SERVO_TC)
+
+#define TIMER_TCCHANNEL(t)      ((t) & 1)
+#define TC_COUNTER_START_VAL    0xFFFF
+
+
+static volatile int8_t currentServoIndex[_Nbr_16timers];    // index for the servo being pulsed for each timer (or -1 if refresh interval)
+
+FORCE_INLINE static uint16_t getTimerCount() {
+  Tc * const tc = TimerConfig[SERVO_TC].pTimer;
+
+  tc->COUNT16.CTRLBSET.reg = TC_CTRLBCLR_CMD_READSYNC;
+  SYNC(tc->COUNT16.SYNCBUSY.bit.CTRLB || tc->COUNT16.SYNCBUSY.bit.COUNT);
+
+  return tc->COUNT16.COUNT.reg;
+}
+
+// ----------------------------
+// Interrupt handler for the TC
+// ----------------------------
+HAL_SERVO_TIMER_ISR() {
+  Tc * const tc = TimerConfig[SERVO_TC].pTimer;
+  const timer16_Sequence_t timer =
+    #ifndef _useTimer1
+      _timer2
+    #elif !defined(_useTimer2)
+      _timer1
+    #else
+      (tc->COUNT16.INTFLAG.reg & tc->COUNT16.INTENSET.reg & TC_INTFLAG_MC0) ? _timer1 : _timer2
+    #endif
+  ;
+  const uint8_t tcChannel = TIMER_TCCHANNEL(timer);
+
+  if (currentServoIndex[timer] < 0) {
+    #if defined(_useTimer1) && defined(_useTimer2)
+      if (currentServoIndex[timer ^ 1] >= 0) {
+        // Wait for both channels
+        // Clear the interrupt
+        tc->COUNT16.INTFLAG.reg = (tcChannel == 0) ? TC_INTFLAG_MC0 : TC_INTFLAG_MC1;
+        return;
+      }
+    #endif
+    tc->COUNT16.COUNT.reg = TC_COUNTER_START_VAL;
+    SYNC(tc->COUNT16.SYNCBUSY.bit.COUNT);
+  }
+  else if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount && SERVO(timer, currentServoIndex[timer]).Pin.isActive)
+    digitalWrite(SERVO(timer, currentServoIndex[timer]).Pin.nbr, LOW);      // pulse this channel low if activated
+
+  // Select the next servo controlled by this timer
+  currentServoIndex[timer]++;
+
+  if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount && currentServoIndex[timer] < SERVOS_PER_TIMER) {
+    if (SERVO(timer, currentServoIndex[timer]).Pin.isActive)                // check if activated
+      digitalWrite(SERVO(timer, currentServoIndex[timer]).Pin.nbr, HIGH);   // it's an active channel so pulse it high
+
+    tc->COUNT16.CC[tcChannel].reg = getTimerCount() - (uint16_t)SERVO(timer, currentServoIndex[timer]).ticks;
+  }
+  else {
+    // finished all channels so wait for the refresh period to expire before starting over
+    currentServoIndex[timer] = -1;   // this will get incremented at the end of the refresh period to start again at the first channel
+
+    const uint16_t tcCounterValue = getTimerCount();
+
+    if ((TC_COUNTER_START_VAL - tcCounterValue) + 4UL < usToTicks(REFRESH_INTERVAL))  // allow a few ticks to ensure the next OCR1A not missed
+      tc->COUNT16.CC[tcChannel].reg = TC_COUNTER_START_VAL - (uint16_t)usToTicks(REFRESH_INTERVAL);
+    else
+      tc->COUNT16.CC[tcChannel].reg = (uint16_t)(tcCounterValue - 4UL);               // at least REFRESH_INTERVAL has elapsed
+  }
+  if (tcChannel == 0) {
+    SYNC(tc->COUNT16.SYNCBUSY.bit.CC0); 
+    // Clear the interrupt
+    tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC0;
+  }
+  else {
+    SYNC(tc->COUNT16.SYNCBUSY.bit.CC1); 
+    // Clear the interrupt
+    tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC1;
+  }
+}
+
+void initISR(timer16_Sequence_t timer) {
+  Tc * const tc = TimerConfig[SERVO_TC].pTimer;
+  const uint8_t tcChannel = TIMER_TCCHANNEL(timer);
+
+  static bool initialized = false;  // Servo TC has been initialized
+  if (!initialized) {
+    NVIC_DisableIRQ(SERVO_IRQn);
+
+    // Disable the timer
+    tc->COUNT16.CTRLA.bit.ENABLE = false;
+    SYNC(tc->COUNT16.SYNCBUSY.bit.ENABLE);
+
+    // Select GCLK0 as timer/counter input clock source
+    GCLK->PCHCTRL[TC_GCLK_ID].bit.CHEN = false;
+    SYNC(GCLK->PCHCTRL[TC_GCLK_ID].bit.CHEN);
+    GCLK->PCHCTRL[TC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0 | GCLK_PCHCTRL_CHEN;   // 120MHz startup code programmed
+    SYNC(!GCLK->PCHCTRL[TC_GCLK_ID].bit.CHEN);
+
+    // Reset the timer
+    tc->COUNT16.CTRLA.bit.SWRST = true;
+    SYNC(tc->COUNT16.SYNCBUSY.bit.SWRST);
+    SYNC(tc->COUNT16.CTRLA.bit.SWRST);
+
+    // Set timer counter mode to 16 bits
+    tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16;
+
+    // Set timer counter mode as normal PWM
+    tc->COUNT16.WAVE.bit.WAVEGEN = TCC_WAVE_WAVEGEN_NPWM_Val;
+
+    // Set the prescaler factor
+    tc->COUNT16.CTRLA.bit.PRESCALER = TC_PRESCALER(SERVO_TIMER_PRESCALER);
+
+    // Count down
+    tc->COUNT16.CTRLBSET.reg = TC_CTRLBCLR_DIR;
+    SYNC(tc->COUNT16.SYNCBUSY.bit.CTRLB);
+
+    // Reset all servo indexes
+    memset(currentServoIndex, 0xFF, sizeof(currentServoIndex));
+
+    // Configure interrupt request
+    NVIC_ClearPendingIRQ(SERVO_IRQn);
+    NVIC_SetPriority(SERVO_IRQn, 5);
+    NVIC_EnableIRQ(SERVO_IRQn);
+
+    initialized = true;
+  }
+
+  if (!tc->COUNT16.CTRLA.bit.ENABLE) {
+    // Reset the timer counter
+    tc->COUNT16.COUNT.reg = TC_COUNTER_START_VAL;
+    SYNC(tc->COUNT16.SYNCBUSY.bit.COUNT);
+
+    // Enable the timer and start it
+    tc->COUNT16.CTRLA.bit.ENABLE = true;
+    SYNC(tc->COUNT16.SYNCBUSY.bit.ENABLE);
+  }
+  // First interrupt request after 1 ms
+  tc->COUNT16.CC[tcChannel].reg = getTimerCount() - (uint16_t)usToTicks(1000UL);
+
+  if (tcChannel == 0 ) {
+    SYNC(tc->COUNT16.SYNCBUSY.bit.CC0);
+
+    // Clear pending match interrupt
+    tc->COUNT16.INTFLAG.reg = TC_INTENSET_MC0;
+    // Enable the match channel interrupt request
+    tc->COUNT16.INTENSET.reg = TC_INTENSET_MC0;
+  }
+  else {
+    SYNC(tc->COUNT16.SYNCBUSY.bit.CC1);
+
+    // Clear pending match interrupt
+    tc->COUNT16.INTFLAG.reg = TC_INTENSET_MC1;
+    // Enable the match channel interrupt request
+    tc->COUNT16.INTENSET.reg = TC_INTENSET_MC1;
+  }
+}
+
+void finISR(timer16_Sequence_t timer) {
+  Tc * const tc = TimerConfig[SERVO_TC].pTimer;
+  const uint8_t tcChannel = TIMER_TCCHANNEL(timer);
+
+  // Disable the match channel interrupt request
+  tc->COUNT16.INTENCLR.reg = (tcChannel == 0) ? TC_INTENCLR_MC0 : TC_INTENCLR_MC1;
+
+  if (true
+    #if defined(_useTimer1) && defined(_useTimer2)
+      && (tc->COUNT16.INTENCLR.reg & (TC_INTENCLR_MC0|TC_INTENCLR_MC1)) == 0
+    #endif
+  ) {
+    // Disable the timer if not used
+    tc->COUNT16.CTRLA.bit.ENABLE = false;
+    SYNC(tc->COUNT16.SYNCBUSY.bit.ENABLE);
+  }
+}
+
+#endif // HAS_SERVOS
+
+#endif // __SAMD51__
diff --git a/Marlin/src/HAL/HAL_SAMD51/Tone.cpp b/Marlin/src/HAL/HAL_SAMD51/Tone.cpp
deleted file mode 100644
index 58b9698251..0000000000
--- a/Marlin/src/HAL/HAL_SAMD51/Tone.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * Marlin 3D Printer Firmware
- *
- * Copyright (C) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
- * SAMD51 HAL developed by Giuliano Zaro (AKA GMagician)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-/**
- * Description: Tone function for Arduino Due and compatible (SAM3X8E)
- * Derived from http://forum.arduino.cc/index.php?topic=136500.msg2903012#msg2903012
- */
-
-#ifdef __SAMD51__
-
-#include "../../inc/MarlinConfig.h"
-#include "HAL_timers_SAMD51.h"
-
-static pin_t tone_pin;
-volatile static int32_t toggles;
-
-void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration) {
-  tone_pin = _pin;
-  toggles = 2 * frequency * duration / 1000;
-  HAL_timer_start(TONE_TIMER_NUM, 2 * frequency);
-}
-
-void noTone(const pin_t _pin) {
-  HAL_timer_disable_interrupt(TONE_TIMER_NUM);
-  extDigitalWrite(_pin, LOW);
-}
-
-HAL_TONE_TIMER_ISR() {
-  static bool pin_state = false;
-  HAL_timer_isr_prologue(TONE_TIMER_NUM);
-
-  if (toggles) {
-    toggles--;
-    extDigitalWrite(tone_pin, (pin_state = !pin_state));
-  }
-  else noTone(tone_pin);                         // turn off interrupt
-
-  HAL_timer_isr_epilogue(TONE_TIMER_NUM);
-}
-
-#endif // __SAMD51__
diff --git a/Marlin/src/HAL/HAL_STM32/HAL_timers_STM32.cpp b/Marlin/src/HAL/HAL_STM32/HAL_timers_STM32.cpp
index 7a2de123d2..beefdb3083 100644
--- a/Marlin/src/HAL/HAL_STM32/HAL_timers_STM32.cpp
+++ b/Marlin/src/HAL/HAL_STM32/HAL_timers_STM32.cpp
@@ -32,8 +32,6 @@
 
 #define NUM_HARDWARE_TIMERS 2
 
-//#define PRESCALER 1
-
 // ------------------------
 // Private Variables
 // ------------------------
diff --git a/Marlin/src/HAL/HAL_STM32_F4_F7/STM32F4/HAL_timers_STM32F4.cpp b/Marlin/src/HAL/HAL_STM32_F4_F7/STM32F4/HAL_timers_STM32F4.cpp
index 85ab5fb896..45ea7fc984 100644
--- a/Marlin/src/HAL/HAL_STM32_F4_F7/STM32F4/HAL_timers_STM32F4.cpp
+++ b/Marlin/src/HAL/HAL_STM32_F4_F7/STM32F4/HAL_timers_STM32F4.cpp
@@ -33,8 +33,6 @@
 #define STEP_TIMER_IRQ_ID TIM5_IRQn
 #define TEMP_TIMER_IRQ_ID TIM7_IRQn
 
-//#define PRESCALER 1
-
 // ------------------------
 // Private Variables
 // ------------------------
diff --git a/Marlin/src/HAL/shared/servo.cpp b/Marlin/src/HAL/shared/servo.cpp
index af33853665..22cf6bc88f 100644
--- a/Marlin/src/HAL/shared/servo.cpp
+++ b/Marlin/src/HAL/shared/servo.cpp
@@ -116,9 +116,8 @@ void Servo::detach() {
 }
 
 void Servo::write(int value) {
-  if (value < MIN_PULSE_WIDTH) { // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
+  if (value < MIN_PULSE_WIDTH)    // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
     value = map(constrain(value, 0, 180), 0, 180, SERVO_MIN(), SERVO_MAX());
-  }
   this->writeMicroseconds(value);
 }
 
@@ -140,7 +139,7 @@ void Servo::writeMicroseconds(int value) {
 int Servo::read() { return map(this->readMicroseconds() + 1, SERVO_MIN(), SERVO_MAX(), 0, 180); }
 
 int Servo::readMicroseconds() {
-  return (this->servoIndex == INVALID_SERVO) ? 0 : ticksToUs(servo_info[this->servoIndex].ticks) + TRIM_DURATION;
+  return (this->servoIndex == INVALID_SERVO) ? 0 : ticksToUs(servo_info[this->servoIndex].ticks) + (TRIM_DURATION);
 }
 
 bool Servo::attached() { return servo_info[this->servoIndex].Pin.isActive; }
diff --git a/Marlin/src/HAL/shared/servo.h b/Marlin/src/HAL/shared/servo.h
index 96644ee262..d1a83bf169 100644
--- a/Marlin/src/HAL/shared/servo.h
+++ b/Marlin/src/HAL/shared/servo.h
@@ -84,10 +84,10 @@
 #else
   #include <stdint.h>
 
-  #if defined(__AVR__) || defined(ARDUINO_ARCH_SAM)
+  #if defined(__AVR__) || defined(ARDUINO_ARCH_SAM) || defined (__SAMD51__)
     // we're good to go
   #else
-    #error "This library only supports boards with an AVR or SAM3X processor."
+    #error "This library only supports boards with an AVR, SAM3X or SAMD51 processor."
   #endif
 
   #define Servo_VERSION           2     // software version of this library
diff --git a/Marlin/src/HAL/shared/servo_private.h b/Marlin/src/HAL/shared/servo_private.h
index 12d4740525..1d4cdc7e3e 100644
--- a/Marlin/src/HAL/shared/servo_private.h
+++ b/Marlin/src/HAL/shared/servo_private.h
@@ -47,8 +47,10 @@
   #include "../HAL_AVR/ServoTimers.h"
 #elif defined(ARDUINO_ARCH_SAM)
   #include "../HAL_DUE/ServoTimers.h"
+#elif defined(__SAMD51__)
+  #include "../HAL_SAMD51/ServoTimers.h"
 #else
-  #error "This library only supports boards with an AVR or SAM3X processor."
+  #error "This library only supports boards with an AVR, SAM3X or SAMD51 processor."
 #endif
 
 // Macros
@@ -64,10 +66,8 @@
 #define INVALID_SERVO         255     // flag indicating an invalid servo index
 
 // Convert microseconds to ticks and back (PRESCALER depends on architecture)
-#define usToTicks(_us)    (clockCyclesPerMicrosecond() * (_us) / (PRESCALER))
-#define ticksToUs(_ticks) (unsigned(_ticks) * (PRESCALER) / clockCyclesPerMicrosecond())
-
-//#define NBR_TIMERS        ((MAX_SERVOS) / (SERVOS_PER_TIMER))
+#define usToTicks(_us)    (clockCyclesPerMicrosecond() * (_us) / (SERVO_TIMER_PRESCALER))
+#define ticksToUs(_ticks) (unsigned(_ticks) * (SERVO_TIMER_PRESCALER) / clockCyclesPerMicrosecond())
 
 // convenience macros
 #define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / (SERVOS_PER_TIMER))) // returns the timer controlling this servo
@@ -78,7 +78,7 @@
 // Types
 
 typedef struct {
-  uint8_t nbr        : 6 ;            // a pin number from 0 to 63
+  uint8_t nbr        : 7 ;            // a pin number from 0 to 127
   uint8_t isActive   : 1 ;            // true if this channel is enabled, pin not pulsed if false
 } ServoPin_t;