From e1dbd13d34069f9ce95036180ad6db9c67d68217 Mon Sep 17 00:00:00 2001
From: Claudio Indellicati <c.indellicati@arduino.cc>
Date: Fri, 13 Mar 2015 19:30:41 +0100
Subject: [PATCH] Added first tone library implementation.

Interrupt based implementation that works on every pin.
---
 cores/arduino/Tone.cpp  | 173 ++++++++++++++++++++++++++++++++++++++++
 cores/arduino/Tone.h    |  12 ++-
 cores/arduino/startup.c |   2 +-
 3 files changed, 185 insertions(+), 2 deletions(-)

diff --git a/cores/arduino/Tone.cpp b/cores/arduino/Tone.cpp
index 91c95c88..739eab83 100644
--- a/cores/arduino/Tone.cpp
+++ b/cores/arduino/Tone.cpp
@@ -15,3 +15,176 @@
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
+
+#include "Tone.h"
+#include "variant.h"
+
+#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.STATUS.bit.SYNCBUSY);
+
+uint32_t toneMaxFrequency = SystemCoreClock / 2;
+uint32_t lastOutputPin = 0xFFFFFFFF;
+
+volatile uint32_t *portToggleRegister;
+volatile uint32_t *portClearRegister;
+volatile uint32_t portBitMask;
+volatile int64_t toggleCount;
+volatile bool toneIsActive = false;
+
+#define TONE_TC         TC5
+#define TONE_TC_IRQn    TC5_IRQn
+#define TONE_TC_TOP     0xFFFF
+#define TONE_TC_CHANNEL 0
+
+static inline void resetTC (Tc* TCx)
+{
+  // Disable TCx
+  TCx->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
+  WAIT_TC16_REGS_SYNC(TCx)
+
+  // Reset TCx
+  TCx->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
+  WAIT_TC16_REGS_SYNC(TCx)
+  while (TCx->COUNT16.CTRLA.bit.SWRST);
+}
+
+void toneAccurateClock (uint32_t accurateSystemCoreClockFrequency)
+{
+  toneMaxFrequency = accurateSystemCoreClockFrequency / 2;
+}
+
+void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration)
+{
+  if (toneIsActive && (outputPin != lastOutputPin))
+    noTone(lastOutputPin);
+
+  //
+  // Calculate best prescaler divider and comparator value for a 16 bit TC peripheral
+  //
+
+  uint32_t prescalerConfigBits;
+  uint32_t ccValue;
+
+  ccValue = toneMaxFrequency / frequency - 1;
+  prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1;
+
+  if (ccValue > TONE_TC_TOP)
+  {
+    ccValue = toneMaxFrequency / frequency / 2 - 1;
+    prescalerConfigBits = TC_CTRLA_PRESCALER_DIV2;
+
+    if (ccValue > TONE_TC_TOP)
+    {
+      ccValue = toneMaxFrequency / frequency / 4 - 1;
+      prescalerConfigBits = TC_CTRLA_PRESCALER_DIV4;
+
+      if (ccValue > TONE_TC_TOP)
+      {
+        ccValue = toneMaxFrequency / frequency / 8 - 1;
+        prescalerConfigBits = TC_CTRLA_PRESCALER_DIV8;
+
+        if (ccValue > TONE_TC_TOP)
+        {
+          ccValue = toneMaxFrequency / frequency / 16 - 1;
+          prescalerConfigBits = TC_CTRLA_PRESCALER_DIV16;
+
+          if (ccValue > TONE_TC_TOP)
+          {
+            ccValue = toneMaxFrequency / frequency / 64 - 1;
+            prescalerConfigBits = TC_CTRLA_PRESCALER_DIV64;
+
+            if (ccValue > TONE_TC_TOP)
+            {
+              ccValue = toneMaxFrequency / frequency / 256 - 1;
+              prescalerConfigBits = TC_CTRLA_PRESCALER_DIV256;
+
+              if (ccValue > TONE_TC_TOP)
+              {
+                ccValue = toneMaxFrequency / frequency / 1024 - 1;
+                prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1024;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  toggleCount = (duration > 0 ? frequency * duration * 2 / 1000UL : -1);
+
+  // Enable GCLK for TC4 and TC5 (timer counter input clock)
+  GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5));
+  while (GCLK->STATUS.bit.SYNCBUSY);
+
+  resetTC(TONE_TC);
+
+  // Set Timer counter Mode to 16 bits
+  TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
+
+  // Set TONE_TC mode as match frequency
+  TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;
+
+  TONE_TC->COUNT16.CTRLA.reg |= prescalerConfigBits;
+
+  TONE_TC->COUNT16.CC[TONE_TC_CHANNEL].reg = (uint16_t) ccValue;
+  WAIT_TC16_REGS_SYNC(TONE_TC)
+
+  // Configure interrupt request
+  NVIC_DisableIRQ(TONE_TC_IRQn);
+  NVIC_ClearPendingIRQ(TONE_TC_IRQn);
+  NVIC_SetPriority(TONE_TC_IRQn, 0);
+  NVIC_EnableIRQ(TONE_TC_IRQn);
+
+  portToggleRegister = &(PORT->Group[g_APinDescription[outputPin].ulPort].OUTTGL.reg);
+  portClearRegister = &(PORT->Group[g_APinDescription[outputPin].ulPort].OUTCLR.reg);
+  portBitMask = (1ul << g_APinDescription[outputPin].ulPin);
+
+  // Enable the TONE_TC interrupt request
+  TONE_TC->COUNT16.INTENSET.bit.MC0 = 1;
+
+  lastOutputPin = outputPin;
+  digitalWrite(outputPin, LOW);
+  pinMode(outputPin, OUTPUT);
+  toneIsActive = true;
+
+  // Enable TONE_TC
+  TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
+  WAIT_TC16_REGS_SYNC(TONE_TC)
+}
+
+void noTone (uint32_t outputPin)
+{
+  resetTC(TONE_TC);
+  digitalWrite(outputPin, LOW);
+  toneIsActive = false;
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void Tone_Handler (void)
+{
+  if (toggleCount != 0)
+  {
+    // Toggle the ouput pin
+    *portToggleRegister = portBitMask;
+
+    if (toggleCount > 0)
+      --toggleCount;
+
+    // Clear the interrupt
+    TONE_TC->COUNT16.INTFLAG.bit.MC0 = 1;
+  }
+  else
+  {
+    resetTC(TONE_TC);
+    *portClearRegister = portBitMask;
+    toneIsActive = false;
+  }
+}
+
+void TC5_Handler (void) __attribute__ ((weak, alias("Tone_Handler")));
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cores/arduino/Tone.h b/cores/arduino/Tone.h
index be29d3e2..af481631 100644
--- a/cores/arduino/Tone.h
+++ b/cores/arduino/Tone.h
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2014 Arduino.  All right reserved.
+  Copyright (c) 2015 Arduino.  All right reserved.
 
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
@@ -19,5 +19,15 @@
 #ifndef _WIRING_TONE_
 #define _WIRING_TONE_
 
+#ifdef __cplusplus
+
+#include "Arduino.h"
+
+void tone(uint32_t _pin, uint32_t frequency, uint32_t duration = 0);
+void noTone(uint32_t _pin);
+
+void toneAccurateClock (uint32_t);
+
+#endif /*  __cplusplus */
 
 #endif /* _WIRING_TONE_ */
diff --git a/cores/arduino/startup.c b/cores/arduino/startup.c
index 82e687fd..ef519302 100644
--- a/cores/arduino/startup.c
+++ b/cores/arduino/startup.c
@@ -93,7 +93,7 @@ void TCC1_Handler            ( void ) __attribute__ ((weak, alias("Dummy_Handler
 void TCC2_Handler            ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
 void TC3_Handler             ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
 void TC4_Handler             ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
-void TC5_Handler             ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
+void TC5_Handler             ( void ) __attribute__ ((weak));
 void TC6_Handler             ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
 void TC7_Handler             ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
 void ADC_Handler             ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
-- 
GitLab