@@ -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()
322318void 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
@@ -488,14 +476,37 @@ void SERCOM::initMasterWIRE( uint32_t baudrate )
488476
489477
490478 // Enable all interrupts
491- // sercom->I2CM.INTENSET.reg = SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB | SERCOM_I2CM_INTENSET_ERROR ;
479+ // sercom->I2CM.INTENSET.reg = SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB | SERCOM_I2CM_INTENSET_ERROR ;
480+
481+ // Determine speed mode based on requested baudrate
482+ const uint32_t topSpeeds[3 ] = {400000 , 1000000 , 3400000 }; // {(sm/fm), (fm+), (hs)}
483+ uint8_t speedBit;
484+ uint8_t clockStretchMode; // See: 28.6.2.4.6 (SERCOM I2C Highspeed mode)
485+
486+ if (baudrate <= topSpeeds[0 ]) {
487+ speedBit = 0 ; // Standard/Fast mode up to 400 khz
488+ clockStretchMode = 0 ;
489+ } else if (baudrate <= topSpeeds[1 ]) {
490+ speedBit = 1 ; // Fast mode+ up to 1 Mhz
491+ clockStretchMode = 0 ;
492+ } else {
493+ // High speed up to 3.4 Mhz
494+ speedBit = 2 ;
495+ clockStretchMode = 1 ;
496+ }
492497
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
498+ sercom->I2CM .CTRLA .bit .SPEED = speedBit;
499+ sercom->I2CM .CTRLA .bit .SCLSM = clockStretchMode;
500+
501+ uint32_t minBaudrate = freqRef / 512 ; // BAUD = 255: SAMD51(@100MHz) ~195kHz, SAMD21 ~94kHz
502+ uint32_t maxBaudrate = topSpeeds[speedBit];
503+ baudrate = max (minBaudrate, min (baudrate, maxBaudrate));
504+
505+ if (speedBit == 0x2 )
506+ sercom->I2CM .BAUD .bit .HSBAUD = freqRef / (2 * baudrate) - 1 ;
507+ else
508+ sercom->I2CM .BAUD .bit .BAUD = freqRef / (2 * baudrate) - 5 -
509+ (freqRef/1000000ul * WIRE_RISE_TIME_NANOSECONDS) / 2000 ;
499510}
500511
501512void SERCOM::prepareNackBitWIRE ( void )
@@ -551,7 +562,8 @@ bool SERCOM::startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag
551562 }
552563
553564 // Send start and address
554- sercom->I2CM .ADDR .bit .ADDR = address;
565+ sercom->I2CM .ADDR .reg = SERCOM_I2CM_ADDR_ADDR (address) |
566+ ((sercom->I2CM .CTRLA .bit .SPEED == 0x2 ) ? SERCOM_I2CM_ADDR_HS : 0 );
555567
556568 // Address Transmitted
557569 if ( flag == WIRE_WRITE_FLAG ) // Write mode
@@ -727,7 +739,7 @@ uint8_t SERCOM::readDataWIRE( void )
727739 }
728740}
729741
730- #if defined(__SAMD51__)
742+ #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__)
731743
732744static const struct {
733745 Sercom *sercomPtr;
@@ -785,7 +797,47 @@ int8_t SERCOM::getSercomIndex(void) {
785797 return -1 ;
786798}
787799
788- #if defined(__SAMD51__)
800+ uint32_t SERCOM::getSercomFreqRef (void )
801+ {
802+ #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__)
803+ int8_t idx = getSercomIndex ();
804+ uint8_t gen = 1 ; // default to GCLK1 (48 MHz) if we can't resolve
805+
806+ if (idx >= 0 )
807+ {
808+ uint8_t pch = sercomData[idx].id_core ;
809+ gen = GCLK->PCHCTRL [pch].bit .GEN ;
810+ }
811+
812+ switch (gen)
813+ {
814+ case 0 :
815+ freqRef = 100000000UL ;
816+ break ;
817+ case 1 :
818+ freqRef = 48000000UL ;
819+ break ;
820+ case 2 :
821+ freqRef = 100000000UL ;
822+ break ;
823+ case 3 :
824+ freqRef = 32768UL ;
825+ break ;
826+ case 4 :
827+ freqRef = 12000000UL ;
828+ break ;
829+ default :
830+ freqRef = 48000000UL ;
831+ break ;
832+ }
833+ #else
834+ freqRef = SystemCoreClock;
835+ #endif
836+
837+ return freqRef;
838+ }
839+
840+ #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__)
789841// This is currently for overriding an SPI SERCOM's clock source only --
790842// NOT for UART or WIRE SERCOMs, where it will have unintended consequences.
791843// It does not check.
@@ -804,16 +856,19 @@ void SERCOM::setClockSource(int8_t idx, SercomClockSource src, bool core) {
804856 if (core) clockSource = src; // Save SercomClockSource value
805857
806858 // From cores/arduino/startup.c:
807- // GCLK0 = F_CPU
859+ // GCLK0 = F_CPU (this is 120 MHz and exceeds SERCOM maximum)
808860 // GCLK1 = 48 MHz
809861 // GCLK2 = 100 MHz
810862 // GCLK3 = XOSC32K
811863 // GCLK4 = 12 MHz
812864 if (src == SERCOM_CLOCK_SOURCE_FCPU) {
813865 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) {
866+ GCLK_PCHCTRL_GEN_GCLK2_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); // Guard Sercom from exceeding 100 MHz maximum
867+ if (core)
868+ freqRef = 100000000 ; // Save clock frequency value
869+ }
870+ else if (src == SERCOM_CLOCK_SOURCE_48M)
871+ {
817872 GCLK->PCHCTRL [clk_id].reg =
818873 GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
819874 if (core) freqRef = 48000000 ;
@@ -840,21 +895,15 @@ void SERCOM::initClockNVIC( void )
840895 int8_t idx = getSercomIndex ();
841896 if (idx < 0 ) return ; // We got a problem here
842897
843- #if defined(__SAMD51__)
898+ #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__)
844899
845900 for (uint8_t i=0 ; i<4 ; i++) {
846901 NVIC_ClearPendingIRQ (sercomData[idx].irq [i]);
847902 NVIC_SetPriority (sercomData[idx].irq [i], SERCOM_NVIC_PRIORITY);
848903 NVIC_EnableIRQ (sercomData[idx].irq [i]);
849904 }
850905
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
906+ setClockSource (idx, clockSource, true ); // true = core clock
858907
859908#else // end if SAMD51 (prob SAMD21)
860909
@@ -875,4 +924,6 @@ void SERCOM::initClockNVIC( void )
875924 while (GCLK->STATUS .reg & GCLK_STATUS_SYNCBUSY); // Wait for synchronization
876925
877926#endif // end !SAMD51
927+
928+ getSercomFreqRef ();
878929}
0 commit comments