Skip to content

Commit e0a883c

Browse files
committed
Unify SERCOM clock frequency handling across UART/SPI/WIRE
1 parent 2917083 commit e0a883c

2 files changed

Lines changed: 71 additions & 45 deletions

File tree

cores/arduino/SERCOM.cpp

Lines changed: 67 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ SERCOM::SERCOM(Sercom* s)
3131
{
3232
sercom = s;
3333

34-
#if defined(__SAMD51__)
34+
#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__)
3535
// A briefly-available but now deprecated feature had the SPI clock source
3636
// set via a compile-time setting (MAX_SPI)...problem was this affected
3737
// ALL SERCOMs, whereas some (anything read/write, e.g. SD cards) should
@@ -40,8 +40,8 @@ SERCOM::SERCOM(Sercom* s)
4040
// per-peripheral basis. Nonetheless, we check SERCOM_SPI_FREQ_REF here
4141
// (MAX_SPI * 2) to retain compatibility with any interim projects that
4242
// might have relied on the compile-time setting. But please, don't.
43-
#if SERCOM_SPI_FREQ_REF == F_CPU // F_CPU clock = GCLK0
44-
clockSource = SERCOM_CLOCK_SOURCE_FCPU;
43+
#if SERCOM_SPI_FREQ_REF == F_CPU // F_CPU clock = GCLK0
44+
clockSource = SERCOM_CLOCK_SOURCE_100M;
4545
#elif SERCOM_SPI_FREQ_REF == 48000000 // 48 MHz clock = GCLK1 (standard)
4646
clockSource = SERCOM_CLOCK_SOURCE_48M;
4747
#elif SERCOM_SPI_FREQ_REF == 100000000 // 100 MHz clock = GCLK2
@@ -80,11 +80,7 @@ void SERCOM::initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint
8080
// Asynchronous fractional mode (Table 24-2 in datasheet)
8181
// BAUD = fref / (sampleRateValue * fbaud)
8282
// (multiply by 8, to calculate fractional piece)
83-
#if defined(__SAMD51__)
84-
uint32_t baudTimes8 = (SERCOM_FREQ_REF * 8) / (sampleRateValue * baudrate);
85-
#else
86-
uint32_t baudTimes8 = (SystemCoreClock * 8) / (sampleRateValue * baudrate);
87-
#endif
83+
uint32_t baudTimes8 = (freqRef * 8) / (sampleRateValue * baudrate);
8884

8985
sercom->USART.BAUD.FRAC.FP = (baudTimes8 % 8);
9086
sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8);
@@ -230,8 +226,8 @@ void SERCOM::initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize ch
230226
resetSPI();
231227
initClockNVIC();
232228

233-
#if defined(__SAMD51__)
234-
sercom->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_MODE(0x3) | // master mode
229+
#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__)
230+
sercom->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_MODE(0x3) | // master mode
235231
SERCOM_SPI_CTRLA_DOPO(mosi) |
236232
SERCOM_SPI_CTRLA_DIPO(miso) |
237233
dataOrder << SERCOM_SPI_CTRLA_DORD_Pos;
@@ -322,13 +318,7 @@ SercomDataOrder SERCOM::getDataOrderSPI()
322318
void SERCOM::setBaudrateSPI(uint8_t divider)
323319
{
324320
disableSPI(); // Register is enable-protected
325-
326-
#if defined(__SAMD51__)
327321
sercom->SPI.BAUD.reg = calculateBaudrateSynchronous(freqRef / divider);
328-
#else
329-
sercom->SPI.BAUD.reg = calculateBaudrateSynchronous(SERCOM_SPI_FREQ_REF / divider);
330-
#endif
331-
332322
enableSPI();
333323
}
334324

@@ -386,13 +376,11 @@ bool SERCOM::isDataRegisterEmptySPI()
386376
// return sercom->SPI.INTFLAG.bit.RXC;
387377
//}
388378

389-
uint8_t SERCOM::calculateBaudrateSynchronous(uint32_t baudrate) {
390-
#if defined(__SAMD51__)
379+
uint8_t SERCOM::calculateBaudrateSynchronous(uint32_t baudrate)
380+
{
391381
uint16_t b = freqRef / (2 * baudrate);
392-
#else
393-
uint16_t b = SERCOM_SPI_FREQ_REF / (2 * baudrate);
394-
#endif
395-
if(b > 0) b--; // Don't -1 on baud calc if already at 0
382+
if (b > 0)
383+
b--; // Don't -1 on baud calc if already at 0
396384
return b;
397385
}
398386

@@ -490,12 +478,10 @@ void SERCOM::initMasterWIRE( uint32_t baudrate )
490478
// Enable all interrupts
491479
// sercom->I2CM.INTENSET.reg = SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB | SERCOM_I2CM_INTENSET_ERROR ;
492480

493-
// Synchronous arithmetic baudrate
494-
#if defined(__SAMD51__)
495-
sercom->I2CM.BAUD.bit.BAUD = SERCOM_FREQ_REF / ( 2 * baudrate) - 1 ;
496-
#else
497-
sercom->I2CM.BAUD.bit.BAUD = SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000));
498-
#endif
481+
if (sercom->I2CM.CTRLA.bit.SPEED == 0x2)
482+
sercom->I2CM.BAUD.bit.HSBAUD = freqRef / (2 * baudrate) - 1;
483+
else
484+
sercom->I2CM.BAUD.bit.BAUD = freqRef / (2 * baudrate) - 5 - freqRef * WIRE_RISE_TIME_NANOSECONDS / (2 * 1e9f);
499485
}
500486

501487
void SERCOM::prepareNackBitWIRE( void )
@@ -727,7 +713,7 @@ uint8_t SERCOM::readDataWIRE( void )
727713
}
728714
}
729715

730-
#if defined(__SAMD51__)
716+
#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__)
731717

732718
static const struct {
733719
Sercom *sercomPtr;
@@ -785,7 +771,47 @@ int8_t SERCOM::getSercomIndex(void) {
785771
return -1;
786772
}
787773

788-
#if defined(__SAMD51__)
774+
uint32_t SERCOM::getSercomFreqRef(void)
775+
{
776+
#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__)
777+
int8_t idx = getSercomIndex();
778+
uint8_t gen = 1; // default to GCLK1 (48 MHz) if we can't resolve
779+
780+
if (idx >= 0)
781+
{
782+
uint8_t pch = sercomData[idx].id_core;
783+
gen = GCLK->PCHCTRL[pch].bit.GEN;
784+
}
785+
786+
switch (gen)
787+
{
788+
case 0:
789+
freqRef = 100000000UL;
790+
break;
791+
case 1:
792+
freqRef = 48000000UL;
793+
break;
794+
case 2:
795+
freqRef = 100000000UL;
796+
break;
797+
case 3:
798+
freqRef = 32768UL;
799+
break;
800+
case 4:
801+
freqRef = 12000000UL;
802+
break;
803+
default:
804+
freqRef = 48000000UL;
805+
break;
806+
}
807+
#else
808+
freqRef = SystemCoreClock;
809+
#endif
810+
811+
return freqRef;
812+
}
813+
814+
#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__)
789815
// This is currently for overriding an SPI SERCOM's clock source only --
790816
// NOT for UART or WIRE SERCOMs, where it will have unintended consequences.
791817
// It does not check.
@@ -804,16 +830,19 @@ void SERCOM::setClockSource(int8_t idx, SercomClockSource src, bool core) {
804830
if(core) clockSource = src; // Save SercomClockSource value
805831

806832
// From cores/arduino/startup.c:
807-
// GCLK0 = F_CPU
833+
// GCLK0 = F_CPU (this is 120 MHz and exceeds SERCOM maximum)
808834
// GCLK1 = 48 MHz
809835
// GCLK2 = 100 MHz
810836
// GCLK3 = XOSC32K
811837
// GCLK4 = 12 MHz
812838
if(src == SERCOM_CLOCK_SOURCE_FCPU) {
813839
GCLK->PCHCTRL[clk_id].reg =
814-
GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
815-
if(core) freqRef = F_CPU; // Save clock frequency value
816-
} else if(src == SERCOM_CLOCK_SOURCE_48M) {
840+
GCLK_PCHCTRL_GEN_GCLK2_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); // Guard Sercom from exceeding 100 MHz maximum
841+
if (core)
842+
freqRef = 100000000; // Save clock frequency value
843+
}
844+
else if (src == SERCOM_CLOCK_SOURCE_48M)
845+
{
817846
GCLK->PCHCTRL[clk_id].reg =
818847
GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
819848
if(core) freqRef = 48000000;
@@ -840,21 +869,15 @@ void SERCOM::initClockNVIC( void )
840869
int8_t idx = getSercomIndex();
841870
if(idx < 0) return; // We got a problem here
842871

843-
#if defined(__SAMD51__)
872+
#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__)
844873

845874
for(uint8_t i=0; i<4; i++) {
846875
NVIC_ClearPendingIRQ(sercomData[idx].irq[i]);
847876
NVIC_SetPriority(sercomData[idx].irq[i], SERCOM_NVIC_PRIORITY);
848877
NVIC_EnableIRQ(sercomData[idx].irq[i]);
849878
}
850879

851-
// SPI DMA speed is dictated by the "slow clock" (I think...maybe) so
852-
// BOTH are set to the same clock source (clk_slow isn't sourced from
853-
// XOSC32K as in prior versions of SAMD core).
854-
// This might have power implications for sleep code.
855-
856-
setClockSource(idx, clockSource, true); // true = core clock
857-
setClockSource(idx, clockSource, false); // false = slow clock
880+
setClockSource(idx, clockSource, true); // true = core clock
858881

859882
#else // end if SAMD51 (prob SAMD21)
860883

@@ -875,4 +898,6 @@ void SERCOM::initClockNVIC( void )
875898
while(GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); // Wait for synchronization
876899

877900
#endif // end !SAMD51
901+
902+
getSercomFreqRef();
878903
}

cores/arduino/SERCOM.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ class SERCOM
237237
int availableWIRE( void ) ;
238238
uint8_t readDataWIRE( void ) ;
239239
int8_t getSercomIndex(void);
240+
uint32_t getSercomFreqRef(void);
240241
#if defined(__SAMD51__)
241242
// SERCOM clock source override is only available on
242243
// SAMD51 (not 21) ... but these functions are declared
@@ -254,10 +255,10 @@ class SERCOM
254255
#endif
255256

256257
private:
257-
Sercom* sercom;
258-
#if defined(__SAMD51__)
258+
Sercom *sercom;
259+
uint32_t freqRef = 48000000; // Frequency corresponding to clockSource
260+
#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__)
259261
SercomClockSource clockSource;
260-
uint32_t freqRef; // Frequency corresponding to clockSource
261262
#endif
262263
uint8_t calculateBaudrateSynchronous(uint32_t baudrate);
263264
uint32_t division(uint32_t dividend, uint32_t divisor) ;

0 commit comments

Comments
 (0)