From aa5fa81bb75c7ee08cb79ea78ee965b812b8c511 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 28 Aug 2020 14:41:48 +0700 Subject: [PATCH] use TC1 for servo, TC0 for tone for samd51 - make Tone_Handler() a strong symbol --- cores/arduino/Tone.cpp | 59 ++++++++++------- libraries/Servo/src/samd/ServoTimers.h | 88 +++++++++++++------------- 2 files changed, 80 insertions(+), 67 deletions(-) diff --git a/cores/arduino/Tone.cpp b/cores/arduino/Tone.cpp index 058c2148..1d4ddc40 100644 --- a/cores/arduino/Tone.cpp +++ b/cores/arduino/Tone.cpp @@ -20,12 +20,6 @@ #include "Tone.h" #include "variant.h" -#if defined(__SAMD51__) -#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.SYNCBUSY.bit.ENABLE); -#else -#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.STATUS.bit.SYNCBUSY); -#endif - uint32_t toneMaxFrequency = F_CPU / 2; uint32_t lastOutputPin = 0xFFFFFFFF; @@ -37,22 +31,24 @@ volatile bool toneIsActive = false; volatile bool firstTimeRunning = false; #if defined(__SAMD51__) -#define TONE_TC TC0 -#define TONE_TC_IRQn TC0_IRQn -#define TONE_TC_GCLK_ID TC0_GCLK_ID + #define TONE_TC TC0 + #define TONE_TC_IRQn TC0_IRQn + #define TONE_TC_GCLK_ID TC0_GCLK_ID + #define Tone_Handler TC0_Handler + + #define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.SYNCBUSY.bit.ENABLE); + #else -#define TONE_TC TC5 -#define TONE_TC_IRQn TC5_IRQn + #define TONE_TC TC5 + #define TONE_TC_IRQn TC5_IRQn + #define Tone_Handler TC5_Handler + + #define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.STATUS.bit.SYNCBUSY); #endif + #define TONE_TC_TOP 0xFFFF #define TONE_TC_CHANNEL 0 -#if defined(__SAMD51__) -void TC0_Handler (void) __attribute__ ((weak, alias("Tone_Handler"))); -#else -void TC5_Handler (void) __attribute__ ((weak, alias("Tone_Handler"))); -#endif - static inline void resetTC (Tc* TCx) { // Disable TCx @@ -72,6 +68,14 @@ void toneAccurateClock (uint32_t accurateSystemCoreClockFrequency) void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration) { + + // Avoid divide by zero error by calling 'noTone' instead + if (frequency == 0) + { + noTone(outputPin); + return; + } + // Configure interrupt request NVIC_DisableIRQ(TONE_TC_IRQn); NVIC_ClearPendingIRQ(TONE_TC_IRQn); @@ -90,9 +94,6 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration) while (GCLK->STATUS.bit.SYNCBUSY); #endif } - - //if it's a rest, set to 1Hz (below audio range) - frequency = (frequency > 0 ? frequency : 1); if (toneIsActive && (outputPin != lastOutputPin)) noTone(lastOutputPin); @@ -167,8 +168,8 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration) lastOutputPin = outputPin; digitalWrite(outputPin, LOW); pinMode(outputPin, OUTPUT); + toneIsActive = true; } - toneIsActive = true; // Enable TONE_TC TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; @@ -179,9 +180,19 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration) void noTone (uint32_t outputPin) { - resetTC(TONE_TC); - digitalWrite(outputPin, LOW); - toneIsActive = false; + /* 'tone' need to run at least once in order to enable GCLK for + * the timers used for the tone-functionality. If 'noTone' is called + * without ever calling 'tone' before then 'WAIT_TC16_REGS_SYNC(TCx)' + * will wait infinitely. The variable 'firstTimeRunning' is set the + * 1st time 'tone' is set so it can be used to detect wether or not + * 'tone' has been called before. + */ + if(firstTimeRunning) + { + resetTC(TONE_TC); + digitalWrite(outputPin, LOW); + toneIsActive = false; + } } #ifdef __cplusplus diff --git a/libraries/Servo/src/samd/ServoTimers.h b/libraries/Servo/src/samd/ServoTimers.h index 81d87b4c..c4da5069 100644 --- a/libraries/Servo/src/samd/ServoTimers.h +++ b/libraries/Servo/src/samd/ServoTimers.h @@ -38,50 +38,52 @@ // to manage more than one channel per timer on the SAMD architecture #if defined(__SAMD51__) - #if defined (_useTimer1) - #define TC_FOR_TIMER1 TC0 - #define CHANNEL_FOR_TIMER1 0 - #define INTENSET_BIT_FOR_TIMER_1 TC_INTENSET_MC0 - #define INTENCLR_BIT_FOR_TIMER_1 TC_INTENCLR_MC0 - #define INTFLAG_BIT_FOR_TIMER_1 TC_INTFLAG_MC0 - #define ID_TC_FOR_TIMER1 ID_TC0 - #define IRQn_FOR_TIMER1 TC0_IRQn - #define HANDLER_FOR_TIMER1 TC0_Handler - #define GCM_FOR_TIMER_1 9 // GCLK_TC0 - #endif - #if defined (_useTimer2) - #define TC_FOR_TIMER2 TC0 - #define CHANNEL_FOR_TIMER2 1 - #define INTENSET_BIT_FOR_TIMER_2 TC_INTENSET_MC1 - #define INTENCLR_BIT_FOR_TIMER_2 TC_INTENCLR_MC1 - #define INTFLAG_BIT_FOR_TIMER_2 TC_INTFLAG_MC1 - #define ID_TC_FOR_TIMER2 ID_TC0 - #define IRQn_FOR_TIMER2 TC0_IRQn - #define HANDLER_FOR_TIMER2 TC0_Handler - #define GCM_FOR_TIMER_2 9 // GCLK_TC0 - #endif + #if defined (_useTimer1) + #define TC_FOR_TIMER1 TC1 + #define CHANNEL_FOR_TIMER1 0 + #define INTENSET_BIT_FOR_TIMER_1 TC_INTENSET_MC0 + #define INTENCLR_BIT_FOR_TIMER_1 TC_INTENCLR_MC0 + #define INTFLAG_BIT_FOR_TIMER_1 TC_INTFLAG_MC0 + #define ID_TC_FOR_TIMER1 ID_TC1 + #define IRQn_FOR_TIMER1 TC1_IRQn + #define HANDLER_FOR_TIMER1 TC1_Handler + #define GCM_FOR_TIMER_1 TC1_GCLK_ID + #endif + + #if defined (_useTimer2) + #define TC_FOR_TIMER2 TC1 + #define CHANNEL_FOR_TIMER2 1 + #define INTENSET_BIT_FOR_TIMER_2 TC_INTENSET_MC1 + #define INTENCLR_BIT_FOR_TIMER_2 TC_INTENCLR_MC1 + #define INTFLAG_BIT_FOR_TIMER_2 TC_INTFLAG_MC1 + #define ID_TC_FOR_TIMER2 ID_TC1 + #define IRQn_FOR_TIMER2 TC1_IRQn + #define HANDLER_FOR_TIMER2 TC1_Handler + #define GCM_FOR_TIMER_2 TC1_GCLK_ID + #endif #else - #if defined (_useTimer1) - #define TC_FOR_TIMER1 TC4 - #define CHANNEL_FOR_TIMER1 0 - #define INTENSET_BIT_FOR_TIMER_1 TC_INTENSET_MC0 - #define INTENCLR_BIT_FOR_TIMER_1 TC_INTENCLR_MC0 - #define INTFLAG_BIT_FOR_TIMER_1 TC_INTFLAG_MC0 - #define ID_TC_FOR_TIMER1 ID_TC4 - #define IRQn_FOR_TIMER1 TC4_IRQn - #define HANDLER_FOR_TIMER1 TC4_Handler - #define GCM_FOR_TIMER_1 GCM_TC4_TC5 - #endif - #if defined (_useTimer2) - #define TC_FOR_TIMER2 TC4 - #define CHANNEL_FOR_TIMER2 1 - #define INTENSET_BIT_FOR_TIMER_2 TC_INTENSET_MC1 - #define INTENCLR_BIT_FOR_TIMER_2 TC_INTENCLR_MC1 - #define ID_TC_FOR_TIMER2 ID_TC4 - #define IRQn_FOR_TIMER2 TC4_IRQn - #define HANDLER_FOR_TIMER2 TC4_Handler - #define GCM_FOR_TIMER_2 GCM_TC4_TC5 - #endif + #if defined (_useTimer1) + #define TC_FOR_TIMER1 TC4 + #define CHANNEL_FOR_TIMER1 0 + #define INTENSET_BIT_FOR_TIMER_1 TC_INTENSET_MC0 + #define INTENCLR_BIT_FOR_TIMER_1 TC_INTENCLR_MC0 + #define INTFLAG_BIT_FOR_TIMER_1 TC_INTFLAG_MC0 + #define ID_TC_FOR_TIMER1 ID_TC4 + #define IRQn_FOR_TIMER1 TC4_IRQn + #define HANDLER_FOR_TIMER1 TC4_Handler + #define GCM_FOR_TIMER_1 GCM_TC4_TC5 + #endif + + #if defined (_useTimer2) + #define TC_FOR_TIMER2 TC4 + #define CHANNEL_FOR_TIMER2 1 + #define INTENSET_BIT_FOR_TIMER_2 TC_INTENSET_MC1 + #define INTENCLR_BIT_FOR_TIMER_2 TC_INTENCLR_MC1 + #define ID_TC_FOR_TIMER2 ID_TC4 + #define IRQn_FOR_TIMER2 TC4_IRQn + #define HANDLER_FOR_TIMER2 TC4_Handler + #define GCM_FOR_TIMER_2 GCM_TC4_TC5 + #endif #endif typedef enum {