Skip to content

Commit 59d869d

Browse files
WIP change SPI SERCOM clock source at runtime rather than compile-time
1 parent 498429e commit 59d869d

File tree

4 files changed

+253
-148
lines changed

4 files changed

+253
-148
lines changed

cores/arduino/SERCOM.cpp

Lines changed: 141 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

cores/arduino/SERCOM.h

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121

2222
#include "sam.h"
2323

24-
// SAMD51 has configurable MAX_SPI, else use peripheral clock default
24+
// SAMD51 has configurable MAX_SPI, else use peripheral clock default.
25+
// Update: changing MAX_SPI via compiler flags is DEPRECATED, because
26+
// this affects ALL SPI peripherals including some that should NOT be
27+
// changed (e.g. anything using SD card). Instead, use setClockSource().
28+
// This is left here for compatibility w/interim MAX_SPI-dependent code:
2529
#if defined(MAX_SPI)
2630
#define SERCOM_SPI_FREQ_REF (MAX_SPI * 2)
2731
#else
@@ -148,6 +152,17 @@ typedef enum
148152
WIRE_MASTER_NACK_ACTION
149153
} SercomMasterAckActionWire;
150154

155+
#if defined(__SAMD51__)
156+
typedef enum SercomClockSource {
157+
SERCOM_CLOCK_SOURCE_FCPU, // F_CPU clock (GCLK0)
158+
SERCOM_CLOCK_SOURCE_48M, // 48 MHz peripheral clock (GCLK1) (standard)
159+
SERCOM_CLOCK_SOURCE_100M, // 100 MHz peripheral clock (GCLK2)
160+
SERCOM_CLOCK_SOURCE_32K, // XOSC32K clock (GCLK3)
161+
SERCOM_CLOCK_SOURCE_12M, // 12 MHz peripheral clock (GCLK4)
162+
SERCOM_CLOCK_SOURCE_NO_CHANGE // Leave clock source setting unchanged
163+
};
164+
#endif // end __SAMD51__
165+
151166
class SERCOM
152167
{
153168
public:
@@ -178,7 +193,6 @@ class SERCOM
178193
/* ========== SPI ========== */
179194
void initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize charSize, SercomDataOrder dataOrder) ;
180195
void initSPIClock(SercomSpiClockMode clockMode, uint32_t baudrate) ;
181-
182196
void resetSPI( void ) ;
183197
void enableSPI( void ) ;
184198
void disableSPI( void ) ;
@@ -217,10 +231,18 @@ class SERCOM
217231
bool isRXNackReceivedWIRE( void ) ;
218232
int availableWIRE( void ) ;
219233
uint8_t readDataWIRE( void ) ;
234+
int8_t getSercomIndex(void);
235+
#if defined(__SAMD51__)
236+
void setClockSource(int idx, SercomClockSource src, bool core);
237+
uint32_t getFreqRef(void) { return freqRef; };
238+
#endif
220239

221240
private:
222241
Sercom* sercom;
223-
uint8_t calculateBaudrateSynchronous(uint32_t baudrate) ;
242+
#if defined(__SAMD51__)
243+
uint32_t freqRef;
244+
#endif
245+
uint8_t calculateBaudrateSynchronous(uint32_t baudrate);
224246
uint32_t division(uint32_t dividend, uint32_t divisor) ;
225247
void initClockNVIC( void ) ;
226248
};

0 commit comments

Comments
 (0)