@@ -305,7 +305,11 @@ void SERCOM::setBaudrateSPI(uint8_t divider)
305305{
306306 disableSPI (); // Register is enable-protected
307307
308+ #if defined(__SAMD51__)
309+ sercom->SPI .BAUD .reg = calculateBaudrateSynchronous (freqRef / divider);
310+ #else
308311 sercom->SPI .BAUD .reg = calculateBaudrateSynchronous (SERCOM_SPI_FREQ_REF / divider);
312+ #endif
309313
310314 enableSPI ();
311315}
@@ -364,9 +368,12 @@ bool SERCOM::isDataRegisterEmptySPI()
364368// return sercom->SPI.INTFLAG.bit.RXC;
365369// }
366370
367- uint8_t SERCOM::calculateBaudrateSynchronous (uint32_t baudrate)
368- {
371+ uint8_t SERCOM::calculateBaudrateSynchronous (uint32_t baudrate) {
372+ #if defined(__SAMD51__)
373+ uint16_t b = freqRef / (2 * baudrate);
374+ #else
369375 uint16_t b = SERCOM_SPI_FREQ_REF / (2 * baudrate);
376+ #endif
370377 if (b > 0 ) b--; // Don't -1 on baud calc if already at 0
371378 return b;
372379}
@@ -664,163 +671,152 @@ uint8_t SERCOM::readDataWIRE( void )
664671 }
665672}
666673
667-
668- void SERCOM::initClockNVIC ( void )
669- {
670674#if defined(__SAMD51__)
671- uint32_t clk_core, clk_slow;
672- IRQn_Type irq0, irq1, irq2, irq3;
673-
674- if (sercom == SERCOM0) {
675- clk_core = SERCOM0_GCLK_ID_CORE;
676- clk_slow = SERCOM0_GCLK_ID_SLOW;
677- irq0 = SERCOM0_0_IRQn;
678- irq1 = SERCOM0_1_IRQn;
679- irq2 = SERCOM0_2_IRQn;
680- irq3 = SERCOM0_3_IRQn;
681- } else if (sercom == SERCOM1) {
682- clk_core = SERCOM1_GCLK_ID_CORE;
683- clk_slow = SERCOM1_GCLK_ID_SLOW;
684- irq0 = SERCOM1_0_IRQn;
685- irq1 = SERCOM1_1_IRQn;
686- irq2 = SERCOM1_2_IRQn;
687- irq3 = SERCOM1_3_IRQn;
688- } else if (sercom == SERCOM2) {
689- clk_core = SERCOM2_GCLK_ID_CORE;
690- clk_slow = SERCOM2_GCLK_ID_SLOW;
691- irq0 = SERCOM2_0_IRQn;
692- irq1 = SERCOM2_1_IRQn;
693- irq2 = SERCOM2_2_IRQn;
694- irq3 = SERCOM2_3_IRQn;
695- } else if (sercom == SERCOM3) {
696- clk_core = SERCOM3_GCLK_ID_CORE;
697- clk_slow = SERCOM3_GCLK_ID_SLOW;
698- irq0 = SERCOM3_0_IRQn;
699- irq1 = SERCOM3_1_IRQn;
700- irq2 = SERCOM3_2_IRQn;
701- irq3 = SERCOM3_3_IRQn;
702- } else if (sercom == SERCOM4) {
703- clk_core = SERCOM4_GCLK_ID_CORE;
704- clk_slow = SERCOM4_GCLK_ID_SLOW;
705- irq0 = SERCOM4_0_IRQn;
706- irq1 = SERCOM4_1_IRQn;
707- irq2 = SERCOM4_2_IRQn;
708- irq3 = SERCOM4_3_IRQn;
709- } else if (sercom == SERCOM5) {
710- clk_core = SERCOM5_GCLK_ID_CORE;
711- clk_slow = SERCOM5_GCLK_ID_SLOW;
712- irq0 = SERCOM5_0_IRQn;
713- irq1 = SERCOM5_1_IRQn;
714- irq2 = SERCOM5_2_IRQn;
715- irq3 = SERCOM5_3_IRQn;
716- }
717- #if defined SERCOM6
718- else if (sercom == SERCOM6) {
719- clk_core = SERCOM6_GCLK_ID_CORE;
720- clk_slow = SERCOM6_GCLK_ID_SLOW;
721- irq0 = (SERCOM6_0_IRQn);
722- irq1 = (SERCOM6_1_IRQn);
723- irq2 = (SERCOM6_2_IRQn);
724- irq3 = (SERCOM6_3_IRQn);
725- }
726- #endif // end SERCOM6
727- #if defined SERCOM7
728- else if (sercom == SERCOM7) {
729- clk_core = SERCOM7_GCLK_ID_CORE;
730- clk_slow = SERCOM7_GCLK_ID_SLOW;
731- irq0 = (SERCOM7_0_IRQn);
732- irq1 = (SERCOM7_1_IRQn);
733- irq2 = (SERCOM7_2_IRQn);
734- irq3 = (SERCOM7_3_IRQn);
675+
676+ static const struct {
677+ Sercom *sercomPtr;
678+ uint8_t id_core;
679+ uint8_t id_slow;
680+ IRQn_Type irq[4 ];
681+ } sercomData[] = {
682+ { SERCOM0, SERCOM0_GCLK_ID_CORE, SERCOM0_GCLK_ID_SLOW,
683+ SERCOM0_0_IRQn, SERCOM0_1_IRQn, SERCOM0_2_IRQn, SERCOM0_3_IRQn },
684+ { SERCOM1, SERCOM1_GCLK_ID_CORE, SERCOM1_GCLK_ID_SLOW,
685+ SERCOM1_0_IRQn, SERCOM1_1_IRQn, SERCOM1_2_IRQn, SERCOM1_3_IRQn },
686+ { SERCOM2, SERCOM2_GCLK_ID_CORE, SERCOM2_GCLK_ID_SLOW,
687+ SERCOM2_0_IRQn, SERCOM2_1_IRQn, SERCOM2_2_IRQn, SERCOM2_3_IRQn },
688+ { SERCOM3, SERCOM3_GCLK_ID_CORE, SERCOM3_GCLK_ID_SLOW,
689+ SERCOM3_0_IRQn, SERCOM3_1_IRQn, SERCOM3_2_IRQn, SERCOM3_3_IRQn },
690+ { SERCOM4, SERCOM4_GCLK_ID_CORE, SERCOM4_GCLK_ID_SLOW,
691+ SERCOM4_0_IRQn, SERCOM4_1_IRQn, SERCOM4_2_IRQn, SERCOM4_3_IRQn },
692+ { SERCOM5, SERCOM5_GCLK_ID_CORE, SERCOM5_GCLK_ID_SLOW,
693+ SERCOM5_0_IRQn, SERCOM5_1_IRQn, SERCOM5_2_IRQn, SERCOM5_3_IRQn },
694+ #if defined(SERCOM6)
695+ { SERCOM6, SERCOM6_GCLK_ID_CORE, SERCOM6_GCLK_ID_SLOW,
696+ SERCOM6_0_IRQn, SERCOM6_1_IRQn, SERCOM6_2_IRQn, SERCOM6_3_IRQn },
697+ #endif
698+ #if defined(SERCOM7)
699+ { SERCOM7, SERCOM7_GCLK_ID_CORE, SERCOM7_GCLK_ID_SLOW,
700+ SERCOM7_0_IRQn, SERCOM7_1_IRQn, SERCOM7_2_IRQn, SERCOM7_3_IRQn },
701+ #endif
702+ };
703+
704+ #else // end if SAMD51 (prob SAMD21)
705+
706+ static const struct {
707+ Sercom *sercomPtr;
708+ uint8_t clock;
709+ IRQn_Type irqn;
710+ } sercomData[] = {
711+ SERCOM0, GCM_SERCOM0_CORE, SERCOM0_IRQn,
712+ SERCOM1, GCM_SERCOM1_CORE, SERCOM1_IRQn,
713+ SERCOM2, GCM_SERCOM2_CORE, SERCOM2_IRQn,
714+ SERCOM3, GCM_SERCOM3_CORE, SERCOM3_IRQn,
715+ #if defined(SERCOM4)
716+ SERCOM4, GCM_SERCOM4_CORE, SERCOM4_IRQn,
717+ #endif
718+ #if defined(SERCOM5)
719+ SERCOM5, GCM_SERCOM5_CORE, SERCOM5_IRQn,
720+ #endif
721+ };
722+
723+ #endif // end !SAMD51
724+
725+ int8_t SERCOM::getSercomIndex (void ) {
726+ for (uint8_t i=0 ; i<(sizeof (sercomData) / sizeof (sercomData[0 ])); i++) {
727+ if (sercom == sercomData[i].sercomPtr ) return i;
735728 }
736- #endif // end SERCOM7
737- NVIC_ClearPendingIRQ (irq0);
738- NVIC_ClearPendingIRQ (irq1);
739- NVIC_ClearPendingIRQ (irq2);
740- NVIC_ClearPendingIRQ (irq3);
741-
742- NVIC_SetPriority (irq0, (1 <<__NVIC_PRIO_BITS) - 1 );
743- NVIC_SetPriority (irq1, (1 <<__NVIC_PRIO_BITS) - 1 );
744- NVIC_SetPriority (irq2, (1 <<__NVIC_PRIO_BITS) - 1 );
745- NVIC_SetPriority (irq3, (1 <<__NVIC_PRIO_BITS) - 1 );
746-
747- NVIC_EnableIRQ (irq0);
748- NVIC_EnableIRQ (irq1);
749- NVIC_EnableIRQ (irq2);
750- NVIC_EnableIRQ (irq3);
751-
752- GCLK->PCHCTRL [clk_core].bit .CHEN = 0 ; // Disable core timer
753- while (GCLK->PCHCTRL [clk_core].bit .CHEN ); // Wait for disable
754- GCLK->PCHCTRL [clk_slow].bit .CHEN = 0 ; // Disable slow timer
755- while (GCLK->PCHCTRL [clk_slow].bit .CHEN ); // Wait for disable
729+ return -1 ;
730+ }
731+
732+ #if defined(__SAMD51__)
733+ // This is currently for overriding an SPI SERCOM's clock source only --
734+ // NOT for UART or WIRE SERCOMs, where it will have unintended consequences.
735+ // It does not check.
736+ void SERCOM::setClockSource (int idx, SercomClockSource src, bool core) {
737+
738+ uint8_t clk_id = core ? sercomData[idx].id_core : sercomData[idx].id_slow ;
739+
740+ GCLK->PCHCTRL [clk_id].bit .CHEN = 0 ; // Disable timer
741+ while (GCLK->PCHCTRL [clk_id].bit .CHEN ); // Wait for disable
756742
757- // SPI DMA speed is dictated by the "slow clock," so BOTH are set to the
758- // same clock source (clk_slow isn't sourced from XOSC32K as before).
759- // This might have power implications for sleep code.
760743 // From cores/arduino/startup.c:
761744 // GCLK0 = F_CPU
762745 // GCLK1 = 48 MHz
763746 // GCLK2 = 100 MHz
764747 // GCLK3 = XOSC32K
765748 // GCLK4 = 12 MHz
766- #if SERCOM_SPI_FREQ_REF == 48000000 // 48 MHz clock = GCLK1
767- GCLK->PCHCTRL [clk_core].reg = GCLK_PCHCTRL_GEN_GCLK1_Val |
768- (1 << GCLK_PCHCTRL_CHEN_Pos);
769- GCLK->PCHCTRL [clk_slow].reg = GCLK_PCHCTRL_GEN_GCLK1_Val |
770- (1 << GCLK_PCHCTRL_CHEN_Pos);
749+ if (src == SERCOM_CLOCK_SOURCE_FCPU) {
750+ GCLK->PCHCTRL [clk_id].reg =
751+ GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
752+ if (core) freqRef = F_CPU;
753+ } else if (src == SERCOM_CLOCK_SOURCE_48M) {
754+ GCLK->PCHCTRL [clk_id].reg =
755+ GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
756+ if (core) freqRef = 48000000 ;
757+ } else if (src == SERCOM_CLOCK_SOURCE_100M) {
758+ GCLK->PCHCTRL [clk_id].reg =
759+ GCLK_PCHCTRL_GEN_GCLK2_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
760+ if (core) freqRef = 100000000 ;
761+ } else if (src == SERCOM_CLOCK_SOURCE_32K) {
762+ GCLK->PCHCTRL [clk_id].reg =
763+ GCLK_PCHCTRL_GEN_GCLK3_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
764+ if (core) freqRef = 32768 ;
765+ } else if (src == SERCOM_CLOCK_SOURCE_12M) {
766+ GCLK->PCHCTRL [clk_id].reg =
767+ GCLK_PCHCTRL_GEN_GCLK4_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
768+ if (core) freqRef = 12000000 ;
769+ }
770+
771+ while (!GCLK->PCHCTRL [clk_id].bit .CHEN ); // Wait for clock enable
772+ }
773+ #endif
774+
775+ void SERCOM::initClockNVIC ( void )
776+ {
777+ int8_t idx = getSercomIndex ();
778+ if (idx < 0 ) return ; // We got a problem here
779+
780+ #if defined(__SAMD51__)
781+
782+ for (uint8_t i=0 ; i<4 ; i++) {
783+ NVIC_ClearPendingIRQ (sercomData[idx].irq [i]);
784+ NVIC_SetPriority (sercomData[idx].irq [i], SERCOM_NVIC_PRIORITY);
785+ NVIC_EnableIRQ (sercomData[idx].irq [i]);
786+ }
787+
788+ // A briefly-availabe but now deprecated feature had the SPI clock source
789+ // set via a compile-time setting (MAX_SPI)...problem was this affected
790+ // ALL SERCOMs, whereas some (anything read/write, e.g. SD cards) should
791+ // not exceed the standard 24 MHz setting. Newer code, if it needs faster
792+ // write-only SPI (e.g. to screen), should override the SERCOM clock on a
793+ // per-peripheral basis. Nonetheless, we check SERCOM_SPI_FREQ_REF here
794+ // (MAX_SPI * 2) to retain compatibility with any interim projects that
795+ // might have relied on the compile-time setting. But please, don't.
796+
797+ // SPI DMA speed is dictated by the "slow clock," so BOTH are set to the
798+ // same clock source (clk_slow isn't sourced from XOSC32K as before).
799+ // This might have power implications for sleep code.
800+
801+ #if SERCOM_SPI_FREQ_REF == F_CPU // F_CPU clock = GCLK0
802+ setClockSource (idx, SERCOM_CLOCK_SOURCE_FCPU, true ); // true = core clock
803+ setClockSource (idx, SERCOM_CLOCK_SOURCE_FCPU, false ); // false = slow clock
804+ #elif SERCOM_SPI_FREQ_REF == 48000000 // 48 MHz clock = GCLK1 (standard)
805+ setClockSource (idx, SERCOM_CLOCK_SOURCE_48M , true );
806+ setClockSource (idx, SERCOM_CLOCK_SOURCE_48M , false );
771807 #elif SERCOM_SPI_FREQ_REF == 100000000 // 100 MHz clock = GCLK2
772- GCLK->PCHCTRL [clk_core].reg = GCLK_PCHCTRL_GEN_GCLK2_Val |
773- (1 << GCLK_PCHCTRL_CHEN_Pos);
774- GCLK->PCHCTRL [clk_slow].reg = GCLK_PCHCTRL_GEN_GCLK2_Val |
775- (1 << GCLK_PCHCTRL_CHEN_Pos);
776- #elif SERCOM_SPI_FREQ_REF == F_CPU // F_CPU clock = GCLK0
777- GCLK->PCHCTRL [clk_core].reg = GCLK_PCHCTRL_GEN_GCLK0_Val |
778- (1 << GCLK_PCHCTRL_CHEN_Pos);
779- GCLK->PCHCTRL [clk_slow].reg = GCLK_PCHCTRL_GEN_GCLK0_Val |
780- (1 << GCLK_PCHCTRL_CHEN_Pos);
808+ setClockSource (idx, SERCOM_CLOCK_SOURCE_100M, true );
809+ setClockSource (idx, SERCOM_CLOCK_SOURCE_100M, false );
781810 #endif
782811
783- while (!GCLK->PCHCTRL [clk_core].bit .CHEN ); // Wait for core clock enable
784- while (!GCLK->PCHCTRL [clk_slow].bit .CHEN ); // Wait for slow clock enable
785-
786- #else // end if SAMD51
787-
788- IRQn_Type IdNvic=PendSV_IRQn ; // Dummy init to intercept potential error later
789-
790- uint8_t clockId = 0 ;
791- if (sercom == SERCOM0) {
792- clockId = GCM_SERCOM0_CORE;
793- IdNvic = SERCOM0_IRQn;
794- } else if (sercom == SERCOM1) {
795- clockId = GCM_SERCOM1_CORE;
796- IdNvic = SERCOM1_IRQn;
797- } else if (sercom == SERCOM2) {
798- clockId = GCM_SERCOM2_CORE;
799- IdNvic = SERCOM2_IRQn;
800- } else if (sercom == SERCOM3) {
801- clockId = GCM_SERCOM3_CORE;
802- IdNvic = SERCOM3_IRQn;
803- }
804- #if defined(SERCOM4)
805- else if (sercom == SERCOM4) {
806- clockId = GCM_SERCOM4_CORE;
807- IdNvic = SERCOM4_IRQn;
808- }
809- #endif // end SERCOM4
810- #if defined(SERCOM5)
811- else if (sercom == SERCOM5) {
812- clockId = GCM_SERCOM5_CORE;
813- IdNvic = SERCOM5_IRQn;
814- }
815- #endif // end SERCOM5
812+ #else // end if SAMD51 (prob SAMD21)
816813
817- if (IdNvic == PendSV_IRQn) {
818- return ; // We got a problem here
819- }
814+ uint8_t clockId = sercomData[idx].clock ;
815+ IRQn_Type IdNvic = sercomData[idx].irqn ;
820816
821- // Setting NVIC
817+ // Setting NVIC
822818 NVIC_ClearPendingIRQ (IdNvic);
823- NVIC_SetPriority (IdNvic, ( 1 <<__NVIC_PRIO_BITS) - 1 );
819+ NVIC_SetPriority (IdNvic, SERCOM_NVIC_PRIORITY );
824820 NVIC_EnableIRQ (IdNvic);
825821
826822 // Setting clock
@@ -830,5 +826,6 @@ void SERCOM::initClockNVIC( void )
830826 GCLK_CLKCTRL_CLKEN;
831827
832828 while (GCLK->STATUS .reg & GCLK_STATUS_SYNCBUSY); // Wait for synchronization
829+
833830#endif // end !SAMD51
834831}
0 commit comments