From 54309a567604610c3ee92d590e8f7d3404a71401 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sat, 20 Jun 2026 08:29:32 -0400 Subject: [PATCH 01/79] Normalize variant SERCOM peripheral and IRQ handler macros --- variants/arduino_mzero/variant.cpp | 8 ++++---- variants/arduino_mzero/variant.h | 5 +++++ variants/arduino_zero/variant.cpp | 8 ++++---- variants/arduino_zero/variant.h | 5 +++++ variants/blm_badge/variant.cpp | 4 ++-- variants/blm_badge/variant.h | 2 ++ variants/circuitplay/variant.cpp | 4 ++-- variants/circuitplay/variant.h | 5 +++++ variants/crickit_m0/variant.cpp | 4 ++-- variants/crickit_m0/variant.h | 2 ++ variants/feather_m0/variant.cpp | 8 ++++---- variants/feather_m0/variant.h | 5 +++++ variants/feather_m0_express/variant.cpp | 4 ++-- variants/feather_m0_express/variant.h | 4 ++++ variants/feather_m4/variant.cpp | 10 +++++----- variants/feather_m4/variant.h | 9 +++++++++ variants/feather_m4_can/variant.cpp | 10 +++++----- variants/feather_m4_can/variant.h | 9 +++++++++ variants/gemma_m0/variant.cpp | 4 ++-- variants/gemma_m0/variant.h | 3 +++ variants/grand_central_m4/variant.cpp | 10 +++++----- variants/grand_central_m4/variant.h | 13 +++++++++++++ variants/hallowing_m0_express/variant.cpp | 4 ++-- variants/hallowing_m0_express/variant.h | 4 ++++ variants/hallowing_m4/variant.cpp | 10 +++++----- variants/hallowing_m4/variant.h | 13 +++++++++++++ variants/itsybitsy_m0/variant.cpp | 4 ++-- variants/itsybitsy_m0/variant.h | 4 ++++ variants/itsybitsy_m4/variant.cpp | 10 +++++----- variants/itsybitsy_m4/variant.h | 9 +++++++++ variants/matrixportal_m4/variant.cpp | 20 ++++++++++---------- variants/matrixportal_m4/variant.h | 18 ++++++++++++++++++ variants/metro_m0/variant.cpp | 4 ++-- variants/metro_m0/variant.h | 4 ++++ variants/metro_m4/variant.cpp | 10 +++++----- variants/metro_m4/variant.h | 9 +++++++++ variants/metro_m4_airlift/variant.cpp | 20 ++++++++++---------- variants/metro_m4_airlift/variant.h | 14 ++++++++++++++ variants/mkr1000/variant.cpp | 4 ++-- variants/mkr1000/variant.h | 4 ++++ variants/mkrfox1200/variant.cpp | 4 ++-- variants/mkrfox1200/variant.h | 4 ++++ variants/mkrgsm1400/variant.cpp | 8 ++++---- variants/mkrgsm1400/variant.h | 5 +++++ variants/mkrnb1500/variant.cpp | 8 ++++---- variants/mkrnb1500/variant.h | 5 +++++ variants/mkrwan1300/variant.cpp | 8 ++++---- variants/mkrwan1300/variant.h | 6 ++++++ variants/mkrwifi1010/variant.cpp | 8 ++++---- variants/mkrwifi1010/variant.h | 6 ++++++ variants/mkrzero/variant.cpp | 4 ++-- variants/mkrzero/variant.h | 4 ++++ variants/monster_m4sk/variant.h | 12 ++++++++++++ variants/neokeytrinkey_m0/variant.h | 1 + variants/neotrinkey_m0/variant.h | 1 + variants/pirkey/variant.cpp | 4 ++-- variants/pirkey/variant.h | 2 ++ variants/pixeltrinkey_m0/variant.h | 1 + variants/proxlighttrinkey_m0/variant.h | 1 + variants/pybadge_airlift_m4/variant.cpp | 10 +++++----- variants/pybadge_airlift_m4/variant.h | 13 +++++++++++++ variants/pybadge_m4/variant.cpp | 10 +++++----- variants/pybadge_m4/variant.h | 17 +++++++++++++++++ variants/pygamer_advance_m4/variant.cpp | 10 +++++----- variants/pygamer_advance_m4/variant.h | 13 +++++++++++++ variants/pygamer_m4/variant.cpp | 10 +++++----- variants/pygamer_m4/variant.h | 13 +++++++++++++ variants/pyportal_m4/variant.cpp | 10 +++++----- variants/pyportal_m4/variant.h | 9 +++++++++ variants/pyportal_m4_titano/variant.cpp | 10 +++++----- variants/pyportal_m4_titano/variant.h | 9 +++++++++ variants/qtpy_m0/variant.cpp | 4 ++-- variants/qtpy_m0/variant.h | 4 ++++ variants/rotarytrinkey_m0/variant.h | 1 + variants/sht4xtrinkey_m0/variant.h | 1 + variants/slidetrinkey_m0/variant.h | 1 + variants/trellis_m4/variant.cpp | 10 +++++----- variants/trellis_m4/variant.h | 9 +++++++++ variants/trinket_m0/variant.cpp | 4 ++-- variants/trinket_m0/variant.h | 3 +++ variants/trrstrinkey_m0/variant.h | 1 + variants/zero_radio/variant.cpp | 4 ++-- variants/zero_radio/variant.h | 4 ++++ 83 files changed, 430 insertions(+), 143 deletions(-) diff --git a/variants/arduino_mzero/variant.cpp b/variants/arduino_mzero/variant.cpp index 7c094b172..f2e8d7ce3 100644 --- a/variants/arduino_mzero/variant.cpp +++ b/variants/arduino_mzero/variant.cpp @@ -204,14 +204,14 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom0, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -Uart Serial( &sercom5, PIN_SERIAL_RX, PIN_SERIAL_TX, PAD_SERIAL_RX, PAD_SERIAL_TX ) ; -void SERCOM0_Handler() +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial( &PERIPH_SERIAL, PIN_SERIAL_RX, PIN_SERIAL_TX, PAD_SERIAL_RX, PAD_SERIAL_TX ) ; +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } -void SERCOM5_Handler() +void SERIAL_IT_HANDLER() { Serial.IrqHandler(); } diff --git a/variants/arduino_mzero/variant.h b/variants/arduino_mzero/variant.h index b48fde2ed..f5298b6cc 100755 --- a/variants/arduino_mzero/variant.h +++ b/variants/arduino_mzero/variant.h @@ -114,12 +114,16 @@ static const uint8_t DAC0 = PIN_DAC0; #define PIN_SERIAL_TX (35ul) #define PAD_SERIAL_TX (UART_TX_PAD_2) #define PAD_SERIAL_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL sercom5 +#define SERIAL_IT_HANDLER SERCOM5_Handler // Serial1 #define PIN_SERIAL1_RX (0ul) #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom0 +#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces @@ -132,6 +136,7 @@ static const uint8_t DAC0 = PIN_DAC0; #define PERIPH_SPI sercom4 #define PAD_SPI_TX SPI_PAD_2_SCK_3 #define PAD_SPI_RX SERCOM_RX_PAD_0 +#define SPI_IT_HANDLER SERCOM4_Handler static const uint8_t SS = 14; //GND static const uint8_t MOSI = PIN_SPI_MOSI; diff --git a/variants/arduino_zero/variant.cpp b/variants/arduino_zero/variant.cpp index dfbaf745a..3653f1bd1 100644 --- a/variants/arduino_zero/variant.cpp +++ b/variants/arduino_zero/variant.cpp @@ -213,14 +213,14 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom0, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -Uart Serial5( &sercom5, PIN_SERIAL_RX, PIN_SERIAL_TX, PAD_SERIAL_RX, PAD_SERIAL_TX ) ; -void SERCOM0_Handler() +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial5( &PERIPH_SERIAL5, PIN_SERIAL_RX, PIN_SERIAL_TX, PAD_SERIAL_RX, PAD_SERIAL_TX ) ; +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } -void SERCOM5_Handler() +void SERIAL5_IT_HANDLER() { Serial5.IrqHandler(); } diff --git a/variants/arduino_zero/variant.h b/variants/arduino_zero/variant.h index 196ef773b..4ecc0f65f 100644 --- a/variants/arduino_zero/variant.h +++ b/variants/arduino_zero/variant.h @@ -121,12 +121,16 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL_TX (30ul) #define PAD_SERIAL_TX (UART_TX_PAD_2) #define PAD_SERIAL_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL5 sercom5 +#define SERIAL5_IT_HANDLER SERCOM5_Handler // Serial1 #define PIN_SERIAL1_RX (0ul) #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom0 +#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces @@ -139,6 +143,7 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom4 #define PAD_SPI_TX SPI_PAD_2_SCK_3 #define PAD_SPI_RX SERCOM_RX_PAD_0 +#define SPI_IT_HANDLER SERCOM4_Handler static const uint8_t SS = PIN_A2 ; // SERCOM4 last PAD is present on A2 but HW SS isn't used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI ; diff --git a/variants/blm_badge/variant.cpp b/variants/blm_badge/variant.cpp index 805b6e262..c83614ce1 100644 --- a/variants/blm_badge/variant.cpp +++ b/variants/blm_badge/variant.cpp @@ -86,9 +86,9 @@ SERCOM sercom1( SERCOM1 ) ; SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; -Uart Serial1( &sercom0, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM0_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } diff --git a/variants/blm_badge/variant.h b/variants/blm_badge/variant.h index dbee168d1..c63af106c 100644 --- a/variants/blm_badge/variant.h +++ b/variants/blm_badge/variant.h @@ -118,6 +118,8 @@ static const uint8_t DAC0 = PIN_DAC0; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_2) #define PIN_SERIAL1_TX (2ul) // captouch pad #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom0 +#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces diff --git a/variants/circuitplay/variant.cpp b/variants/circuitplay/variant.cpp index 0e39cf35e..fca2c4f4e 100644 --- a/variants/circuitplay/variant.cpp +++ b/variants/circuitplay/variant.cpp @@ -112,9 +112,9 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom4, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM4_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } diff --git a/variants/circuitplay/variant.h b/variants/circuitplay/variant.h index 7a0a5a55c..928d7445c 100644 --- a/variants/circuitplay/variant.h +++ b/variants/circuitplay/variant.h @@ -154,6 +154,7 @@ static const uint8_t DAC0 = PIN_DAC0; #define PERIPH_SPI sercom3 #define PAD_SPI_TX SPI_PAD_2_SCK_3 // MOSI / SCK #define PAD_SPI_RX SERCOM_RX_PAD_0 // MISO +#define SPI_IT_HANDLER SERCOM3_Handler static const uint8_t SS = 33 ; static const uint8_t MOSI = PIN_SPI_MOSI ; @@ -168,6 +169,7 @@ static const uint8_t SCK = PIN_SPI_SCK ; #define PERIPH_SPI1 sercom5 #define PAD_SPI1_TX SPI_PAD_0_SCK_1 // MOSI / SCK #define PAD_SPI1_RX SERCOM_RX_PAD_2 // MISO not avail +#define SPI1_IT_HANDLER SERCOM5_Handler static const uint8_t SS1 = 42 ; static const uint8_t MOSI1 = PIN_SPI_MOSI ; @@ -183,6 +185,7 @@ static const uint8_t SCK1 = PIN_SPI_SCK ; #define PERIPH_SPI2 sercom0 #define PAD_SPI2_TX SPI_PAD_2_SCK_3 // MOSI / SCK #define PAD_SPI2_RX SERCOM_RX_PAD_1 // MISO not avail +#define SPI2_IT_HANDLER SERCOM0_Handler static const uint8_t SS2 = 5 ; static const uint8_t MOSI2 = PIN_SPI2_MOSI ; @@ -248,6 +251,8 @@ extern Uart Serial1; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) +#define PERIPH_SERIAL1 sercom4 +#define SERIAL1_IT_HANDLER SERCOM4_Handler #endif // __cplusplus diff --git a/variants/crickit_m0/variant.cpp b/variants/crickit_m0/variant.cpp index 944e731e4..a8745f6d1 100644 --- a/variants/crickit_m0/variant.cpp +++ b/variants/crickit_m0/variant.cpp @@ -103,9 +103,9 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM5_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } diff --git a/variants/crickit_m0/variant.h b/variants/crickit_m0/variant.h index 9b7646578..276ae4429 100644 --- a/variants/crickit_m0/variant.h +++ b/variants/crickit_m0/variant.h @@ -161,6 +161,8 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_RX (36ul) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER SERCOM5_Handler /* * SPI Interfaces diff --git a/variants/feather_m0/variant.cpp b/variants/feather_m0/variant.cpp index f9e76061c..10c17a592 100644 --- a/variants/feather_m0/variant.cpp +++ b/variants/feather_m0/variant.cpp @@ -125,14 +125,14 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom0, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -Uart Serial5( &sercom5, PIN_SERIAL_RX, PIN_SERIAL_TX, PAD_SERIAL_RX, PAD_SERIAL_TX ) ; -void SERCOM0_Handler() +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial5( &PERIPH_SERIAL5, PIN_SERIAL_RX, PIN_SERIAL_TX, PAD_SERIAL_RX, PAD_SERIAL_TX ) ; +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } -void SERCOM5_Handler() +void SERIAL5_IT_HANDLER() { Serial5.IrqHandler(); } diff --git a/variants/feather_m0/variant.h b/variants/feather_m0/variant.h index 20ecbbbaf..baf4b6726 100644 --- a/variants/feather_m0/variant.h +++ b/variants/feather_m0/variant.h @@ -122,12 +122,16 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL_TX (30ul) #define PAD_SERIAL_TX (UART_TX_PAD_2) #define PAD_SERIAL_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL5 sercom5 +#define SERIAL5_IT_HANDLER SERCOM5_Handler // Serial1 #define PIN_SERIAL1_RX (0ul) #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom0 +#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces @@ -140,6 +144,7 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom4 #define PAD_SPI_TX SPI_PAD_2_SCK_3 #define PAD_SPI_RX SERCOM_RX_PAD_0 +#define SPI_IT_HANDLER SERCOM4_Handler static const uint8_t SS = PIN_A2 ; // SERCOM4 last PAD is present on A2 but HW SS isn't used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI ; diff --git a/variants/feather_m0_express/variant.cpp b/variants/feather_m0_express/variant.cpp index a3e87b89e..e177c6a3f 100644 --- a/variants/feather_m0_express/variant.cpp +++ b/variants/feather_m0_express/variant.cpp @@ -119,9 +119,9 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom0, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM0_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } diff --git a/variants/feather_m0_express/variant.h b/variants/feather_m0_express/variant.h index f4d3ae9bf..3eb2bd851 100644 --- a/variants/feather_m0_express/variant.h +++ b/variants/feather_m0_express/variant.h @@ -142,6 +142,8 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom0 +#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces @@ -154,6 +156,7 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom4 #define PAD_SPI_TX SPI_PAD_2_SCK_3 #define PAD_SPI_RX SERCOM_RX_PAD_0 +#define SPI_IT_HANDLER SERCOM4_Handler static const uint8_t SS = PIN_A2 ; // SERCOM4 last PAD is present on A2 but HW SS isn't used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI ; @@ -167,6 +170,7 @@ static const uint8_t SCK = PIN_SPI_SCK ; #define PERIPH_SPI1 sercom2 #define PAD_SPI1_TX SPI_PAD_0_SCK_1 #define PAD_SPI1_RX SERCOM_RX_PAD_2 +#define SPI1_IT_HANDLER SERCOM2_Handler static const uint8_t SS1 = 39 ; // HW SS isn't used. Set here only for reference. static const uint8_t MOSI1 = PIN_SPI1_MOSI ; diff --git a/variants/feather_m4/variant.cpp b/variants/feather_m4/variant.cpp index 88ba1ff6c..e550ee3e5 100644 --- a/variants/feather_m4/variant.cpp +++ b/variants/feather_m4/variant.cpp @@ -120,21 +120,21 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM5_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM5_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM5_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM5_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } diff --git a/variants/feather_m4/variant.h b/variants/feather_m4/variant.h index 6be35dccb..44529c810 100644 --- a/variants/feather_m4/variant.h +++ b/variants/feather_m4/variant.h @@ -130,6 +130,11 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER_0 SERCOM5_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM5_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM5_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM5_3_Handler /* * SPI Interfaces @@ -142,6 +147,10 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom1 #define PAD_SPI_TX SPI_PAD_3_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER_0 SERCOM1_0_Handler +#define SPI_IT_HANDLER_1 SERCOM1_1_Handler +#define SPI_IT_HANDLER_2 SERCOM1_2_Handler +#define SPI_IT_HANDLER_3 SERCOM1_3_Handler static const uint8_t SS = 9 ; // SERCOM1 last PAD is present on d9 but HW SS isn't used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI ; diff --git a/variants/feather_m4_can/variant.cpp b/variants/feather_m4_can/variant.cpp index 64d982e5e..f74581804 100644 --- a/variants/feather_m4_can/variant.cpp +++ b/variants/feather_m4_can/variant.cpp @@ -128,21 +128,21 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM5_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM5_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM5_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM5_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } diff --git a/variants/feather_m4_can/variant.h b/variants/feather_m4_can/variant.h index 853086c65..b25d99673 100644 --- a/variants/feather_m4_can/variant.h +++ b/variants/feather_m4_can/variant.h @@ -131,6 +131,11 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER_0 SERCOM5_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM5_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM5_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM5_3_Handler /* * SPI Interfaces @@ -143,6 +148,10 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom1 #define PAD_SPI_TX SPI_PAD_3_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER_0 SERCOM1_0_Handler +#define SPI_IT_HANDLER_1 SERCOM1_1_Handler +#define SPI_IT_HANDLER_2 SERCOM1_2_Handler +#define SPI_IT_HANDLER_3 SERCOM1_3_Handler static const uint8_t SS = 9 ; // SERCOM1 last PAD is present on d9 but HW SS isn't used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI ; diff --git a/variants/gemma_m0/variant.cpp b/variants/gemma_m0/variant.cpp index c0fa9700e..646f743b1 100644 --- a/variants/gemma_m0/variant.cpp +++ b/variants/gemma_m0/variant.cpp @@ -83,13 +83,13 @@ SERCOM sercom1( SERCOM1 ) ; SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; -Uart Serial1( &sercom0, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; // If wire.h isnt included, just do nothing void WIRE_IT_HANDLER(void) __attribute__ ((weak)); -void SERCOM0_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); WIRE_IT_HANDLER(); diff --git a/variants/gemma_m0/variant.h b/variants/gemma_m0/variant.h index ee1273b55..28f81f224 100644 --- a/variants/gemma_m0/variant.h +++ b/variants/gemma_m0/variant.h @@ -116,6 +116,8 @@ static const uint8_t DAC0 = PIN_DAC0; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PIN_SERIAL1_TX (0ul) // PA04 #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom0 +#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces @@ -128,6 +130,7 @@ static const uint8_t DAC0 = PIN_DAC0; #define PERIPH_SPI sercom0 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER SERCOM0_Handler static const uint8_t SS = 1 ; // only pin remaining (shrug) static const uint8_t MOSI = PIN_SPI_MOSI ; diff --git a/variants/grand_central_m4/variant.cpp b/variants/grand_central_m4/variant.cpp index 78aa1495d..6b338a966 100644 --- a/variants/grand_central_m4/variant.cpp +++ b/variants/grand_central_m4/variant.cpp @@ -199,21 +199,21 @@ SERCOM sercom5( SERCOM5 ) ; SERCOM sercom6( SERCOM6 ) ; SERCOM sercom7( SERCOM7 ) ; -Uart Serial1( &sercom0, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM0_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM0_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM0_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM0_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } diff --git a/variants/grand_central_m4/variant.h b/variants/grand_central_m4/variant.h index 3092191b1..2a37aab3e 100644 --- a/variants/grand_central_m4/variant.h +++ b/variants/grand_central_m4/variant.h @@ -150,6 +150,11 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) +#define PERIPH_SERIAL1 sercom0 +#define SERIAL1_IT_HANDLER_0 SERCOM0_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM0_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM0_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM0_3_Handler // Serial2 #define PIN_SERIAL2_RX (19) @@ -182,6 +187,10 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom7 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_3 +#define SPI_IT_HANDLER_0 SERCOM7_0_Handler +#define SPI_IT_HANDLER_1 SERCOM7_1_Handler +#define SPI_IT_HANDLER_2 SERCOM7_2_Handler +#define SPI_IT_HANDLER_3 SERCOM7_3_Handler static const uint8_t SS = (53); static const uint8_t MOSI = PIN_SPI_MOSI; @@ -195,6 +204,10 @@ static const uint8_t SCK = PIN_SPI_SCK; #define PERIPH_SPI1 sercom2 #define PAD_SPI1_TX SPI_PAD_0_SCK_1 #define PAD_SPI1_RX SERCOM_RX_PAD_3 +#define SPI1_IT_HANDLER_0 SERCOM2_0_Handler +#define SPI1_IT_HANDLER_1 SERCOM2_1_Handler +#define SPI1_IT_HANDLER_2 SERCOM2_2_Handler +#define SPI1_IT_HANDLER_3 SERCOM2_3_Handler static const uint8_t SS1 = PIN_SPI1_SS; static const uint8_t MOSI1 = PIN_SPI1_MOSI; diff --git a/variants/hallowing_m0_express/variant.cpp b/variants/hallowing_m0_express/variant.cpp index 441e2faee..30e79d1af 100644 --- a/variants/hallowing_m0_express/variant.cpp +++ b/variants/hallowing_m0_express/variant.cpp @@ -121,9 +121,9 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom2, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM2_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } diff --git a/variants/hallowing_m0_express/variant.h b/variants/hallowing_m0_express/variant.h index 8a84c6023..4defd5783 100644 --- a/variants/hallowing_m0_express/variant.h +++ b/variants/hallowing_m0_express/variant.h @@ -139,6 +139,8 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_2) +#define PERIPH_SERIAL1 sercom2 +#define SERIAL1_IT_HANDLER SERCOM2_Handler /* * SPI Interfaces @@ -151,6 +153,7 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom5 #define PAD_SPI_TX SPI_PAD_2_SCK_3 #define PAD_SPI_RX SERCOM_RX_PAD_1 +#define SPI_IT_HANDLER SERCOM5_Handler static const uint8_t SS = PIN_A2 ; // SERCOM4 last PAD is present on A2 but HW SS isn't used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI ; @@ -164,6 +167,7 @@ static const uint8_t SCK = PIN_SPI_SCK ; #define PERIPH_SPI1 sercom4 #define PAD_SPI1_TX SPI_PAD_2_SCK_3 #define PAD_SPI1_RX SERCOM_RX_PAD_1 +#define SPI1_IT_HANDLER SERCOM4_Handler static const uint8_t SS1 = 36; static const uint8_t MOSI1 = PIN_SPI1_MOSI ; diff --git a/variants/hallowing_m4/variant.cpp b/variants/hallowing_m4/variant.cpp index 8f9ab0838..f778691d2 100644 --- a/variants/hallowing_m4/variant.cpp +++ b/variants/hallowing_m4/variant.cpp @@ -128,21 +128,21 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom4, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM4_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM4_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM4_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM4_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } diff --git a/variants/hallowing_m4/variant.h b/variants/hallowing_m4/variant.h index 221821d59..cdb5050ef 100644 --- a/variants/hallowing_m4/variant.h +++ b/variants/hallowing_m4/variant.h @@ -127,6 +127,11 @@ static const uint8_t DAC1 = PIN_DAC1; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom4 +#define SERIAL1_IT_HANDLER_0 SERCOM4_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM4_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM4_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM4_3_Handler /* * SPI Interfaces @@ -139,6 +144,10 @@ static const uint8_t DAC1 = PIN_DAC1; #define PERIPH_SPI sercom5 #define PAD_SPI_TX SPI_PAD_3_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER_0 SERCOM5_0_Handler +#define SPI_IT_HANDLER_1 SERCOM5_1_Handler +#define SPI_IT_HANDLER_2 SERCOM5_2_Handler +#define SPI_IT_HANDLER_3 SERCOM5_3_Handler static const uint8_t SS = 10 ; static const uint8_t MOSI = PIN_SPI_MOSI ; @@ -152,6 +161,10 @@ static const uint8_t SCK = PIN_SPI_SCK ; #define PERIPH_SPI1 sercom1 #define PAD_SPI1_TX SPI_PAD_0_SCK_1 #define PAD_SPI1_RX SERCOM_RX_PAD_2 +#define SPI1_IT_HANDLER_0 SERCOM1_0_Handler +#define SPI1_IT_HANDLER_1 SERCOM1_1_Handler +#define SPI1_IT_HANDLER_2 SERCOM1_2_Handler +#define SPI1_IT_HANDLER_3 SERCOM1_3_Handler static const uint8_t SS1 = 44 ; static const uint8_t MOSI1 = PIN_SPI1_MOSI ; diff --git a/variants/itsybitsy_m0/variant.cpp b/variants/itsybitsy_m0/variant.cpp index fa35301d7..12964423d 100644 --- a/variants/itsybitsy_m0/variant.cpp +++ b/variants/itsybitsy_m0/variant.cpp @@ -117,9 +117,9 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom0, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM0_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } diff --git a/variants/itsybitsy_m0/variant.h b/variants/itsybitsy_m0/variant.h index e9b945fed..a546d6268 100644 --- a/variants/itsybitsy_m0/variant.h +++ b/variants/itsybitsy_m0/variant.h @@ -137,6 +137,8 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom0 +#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces @@ -149,6 +151,7 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom4 #define PAD_SPI_TX SPI_PAD_2_SCK_3 #define PAD_SPI_RX SERCOM_RX_PAD_0 +#define SPI_IT_HANDLER SERCOM4_Handler static const uint8_t SS = PIN_A2 ; // SERCOM4 last PAD is present on A2 but HW SS isn't used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI ; @@ -162,6 +165,7 @@ static const uint8_t SCK = PIN_SPI_SCK ; #define PERIPH_SPI1 sercom5 #define PAD_SPI1_TX SPI_PAD_2_SCK_3 #define PAD_SPI1_RX SERCOM_RX_PAD_1 +#define SPI1_IT_HANDLER SERCOM5_Handler static const uint8_t SS1 = 39 ; // HW SS isn't used. Set here only for reference. static const uint8_t MOSI1 = PIN_SPI_MOSI ; diff --git a/variants/itsybitsy_m4/variant.cpp b/variants/itsybitsy_m4/variant.cpp index 35323cfcd..23f74bffd 100644 --- a/variants/itsybitsy_m4/variant.cpp +++ b/variants/itsybitsy_m4/variant.cpp @@ -111,21 +111,21 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom3, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM3_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM3_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM3_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM3_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } diff --git a/variants/itsybitsy_m4/variant.h b/variants/itsybitsy_m4/variant.h index dd6da9ba7..324311489 100644 --- a/variants/itsybitsy_m4/variant.h +++ b/variants/itsybitsy_m4/variant.h @@ -129,6 +129,11 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom3 +#define SERIAL1_IT_HANDLER_0 SERCOM3_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM3_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM3_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM3_3_Handler /* * SPI Interfaces @@ -141,6 +146,10 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom1 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_3 +#define SPI_IT_HANDLER_0 SERCOM1_0_Handler +#define SPI_IT_HANDLER_1 SERCOM1_1_Handler +#define SPI_IT_HANDLER_2 SERCOM1_2_Handler +#define SPI_IT_HANDLER_3 SERCOM1_3_Handler static const uint8_t SS = PIN_A2 ; static const uint8_t MOSI = PIN_SPI_MOSI ; diff --git a/variants/matrixportal_m4/variant.cpp b/variants/matrixportal_m4/variant.cpp index df23508fe..45ad3e4df 100644 --- a/variants/matrixportal_m4/variant.cpp +++ b/variants/matrixportal_m4/variant.cpp @@ -132,42 +132,42 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM1_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM1_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM1_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM1_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } // sercom for internal ESP32 UART connection -Uart Serial2( &sercom4, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX ) ; +Uart Serial2( &PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX ) ; -void SERCOM4_0_Handler() +void SERIAL2_IT_HANDLER_0() { Serial2.IrqHandler(); } -void SERCOM4_1_Handler() +void SERIAL2_IT_HANDLER_1() { Serial2.IrqHandler(); } -void SERCOM4_2_Handler() +void SERIAL2_IT_HANDLER_2() { Serial2.IrqHandler(); } -void SERCOM4_3_Handler() +void SERIAL2_IT_HANDLER_3() { Serial2.IrqHandler(); } diff --git a/variants/matrixportal_m4/variant.h b/variants/matrixportal_m4/variant.h index deedf8502..0052f990c 100644 --- a/variants/matrixportal_m4/variant.h +++ b/variants/matrixportal_m4/variant.h @@ -140,12 +140,22 @@ static const uint8_t DAC1 = PIN_DAC1; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom1 +#define SERIAL1_IT_HANDLER_0 SERCOM1_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM1_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM1_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM1_3_Handler // Serial2 #define PIN_SERIAL2_RX (27ul) #define PIN_SERIAL2_TX (28ul) #define PAD_SERIAL2_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL2_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL2 sercom4 +#define SERIAL2_IT_HANDLER_0 SERCOM4_0_Handler +#define SERIAL2_IT_HANDLER_1 SERCOM4_1_Handler +#define SERIAL2_IT_HANDLER_2 SERCOM4_2_Handler +#define SERIAL2_IT_HANDLER_3 SERCOM4_3_Handler /* @@ -159,6 +169,10 @@ static const uint8_t DAC1 = PIN_DAC1; #define PERIPH_SPI sercom3 #define PAD_SPI_TX SPI_PAD_3_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_0 +#define SPI_IT_HANDLER_0 SERCOM3_0_Handler +#define SPI_IT_HANDLER_1 SERCOM3_1_Handler +#define SPI_IT_HANDLER_2 SERCOM3_2_Handler +#define SPI_IT_HANDLER_3 SERCOM3_3_Handler static const uint8_t SS = 33 ; static const uint8_t MOSI = PIN_SPI_MOSI ; @@ -172,6 +186,10 @@ static const uint8_t SCK = PIN_SPI_SCK ; #define PERIPH_SPI1 sercom0 #define PAD_SPI1_TX SPI_PAD_0_SCK_1 #define PAD_SPI1_RX SERCOM_RX_PAD_3 +#define SPI1_IT_HANDLER_0 SERCOM0_0_Handler +#define SPI1_IT_HANDLER_1 SERCOM0_1_Handler +#define SPI1_IT_HANDLER_2 SERCOM0_2_Handler +#define SPI1_IT_HANDLER_3 SERCOM0_3_Handler static const uint8_t SS1 = PIN_A3 ; static const uint8_t MOSI1 = PIN_SPI1_MOSI ; diff --git a/variants/metro_m0/variant.cpp b/variants/metro_m0/variant.cpp index b4836efe8..f4eee3f28 100644 --- a/variants/metro_m0/variant.cpp +++ b/variants/metro_m0/variant.cpp @@ -118,9 +118,9 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom0, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM0_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } diff --git a/variants/metro_m0/variant.h b/variants/metro_m0/variant.h index 5aa2e8525..241b2c643 100644 --- a/variants/metro_m0/variant.h +++ b/variants/metro_m0/variant.h @@ -139,6 +139,8 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom0 +#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces @@ -151,6 +153,7 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom4 #define PAD_SPI_TX SPI_PAD_2_SCK_3 #define PAD_SPI_RX SERCOM_RX_PAD_0 +#define SPI_IT_HANDLER SERCOM4_Handler static const uint8_t SS = PIN_A2 ; // SERCOM4 last PAD is present on A2 but HW SS isn't used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI ; @@ -164,6 +167,7 @@ static const uint8_t SCK = PIN_SPI_SCK ; #define PERIPH_SPI1 sercom5 #define PAD_SPI1_TX SPI_PAD_2_SCK_3 #define PAD_SPI1_RX SERCOM_RX_PAD_1 +#define SPI1_IT_HANDLER SERCOM5_Handler static const uint8_t SS1 = 39 ; // HW SS isn't used. Set here only for reference. static const uint8_t MOSI1 = PIN_SPI_MOSI ; diff --git a/variants/metro_m4/variant.cpp b/variants/metro_m4/variant.cpp index 528a25ce7..eee422c28 100644 --- a/variants/metro_m4/variant.cpp +++ b/variants/metro_m4/variant.cpp @@ -128,21 +128,21 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom3, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM3_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM3_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM3_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM3_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } diff --git a/variants/metro_m4/variant.h b/variants/metro_m4/variant.h index 2b510e744..13dc088ec 100644 --- a/variants/metro_m4/variant.h +++ b/variants/metro_m4/variant.h @@ -131,6 +131,11 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) +#define PERIPH_SERIAL1 sercom3 +#define SERIAL1_IT_HANDLER_0 SERCOM3_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM3_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM3_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM3_3_Handler /* * SPI Interfaces @@ -143,6 +148,10 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom2 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER_0 SERCOM2_0_Handler +#define SPI_IT_HANDLER_1 SERCOM2_1_Handler +#define SPI_IT_HANDLER_2 SERCOM2_2_Handler +#define SPI_IT_HANDLER_3 SERCOM2_3_Handler static const uint8_t SS = PIN_A2 ; static const uint8_t MOSI = PIN_SPI_MOSI ; diff --git a/variants/metro_m4_airlift/variant.cpp b/variants/metro_m4_airlift/variant.cpp index a160346fd..d75cb9e1d 100644 --- a/variants/metro_m4_airlift/variant.cpp +++ b/variants/metro_m4_airlift/variant.cpp @@ -132,41 +132,41 @@ SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; // sercom for pins 0 & 1 UART -Uart Serial1( &sercom3, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM3_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM3_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM3_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM3_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } // sercom for internal ESP32 UART connection -Uart Serial2( &sercom0, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX ) ; +Uart Serial2( &PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX ) ; -void SERCOM0_0_Handler() +void SERIAL2_IT_HANDLER_0() { Serial2.IrqHandler(); } -void SERCOM0_1_Handler() +void SERIAL2_IT_HANDLER_1() { Serial2.IrqHandler(); } -void SERCOM0_2_Handler() +void SERIAL2_IT_HANDLER_2() { Serial2.IrqHandler(); } -void SERCOM0_3_Handler() +void SERIAL2_IT_HANDLER_3() { Serial2.IrqHandler(); } diff --git a/variants/metro_m4_airlift/variant.h b/variants/metro_m4_airlift/variant.h index d078f2099..79252b21e 100644 --- a/variants/metro_m4_airlift/variant.h +++ b/variants/metro_m4_airlift/variant.h @@ -143,12 +143,22 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom3 +#define SERIAL1_IT_HANDLER_0 SERCOM3_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM3_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM3_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM3_3_Handler // Serial2 #define PIN_SERIAL2_RX (32ul) #define PIN_SERIAL2_TX (33ul) #define PAD_SERIAL2_RX (SERCOM_RX_PAD_3) #define PAD_SERIAL2_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL2 sercom0 +#define SERIAL2_IT_HANDLER_0 SERCOM0_0_Handler +#define SERIAL2_IT_HANDLER_1 SERCOM0_1_Handler +#define SERIAL2_IT_HANDLER_2 SERCOM0_2_Handler +#define SERIAL2_IT_HANDLER_3 SERCOM0_3_Handler /* * SPI Interfaces @@ -161,6 +171,10 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom2 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER_0 SERCOM2_0_Handler +#define SPI_IT_HANDLER_1 SERCOM2_1_Handler +#define SPI_IT_HANDLER_2 SERCOM2_2_Handler +#define SPI_IT_HANDLER_3 SERCOM2_3_Handler static const uint8_t SS = SPIWIFI_SS ; static const uint8_t MOSI = PIN_SPI_MOSI ; diff --git a/variants/mkr1000/variant.cpp b/variants/mkr1000/variant.cpp index 51d0b37eb..d025fe7e9 100644 --- a/variants/mkr1000/variant.cpp +++ b/variants/mkr1000/variant.cpp @@ -171,9 +171,9 @@ SERCOM sercom4(SERCOM4); SERCOM sercom5(SERCOM5); // Serial1 -Uart Serial1(&sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); -void SERCOM5_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } diff --git a/variants/mkr1000/variant.h b/variants/mkr1000/variant.h index ce9f6c12c..31ae7fe73 100644 --- a/variants/mkr1000/variant.h +++ b/variants/mkr1000/variant.h @@ -99,6 +99,7 @@ static const uint8_t DAC0 = PIN_DAC0; #define PERIPH_SPI sercom1 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_3 +#define SPI_IT_HANDLER SERCOM1_Handler static const uint8_t SS = PIN_SPI_SS; // SPI Slave SS not used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI; static const uint8_t MISO = PIN_SPI_MISO; @@ -112,6 +113,7 @@ static const uint8_t SCK = PIN_SPI_SCK; #define PERIPH_SPI1 sercom4 #define PAD_SPI1_TX SPI_PAD_0_SCK_1 #define PAD_SPI1_RX SERCOM_RX_PAD_3 +#define SPI1_IT_HANDLER SERCOM4_Handler static const uint8_t SS1 = PIN_SPI1_SS; static const uint8_t MOSI1 = PIN_SPI1_MOSI; static const uint8_t MISO1 = PIN_SPI1_MISO; @@ -174,6 +176,8 @@ extern Uart Serial1; #define PIN_SERIAL1_TX (14ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER SERCOM5_Handler #endif // __cplusplus // These serial port names are intended to allow libraries and architecture-neutral diff --git a/variants/mkrfox1200/variant.cpp b/variants/mkrfox1200/variant.cpp index 9b46cafd9..c9faf16cd 100644 --- a/variants/mkrfox1200/variant.cpp +++ b/variants/mkrfox1200/variant.cpp @@ -169,9 +169,9 @@ SERCOM sercom4(SERCOM4); SERCOM sercom5(SERCOM5); // Serial1 -Uart Serial1(&sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); -void SERCOM5_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } diff --git a/variants/mkrfox1200/variant.h b/variants/mkrfox1200/variant.h index ade82e8ba..a443cc4af 100644 --- a/variants/mkrfox1200/variant.h +++ b/variants/mkrfox1200/variant.h @@ -98,6 +98,7 @@ static const uint8_t A6 = PIN_A6; #define PERIPH_SPI sercom1 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_3 +#define SPI_IT_HANDLER SERCOM1_Handler static const uint8_t SS = PIN_SPI_SS; // SPI Slave SS not used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI; @@ -115,6 +116,7 @@ static const uint8_t SCK = PIN_SPI_SCK; #define PERIPH_SPI1 sercom4 #define PAD_SPI1_TX SPI_PAD_0_SCK_1 #define PAD_SPI1_RX SERCOM_RX_PAD_3 +#define SPI1_IT_HANDLER SERCOM4_Handler static const uint8_t SS1 = PIN_SPI1_SS; static const uint8_t MOSI1 = PIN_SPI1_MOSI; static const uint8_t MISO1 = PIN_SPI1_MISO; @@ -176,6 +178,8 @@ extern Uart Serial1; #define PIN_SERIAL1_TX (14ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER SERCOM5_Handler #endif // __cplusplus // These serial port names are intended to allow libraries and architecture-neutral diff --git a/variants/mkrgsm1400/variant.cpp b/variants/mkrgsm1400/variant.cpp index c534367c2..82c132afc 100644 --- a/variants/mkrgsm1400/variant.cpp +++ b/variants/mkrgsm1400/variant.cpp @@ -236,17 +236,17 @@ void initVariant() { } // Serial1 -Uart Serial1(&sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); -void SERCOM5_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } // SerialGSM -Uart Serial2(&sercom4, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX, PIN_SERIAL2_RTS, PIN_SERIAL2_CTS); +Uart Serial2( &PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX, PIN_SERIAL2_RTS, PIN_SERIAL2_CTS); -void SERCOM4_Handler() +void SERIAL2_IT_HANDLER() { Serial2.IrqHandler(); } diff --git a/variants/mkrgsm1400/variant.h b/variants/mkrgsm1400/variant.h index f3c2832aa..8820dec24 100644 --- a/variants/mkrgsm1400/variant.h +++ b/variants/mkrgsm1400/variant.h @@ -104,6 +104,7 @@ static const uint8_t A6 = PIN_A6; #define PERIPH_SPI sercom1 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_3 +#define SPI_IT_HANDLER SERCOM1_Handler static const uint8_t SS = PIN_SPI_SS; // SPI Slave SS not used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI; @@ -156,6 +157,8 @@ extern Uart Serial1; #define PIN_SERIAL1_TX (14ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER SERCOM5_Handler // Serial2 - GSM extern Uart Serial2; @@ -163,6 +166,8 @@ extern Uart Serial2; #define PIN_SERIAL2_TX (26ul) #define PAD_SERIAL2_TX (UART_TX_RTS_CTS_PAD_0_2_3) #define PAD_SERIAL2_RX (SERCOM_RX_PAD_1) +#define PERIPH_SERIAL2 sercom4 +#define SERIAL2_IT_HANDLER SERCOM4_Handler #define PIN_SERIAL2_RTS (28u) #define PIN_SERIAL2_CTS (29u) #define PIN_SERIAL2_DTR (35u) diff --git a/variants/mkrnb1500/variant.cpp b/variants/mkrnb1500/variant.cpp index 3cc0a5921..f30cf7144 100644 --- a/variants/mkrnb1500/variant.cpp +++ b/variants/mkrnb1500/variant.cpp @@ -240,17 +240,17 @@ void initVariant() { } // Serial1 -Uart Serial1(&sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); -void SERCOM5_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } // SerialSARA -Uart Serial2(&sercom4, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX); +Uart Serial2( &PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX); -void SERCOM4_Handler() +void SERIAL2_IT_HANDLER() { Serial2.IrqHandler(); } diff --git a/variants/mkrnb1500/variant.h b/variants/mkrnb1500/variant.h index 3482528ce..aa4f84af0 100644 --- a/variants/mkrnb1500/variant.h +++ b/variants/mkrnb1500/variant.h @@ -105,6 +105,7 @@ static const uint8_t A6 = PIN_A6; #define PERIPH_SPI sercom1 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_3 +#define SPI_IT_HANDLER SERCOM1_Handler static const uint8_t SS = PIN_SPI_SS; // SPI Slave SS not used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI; @@ -157,6 +158,8 @@ extern Uart Serial1; #define PIN_SERIAL1_TX (14ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER SERCOM5_Handler // Serial2 - GSM extern Uart Serial2; @@ -164,6 +167,8 @@ extern Uart Serial2; #define PIN_SERIAL2_TX (26ul) #define PAD_SERIAL2_TX (UART_TX_PAD_0) #define PAD_SERIAL2_RX (SERCOM_RX_PAD_1) +#define PERIPH_SERIAL2 sercom4 +#define SERIAL2_IT_HANDLER SERCOM4_Handler #define PIN_SERIAL2_RTS (28u) #define PIN_SERIAL2_CTS (29u) diff --git a/variants/mkrwan1300/variant.cpp b/variants/mkrwan1300/variant.cpp index c9a19c5be..06d0c8785 100644 --- a/variants/mkrwan1300/variant.cpp +++ b/variants/mkrwan1300/variant.cpp @@ -172,17 +172,17 @@ SERCOM sercom4(SERCOM4); SERCOM sercom5(SERCOM5); // Serial1 -Uart Serial1(&sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); -void SERCOM5_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } // SerialLoRa -Uart Serial2(&sercom4, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX); +Uart Serial2( &PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX); -void SERCOM4_Handler() +void SERIAL2_IT_HANDLER() { Serial2.IrqHandler(); } \ No newline at end of file diff --git a/variants/mkrwan1300/variant.h b/variants/mkrwan1300/variant.h index 7368e02f5..5f35c0872 100644 --- a/variants/mkrwan1300/variant.h +++ b/variants/mkrwan1300/variant.h @@ -102,6 +102,7 @@ static const uint8_t A6 = PIN_A6; #define PERIPH_SPI sercom1 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_3 +#define SPI_IT_HANDLER SERCOM1_Handler static const uint8_t SS = PIN_SPI_SS; // SPI Slave SS not used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI; @@ -116,6 +117,7 @@ static const uint8_t SCK = PIN_SPI_SCK; #define PERIPH_SPI1 sercom4 #define PAD_SPI1_TX SPI_PAD_0_SCK_1 #define PAD_SPI1_RX SERCOM_RX_PAD_3 +#define SPI1_IT_HANDLER SERCOM4_Handler static const uint8_t MOSI1 = PIN_SPI1_MOSI; static const uint8_t MISO1 = PIN_SPI1_MISO; static const uint8_t SCK1 = PIN_SPI1_SCK; @@ -166,6 +168,8 @@ extern Uart Serial1; #define PIN_SERIAL1_TX (14ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER SERCOM5_Handler // Serial2 extern Uart Serial2; @@ -173,6 +177,8 @@ extern Uart Serial2; #define PIN_SERIAL2_TX (26ul) #define PAD_SERIAL2_TX (UART_TX_PAD_0) #define PAD_SERIAL2_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL2 sercom4 +#define SERIAL2_IT_HANDLER SERCOM4_Handler #endif // __cplusplus diff --git a/variants/mkrwifi1010/variant.cpp b/variants/mkrwifi1010/variant.cpp index f1938506b..02151daaa 100644 --- a/variants/mkrwifi1010/variant.cpp +++ b/variants/mkrwifi1010/variant.cpp @@ -236,17 +236,17 @@ void initVariant() { } // Serial1 -Uart Serial1(&sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); -void SERCOM5_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } // Serial2 -Uart Serial2(&sercom4, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX, PIN_SERIAL2_RTS, PIN_SERIAL2_CTS); +Uart Serial2( &PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX, PIN_SERIAL2_RTS, PIN_SERIAL2_CTS); -void SERCOM4_Handler() +void SERIAL2_IT_HANDLER() { Serial2.IrqHandler(); } diff --git a/variants/mkrwifi1010/variant.h b/variants/mkrwifi1010/variant.h index dcd7f7775..e86ba8c99 100644 --- a/variants/mkrwifi1010/variant.h +++ b/variants/mkrwifi1010/variant.h @@ -103,6 +103,7 @@ static const uint8_t A6 = PIN_A6; #define PERIPH_SPI sercom1 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_3 +#define SPI_IT_HANDLER SERCOM1_Handler static const uint8_t SS = PIN_SPI_SS; // SPI Slave SS not used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI; @@ -117,6 +118,7 @@ static const uint8_t SCK = PIN_SPI_SCK; #define PERIPH_SPI1 sercom4 #define PAD_SPI1_TX SPI_PAD_0_SCK_3 #define PAD_SPI1_RX SERCOM_RX_PAD_1 +#define SPI1_IT_HANDLER SERCOM4_Handler static const uint8_t SS1 = PIN_SPI1_SS; static const uint8_t MOSI1 = PIN_SPI1_MOSI; static const uint8_t MISO1 = PIN_SPI1_MISO; @@ -173,6 +175,8 @@ extern Uart Serial1; #define PIN_SERIAL1_TX (14ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER SERCOM5_Handler // Serial2 extern Uart Serial2; @@ -180,6 +184,8 @@ extern Uart Serial2; #define PIN_SERIAL2_TX (26ul) #define PAD_SERIAL2_TX (UART_TX_RTS_CTS_PAD_0_2_3) #define PAD_SERIAL2_RX (SERCOM_RX_PAD_1) +#define PERIPH_SERIAL2 sercom4 +#define SERIAL2_IT_HANDLER SERCOM4_Handler #define PIN_SERIAL2_RTS (28u) #define PIN_SERIAL2_CTS (29u) diff --git a/variants/mkrzero/variant.cpp b/variants/mkrzero/variant.cpp index 30b359150..c6f008d1d 100644 --- a/variants/mkrzero/variant.cpp +++ b/variants/mkrzero/variant.cpp @@ -170,9 +170,9 @@ SERCOM sercom4(SERCOM4); SERCOM sercom5(SERCOM5); // Serial1 -Uart Serial1(&sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); -void SERCOM5_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } diff --git a/variants/mkrzero/variant.h b/variants/mkrzero/variant.h index ca59b5ab2..afd4ab0c0 100644 --- a/variants/mkrzero/variant.h +++ b/variants/mkrzero/variant.h @@ -98,6 +98,7 @@ static const uint8_t A6 = PIN_A6; #define PERIPH_SPI sercom1 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_3 +#define SPI_IT_HANDLER SERCOM1_Handler static const uint8_t SS = PIN_SPI_SS; // SPI Slave SS not used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI; @@ -112,6 +113,7 @@ static const uint8_t SCK = PIN_SPI_SCK; #define PERIPH_SPI1 sercom4 #define PAD_SPI1_TX SPI_PAD_0_SCK_1 #define PAD_SPI1_RX SERCOM_RX_PAD_3 +#define SPI1_IT_HANDLER SERCOM4_Handler static const uint8_t SS1 = PIN_SPI1_SS; static const uint8_t MOSI1 = PIN_SPI1_MOSI; static const uint8_t MISO1 = PIN_SPI1_MISO; @@ -170,6 +172,8 @@ extern Uart Serial1; #define PIN_SERIAL1_TX (14ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER SERCOM5_Handler #endif // __cplusplus // These serial port names are intended to allow libraries and architecture-neutral diff --git a/variants/monster_m4sk/variant.h b/variants/monster_m4sk/variant.h index eb3a283fe..01b6cc6cb 100644 --- a/variants/monster_m4sk/variant.h +++ b/variants/monster_m4sk/variant.h @@ -131,6 +131,10 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom2 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_3 +#define SPI_IT_HANDLER_0 SERCOM2_0_Handler +#define SPI_IT_HANDLER_1 SERCOM2_1_Handler +#define SPI_IT_HANDLER_2 SERCOM2_2_Handler +#define SPI_IT_HANDLER_3 SERCOM2_3_Handler static const uint8_t SS = 5 ; static const uint8_t MOSI = PIN_SPI_MOSI ; @@ -144,6 +148,10 @@ static const uint8_t SCK = PIN_SPI_SCK ; #define PERIPH_SPI1 sercom5 #define PAD_SPI1_TX SPI_PAD_0_SCK_1 #define PAD_SPI1_RX SERCOM_RX_PAD_3 +#define SPI1_IT_HANDLER_0 SERCOM5_0_Handler +#define SPI1_IT_HANDLER_1 SERCOM5_1_Handler +#define SPI1_IT_HANDLER_2 SERCOM5_2_Handler +#define SPI1_IT_HANDLER_3 SERCOM5_3_Handler static const uint8_t SS1 = 9 ; static const uint8_t MOSI1 = PIN_SPI_MOSI ; @@ -157,6 +165,10 @@ static const uint8_t SCK1 = PIN_SPI_SCK ; #define PERIPH_SPI2 sercom3 #define PAD_SPI2_TX SPI_PAD_3_SCK_1 #define PAD_SPI2_RX SERCOM_RX_PAD_0 +#define SPI2_IT_HANDLER_0 SERCOM3_0_Handler +#define SPI2_IT_HANDLER_1 SERCOM3_1_Handler +#define SPI2_IT_HANDLER_2 SERCOM3_2_Handler +#define SPI2_IT_HANDLER_3 SERCOM3_3_Handler static const uint8_t SS2 = 13 ; // also ignored static const uint8_t MOSI2 = PIN_SPI_MOSI ; diff --git a/variants/neokeytrinkey_m0/variant.h b/variants/neokeytrinkey_m0/variant.h index 27e532bf4..d40ba3059 100644 --- a/variants/neokeytrinkey_m0/variant.h +++ b/variants/neokeytrinkey_m0/variant.h @@ -108,6 +108,7 @@ static const uint8_t A1 = PIN_A1; #define PERIPH_SPI sercom0 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_0 +#define SPI_IT_HANDLER SERCOM0_Handler static const uint8_t SS = PIN_A0; static const uint8_t MOSI = PIN_SPI_MOSI; diff --git a/variants/neotrinkey_m0/variant.h b/variants/neotrinkey_m0/variant.h index 3cf172c23..36fb6a5b5 100644 --- a/variants/neotrinkey_m0/variant.h +++ b/variants/neotrinkey_m0/variant.h @@ -107,6 +107,7 @@ static const uint8_t A2 = PIN_A2; #define PERIPH_SPI sercom0 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_0 +#define SPI_IT_HANDLER SERCOM0_Handler static const uint8_t SS = PIN_A0; static const uint8_t MOSI = PIN_SPI_MOSI; diff --git a/variants/pirkey/variant.cpp b/variants/pirkey/variant.cpp index 61734c020..225a6eb42 100644 --- a/variants/pirkey/variant.cpp +++ b/variants/pirkey/variant.cpp @@ -87,9 +87,9 @@ SERCOM sercom1( SERCOM1 ) ; SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; -Uart Serial1( &sercom1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM1_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } diff --git a/variants/pirkey/variant.h b/variants/pirkey/variant.h index e6d0ce74a..137178745 100644 --- a/variants/pirkey/variant.h +++ b/variants/pirkey/variant.h @@ -117,6 +117,8 @@ static const uint8_t DAC0 = PIN_DAC0; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PIN_SERIAL1_TX (3ul) // SWCLK PA30 #define PAD_SERIAL1_TX (UART_TX_PAD_2) +#define PERIPH_SERIAL1 sercom1 +#define SERIAL1_IT_HANDLER SERCOM1_Handler /* * SPI Interfaces diff --git a/variants/pixeltrinkey_m0/variant.h b/variants/pixeltrinkey_m0/variant.h index 9a2b8222f..b6c99a6b9 100644 --- a/variants/pixeltrinkey_m0/variant.h +++ b/variants/pixeltrinkey_m0/variant.h @@ -109,6 +109,7 @@ static const uint8_t A6 = PIN_A6; #define PERIPH_SPI sercom0 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER SERCOM0_Handler static const uint8_t SS = (5ul); // not actually broken out static const uint8_t MOSI = PIN_SPI_MOSI; diff --git a/variants/proxlighttrinkey_m0/variant.h b/variants/proxlighttrinkey_m0/variant.h index 93554eafc..c25384f50 100644 --- a/variants/proxlighttrinkey_m0/variant.h +++ b/variants/proxlighttrinkey_m0/variant.h @@ -113,6 +113,7 @@ static const uint8_t A2 = PIN_A2; #define PERIPH_SPI sercom0 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_0 +#define SPI_IT_HANDLER SERCOM0_Handler static const uint8_t SS = PIN_A0; static const uint8_t MOSI = PIN_SPI_MOSI; diff --git a/variants/pybadge_airlift_m4/variant.cpp b/variants/pybadge_airlift_m4/variant.cpp index ce2304ba1..533259cbf 100644 --- a/variants/pybadge_airlift_m4/variant.cpp +++ b/variants/pybadge_airlift_m4/variant.cpp @@ -134,21 +134,21 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM5_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM5_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM5_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM5_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } diff --git a/variants/pybadge_airlift_m4/variant.h b/variants/pybadge_airlift_m4/variant.h index 0887238d5..7d95f60b6 100644 --- a/variants/pybadge_airlift_m4/variant.h +++ b/variants/pybadge_airlift_m4/variant.h @@ -144,6 +144,11 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER_0 SERCOM5_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM5_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM5_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM5_3_Handler /* * SPI Interfaces @@ -156,6 +161,10 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom1 #define PAD_SPI_TX SPI_PAD_3_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER_0 SERCOM1_0_Handler +#define SPI_IT_HANDLER_1 SERCOM1_1_Handler +#define SPI_IT_HANDLER_2 SERCOM1_2_Handler +#define SPI_IT_HANDLER_3 SERCOM1_3_Handler static const uint8_t SS = 9 ; // SERCOM1 last PAD is present on d9 but HW SS isn't used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI ; @@ -169,6 +178,10 @@ static const uint8_t SCK = PIN_SPI_SCK ; #define PERIPH_SPI1 sercom4 #define PAD_SPI1_TX SPI_PAD_3_SCK_1 #define PAD_SPI1_RX SERCOM_RX_PAD_0 +#define SPI1_IT_HANDLER_0 SERCOM4_0_Handler +#define SPI1_IT_HANDLER_1 SERCOM4_1_Handler +#define SPI1_IT_HANDLER_2 SERCOM4_2_Handler +#define SPI1_IT_HANDLER_3 SERCOM4_3_Handler static const uint8_t SS1 = 44 ; static const uint8_t MOSI1 = PIN_SPI1_MOSI ; diff --git a/variants/pybadge_m4/variant.cpp b/variants/pybadge_m4/variant.cpp index ebc2c7f48..b6d4dbc56 100644 --- a/variants/pybadge_m4/variant.cpp +++ b/variants/pybadge_m4/variant.cpp @@ -132,21 +132,21 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM5_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM5_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM5_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM5_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } diff --git a/variants/pybadge_m4/variant.h b/variants/pybadge_m4/variant.h index fbfb6824a..3f57b8199 100644 --- a/variants/pybadge_m4/variant.h +++ b/variants/pybadge_m4/variant.h @@ -132,6 +132,11 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER_0 SERCOM5_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM5_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM5_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM5_3_Handler /* * SPI Interfaces @@ -144,6 +149,10 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom1 #define PAD_SPI_TX SPI_PAD_3_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER_0 SERCOM1_0_Handler +#define SPI_IT_HANDLER_1 SERCOM1_1_Handler +#define SPI_IT_HANDLER_2 SERCOM1_2_Handler +#define SPI_IT_HANDLER_3 SERCOM1_3_Handler static const uint8_t SS = 9 ; // SERCOM1 last PAD is present on d9 but HW SS isn't used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI ; @@ -157,6 +166,10 @@ static const uint8_t SCK = PIN_SPI_SCK ; #define PERIPH_SPI1 sercom4 #define PAD_SPI1_TX SPI_PAD_3_SCK_1 #define PAD_SPI1_RX SERCOM_RX_PAD_0 +#define SPI1_IT_HANDLER_0 SERCOM4_0_Handler +#define SPI1_IT_HANDLER_1 SERCOM4_1_Handler +#define SPI1_IT_HANDLER_2 SERCOM4_2_Handler +#define SPI1_IT_HANDLER_3 SERCOM4_3_Handler static const uint8_t SS1 = 44 ; static const uint8_t MOSI1 = PIN_SPI1_MOSI ; @@ -170,6 +183,10 @@ static const uint8_t SCK1 = PIN_SPI1_SCK ; #define PERIPH_SPI2 sercom3 #define PAD_SPI2_TX SPI_PAD_3_SCK_1 #define PAD_SPI2_RX SERCOM_RX_PAD_2 +#define SPI2_IT_HANDLER_0 SERCOM3_0_Handler +#define SPI2_IT_HANDLER_1 SERCOM3_1_Handler +#define SPI2_IT_HANDLER_2 SERCOM3_2_Handler +#define SPI2_IT_HANDLER_3 SERCOM3_3_Handler static const uint8_t SS2 = 10 ; static const uint8_t MOSI2 = PIN_SPI2_MOSI ; diff --git a/variants/pygamer_advance_m4/variant.cpp b/variants/pygamer_advance_m4/variant.cpp index 34f7e9831..f4a4dbadc 100644 --- a/variants/pygamer_advance_m4/variant.cpp +++ b/variants/pygamer_advance_m4/variant.cpp @@ -131,21 +131,21 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM5_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM5_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM5_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM5_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } diff --git a/variants/pygamer_advance_m4/variant.h b/variants/pygamer_advance_m4/variant.h index cd5e271dd..e3984bb9c 100644 --- a/variants/pygamer_advance_m4/variant.h +++ b/variants/pygamer_advance_m4/variant.h @@ -136,6 +136,11 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER_0 SERCOM5_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM5_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM5_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM5_3_Handler /* * SPI Interfaces @@ -148,6 +153,10 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom1 #define PAD_SPI_TX SPI_PAD_3_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER_0 SERCOM1_0_Handler +#define SPI_IT_HANDLER_1 SERCOM1_1_Handler +#define SPI_IT_HANDLER_2 SERCOM1_2_Handler +#define SPI_IT_HANDLER_3 SERCOM1_3_Handler static const uint8_t SS = 4 ; // SD card CS static const uint8_t MOSI = PIN_SPI_MOSI ; @@ -161,6 +170,10 @@ static const uint8_t SCK = PIN_SPI_SCK ; #define PERIPH_SPI1 sercom4 #define PAD_SPI1_TX SPI_PAD_0_SCK_1 #define PAD_SPI1_RX SERCOM_RX_PAD_3 +#define SPI1_IT_HANDLER_0 SERCOM4_0_Handler +#define SPI1_IT_HANDLER_1 SERCOM4_1_Handler +#define SPI1_IT_HANDLER_2 SERCOM4_2_Handler +#define SPI1_IT_HANDLER_3 SERCOM4_3_Handler static const uint8_t SS1 = 44 ; static const uint8_t MOSI1 = PIN_SPI1_MOSI ; diff --git a/variants/pygamer_m4/variant.cpp b/variants/pygamer_m4/variant.cpp index ffbccefe8..70c1287df 100644 --- a/variants/pygamer_m4/variant.cpp +++ b/variants/pygamer_m4/variant.cpp @@ -131,21 +131,21 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM5_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM5_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM5_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM5_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } diff --git a/variants/pygamer_m4/variant.h b/variants/pygamer_m4/variant.h index 12c5b4ffb..a25c84f45 100644 --- a/variants/pygamer_m4/variant.h +++ b/variants/pygamer_m4/variant.h @@ -136,6 +136,11 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER_0 SERCOM5_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM5_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM5_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM5_3_Handler /* * SPI Interfaces @@ -148,6 +153,10 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom1 #define PAD_SPI_TX SPI_PAD_3_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER_0 SERCOM1_0_Handler +#define SPI_IT_HANDLER_1 SERCOM1_1_Handler +#define SPI_IT_HANDLER_2 SERCOM1_2_Handler +#define SPI_IT_HANDLER_3 SERCOM1_3_Handler static const uint8_t SS = 4 ; // SD card CS static const uint8_t MOSI = PIN_SPI_MOSI ; @@ -161,6 +170,10 @@ static const uint8_t SCK = PIN_SPI_SCK ; #define PERIPH_SPI1 sercom4 #define PAD_SPI1_TX SPI_PAD_3_SCK_1 #define PAD_SPI1_RX SERCOM_RX_PAD_0 +#define SPI1_IT_HANDLER_0 SERCOM4_0_Handler +#define SPI1_IT_HANDLER_1 SERCOM4_1_Handler +#define SPI1_IT_HANDLER_2 SERCOM4_2_Handler +#define SPI1_IT_HANDLER_3 SERCOM4_3_Handler static const uint8_t SS1 = 44 ; static const uint8_t MOSI1 = PIN_SPI1_MOSI ; diff --git a/variants/pyportal_m4/variant.cpp b/variants/pyportal_m4/variant.cpp index a74da78f6..22ee43d4b 100644 --- a/variants/pyportal_m4/variant.cpp +++ b/variants/pyportal_m4/variant.cpp @@ -131,21 +131,21 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom4, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM4_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM4_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM4_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM4_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } diff --git a/variants/pyportal_m4/variant.h b/variants/pyportal_m4/variant.h index 75fcb354c..d38410c6d 100644 --- a/variants/pyportal_m4/variant.h +++ b/variants/pyportal_m4/variant.h @@ -146,6 +146,11 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom4 +#define SERIAL1_IT_HANDLER_0 SERCOM4_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM4_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM4_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM4_3_Handler /* @@ -159,6 +164,10 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom2 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER_0 SERCOM2_0_Handler +#define SPI_IT_HANDLER_1 SERCOM2_1_Handler +#define SPI_IT_HANDLER_2 SERCOM2_2_Handler +#define SPI_IT_HANDLER_3 SERCOM2_3_Handler static const uint8_t SS = 32; static const uint8_t MOSI = PIN_SPI_MOSI; diff --git a/variants/pyportal_m4_titano/variant.cpp b/variants/pyportal_m4_titano/variant.cpp index 53cfdaca5..b0dd42ef3 100644 --- a/variants/pyportal_m4_titano/variant.cpp +++ b/variants/pyportal_m4_titano/variant.cpp @@ -131,21 +131,21 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom4, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM4_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM4_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM4_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM4_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } diff --git a/variants/pyportal_m4_titano/variant.h b/variants/pyportal_m4_titano/variant.h index 0da7c158a..9b0c8388c 100644 --- a/variants/pyportal_m4_titano/variant.h +++ b/variants/pyportal_m4_titano/variant.h @@ -146,6 +146,11 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (1ul) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom4 +#define SERIAL1_IT_HANDLER_0 SERCOM4_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM4_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM4_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM4_3_Handler /* @@ -159,6 +164,10 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom2 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER_0 SERCOM2_0_Handler +#define SPI_IT_HANDLER_1 SERCOM2_1_Handler +#define SPI_IT_HANDLER_2 SERCOM2_2_Handler +#define SPI_IT_HANDLER_3 SERCOM2_3_Handler static const uint8_t SS = 32; static const uint8_t MOSI = PIN_SPI_MOSI; diff --git a/variants/qtpy_m0/variant.cpp b/variants/qtpy_m0/variant.cpp index 4d5135036..a99f10620 100644 --- a/variants/qtpy_m0/variant.cpp +++ b/variants/qtpy_m0/variant.cpp @@ -70,9 +70,9 @@ SERCOM sercom1( SERCOM1 ) ; SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; -Uart Serial1( &sercom0, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM0_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } diff --git a/variants/qtpy_m0/variant.h b/variants/qtpy_m0/variant.h index 1e4c4e0e1..400eda569 100644 --- a/variants/qtpy_m0/variant.h +++ b/variants/qtpy_m0/variant.h @@ -123,6 +123,8 @@ static const uint8_t DAC0 = PIN_DAC0; #define PIN_SERIAL1_RX (7ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom0 +#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces @@ -135,6 +137,7 @@ static const uint8_t DAC0 = PIN_DAC0; #define PERIPH_SPI sercom2 #define PAD_SPI_TX SPI_PAD_2_SCK_3 #define PAD_SPI_RX SERCOM_RX_PAD_1 +#define SPI_IT_HANDLER SERCOM2_Handler static const uint8_t SS = PIN_A0 ; // unused, just for reference static const uint8_t MOSI = PIN_SPI_MOSI ; @@ -148,6 +151,7 @@ static const uint8_t SCK = PIN_SPI_SCK ; #define PERIPH_SPI1 sercom3 #define PAD_SPI1_TX SPI_PAD_0_SCK_1 #define PAD_SPI1_RX SERCOM_RX_PAD_3 +#define SPI1_IT_HANDLER SERCOM3_Handler static const uint8_t SS1 = 17; static const uint8_t MOSI1 = PIN_SPI1_MOSI ; diff --git a/variants/rotarytrinkey_m0/variant.h b/variants/rotarytrinkey_m0/variant.h index a6442918e..6d79bfb36 100644 --- a/variants/rotarytrinkey_m0/variant.h +++ b/variants/rotarytrinkey_m0/variant.h @@ -114,6 +114,7 @@ static const uint8_t A2 = PIN_A2; #define PERIPH_SPI sercom0 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_0 +#define SPI_IT_HANDLER SERCOM0_Handler static const uint8_t SS = PIN_A0; static const uint8_t MOSI = PIN_SPI_MOSI; diff --git a/variants/sht4xtrinkey_m0/variant.h b/variants/sht4xtrinkey_m0/variant.h index 2a3a0ee9b..69afcffa0 100644 --- a/variants/sht4xtrinkey_m0/variant.h +++ b/variants/sht4xtrinkey_m0/variant.h @@ -108,6 +108,7 @@ static const uint8_t A1 = PIN_A1; #define PERIPH_SPI sercom0 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_0 +#define SPI_IT_HANDLER SERCOM0_Handler static const uint8_t SS = PIN_A0; static const uint8_t MOSI = PIN_SPI_MOSI; diff --git a/variants/slidetrinkey_m0/variant.h b/variants/slidetrinkey_m0/variant.h index 65d2e75e8..51e2270cb 100644 --- a/variants/slidetrinkey_m0/variant.h +++ b/variants/slidetrinkey_m0/variant.h @@ -111,6 +111,7 @@ static const uint8_t A2 = PIN_A2; #define PERIPH_SPI sercom0 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_0 +#define SPI_IT_HANDLER SERCOM0_Handler static const uint8_t SS = PIN_A0; static const uint8_t MOSI = PIN_SPI_MOSI; diff --git a/variants/trellis_m4/variant.cpp b/variants/trellis_m4/variant.cpp index cbe7be633..473143f43 100644 --- a/variants/trellis_m4/variant.cpp +++ b/variants/trellis_m4/variant.cpp @@ -110,21 +110,21 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &sercom4, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM4_0_Handler() +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } -void SERCOM4_1_Handler() +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } -void SERCOM4_2_Handler() +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } -void SERCOM4_3_Handler() +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } diff --git a/variants/trellis_m4/variant.h b/variants/trellis_m4/variant.h index 46e66b754..c90573ef2 100644 --- a/variants/trellis_m4/variant.h +++ b/variants/trellis_m4/variant.h @@ -128,6 +128,11 @@ static const uint8_t LINE_RIGHT = PIN_LINE_RIGHT; #define PIN_SERIAL1_TX (21ul) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) +#define PERIPH_SERIAL1 sercom4 +#define SERIAL1_IT_HANDLER_0 SERCOM4_0_Handler +#define SERIAL1_IT_HANDLER_1 SERCOM4_1_Handler +#define SERIAL1_IT_HANDLER_2 SERCOM4_2_Handler +#define SERIAL1_IT_HANDLER_3 SERCOM4_3_Handler /* * SPI Interfaces @@ -140,6 +145,10 @@ static const uint8_t LINE_RIGHT = PIN_LINE_RIGHT; #define PERIPH_SPI sercom3 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_3 +#define SPI_IT_HANDLER_0 SERCOM3_0_Handler +#define SPI_IT_HANDLER_1 SERCOM3_1_Handler +#define SPI_IT_HANDLER_2 SERCOM3_2_Handler +#define SPI_IT_HANDLER_3 SERCOM3_3_Handler static const uint8_t SS = PIN_A2 ; static const uint8_t MOSI = PIN_SPI_MOSI ; diff --git a/variants/trinket_m0/variant.cpp b/variants/trinket_m0/variant.cpp index 5363f8daf..739e7b071 100644 --- a/variants/trinket_m0/variant.cpp +++ b/variants/trinket_m0/variant.cpp @@ -94,9 +94,9 @@ SERCOM sercom1( SERCOM1 ) ; SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; -Uart Serial1( &sercom0, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM0_Handler() +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } diff --git a/variants/trinket_m0/variant.h b/variants/trinket_m0/variant.h index 12e99e9dc..ed1310f37 100644 --- a/variants/trinket_m0/variant.h +++ b/variants/trinket_m0/variant.h @@ -120,6 +120,8 @@ static const uint8_t DAC0 = PIN_DAC0; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PIN_SERIAL1_TX (4ul) // PA06 #define PAD_SERIAL1_TX (UART_TX_PAD_2) +#define PERIPH_SERIAL1 sercom0 +#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces @@ -132,6 +134,7 @@ static const uint8_t DAC0 = PIN_DAC0; #define PERIPH_SPI sercom0 #define PAD_SPI_TX SPI_PAD_2_SCK_3 #define PAD_SPI_RX SERCOM_RX_PAD_1 +#define SPI_IT_HANDLER SERCOM0_Handler static const uint8_t SS = 5 ; // SERCOM0 last PAD is present on 5 but HW SS isn't used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI ; diff --git a/variants/trrstrinkey_m0/variant.h b/variants/trrstrinkey_m0/variant.h index 998d00ddb..c40cde2b6 100644 --- a/variants/trrstrinkey_m0/variant.h +++ b/variants/trrstrinkey_m0/variant.h @@ -120,6 +120,7 @@ static const uint8_t A5 = PIN_A5; #define PERIPH_SPI sercom0 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER SERCOM0_Handler static const uint8_t SS = PIN_TIP; static const uint8_t MOSI = PIN_SPI_MOSI; diff --git a/variants/zero_radio/variant.cpp b/variants/zero_radio/variant.cpp index 8e3150122..59303e64f 100644 --- a/variants/zero_radio/variant.cpp +++ b/variants/zero_radio/variant.cpp @@ -218,8 +218,8 @@ SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; // We'll use sercom5 for Serial1 hardware serial since pins 0 and 1 are not external -Uart Serial1( &sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERCOM5_Handler() +Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; +void SERIAL1_IT_HANDLER() { Serial1.IrqHandler(); } diff --git a/variants/zero_radio/variant.h b/variants/zero_radio/variant.h index dfc5b93d7..6718c372e 100644 --- a/variants/zero_radio/variant.h +++ b/variants/zero_radio/variant.h @@ -139,6 +139,8 @@ static const uint8_t ATN = PIN_ATN; #define PIN_SERIAL1_TX (30ul) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) +#define PERIPH_SERIAL1 sercom5 +#define SERIAL1_IT_HANDLER SERCOM5_Handler /* * SPI Interfaces @@ -152,6 +154,7 @@ static const uint8_t ATN = PIN_ATN; #define PERIPH_SPI sercom2 #define PAD_SPI_TX SPI_PAD_0_SCK_1 #define PAD_SPI_RX SERCOM_RX_PAD_2 +#define SPI_IT_HANDLER SERCOM2_Handler static const uint8_t SS = PIN_A2 ; // SERCOM4 last PAD is present on A2 but HW SS isn't used. Set here only for reference. static const uint8_t MOSI = PIN_SPI_MOSI ; @@ -166,6 +169,7 @@ static const uint8_t SCK = PIN_SPI_SCK ; #define PERIPH_SPI1 sercom4 #define PAD_SPI1_TX SPI_PAD_2_SCK_3 #define PAD_SPI1_RX SERCOM_RX_PAD_0 +#define SPI1_IT_HANDLER SERCOM4_Handler static const uint8_t SS1 = PIN_SPI1_SEL ; static const uint8_t MOSI1 = PIN_SPI_MOSI ; From 230b3ed3f24902f96c86916079ba46e140a075cf Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sat, 20 Jun 2026 09:20:42 -0400 Subject: [PATCH 02/79] Refactor UART variants to use shared core serial emitters --- cores/arduino/Uart.cpp | 236 ++++++++++++++++++++++ variants/arduino_mzero/variant.cpp | 12 -- variants/arduino_mzero/variant.h | 2 - variants/arduino_zero/variant.cpp | 13 -- variants/arduino_zero/variant.h | 2 - variants/blm_badge/variant.cpp | 8 - variants/blm_badge/variant.h | 1 - variants/circuitplay/variant.cpp | 12 +- variants/circuitplay/variant.h | 1 - variants/crickit_m0/variant.cpp | 14 +- variants/crickit_m0/variant.h | 1 - variants/feather_m0/variant.cpp | 13 -- variants/feather_m0/variant.h | 2 - variants/feather_m0_express/variant.cpp | 8 - variants/feather_m0_express/variant.h | 1 - variants/feather_m4/variant.cpp | 19 -- variants/feather_m4/variant.h | 4 - variants/feather_m4_can/variant.cpp | 18 -- variants/feather_m4_can/variant.h | 4 - variants/gemma_m0/variant.cpp | 2 - variants/gemma_m0/variant.h | 2 +- variants/grand_central_m4/variant.cpp | 19 -- variants/grand_central_m4/variant.h | 4 - variants/hallowing_m0_express/variant.cpp | 8 - variants/hallowing_m0_express/variant.h | 1 - variants/hallowing_m4/variant.cpp | 23 +-- variants/hallowing_m4/variant.h | 4 - variants/itsybitsy_m0/variant.cpp | 8 - variants/itsybitsy_m0/variant.h | 1 - variants/itsybitsy_m4/variant.cpp | 19 -- variants/itsybitsy_m4/variant.h | 4 - variants/matrixportal_m4/variant.cpp | 37 ---- variants/matrixportal_m4/variant.h | 8 - variants/metro_m0/variant.cpp | 8 - variants/metro_m0/variant.h | 1 - variants/metro_m4/variant.cpp | 19 -- variants/metro_m4/variant.h | 4 - variants/metro_m4_airlift/variant.cpp | 38 +--- variants/metro_m4_airlift/variant.h | 8 - variants/mkr1000/variant.cpp | 7 - variants/mkr1000/variant.h | 1 - variants/mkrfox1200/variant.cpp | 9 +- variants/mkrfox1200/variant.h | 1 - variants/mkrgsm1400/variant.cpp | 16 -- variants/mkrgsm1400/variant.h | 2 - variants/mkrnb1500/variant.cpp | 16 -- variants/mkrnb1500/variant.h | 2 - variants/mkrwan1300/variant.cpp | 14 +- variants/mkrwan1300/variant.h | 2 - variants/mkrwifi1010/variant.cpp | 16 -- variants/mkrwifi1010/variant.h | 2 - variants/mkrzero/variant.cpp | 9 +- variants/mkrzero/variant.h | 1 - variants/neokeytrinkey_m0/variant.cpp | 8 +- variants/neotrinkey_m0/variant.cpp | 4 +- variants/pirkey/variant.cpp | 8 - variants/pirkey/variant.h | 1 - variants/pixeltrinkey_m0/variant.cpp | 2 +- variants/proxlighttrinkey_m0/variant.cpp | 4 +- variants/pybadge_airlift_m4/variant.cpp | 21 +- variants/pybadge_airlift_m4/variant.h | 4 - variants/pybadge_m4/variant.cpp | 23 +-- variants/pybadge_m4/variant.h | 4 - variants/pygamer_advance_m4/variant.cpp | 25 +-- variants/pygamer_advance_m4/variant.h | 4 - variants/pygamer_m4/variant.cpp | 23 +-- variants/pygamer_m4/variant.h | 4 - variants/pyportal_m4/variant.cpp | 19 -- variants/pyportal_m4/variant.h | 4 - variants/pyportal_m4_titano/variant.cpp | 19 -- variants/pyportal_m4_titano/variant.h | 4 - variants/qtpy_m0/variant.cpp | 10 +- variants/qtpy_m0/variant.h | 1 - variants/rotarytrinkey_m0/variant.cpp | 10 +- variants/sht4xtrinkey_m0/variant.cpp | 2 +- variants/slidetrinkey_m0/variant.cpp | 2 +- variants/trellis_m4/variant.cpp | 21 +- variants/trellis_m4/variant.h | 4 - variants/trinket_m0/variant.cpp | 8 - variants/trinket_m0/variant.h | 1 - variants/zero_radio/variant.cpp | 5 - variants/zero_radio/variant.h | 1 - 82 files changed, 275 insertions(+), 658 deletions(-) diff --git a/cores/arduino/Uart.cpp b/cores/arduino/Uart.cpp index dd797da4d..dc9d63d60 100644 --- a/cores/arduino/Uart.cpp +++ b/cores/arduino/Uart.cpp @@ -24,6 +24,50 @@ #define NO_CTS_PIN 255 #define RTS_RX_THRESHOLD 10 +// Derive SERCOM ISR symbols from PERIPH_SERIAL* tokens (e.g. sercom4 -> +// SERCOM4_Handler) so variants only need to describe the UART routing. +#define UART_SERCOM_INDEX_sercom0 0 +#define UART_SERCOM_INDEX_sercom1 1 +#define UART_SERCOM_INDEX_sercom2 2 +#define UART_SERCOM_INDEX_sercom3 3 +#define UART_SERCOM_INDEX_sercom4 4 +#define UART_SERCOM_INDEX_sercom5 5 +#if defined(SERCOM6) +#define UART_SERCOM_INDEX_sercom6 6 +#endif +#if defined(SERCOM7) +#define UART_SERCOM_INDEX_sercom7 7 +#endif + +#define UART_SERCOM_INDEX(token) UART_SERCOM_INDEX_##token +#define UART_SERCOM_HANDLER_FROM_INDEX(idx) \ + UART_SERCOM_HANDLER_FROM_INDEX_2(idx) +#define UART_SERCOM_HANDLER_FROM_INDEX_2(idx) SERCOM##idx##_Handler +#define UART_SERCOM_HANDLER_FROM_TOKEN(token) \ + UART_SERCOM_HANDLER_FROM_INDEX(UART_SERCOM_INDEX(token)) + +#define UART_SERCOM_HANDLER0_FROM_INDEX(idx) \ + UART_SERCOM_HANDLER0_FROM_INDEX_2(idx) +#define UART_SERCOM_HANDLER0_FROM_INDEX_2(idx) SERCOM##idx##_0_Handler +#define UART_SERCOM_HANDLER1_FROM_INDEX(idx) \ + UART_SERCOM_HANDLER1_FROM_INDEX_2(idx) +#define UART_SERCOM_HANDLER1_FROM_INDEX_2(idx) SERCOM##idx##_1_Handler +#define UART_SERCOM_HANDLER2_FROM_INDEX(idx) \ + UART_SERCOM_HANDLER2_FROM_INDEX_2(idx) +#define UART_SERCOM_HANDLER2_FROM_INDEX_2(idx) SERCOM##idx##_2_Handler +#define UART_SERCOM_HANDLER3_FROM_INDEX(idx) \ + UART_SERCOM_HANDLER3_FROM_INDEX_2(idx) +#define UART_SERCOM_HANDLER3_FROM_INDEX_2(idx) SERCOM##idx##_3_Handler + +#define UART_SERCOM_HANDLER0_FROM_TOKEN(token) \ + UART_SERCOM_HANDLER0_FROM_INDEX(UART_SERCOM_INDEX(token)) +#define UART_SERCOM_HANDLER1_FROM_TOKEN(token) \ + UART_SERCOM_HANDLER1_FROM_INDEX(UART_SERCOM_INDEX(token)) +#define UART_SERCOM_HANDLER2_FROM_TOKEN(token) \ + UART_SERCOM_HANDLER2_FROM_INDEX(UART_SERCOM_INDEX(token)) +#define UART_SERCOM_HANDLER3_FROM_TOKEN(token) \ + UART_SERCOM_HANDLER3_FROM_INDEX(UART_SERCOM_INDEX(token)) + Uart::Uart(SERCOM *_s, uint8_t _pinRX, uint8_t _pinTX, SercomRXPad _padRX, SercomUartTXPad _padTX) : Uart(_s, _pinRX, _pinTX, _padRX, _padTX, NO_RTS_PIN, NO_CTS_PIN) { @@ -238,3 +282,195 @@ SercomParityMode Uart::extractParity(uint16_t config) return SERCOM_ODD_PARITY; } } + +#if defined(PERIPH_SERIAL) && !defined(UART_VARIANT_OWNS_SERIAL) +Uart Serial(&PERIPH_SERIAL, PIN_SERIAL_RX, PIN_SERIAL_TX, PAD_SERIAL_RX, + PAD_SERIAL_TX); + +#ifndef SERIAL_IT_HANDLER +#define SERIAL_IT_HANDLER UART_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SERIAL) +#endif +void SERIAL_IT_HANDLER() +{ + Serial.IrqHandler(); +} + +#ifdef FAMILY_SAMD5X +#ifndef SERIAL_IT_HANDLER_0 +#define SERIAL_IT_HANDLER_0 UART_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SERIAL) +#define SERIAL_IT_HANDLER_1 UART_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SERIAL) +#define SERIAL_IT_HANDLER_2 UART_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SERIAL) +#define SERIAL_IT_HANDLER_3 UART_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SERIAL) +#endif +void SERIAL_IT_HANDLER_0() { Serial.IrqHandler(); } +void SERIAL_IT_HANDLER_1() { Serial.IrqHandler(); } +void SERIAL_IT_HANDLER_2() { Serial.IrqHandler(); } +void SERIAL_IT_HANDLER_3() { Serial.IrqHandler(); } +#endif +#endif + +#if defined(PERIPH_SERIAL_UART) && !defined(UART_VARIANT_OWNS_SERIAL_UART) +Uart SerialUART(&PERIPH_SERIAL_UART, PIN_SERIAL_UART_RX, PIN_SERIAL_UART_TX, + PAD_SERIAL_UART_RX, PAD_SERIAL_UART_TX); + +#ifndef SERIAL_UART_IT_HANDLER +#define SERIAL_UART_IT_HANDLER UART_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SERIAL_UART) +#endif +#ifndef FAMILY_SAMD5X +void SERIAL_UART_IT_HANDLER() +{ + SerialUART.IrqHandler(); +} +#else +#ifndef SERIAL_UART_IT_HANDLER_0 +#define SERIAL_UART_IT_HANDLER_0 UART_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SERIAL_UART) +#define SERIAL_UART_IT_HANDLER_1 UART_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SERIAL_UART) +#define SERIAL_UART_IT_HANDLER_2 UART_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SERIAL_UART) +#define SERIAL_UART_IT_HANDLER_3 UART_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SERIAL_UART) +#endif +void SERIAL_UART_IT_HANDLER_0() { SerialUART.IrqHandler(); } +void SERIAL_UART_IT_HANDLER_1() { SerialUART.IrqHandler(); } +void SERIAL_UART_IT_HANDLER_2() { SerialUART.IrqHandler(); } +void SERIAL_UART_IT_HANDLER_3() { SerialUART.IrqHandler(); } +#endif +#endif + +#if defined(PERIPH_SERIAL1) && !defined(UART_VARIANT_OWNS_SERIAL1) +#ifdef PIN_SERIAL1_RTS +Uart Serial1(&PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, + PAD_SERIAL1_TX, PIN_SERIAL1_RTS, PIN_SERIAL1_CTS); +#else +Uart Serial1(&PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, + PAD_SERIAL1_TX); +#endif + +#ifndef SERIAL1_IT_HANDLER +#define SERIAL1_IT_HANDLER UART_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SERIAL1) +#endif +#ifndef FAMILY_SAMD5X +void SERIAL1_IT_HANDLER() +{ + Serial1.IrqHandler(); +} +#else +#ifndef SERIAL1_IT_HANDLER_0 +#define SERIAL1_IT_HANDLER_0 UART_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SERIAL1) +#define SERIAL1_IT_HANDLER_1 UART_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SERIAL1) +#define SERIAL1_IT_HANDLER_2 UART_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SERIAL1) +#define SERIAL1_IT_HANDLER_3 UART_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SERIAL1) +#endif +void SERIAL1_IT_HANDLER_0() { Serial1.IrqHandler(); } +void SERIAL1_IT_HANDLER_1() { Serial1.IrqHandler(); } +void SERIAL1_IT_HANDLER_2() { Serial1.IrqHandler(); } +void SERIAL1_IT_HANDLER_3() { Serial1.IrqHandler(); } +#endif +#endif + +#if defined(PERIPH_SERIAL2) && !defined(UART_VARIANT_OWNS_SERIAL2) +#ifdef PIN_SERIAL2_RTS +Uart Serial2(&PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, + PAD_SERIAL2_TX, PIN_SERIAL2_RTS, PIN_SERIAL2_CTS); +#else +Uart Serial2(&PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, + PAD_SERIAL2_TX); +#endif + +#ifndef SERIAL2_IT_HANDLER +#define SERIAL2_IT_HANDLER UART_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SERIAL2) +#endif +#ifndef FAMILY_SAMD5X +void SERIAL2_IT_HANDLER() +{ + Serial2.IrqHandler(); +} +#else +#ifndef SERIAL2_IT_HANDLER_0 +#define SERIAL2_IT_HANDLER_0 UART_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SERIAL2) +#define SERIAL2_IT_HANDLER_1 UART_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SERIAL2) +#define SERIAL2_IT_HANDLER_2 UART_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SERIAL2) +#define SERIAL2_IT_HANDLER_3 UART_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SERIAL2) +#endif +void SERIAL2_IT_HANDLER_0() { Serial2.IrqHandler(); } +void SERIAL2_IT_HANDLER_1() { Serial2.IrqHandler(); } +void SERIAL2_IT_HANDLER_2() { Serial2.IrqHandler(); } +void SERIAL2_IT_HANDLER_3() { Serial2.IrqHandler(); } +#endif +#endif + +#if defined(PERIPH_SERIAL3) && !defined(UART_VARIANT_OWNS_SERIAL3) +Uart Serial3(&PERIPH_SERIAL3, PIN_SERIAL3_RX, PIN_SERIAL3_TX, PAD_SERIAL3_RX, + PAD_SERIAL3_TX); + +#ifndef SERIAL3_IT_HANDLER +#define SERIAL3_IT_HANDLER UART_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SERIAL3) +#endif +#ifndef FAMILY_SAMD5X +void SERIAL3_IT_HANDLER() +{ + Serial3.IrqHandler(); +} +#else +#ifndef SERIAL3_IT_HANDLER_0 +#define SERIAL3_IT_HANDLER_0 UART_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SERIAL3) +#define SERIAL3_IT_HANDLER_1 UART_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SERIAL3) +#define SERIAL3_IT_HANDLER_2 UART_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SERIAL3) +#define SERIAL3_IT_HANDLER_3 UART_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SERIAL3) +#endif +void SERIAL3_IT_HANDLER_0() { Serial3.IrqHandler(); } +void SERIAL3_IT_HANDLER_1() { Serial3.IrqHandler(); } +void SERIAL3_IT_HANDLER_2() { Serial3.IrqHandler(); } +void SERIAL3_IT_HANDLER_3() { Serial3.IrqHandler(); } +#endif +#endif + +#if defined(PERIPH_SERIAL4) && !defined(UART_VARIANT_OWNS_SERIAL4) +Uart Serial4(&PERIPH_SERIAL4, PIN_SERIAL4_RX, PIN_SERIAL4_TX, PAD_SERIAL4_RX, + PAD_SERIAL4_TX); + +#ifndef SERIAL4_IT_HANDLER +#define SERIAL4_IT_HANDLER UART_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SERIAL4) +#endif +#ifndef FAMILY_SAMD5X +void SERIAL4_IT_HANDLER() +{ + Serial4.IrqHandler(); +} +#else +#ifndef SERIAL4_IT_HANDLER_0 +#define SERIAL4_IT_HANDLER_0 UART_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SERIAL4) +#define SERIAL4_IT_HANDLER_1 UART_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SERIAL4) +#define SERIAL4_IT_HANDLER_2 UART_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SERIAL4) +#define SERIAL4_IT_HANDLER_3 UART_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SERIAL4) +#endif +void SERIAL4_IT_HANDLER_0() { Serial4.IrqHandler(); } +void SERIAL4_IT_HANDLER_1() { Serial4.IrqHandler(); } +void SERIAL4_IT_HANDLER_2() { Serial4.IrqHandler(); } +void SERIAL4_IT_HANDLER_3() { Serial4.IrqHandler(); } +#endif +#endif + +#if defined(PERIPH_SERIAL5) && !defined(UART_VARIANT_OWNS_SERIAL5) +Uart Serial5(&PERIPH_SERIAL5, PIN_SERIAL_RX, PIN_SERIAL_TX, PAD_SERIAL_RX, + PAD_SERIAL_TX); + +#ifndef SERIAL5_IT_HANDLER +#define SERIAL5_IT_HANDLER UART_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SERIAL5) +#endif +void SERIAL5_IT_HANDLER() +{ + Serial5.IrqHandler(); +} + +#ifdef FAMILY_SAMD5X +#ifndef SERIAL5_IT_HANDLER_0 +#define SERIAL5_IT_HANDLER_0 UART_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SERIAL5) +#define SERIAL5_IT_HANDLER_1 UART_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SERIAL5) +#define SERIAL5_IT_HANDLER_2 UART_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SERIAL5) +#define SERIAL5_IT_HANDLER_3 UART_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SERIAL5) +#endif +void SERIAL5_IT_HANDLER_0() { Serial5.IrqHandler(); } +void SERIAL5_IT_HANDLER_1() { Serial5.IrqHandler(); } +void SERIAL5_IT_HANDLER_2() { Serial5.IrqHandler(); } +void SERIAL5_IT_HANDLER_3() { Serial5.IrqHandler(); } +#endif +#endif diff --git a/variants/arduino_mzero/variant.cpp b/variants/arduino_mzero/variant.cpp index f2e8d7ce3..41b128474 100644 --- a/variants/arduino_mzero/variant.cpp +++ b/variants/arduino_mzero/variant.cpp @@ -203,15 +203,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -Uart Serial( &PERIPH_SERIAL, PIN_SERIAL_RX, PIN_SERIAL_TX, PAD_SERIAL_RX, PAD_SERIAL_TX ) ; -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - -void SERIAL_IT_HANDLER() -{ - Serial.IrqHandler(); -} diff --git a/variants/arduino_mzero/variant.h b/variants/arduino_mzero/variant.h index f5298b6cc..0ffec41ae 100755 --- a/variants/arduino_mzero/variant.h +++ b/variants/arduino_mzero/variant.h @@ -115,7 +115,6 @@ static const uint8_t DAC0 = PIN_DAC0; #define PAD_SERIAL_TX (UART_TX_PAD_2) #define PAD_SERIAL_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL sercom5 -#define SERIAL_IT_HANDLER SERCOM5_Handler // Serial1 #define PIN_SERIAL1_RX (0ul) @@ -123,7 +122,6 @@ static const uint8_t DAC0 = PIN_DAC0; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom0 -#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces diff --git a/variants/arduino_zero/variant.cpp b/variants/arduino_zero/variant.cpp index 3653f1bd1..de911f261 100644 --- a/variants/arduino_zero/variant.cpp +++ b/variants/arduino_zero/variant.cpp @@ -212,16 +212,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -Uart Serial5( &PERIPH_SERIAL5, PIN_SERIAL_RX, PIN_SERIAL_TX, PAD_SERIAL_RX, PAD_SERIAL_TX ) ; -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - -void SERIAL5_IT_HANDLER() -{ - Serial5.IrqHandler(); -} - diff --git a/variants/arduino_zero/variant.h b/variants/arduino_zero/variant.h index 4ecc0f65f..57d479126 100644 --- a/variants/arduino_zero/variant.h +++ b/variants/arduino_zero/variant.h @@ -122,7 +122,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL_TX (UART_TX_PAD_2) #define PAD_SERIAL_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL5 sercom5 -#define SERIAL5_IT_HANDLER SERCOM5_Handler // Serial1 #define PIN_SERIAL1_RX (0ul) @@ -130,7 +129,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom0 -#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces diff --git a/variants/blm_badge/variant.cpp b/variants/blm_badge/variant.cpp index c83614ce1..1f1938468 100644 --- a/variants/blm_badge/variant.cpp +++ b/variants/blm_badge/variant.cpp @@ -85,11 +85,3 @@ SERCOM sercom0( SERCOM0 ) ; SERCOM sercom1( SERCOM1 ) ; SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - diff --git a/variants/blm_badge/variant.h b/variants/blm_badge/variant.h index c63af106c..88441453d 100644 --- a/variants/blm_badge/variant.h +++ b/variants/blm_badge/variant.h @@ -119,7 +119,6 @@ static const uint8_t DAC0 = PIN_DAC0; #define PIN_SERIAL1_TX (2ul) // captouch pad #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom0 -#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces diff --git a/variants/circuitplay/variant.cpp b/variants/circuitplay/variant.cpp index fca2c4f4e..81aa3b247 100644 --- a/variants/circuitplay/variant.cpp +++ b/variants/circuitplay/variant.cpp @@ -61,7 +61,7 @@ const PinDescription g_APinDescription[]= { PORTA, 11, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG), ADC_Channel19, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_11 }, // A8 / Light Sensor { PORTA, 9, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER|PIN_ATTR_ANALOG), ADC_Channel17, PWM0_CH1, TCC0_CH1, EXTERNAL_INT_9 }, // A9 / Thermistor { PORTA, 4, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG), ADC_Channel4, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_4 }, // A10 Proximity - + // GPIO 25 / IR Transmit { PORTA, 23, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM4_CH1, TC4_CH1, EXTERNAL_INT_7 }, // GPIO D25 - IR Transmitter // GPIO 26 / IR Remote in @@ -89,7 +89,7 @@ const PinDescription g_APinDescription[]= // 36..38 - USB - // -------------------- + // -------------------- { PORTA, 22, PIO_SERCOM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // GPIO 29 - Host USB (not used) { PORTA, 24, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB/DM { PORTA, 25, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB/DP @@ -111,11 +111,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - diff --git a/variants/circuitplay/variant.h b/variants/circuitplay/variant.h index 928d7445c..7cbcd9aeb 100644 --- a/variants/circuitplay/variant.h +++ b/variants/circuitplay/variant.h @@ -252,7 +252,6 @@ extern Uart Serial1; #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PERIPH_SERIAL1 sercom4 -#define SERIAL1_IT_HANDLER SERCOM4_Handler #endif // __cplusplus diff --git a/variants/crickit_m0/variant.cpp b/variants/crickit_m0/variant.cpp index a8745f6d1..6c299b973 100644 --- a/variants/crickit_m0/variant.cpp +++ b/variants/crickit_m0/variant.cpp @@ -45,13 +45,13 @@ const PinDescription g_APinDescription[]= { PORTA, 27, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 }, // D11 External NeoPixel - { PORTA, 20, PIO_TIMER_ALT, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER_ALT), No_ADC_Channel, PWM0_CH6, TCC0_CH6, EXTERNAL_INT_4 }, + { PORTA, 20, PIO_TIMER_ALT, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER_ALT), No_ADC_Channel, PWM0_CH6, TCC0_CH6, EXTERNAL_INT_4 }, // D12 IRQ - { PORTA, 28, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_8 }, + { PORTA, 28, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_8 }, // 13 (LED) - { PORTB, 22, PIO_SERCOM_ALT, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // Yellow LED next to USB + { PORTB, 22, PIO_SERCOM_ALT, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // Yellow LED next to USB // A0-A8 - Analog signal pins // A0 is duplicated since we mark it starting at analog1, so might as well! @@ -102,11 +102,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - diff --git a/variants/crickit_m0/variant.h b/variants/crickit_m0/variant.h index 276ae4429..ff92c62bd 100644 --- a/variants/crickit_m0/variant.h +++ b/variants/crickit_m0/variant.h @@ -162,7 +162,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER SERCOM5_Handler /* * SPI Interfaces diff --git a/variants/feather_m0/variant.cpp b/variants/feather_m0/variant.cpp index 10c17a592..9806c68b5 100644 --- a/variants/feather_m0/variant.cpp +++ b/variants/feather_m0/variant.cpp @@ -124,16 +124,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -Uart Serial5( &PERIPH_SERIAL5, PIN_SERIAL_RX, PIN_SERIAL_TX, PAD_SERIAL_RX, PAD_SERIAL_TX ) ; -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - -void SERIAL5_IT_HANDLER() -{ - Serial5.IrqHandler(); -} - diff --git a/variants/feather_m0/variant.h b/variants/feather_m0/variant.h index baf4b6726..d323c8f41 100644 --- a/variants/feather_m0/variant.h +++ b/variants/feather_m0/variant.h @@ -123,7 +123,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL_TX (UART_TX_PAD_2) #define PAD_SERIAL_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL5 sercom5 -#define SERIAL5_IT_HANDLER SERCOM5_Handler // Serial1 #define PIN_SERIAL1_RX (0ul) @@ -131,7 +130,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom0 -#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces diff --git a/variants/feather_m0_express/variant.cpp b/variants/feather_m0_express/variant.cpp index e177c6a3f..ecedbb2d1 100644 --- a/variants/feather_m0_express/variant.cpp +++ b/variants/feather_m0_express/variant.cpp @@ -118,11 +118,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - diff --git a/variants/feather_m0_express/variant.h b/variants/feather_m0_express/variant.h index 3eb2bd851..f253347b2 100644 --- a/variants/feather_m0_express/variant.h +++ b/variants/feather_m0_express/variant.h @@ -143,7 +143,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom0 -#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces diff --git a/variants/feather_m4/variant.cpp b/variants/feather_m4/variant.cpp index e550ee3e5..a2e53d446 100644 --- a/variants/feather_m4/variant.cpp +++ b/variants/feather_m4/variant.cpp @@ -119,22 +119,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} diff --git a/variants/feather_m4/variant.h b/variants/feather_m4/variant.h index 44529c810..5ed31420c 100644 --- a/variants/feather_m4/variant.h +++ b/variants/feather_m4/variant.h @@ -131,10 +131,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER_0 SERCOM5_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM5_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM5_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM5_3_Handler /* * SPI Interfaces diff --git a/variants/feather_m4_can/variant.cpp b/variants/feather_m4_can/variant.cpp index f74581804..bfaa62d63 100644 --- a/variants/feather_m4_can/variant.cpp +++ b/variants/feather_m4_can/variant.cpp @@ -128,24 +128,6 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} void initVariant(void) { // special initialization code just for us diff --git a/variants/feather_m4_can/variant.h b/variants/feather_m4_can/variant.h index b25d99673..0c0060fd2 100644 --- a/variants/feather_m4_can/variant.h +++ b/variants/feather_m4_can/variant.h @@ -132,10 +132,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER_0 SERCOM5_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM5_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM5_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM5_3_Handler /* * SPI Interfaces diff --git a/variants/gemma_m0/variant.cpp b/variants/gemma_m0/variant.cpp index 646f743b1..d04220ff4 100644 --- a/variants/gemma_m0/variant.cpp +++ b/variants/gemma_m0/variant.cpp @@ -85,7 +85,6 @@ SERCOM sercom3( SERCOM3 ) ; Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - // If wire.h isnt included, just do nothing void WIRE_IT_HANDLER(void) __attribute__ ((weak)); @@ -94,4 +93,3 @@ void SERIAL1_IT_HANDLER() Serial1.IrqHandler(); WIRE_IT_HANDLER(); } - diff --git a/variants/gemma_m0/variant.h b/variants/gemma_m0/variant.h index 28f81f224..dd00fc670 100644 --- a/variants/gemma_m0/variant.h +++ b/variants/gemma_m0/variant.h @@ -118,6 +118,7 @@ static const uint8_t DAC0 = PIN_DAC0; #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom0 #define SERIAL1_IT_HANDLER SERCOM0_Handler +#define UART_VARIANT_OWNS_SERIAL1 /* * SPI Interfaces @@ -203,4 +204,3 @@ extern Uart Serial1; #define SERIAL_PORT_HARDWARE_OPEN Serial1 #endif /* _VARIANT_ARDUINO_ZERO_ */ - diff --git a/variants/grand_central_m4/variant.cpp b/variants/grand_central_m4/variant.cpp index 6b338a966..7163d11f3 100644 --- a/variants/grand_central_m4/variant.cpp +++ b/variants/grand_central_m4/variant.cpp @@ -198,22 +198,3 @@ SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; SERCOM sercom6( SERCOM6 ) ; SERCOM sercom7( SERCOM7 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} diff --git a/variants/grand_central_m4/variant.h b/variants/grand_central_m4/variant.h index 2a37aab3e..cdbf7b0a9 100644 --- a/variants/grand_central_m4/variant.h +++ b/variants/grand_central_m4/variant.h @@ -151,10 +151,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PERIPH_SERIAL1 sercom0 -#define SERIAL1_IT_HANDLER_0 SERCOM0_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM0_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM0_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM0_3_Handler // Serial2 #define PIN_SERIAL2_RX (19) diff --git a/variants/hallowing_m0_express/variant.cpp b/variants/hallowing_m0_express/variant.cpp index 30e79d1af..2dec78875 100644 --- a/variants/hallowing_m0_express/variant.cpp +++ b/variants/hallowing_m0_express/variant.cpp @@ -120,11 +120,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - diff --git a/variants/hallowing_m0_express/variant.h b/variants/hallowing_m0_express/variant.h index 4defd5783..9be3eb0b2 100644 --- a/variants/hallowing_m0_express/variant.h +++ b/variants/hallowing_m0_express/variant.h @@ -140,7 +140,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PERIPH_SERIAL1 sercom2 -#define SERIAL1_IT_HANDLER SERCOM2_Handler /* * SPI Interfaces diff --git a/variants/hallowing_m4/variant.cpp b/variants/hallowing_m4/variant.cpp index f778691d2..0a3679c95 100644 --- a/variants/hallowing_m4/variant.cpp +++ b/variants/hallowing_m4/variant.cpp @@ -35,7 +35,7 @@ const PinDescription g_APinDescription[]= { PORTB, 2, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel14, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_2 }, // D3 { PORTA, 14, PIO_DIGITAL, PIN_ATTR_PWM_E, No_ADC_Channel, TC3_CH0, TC3_CH0, EXTERNAL_INT_14 }, // D4 { PORTA, 16, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_0 }, // D5 - { PORTA, 17, PIO_DIGITAL, (PIN_ATTR_DIGITAL), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_1 }, // D6 + { PORTA, 17, PIO_DIGITAL, (PIN_ATTR_DIGITAL), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_1 }, // D6 { PORTB, 15, PIO_DIGITAL, PIN_ATTR_PWM_F, No_ADC_Channel, TCC4_CH0, TC5_CH0, EXTERNAL_INT_15 }, // D7 (LISIRQ) // Digital High @@ -47,7 +47,7 @@ const PinDescription g_APinDescription[]= { PORTA, 21, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_5 }, // D12 // 13 (LED) - { PORTA, 23, PIO_SERCOM, PIN_ATTR_PWM_G, No_ADC_Channel, TCC0_CH3, TC4_CH1, EXTERNAL_INT_7 }, + { PORTA, 23, PIO_SERCOM, PIN_ATTR_PWM_G, No_ADC_Channel, TCC0_CH3, TC4_CH1, EXTERNAL_INT_7 }, // 14..23 - Analog pins @@ -127,22 +127,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} diff --git a/variants/hallowing_m4/variant.h b/variants/hallowing_m4/variant.h index cdb5050ef..31cedb68a 100644 --- a/variants/hallowing_m4/variant.h +++ b/variants/hallowing_m4/variant.h @@ -128,10 +128,6 @@ static const uint8_t DAC1 = PIN_DAC1; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom4 -#define SERIAL1_IT_HANDLER_0 SERCOM4_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM4_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM4_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM4_3_Handler /* * SPI Interfaces diff --git a/variants/itsybitsy_m0/variant.cpp b/variants/itsybitsy_m0/variant.cpp index 12964423d..623e26d59 100644 --- a/variants/itsybitsy_m0/variant.cpp +++ b/variants/itsybitsy_m0/variant.cpp @@ -116,11 +116,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - diff --git a/variants/itsybitsy_m0/variant.h b/variants/itsybitsy_m0/variant.h index a546d6268..382789a02 100644 --- a/variants/itsybitsy_m0/variant.h +++ b/variants/itsybitsy_m0/variant.h @@ -138,7 +138,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom0 -#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces diff --git a/variants/itsybitsy_m4/variant.cpp b/variants/itsybitsy_m4/variant.cpp index 23f74bffd..dadba4510 100644 --- a/variants/itsybitsy_m4/variant.cpp +++ b/variants/itsybitsy_m4/variant.cpp @@ -110,22 +110,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} diff --git a/variants/itsybitsy_m4/variant.h b/variants/itsybitsy_m4/variant.h index 324311489..021619f01 100644 --- a/variants/itsybitsy_m4/variant.h +++ b/variants/itsybitsy_m4/variant.h @@ -130,10 +130,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom3 -#define SERIAL1_IT_HANDLER_0 SERCOM3_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM3_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM3_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM3_3_Handler /* * SPI Interfaces diff --git a/variants/matrixportal_m4/variant.cpp b/variants/matrixportal_m4/variant.cpp index 45ad3e4df..be25f6652 100644 --- a/variants/matrixportal_m4/variant.cpp +++ b/variants/matrixportal_m4/variant.cpp @@ -132,42 +132,5 @@ SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} - // sercom for internal ESP32 UART connection -Uart Serial2( &PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX ) ; - -void SERIAL2_IT_HANDLER_0() -{ - Serial2.IrqHandler(); -} -void SERIAL2_IT_HANDLER_1() -{ - Serial2.IrqHandler(); -} -void SERIAL2_IT_HANDLER_2() -{ - Serial2.IrqHandler(); -} -void SERIAL2_IT_HANDLER_3() -{ - Serial2.IrqHandler(); -} diff --git a/variants/matrixportal_m4/variant.h b/variants/matrixportal_m4/variant.h index 0052f990c..21a2ae294 100644 --- a/variants/matrixportal_m4/variant.h +++ b/variants/matrixportal_m4/variant.h @@ -141,10 +141,6 @@ static const uint8_t DAC1 = PIN_DAC1; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom1 -#define SERIAL1_IT_HANDLER_0 SERCOM1_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM1_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM1_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM1_3_Handler // Serial2 #define PIN_SERIAL2_RX (27ul) @@ -152,10 +148,6 @@ static const uint8_t DAC1 = PIN_DAC1; #define PAD_SERIAL2_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL2_TX (UART_TX_PAD_0) #define PERIPH_SERIAL2 sercom4 -#define SERIAL2_IT_HANDLER_0 SERCOM4_0_Handler -#define SERIAL2_IT_HANDLER_1 SERCOM4_1_Handler -#define SERIAL2_IT_HANDLER_2 SERCOM4_2_Handler -#define SERIAL2_IT_HANDLER_3 SERCOM4_3_Handler /* diff --git a/variants/metro_m0/variant.cpp b/variants/metro_m0/variant.cpp index f4eee3f28..de192fa60 100644 --- a/variants/metro_m0/variant.cpp +++ b/variants/metro_m0/variant.cpp @@ -117,11 +117,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - diff --git a/variants/metro_m0/variant.h b/variants/metro_m0/variant.h index 241b2c643..92ba44d84 100644 --- a/variants/metro_m0/variant.h +++ b/variants/metro_m0/variant.h @@ -140,7 +140,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom0 -#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces diff --git a/variants/metro_m4/variant.cpp b/variants/metro_m4/variant.cpp index eee422c28..7a99558c9 100644 --- a/variants/metro_m4/variant.cpp +++ b/variants/metro_m4/variant.cpp @@ -127,22 +127,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} diff --git a/variants/metro_m4/variant.h b/variants/metro_m4/variant.h index 13dc088ec..2f162f487 100644 --- a/variants/metro_m4/variant.h +++ b/variants/metro_m4/variant.h @@ -132,10 +132,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PERIPH_SERIAL1 sercom3 -#define SERIAL1_IT_HANDLER_0 SERCOM3_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM3_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM3_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM3_3_Handler /* * SPI Interfaces diff --git a/variants/metro_m4_airlift/variant.cpp b/variants/metro_m4_airlift/variant.cpp index d75cb9e1d..cf038d8c8 100644 --- a/variants/metro_m4_airlift/variant.cpp +++ b/variants/metro_m4_airlift/variant.cpp @@ -94,7 +94,7 @@ const PinDescription g_APinDescription[]= { PORTB, 4, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_4 }, { PORTB, 5, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_5 }, - // 39 - SWO + // 39 - SWO { PORTB, 30, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_6 }, // used as output only // 40 - Internal NeoPixel @@ -132,41 +132,5 @@ SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; // sercom for pins 0 & 1 UART -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} // sercom for internal ESP32 UART connection -Uart Serial2( &PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX ) ; - -void SERIAL2_IT_HANDLER_0() -{ - Serial2.IrqHandler(); -} -void SERIAL2_IT_HANDLER_1() -{ - Serial2.IrqHandler(); -} -void SERIAL2_IT_HANDLER_2() -{ - Serial2.IrqHandler(); -} -void SERIAL2_IT_HANDLER_3() -{ - Serial2.IrqHandler(); -} diff --git a/variants/metro_m4_airlift/variant.h b/variants/metro_m4_airlift/variant.h index 79252b21e..b978bb456 100644 --- a/variants/metro_m4_airlift/variant.h +++ b/variants/metro_m4_airlift/variant.h @@ -144,10 +144,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom3 -#define SERIAL1_IT_HANDLER_0 SERCOM3_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM3_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM3_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM3_3_Handler // Serial2 #define PIN_SERIAL2_RX (32ul) @@ -155,10 +151,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL2_RX (SERCOM_RX_PAD_3) #define PAD_SERIAL2_TX (UART_TX_PAD_0) #define PERIPH_SERIAL2 sercom0 -#define SERIAL2_IT_HANDLER_0 SERCOM0_0_Handler -#define SERIAL2_IT_HANDLER_1 SERCOM0_1_Handler -#define SERIAL2_IT_HANDLER_2 SERCOM0_2_Handler -#define SERIAL2_IT_HANDLER_3 SERCOM0_3_Handler /* * SPI Interfaces diff --git a/variants/mkr1000/variant.cpp b/variants/mkr1000/variant.cpp index d025fe7e9..76af54188 100644 --- a/variants/mkr1000/variant.cpp +++ b/variants/mkr1000/variant.cpp @@ -171,10 +171,3 @@ SERCOM sercom4(SERCOM4); SERCOM sercom5(SERCOM5); // Serial1 -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - diff --git a/variants/mkr1000/variant.h b/variants/mkr1000/variant.h index 31ae7fe73..52ab20e3c 100644 --- a/variants/mkr1000/variant.h +++ b/variants/mkr1000/variant.h @@ -177,7 +177,6 @@ extern Uart Serial1; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER SERCOM5_Handler #endif // __cplusplus // These serial port names are intended to allow libraries and architecture-neutral diff --git a/variants/mkrfox1200/variant.cpp b/variants/mkrfox1200/variant.cpp index c9faf16cd..a17081b82 100644 --- a/variants/mkrfox1200/variant.cpp +++ b/variants/mkrfox1200/variant.cpp @@ -149,7 +149,7 @@ const PinDescription g_APinDescription[] = { { PORTA, 14, PIO_DIGITAL, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // SS: as GPIO { PORTA, 15, PIO_SERCOM_ALT, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // MISO: SERCOM4/PAD[3] { PORTA, 27, PIO_DIGITAL, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, - + { PORTA, 28, PIO_DIGITAL, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, { PORTB, 8, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG ), ADC_Channel2, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, { PORTB, 9, PIO_ANALOG, (PIN_ATTR_PWM|PIN_ATTR_TIMER ), ADC_Channel3, PWM4_CH1, TC4_CH1, EXTERNAL_INT_9 }, @@ -169,10 +169,3 @@ SERCOM sercom4(SERCOM4); SERCOM sercom5(SERCOM5); // Serial1 -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - diff --git a/variants/mkrfox1200/variant.h b/variants/mkrfox1200/variant.h index a443cc4af..dbc3afacf 100644 --- a/variants/mkrfox1200/variant.h +++ b/variants/mkrfox1200/variant.h @@ -179,7 +179,6 @@ extern Uart Serial1; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER SERCOM5_Handler #endif // __cplusplus // These serial port names are intended to allow libraries and architecture-neutral diff --git a/variants/mkrgsm1400/variant.cpp b/variants/mkrgsm1400/variant.cpp index 82c132afc..4af7e72f8 100644 --- a/variants/mkrgsm1400/variant.cpp +++ b/variants/mkrgsm1400/variant.cpp @@ -234,19 +234,3 @@ void initVariant() { pinMode(PIN_SERIAL2_DTR, OUTPUT); digitalWrite(PIN_SERIAL2_DTR, LOW); } - -// Serial1 -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - -// SerialGSM -Uart Serial2( &PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX, PIN_SERIAL2_RTS, PIN_SERIAL2_CTS); - -void SERIAL2_IT_HANDLER() -{ - Serial2.IrqHandler(); -} diff --git a/variants/mkrgsm1400/variant.h b/variants/mkrgsm1400/variant.h index 8820dec24..6a973c24c 100644 --- a/variants/mkrgsm1400/variant.h +++ b/variants/mkrgsm1400/variant.h @@ -158,7 +158,6 @@ extern Uart Serial1; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER SERCOM5_Handler // Serial2 - GSM extern Uart Serial2; @@ -167,7 +166,6 @@ extern Uart Serial2; #define PAD_SERIAL2_TX (UART_TX_RTS_CTS_PAD_0_2_3) #define PAD_SERIAL2_RX (SERCOM_RX_PAD_1) #define PERIPH_SERIAL2 sercom4 -#define SERIAL2_IT_HANDLER SERCOM4_Handler #define PIN_SERIAL2_RTS (28u) #define PIN_SERIAL2_CTS (29u) #define PIN_SERIAL2_DTR (35u) diff --git a/variants/mkrnb1500/variant.cpp b/variants/mkrnb1500/variant.cpp index f30cf7144..d23633e63 100644 --- a/variants/mkrnb1500/variant.cpp +++ b/variants/mkrnb1500/variant.cpp @@ -238,19 +238,3 @@ void initVariant() { pinMode(SARA_RTS, OUTPUT); digitalWrite(SARA_RTS, LOW); } - -// Serial1 -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - -// SerialSARA -Uart Serial2( &PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX); - -void SERIAL2_IT_HANDLER() -{ - Serial2.IrqHandler(); -} diff --git a/variants/mkrnb1500/variant.h b/variants/mkrnb1500/variant.h index aa4f84af0..4eaa1dc2d 100644 --- a/variants/mkrnb1500/variant.h +++ b/variants/mkrnb1500/variant.h @@ -159,7 +159,6 @@ extern Uart Serial1; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER SERCOM5_Handler // Serial2 - GSM extern Uart Serial2; @@ -168,7 +167,6 @@ extern Uart Serial2; #define PAD_SERIAL2_TX (UART_TX_PAD_0) #define PAD_SERIAL2_RX (SERCOM_RX_PAD_1) #define PERIPH_SERIAL2 sercom4 -#define SERIAL2_IT_HANDLER SERCOM4_Handler #define PIN_SERIAL2_RTS (28u) #define PIN_SERIAL2_CTS (29u) diff --git a/variants/mkrwan1300/variant.cpp b/variants/mkrwan1300/variant.cpp index 06d0c8785..7be6e72eb 100644 --- a/variants/mkrwan1300/variant.cpp +++ b/variants/mkrwan1300/variant.cpp @@ -148,7 +148,7 @@ const PinDescription g_APinDescription[] = { { PORTA, 14, PIO_DIGITAL, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // SS: as GPIO { PORTA, 15, PIO_SERCOM_ALT, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // MISO: SERCOM4/PAD[3] { PORTA, 27, PIO_DIGITAL, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, - + { PORTA, 28, PIO_DIGITAL, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, { PORTB, 8, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG ), ADC_Channel2, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, { PORTB, 9, PIO_DIGITAL, (PIN_ATTR_PWM|PIN_ATTR_TIMER ), ADC_Channel3, PWM4_CH1, TC4_CH1, EXTERNAL_INT_9 }, @@ -172,17 +172,5 @@ SERCOM sercom4(SERCOM4); SERCOM sercom5(SERCOM5); // Serial1 -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} // SerialLoRa -Uart Serial2( &PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX); - -void SERIAL2_IT_HANDLER() -{ - Serial2.IrqHandler(); -} \ No newline at end of file diff --git a/variants/mkrwan1300/variant.h b/variants/mkrwan1300/variant.h index 5f35c0872..39e1c8778 100644 --- a/variants/mkrwan1300/variant.h +++ b/variants/mkrwan1300/variant.h @@ -169,7 +169,6 @@ extern Uart Serial1; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER SERCOM5_Handler // Serial2 extern Uart Serial2; @@ -178,7 +177,6 @@ extern Uart Serial2; #define PAD_SERIAL2_TX (UART_TX_PAD_0) #define PAD_SERIAL2_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL2 sercom4 -#define SERIAL2_IT_HANDLER SERCOM4_Handler #endif // __cplusplus diff --git a/variants/mkrwifi1010/variant.cpp b/variants/mkrwifi1010/variant.cpp index 02151daaa..668e78dcd 100644 --- a/variants/mkrwifi1010/variant.cpp +++ b/variants/mkrwifi1010/variant.cpp @@ -234,19 +234,3 @@ void initVariant() { pinMode(NINA_RESETN, OUTPUT); digitalWrite(NINA_RESETN, HIGH); } - -// Serial1 -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - -// Serial2 -Uart Serial2( &PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX, PIN_SERIAL2_RTS, PIN_SERIAL2_CTS); - -void SERIAL2_IT_HANDLER() -{ - Serial2.IrqHandler(); -} diff --git a/variants/mkrwifi1010/variant.h b/variants/mkrwifi1010/variant.h index e86ba8c99..f587d5b05 100644 --- a/variants/mkrwifi1010/variant.h +++ b/variants/mkrwifi1010/variant.h @@ -176,7 +176,6 @@ extern Uart Serial1; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER SERCOM5_Handler // Serial2 extern Uart Serial2; @@ -185,7 +184,6 @@ extern Uart Serial2; #define PAD_SERIAL2_TX (UART_TX_RTS_CTS_PAD_0_2_3) #define PAD_SERIAL2_RX (SERCOM_RX_PAD_1) #define PERIPH_SERIAL2 sercom4 -#define SERIAL2_IT_HANDLER SERCOM4_Handler #define PIN_SERIAL2_RTS (28u) #define PIN_SERIAL2_CTS (29u) diff --git a/variants/mkrzero/variant.cpp b/variants/mkrzero/variant.cpp index c6f008d1d..96feae51e 100644 --- a/variants/mkrzero/variant.cpp +++ b/variants/mkrzero/variant.cpp @@ -150,7 +150,7 @@ const PinDescription g_APinDescription[] = { { PORTA, 14, PIO_DIGITAL, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // SS: as GPIO { PORTA, 15, PIO_SERCOM_ALT, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // MISO: SERCOM4/PAD[3] { PORTA, 27, PIO_DIGITAL, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, - + { PORTA, 28, PIO_DIGITAL, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, { PORTB, 8, PIO_DIGITAL, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, { PORTB, 9, PIO_ANALOG, (PIN_ATTR_PWM|PIN_ATTR_TIMER ), ADC_Channel3, PWM4_CH1, TC4_CH1, EXTERNAL_INT_9 }, @@ -170,10 +170,3 @@ SERCOM sercom4(SERCOM4); SERCOM sercom5(SERCOM5); // Serial1 -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - diff --git a/variants/mkrzero/variant.h b/variants/mkrzero/variant.h index afd4ab0c0..31e1852b6 100644 --- a/variants/mkrzero/variant.h +++ b/variants/mkrzero/variant.h @@ -173,7 +173,6 @@ extern Uart Serial1; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER SERCOM5_Handler #endif // __cplusplus // These serial port names are intended to allow libraries and architecture-neutral diff --git a/variants/neokeytrinkey_m0/variant.cpp b/variants/neokeytrinkey_m0/variant.cpp index e64ad7b21..4e58e9f3c 100644 --- a/variants/neokeytrinkey_m0/variant.cpp +++ b/variants/neokeytrinkey_m0/variant.cpp @@ -27,19 +27,19 @@ const PinDescription g_APinDescription[]= { PORTA, 15, PIO_DIGITAL, (PIN_ATTR_DIGITAL), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 }, // Mechanical switch - { PORTA, 28, PIO_DIGITAL, (PIN_ATTR_DIGITAL), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_8 }, + { PORTA, 28, PIO_DIGITAL, (PIN_ATTR_DIGITAL), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_8 }, // Touch pad - { PORTA, 7, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel7, PWM1_CH1, TCC1_CH1, EXTERNAL_INT_7 }, + { PORTA, 7, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel7, PWM1_CH1, TCC1_CH1, EXTERNAL_INT_7 }, + - // USB pins { PORTA, 28, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB Host enable { PORTA, 24, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB/DM { PORTA, 25, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB/DP // Fake DAC pin just so we can compile stuff - { PORTA, 2, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel0, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_2 }, // A0 / D0 / DAC + { PORTA, 2, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel0, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_2 }, // A0 / D0 / DAC } ; const void* g_apTCInstances[TCC_INST_NUM+TC_INST_NUM]={ TCC0, TCC1, TCC2, TC3, TC4, TC5 } ; diff --git a/variants/neotrinkey_m0/variant.cpp b/variants/neotrinkey_m0/variant.cpp index 150034974..e4e9adbb3 100644 --- a/variants/neotrinkey_m0/variant.cpp +++ b/variants/neotrinkey_m0/variant.cpp @@ -30,7 +30,7 @@ const PinDescription g_APinDescription[]= { PORTA, 3, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG), ADC_Channel1, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_3 }, // Touch Pin 2 - { PORTA, 7, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel7, PWM1_CH1, TCC1_CH1, EXTERNAL_INT_7 }, + { PORTA, 7, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel7, PWM1_CH1, TCC1_CH1, EXTERNAL_INT_7 }, // USB pins { PORTA, 28, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB Host enable @@ -38,7 +38,7 @@ const PinDescription g_APinDescription[]= { PORTA, 25, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB/DP // Fake DAC pin just so we can compile stuff - { PORTA, 2, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel0, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_2 }, // A0 / D0 / DAC + { PORTA, 2, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel0, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_2 }, // A0 / D0 / DAC } ; const void* g_apTCInstances[TCC_INST_NUM+TC_INST_NUM]={ TCC0, TCC1, TCC2, TC3, TC4, TC5 } ; diff --git a/variants/pirkey/variant.cpp b/variants/pirkey/variant.cpp index 225a6eb42..f0cdbe448 100644 --- a/variants/pirkey/variant.cpp +++ b/variants/pirkey/variant.cpp @@ -86,11 +86,3 @@ SERCOM sercom0( SERCOM0 ) ; SERCOM sercom1( SERCOM1 ) ; SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - diff --git a/variants/pirkey/variant.h b/variants/pirkey/variant.h index 137178745..5c6d03495 100644 --- a/variants/pirkey/variant.h +++ b/variants/pirkey/variant.h @@ -118,7 +118,6 @@ static const uint8_t DAC0 = PIN_DAC0; #define PIN_SERIAL1_TX (3ul) // SWCLK PA30 #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PERIPH_SERIAL1 sercom1 -#define SERIAL1_IT_HANDLER SERCOM1_Handler /* * SPI Interfaces diff --git a/variants/pixeltrinkey_m0/variant.cpp b/variants/pixeltrinkey_m0/variant.cpp index 15c9cce9c..2ce95e009 100644 --- a/variants/pixeltrinkey_m0/variant.cpp +++ b/variants/pixeltrinkey_m0/variant.cpp @@ -24,7 +24,7 @@ const PinDescription g_APinDescription[]= { // DAC / A0 for monitoring 5V - { PORTA, 2, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel0, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_2 }, // A0 / D0 / DAC + { PORTA, 2, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel0, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_2 }, // A0 / D0 / DAC // Internal NeoPixel / D1 { PORTA, 1, PIO_ANALOG, PIN_ATTR_ANALOG, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, diff --git a/variants/proxlighttrinkey_m0/variant.cpp b/variants/proxlighttrinkey_m0/variant.cpp index d9688d084..8efea6e18 100644 --- a/variants/proxlighttrinkey_m0/variant.cpp +++ b/variants/proxlighttrinkey_m0/variant.cpp @@ -24,7 +24,7 @@ const PinDescription g_APinDescription[]= { // Fake DAC A0 pin just so we can compile stuff - { PORTA, 2, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel0, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_2 }, // A0 / D0 / DAC + { PORTA, 2, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel0, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_2 }, // A0 / D0 / DAC // touch 1 / A1 { PORTA, 7, PIO_ANALOG, (PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel7, PWM1_CH1, TCC1_CH1, EXTERNAL_INT_7 }, // TCC1/WO[1] @@ -40,7 +40,7 @@ const PinDescription g_APinDescription[]= // Interrupt D6 { PORTA, 0, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_0 }, - + // USB pins D7, D8, D9 { PORTA, 28, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB Host enable { PORTA, 24, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB/DM diff --git a/variants/pybadge_airlift_m4/variant.cpp b/variants/pybadge_airlift_m4/variant.cpp index 533259cbf..fbc8dd93a 100644 --- a/variants/pybadge_airlift_m4/variant.cpp +++ b/variants/pybadge_airlift_m4/variant.cpp @@ -119,7 +119,7 @@ const PinDescription g_APinDescription[] = // ESP GPIO0, BUSY, RESET (52-54) {PORTA, 31, PIO_DIGITAL, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE}, // D52 ESP32_GPIO0 {PORTA, 0, PIO_DIGITAL, PIN_ATTR_PWM_E, No_ADC_Channel, TC2_CH0, TC2_CH0, EXTERNAL_INT_0}, // D53 ESP32_BUSY - {PORTB, 12, PIO_DIGITAL, PIN_ATTR_PWM_F, No_ADC_Channel, TCC3_CH0, TC4_CH0, EXTERNAL_INT_12}, // D54 ESP32_RESET + {PORTB, 12, PIO_DIGITAL, PIN_ATTR_PWM_F, No_ADC_Channel, TCC3_CH0, TC4_CH0, EXTERNAL_INT_12}, // D54 ESP32_RESET }; @@ -133,22 +133,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} diff --git a/variants/pybadge_airlift_m4/variant.h b/variants/pybadge_airlift_m4/variant.h index 7d95f60b6..2e9374853 100644 --- a/variants/pybadge_airlift_m4/variant.h +++ b/variants/pybadge_airlift_m4/variant.h @@ -145,10 +145,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER_0 SERCOM5_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM5_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM5_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM5_3_Handler /* * SPI Interfaces diff --git a/variants/pybadge_m4/variant.cpp b/variants/pybadge_m4/variant.cpp index b6d4dbc56..61aaee045 100644 --- a/variants/pybadge_m4/variant.cpp +++ b/variants/pybadge_m4/variant.cpp @@ -46,7 +46,7 @@ const PinDescription g_APinDescription[]= { PORTA, 22, PIO_SERCOM, PIN_ATTR_PWM_G, No_ADC_Channel, TCC0_CH2, TC4_CH0, EXTERNAL_INT_6 }, // 13 (LED) - { PORTA, 23, PIO_SERCOM, PIN_ATTR_PWM_G, No_ADC_Channel, TCC0_CH3, TC4_CH1, EXTERNAL_INT_7 }, + { PORTA, 23, PIO_SERCOM, PIN_ATTR_PWM_G, No_ADC_Channel, TCC0_CH3, TC4_CH1, EXTERNAL_INT_7 }, // 14..23 - Analog pins // -------------------- @@ -113,7 +113,7 @@ const PinDescription g_APinDescription[]= { PORTB, 31, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 }, // D48 button clock { PORTB, 30, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_14 }, // D49 button data { PORTB, 0, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_0 }, // D50 button latch - + // D51 Speaker enable { PORTA, 27, PIO_DIGITAL, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, @@ -131,22 +131,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} diff --git a/variants/pybadge_m4/variant.h b/variants/pybadge_m4/variant.h index 3f57b8199..8a6a1937f 100644 --- a/variants/pybadge_m4/variant.h +++ b/variants/pybadge_m4/variant.h @@ -133,10 +133,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER_0 SERCOM5_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM5_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM5_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM5_3_Handler /* * SPI Interfaces diff --git a/variants/pygamer_advance_m4/variant.cpp b/variants/pygamer_advance_m4/variant.cpp index f4a4dbadc..70eb5449d 100644 --- a/variants/pygamer_advance_m4/variant.cpp +++ b/variants/pygamer_advance_m4/variant.cpp @@ -46,7 +46,7 @@ const PinDescription g_APinDescription[]= { PORTA, 22, PIO_SERCOM, PIN_ATTR_PWM_G, No_ADC_Channel, TCC0_CH2, TC4_CH0, EXTERNAL_INT_6 }, // 13 (LED) - { PORTA, 23, PIO_SERCOM, PIN_ATTR_PWM_G, No_ADC_Channel, TCC0_CH3, TC4_CH1, EXTERNAL_INT_7 }, + { PORTA, 23, PIO_SERCOM, PIN_ATTR_PWM_G, No_ADC_Channel, TCC0_CH3, TC4_CH1, EXTERNAL_INT_7 }, // 14..25 - Analog pins // -------------------- @@ -95,7 +95,7 @@ const PinDescription g_APinDescription[]= // 41..46 - TFT SPI port + control pins // -------------------- - { PORTB, 12, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_12 }, // SERCOM 4.0 MOSI + { PORTB, 12, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_12 }, // SERCOM 4.0 MOSI { PORTB, 13, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_13 }, // SERCOM 4.1 SCK { PORTB, 15, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 }, // SERCOM 4.3 'miso' (NC) { PORTB, 15, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 }, // D44 TFT CS @@ -109,7 +109,7 @@ const PinDescription g_APinDescription[]= { PORTB, 31, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 }, // D48 button clock { PORTB, 30, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_14 }, // D49 button data { PORTB, 0, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_0 }, // D50 button latch - + // D51 Speaker enable { PORTA, 27, PIO_DIGITAL, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, @@ -130,22 +130,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} diff --git a/variants/pygamer_advance_m4/variant.h b/variants/pygamer_advance_m4/variant.h index e3984bb9c..1bf3d1a1b 100644 --- a/variants/pygamer_advance_m4/variant.h +++ b/variants/pygamer_advance_m4/variant.h @@ -137,10 +137,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER_0 SERCOM5_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM5_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM5_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM5_3_Handler /* * SPI Interfaces diff --git a/variants/pygamer_m4/variant.cpp b/variants/pygamer_m4/variant.cpp index 70c1287df..813acecf8 100644 --- a/variants/pygamer_m4/variant.cpp +++ b/variants/pygamer_m4/variant.cpp @@ -46,7 +46,7 @@ const PinDescription g_APinDescription[]= { PORTA, 22, PIO_SERCOM, PIN_ATTR_PWM_G, No_ADC_Channel, TCC0_CH2, TC4_CH0, EXTERNAL_INT_6 }, // 13 (LED) - { PORTA, 23, PIO_SERCOM, PIN_ATTR_PWM_G, No_ADC_Channel, TCC0_CH3, TC4_CH1, EXTERNAL_INT_7 }, + { PORTA, 23, PIO_SERCOM, PIN_ATTR_PWM_G, No_ADC_Channel, TCC0_CH3, TC4_CH1, EXTERNAL_INT_7 }, // 14..25 - Analog pins // -------------------- @@ -109,7 +109,7 @@ const PinDescription g_APinDescription[]= { PORTB, 31, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 }, // D48 button clock { PORTB, 30, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_14 }, // D49 button data { PORTB, 0, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_0 }, // D50 button latch - + // D51 Speaker enable { PORTA, 27, PIO_DIGITAL, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, @@ -130,22 +130,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} diff --git a/variants/pygamer_m4/variant.h b/variants/pygamer_m4/variant.h index a25c84f45..969b5a14e 100644 --- a/variants/pygamer_m4/variant.h +++ b/variants/pygamer_m4/variant.h @@ -137,10 +137,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER_0 SERCOM5_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM5_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM5_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM5_3_Handler /* * SPI Interfaces diff --git a/variants/pyportal_m4/variant.cpp b/variants/pyportal_m4/variant.cpp index 22ee43d4b..de93d0e3f 100644 --- a/variants/pyportal_m4/variant.cpp +++ b/variants/pyportal_m4/variant.cpp @@ -130,22 +130,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} diff --git a/variants/pyportal_m4/variant.h b/variants/pyportal_m4/variant.h index d38410c6d..f95afe384 100644 --- a/variants/pyportal_m4/variant.h +++ b/variants/pyportal_m4/variant.h @@ -147,10 +147,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom4 -#define SERIAL1_IT_HANDLER_0 SERCOM4_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM4_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM4_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM4_3_Handler /* diff --git a/variants/pyportal_m4_titano/variant.cpp b/variants/pyportal_m4_titano/variant.cpp index b0dd42ef3..b0b03adad 100644 --- a/variants/pyportal_m4_titano/variant.cpp +++ b/variants/pyportal_m4_titano/variant.cpp @@ -130,22 +130,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} diff --git a/variants/pyportal_m4_titano/variant.h b/variants/pyportal_m4_titano/variant.h index 9b0c8388c..318615f89 100644 --- a/variants/pyportal_m4_titano/variant.h +++ b/variants/pyportal_m4_titano/variant.h @@ -147,10 +147,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom4 -#define SERIAL1_IT_HANDLER_0 SERCOM4_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM4_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM4_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM4_3_Handler /* diff --git a/variants/qtpy_m0/variant.cpp b/variants/qtpy_m0/variant.cpp index a99f10620..0f0fcd02a 100644 --- a/variants/qtpy_m0/variant.cpp +++ b/variants/qtpy_m0/variant.cpp @@ -38,11 +38,11 @@ const PinDescription g_APinDescription[]= // SPI SCK, MISO, MOSI { PORTA, 11, PIO_SERCOM_ALT, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER_ALT), ADC_Channel19, PWM0_CH3, TCC0_CH3, EXTERNAL_INT_11 }, // A8 / D8 / SCK / PWM - { PORTA, 9, PIO_SERCOM_ALT, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER_ALT), ADC_Channel17, PWM1_CH3, TCC1_CH3, EXTERNAL_INT_9 }, // A9 / D9 / MISO / PWM + { PORTA, 9, PIO_SERCOM_ALT, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER_ALT), ADC_Channel17, PWM1_CH3, TCC1_CH3, EXTERNAL_INT_9 }, // A9 / D9 / MISO / PWM { PORTA, 10, PIO_SERCOM_ALT, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER_ALT), ADC_Channel18, PWM0_CH2, TCC0_CH2, EXTERNAL_INT_10 }, // A10 / D10 / MOSI / PWM { PORTA, 18, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_2 }, // D11 Neopix - { PORTA, 15, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 }, // D12 Neopix power + { PORTA, 15, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 }, // D12 Neopix power // D13 fake pin { PORTA, 27, PIO_OUTPUT, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // used as fake output only @@ -70,12 +70,6 @@ SERCOM sercom1( SERCOM1 ) ; SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} void initVariant(void) { // special initialization code just for us diff --git a/variants/qtpy_m0/variant.h b/variants/qtpy_m0/variant.h index 400eda569..cea9a43da 100644 --- a/variants/qtpy_m0/variant.h +++ b/variants/qtpy_m0/variant.h @@ -124,7 +124,6 @@ static const uint8_t DAC0 = PIN_DAC0; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom0 -#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces diff --git a/variants/rotarytrinkey_m0/variant.cpp b/variants/rotarytrinkey_m0/variant.cpp index e3c824ff5..0b5aa7f4b 100644 --- a/variants/rotarytrinkey_m0/variant.cpp +++ b/variants/rotarytrinkey_m0/variant.cpp @@ -30,22 +30,22 @@ const PinDescription g_APinDescription[]= { PORTA, 0, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_0 }, // Encoder pin 2 - { PORTA, 4, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel4, PWM0_CH0, TCC0_CH0, EXTERNAL_INT_4 }, + { PORTA, 4, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel4, PWM0_CH0, TCC0_CH0, EXTERNAL_INT_4 }, // Encoder switch - { PORTA, 27, PIO_DIGITAL, (PIN_ATTR_DIGITAL), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 }, + { PORTA, 27, PIO_DIGITAL, (PIN_ATTR_DIGITAL), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 }, // Touch pad - { PORTA, 6, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel6, PWM1_CH0, TCC1_CH0, EXTERNAL_INT_6 }, + { PORTA, 6, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel6, PWM1_CH0, TCC1_CH0, EXTERNAL_INT_6 }, + - // USB pins { PORTA, 28, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB Host enable { PORTA, 24, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB/DM { PORTA, 25, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB/DP // Fake DAC pin just so we can compile stuff - { PORTA, 2, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel0, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_2 }, // A0 / D0 / DAC + { PORTA, 2, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel0, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_2 }, // A0 / D0 / DAC } ; const void* g_apTCInstances[TCC_INST_NUM+TC_INST_NUM]={ TCC0, TCC1, TCC2, TC3, TC4, TC5 } ; diff --git a/variants/sht4xtrinkey_m0/variant.cpp b/variants/sht4xtrinkey_m0/variant.cpp index 7d25cd281..7f69b38dd 100644 --- a/variants/sht4xtrinkey_m0/variant.cpp +++ b/variants/sht4xtrinkey_m0/variant.cpp @@ -24,7 +24,7 @@ const PinDescription g_APinDescription[]= { // Fake DAC A0 pin just so we can compile stuff - { PORTA, 2, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel0, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_2 }, // A0 / D0 / DAC + { PORTA, 2, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel0, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_2 }, // A0 / D0 / DAC // touch / A1 { PORTA, 7, PIO_ANALOG, (PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel7, PWM1_CH1, TCC1_CH1, EXTERNAL_INT_7 }, // TCC1/WO[1] diff --git a/variants/slidetrinkey_m0/variant.cpp b/variants/slidetrinkey_m0/variant.cpp index 88a348db7..bbbb14862 100644 --- a/variants/slidetrinkey_m0/variant.cpp +++ b/variants/slidetrinkey_m0/variant.cpp @@ -30,7 +30,7 @@ const PinDescription g_APinDescription[]= { PORTA, 7, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel7, PWM1_CH1, TCC1_CH1, EXTERNAL_INT_7 }, // NeoPixel - { PORTA, 4, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel4, PWM0_CH0, TCC0_CH0, EXTERNAL_INT_4 }, + { PORTA, 4, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel4, PWM0_CH0, TCC0_CH0, EXTERNAL_INT_4 }, // USB pins { PORTA, 28, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB Host enable diff --git a/variants/trellis_m4/variant.cpp b/variants/trellis_m4/variant.cpp index 473143f43..8285c4096 100644 --- a/variants/trellis_m4/variant.cpp +++ b/variants/trellis_m4/variant.cpp @@ -93,7 +93,7 @@ const PinDescription g_APinDescription[]= // Unused { PORTB, 2, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel14, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_2 }, - { PORTB, 3, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel15, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_3 }, + { PORTB, 3, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel15, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_3 }, // AREF tied to 3.3V { PORTA, 3, PIO_ANALOG, PIN_ATTR_ANALOG, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_3 }, // DAC/VREFP @@ -109,22 +109,3 @@ SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER_0() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_1() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_2() -{ - Serial1.IrqHandler(); -} -void SERIAL1_IT_HANDLER_3() -{ - Serial1.IrqHandler(); -} diff --git a/variants/trellis_m4/variant.h b/variants/trellis_m4/variant.h index c90573ef2..eaf6f042f 100644 --- a/variants/trellis_m4/variant.h +++ b/variants/trellis_m4/variant.h @@ -129,10 +129,6 @@ static const uint8_t LINE_RIGHT = PIN_LINE_RIGHT; #define PAD_SERIAL1_RX (SERCOM_RX_PAD_1) #define PAD_SERIAL1_TX (UART_TX_PAD_0) #define PERIPH_SERIAL1 sercom4 -#define SERIAL1_IT_HANDLER_0 SERCOM4_0_Handler -#define SERIAL1_IT_HANDLER_1 SERCOM4_1_Handler -#define SERIAL1_IT_HANDLER_2 SERCOM4_2_Handler -#define SERIAL1_IT_HANDLER_3 SERCOM4_3_Handler /* * SPI Interfaces diff --git a/variants/trinket_m0/variant.cpp b/variants/trinket_m0/variant.cpp index 739e7b071..ebff53f2e 100644 --- a/variants/trinket_m0/variant.cpp +++ b/variants/trinket_m0/variant.cpp @@ -93,11 +93,3 @@ SERCOM sercom0( SERCOM0 ) ; SERCOM sercom1( SERCOM1 ) ; SERCOM sercom2( SERCOM2 ) ; SERCOM sercom3( SERCOM3 ) ; - -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; - -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} - diff --git a/variants/trinket_m0/variant.h b/variants/trinket_m0/variant.h index ed1310f37..f2ce8ee47 100644 --- a/variants/trinket_m0/variant.h +++ b/variants/trinket_m0/variant.h @@ -121,7 +121,6 @@ static const uint8_t DAC0 = PIN_DAC0; #define PIN_SERIAL1_TX (4ul) // PA06 #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PERIPH_SERIAL1 sercom0 -#define SERIAL1_IT_HANDLER SERCOM0_Handler /* * SPI Interfaces diff --git a/variants/zero_radio/variant.cpp b/variants/zero_radio/variant.cpp index 59303e64f..ac1d42d04 100644 --- a/variants/zero_radio/variant.cpp +++ b/variants/zero_radio/variant.cpp @@ -218,8 +218,3 @@ SERCOM sercom4( SERCOM4 ) ; SERCOM sercom5( SERCOM5 ) ; // We'll use sercom5 for Serial1 hardware serial since pins 0 and 1 are not external -Uart Serial1( &PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ; -void SERIAL1_IT_HANDLER() -{ - Serial1.IrqHandler(); -} diff --git a/variants/zero_radio/variant.h b/variants/zero_radio/variant.h index 6718c372e..42fdd8111 100644 --- a/variants/zero_radio/variant.h +++ b/variants/zero_radio/variant.h @@ -140,7 +140,6 @@ static const uint8_t ATN = PIN_ATN; #define PAD_SERIAL1_TX (UART_TX_PAD_2) #define PAD_SERIAL1_RX (SERCOM_RX_PAD_3) #define PERIPH_SERIAL1 sercom5 -#define SERIAL1_IT_HANDLER SERCOM5_Handler /* * SPI Interfaces From d8e11975a36200a2e6f531009154044245971ac9 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sat, 20 Jun 2026 16:32:53 -0400 Subject: [PATCH 03/79] Add shared PendSV deferred service dispatcher --- cores/arduino/PendSV.cpp | 116 +++++++++++++++++++++++++++++++++++++++ cores/arduino/PendSV.h | 34 ++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 cores/arduino/PendSV.cpp create mode 100644 cores/arduino/PendSV.h diff --git a/cores/arduino/PendSV.cpp b/cores/arduino/PendSV.cpp new file mode 100644 index 000000000..bb5f32c7b --- /dev/null +++ b/cores/arduino/PendSV.cpp @@ -0,0 +1,116 @@ +#include "PendSV.h" + +#include +#include + +namespace { +PendSV pendSv; +} + +PendSV &PendSV::instance() { + return pendSv; +} + +uint32_t PendSV::enterCritical() { + const uint32_t primask = __get_PRIMASK(); + __disable_irq(); + return primask; +} + +void PendSV::exitCritical(uint32_t primask) { + __set_PRIMASK(primask); +} + +bool PendSV::registerService(uint8_t serviceId, ServiceFn fn, void *context) { + if (serviceId >= kMaxServices || fn == nullptr) + return false; + + const uint32_t primask = enterCritical(); + pendingCount_[serviceId] = 0; + pendingMask_ &= ~(1u << serviceId); + services_[serviceId].fn = fn; + services_[serviceId].context = context; + exitCritical(primask); + + return true; +} + +void PendSV::clearService(uint8_t serviceId) { + if (serviceId >= kMaxServices) + return; + + const uint32_t primask = enterCritical(); + services_[serviceId].fn = nullptr; + services_[serviceId].context = nullptr; + pendingCount_[serviceId] = 0; + pendingMask_ &= ~(1u << serviceId); + exitCritical(primask); +} + +void PendSV::setPending(uint8_t serviceId) { + if (serviceId >= kMaxServices) + return; + + const uint32_t primask = enterCritical(); + uint16_t &pendingCount = pendingCount_[serviceId]; + if (pendingCount < UINT16_MAX) + ++pendingCount; + pendingMask_ |= (1u << serviceId); + exitCritical(primask); + + __DMB(); + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; +} + +void PendSV::dispatchPending() { + // Bound one PendSV entry so high-rate producers do not monopolize return to + // thread mode. Remaining work re-pends PendSV below. + uint8_t dispatched = 0; + + while (dispatched < kDispatchBudget) { + uint32_t primask = enterCritical(); + const uint32_t pending = pendingMask_; + if (pending == 0) { + exitCritical(primask); + return; + } + + const uint8_t serviceId = static_cast(__builtin_ctz(pending)); + uint16_t &pendingCount = pendingCount_[serviceId]; + + if (pendingCount == 0) { + // Defensive scrub in case mask and count drift out of sync. + pendingMask_ &= ~(1u << serviceId); + exitCritical(primask); + continue; + } + + --pendingCount; + if (pendingCount == 0) + pendingMask_ &= ~(1u << serviceId); + + ServiceEntry entry = services_[serviceId]; + exitCritical(primask); + + // Pending work without a registered service is intentionally dropped. + // Producers are expected to register before calling setPending(), and + // clearService() cancels queued work for that service. + if (entry.fn != nullptr) + entry.fn(serviceId, entry.context); + + ++dispatched; + } + + const uint32_t primask = enterCritical(); + const bool hasRemaining = (pendingMask_ != 0); + exitCritical(primask); + + if (hasRemaining) { + __DMB(); + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; + } +} + +extern "C" void PendSV_Handler(void) { + PendSV::instance().dispatchPending(); +} diff --git a/cores/arduino/PendSV.h b/cores/arduino/PendSV.h new file mode 100644 index 000000000..ab22a43e6 --- /dev/null +++ b/cores/arduino/PendSV.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +class PendSV { + public: + using ServiceFn = void (*)(uint8_t serviceId, void *context); + static constexpr uint8_t kMaxServices = 32; + static constexpr uint8_t kDispatchBudget = 16; + static_assert(kMaxServices <= 32, "PendSV pending mask supports at most 32 services"); + + static PendSV &instance(); + + bool registerService(uint8_t serviceId, ServiceFn fn, void *context = nullptr); + // Cancels queued work that has not started dispatching. If a callback was + // already copied for dispatch, its context must remain valid until it returns. + void clearService(uint8_t serviceId); + void dispatchPending(); + void setPending(uint8_t serviceId); + + private: + static uint32_t enterCritical(); + static void exitCritical(uint32_t primask); + + struct ServiceEntry { + ServiceFn fn = nullptr; + void *context = nullptr; + }; + + std::array services_{}; + std::array pendingCount_{}; + volatile uint32_t pendingMask_ = 0; +}; From b04854cd62be46d222a9c8a7de1fdee71c0552dc Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Tue, 27 Jan 2026 19:38:10 -0500 Subject: [PATCH 04/79] define sercom transaction structure for enqueueing async transactions. --- cores/arduino/SERCOM.h | 1 + cores/arduino/SERCOM_Txn.h | 66 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 cores/arduino/SERCOM_Txn.h diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index 35c90f1c4..9cd3ef0f3 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -20,6 +20,7 @@ #define _SERCOM_CLASS_ #include "sam.h" +#include "SERCOM_Txn.h" // SAMD51 has configurable MAX_SPI, else use peripheral clock default. // Update: changing MAX_SPI via compiler flags is DEPRECATED, because diff --git a/cores/arduino/SERCOM_Txn.h b/cores/arduino/SERCOM_Txn.h new file mode 100644 index 000000000..438de28a3 --- /dev/null +++ b/cores/arduino/SERCOM_Txn.h @@ -0,0 +1,66 @@ +#ifndef _SERCOM_TXN_H_ +#define _SERCOM_TXN_H_ + +#include +#include + +struct SercomTxn { + uint16_t config; // common + protocol-specific flags/fields + uint16_t address; // I2C addr, SPI CS/baud, UART RTS/CTS + size_t length; + const uint8_t* txPtr; + uint8_t* rxPtr; + void (*onComplete)(void* user, int status); + void* user; +}; + +// I2C config flags and helpers +enum : uint16_t { + I2C_CFG_READ = 1u << 0, + I2C_CFG_STOP = 1u << 1, + I2C_CFG_CRC = 1u << 2, + I2C_CFG_10BIT = 1u << 3, + I2C_CFG_RESTART = 1u << 4, +}; + +static inline uint16_t I2C_ADDR(uint16_t addr10) { return addr10 & 0x03FFu; } +static inline uint8_t I2C_ADDR7(uint16_t addr) { return (uint8_t)(addr & 0x7Fu); } + +// SPI config flags and helpers +enum : uint16_t { + SPI_CFG_READ = 1u << 0, + SPI_CFG_STOP = 1u << 1, + SPI_CFG_CRC = 1u << 2, + SPI_CFG_CPOL = 1u << 3, + SPI_CFG_CPHA = 1u << 4, + SPI_CFG_DORD = 1u << 5, + SPI_CFG_CHAR9 = 1u << 6, + SPI_CFG_CS_HOLD = 1u << 7, + SPI_CFG_DUMMY = 1u << 8, + SPI_CFG_TX_ONLY = 1u << 9, +}; + +static inline uint16_t SPI_ADDR_CS(uint8_t cs) { return (uint16_t)cs; } +static inline uint16_t SPI_ADDR_BAUD(uint8_t baud) { return (uint16_t)baud << 8; } +static inline uint8_t SPI_ADDR_GET_CS(uint16_t addr) { return (uint8_t)(addr & 0x00FFu); } +static inline uint8_t SPI_ADDR_GET_BAUD(uint16_t addr) { return (uint8_t)(addr >> 8); } + +// UART config flags and helpers +enum : uint16_t { + UART_CFG_READ = 1u << 0, + UART_CFG_STOP = 1u << 1, + UART_CFG_CRC = 1u << 2, + UART_CFG_BREAK = 1u << 3, + UART_CFG_MARK = 1u << 4, + UART_CFG_TX_ONLY = 1u << 5, + UART_CFG_RX_ONLY = 1u << 6, +}; + +static inline uint16_t UART_ADDR_RTS(uint8_t pin) { return (uint16_t)pin; } +static inline uint16_t UART_ADDR_CTS(uint8_t pin) { return (uint16_t)pin << 8; } +static inline uint8_t UART_ADDR_GET_RTS(uint16_t addr) { return (uint8_t)(addr & 0x00FFu); } +static inline uint8_t UART_ADDR_GET_CTS(uint16_t addr) { return (uint8_t)(addr >> 8); } + +static constexpr uint8_t SERCOM_PIN_NONE = 0xFFu; + +#endif From 60af7ce629100c82894ca964edc6bd5c8316a26a Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Wed, 28 Jan 2026 07:38:52 -0500 Subject: [PATCH 05/79] provide unified structure for maintaining and accessing sercom protocol configuration --- cores/arduino/SERCOM.cpp | 73 ++++++++++++++++++++++++++++++++++++++++ cores/arduino/SERCOM.h | 22 ++++++++++++ 2 files changed, 95 insertions(+) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index f945a847f..afba3c5ac 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -794,6 +794,79 @@ static const struct { #endif // end !SAMD51 +std::array SERCOM::s_states = {}; +volatile uint32_t SERCOM::s_pendingMask = 0; + +bool SERCOM::claim(uint8_t sercomId, Role role) +{ + if (sercomId >= kSercomCount) + return false; + + SercomState &state = s_states[sercomId]; + if (state.role != Role::None && state.role != role) + return false; + + state.role = role; + return true; +} + +void SERCOM::release(uint8_t sercomId) +{ + if (sercomId >= kSercomCount) + return; + + SercomState &state = s_states[sercomId]; + state.role = Role::None; + state.service = nullptr; + s_pendingMask &= ~(1u << sercomId); +} + +bool SERCOM::registerService(uint8_t sercomId, ServiceFn fn) +{ + if (sercomId >= kSercomCount) + return false; + + s_states[sercomId].service = fn; + return true; +} + +void SERCOM::setPending(uint8_t sercomId) +{ + if (sercomId >= kSercomCount) + return; + + __disable_irq(); + s_pendingMask |= (1u << sercomId); + __enable_irq(); + __DMB(); + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; +} + +void SERCOM::dispatchPending(void) +{ + uint32_t pending; + + __disable_irq(); + pending = s_pendingMask; + s_pendingMask = 0; + __enable_irq(); + + for (size_t i = 0; i < kSercomCount; ++i) + { + if ((pending & (1u << i)) == 0) + continue; + + ServiceFn fn = s_states[i].service; + if (fn) + fn(); + } +} + +extern "C" void PendSV_Handler(void) +{ + SERCOM::dispatchPending(); +} + int8_t SERCOM::getSercomIndex(void) { for(uint8_t i=0; i<(sizeof(sercomData) / sizeof(sercomData[0])); i++) { if(sercom == sercomData[i].sercomPtr) return i; diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index 9cd3ef0f3..24e0ccdd4 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -21,6 +21,7 @@ #include "sam.h" #include "SERCOM_Txn.h" +#include // SAMD51 has configurable MAX_SPI, else use peripheral clock default. // Update: changing MAX_SPI via compiler flags is DEPRECATED, because @@ -255,12 +256,33 @@ class SERCOM uint32_t getFreqRef(void) { return F_CPU; }; #endif + // --- Async SERCOM scaffolding (Phase 1) --- + enum class Role : uint8_t { None = 0, UART, SPI, I2C }; + using ServiceFn = void (*)(); + + static bool claim(uint8_t sercomId, Role role); + static void release(uint8_t sercomId); + static bool registerService(uint8_t sercomId, ServiceFn fn); + static void setPending(uint8_t sercomId); + static void dispatchPending(void); + private: Sercom *sercom; uint32_t freqRef = 48000000ul; // Frequency corresponding to clockSource #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) SercomClockSource clockSource; #endif + #if defined(SERCOM_INST_NUM) && (SERCOM_INST_NUM > 0) + static constexpr size_t kSercomCount = SERCOM_INST_NUM; + #else + static constexpr size_t kSercomCount = 6; + #endif + struct SercomState { + Role role = Role::None; + ServiceFn service = nullptr; + }; + static std::array s_states; + static volatile uint32_t s_pendingMask; uint8_t calculateBaudrateSynchronous(uint32_t baudrate); uint32_t division(uint32_t dividend, uint32_t divisor) ; void initClockNVIC( void ) ; From 042458f8be75f10ee2103007bb0092177c26b59c Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Wed, 28 Jan 2026 09:08:32 -0500 Subject: [PATCH 06/79] add support for mux pin deconfliction during development. --- cores/arduino/SERCOM.cpp | 54 +++++++++++++++++++++++++++++++++++++++- cores/arduino/SERCOM.h | 45 ++++++++++++++++++++++++++------- 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index afba3c5ac..62f08039d 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -818,6 +818,9 @@ void SERCOM::release(uint8_t sercomId) SercomState &state = s_states[sercomId]; state.role = Role::None; state.service = nullptr; +#ifdef SERCOM_STRICT_PADS + clearPads(sercomId); +#endif // SERCOM_STRICT_PADS s_pendingMask &= ~(1u << sercomId); } @@ -830,9 +833,58 @@ bool SERCOM::registerService(uint8_t sercomId, ServiceFn fn) return true; } -void SERCOM::setPending(uint8_t sercomId) +#ifdef SERCOM_STRICT_PADS +bool SERCOM::registerPads(uint8_t sercomId, const PadFunc (&pads)[4], bool muxFunctionD) { if (sercomId >= kSercomCount) + return false; + + SercomState &state = s_states[sercomId]; + for (size_t i = 0; i < 4; ++i) + { + PadFunc desired = pads[i]; + if (desired == PadFunc::None) + continue; + PadFunc existing = state.pads[i]; + if (existing != PadFunc::None && existing != desired) + return false; + } + if (state.padsConfigured && state.muxFunctionD != muxFunctionD) + return false; + + bool any = false; + for (size_t i = 0; i < 4; ++i) + { + PadFunc desired = pads[i]; + if (desired == PadFunc::None) + continue; + state.pads[i] = desired; + any = true; + } + if (any) + { + state.padsConfigured = true; + state.muxFunctionD = muxFunctionD; + } + return true; +} + +void SERCOM::clearPads(uint8_t sercomId) +{ + if (sercomId >= kSercomCount) + return; + + SercomState &state = s_states[sercomId]; + for (size_t i = 0; i < 4; ++i) + state.pads[i] = PadFunc::None; + state.padsConfigured = false; + state.muxFunctionD = false; +} +#endif // SERCOM_STRICT_PADS + +void SERCOM::setPending(uint8_t sercomId) +{ + if (sercomId >= kSercomCount || sercomId < 0) return; __disable_irq(); diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index 24e0ccdd4..f2ee1532b 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -239,7 +239,8 @@ class SERCOM int availableWIRE( void ) ; uint8_t readDataWIRE( void ) ; int8_t getSercomIndex(void); - uint32_t getSercomFreqRef(void); + uint32_t getSercomFreqRef(void); + #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) // SERCOM clock source override is only available on // SAMD51 (not 21) ... but these functions are declared @@ -266,21 +267,47 @@ class SERCOM static void setPending(uint8_t sercomId); static void dispatchPending(void); - private: - Sercom *sercom; - uint32_t freqRef = 48000000ul; // Frequency corresponding to clockSource +#ifdef SERCOM_STRICT_PADS + enum class PadFunc : uint8_t { + None = 0, + UartTx, + UartRx, + SpiMosi, + SpiMiso, + SpiSck, + SpiSs, + WireSda, + WireScl + }; + + static bool registerPads(uint8_t sercomId, const PadFunc (&pads)[4], bool muxFunctionD); + static void clearPads(uint8_t sercomId); +#endif // SERCOM_STRICT_PADS + + private: + Sercom *sercom; + uint32_t freqRef = 48000000ul; // Frequency corresponding to clockSource #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) - SercomClockSource clockSource; + SercomClockSource clockSource; #endif - #if defined(SERCOM_INST_NUM) && (SERCOM_INST_NUM > 0) + +#if defined(SERCOM_INST_NUM) && (SERCOM_INST_NUM > 0) static constexpr size_t kSercomCount = SERCOM_INST_NUM; - #else - static constexpr size_t kSercomCount = 6; - #endif +#else + #pragma message("SERCOM_INST_NUM not defined; SERCOM support disabled.") + static constexpr size_t kSercomCount = 0; +#endif // SERCOM_INST_NUM + struct SercomState { Role role = Role::None; ServiceFn service = nullptr; +#ifdef SERCOM_STRICT_PADS + PadFunc pads[4] = { PadFunc::None, PadFunc::None, PadFunc::None, PadFunc::None }; + bool padsConfigured = false; + bool muxFunctionD = false; +#endif // SERCOM_STRICT_PADS }; + static std::array s_states; static volatile uint32_t s_pendingMask; uint8_t calculateBaudrateSynchronous(uint32_t baudrate); From 755e70ab7afee363f0fe02f697dfd14392f86175 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Wed, 28 Jan 2026 12:06:15 -0500 Subject: [PATCH 07/79] integrate needed dma sercom structure into the SERCOM class --- cores/arduino/SERCOM.cpp | 251 +++++++++++++++++++++++++++++++++++++++ cores/arduino/SERCOM.h | 47 ++++++++ 2 files changed, 298 insertions(+) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 62f08039d..1db3095b2 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -21,6 +21,10 @@ #include "variant.h" #include "Arduino.h" +#ifdef USE_ZERODMA +#include +#endif + #ifndef WIRE_RISE_TIME_NANOSECONDS // Default rise time in nanoseconds, based on 4.7K ohm pull up resistors // you can override this value in your variant if needed @@ -833,6 +837,253 @@ bool SERCOM::registerService(uint8_t sercomId, ServiceFn fn) return true; } +#ifdef USE_ZERODMA +SERCOM::DmaStatus SERCOM::dmaInit(uint8_t txTrigger, uint8_t rxTrigger) +{ + if (_dmaConfigured) + return DmaStatus::Ok; + + _dmaTxTrigger = txTrigger; + _dmaRxTrigger = rxTrigger; + + if (!_dmaTx) + _dmaTx = new Adafruit_ZeroDMA(); + if (!_dmaRx) + _dmaRx = new Adafruit_ZeroDMA(); + if (!_dmaTx || !_dmaRx) + { + _dmaLastError = DmaStatus::AllocateFailed; + dmaRelease(); + return _dmaLastError; + } + + if (_dmaTx->allocate() != DMA_STATUS_OK) + { + _dmaLastError = DmaStatus::AllocateFailed; + dmaRelease(); + return _dmaLastError; + } + if (_dmaRx->allocate() != DMA_STATUS_OK) + { + _dmaLastError = DmaStatus::AllocateFailed; + dmaRelease(); + return _dmaLastError; + } + + _dmaTx->setTrigger(_dmaTxTrigger); + _dmaTx->setAction(DMA_TRIGGER_ACTON_BEAT); + _dmaRx->setTrigger(_dmaRxTrigger); + _dmaRx->setAction(DMA_TRIGGER_ACTON_BEAT); + + if (_dmaTxCb) + _dmaTx->setCallback(_dmaTxCb); + if (_dmaRxCb) + _dmaRx->setCallback(_dmaRxCb); + + _dmaConfigured = true; + _dmaLastError = DmaStatus::Ok; + return _dmaLastError; +} + +void SERCOM::dmaSetCallbacks(DmaCallback txCb, DmaCallback rxCb) +{ + _dmaTxCb = txCb; + _dmaRxCb = rxCb; + + if (_dmaConfigured) + { + if (_dmaTxCb) + _dmaTx->setCallback(_dmaTxCb); + if (_dmaRxCb) + _dmaRx->setCallback(_dmaRxCb); + } +} + +SERCOM::DmaStatus SERCOM::dmaStartTx(const void* src, void* dstReg, size_t len) +{ + if (!_dmaConfigured || !_dmaTx) + { + _dmaLastError = DmaStatus::NotConfigured; + return _dmaLastError; + } + if (src == nullptr || dstReg == nullptr) + { + _dmaLastError = DmaStatus::NullPtr; + return _dmaLastError; + } + if (len == 0) + { + _dmaLastError = DmaStatus::ZeroLen; + return _dmaLastError; + } + + if (_dmaTxDesc == nullptr) + _dmaTxDesc = _dmaTx->addDescriptor((void*)src, dstReg, len, DMA_BEAT_SIZE_BYTE, true, false); + else + _dmaTx->changeDescriptor(_dmaTxDesc, (void*)src, dstReg, len); + + if (_dmaTxDesc == nullptr) + { + _dmaLastError = DmaStatus::DescriptorFailed; + return _dmaLastError; + } + + ZeroDMAstatus status = _dmaTx->startJob(); + if (status != DMA_STATUS_OK) + { + _dmaTx->abort(); + _dmaLastError = DmaStatus::StartFailed; + return _dmaLastError; + } + + _dmaTxActive = true; + _dmaLastError = DmaStatus::Ok; + return _dmaLastError; +} + +SERCOM::DmaStatus SERCOM::dmaStartRx(void* dst, void* srcReg, size_t len) +{ + if (!_dmaConfigured || !_dmaRx) + { + _dmaLastError = DmaStatus::NotConfigured; + return _dmaLastError; + } + if (dst == nullptr || srcReg == nullptr) + { + _dmaLastError = DmaStatus::NullPtr; + return _dmaLastError; + } + if (len == 0) + { + _dmaLastError = DmaStatus::ZeroLen; + return _dmaLastError; + } + + if (_dmaRxDesc == nullptr) + _dmaRxDesc = _dmaRx->addDescriptor(srcReg, dst, len, DMA_BEAT_SIZE_BYTE, false, true); + else + _dmaRx->changeDescriptor(_dmaRxDesc, srcReg, dst, len); + + if (_dmaRxDesc == nullptr) + { + _dmaLastError = DmaStatus::DescriptorFailed; + return _dmaLastError; + } + + ZeroDMAstatus status = _dmaRx->startJob(); + if (status != DMA_STATUS_OK) + { + _dmaRx->abort(); + _dmaLastError = DmaStatus::StartFailed; + return _dmaLastError; + } + + _dmaRxActive = true; + _dmaLastError = DmaStatus::Ok; + return _dmaLastError; +} + +SERCOM::DmaStatus SERCOM::dmaStartDuplex(const void* txSrc, void* rxDst, void* txReg, void* rxReg, size_t len, + const uint8_t* dummyTx) +{ + if (len == 0) + { + _dmaLastError = DmaStatus::ZeroLen; + return _dmaLastError; + } + DmaStatus st = dmaStartRx(rxDst, rxReg, len); + if (st != DmaStatus::Ok) + return st; + static const uint8_t kDummyByte = 0xFF; + const void* txPtr = txSrc ? txSrc : (dummyTx ? dummyTx : &kDummyByte); + st = dmaStartTx(txPtr, txReg, len); + if (st != DmaStatus::Ok) + { + dmaAbortRx(); + return st; + } + return DmaStatus::Ok; +} + +void SERCOM::dmaAbortTx() +{ + if (_dmaTx) + _dmaTx->abort(); + _dmaTxActive = false; +} + +void SERCOM::dmaAbortRx() +{ + if (_dmaRx) + _dmaRx->abort(); + _dmaRxActive = false; +} + +void SERCOM::dmaRelease() +{ + if (!_dmaConfigured) + { + dmaResetDescriptors(); + if (_dmaTx) + { + delete _dmaTx; + _dmaTx = nullptr; + } + if (_dmaRx) + { + delete _dmaRx; + _dmaRx = nullptr; + } + _dmaLastError = DmaStatus::Ok; + return; + } + + dmaAbortTx(); + dmaAbortRx(); + + if (_dmaTx) + _dmaTx->free(); + if (_dmaRx) + _dmaRx->free(); + + dmaResetDescriptors(); + + _dmaConfigured = false; + if (_dmaTx) + { + delete _dmaTx; + _dmaTx = nullptr; + } + if (_dmaRx) + { + delete _dmaRx; + _dmaRx = nullptr; + } + _dmaLastError = DmaStatus::Ok; +} + +void SERCOM::dmaResetDescriptors() +{ + _dmaTxDesc = nullptr; + _dmaRxDesc = nullptr; +} + +bool SERCOM::dmaTxBusy() const +{ + return _dmaTxActive; +} + +bool SERCOM::dmaRxBusy() const +{ + return _dmaRxActive; +} + +SERCOM::DmaStatus SERCOM::dmaLastError() const +{ + return _dmaLastError; +} +#endif + #ifdef SERCOM_STRICT_PADS bool SERCOM::registerPads(uint8_t sercomId, const PadFunc (&pads)[4], bool muxFunctionD) { diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index f2ee1532b..8d577bcee 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -23,6 +23,10 @@ #include "SERCOM_Txn.h" #include +#ifdef USE_ZERODMA +class Adafruit_ZeroDMA; +#endif + // SAMD51 has configurable MAX_SPI, else use peripheral clock default. // Update: changing MAX_SPI via compiler flags is DEPRECATED, because // this affects ALL SPI peripherals including some that should NOT be @@ -267,6 +271,34 @@ class SERCOM static void setPending(uint8_t sercomId); static void dispatchPending(void); +#ifdef USE_ZERODMA + // --- SERCOM ZeroDMA helpers (Phase 1) --- + using DmaCallback = void (*)(Adafruit_ZeroDMA*); + enum class DmaStatus : uint8_t { + Ok = 0, + NotConfigured, + NullPtr, + ZeroLen, + AllocateFailed, + DescriptorFailed, + StartFailed + }; + + DmaStatus dmaInit(uint8_t txTrigger, uint8_t rxTrigger); + void dmaSetCallbacks(DmaCallback txCb, DmaCallback rxCb); + DmaStatus dmaStartTx(const void* src, void* dstReg, size_t len); + DmaStatus dmaStartRx(void* dst, void* srcReg, size_t len); + DmaStatus dmaStartDuplex(const void* txSrc, void* rxDst, void* txReg, void* rxReg, size_t len, + const uint8_t* dummyTx = nullptr); + void dmaRelease(); + void dmaResetDescriptors(); + void dmaAbortTx(); + void dmaAbortRx(); + bool dmaTxBusy() const; + bool dmaRxBusy() const; + DmaStatus dmaLastError() const; +#endif + #ifdef SERCOM_STRICT_PADS enum class PadFunc : uint8_t { None = 0, @@ -313,6 +345,21 @@ class SERCOM uint8_t calculateBaudrateSynchronous(uint32_t baudrate); uint32_t division(uint32_t dividend, uint32_t divisor) ; void initClockNVIC( void ) ; + +#ifdef USE_ZERODMA + Adafruit_ZeroDMA* _dmaTx = nullptr; + Adafruit_ZeroDMA* _dmaRx = nullptr; + DmacDescriptor* _dmaTxDesc = nullptr; + DmacDescriptor* _dmaRxDesc = nullptr; + DmaCallback _dmaTxCb = nullptr; + DmaCallback _dmaRxCb = nullptr; + uint8_t _dmaTxTrigger = 0; + uint8_t _dmaRxTrigger = 0; + bool _dmaConfigured = false; + bool _dmaTxActive = false; + bool _dmaRxActive = false; + DmaStatus _dmaLastError = DmaStatus::Ok; +#endif }; #endif From b6768fab89c06f7e15ba243b0452c8c0dcd5a199 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Thu, 29 Jan 2026 11:13:42 -0500 Subject: [PATCH 08/79] refactored initMaster/SlaveWIRE() to support rapid swapping of roles in a multi-master configuration --- cores/arduino/SERCOM.cpp | 134 ++++++++++++++++++++------------------- cores/arduino/SERCOM.h | 43 +++++++++++-- 2 files changed, 106 insertions(+), 71 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 1db3095b2..5792154d8 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -402,87 +402,90 @@ void SERCOM::resetWIRE() //Wait both bits Software Reset from CTRLA and SYNCBUSY are equal to 0 while(sercom->I2CM.CTRLA.bit.SWRST || sercom->I2CM.SYNCBUSY.bit.SWRST); + + _wire = WireConfig{}; } -void SERCOM::enableWIRE() +void SERCOM::initSlaveWIRE( uint8_t ucAddress, bool enableGeneralCall, uint8_t speed ) { - // I2C Master and Slave modes share the ENABLE bit function. - - // Enable the I2C master mode - sercom->I2CM.CTRLA.bit.ENABLE = 1 ; - - while ( sercom->I2CM.SYNCBUSY.bit.ENABLE != 0 ) - { - // Waiting the enable bit from SYNCBUSY is equal to 0; + if (!_wire.inited) { + initClockNVIC(); + _wire.inited = true; } - // Setting bus idle mode - sercom->I2CM.STATUS.bit.BUSSTATE = 1 ; - - while ( sercom->I2CM.SYNCBUSY.bit.SYSOP != 0 ) - { - // Wait the SYSOP bit from SYNCBUSY coming back to 0 - } + _wire.slaveSpeed = speed; + _wire.addr = SERCOM_I2CS_ADDR_ADDR(ucAddress & 0x7Ful) | // 0x7F, select only 7 bits + SERCOM_I2CS_ADDR_ADDRMASK(0x00ul) | // 0x00, only match exact address + enableGeneralCall; // enable general call (address 0x00) + setSlaveWIRE(); } -void SERCOM::disableWIRE() +void SERCOM::initMasterWIRE( uint32_t baudrate ) { - // I2C Master and Slave modes share the ENABLE bit function. - - // Enable the I2C master mode - sercom->I2CM.CTRLA.bit.ENABLE = 0 ; - - while ( sercom->I2CM.SYNCBUSY.bit.ENABLE != 0 ) - { - // Waiting the enable bit from SYNCBUSY is equal to 0; + if (!_wire.inited) { + initClockNVIC(); + _wire.inited = true; } + + setBaudrateWIRE(baudrate); + setMasterWIRE(); } -void SERCOM::initSlaveWIRE( uint8_t ucAddress, bool enableGeneralCall ) +void SERCOM::setMasterWIRE(void) { - // Initialize the peripheral clock and interruption - initClockNVIC() ; - resetWIRE() ; + disableWIRE(); - // Set slave mode - sercom->I2CS.CTRLA.bit.MODE = I2C_SLAVE_OPERATION; + sercom->I2CM.CTRLB.reg = _wire.ctrlb |SERCOM_I2CM_CTRLB_QCEN; + sercom->I2CM.BAUD.reg = _wire.baud; + bool sclsm = (_wire.masterSpeed == 0x2); - sercom->I2CS.ADDR.reg = SERCOM_I2CS_ADDR_ADDR( ucAddress & 0x7Ful ) | // 0x7F, select only 7 bits - SERCOM_I2CS_ADDR_ADDRMASK( 0x00ul ); // 0x00, only match exact address - if (enableGeneralCall) { - sercom->I2CS.ADDR.reg |= SERCOM_I2CS_ADDR_GENCEN; // enable general call (address 0x00) - } + // Set master mode and clock settings + sercom->I2CM.CTRLA.reg = _wire.ctrla | + SERCOM_I2CM_CTRLA_MODE(I2C_MASTER_OPERATION) | + SERCOM_I2CM_CTRLA_SPEED(_wire.masterSpeed) | + (sclsm ? SERCOM_I2CM_CTRLA_SCLSM : 0 ); - // Set the interrupt register - sercom->I2CS.INTENSET.reg = SERCOM_I2CS_INTENSET_PREC | // Stop - SERCOM_I2CS_INTENSET_AMATCH | // Address Match - SERCOM_I2CS_INTENSET_DRDY ; // Data Ready + while (sercom->I2CM.SYNCBUSY.bit.ENABLE != 0) ; - while ( sercom->I2CM.SYNCBUSY.bit.SYSOP != 0 ) - { - // Wait the SYSOP bit from SYNCBUSY to come back to 0 - } + // Setting bus idle mode + sercom->I2CM.STATUS.bit.BUSSTATE = 1 ; + while (sercom->I2CM.SYNCBUSY.bit.SYSOP != 0) ; + + // Disable slave interrupts: address match, data ready, stop/restart. + sercom->I2CS.INTENCLR.reg = SERCOM_I2CS_INTENSET_AMATCH | + SERCOM_I2CS_INTENSET_DRDY | + SERCOM_I2CS_INTENSET_PREC; } -void SERCOM::initMasterWIRE( uint32_t baudrate ) +void SERCOM::setSlaveWIRE(void) { - // Initialize the peripheral clock and interruption - initClockNVIC() ; + disableWIRE(); - resetWIRE() ; + sercom->I2CS.ADDR.reg = _wire.addr; + sercom->I2CS.CTRLB.reg = _wire.ctrlb; + bool sclsm = (_wire.slaveSpeed == 0x2); - // Set master mode and enable SCL Clock Stretch mode (stretch after ACK bit) - sercom->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_MODE( I2C_MASTER_OPERATION )/* | - SERCOM_I2CM_CTRLA_SCLSM*/ ; + // Set master mode and clock settings + sercom->I2CS.CTRLA.reg = _wire.ctrla | + SERCOM_I2CS_CTRLA_MODE(I2C_SLAVE_OPERATION) | + SERCOM_I2CS_CTRLA_SPEED(_wire.slaveSpeed) | + (sclsm ? SERCOM_I2CS_CTRLA_SCLSM : 0 ); - // Enable Smart mode and Quick Command - //sercom->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN /*| SERCOM_I2CM_CTRLB_QCEN*/ ; + while (sercom->I2CS.SYNCBUSY.bit.ENABLE != 0) ; + // Setting bus idle mode + sercom->I2CS.STATUS.bit.BUSSTATE = 1 ; + while (sercom->I2CS.SYNCBUSY.bit.SYSOP != 0) ; - // Enable all interrupts - // sercom->I2CM.INTENSET.reg = SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB | SERCOM_I2CM_INTENSET_ERROR ; + // Enable slave interrupts: address match, data ready, stop/restart. + sercom->I2CS.INTENSET.reg = SERCOM_I2CS_INTENSET_PREC | // Stop + SERCOM_I2CS_INTENSET_AMATCH | // Address Match + SERCOM_I2CS_INTENSET_DRDY; // Data Ready +} - // Determine speed mode based on requested baudrate +void SERCOM::setBaudrateWIRE(uint32_t baudrate) +{ +// Determine speed mode based on requested baudrate const uint32_t topSpeeds[3] = {400000, 1000000, 3400000}; // {(sm/fm), (fm+), (hs)} uint8_t speedBit; uint8_t clockStretchMode; // See: 28.6.2.4.6 (SERCOM I2C Highspeed mode) @@ -499,18 +502,21 @@ void SERCOM::initMasterWIRE( uint32_t baudrate ) clockStretchMode = 1; } - sercom->I2CM.CTRLA.bit.SPEED = speedBit; - sercom->I2CM.CTRLA.bit.SCLSM = clockStretchMode; + _wire.masterSpeed = speedBit; - uint32_t minBaudrate = freqRef / 512; // BAUD = 255: SAMD51(@100MHz) ~195kHz, SAMD21 ~94kHz + uint32_t fREF = getSercomFreqRef(); + uint32_t minBaudrate = fREF / 512; // BAUD = 255: SAMD51(@100MHz) ~195kHz, SAMD21 ~94kHz uint32_t maxBaudrate = topSpeeds[speedBit]; baudrate = max(minBaudrate, min(baudrate, maxBaudrate)); if (speedBit == 0x2) - sercom->I2CM.BAUD.bit.HSBAUD = freqRef / (2 * baudrate) - 1; + _wire.baud = SERCOM_I2CM_BAUD_HSBAUD(fREF / (2 * baudrate) - 1); else - sercom->I2CM.BAUD.bit.BAUD = freqRef / (2 * baudrate) - 5 - - (freqRef/1000000ul * WIRE_RISE_TIME_NANOSECONDS) / 2000; + _wire.baud = SERCOM_I2CM_BAUD_BAUD(fREF / (2 * baudrate) - 5 - + (fREF/1000000ul * WIRE_RISE_TIME_NANOSECONDS) / 2000); + + if (isMasterWIRE()) + setMasterWIRE(); } void SERCOM::prepareNackBitWIRE( void ) @@ -568,7 +574,7 @@ bool SERCOM::startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag // Send start and address sercom->I2CM.INTFLAG.bit.ERROR = 1; sercom->I2CM.ADDR.reg = SERCOM_I2CM_ADDR_ADDR(address) | - ((sercom->I2CM.CTRLA.bit.SPEED == 0x2) ? SERCOM_I2CM_ADDR_HS : 0); + ((_wire.masterSpeed == 0x2) ? SERCOM_I2CM_ADDR_HS : 0); // Address Transmitted if ( flag == WIRE_WRITE_FLAG ) // Write mode @@ -651,7 +657,7 @@ bool SERCOM::sendDataSlaveWIRE(uint8_t data) bool SERCOM::isMasterWIRE( void ) { - return sercom->I2CS.CTRLA.bit.MODE == I2C_MASTER_OPERATION; + return sercom->I2CM.CTRLA.bit.MODE == I2C_MASTER_OPERATION; } bool SERCOM::isSlaveWIRE( void ) diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index 8d577bcee..f2f521338 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -215,15 +215,32 @@ class SERCOM bool isReceiveCompleteSPI( void ) ; /* ========== WIRE ========== */ - void initSlaveWIRE(uint8_t address, bool enableGeneralCall = false) ; + void initSlaveWIRE(uint8_t address, bool enableGeneralCall = false, uint8_t speed = 0x0) ; void initMasterWIRE(uint32_t baudrate) ; + void setSlaveWIRE( void ) ; + void setMasterWIRE( void ) ; void resetWIRE( void ) ; - void enableWIRE( void ) ; - void disableWIRE( void ); - void prepareNackBitWIRE( void ) ; - void prepareAckBitWIRE( void ) ; - void prepareCommandBitsWire(uint8_t cmd); + inline void enableWIRE( void ) + { + // I2C Master and Slave modes share the ENABLE bit function. + sercom->I2CM.CTRLA.bit.ENABLE = 1 ; + while (sercom->I2CM.SYNCBUSY.bit.ENABLE != 0) ; + + // Setting bus idle mode + sercom->I2CM.STATUS.bit.BUSSTATE = 1 ; + while (sercom->I2CM.SYNCBUSY.bit.SYSOP != 0) ; + } + inline void disableWIRE( void ) + { + // I2C Master and Slave modes share the ENABLE bit function. + sercom->I2CM.CTRLA.bit.ENABLE = 0 ; + while (sercom->I2CM.SYNCBUSY.bit.ENABLE != 0) ; + } + void setBaudrateWIRE(uint32_t baudrate) ; + void prepareNackBitWIRE( void ) ; + void prepareAckBitWIRE( void ) ; + void prepareCommandBitsWire(uint8_t cmd) ; bool startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag) ; bool sendDataMasterWIRE(uint8_t data) ; bool sendDataSlaveWIRE(uint8_t data) ; @@ -239,9 +256,10 @@ class SERCOM bool isRestartDetectedWIRE( void ) ; bool isAddressMatch( void ) ; bool isMasterReadOperationWIRE( void ) ; - bool isRXNackReceivedWIRE( void ) ; + bool isRXNackReceivedWIRE( void ) ; int availableWIRE( void ) ; uint8_t readDataWIRE( void ) ; + int8_t getSercomIndex(void); uint32_t getSercomFreqRef(void); @@ -346,6 +364,17 @@ class SERCOM uint32_t division(uint32_t dividend, uint32_t divisor) ; void initClockNVIC( void ) ; + // Cached I2C master/slave configuration for fast role switching + struct WireConfig { + uint32_t ctrla = 0x00000002; // default CTRLA value: auto ENABLE + uint32_t ctrlb = 0x00000500; // default CTRLB value: SMEN | AACKEN + uint32_t baud = 0x000000FF; // default to lowest supported speed + uint32_t addr = 0x00000000; // default address no GCEN, no ADDRMASK, 7-bit address only + uint8_t masterSpeed = 0x0; // default to lowest speed + uint8_t slaveSpeed = 0x0; // default to lowest speed + bool inited = false; + } _wire; + #ifdef USE_ZERODMA Adafruit_ZeroDMA* _dmaTx = nullptr; Adafruit_ZeroDMA* _dmaRx = nullptr; From d16a831e98af2ba7b4cf30533101e7a63fa4de67 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Thu, 29 Jan 2026 11:51:10 -0500 Subject: [PATCH 09/79] generalize RingBuffer to support any type not just uint8_t while preserving legacy behavior. --- cores/arduino/RingBuffer.h | 89 ++++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 27 deletions(-) diff --git a/cores/arduino/RingBuffer.h b/cores/arduino/RingBuffer.h index deecb733c..b6989a5e0 100644 --- a/cores/arduino/RingBuffer.h +++ b/cores/arduino/RingBuffer.h @@ -22,6 +22,7 @@ #define _RING_BUFFER_ #include +#include // Define constants and variables for buffering incoming serial data. We're // using a ring buffer (I think), in which head is the index of the location @@ -32,16 +33,19 @@ #define SERIAL_BUFFER_SIZE 350 #endif -template +template class RingBufferN { public: - uint8_t _aucBuffer[N] ; + T _aucBuffer[N] ; volatile int _iHead ; volatile int _iTail ; public: RingBufferN( void ) ; + bool store( const T& c ) ; + bool read( T& out ) ; + bool peek( T& out ) const ; void store_char( uint8_t c ) ; void clear(); int read_char(); @@ -57,15 +61,16 @@ class RingBufferN typedef RingBufferN RingBuffer; -template +template RingBufferN::RingBufferN( void ) { - memset( _aucBuffer, 0, N ) ; + for (int i = 0; i < N; ++i) + _aucBuffer[i] = T{}; clear(); } -template -void RingBufferN::store_char( uint8_t c ) +template +bool RingBufferN::store( const T& c ) { int i = nextIndex(_iHead); @@ -77,30 +82,58 @@ void RingBufferN::store_char( uint8_t c ) { _aucBuffer[_iHead] = c ; _iHead = i ; + return true; } + return false; } -template -void RingBufferN::clear() +template +bool RingBufferN::read( T& out ) { - _iHead = 0; - _iTail = 0; + if(_iTail == _iHead) + return false; + + out = _aucBuffer[_iTail]; + _iTail = nextIndex(_iTail); + return true; } -template -int RingBufferN::read_char() +template +bool RingBufferN::peek( T& out ) const { if(_iTail == _iHead) - return -1; + return false; - uint8_t value = _aucBuffer[_iTail]; - _iTail = nextIndex(_iTail); + out = _aucBuffer[_iTail]; + return true; +} + +template +void RingBufferN::store_char( uint8_t c ) +{ + static_assert(std::is_same::value, "store_char only valid for uint8_t buffers"); + (void)store(static_cast(c)); +} + +template +void RingBufferN::clear() +{ + _iHead = 0; + _iTail = 0; +} +template +int RingBufferN::read_char() +{ + static_assert(std::is_same::value, "read_char only valid for uint8_t buffers"); + uint8_t value; + if (!read(value)) + return -1; return value; } -template -int RingBufferN::available() +template +int RingBufferN::available() { int delta = _iHead - _iTail; @@ -110,8 +143,8 @@ int RingBufferN::available() return delta; } -template -int RingBufferN::availableForStore() +template +int RingBufferN::availableForStore() { if (_iHead >= _iTail) return N - 1 - _iHead + _iTail; @@ -119,23 +152,25 @@ int RingBufferN::availableForStore() return _iTail - _iHead - 1; } -template -int RingBufferN::peek() +template +int RingBufferN::peek() { - if(_iTail == _iHead) + static_assert(std::is_same::value, "peek() only valid for uint8_t buffers"); + uint8_t value; + if (!peek(value)) return -1; - return _aucBuffer[_iTail]; + return value; } -template -int RingBufferN::nextIndex(int index) +template +int RingBufferN::nextIndex(int index) { return (uint32_t)(index + 1) % N; } -template -bool RingBufferN::isFull() +template +bool RingBufferN::isFull() { return (nextIndex(_iHead) == _iTail); } From 57c89a01af96219af16a041c951ad08c6a151eab Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Thu, 29 Jan 2026 15:43:40 -0500 Subject: [PATCH 10/79] refactored stopTransmissionWIRE to support DMA and async operations, added stopTransmissionWIRE to allow sync closeout, handle errors and continue processing the transaction queue --- cores/arduino/SERCOM.cpp | 122 ++++++++++++++++++++++++------------- cores/arduino/SERCOM.h | 20 +++++- cores/arduino/SERCOM_Txn.h | 19 ++++++ 3 files changed, 116 insertions(+), 45 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 5792154d8..d7b309c9d 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -478,7 +478,8 @@ void SERCOM::setSlaveWIRE(void) while (sercom->I2CS.SYNCBUSY.bit.SYSOP != 0) ; // Enable slave interrupts: address match, data ready, stop/restart. - sercom->I2CS.INTENSET.reg = SERCOM_I2CS_INTENSET_PREC | // Stop + sercom->I2CS.INTENSET.reg = SERCOM_I2CS_INTENSET_ERROR | // Error + SERCOM_I2CS_INTENSET_PREC | // Stop SERCOM_I2CS_INTENSET_AMATCH | // Address Match SERCOM_I2CS_INTENSET_DRDY; // Data Ready } @@ -553,10 +554,15 @@ void SERCOM::prepareCommandBitsWire(uint8_t cmd) } } -bool SERCOM::startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag) +bool SERCOM::startTransmissionWIRE(void) { - // 7-bits address + 1-bits R/W - address = (address << 0x1ul) | flag; + SercomTxn* txn = nullptr; + if (!_txnQueue.peek(txn) || txn == nullptr) + return false; + + const bool read = (txn->config & I2C_CFG_READ) != 0; + uint16_t addr = (txn->config & I2C_CFG_10BIT) ? I2C_ADDR(txn->address) : I2C_ADDR7(txn->address); + addr = (uint16_t)((addr << 1) | (read ? 1u : 0u)); // If another master owns the bus or the last bus owner has not properly // sent a stop, return failure early. This will prevent some misbehaved @@ -565,58 +571,90 @@ bool SERCOM::startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag // possible bus states. if(!isBusOwnerWIRE()) { - if( isBusBusyWIRE() || (isArbLostWIRE() && !isBusIdleWIRE()) || isBusUnknownWIRE() ) - { + if (isArbLostWIRE() && !isBusIdleWIRE()) { + stopTransmissionWIRE(SercomWireError::ARBITRATION_LOST); + return false; + } + if (isBusUnknownWIRE()) { + stopTransmissionWIRE(SercomWireError::BUS_STATE_UNKNOWN); return false; } } - // Send start and address - sercom->I2CM.INTFLAG.bit.ERROR = 1; - sercom->I2CM.ADDR.reg = SERCOM_I2CM_ADDR_ADDR(address) | - ((_wire.masterSpeed == 0x2) ? SERCOM_I2CM_ADDR_HS : 0); + uint32_t addrReg = SERCOM_I2CM_ADDR_ADDR(addr) | + ((txn->config & I2C_CFG_10BIT) ? SERCOM_I2CM_ADDR_TENBITEN : 0) | + ((_wire.masterSpeed == 0x2) ? SERCOM_I2CM_ADDR_HS : 0); - // Address Transmitted - if ( flag == WIRE_WRITE_FLAG ) // Write mode +#ifdef USE_ZERODMA + if (txn->length > 0 && txn->length < 256 && (txn->config & I2C_CFG_STOP)) { - while( !sercom->I2CM.INTFLAG.bit.MB ) { - // Wait transmission complete - - // If certain errors occur, the MB bit may never be set (RFTM: SAMD21 sec:28.10.6; SAMD51 sec:36.10.7). - // The data transfer errors that can occur (including BUSERR) are all - // rolled up into INTFLAG.bit.ERROR from STATUS.reg - if (sercom->I2CM.INTFLAG.bit.ERROR) { - return false; - } + if (!_dmaConfigured) + dmaInit(_dmaTxTrigger, _dmaRxTrigger); + + if (!_dmaConfigured || !_dmaTx || !_dmaRx) { + stopTransmissionWIRE(SercomWireError::OTHER); + return false; } + + addrReg |= SERCOM_I2CM_ADDR_LENEN | SERCOM_I2CM_ADDR_LEN((uint8_t)txn->length); } - else // Read mode - { - while( !sercom->I2CM.INTFLAG.bit.SB ) { - // Wait transmission complete +#endif - // If the slave NACKS the address, the MB bit will be set. - // A variety of errors in the STATUS register can set the ERROR bit in the INTFLAG register - // In that case, send a stop condition and return false. - if (sercom->I2CM.INTFLAG.bit.MB || sercom->I2CM.INTFLAG.bit.ERROR) { - sercom->I2CM.CTRLB.bit.CMD = 3; // Stop condition - return false; - } - } + // Send start and address (non-blocking; ISR handles MB/SB) + sercom->I2CM.INTENSET.reg = SERCOM_I2CM_INTENSET_ERROR | SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + sercom->I2CM.ADDR.reg = addrReg; + + return true; +} - // Clean the 'Slave on Bus' flag, for further usage. - //sercom->I2CM.INTFLAG.bit.SB = 0x1ul; +SercomTxn* SERCOM::stopTransmissionWIRE( void ) +{ + return stopTransmissionWIRE( _wire.returnValue ); +} + +SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) +{ + // Policy: only auto-retry recoverable bus-state errors here. All other + // errors are surfaced to the transaction callback for protocol handling. + // Retry/backoff policy is intentionally deferred; a future change may add + // a retry budget or tick-based delay if needed. + + SercomTxn* txn = nullptr; + _txnQueue.peek(txn); + + if (error == SercomWireError::BUS_STATE_UNKNOWN) { + sercom->I2CM.STATUS.bit.BUSSTATE = 1; + + while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; + + if (txn) + startTransmissionWIRE(); + + return txn; } - //ACK received (0: ACK, 1: NACK) - if(sercom->I2CM.STATUS.bit.RXNACK) - { - return false; + if (error == SercomWireError::ARBITRATION_LOST || error == SercomWireError::BUS_ERROR) { + sercom->I2CM.STATUS.bit.ARBLOST = 1; // Clear arbitration lost flag + sercom->I2CM.INTFLAG.reg = SERCOM_I2CM_INTFLAG_ERROR; + + if (txn) + startTransmissionWIRE(); + + return txn; } - else - { - return true; + + // Callbacks are expected to run in non-ISR context (main loop/PendSV). + if (_txnQueue.read(txn) && txn != nullptr) { + if (txn->onComplete) + txn->onComplete(txn->user, static_cast(error)); } + + SercomTxn* next = nullptr; + + if (_txnQueue.peek(next) && next) + startTransmissionWIRE(); + + return txn; } bool SERCOM::sendDataMasterWIRE(uint8_t data) diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index f2f521338..d933067d2 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -21,6 +21,7 @@ #include "sam.h" #include "SERCOM_Txn.h" +#include "RingBuffer.h" #include #ifdef USE_ZERODMA @@ -41,6 +42,10 @@ class Adafruit_ZeroDMA; #define SERCOM_FREQ_REF 48000000ul #define SERCOM_NVIC_PRIORITY ((1<<__NVIC_PRIO_BITS) - 1) +#ifndef SERCOM_QUEUE_LENGTH +#define SERCOM_QUEUE_LENGTH 8 +#endif + typedef enum { UART_EXT_CLOCK = 0, @@ -241,7 +246,10 @@ class SERCOM void prepareNackBitWIRE( void ) ; void prepareAckBitWIRE( void ) ; void prepareCommandBitsWire(uint8_t cmd) ; - bool startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag) ; + bool startTransmissionWIRE( void ) ; + bool startTransmissionWIRE( uint8_t address, SercomWireReadWriteFlag flag ) = delete; + SercomTxn* stopTransmissionWIRE( void ) ; + SercomTxn* stopTransmissionWIRE( SercomWireError error ) ; bool sendDataMasterWIRE(uint8_t data) ; bool sendDataSlaveWIRE(uint8_t data) ; bool isMasterWIRE( void ) ; @@ -364,17 +372,23 @@ class SERCOM uint32_t division(uint32_t dividend, uint32_t divisor) ; void initClockNVIC( void ) ; - // Cached I2C master/slave configuration for fast role switching + // Cached I2C master/slave configuration for fast role switching. + // This can be expanded to support additional configuration options + // as needed in the future. For now, it just provides default support + // for (hs) mode and DMA. struct WireConfig { uint32_t ctrla = 0x00000002; // default CTRLA value: auto ENABLE - uint32_t ctrlb = 0x00000500; // default CTRLB value: SMEN | AACKEN + uint32_t ctrlb = 0x00000500; // default CTRLB value: SMEN (both) | AACKEN (Slave only) uint32_t baud = 0x000000FF; // default to lowest supported speed uint32_t addr = 0x00000000; // default address no GCEN, no ADDRMASK, 7-bit address only uint8_t masterSpeed = 0x0; // default to lowest speed uint8_t slaveSpeed = 0x0; // default to lowest speed bool inited = false; + SercomWireError returnValue = SercomWireError::SUCCESS; } _wire; + RingBufferN _txnQueue; + #ifdef USE_ZERODMA Adafruit_ZeroDMA* _dmaTx = nullptr; Adafruit_ZeroDMA* _dmaRx = nullptr; diff --git a/cores/arduino/SERCOM_Txn.h b/cores/arduino/SERCOM_Txn.h index 438de28a3..5ce7c5efe 100644 --- a/cores/arduino/SERCOM_Txn.h +++ b/cores/arduino/SERCOM_Txn.h @@ -14,6 +14,25 @@ struct SercomTxn { void* user; }; +// Mirrors WireDMA I2CError values for SERCOM-level reporting. +enum class SercomWireError : uint8_t +{ + SUCCESS = 0, // No error/operation successful/ACK received + DATA_TOO_LONG = 1, // Payload exceeds DMA buffer or queue slot + NACK_ON_ADDRESS = 2, // Target NACKed during address phase + NACK_ON_DATA = 3, // Target NACKed during data phase + OTHER = 4, // Generic catch-all + BUS_CONFLICT = 5, // Local transaction blocked by active bus use + QUEUE_FULL = 6, // No room in transaction queue + ARBITRATION_LOST = 7, // Lost multi-master arbitration (STATUS.ARBLOST) + BUS_ERROR = 8, // Misplaced START/STOP or illegal bus condition (STATUS.BUSERR) + BUS_STATE_UNKNOWN = 9, // BUSSTATE = 0b00 (unknown/idle state reported) + MASTER_TIMEOUT = 10, // Master timeout (STATUS.MEXTTOUT) + SLAVE_TIMEOUT = 11, // Slave timeout (STATUS.SEXTTOUT) + LENGTH_ERROR = 12, // LENERR when LEN/LENEN mismatch (STATUS.LENERR) + UNKNOWN_ERROR = 13 // Error flag set but no specific bit matched +}; + // I2C config flags and helpers enum : uint16_t { I2C_CFG_READ = 1u << 0, From 8d1b6b5d866fba5c1fef7a6960f03f2c308defdb Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Thu, 29 Jan 2026 16:10:30 -0500 Subject: [PATCH 11/79] add 10-bit address support for client operations --- cores/arduino/SERCOM.cpp | 16 ++++++++++++---- cores/arduino/SERCOM.h | 3 ++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index d7b309c9d..757ed67fc 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -406,17 +406,25 @@ void SERCOM::resetWIRE() _wire = WireConfig{}; } -void SERCOM::initSlaveWIRE( uint8_t ucAddress, bool enableGeneralCall, uint8_t speed ) +void SERCOM::initSlaveWIRE( uint8_t ucAddress, bool enableGeneralCall ) +{ + initSlaveWIRE( ucAddress & 0x7Fu, enableGeneralCall, false, 0x0, false ); +} + +void SERCOM::initSlaveWIRE( uint16_t ucAddress, bool enableGeneralCall, uint8_t speed, bool enable10Bit ) { if (!_wire.inited) { initClockNVIC(); _wire.inited = true; } + uint16_t mask = enable10Bit ? 0x03FFul : 0x007Ful; + _wire.slaveSpeed = speed; - _wire.addr = SERCOM_I2CS_ADDR_ADDR(ucAddress & 0x7Ful) | // 0x7F, select only 7 bits - SERCOM_I2CS_ADDR_ADDRMASK(0x00ul) | // 0x00, only match exact address - enableGeneralCall; // enable general call (address 0x00) + _wire.addr = SERCOM_I2CS_ADDR_ADDR(ucAddress & mask) | // select either 7 or 10-bits + SERCOM_I2CS_ADDR_ADDRMASK(0x00ul) | // 0x00, only match exact address + (enable10Bit ? SERCOM_I2CS_ADDR_TENBITEN : 0) | // 10-bit addressing + enableGeneralCall; // enable general call (address 0x00) setSlaveWIRE(); } diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index d933067d2..088beca95 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -220,7 +220,8 @@ class SERCOM bool isReceiveCompleteSPI( void ) ; /* ========== WIRE ========== */ - void initSlaveWIRE(uint8_t address, bool enableGeneralCall = false, uint8_t speed = 0x0) ; + void initSlaveWIRE(uint8_t address, bool enableGeneralCall = false) ; + void initSlaveWIRE(uint16_t address, bool enableGeneralCall = false, uint8_t speed = 0x0, bool enable10Bit = false) ; void initMasterWIRE(uint32_t baudrate) ; void setSlaveWIRE( void ) ; void setMasterWIRE( void ) ; From c9d1336809c776c07ef49c0f12e8c8c0b409ff18 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Thu, 29 Jan 2026 22:17:13 -0500 Subject: [PATCH 12/79] finish implementing wire async and dma callback structure and inlining ISR called APIs --- cores/arduino/SERCOM.cpp | 230 +++++++++------------------------- cores/arduino/SERCOM.h | 65 +++++----- cores/arduino/SERCOM_inline.h | 189 ++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+), 199 deletions(-) create mode 100644 cores/arduino/SERCOM_inline.h diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 757ed67fc..aec9d3973 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -34,6 +34,9 @@ SERCOM::SERCOM(Sercom* s) { sercom = s; + int8_t idx = getSercomIndex(); + if (idx >= 0 && idx < (int8_t)kSercomCount) + s_instances[idx] = this; #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) // A briefly-available but now deprecated feature had the SPI clock source @@ -405,21 +408,50 @@ void SERCOM::resetWIRE() _wire = WireConfig{}; } +void SERCOM::initWIRE(void) +{ + if (!_wire.inited) { + uint8_t idx = getSercomIndex(); + initClockNVIC(); + registerService(idx, static_cast(&SERCOM::stopTransmissionWIRE)); + _wire.inited = true; + } +#ifdef USE_ZERODMA + initWireDma(); +#endif +} + +#ifdef USE_ZERODMA +void SERCOM::initWireDma(void) +{ +#ifdef SERCOM0_DMAC_ID_TX + if (_dmaConfigured) + return; + + int8_t id = getSercomIndex(); + if (id < 0) + return; + + _dmaTxTrigger = SERCOM0_DMAC_ID_TX + (id * 2); + _dmaRxTrigger = SERCOM0_DMAC_ID_RX + (id * 2); + dmaSetCallbacks(SERCOM::dmaTxCallbackWIRE, SERCOM::dmaRxCallbackWIRE); + dmaInit(_dmaTxTrigger, _dmaRxTrigger); +#else + (void)0; +#endif +} +#endif -void SERCOM::initSlaveWIRE( uint8_t ucAddress, bool enableGeneralCall ) +void SERCOM::initSlaveWIRE( uint8_t ucAddress, bool enableGeneralCall, uint8_t speed ) { - initSlaveWIRE( ucAddress & 0x7Fu, enableGeneralCall, false, 0x0, false ); + initSlaveWIRE( ucAddress & 0x7Fu, enableGeneralCall, speed, false ); } void SERCOM::initSlaveWIRE( uint16_t ucAddress, bool enableGeneralCall, uint8_t speed, bool enable10Bit ) { - if (!_wire.inited) { - initClockNVIC(); - _wire.inited = true; - } + initWIRE(); uint16_t mask = enable10Bit ? 0x03FFul : 0x007Ful; - _wire.slaveSpeed = speed; _wire.addr = SERCOM_I2CS_ADDR_ADDR(ucAddress & mask) | // select either 7 or 10-bits SERCOM_I2CS_ADDR_ADDRMASK(0x00ul) | // 0x00, only match exact address @@ -430,10 +462,7 @@ void SERCOM::initSlaveWIRE( uint16_t ucAddress, bool enableGeneralCall, uint8_t void SERCOM::initMasterWIRE( uint32_t baudrate ) { - if (!_wire.inited) { - initClockNVIC(); - _wire.inited = true; - } + initWIRE(); setBaudrateWIRE(baudrate); setMasterWIRE(); @@ -568,6 +597,10 @@ bool SERCOM::startTransmissionWIRE(void) if (!_txnQueue.peek(txn) || txn == nullptr) return false; + _wire.currentTxn = txn; + _wire.txnIndex = 0; + _wire.txnLength = txn->length; + const bool read = (txn->config & I2C_CFG_READ) != 0; uint16_t addr = (txn->config & I2C_CFG_10BIT) ? I2C_ADDR(txn->address) : I2C_ADDR7(txn->address); addr = (uint16_t)((addr << 1) | (read ? 1u : 0u)); @@ -594,7 +627,9 @@ bool SERCOM::startTransmissionWIRE(void) ((_wire.masterSpeed == 0x2) ? SERCOM_I2CM_ADDR_HS : 0); #ifdef USE_ZERODMA - if (txn->length > 0 && txn->length < 256 && (txn->config & I2C_CFG_STOP)) + _wire.useDma = txn->length > 0 && txn->length < 256 && (txn->config & I2C_CFG_STOP); + + if (_wire.useDma) { if (!_dmaConfigured) dmaInit(_dmaTxTrigger, _dmaRxTrigger); @@ -665,52 +700,6 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) return txn; } -bool SERCOM::sendDataMasterWIRE(uint8_t data) -{ - //Send data - sercom->I2CM.INTFLAG.bit.ERROR = 1; - sercom->I2CM.DATA.bit.DATA = data; - - //Wait transmission successful - while(!sercom->I2CM.INTFLAG.bit.MB) { - // If a data transfer error occurs, the MB bit may never be set. - // Check the error bit and bail if it's set. - // The data transfer errors that can occur (including BUSERR) are all - // rolled up into INTFLAG.bit.ERROR from STATUS.reg - if (sercom->I2CM.INTFLAG.bit.ERROR) { - return false; - } - } - - //Problems on line? nack received? - if(sercom->I2CM.STATUS.bit.RXNACK) - return false; - else - return true; -} - -bool SERCOM::sendDataSlaveWIRE(uint8_t data) -{ - //Send data - sercom->I2CS.DATA.bit.DATA = data; - - //Problems on line? nack received? - if(!sercom->I2CS.INTFLAG.bit.DRDY || sercom->I2CS.STATUS.bit.RXNACK) - return false; - else - return true; -} - -bool SERCOM::isMasterWIRE( void ) -{ - return sercom->I2CM.CTRLA.bit.MODE == I2C_MASTER_OPERATION; -} - -bool SERCOM::isSlaveWIRE( void ) -{ - return sercom->I2CS.CTRLA.bit.MODE == I2C_SLAVE_OPERATION; -} - bool SERCOM::isBusIdleWIRE( void ) { return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_IDLE_STATE; @@ -774,30 +763,6 @@ int SERCOM::availableWIRE( void ) return sercom->I2CS.INTFLAG.bit.DRDY; } -uint8_t SERCOM::readDataWIRE( void ) -{ - if(isMasterWIRE()) - { - while (sercom->I2CM.INTFLAG.bit.SB == 0) { - // Waiting complete receive - // A variety of errors in the STATUS register can set the ERROR bit in the INTFLAG register - // In that case, send a stop condition and return false. - // readDataWIRE should really be able to indicate an error (which would never be used - // because the readDataWIRE callers (in Wire.cpp) should have checked availableWIRE() first and timed it - // out if the data never showed up - if (sercom->I2CM.INTFLAG.bit.MB || sercom->I2CM.INTFLAG.bit.ERROR) { - sercom->I2CM.CTRLB.bit.CMD = 3; // Stop condition - return 0xFF; - } - } - - return sercom->I2CM.DATA.bit.DATA ; - } - else - { - return sercom->I2CS.DATA.reg ; - } -} #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) @@ -851,6 +816,7 @@ static const struct { #endif // end !SAMD51 std::array SERCOM::s_states = {}; +std::array SERCOM::s_instances = {}; volatile uint32_t SERCOM::s_pendingMask = 0; bool SERCOM::claim(uint8_t sercomId, Role role) @@ -932,6 +898,17 @@ SERCOM::DmaStatus SERCOM::dmaInit(uint8_t txTrigger, uint8_t rxTrigger) if (_dmaRxCb) _dmaRx->setCallback(_dmaRxCb); + if (_dmaTxDesc == nullptr) + _dmaTxDesc = _dmaTx->addDescriptor(&_dmaDummy, (void*)&sercom->I2CM.DATA.reg, 1, DMA_BEAT_SIZE_BYTE, true, false); + if (_dmaRxDesc == nullptr) + _dmaRxDesc = _dmaRx->addDescriptor((void*)&sercom->I2CM.DATA.reg, &_dmaDummy, 1, DMA_BEAT_SIZE_BYTE, false, true); + if (_dmaTxDesc == nullptr || _dmaRxDesc == nullptr) + { + _dmaLastError = DmaStatus::DescriptorFailed; + dmaRelease(); + return _dmaLastError; + } + _dmaConfigured = true; _dmaLastError = DmaStatus::Ok; return _dmaLastError; @@ -951,90 +928,6 @@ void SERCOM::dmaSetCallbacks(DmaCallback txCb, DmaCallback rxCb) } } -SERCOM::DmaStatus SERCOM::dmaStartTx(const void* src, void* dstReg, size_t len) -{ - if (!_dmaConfigured || !_dmaTx) - { - _dmaLastError = DmaStatus::NotConfigured; - return _dmaLastError; - } - if (src == nullptr || dstReg == nullptr) - { - _dmaLastError = DmaStatus::NullPtr; - return _dmaLastError; - } - if (len == 0) - { - _dmaLastError = DmaStatus::ZeroLen; - return _dmaLastError; - } - - if (_dmaTxDesc == nullptr) - _dmaTxDesc = _dmaTx->addDescriptor((void*)src, dstReg, len, DMA_BEAT_SIZE_BYTE, true, false); - else - _dmaTx->changeDescriptor(_dmaTxDesc, (void*)src, dstReg, len); - - if (_dmaTxDesc == nullptr) - { - _dmaLastError = DmaStatus::DescriptorFailed; - return _dmaLastError; - } - - ZeroDMAstatus status = _dmaTx->startJob(); - if (status != DMA_STATUS_OK) - { - _dmaTx->abort(); - _dmaLastError = DmaStatus::StartFailed; - return _dmaLastError; - } - - _dmaTxActive = true; - _dmaLastError = DmaStatus::Ok; - return _dmaLastError; -} - -SERCOM::DmaStatus SERCOM::dmaStartRx(void* dst, void* srcReg, size_t len) -{ - if (!_dmaConfigured || !_dmaRx) - { - _dmaLastError = DmaStatus::NotConfigured; - return _dmaLastError; - } - if (dst == nullptr || srcReg == nullptr) - { - _dmaLastError = DmaStatus::NullPtr; - return _dmaLastError; - } - if (len == 0) - { - _dmaLastError = DmaStatus::ZeroLen; - return _dmaLastError; - } - - if (_dmaRxDesc == nullptr) - _dmaRxDesc = _dmaRx->addDescriptor(srcReg, dst, len, DMA_BEAT_SIZE_BYTE, false, true); - else - _dmaRx->changeDescriptor(_dmaRxDesc, srcReg, dst, len); - - if (_dmaRxDesc == nullptr) - { - _dmaLastError = DmaStatus::DescriptorFailed; - return _dmaLastError; - } - - ZeroDMAstatus status = _dmaRx->startJob(); - if (status != DMA_STATUS_OK) - { - _dmaRx->abort(); - _dmaLastError = DmaStatus::StartFailed; - return _dmaLastError; - } - - _dmaRxActive = true; - _dmaLastError = DmaStatus::Ok; - return _dmaLastError; -} - SERCOM::DmaStatus SERCOM::dmaStartDuplex(const void* txSrc, void* rxDst, void* txReg, void* rxReg, size_t len, const uint8_t* dummyTx) { @@ -1212,8 +1105,9 @@ void SERCOM::dispatchPending(void) continue; ServiceFn fn = s_states[i].service; - if (fn) - fn(); + SERCOM* inst = s_instances[i]; + if (fn && inst) + (inst->*fn)(); } } diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index 088beca95..88358ce8a 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -220,46 +220,31 @@ class SERCOM bool isReceiveCompleteSPI( void ) ; /* ========== WIRE ========== */ - void initSlaveWIRE(uint8_t address, bool enableGeneralCall = false) ; + void initSlaveWIRE(uint8_t address, bool enableGeneralCall = false, uint8_t speed = 0x0) ; void initSlaveWIRE(uint16_t address, bool enableGeneralCall = false, uint8_t speed = 0x0, bool enable10Bit = false) ; void initMasterWIRE(uint32_t baudrate) ; void setSlaveWIRE( void ) ; void setMasterWIRE( void ) ; void resetWIRE( void ) ; - inline void enableWIRE( void ) - { - // I2C Master and Slave modes share the ENABLE bit function. - sercom->I2CM.CTRLA.bit.ENABLE = 1 ; - while (sercom->I2CM.SYNCBUSY.bit.ENABLE != 0) ; - - // Setting bus idle mode - sercom->I2CM.STATUS.bit.BUSSTATE = 1 ; - while (sercom->I2CM.SYNCBUSY.bit.SYSOP != 0) ; - } - inline void disableWIRE( void ) - { - // I2C Master and Slave modes share the ENABLE bit function. - sercom->I2CM.CTRLA.bit.ENABLE = 0 ; - while (sercom->I2CM.SYNCBUSY.bit.ENABLE != 0) ; - } + inline void enableWIRE( void ) ; + inline void disableWIRE( void ) ; void setBaudrateWIRE(uint32_t baudrate) ; void prepareNackBitWIRE( void ) ; void prepareAckBitWIRE( void ) ; void prepareCommandBitsWire(uint8_t cmd) ; bool startTransmissionWIRE( void ) ; - bool startTransmissionWIRE( uint8_t address, SercomWireReadWriteFlag flag ) = delete; + bool startTransmissionWIRE( uint8_t address, SercomWireReadWriteFlag flag ) = delete ; SercomTxn* stopTransmissionWIRE( void ) ; SercomTxn* stopTransmissionWIRE( SercomWireError error ) ; - bool sendDataMasterWIRE(uint8_t data) ; - bool sendDataSlaveWIRE(uint8_t data) ; - bool isMasterWIRE( void ) ; - bool isSlaveWIRE( void ) ; + inline bool sendDataWIRE( void ) ; + inline bool isMasterWIRE( void ) ; + inline bool isSlaveWIRE( void ) ; bool isBusIdleWIRE( void ) ; bool isBusOwnerWIRE( void ) ; bool isBusUnknownWIRE( void ) ; - bool isArbLostWIRE( void ); - bool isBusBusyWIRE( void ); + bool isArbLostWIRE( void ) ; + bool isBusBusyWIRE( void ) ; bool isDataReadyWIRE( void ) ; bool isStopDetectedWIRE( void ) ; bool isRestartDetectedWIRE( void ) ; @@ -267,10 +252,10 @@ class SERCOM bool isMasterReadOperationWIRE( void ) ; bool isRXNackReceivedWIRE( void ) ; int availableWIRE( void ) ; - uint8_t readDataWIRE( void ) ; + inline bool readDataWIRE( void ); - int8_t getSercomIndex(void); - uint32_t getSercomFreqRef(void); + int8_t getSercomIndex(void) ; + uint32_t getSercomFreqRef(void) ; #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) // SERCOM clock source override is only available on @@ -290,7 +275,7 @@ class SERCOM // --- Async SERCOM scaffolding (Phase 1) --- enum class Role : uint8_t { None = 0, UART, SPI, I2C }; - using ServiceFn = void (*)(); + using ServiceFn = void (SERCOM::*)(); static bool claim(uint8_t sercomId, Role role); static void release(uint8_t sercomId); @@ -313,8 +298,8 @@ class SERCOM DmaStatus dmaInit(uint8_t txTrigger, uint8_t rxTrigger); void dmaSetCallbacks(DmaCallback txCb, DmaCallback rxCb); - DmaStatus dmaStartTx(const void* src, void* dstReg, size_t len); - DmaStatus dmaStartRx(void* dst, void* srcReg, size_t len); + inline DmaStatus dmaStartTx(const void* src, void* dstReg, size_t len); + inline DmaStatus dmaStartRx(void* dst, void* srcReg, size_t len); DmaStatus dmaStartDuplex(const void* txSrc, void* rxDst, void* txReg, void* rxReg, size_t len, const uint8_t* dummyTx = nullptr); void dmaRelease(); @@ -324,6 +309,12 @@ class SERCOM bool dmaTxBusy() const; bool dmaRxBusy() const; DmaStatus dmaLastError() const; + // DMA callbacks are protocol-owned (Wire/SPI/UART) and registered via dmaSetCallbacks(). + static inline SERCOM* findDmaOwner(Adafruit_ZeroDMA* dma, bool tx); + + // --- WIRE DMA callbacks (ISR-safe, PendSV-only completion) --- + static inline void dmaTxCallbackWIRE(Adafruit_ZeroDMA* dma); + static inline void dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma); #endif #ifdef SERCOM_STRICT_PADS @@ -368,10 +359,15 @@ class SERCOM }; static std::array s_states; + static std::array s_instances; static volatile uint32_t s_pendingMask; uint8_t calculateBaudrateSynchronous(uint32_t baudrate); uint32_t division(uint32_t dividend, uint32_t divisor) ; void initClockNVIC( void ) ; +#ifdef USE_ZERODMA + void initWireDma(void); +#endif + void initWIRE(void); // Cached I2C master/slave configuration for fast role switching. // This can be expanded to support additional configuration options @@ -384,8 +380,12 @@ class SERCOM uint32_t addr = 0x00000000; // default address no GCEN, no ADDRMASK, 7-bit address only uint8_t masterSpeed = 0x0; // default to lowest speed uint8_t slaveSpeed = 0x0; // default to lowest speed - bool inited = false; + bool inited = false; // whether initMaster/SlaveWIRE has been called + bool useDma = false; // per transaction DMA use flag for Host/Client modes SercomWireError returnValue = SercomWireError::SUCCESS; + SercomTxn* currentTxn = nullptr; + size_t txnIndex = 0; + size_t txnLength = 0; } _wire; RingBufferN _txnQueue; @@ -397,6 +397,7 @@ class SERCOM DmacDescriptor* _dmaRxDesc = nullptr; DmaCallback _dmaTxCb = nullptr; DmaCallback _dmaRxCb = nullptr; + uint8_t _dmaDummy = 0; uint8_t _dmaTxTrigger = 0; uint8_t _dmaRxTrigger = 0; bool _dmaConfigured = false; @@ -406,4 +407,6 @@ class SERCOM #endif }; +#include "SERCOM_inline.h" + #endif diff --git a/cores/arduino/SERCOM_inline.h b/cores/arduino/SERCOM_inline.h new file mode 100644 index 000000000..244869163 --- /dev/null +++ b/cores/arduino/SERCOM_inline.h @@ -0,0 +1,189 @@ +#ifndef SERCOM_INLINE_H +#define SERCOM_INLINE_H + +inline void SERCOM::enableWIRE(void) +{ + // I2C Master and Slave modes share the ENABLE bit function. + sercom->I2CM.CTRLA.bit.ENABLE = 1; + while (sercom->I2CM.SYNCBUSY.bit.ENABLE != 0) ; + + // Setting bus idle mode + sercom->I2CM.STATUS.bit.BUSSTATE = 1; + while (sercom->I2CM.SYNCBUSY.bit.SYSOP != 0) ; +} + +inline void SERCOM::disableWIRE(void) +{ + // I2C Master and Slave modes share the ENABLE bit function. + sercom->I2CM.CTRLA.bit.ENABLE = 0; + while (sercom->I2CM.SYNCBUSY.bit.ENABLE != 0) ; +} + +inline bool SERCOM::sendDataWIRE( void ) +{ + SercomTxn* txn = _wire.currentTxn; + if (txn && txn->txPtr == nullptr) return false; + +#ifdef USE_ZERODMA + if (_wire.useDma) { + if (!_dmaTxActive) + dmaStartTx(txn->txPtr, &sercom->I2CM.DATA.reg, _wire.txnLength); + return true; + } +#endif + if (_wire.txnIndex < _wire.txnLength) { + sercom->I2CM.DATA.bit.DATA = txn->txPtr[_wire.txnIndex++]; + return true; + } + + return false; +} + +inline bool SERCOM::isMasterWIRE( void ) +{ + return sercom->I2CM.CTRLA.bit.MODE == I2C_MASTER_OPERATION; +} + +inline bool SERCOM::isSlaveWIRE( void ) +{ + return sercom->I2CS.CTRLA.bit.MODE == I2C_SLAVE_OPERATION; +} + +inline bool SERCOM::readDataWIRE( void ) +{ + SercomTxn* txn = _wire.currentTxn; + if (txn && txn->rxPtr == nullptr) return false; + + if (isMasterWIRE() && (_wire.txnIndex + 1 >= _wire.txnLength)) + sercom->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + else + sercom->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + +#ifdef USE_ZERODMA + if (_wire.useDma) { + if (!_dmaRxActive) + dmaStartRx(txn->rxPtr, &sercom->I2CM.DATA.reg, _wire.txnLength); + return true; + } +#endif + + if ( _wire.txnIndex >= _wire.txnLength) + return false; + + txn->rxPtr[_wire.txnIndex++] = sercom->I2CM.DATA.bit.DATA; + return true; +} + +#ifdef USE_ZERODMA +inline SERCOM* SERCOM::findDmaOwner(Adafruit_ZeroDMA* dma, bool tx) +{ + if (dma == nullptr) + return nullptr; + for (size_t i = 0; i < kSercomCount; ++i) + { + SERCOM* inst = s_instances[i]; + if (inst == nullptr) + continue; + if (tx) + { + if (inst->_dmaTx == dma) + return inst; + } + else + { + if (inst->_dmaRx == dma) + return inst; + } + } + return nullptr; +} + +inline SERCOM::DmaStatus SERCOM::dmaStartTx(const void* src, void* dstReg, size_t len) +{ + if (!_dmaConfigured || !_dmaTx) { + _dmaLastError = DmaStatus::NotConfigured; + return _dmaLastError; + } + if (src == nullptr || dstReg == nullptr) { + _dmaLastError = DmaStatus::NullPtr; + return _dmaLastError; + } + if (len == 0) { + _dmaLastError = DmaStatus::ZeroLen; + return _dmaLastError; + } + if (_dmaTxDesc == nullptr) { + _dmaLastError = DmaStatus::DescriptorFailed; + return _dmaLastError; + } + + _dmaTx->changeDescriptor(_dmaTxDesc, (void*)src, dstReg, len); + + if (_dmaTx->startJob() != DMA_STATUS_OK) { + _dmaTx->abort(); + _dmaLastError = DmaStatus::StartFailed; + return _dmaLastError; + } + + _dmaTxActive = true; + _dmaLastError = DmaStatus::Ok; + return _dmaLastError; +} + +inline SERCOM::DmaStatus SERCOM::dmaStartRx(void* dst, void* srcReg, size_t len) +{ + if (!_dmaConfigured || !_dmaRx) { + _dmaLastError = DmaStatus::NotConfigured; + return _dmaLastError; + } + if (dst == nullptr || srcReg == nullptr) { + _dmaLastError = DmaStatus::NullPtr; + return _dmaLastError; + } + if (len == 0) { + _dmaLastError = DmaStatus::ZeroLen; + return _dmaLastError; + } + if (_dmaRxDesc == nullptr) { + _dmaLastError = DmaStatus::DescriptorFailed; + return _dmaLastError; + } + + _dmaRx->changeDescriptor(_dmaRxDesc, srcReg, dst, len); + + if (_dmaRx->startJob() != DMA_STATUS_OK) { + _dmaRx->abort(); + _dmaLastError = DmaStatus::StartFailed; + return _dmaLastError; + } + + _dmaRxActive = true; + _dmaLastError = DmaStatus::Ok; + return _dmaLastError; +} + +inline void SERCOM::dmaTxCallbackWIRE(Adafruit_ZeroDMA* dma) +{ + SERCOM* inst = findDmaOwner(dma, true); + if (!inst) return; + + inst->sercom->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(WIRE_MASTER_ACT_STOP); + inst->_wire.returnValue = SercomWireError::SUCCESS; + inst->_dmaTxActive = false; + SERCOM::setPending((uint8_t)inst->getSercomIndex()); +} + +inline void SERCOM::dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma) +{ + SERCOM* inst = findDmaOwner(dma, false); + if (!inst) return; + + inst->sercom->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(WIRE_MASTER_ACT_STOP); + inst->_wire.txnIndex = inst->_wire.txnLength; + inst->_wire.returnValue = SercomWireError::SUCCESS; + inst->_dmaRxActive = false; + SERCOM::setPending((uint8_t)inst->getSercomIndex()); +} +#endif + +#endif // SERCOM_INLINE_H From a789b2e73d397393cd5d0f7a934288ea07764f93 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sat, 31 Jan 2026 17:41:07 -0500 Subject: [PATCH 13/79] add async and automatic dma support for client mode operations. --- cores/arduino/SERCOM.cpp | 149 +++++--------- cores/arduino/SERCOM.h | 47 +++-- cores/arduino/SERCOM_inline.h | 87 ++++++-- libraries/Wire/Wire.cpp | 365 ++++++++++++++++++++++++++-------- libraries/Wire/Wire.h | 33 ++- 5 files changed, 465 insertions(+), 216 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index aec9d3973..0b6486c77 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -468,6 +468,19 @@ void SERCOM::initMasterWIRE( uint32_t baudrate ) setMasterWIRE(); } +void SERCOM::registerWireReceive(void (*cb)(void* user, int length), void* user) +{ + _wireDeferredCb = cb; + _wireDeferredUser = user; +} + +void SERCOM::deferWireReceive(int length) +{ + _wireDeferredLength = length; + _wireDeferredPending = true; + setPending((uint8_t)getSercomIndex()); +} + void SERCOM::setMasterWIRE(void) { disableWIRE(); @@ -557,45 +570,12 @@ void SERCOM::setBaudrateWIRE(uint32_t baudrate) setMasterWIRE(); } -void SERCOM::prepareNackBitWIRE( void ) -{ - if(isMasterWIRE()) { - // Send a NACK - sercom->I2CM.CTRLB.bit.ACKACT = 1; - } else { - sercom->I2CS.CTRLB.bit.ACKACT = 1; - } -} - -void SERCOM::prepareAckBitWIRE( void ) -{ - if(isMasterWIRE()) { - // Send an ACK - sercom->I2CM.CTRLB.bit.ACKACT = 0; - } else { - sercom->I2CS.CTRLB.bit.ACKACT = 0; - } -} - -void SERCOM::prepareCommandBitsWire(uint8_t cmd) -{ - if(isMasterWIRE()) { - sercom->I2CM.CTRLB.bit.CMD = cmd; - - while(sercom->I2CM.SYNCBUSY.bit.SYSOP) - { - // Waiting for synchronization - } - } else { - sercom->I2CS.CTRLB.bit.CMD = cmd; - } -} -bool SERCOM::startTransmissionWIRE(void) +SercomTxn* SERCOM::startTransmissionWIRE(void) { SercomTxn* txn = nullptr; if (!_txnQueue.peek(txn) || txn == nullptr) - return false; + return nullptr; _wire.currentTxn = txn; _wire.txnIndex = 0; @@ -614,11 +594,11 @@ bool SERCOM::startTransmissionWIRE(void) { if (isArbLostWIRE() && !isBusIdleWIRE()) { stopTransmissionWIRE(SercomWireError::ARBITRATION_LOST); - return false; + return nullptr; } if (isBusUnknownWIRE()) { stopTransmissionWIRE(SercomWireError::BUS_STATE_UNKNOWN); - return false; + return nullptr; } } @@ -627,16 +607,16 @@ bool SERCOM::startTransmissionWIRE(void) ((_wire.masterSpeed == 0x2) ? SERCOM_I2CM_ADDR_HS : 0); #ifdef USE_ZERODMA - _wire.useDma = txn->length > 0 && txn->length < 256 && (txn->config & I2C_CFG_STOP); + setWireDma(txn->length > 0 && txn->length < 256 && (txn->config & I2C_CFG_STOP)); - if (_wire.useDma) + if (isWireDma()) { if (!_dmaConfigured) dmaInit(_dmaTxTrigger, _dmaRxTrigger); if (!_dmaConfigured || !_dmaTx || !_dmaRx) { stopTransmissionWIRE(SercomWireError::OTHER); - return false; + return nullptr; } addrReg |= SERCOM_I2CM_ADDR_LENEN | SERCOM_I2CM_ADDR_LEN((uint8_t)txn->length); @@ -644,12 +624,30 @@ bool SERCOM::startTransmissionWIRE(void) #endif // Send start and address (non-blocking; ISR handles MB/SB) + _wire.active = true; sercom->I2CM.INTENSET.reg = SERCOM_I2CM_INTENSET_ERROR | SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; sercom->I2CM.ADDR.reg = addrReg; + return txn; +} + +bool SERCOM::enqueueWIRE(SercomTxn* txn) +{ + if (txn == nullptr) + return false; + if (!_txnQueue.store(txn)) + return false; + if (!_wire.active) + return startTransmissionWIRE() != nullptr; return true; } +void SERCOM::deferStopWIRE(SercomWireError error) +{ + _wire.returnValue = error; + setPending((uint8_t)getSercomIndex()); +} + SercomTxn* SERCOM::stopTransmissionWIRE( void ) { return stopTransmissionWIRE( _wire.returnValue ); @@ -688,6 +686,8 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) // Callbacks are expected to run in non-ISR context (main loop/PendSV). if (_txnQueue.read(txn) && txn != nullptr) { + _wire.active = false; + _wire.currentTxn = nullptr; if (txn->onComplete) txn->onComplete(txn->user, static_cast(error)); } @@ -697,73 +697,14 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) if (_txnQueue.peek(next) && next) startTransmissionWIRE(); - return txn; -} - -bool SERCOM::isBusIdleWIRE( void ) -{ - return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_IDLE_STATE; -} - -bool SERCOM::isBusOwnerWIRE( void ) -{ - return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_OWNER_STATE; -} - -bool SERCOM::isBusUnknownWIRE( void ) -{ - return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_UNKNOWN_STATE; -} - -bool SERCOM::isArbLostWIRE( void ) -{ - return sercom->I2CM.STATUS.bit.ARBLOST == 1; -} - -bool SERCOM::isBusBusyWIRE( void ) -{ - return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_BUSY_STATE; -} - -bool SERCOM::isDataReadyWIRE( void ) -{ - return sercom->I2CS.INTFLAG.bit.DRDY; -} - -bool SERCOM::isStopDetectedWIRE( void ) -{ - return sercom->I2CS.INTFLAG.bit.PREC; -} - -bool SERCOM::isRestartDetectedWIRE( void ) -{ - return sercom->I2CS.STATUS.bit.SR; -} - -bool SERCOM::isAddressMatch( void ) -{ - return sercom->I2CS.INTFLAG.bit.AMATCH; -} - -bool SERCOM::isMasterReadOperationWIRE( void ) -{ - return sercom->I2CS.STATUS.bit.DIR; -} - -bool SERCOM::isRXNackReceivedWIRE( void ) -{ - return sercom->I2CM.STATUS.bit.RXNACK; -} + if (_wireDeferredPending && _wireDeferredCb) { + _wireDeferredPending = false; + _wireDeferredCb(_wireDeferredUser, _wireDeferredLength); + } -int SERCOM::availableWIRE( void ) -{ - if(isMasterWIRE()) - return sercom->I2CM.INTFLAG.bit.SB; - else - return sercom->I2CS.INTFLAG.bit.DRDY; + return txn; } - #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) static const struct { diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index 88358ce8a..2e7e137c9 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -223,6 +223,11 @@ class SERCOM void initSlaveWIRE(uint8_t address, bool enableGeneralCall = false, uint8_t speed = 0x0) ; void initSlaveWIRE(uint16_t address, bool enableGeneralCall = false, uint8_t speed = 0x0, bool enable10Bit = false) ; void initMasterWIRE(uint32_t baudrate) ; + inline void setWireTxn(SercomTxn* txn, size_t length, bool useDma); + inline void setWireDma(bool useDma); + inline bool isWireDma(void) const; + void registerWireReceive(void (*cb)(void* user, int length), void* user); + void deferWireReceive(int length); void setSlaveWIRE( void ) ; void setMasterWIRE( void ) ; @@ -230,29 +235,36 @@ class SERCOM inline void enableWIRE( void ) ; inline void disableWIRE( void ) ; void setBaudrateWIRE(uint32_t baudrate) ; - void prepareNackBitWIRE( void ) ; - void prepareAckBitWIRE( void ) ; - void prepareCommandBitsWire(uint8_t cmd) ; + inline void prepareNackBitWIRE( void ) ; + inline void prepareAckBitWIRE( void ) ; + inline void prepareCommandBitsWire(uint8_t cmd) ; bool startTransmissionWIRE( void ) ; bool startTransmissionWIRE( uint8_t address, SercomWireReadWriteFlag flag ) = delete ; SercomTxn* stopTransmissionWIRE( void ) ; SercomTxn* stopTransmissionWIRE( SercomWireError error ) ; + bool enqueueWIRE(SercomTxn* txn); + void deferStopWIRE(SercomWireError error); + inline uint8_t getINTFLAG( void ) const; + inline uint16_t getSTATUS( void ) const; + inline void clearINTFLAG( void ); inline bool sendDataWIRE( void ) ; inline bool isMasterWIRE( void ) ; inline bool isSlaveWIRE( void ) ; - bool isBusIdleWIRE( void ) ; - bool isBusOwnerWIRE( void ) ; - bool isBusUnknownWIRE( void ) ; - bool isArbLostWIRE( void ) ; - bool isBusBusyWIRE( void ) ; - bool isDataReadyWIRE( void ) ; - bool isStopDetectedWIRE( void ) ; - bool isRestartDetectedWIRE( void ) ; - bool isAddressMatch( void ) ; - bool isMasterReadOperationWIRE( void ) ; - bool isRXNackReceivedWIRE( void ) ; - int availableWIRE( void ) ; + inline bool isBusIdleWIRE( void ) ; + inline bool isBusOwnerWIRE( void ) ; + inline bool isBusUnknownWIRE( void ) ; + inline bool isArbLostWIRE( void ) ; + inline bool isBusBusyWIRE( void ) ; + inline bool isDataReadyWIRE( void ) ; + inline bool isStopDetectedWIRE( void ) ; + inline bool isRestartDetectedWIRE( void ) ; + inline bool isAddressMatch( void ) ; + inline bool isMasterReadOperationWIRE( void ) ; + inline bool isRXNackReceivedWIRE( void ) ; + inline int availableWIRE( void ) ; inline bool readDataWIRE( void ); + inline void writeDataWIRE(uint8_t data); + inline uint8_t readDataWIREByte(void); int8_t getSercomIndex(void) ; uint32_t getSercomFreqRef(void) ; @@ -382,6 +394,7 @@ class SERCOM uint8_t slaveSpeed = 0x0; // default to lowest speed bool inited = false; // whether initMaster/SlaveWIRE has been called bool useDma = false; // per transaction DMA use flag for Host/Client modes + bool active = false; // active transaction in progress SercomWireError returnValue = SercomWireError::SUCCESS; SercomTxn* currentTxn = nullptr; size_t txnIndex = 0; @@ -389,6 +402,10 @@ class SERCOM } _wire; RingBufferN _txnQueue; + void (*_wireDeferredCb)(void* user, int length) = nullptr; + void* _wireDeferredUser = nullptr; + int _wireDeferredLength = 0; + bool _wireDeferredPending = false; #ifdef USE_ZERODMA Adafruit_ZeroDMA* _dmaTx = nullptr; diff --git a/cores/arduino/SERCOM_inline.h b/cores/arduino/SERCOM_inline.h index 244869163..36c32eba0 100644 --- a/cores/arduino/SERCOM_inline.h +++ b/cores/arduino/SERCOM_inline.h @@ -1,7 +1,9 @@ #ifndef SERCOM_INLINE_H #define SERCOM_INLINE_H -inline void SERCOM::enableWIRE(void) +#ifdef __cplusplus + +inline void SERCOM::enableWIRE( void ) { // I2C Master and Slave modes share the ENABLE bit function. sercom->I2CM.CTRLA.bit.ENABLE = 1; @@ -12,7 +14,7 @@ inline void SERCOM::enableWIRE(void) while (sercom->I2CM.SYNCBUSY.bit.SYSOP != 0) ; } -inline void SERCOM::disableWIRE(void) +inline void SERCOM::disableWIRE( void ) { // I2C Master and Slave modes share the ENABLE bit function. sercom->I2CM.CTRLA.bit.ENABLE = 0; @@ -25,7 +27,7 @@ inline bool SERCOM::sendDataWIRE( void ) if (txn && txn->txPtr == nullptr) return false; #ifdef USE_ZERODMA - if (_wire.useDma) { + if (isWireDma()) { if (!_dmaTxActive) dmaStartTx(txn->txPtr, &sercom->I2CM.DATA.reg, _wire.txnLength); return true; @@ -39,15 +41,34 @@ inline bool SERCOM::sendDataWIRE( void ) return false; } -inline bool SERCOM::isMasterWIRE( void ) -{ - return sercom->I2CM.CTRLA.bit.MODE == I2C_MASTER_OPERATION; -} - -inline bool SERCOM::isSlaveWIRE( void ) +inline bool SERCOM::isMasterWIRE( void ) { return sercom->I2CM.CTRLA.bit.MODE == I2C_MASTER_OPERATION; } +inline bool SERCOM::isSlaveWIRE( void ) { return sercom->I2CS.CTRLA.bit.MODE == I2C_SLAVE_OPERATION; } + +inline bool SERCOM::isBusIdleWIRE( void ) { return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_IDLE_STATE; } +inline bool SERCOM::isBusOwnerWIRE( void ) { return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_OWNER_STATE; } +inline bool SERCOM::isBusUnknownWIRE( void ) { return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_UNKNOWN_STATE; } +inline bool SERCOM::isArbLostWIRE( void ) { return sercom->I2CM.STATUS.bit.ARBLOST == 1; } +inline bool SERCOM::isBusBusyWIRE( void ) { return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_BUSY_STATE; } +inline bool SERCOM::isDataReadyWIRE( void ) { return sercom->I2CS.INTFLAG.bit.DRDY; } +inline bool SERCOM::isStopDetectedWIRE( void ) { return sercom->I2CS.INTFLAG.bit.PREC; } +inline bool SERCOM::isRestartDetectedWIRE( void ) { return sercom->I2CS.INTFLAG.bit.SR; } +inline bool SERCOM::isAddressMatch( void ) { return sercom->I2CS.INTFLAG.bit.AMATCH; } + +inline void SERCOM::prepareNackBitWIRE( void ) { sercom->I2CM.CTRLB.bit.ACKACT = 1; } +inline void SERCOM::prepareAckBitWIRE( void ) { sercom->I2CM.CTRLB.bit.ACKACT = 0; } +inline void SERCOM::prepareCommandBitsWire(uint8_t cmd) { - return sercom->I2CS.CTRLA.bit.MODE == I2C_SLAVE_OPERATION; + if (isMasterWIRE()) { + sercom->I2CM.CTRLB.bit.CMD = cmd; + while (sercom->I2CM.SYNCBUSY.bit.SYSOP) + { + // Waiting for synchronization + } + } else { + sercom->I2CS.CTRLB.bit.CMD = cmd; + } } +inline int SERCOM::availableWIRE( void ) { return isMasterWIRE() ? sercom->I2CM.INTFLAG.bit.SB : sercom->I2CS.INTFLAG.bit.DRDY; } inline bool SERCOM::readDataWIRE( void ) { @@ -60,7 +81,7 @@ inline bool SERCOM::readDataWIRE( void ) sercom->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; #ifdef USE_ZERODMA - if (_wire.useDma) { + if (isWireDma()) { if (!_dmaRxActive) dmaStartRx(txn->rxPtr, &sercom->I2CM.DATA.reg, _wire.txnLength); return true; @@ -74,6 +95,38 @@ inline bool SERCOM::readDataWIRE( void ) return true; } +inline uint8_t SERCOM::getINTFLAG( void ) const { return sercom->I2CM.INTFLAG.reg; } +inline uint16_t SERCOM::getSTATUS( void ) const { return sercom->I2CM.STATUS.reg; } +inline void SERCOM::clearINTFLAG( void ) { sercom->I2CM.INTFLAG.reg = 0xFF; } + +inline void SERCOM::setWireTxn(SercomTxn* txn, size_t length, bool useDma) +{ + _wire.currentTxn = txn; + _wire.txnLength = length; + _wire.txnIndex = 0; + _wire.useDma = useDma; +} + +inline void SERCOM::setWireDma(bool useDma) +{ + _wire.useDma = useDma; +} + +inline bool SERCOM::isWireDma(void) const +{ + return _wire.useDma; +} + +inline bool SERCOM::isMasterReadOperationWIRE( void ) +{ + return sercom->I2CS.STATUS.bit.DIR; +} + +inline bool SERCOM::isRXNackReceivedWIRE( void ) +{ + return sercom->I2CM.STATUS.bit.RXNACK; +} + #ifdef USE_ZERODMA inline SERCOM* SERCOM::findDmaOwner(Adafruit_ZeroDMA* dma, bool tx) { @@ -167,7 +220,10 @@ inline void SERCOM::dmaTxCallbackWIRE(Adafruit_ZeroDMA* dma) SERCOM* inst = findDmaOwner(dma, true); if (!inst) return; - inst->sercom->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(WIRE_MASTER_ACT_STOP); + if (inst->isMasterWIRE()) + inst->sercom->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(WIRE_MASTER_ACT_STOP); + else + inst->sercom->I2CS.CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); inst->_wire.returnValue = SercomWireError::SUCCESS; inst->_dmaTxActive = false; SERCOM::setPending((uint8_t)inst->getSercomIndex()); @@ -178,7 +234,10 @@ inline void SERCOM::dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma) SERCOM* inst = findDmaOwner(dma, false); if (!inst) return; - inst->sercom->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(WIRE_MASTER_ACT_STOP); + if (inst->isMasterWIRE()) + inst->sercom->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(WIRE_MASTER_ACT_STOP); + else + inst->sercom->I2CS.CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); inst->_wire.txnIndex = inst->_wire.txnLength; inst->_wire.returnValue = SercomWireError::SUCCESS; inst->_dmaRxActive = false; @@ -186,4 +245,6 @@ inline void SERCOM::dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma) } #endif +#endif // __cplusplus + #endif // SERCOM_INLINE_H diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index da9732c59..9066f4a97 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -37,12 +37,25 @@ TwoWire::TwoWire(SERCOM * s, uint8_t pinSDA, uint8_t pinSCL) this->_uc_pinSDA=pinSDA; this->_uc_pinSCL=pinSCL; transmissionBegun = false; + rxLength = 0; + rxIndex = 0; + txLength = 0; + txIndex = 0; + masterIndex = 0; + awaitingAddressAck = false; + txnDone = false; + txnStatus = 0; + rxBufferPtr = rxBuffer; + rxBufferCapacity = WIRE_RX_BUFFER_LENGTH; + pendingReceive = false; + pendingReceiveLength = 0; } void TwoWire::begin(void) { //Master Mode sercom->initMasterWIRE(TWI_CLOCK); sercom->enableWIRE(); + sercom->registerWireReceive(&TwoWire::onDeferredReceive, this); pinPeripheral(_uc_pinSDA, g_APinDescription[_uc_pinSDA].ulPinType); pinPeripheral(_uc_pinSCL, g_APinDescription[_uc_pinSCL].ulPinType); @@ -52,6 +65,7 @@ void TwoWire::begin(uint8_t address, bool enableGeneralCall) { //Slave mode sercom->initSlaveWIRE(address, enableGeneralCall); sercom->enableWIRE(); + sercom->registerWireReceive(&TwoWire::onDeferredReceive, this); pinPeripheral(_uc_pinSDA, g_APinDescription[_uc_pinSDA].ulPinType); pinPeripheral(_uc_pinSCL, g_APinDescription[_uc_pinSCL].ulPinType); @@ -68,38 +82,54 @@ void TwoWire::end() { } uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit) +{ + return requestFrom(address, quantity, static_cast(nullptr), stopBit); +} + +uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, uint8_t* rxBuffer, bool stopBit) { if(quantity == 0) { return 0; } - size_t byteRead = 0; - - rxBuffer.clear(); - - if(sercom->startTransmissionWIRE(address, WIRE_READ_FLAG)) - { - // Read first data - rxBuffer.store_char(sercom->readDataWIRE()); + if (rxBuffer != nullptr) { + rxBufferPtr = rxBuffer; + rxBufferCapacity = quantity; + } else { + rxBufferPtr = this->rxBuffer; + rxBufferCapacity = WIRE_RX_BUFFER_LENGTH; + if (quantity > rxBufferCapacity) + quantity = rxBufferCapacity; + } - // Connected to slave - for (byteRead = 1; byteRead < quantity; ++byteRead) - { - sercom->prepareAckBitWIRE(); // Prepare Acknowledge - sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_READ); // Prepare the ACK command for the slave - rxBuffer.store_char(sercom->readDataWIRE()); // Read data and send the ACK - } - sercom->prepareNackBitWIRE(); // Prepare NACK to stop slave transmission - //sercom->readDataWIRE(); // Clear data register to send NACK + rxLength = 0; + rxIndex = 0; + + txn.config = I2C_CFG_READ | (stopBit ? I2C_CFG_STOP : 0); + txn.address = address; + txn.length = quantity; + txn.txPtr = nullptr; + txn.rxPtr = rxBufferPtr; + txn.onComplete = &TwoWire::onTxnComplete; + txn.user = this; + txnDone = false; + txnStatus = static_cast(SercomWireError::SUCCESS); + masterIndex = 0; + awaitingAddressAck = true; + + if (!sercom->enqueueWIRE(&txn)) + return 0; - if (stopBit) - { - sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); // Send Stop - } + while (!txnDone) { + yield(); } - return byteRead; + if (txnStatus != static_cast(SercomWireError::SUCCESS)) + return 0; + + rxLength = quantity; + return rxLength; } uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity) @@ -110,7 +140,8 @@ uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity) void TwoWire::beginTransmission(uint8_t address) { // save address of target and clear buffer txAddress = address; - txBuffer.clear(); + txLength = 0; + txIndex = 0; transmissionBegun = true; } @@ -125,30 +156,38 @@ uint8_t TwoWire::endTransmission(bool stopBit) { transmissionBegun = false ; - // Start I2C transmission - if ( !sercom->startTransmissionWIRE( txAddress, WIRE_WRITE_FLAG ) ) - { - sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); - return 2 ; // Address error + txn.config = stopBit ? I2C_CFG_STOP : 0; + txn.address = txAddress; + txn.length = txLength; + txn.txPtr = txBuffer; + txn.rxPtr = nullptr; + txn.onComplete = &TwoWire::onTxnComplete; + txn.user = this; + txnDone = false; + txnStatus = static_cast(SercomWireError::SUCCESS); + masterIndex = 0; + awaitingAddressAck = true; + + if (!sercom->enqueueWIRE(&txn)) + return static_cast(SercomWireError::QUEUE_FULL); + + while (!txnDone) { + yield(); } - // Send all buffer - while( txBuffer.available() ) - { - // Trying to send data - if ( !sercom->sendDataMasterWIRE( txBuffer.read_char() ) ) - { - sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); - return 3 ; // Nack or error - } + SercomWireError err = static_cast(txnStatus); + switch (err) { + case SercomWireError::SUCCESS: + return 0; + case SercomWireError::DATA_TOO_LONG: + return 1; + case SercomWireError::NACK_ON_ADDRESS: + return 2; + case SercomWireError::NACK_ON_DATA: + return 3; + default: + return 4; } - - if (stopBit) - { - sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); - } - - return 0; } uint8_t TwoWire::endTransmission() @@ -159,12 +198,12 @@ uint8_t TwoWire::endTransmission() size_t TwoWire::write(uint8_t ucData) { // No writing, without begun transmission or a full buffer - if ( !transmissionBegun || txBuffer.isFull() ) + if ( !transmissionBegun || txLength >= WIRE_TX_BUFFER_LENGTH ) { return 0 ; } - txBuffer.store_char( ucData ) ; + txBuffer[txLength++] = ucData; return 1 ; } @@ -185,17 +224,21 @@ size_t TwoWire::write(const uint8_t *data, size_t quantity) int TwoWire::available(void) { - return rxBuffer.available(); + return (rxLength > rxIndex) ? (int)(rxLength - rxIndex) : 0; } int TwoWire::read(void) { - return rxBuffer.read_char(); + if (rxIndex >= rxLength) + return -1; + return rxBufferPtr[rxIndex++]; } int TwoWire::peek(void) { - return rxBuffer.peek(); + if (rxIndex >= rxLength) + return -1; + return rxBufferPtr[rxIndex]; } void TwoWire::flush(void) @@ -214,23 +257,143 @@ void TwoWire::onRequest(void(*function)(void)) onRequestCallback = function; } +void TwoWire::setRxBuffer(uint8_t* buffer, size_t length) +{ + if (buffer == nullptr || length == 0) + { + clearRxBuffer(); + return; + } + rxBufferPtr = buffer; + rxBufferCapacity = length; +} + +void TwoWire::clearRxBuffer(void) +{ + if (rxBufferPtr && rxBufferCapacity > 0) + memset(rxBufferPtr, 0, rxBufferCapacity); + rxLength = 0; + rxIndex = 0; +} + +void TwoWire::resetRxBuffer(void) +{ + rxBufferPtr = rxBuffer; + rxBufferCapacity = WIRE_RX_BUFFER_LENGTH; + clearRxBuffer(); +} + +uint8_t* TwoWire::getRxBuffer(void) +{ + return rxBufferPtr; +} + +size_t TwoWire::getRxLength(void) const +{ + return rxLength; +} + void TwoWire::onService(void) { + SercomTxn* t = &txn; + uint8_t flags = (uint8_t)sercom->getINTFLAG(); + + if (flags == 0) + return; + + if (sercom->isRXNackReceivedWIRE()) + { + sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); + SercomWireError err = awaitingAddressAck ? SercomWireError::NACK_ON_ADDRESS + : SercomWireError::NACK_ON_DATA; + sercom->deferStopWIRE(err); + return; + } + + if (sercom->isMasterWIRE()) + { + if (flags & SERCOM_I2CM_INTFLAG_ERROR) + { + sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); + uint16_t status = (uint16_t)sercom->getSTATUS(); + uint8_t busState = (status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> SERCOM_I2CM_STATUS_BUSSTATE_Pos; + SercomWireError err; + + if (status & SERCOM_I2CM_STATUS_ARBLOST) + err = SercomWireError::ARBITRATION_LOST; + else if (status & SERCOM_I2CM_STATUS_BUSERR) + err = SercomWireError::BUS_ERROR; + else if (status & SERCOM_I2CM_STATUS_MEXTTOUT) + err = SercomWireError::MASTER_TIMEOUT; + else if (status & SERCOM_I2CM_STATUS_SEXTTOUT) + err = SercomWireError::SLAVE_TIMEOUT; + else if (status & SERCOM_I2CM_STATUS_LENERR) + err = SercomWireError::LENGTH_ERROR; + else if (busState == 0x0) + err = SercomWireError::BUS_STATE_UNKNOWN; + else + err = SercomWireError::UNKNOWN_ERROR; + + sercom->clearINTFLAG(); + sercom->deferStopWIRE(err); + return; + } + + bool ok = (t->config & I2C_CFG_READ) ? sercom->readDataWIRE() + : sercom->sendDataWIRE(); + + if (ok){ + awaitingAddressAck = false; +#ifdef USE_ZERODMA + if (t->length > 0 && t->length < 256 && (t->config & I2C_CFG_STOP)) + return; +#endif + sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_READ); + return; + } + + // Transaction complete + if (t->config & I2C_CFG_STOP) + sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); + else + sercom->clearINTFLAG(); + + sercom->deferStopWIRE(SercomWireError::SUCCESS); + return; + } + if ( sercom->isSlaveWIRE() ) { + if (flags & SERCOM_I2CS_INTFLAG_ERROR) + { + uint16_t status = (uint16_t)sercom->getSTATUS(); + SercomWireError err; + + if (status & SERCOM_I2CS_STATUS_BUSERR) + err = SercomWireError::BUS_ERROR; + else if (status & SERCOM_I2CS_STATUS_COLL) + err = SercomWireError::ARBITRATION_LOST; + else if (status & SERCOM_I2CS_STATUS_SEXTTOUT) + err = SercomWireError::SLAVE_TIMEOUT; + else if (status & SERCOM_I2CS_STATUS_LOWTOUT) + err = SercomWireError::SLAVE_TIMEOUT; + else + err = SercomWireError::UNKNOWN_ERROR; + + sercom->clearINTFLAG(); + sercom->deferStopWIRE(err); + return; + } + if(sercom->isStopDetectedWIRE() || (sercom->isAddressMatch() && sercom->isRestartDetectedWIRE() && !sercom->isMasterReadOperationWIRE())) //Stop or Restart detected { sercom->prepareAckBitWIRE(); sercom->prepareCommandBitsWire(0x03); - //Calling onReceiveCallback, if exists - if(onReceiveCallback) - { - onReceiveCallback(available()); - } - - rxBuffer.clear(); + pendingReceive = true; + pendingReceiveLength = available(); + sercom->deferWireReceive(pendingReceiveLength); } else if(sercom->isAddressMatch()) //Address Match { @@ -239,44 +402,89 @@ void TwoWire::onService(void) if(sercom->isMasterReadOperationWIRE()) //Is a request ? { - txBuffer.clear(); + txLength = 0; + txIndex = 0; transmissionBegun = true; - //Calling onRequestCallback, if exists + // onRequestCallback runs in ISR context here. Deferring to PendSV + // would require stalling DRDY or returning 0xFF until the buffer is filled. if(onRequestCallback) - { onRequestCallback(); - } + + t->txPtr = txBuffer; + t->rxPtr = nullptr; + t->length = txLength; + t->config = I2C_CFG_READ; + bool useDma = (txLength > 0 && txLength < 256); + sercom->setWireTxn(t, txLength, useDma); + } else { + rxLength = 0; + rxIndex = 0; + t->txPtr = nullptr; + t->rxPtr = rxBufferPtr; + t->length = rxBufferCapacity; + t->config = 0; + bool useDma = (rxBufferPtr != rxBuffer) && (rxBufferCapacity > 0 && rxBufferCapacity < 256); + sercom->setWireTxn(t, rxBufferCapacity, useDma); + if (useDma) + rxLength = rxBufferCapacity; } } else if(sercom->isDataReadyWIRE()) { - if (sercom->isMasterReadOperationWIRE()) - { - uint8_t c = 0xff; - - if( txBuffer.available() ) { - c = txBuffer.read_char(); - } - - transmissionBegun = sercom->sendDataSlaveWIRE(c); - } else { //Received data - if (rxBuffer.isFull()) { - sercom->prepareNackBitWIRE(); - } else { - //Store data - rxBuffer.store_char(sercom->readDataWIRE()); - - sercom->prepareAckBitWIRE(); - } + bool ok = sercom->isMasterReadOperationWIRE() + ? sercom->sendDataWIRE() + : sercom->readDataWIRE(); + + if (ok) { +#ifdef USE_ZERODMA + if (sercom->isWireDma()) + return; +#endif + if (!sercom->isMasterReadOperationWIRE()) + rxLength++; + sercom->prepareCommandBitsWire(0x03); + return; + } + if (!sercom->isMasterReadOperationWIRE()) { + sercom->prepareNackBitWIRE(); sercom->prepareCommandBitsWire(0x03); + return; } + + sercom->I2CS.DATA.reg = 0xFF; + sercom->prepareCommandBitsWire(0x03); + return; } } } +void TwoWire::onTxnComplete(void* user, int status) +{ + if (!user) + return; + TwoWire* self = static_cast(user); + self->txnStatus = status; + self->txnDone = true; +} + +void TwoWire::onDeferredReceive(void* user, int length) +{ + if (!user) + return; + TwoWire* self = static_cast(user); + if (!self->pendingReceive) + return; + if (self->onReceiveCallback) + self->onReceiveCallback(length); + self->rxLength = 0; + self->rxIndex = 0; + self->pendingReceive = false; + self->pendingReceiveLength = 0; +} + #if WIRE_INTERFACES_COUNT > 0 /* In case new variant doesn't define these macros, * we put here the ones for Arduino Zero. @@ -375,4 +583,3 @@ void TwoWire::onService(void) void WIRE5_IT_HANDLER_3(void) { Wire5.onService(); } #endif // __SAMD51__ #endif - diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index db2ae646e..da2834358 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -25,6 +25,7 @@ #include "SERCOM.h" #include "RingBuffer.h" +#include // WIRE_HAS_END means Wire has end() #define WIRE_HAS_END 1 @@ -43,6 +44,7 @@ class TwoWire : public Stream uint8_t endTransmission(void); uint8_t requestFrom(uint8_t address, size_t quantity, bool stopBit); + uint8_t requestFrom(uint8_t address, size_t quantity, uint8_t* rxBuffer, bool stopBit = true); uint8_t requestFrom(uint8_t address, size_t quantity); size_t write(uint8_t data); @@ -54,6 +56,11 @@ class TwoWire : public Stream virtual void flush(void); void onReceive(void(*)(int)); void onRequest(void(*)(void)); + void setRxBuffer(uint8_t* buffer, size_t length); + void clearRxBuffer(void); + void resetRxBuffer(void); + uint8_t* getRxBuffer(void); + size_t getRxLength(void) const; inline size_t write(unsigned long n) { return write((uint8_t)n); } inline size_t write(long n) { return write((uint8_t)n); } @@ -70,17 +77,33 @@ class TwoWire : public Stream bool transmissionBegun; - // RX Buffer - RingBufferN<256> rxBuffer; - - //TX buffer - RingBufferN<256> txBuffer; + // RX/TX buffers (sync compatibility, async staging) + static constexpr size_t WIRE_TX_BUFFER_LENGTH = 255; + static constexpr size_t WIRE_RX_BUFFER_LENGTH = SERIAL_BUFFER_SIZE; + uint8_t rxBuffer[WIRE_RX_BUFFER_LENGTH]; + uint8_t txBuffer[WIRE_TX_BUFFER_LENGTH]; + uint8_t* rxBufferPtr; + size_t rxBufferCapacity; + size_t rxLength; + size_t rxIndex; + size_t txLength; + size_t txIndex; + size_t masterIndex; + bool awaitingAddressAck; uint8_t txAddress; + volatile bool txnDone; + volatile int txnStatus; + bool pendingReceive; + int pendingReceiveLength; + SercomTxn txn; // Callback user functions void (*onRequestCallback)(void); void (*onReceiveCallback)(int); + static void onTxnComplete(void* user, int status); + static void onDeferredReceive(void* user, int length); + // TWI clock frequency static const uint32_t TWI_CLOCK = 100000; }; From 0e91a3fa2203db7f278f9b38c5b4c46a15c225cf Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sat, 31 Jan 2026 21:37:46 -0500 Subject: [PATCH 14/79] refactor SPI to use SERCOM dma implementation --- cores/arduino/SERCOM.cpp | 137 +++++++++++++++ cores/arduino/SERCOM.h | 22 +++ cores/arduino/SERCOM_Txn.h | 8 + cores/arduino/SERCOM_inline.h | 22 +++ libraries/SPI/SPI.cpp | 321 ++++++++++++---------------------- libraries/SPI/SPI.h | 22 +-- 6 files changed, 306 insertions(+), 226 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 0b6486c77..85425c3bc 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -233,6 +233,12 @@ void SERCOM::initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize ch resetSPI(); initClockNVIC(); +#ifdef USE_ZERODMA + dmaSetCallbacks(SERCOM::dmaTxCallbackSPI, SERCOM::dmaRxCallbackSPI); +#endif + + registerService(getSercomIndex(), &SERCOM::stopTransmissionSPI); + #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) sercom->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_MODE(0x3) | // master mode SERCOM_SPI_CTRLA_DOPO(mosi) | @@ -253,6 +259,137 @@ void SERCOM::initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize ch while( sercom->SPI.SYNCBUSY.bit.CTRLB == 1 ); } +bool SERCOM::startTransmissionSPI(void) +{ + SercomTxn* txn = nullptr; + if (!_txnQueue.peek(txn) || txn == nullptr) + return false; + + _spi.currentTxn = txn; + _spi.index = 0; + _spi.length = txn->length; + _spi.active = true; + +#ifdef USE_ZERODMA + _spi.useDma = _dmaConfigured; +#else + _spi.useDma = false; +#endif + + if (_spi.useDma) { +#ifdef USE_ZERODMA + void* dataReg = (void*)&sercom->SPI.DATA.reg; + _spi.dmaNeedTx = (txn->txPtr != nullptr); + _spi.dmaNeedRx = (txn->rxPtr != nullptr); + _spi.dmaTxDone = !_spi.dmaNeedTx; + _spi.dmaRxDone = !_spi.dmaNeedRx; + + DmaStatus st = DmaStatus::Ok; + if (_spi.dmaNeedTx && _spi.dmaNeedRx) + st = dmaStartDuplex(txn->txPtr, txn->rxPtr, dataReg, dataReg, txn->length, nullptr); + else if (_spi.dmaNeedTx) + st = dmaStartTx(txn->txPtr, dataReg, txn->length); + else + st = dmaStartDuplex(nullptr, txn->rxPtr, dataReg, dataReg, txn->length, nullptr); + + if (st != DmaStatus::Ok) { + _spi.returnValue = SercomSpiError::UNKNOWN_ERROR; + deferStopSPI(_spi.returnValue); + return false; + } + return true; +#endif + } + + sercom->SPI.INTENSET.reg = SERCOM_SPI_INTENSET_DRE | + SERCOM_SPI_INTENSET_RXC | + SERCOM_SPI_INTENSET_ERROR; + return true; +} + +bool SERCOM::enqueueSPI(SercomTxn* txn) +{ + if (txn == nullptr) + return false; + if (!_txnQueue.store(txn)) + return false; + if (!_spi.active) { + startTransmissionSPI(); + } + return true; +} + +void SERCOM::serviceSPI(void) +{ + if (!_spi.active || _spi.currentTxn == nullptr) + return; + + uint8_t flags = sercom->SPI.INTFLAG.reg; + + if (flags & SERCOM_SPI_INTFLAG_ERROR) + { + _spi.returnValue = SercomSpiError::BUF_OVERFLOW; + sercom->SPI.INTFLAG.reg = SERCOM_SPI_INTFLAG_ERROR; + deferStopSPI(_spi.returnValue); + return; + } + + SercomTxn* txn = _spi.currentTxn; + + if (flags & SERCOM_SPI_INTFLAG_RXC) { + uint8_t rx = sercom->SPI.DATA.reg; + if (txn->rxPtr && _spi.index > 0 && (_spi.index - 1) < _spi.length) + txn->rxPtr[_spi.index - 1] = rx; + if (_spi.index >= _spi.length) { + sercom->SPI.INTENCLR.reg = SERCOM_SPI_INTENCLR_DRE | SERCOM_SPI_INTENCLR_RXC | SERCOM_SPI_INTENCLR_ERROR; + _spi.returnValue = SercomSpiError::SUCCESS; + deferStopSPI(_spi.returnValue); + return; + } + } + + if (flags & SERCOM_SPI_INTFLAG_DRE) { + if (_spi.index < _spi.length) { + uint8_t out = 0xFF; + if (txn->txPtr) + out = txn->txPtr[_spi.index]; + sercom->SPI.DATA.reg = out; + _spi.index++; + return; + } + sercom->SPI.INTENCLR.reg = SERCOM_SPI_INTENCLR_DRE; + } +} + +void SERCOM::deferStopSPI(SercomSpiError error) +{ + _spi.returnValue = error; + setPending((uint8_t)getSercomIndex()); +} + +SercomTxn* SERCOM::stopTransmissionSPI(void) +{ + return stopTransmissionSPI(_spi.returnValue); +} + +SercomTxn* SERCOM::stopTransmissionSPI(SercomSpiError error) +{ + SercomTxn* txn = nullptr; + if (_txnQueue.read(txn) && txn != nullptr) + { + _spi.active = false; + _spi.currentTxn = nullptr; + if (txn->onComplete) + txn->onComplete(txn->user, static_cast(error)); + } + + SercomTxn* next = nullptr; + if (_txnQueue.peek(next) && next) + startTransmissionSPI(); + + return txn; +} + void SERCOM::initSPIClock(SercomSpiClockMode clockMode, uint32_t baudrate) { //Extract data from clockMode diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index 2e7e137c9..5ac6b1424 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -218,6 +218,12 @@ class SERCOM bool isDataRegisterEmptySPI( void ) ; bool isTransmitCompleteSPI( void ) ; bool isReceiveCompleteSPI( void ) ; + bool enqueueSPI(SercomTxn* txn); + bool startTransmissionSPI(void); + void serviceSPI(void); + void deferStopSPI(SercomSpiError error); + SercomTxn* stopTransmissionSPI(void); + SercomTxn* stopTransmissionSPI(SercomSpiError error); /* ========== WIRE ========== */ void initSlaveWIRE(uint8_t address, bool enableGeneralCall = false, uint8_t speed = 0x0) ; @@ -327,6 +333,9 @@ class SERCOM // --- WIRE DMA callbacks (ISR-safe, PendSV-only completion) --- static inline void dmaTxCallbackWIRE(Adafruit_ZeroDMA* dma); static inline void dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma); + // --- SPI DMA callbacks (protocol-owned) --- + static inline void dmaTxCallbackSPI(Adafruit_ZeroDMA* dma); + static inline void dmaRxCallbackSPI(Adafruit_ZeroDMA* dma); #endif #ifdef SERCOM_STRICT_PADS @@ -401,6 +410,19 @@ class SERCOM size_t txnLength = 0; } _wire; + struct SpiConfig { + bool active = false; + bool useDma = false; + bool dmaNeedTx = false; + bool dmaNeedRx = false; + bool dmaTxDone = false; + bool dmaRxDone = false; + size_t index = 0; + size_t length = 0; + SercomTxn* currentTxn = nullptr; + SercomSpiError returnValue = SercomSpiError::SUCCESS; + } _spi; + RingBufferN _txnQueue; void (*_wireDeferredCb)(void* user, int length) = nullptr; void* _wireDeferredUser = nullptr; diff --git a/cores/arduino/SERCOM_Txn.h b/cores/arduino/SERCOM_Txn.h index 5ce7c5efe..d109abecc 100644 --- a/cores/arduino/SERCOM_Txn.h +++ b/cores/arduino/SERCOM_Txn.h @@ -33,6 +33,14 @@ enum class SercomWireError : uint8_t UNKNOWN_ERROR = 13 // Error flag set but no specific bit matched }; +// SPI error reporting (async callbacks) +enum class SercomSpiError : uint8_t +{ + SUCCESS = 0, + BUF_OVERFLOW = 1, + UNKNOWN_ERROR = 2 +}; + // I2C config flags and helpers enum : uint16_t { I2C_CFG_READ = 1u << 0, diff --git a/cores/arduino/SERCOM_inline.h b/cores/arduino/SERCOM_inline.h index 36c32eba0..76a79a69a 100644 --- a/cores/arduino/SERCOM_inline.h +++ b/cores/arduino/SERCOM_inline.h @@ -243,6 +243,28 @@ inline void SERCOM::dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma) inst->_dmaRxActive = false; SERCOM::setPending((uint8_t)inst->getSercomIndex()); } + +inline void SERCOM::dmaTxCallbackSPI(Adafruit_ZeroDMA* dma) +{ + SERCOM* inst = findDmaOwner(dma, true); + if (!inst) return; + inst->_spi.dmaTxDone = true; + if (inst->_spi.dmaNeedRx && !inst->_spi.dmaRxDone) + return; + inst->_spi.returnValue = SercomSpiError::SUCCESS; + SERCOM::setPending((uint8_t)inst->getSercomIndex()); +} + +inline void SERCOM::dmaRxCallbackSPI(Adafruit_ZeroDMA* dma) +{ + SERCOM* inst = findDmaOwner(dma, false); + if (!inst) return; + inst->_spi.dmaRxDone = true; + if (inst->_spi.dmaNeedTx && !inst->_spi.dmaTxDone) + return; + inst->_spi.returnValue = SercomSpiError::SUCCESS; + SERCOM::setPending((uint8_t)inst->getSercomIndex()); +} #endif #endif // __cplusplus diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 0393752d7..6b1cbfd21 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -58,9 +58,9 @@ void SPIClass::begin() initialized = true; } - if(!use_dma) { - dmaAllocate(); - } +#ifdef USE_ZERODMA + _p_sercom->dmaInit(getDMAC_ID_TX(), getDMAC_ID_RX()); +#endif // PIO init pinPeripheral(_uc_pinMiso, g_APinDescription[_uc_pinMiso].ulPinType); @@ -240,131 +240,8 @@ void SPIClass::transfer(void *buf, size_t count) } } -// DMA-based SPI transfer() function --------------------------------------- - -// IMPORTANT: references to 65535 throughout the DMA code are INTENTIONAL. -// DO NOT try to 'fix' by changing to 65536, or large transfers will fail! -// The BTCNT value of a DMA descriptor is an unsigned 16-bit value with a -// max of 65535. Larger transfers are handled by linked descriptors. - -// Pointer to SPIClass object, one per DMA channel. This allows the -// DMA callback (which has to exist outside the class context) to have -// a reference back to the originating SPIClass object. -static SPIClass *spiPtr[DMAC_CH_NUM] = { 0 }; // Legit inits list to NULL - -void SPIClass::dmaCallback(Adafruit_ZeroDMA *dma) { - // dmaCallback() receives an Adafruit_ZeroDMA object. From this we can get - // a channel number (0 to DMAC_CH_NUM-1, always unique per ZeroDMA object), - // then locate the originating SPIClass object using array lookup, setting - // the dma_busy element 'false' to indicate end of transfer. Doesn't matter - // if it's a read or write transfer...both channels get pointers to it. - spiPtr[dma->getChannel()]->dma_busy = false; -} - -// For read-only and read+write transfers, a callback is assigned only -// to the read channel to indicate end-of-transfer, and the write channel's -// callback is assigned to this nonsense function (for reasons I'm not -// entirely sure of, setting the callback to NULL doesn't work). -static void dmaDoNothingCallback(Adafruit_ZeroDMA *dma) { (void)dma; } - -// This could've gone in begin(), but for the sake of organization... -void SPIClass::dmaAllocate(void) { - // In order to support fully non-blocking SPI transfers, DMA descriptor - // lists must be created for the input and/or output data. Rather than - // do this dynamically with every transfer, the lists are allocated once - // on SPI init. Maximum list size is finite and knowable -- transfers to - // or from RAM or from flash memory will never exceed the corresponding - // memory size (if they do, you have bigger problems). Descriptors - // aren't large and there's usually only a handful to a dozen, so this - // isn't an excessive burden in exchange for big non-blocking transfers. - uint32_t maxWriteBytes = FLASH_SIZE; // Writes can't exceed all of flash -#if defined(__SAMD51__) - uint32_t maxReadBytes = HSRAM_SIZE; // Reads can't exceed all of RAM -#else - uint32_t maxReadBytes = HMCRAMC0_SIZE; -#endif - if(maxReadBytes > maxWriteBytes) { // I don't think any SAMD devices - maxWriteBytes = maxReadBytes; // have RAM > flash, but just in case - } - - // VITAL to alloc read channel first, assigns it a higher DMA priority! - if(readChannel.allocate() == DMA_STATUS_OK) { - if(writeChannel.allocate() == DMA_STATUS_OK) { - - // Both DMA channels (read and write) allocated successfully, - // set up transfer triggers and other basics... - - // readChannel callback only needs to be set up once. - // Unlike the write callback which may get switched on or off, - // read callback stays put. In certain cases the read DMA job - // just isn't started and the callback is a non-issue then. - readChannel.setTrigger(getDMAC_ID_RX()); - readChannel.setAction(DMA_TRIGGER_ACTON_BEAT); - readChannel.setCallback(dmaCallback); - spiPtr[readChannel.getChannel()] = this; - - writeChannel.setTrigger(getDMAC_ID_TX()); - writeChannel.setAction(DMA_TRIGGER_ACTON_BEAT); - spiPtr[writeChannel.getChannel()] = this; - - // One descriptor per channel has already been allocated - // in Adafruit_ZeroDMA, this just gets pointers to them... - firstReadDescriptor = readChannel.addDescriptor( - (void *)getDataRegister(), // Source address (SPI data reg) - NULL, // Dest address (set later) - 0, // Count (set later) - DMA_BEAT_SIZE_BYTE, // Bytes/hwords/words - false, // Don't increment source address - true); // Increment dest address - firstWriteDescriptor = writeChannel.addDescriptor( - NULL, // Source address (set later) - (void *)getDataRegister(), // Dest (SPI data register) - 0, // Count (set later) - DMA_BEAT_SIZE_BYTE, // Bytes/hwords/words - true, // Increment source address - false); // Don't increment dest address - // This is the number of EXTRA descriptors beyond the first. - int numReadDescriptors = ((maxReadBytes + 65534) / 65535) - 1; - int numWriteDescriptors = ((maxWriteBytes + 65534) / 65535) - 1; - int totalDescriptors = numReadDescriptors + numWriteDescriptors; - - if(totalDescriptors <= 0) { // Don't need extra descriptors, - use_dma = true; // channels are allocated, we're good. - } else { // Else allocate extra descriptor lists... - // Although DMA descriptors are technically a linked list, we just - // allocate a chunk all at once, and finesse the pointers later. - if((extraReadDescriptors = (DmacDescriptor *)malloc( - totalDescriptors * sizeof(DmacDescriptor)))) { - use_dma = true; // Everything allocated successfully - extraWriteDescriptors = &extraReadDescriptors[numReadDescriptors]; - - // Initialize descriptors (copy from first ones) - // cast to void* to suppress warning: with no trivial copy-assignment [-Wclass-memaccess] - for(int i=0; i 65535) { // Limit each descriptor - bytesThisDescriptor = 65535; // to 65535 (not 65536) bytes - } - rDesc->BTCNT.reg = wDesc->BTCNT.reg = bytesThisDescriptor; - if(rxbuf) { // Read-only or read+write - // Auto-inc addresses in DMA descriptors must point to END of data. - // Buf pointers would advance at end of loop anyway, do it now... - rxbuf8 += bytesThisDescriptor; - rDesc->DSTADDR.reg = (uint32_t)rxbuf8; - } - if(txbuf) { // Write-only or read+write - txbuf8 += bytesThisDescriptor; // Same as above - wDesc->SRCADDR.reg = (uint32_t)txbuf8; - wDesc->BTCTRL.bit.SRCINC = 1; // Increment source pointer - } else { // Read-only requires dummy write - wDesc->SRCADDR.reg = (uint32_t)&dum; - wDesc->BTCTRL.bit.SRCINC = 0; // Don't increment source pointer - } - count -= bytesThisDescriptor; - if(count) { // Still more data? - // Link to next descriptors. Extra descriptors are IN ADDITION - // to first, so it's safe and correct that descIdx starts at 0. - rDesc->DESCADDR.reg = (uint32_t)&extraReadDescriptors[descIdx]; - wDesc->DESCADDR.reg = (uint32_t)&extraWriteDescriptors[descIdx]; - rDesc = &extraReadDescriptors[descIdx]; // Update pointers to - wDesc = &extraWriteDescriptors[descIdx]; // next descriptors - descIdx++; - // A write-only transfer doesn't use the read descriptor list, but - // it's quicker to build it (full of nonsense) anyway than to check. - } else { // No more data, end descriptor linked lists - rDesc->DESCADDR.reg = wDesc->DESCADDR.reg = 0; - } - } - - // Set up DMA transfer job(s) ------------------------------------------ - - if(rxbuf) { // Read+write or read-only - // End-of-read callback is already set up, disable write CB, start job - writeChannel.setCallback(dmaDoNothingCallback); - readChannel.startJob(); - } else { // Write-only, use end-of-write callback - writeChannel.setCallback(dmaCallback); - } - - // Run DMA jobs, blocking if requested --------------------------------- + _txn.txPtr = static_cast(txbuf); + _txn.rxPtr = static_cast(rxbuf); + _txn.length = count; + _txn.onComplete = onComplete ? onComplete : &SPIClass::onTxnComplete; + _txn.user = onComplete ? user : this; + txnDone = false; + txnStatus = 0; + + if (!_p_sercom->enqueueSPI(&_txn)) { + if (onComplete) + onComplete(user, static_cast(SercomSpiError::UNKNOWN_ERROR)); + return; + } - dma_busy = true; - writeChannel.startJob(); // All xfers, even read-only, need write job. - if(block) { // If blocking transfer requested, - while(dma_busy); // wait for job to finish - } + if (!onComplete && block) { + while (!txnDone) ; + } +} - } else { // NON-DMA FALLBACK --------------------------------------------- - - if(txbuf8) { - if(rxbuf8) { // Write + read simultaneously - while(count--) { - *rxbuf8++ = _p_sercom->transferDataSPI(*txbuf8++); - } - } else { // Write only - while(count--) { - (void)_p_sercom->transferDataSPI(*txbuf8++); - } - } - } else { // Read only - while(count--) { - *rxbuf8++ = _p_sercom->transferDataSPI(0xFF); - } - } +void SPIClass::onService(void) +{ + _p_sercom->serviceSPI(); +} - } // end non-DMA +void SPIClass::onTxnComplete(void* user, int status) +{ + if (!user) + return; + SPIClass* self = static_cast(user); + self->txnStatus = status; + self->txnDone = true; } -// Waits for a prior in-background DMA transfer to complete. +// Waits for a prior in-background transfer to complete. void SPIClass::waitForTransfer(void) { - while(dma_busy); + while(!txnDone); } /* returns the current DMA transfer status to allow non-blocking polling */ bool SPIClass::isBusy(void) { - return dma_busy; + return !txnDone; } @@ -552,19 +374,92 @@ void SPIClass::setClockSource(SercomClockSource clk) { #define PAD_SPI_RX SERCOM_RX_PAD_0 #endif // PERIPH_SPI SPIClass SPI (&PERIPH_SPI, PIN_SPI_MISO, PIN_SPI_SCK, PIN_SPI_MOSI, PAD_SPI_TX, PAD_SPI_RX); + + #ifndef SPI_IT_HANDLER + #define SPI_IT_HANDLER SERCOM4_Handler + #endif + void SPI_IT_HANDLER(void) { SPI.onService(); } + + #if defined(__SAMD51__) + #ifndef SPI_IT_HANDLER_0 + #define SPI_IT_HANDLER_0 SERCOM4_0_Handler + #define SPI_IT_HANDLER_1 SERCOM4_1_Handler + #define SPI_IT_HANDLER_2 SERCOM4_2_Handler + #define SPI_IT_HANDLER_3 SERCOM4_3_Handler + #endif + void SPI_IT_HANDLER_0(void) { SPI.onService(); } + void SPI_IT_HANDLER_1(void) { SPI.onService(); } + void SPI_IT_HANDLER_2(void) { SPI.onService(); } + void SPI_IT_HANDLER_3(void) { SPI.onService(); } + #endif #endif #if SPI_INTERFACES_COUNT > 1 SPIClass SPI1(&PERIPH_SPI1, PIN_SPI1_MISO, PIN_SPI1_SCK, PIN_SPI1_MOSI, PAD_SPI1_TX, PAD_SPI1_RX); + + #if defined(SPI1_IT_HANDLER) + void SPI1_IT_HANDLER(void) { SPI1.onService(); } + #endif + + #if defined(__SAMD51__) && defined(SPI1_IT_HANDLER_0) + void SPI1_IT_HANDLER_0(void) { SPI1.onService(); } + void SPI1_IT_HANDLER_1(void) { SPI1.onService(); } + void SPI1_IT_HANDLER_2(void) { SPI1.onService(); } + void SPI1_IT_HANDLER_3(void) { SPI1.onService(); } + #endif #endif #if SPI_INTERFACES_COUNT > 2 SPIClass SPI2(&PERIPH_SPI2, PIN_SPI2_MISO, PIN_SPI2_SCK, PIN_SPI2_MOSI, PAD_SPI2_TX, PAD_SPI2_RX); + + #if defined(SPI2_IT_HANDLER) + void SPI2_IT_HANDLER(void) { SPI2.onService(); } + #endif + + #if defined(__SAMD51__) && defined(SPI2_IT_HANDLER_0) + void SPI2_IT_HANDLER_0(void) { SPI2.onService(); } + void SPI2_IT_HANDLER_1(void) { SPI2.onService(); } + void SPI2_IT_HANDLER_2(void) { SPI2.onService(); } + void SPI2_IT_HANDLER_3(void) { SPI2.onService(); } + #endif #endif #if SPI_INTERFACES_COUNT > 3 SPIClass SPI3(&PERIPH_SPI3, PIN_SPI3_MISO, PIN_SPI3_SCK, PIN_SPI3_MOSI, PAD_SPI3_TX, PAD_SPI3_RX); + + #if defined(SPI3_IT_HANDLER) + void SPI3_IT_HANDLER(void) { SPI3.onService(); } + #endif + + #if defined(__SAMD51__) && defined(SPI3_IT_HANDLER_0) + void SPI3_IT_HANDLER_0(void) { SPI3.onService(); } + void SPI3_IT_HANDLER_1(void) { SPI3.onService(); } + void SPI3_IT_HANDLER_2(void) { SPI3.onService(); } + void SPI3_IT_HANDLER_3(void) { SPI3.onService(); } + #endif #endif #if SPI_INTERFACES_COUNT > 4 SPIClass SPI4(&PERIPH_SPI4, PIN_SPI4_MISO, PIN_SPI4_SCK, PIN_SPI4_MOSI, PAD_SPI4_TX, PAD_SPI4_RX); + + #if defined(SPI4_IT_HANDLER) + void SPI4_IT_HANDLER(void) { SPI4.onService(); } + #endif + + #if defined(__SAMD51__) && defined(SPI4_IT_HANDLER_0) + void SPI4_IT_HANDLER_0(void) { SPI4.onService(); } + void SPI4_IT_HANDLER_1(void) { SPI4.onService(); } + void SPI4_IT_HANDLER_2(void) { SPI4.onService(); } + void SPI4_IT_HANDLER_3(void) { SPI4.onService(); } + #endif #endif #if SPI_INTERFACES_COUNT > 5 SPIClass SPI5(&PERIPH_SPI5, PIN_SPI5_MISO, PIN_SPI5_SCK, PIN_SPI5_MOSI, PAD_SPI5_TX, PAD_SPI5_RX); + + #if defined(SPI5_IT_HANDLER) + void SPI5_IT_HANDLER(void) { SPI5.onService(); } + #endif + + #if defined(__SAMD51__) && defined(SPI5_IT_HANDLER_0) + void SPI5_IT_HANDLER_0(void) { SPI5.onService(); } + void SPI5_IT_HANDLER_1(void) { SPI5.onService(); } + void SPI5_IT_HANDLER_2(void) { SPI5.onService(); } + void SPI5_IT_HANDLER_3(void) { SPI5.onService(); } + #endif #endif diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index 7c719f695..794436e3b 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -21,7 +21,7 @@ #define _SPI_H_INCLUDED #include -#include +#include "SERCOM.h" // SPI_HAS_TRANSACTION means SPI has // - beginTransaction() @@ -118,7 +118,10 @@ class SPIClass { uint16_t transfer16(uint16_t data); void transfer(void *buf, size_t count); void transfer(const void* txbuf, void* rxbuf, size_t count, - bool block = true); + bool block = true, + void (*onComplete)(void* user, int status) = nullptr, + void* user = nullptr); + void onService(void); void waitForTransfer(void); bool isBusy(void); @@ -169,17 +172,10 @@ class SPIClass { char interruptSave; uint32_t interruptMask; - // transfer(txbuf, rxbuf, count, block) uses DMA when possible - Adafruit_ZeroDMA readChannel; - Adafruit_ZeroDMA writeChannel; - DmacDescriptor *firstReadDescriptor = NULL; // List entry point - DmacDescriptor *firstWriteDescriptor = NULL; - DmacDescriptor *extraReadDescriptors = NULL; // Add'l descriptors - DmacDescriptor *extraWriteDescriptors = NULL; - bool use_dma = false; // true on successful alloc - volatile bool dma_busy = false; - void dmaAllocate(void); - static void dmaCallback(Adafruit_ZeroDMA *dma); + volatile bool txnDone = false; + volatile int txnStatus = 0; + SercomTxn _txn; + static void onTxnComplete(void* user, int status); }; #if SPI_INTERFACES_COUNT > 0 From 37bca6e6f63faf6281b6c1b5d77b750ac1611bb8 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sat, 14 Feb 2026 23:50:21 -0600 Subject: [PATCH 15/79] stage more UART DMA structure --- cores/arduino/SERCOM.cpp | 133 +++++++++++++++++++++++++++++++++++---- cores/arduino/SERCOM.h | 24 ++++++- cores/arduino/Uart.cpp | 118 ++++++++++++++++++++++++++++++++++ cores/arduino/Uart.h | 13 ++++ 4 files changed, 276 insertions(+), 12 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 85425c3bc..552e0b408 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -66,6 +66,27 @@ void SERCOM::initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint initClockNVIC(); resetUART(); +#ifdef USE_ZERODMA +#ifdef SERCOM0_DMAC_ID_TX + int8_t id = getSercomIndex(); + if (id < 0) + return; + + dmaSetCallbacks(SERCOM::dmaTxCallbackUART, SERCOM::dmaRxCallbackUART); + + if (_dmaConfigured) + return; + + _dmaTxTrigger = SERCOM0_DMAC_ID_TX + (id * 2); + _dmaRxTrigger = SERCOM0_DMAC_ID_RX + (id * 2); + dmaInit(_dmaTxTrigger, _dmaRxTrigger); +#else + (void)0; +#endif // SERCOM0_DMAC_ID_TX +#endif // USE_ZERODMA + + registerService(getSercomIndex(), &SERCOM::stopTransmissionUART); + //Setting the CTRLA register sercom->USART.CTRLA.reg = SERCOM_USART_CTRLA_MODE(mode) | SERCOM_USART_CTRLA_SAMPR(sampleRate); @@ -93,6 +114,7 @@ void SERCOM::initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8); } } + void SERCOM::initFrame(SercomUartCharSize charSize, SercomDataOrder dataOrder, SercomParityMode parityMode, SercomNumberStopBit nbStopBits) { //Setting the CTRLA register @@ -224,14 +246,111 @@ void SERCOM::disableDataRegisterEmptyInterruptUART() sercom->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_DRE; } +bool SERCOM::startTransmissionUART(void) +{ + SercomTxn* txn = nullptr; + if (!_txnQueue.peek(txn) || txn == nullptr) + return false; + + _uart.currentTxn = txn; + _uart.index = 0; + _uart.length = txn->length; + _uart.active = true; + +#ifdef USE_ZERODMA + _uart.useDma = _dmaConfigured; +#else + _uart.useDma = false; +#endif + + if (!_uart.useDma) + return false; + +#ifdef USE_ZERODMA + void* dataReg = (void*)&sercom->USART.DATA.reg; + _uart.dmaNeedTx = (txn->txPtr != nullptr); + _uart.dmaNeedRx = (txn->rxPtr != nullptr); + _uart.dmaTxDone = !_uart.dmaNeedTx; + _uart.dmaRxDone = !_uart.dmaNeedRx; + + DmaStatus st = DmaStatus::Ok; + if (_uart.dmaNeedTx) + st = dmaStartTx(txn->txPtr, dataReg, txn->length); + else if (_uart.dmaNeedRx) + st = dmaStartRx(txn->rxPtr, dataReg, txn->length); + + if (st != DmaStatus::Ok) { + _uart.returnValue = SercomUartError::UNKNOWN_ERROR; + deferStopUART(_uart.returnValue); + return false; + } + return true; +#else + return false; +#endif +} + +bool SERCOM::enqueueUART(SercomTxn* txn) +{ + if (txn == nullptr) + return false; +#ifdef USE_ZERODMA + if (!_dmaConfigured) + return false; +#else + return false; +#endif + if (!_txnQueue.store(txn)) + return false; + if (!_uart.active) { + if (!startTransmissionUART()) { + SercomTxn* tmp = nullptr; + _txnQueue.read(tmp); + if (tmp && tmp->onComplete) + tmp->onComplete(tmp->user, static_cast(SercomUartError::UNKNOWN_ERROR)); + return false; + } + } + return true; +} + +void SERCOM::deferStopUART(SercomUartError error) +{ + _uart.returnValue = error; + setPending((uint8_t)getSercomIndex()); +} + +SercomTxn* SERCOM::stopTransmissionUART(void) +{ + return stopTransmissionUART(_uart.returnValue); +} + +SercomTxn* SERCOM::stopTransmissionUART(SercomUartError error) +{ + SercomTxn* txn = nullptr; + if (_txnQueue.read(txn) && txn != nullptr) + { + _uart.active = false; + _uart.currentTxn = nullptr; + if (txn->onComplete) + txn->onComplete(txn->user, static_cast(error)); + } + + SercomTxn* next = nullptr; + if (_txnQueue.peek(next) && next) + startTransmissionUART(); + + return txn; +} + /* ========================= * ===== Sercom SPI * ========================= */ void SERCOM::initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize charSize, SercomDataOrder dataOrder) { - resetSPI(); initClockNVIC(); + resetSPI(); #ifdef USE_ZERODMA dmaSetCallbacks(SERCOM::dmaTxCallbackSPI, SERCOM::dmaRxCallbackSPI); @@ -497,16 +616,8 @@ uint8_t SERCOM::transferDataSPI(uint8_t data) return sercom->SPI.DATA.bit.DATA; // Reading data } -bool SERCOM::isBufferOverflowErrorSPI() -{ - return sercom->SPI.STATUS.bit.BUFOVF; -} - -bool SERCOM::isDataRegisterEmptySPI() -{ - //DRE : Data Register Empty - return sercom->SPI.INTFLAG.bit.DRE; -} +bool SERCOM::isBufferOverflowErrorSPI() { return sercom->SPI.STATUS.bit.BUFOVF; } +bool SERCOM::isDataRegisterEmptySPI() { return sercom->SPI.INTFLAG.bit.DRE; } //bool SERCOM::isTransmitCompleteSPI() //{ diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index 5ac6b1424..f3b797d3b 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -202,6 +202,11 @@ class SERCOM void acknowledgeUARTError() ; void enableDataRegisterEmptyInterruptUART(); void disableDataRegisterEmptyInterruptUART(); + bool enqueueUART(SercomTxn* txn); + bool startTransmissionUART(void); + SercomTxn* stopTransmissionUART(void); + SercomTxn* stopTransmissionUART(SercomUartError error); + void deferStopUART(SercomUartError error); /* ========== SPI ========== */ void initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize charSize, SercomDataOrder dataOrder) ; @@ -336,7 +341,10 @@ class SERCOM // --- SPI DMA callbacks (protocol-owned) --- static inline void dmaTxCallbackSPI(Adafruit_ZeroDMA* dma); static inline void dmaRxCallbackSPI(Adafruit_ZeroDMA* dma); -#endif + // --- UART DMA callbacks (protocol-owned) --- + static inline void dmaTxCallbackUART(Adafruit_ZeroDMA* dma); + static inline void dmaRxCallbackUART(Adafruit_ZeroDMA* dma); +#endif // USE_ZERODMA #ifdef SERCOM_STRICT_PADS enum class PadFunc : uint8_t { @@ -387,6 +395,7 @@ class SERCOM void initClockNVIC( void ) ; #ifdef USE_ZERODMA void initWireDma(void); + void initUartDma(void); #endif void initWIRE(void); @@ -423,6 +432,19 @@ class SERCOM SercomSpiError returnValue = SercomSpiError::SUCCESS; } _spi; + struct UartConfig { + bool active = false; + bool useDma = false; + bool dmaNeedTx = false; + bool dmaNeedRx = false; + bool dmaTxDone = false; + bool dmaRxDone = false; + size_t index = 0; + size_t length = 0; + SercomTxn* currentTxn = nullptr; + SercomUartError returnValue = SercomUartError::SUCCESS; + } _uart; + RingBufferN _txnQueue; void (*_wireDeferredCb)(void* user, int length) = nullptr; void* _wireDeferredUser = nullptr; diff --git a/cores/arduino/Uart.cpp b/cores/arduino/Uart.cpp index dd797da4d..6d0262ace 100644 --- a/cores/arduino/Uart.cpp +++ b/cores/arduino/Uart.cpp @@ -190,6 +190,124 @@ size_t Uart::write(const uint8_t data) return 1; } +size_t Uart::write(const uint8_t* buffer, size_t size) +{ + if (buffer == nullptr || size == 0) + return 0; + +#ifdef USE_ZERODMA + _txn.txPtr = buffer; + _txn.rxPtr = nullptr; + _txn.length = size; + _txn.onComplete = &Uart::onTxnComplete; + _txn.user = this; + txnDone = false; + txnStatus = 0; + + if (sercom->enqueueUART(&_txn)) { + while (!txnDone) ; + return size; + } +#endif + + for (size_t i = 0; i < size; ++i) + write(buffer[i]); + return size; +} + +size_t Uart::writeAsync(const uint8_t* buffer, size_t size, void (*onComplete)(void* user, int status), void* user) +{ + if (buffer == nullptr || size == 0) + return 0; + +#ifdef USE_ZERODMA + _txn.txPtr = buffer; + _txn.rxPtr = nullptr; + _txn.length = size; + _txn.onComplete = onComplete ? onComplete : &Uart::onTxnComplete; + _txn.user = onComplete ? user : this; + txnDone = false; + txnStatus = 0; + + if (!sercom->enqueueUART(&_txn)) + return 0; + + return size; +#else + (void)onComplete; + (void)user; + for (size_t i = 0; i < size; ++i) + write(buffer[i]); + return size; +#endif +} + +size_t Uart::read(uint8_t* buffer, size_t size, void (*onComplete)(void* user, int status), void* user) +{ + if (buffer == nullptr || size == 0) + return 0; + + if (onComplete == nullptr) { + size_t readCount = 0; + while (readCount < size) { + int c = read(); + if (c >= 0) + buffer[readCount++] = static_cast(c); + } + return readCount; + } + +#ifdef USE_ZERODMA + pendingRxCb = onComplete; + pendingRxUser = user; + rxExternalActive = true; + + sercom->getSercom()->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_RXC; + + _txn.txPtr = nullptr; + _txn.rxPtr = buffer; + _txn.length = size; + _txn.onComplete = &Uart::onTxnComplete; + _txn.user = this; + txnDone = false; + txnStatus = 0; + + if (!sercom->enqueueUART(&_txn)) { + sercom->getSercom()->USART.INTENSET.reg = SERCOM_USART_INTENSET_RXC; + rxExternalActive = false; + pendingRxCb = nullptr; + pendingRxUser = nullptr; + return 0; + } + + return size; +#else + (void)onComplete; + (void)user; + return 0; +#endif +} + +void Uart::onTxnComplete(void* user, int status) +{ + if (!user) + return; + Uart* self = static_cast(user); + self->txnStatus = status; + self->txnDone = true; + if (self->rxExternalActive) { + self->rxExternalActive = false; + self->sercom->getSercom()->USART.INTENSET.reg = SERCOM_USART_INTENSET_RXC; + if (self->pendingRxCb) { + void (*cb)(void*, int) = self->pendingRxCb; + void* cbUser = self->pendingRxUser; + self->pendingRxCb = nullptr; + self->pendingRxUser = nullptr; + cb(cbUser, status); + } + } +} + SercomNumberStopBit Uart::extractNbStopBit(uint16_t config) { switch(config & HARDSER_STOP_BIT_MASK) diff --git a/cores/arduino/Uart.h b/cores/arduino/Uart.h index 8f80ea9ce..9687dbbbb 100644 --- a/cores/arduino/Uart.h +++ b/cores/arduino/Uart.h @@ -36,8 +36,13 @@ class Uart : public HardwareSerial int availableForWrite(); int peek(); int read(); + size_t read(uint8_t* buffer, size_t size, + void (*onComplete)(void* user, int status) = nullptr, + void* user = nullptr); void flush(); size_t write(const uint8_t data); + size_t write(const uint8_t* buffer, size_t size); + size_t writeAsync(const uint8_t* buffer, size_t size, void (*onComplete)(void* user, int status), void* user); using Print::write; // pull in write(str) and write(buf, size) from Print void IrqHandler(); @@ -59,6 +64,14 @@ class Uart : public HardwareSerial uint32_t ul_pinMaskRTS; uint8_t uc_pinCTS; + volatile bool txnDone = false; + volatile int txnStatus = 0; + SercomTxn _txn; + static void onTxnComplete(void* user, int status); + bool rxExternalActive = false; + void (*pendingRxCb)(void* user, int status) = nullptr; + void* pendingRxUser = nullptr; + SercomNumberStopBit extractNbStopBit(uint16_t config); SercomUartCharSize extractCharSize(uint16_t config); SercomParityMode extractParity(uint16_t config); From 639e539da1c6254b34de3bbb582f13a928c2765b Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sun, 15 Feb 2026 00:03:37 -0600 Subject: [PATCH 16/79] unify structure between different sercoms to mirror each other more consistently --- cores/arduino/RingBuffer.h | 2 +- cores/arduino/SERCOM.cpp | 281 +++++++++++++++++++--------------- cores/arduino/SERCOM.h | 47 +++--- cores/arduino/SERCOM_Txn.h | 15 +- cores/arduino/SERCOM_inline.h | 114 +++----------- libraries/SPI/SPI.cpp | 91 ++++------- libraries/SPI/SPI.h | 4 +- 7 files changed, 256 insertions(+), 298 deletions(-) diff --git a/cores/arduino/RingBuffer.h b/cores/arduino/RingBuffer.h index b6989a5e0..3c6d1b95c 100644 --- a/cores/arduino/RingBuffer.h +++ b/cores/arduino/RingBuffer.h @@ -62,7 +62,7 @@ typedef RingBufferN RingBuffer; template -RingBufferN::RingBufferN( void ) +RingBufferN::RingBufferN( void ) { for (int i = 0; i < N; ++i) _aucBuffer[i] = T{}; diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 552e0b408..56497287f 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -57,6 +57,19 @@ SERCOM::SERCOM(Sercom* s) #endif // end __SAMD51__ } +void SERCOM::resetSERCOM() +{ + // UART, SPI, I2CS, and I2CM use the same SWRST and DBGCTRL bits, so this works for all modes + sercom->USART.CTRLA.bit.SWRST = 1 ; + + while ( sercom->USART.CTRLA.bit.SWRST || sercom->USART.SYNCBUSY.bit.SWRST ) + ; // Wait for both bits Software Reset from CTRLA and SYNCBUSY coming back to 0 + + // DBGCTRL is not affected by SWRST, so explicitly clear it here to ensure debug behavior is + // consistent after reset + sercom->USART.DBGCTRL.bit.DBGSTOP = 0; +} + /* ========================= * ===== Sercom UART * ========================= @@ -67,22 +80,11 @@ void SERCOM::initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint resetUART(); #ifdef USE_ZERODMA -#ifdef SERCOM0_DMAC_ID_TX int8_t id = getSercomIndex(); - if (id < 0) - return; - + if (id >= 0) { dmaSetCallbacks(SERCOM::dmaTxCallbackUART, SERCOM::dmaRxCallbackUART); - - if (_dmaConfigured) - return; - - _dmaTxTrigger = SERCOM0_DMAC_ID_TX + (id * 2); - _dmaRxTrigger = SERCOM0_DMAC_ID_RX + (id * 2); - dmaInit(_dmaTxTrigger, _dmaRxTrigger); -#else - (void)0; -#endif // SERCOM0_DMAC_ID_TX + dmaInit(id); + } #endif // USE_ZERODMA registerService(getSercomIndex(), &SERCOM::stopTransmissionUART); @@ -141,22 +143,7 @@ void SERCOM::initPads(SercomUartTXPad txPad, SercomRXPad rxPad) void SERCOM::resetUART() { - // Start the Software Reset - sercom->USART.CTRLA.bit.SWRST = 1 ; - - while ( sercom->USART.CTRLA.bit.SWRST || sercom->USART.SYNCBUSY.bit.SWRST ) - { - // Wait for both bits Software Reset from CTRLA and SYNCBUSY coming back to 0 - } -} - -void SERCOM::enableUART() -{ - //Setting the enable bit to 1 - sercom->USART.CTRLA.bit.ENABLE = 0x1u; - - //Wait for then enable bit from SYNCBUSY is equal to 0; - while(sercom->USART.SYNCBUSY.bit.ENABLE); + resetSERCOM(); } void SERCOM::flushUART() @@ -353,8 +340,12 @@ void SERCOM::initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize ch resetSPI(); #ifdef USE_ZERODMA + int8_t id = getSercomIndex(); + if (id >= 0) { dmaSetCallbacks(SERCOM::dmaTxCallbackSPI, SERCOM::dmaRxCallbackSPI); -#endif + dmaInit(id); + } +#endif // USE_ZERODMA registerService(getSercomIndex(), &SERCOM::stopTransmissionSPI); @@ -438,48 +429,6 @@ bool SERCOM::enqueueSPI(SercomTxn* txn) return true; } -void SERCOM::serviceSPI(void) -{ - if (!_spi.active || _spi.currentTxn == nullptr) - return; - - uint8_t flags = sercom->SPI.INTFLAG.reg; - - if (flags & SERCOM_SPI_INTFLAG_ERROR) - { - _spi.returnValue = SercomSpiError::BUF_OVERFLOW; - sercom->SPI.INTFLAG.reg = SERCOM_SPI_INTFLAG_ERROR; - deferStopSPI(_spi.returnValue); - return; - } - - SercomTxn* txn = _spi.currentTxn; - - if (flags & SERCOM_SPI_INTFLAG_RXC) { - uint8_t rx = sercom->SPI.DATA.reg; - if (txn->rxPtr && _spi.index > 0 && (_spi.index - 1) < _spi.length) - txn->rxPtr[_spi.index - 1] = rx; - if (_spi.index >= _spi.length) { - sercom->SPI.INTENCLR.reg = SERCOM_SPI_INTENCLR_DRE | SERCOM_SPI_INTENCLR_RXC | SERCOM_SPI_INTENCLR_ERROR; - _spi.returnValue = SercomSpiError::SUCCESS; - deferStopSPI(_spi.returnValue); - return; - } - } - - if (flags & SERCOM_SPI_INTFLAG_DRE) { - if (_spi.index < _spi.length) { - uint8_t out = 0xFF; - if (txn->txPtr) - out = txn->txPtr[_spi.index]; - sercom->SPI.DATA.reg = out; - _spi.index++; - return; - } - sercom->SPI.INTENCLR.reg = SERCOM_SPI_INTENCLR_DRE; - } -} - void SERCOM::deferStopSPI(SercomSpiError error) { _spi.returnValue = error; @@ -534,33 +483,7 @@ void SERCOM::initSPIClock(SercomSpiClockMode clockMode, uint32_t baudrate) void SERCOM::resetSPI() { - //Setting the Software Reset bit to 1 - sercom->SPI.CTRLA.bit.SWRST = 1; - - //Wait both bits Software Reset from CTRLA and SYNCBUSY are equal to 0 - while(sercom->SPI.CTRLA.bit.SWRST || sercom->SPI.SYNCBUSY.bit.SWRST); -} - -void SERCOM::enableSPI() -{ - //Setting the enable bit to 1 - sercom->SPI.CTRLA.bit.ENABLE = 1; - - while(sercom->SPI.SYNCBUSY.bit.ENABLE) - { - //Waiting then enable bit from SYNCBUSY is equal to 0; - } -} - -void SERCOM::disableSPI() -{ - while(sercom->SPI.SYNCBUSY.bit.ENABLE) - { - //Waiting then enable bit from SYNCBUSY is equal to 0; - } - - //Setting the enable bit to 0 - sercom->SPI.CTRLA.bit.ENABLE = 0; + resetSERCOM(); } void SERCOM::setDataOrderSPI(SercomDataOrder dataOrder) @@ -953,52 +876,71 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) return txn; } +// Hardware metadata structure for SERCOM peripherals - private to this file #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) - -static const struct { +// SAMD51 has separate core and slow clocks, and extended interrupt array +struct SercomData { Sercom *sercomPtr; uint8_t id_core; uint8_t id_slow; IRQn_Type irq[4]; -} sercomData[] = { + uint8_t dmaTxTrigger; + uint8_t dmaRxTrigger; + void *dataReg; // Pointer to DATA register +}; + +static const SercomData sercomData[] = { { SERCOM0, SERCOM0_GCLK_ID_CORE, SERCOM0_GCLK_ID_SLOW, - SERCOM0_0_IRQn, SERCOM0_1_IRQn, SERCOM0_2_IRQn, SERCOM0_3_IRQn }, + SERCOM0_0_IRQn, SERCOM0_1_IRQn, SERCOM0_2_IRQn, SERCOM0_3_IRQn, + SERCOM0_DMAC_ID_TX, SERCOM0_DMAC_ID_RX, (void*)&SERCOM0->I2CM.DATA.reg }, { SERCOM1, SERCOM1_GCLK_ID_CORE, SERCOM1_GCLK_ID_SLOW, - SERCOM1_0_IRQn, SERCOM1_1_IRQn, SERCOM1_2_IRQn, SERCOM1_3_IRQn }, + SERCOM1_0_IRQn, SERCOM1_1_IRQn, SERCOM1_2_IRQn, SERCOM1_3_IRQn, + SERCOM1_DMAC_ID_TX, SERCOM1_DMAC_ID_RX, (void*)&SERCOM1->I2CM.DATA.reg }, { SERCOM2, SERCOM2_GCLK_ID_CORE, SERCOM2_GCLK_ID_SLOW, - SERCOM2_0_IRQn, SERCOM2_1_IRQn, SERCOM2_2_IRQn, SERCOM2_3_IRQn }, + SERCOM2_0_IRQn, SERCOM2_1_IRQn, SERCOM2_2_IRQn, SERCOM2_3_IRQn, + SERCOM2_DMAC_ID_TX, SERCOM2_DMAC_ID_RX, (void*)&SERCOM2->I2CM.DATA.reg }, { SERCOM3, SERCOM3_GCLK_ID_CORE, SERCOM3_GCLK_ID_SLOW, - SERCOM3_0_IRQn, SERCOM3_1_IRQn, SERCOM3_2_IRQn, SERCOM3_3_IRQn }, + SERCOM3_0_IRQn, SERCOM3_1_IRQn, SERCOM3_2_IRQn, SERCOM3_3_IRQn, + SERCOM3_DMAC_ID_TX, SERCOM3_DMAC_ID_RX, (void*)&SERCOM3->I2CM.DATA.reg }, { SERCOM4, SERCOM4_GCLK_ID_CORE, SERCOM4_GCLK_ID_SLOW, - SERCOM4_0_IRQn, SERCOM4_1_IRQn, SERCOM4_2_IRQn, SERCOM4_3_IRQn }, + SERCOM4_0_IRQn, SERCOM4_1_IRQn, SERCOM4_2_IRQn, SERCOM4_3_IRQn, + SERCOM4_DMAC_ID_TX, SERCOM4_DMAC_ID_RX, (void*)&SERCOM4->I2CM.DATA.reg }, { SERCOM5, SERCOM5_GCLK_ID_CORE, SERCOM5_GCLK_ID_SLOW, - SERCOM5_0_IRQn, SERCOM5_1_IRQn, SERCOM5_2_IRQn, SERCOM5_3_IRQn }, + SERCOM5_0_IRQn, SERCOM5_1_IRQn, SERCOM5_2_IRQn, SERCOM5_3_IRQn, + SERCOM5_DMAC_ID_TX, SERCOM5_DMAC_ID_RX, (void*)&SERCOM5->I2CM.DATA.reg }, #if defined(SERCOM6) { SERCOM6, SERCOM6_GCLK_ID_CORE, SERCOM6_GCLK_ID_SLOW, - SERCOM6_0_IRQn, SERCOM6_1_IRQn, SERCOM6_2_IRQn, SERCOM6_3_IRQn }, + SERCOM6_0_IRQn, SERCOM6_1_IRQn, SERCOM6_2_IRQn, SERCOM6_3_IRQn, + SERCOM6_DMAC_ID_TX, SERCOM6_DMAC_ID_RX, (void*)&SERCOM6->I2CM.DATA.reg }, #endif #if defined(SERCOM7) { SERCOM7, SERCOM7_GCLK_ID_CORE, SERCOM7_GCLK_ID_SLOW, - SERCOM7_0_IRQn, SERCOM7_1_IRQn, SERCOM7_2_IRQn, SERCOM7_3_IRQn }, + SERCOM7_0_IRQn, SERCOM7_1_IRQn, SERCOM7_2_IRQn, SERCOM7_3_IRQn, + SERCOM7_DMAC_ID_TX, SERCOM7_DMAC_ID_RX, (void*)&SERCOM7->I2CM.DATA.reg }, #endif }; #else // end if SAMD51 (prob SAMD21) - -static const struct { +// SAMD21 has unified clock and single interrupt +struct SercomData { Sercom *sercomPtr; uint8_t clock; IRQn_Type irqn; -} sercomData[] = { - SERCOM0, GCM_SERCOM0_CORE, SERCOM0_IRQn, - SERCOM1, GCM_SERCOM1_CORE, SERCOM1_IRQn, - SERCOM2, GCM_SERCOM2_CORE, SERCOM2_IRQn, - SERCOM3, GCM_SERCOM3_CORE, SERCOM3_IRQn, + uint8_t dmaTxTrigger; + uint8_t dmaRxTrigger; + void *dataReg; // Pointer to DATA register +}; + +static const SercomData sercomData[] = { + { SERCOM0, GCM_SERCOM0_CORE, SERCOM0_IRQn, SERCOM0_DMAC_ID_TX, SERCOM0_DMAC_ID_RX, (void*)&SERCOM0->I2CM.DATA.reg }, + { SERCOM1, GCM_SERCOM1_CORE, SERCOM1_IRQn, SERCOM1_DMAC_ID_TX, SERCOM1_DMAC_ID_RX, (void*)&SERCOM1->I2CM.DATA.reg }, + { SERCOM2, GCM_SERCOM2_CORE, SERCOM2_IRQn, SERCOM2_DMAC_ID_TX, SERCOM2_DMAC_ID_RX, (void*)&SERCOM2->I2CM.DATA.reg }, + { SERCOM3, GCM_SERCOM3_CORE, SERCOM3_IRQn, SERCOM3_DMAC_ID_TX, SERCOM3_DMAC_ID_RX, (void*)&SERCOM3->I2CM.DATA.reg }, #if defined(SERCOM4) - SERCOM4, GCM_SERCOM4_CORE, SERCOM4_IRQn, + { SERCOM4, GCM_SERCOM4_CORE, SERCOM4_IRQn, SERCOM4_DMAC_ID_TX, SERCOM4_DMAC_ID_RX, (void*)&SERCOM4->I2CM.DATA.reg }, #endif #if defined(SERCOM5) - SERCOM5, GCM_SERCOM5_CORE, SERCOM5_IRQn, + { SERCOM5, GCM_SERCOM5_CORE, SERCOM5_IRQn, SERCOM5_DMAC_ID_TX, SERCOM5_DMAC_ID_RX, (void*)&SERCOM5->I2CM.DATA.reg }, #endif }; @@ -1045,13 +987,33 @@ bool SERCOM::registerService(uint8_t sercomId, ServiceFn fn) } #ifdef USE_ZERODMA -SERCOM::DmaStatus SERCOM::dmaInit(uint8_t txTrigger, uint8_t rxTrigger) +SERCOM::DmaStatus SERCOM::dmaInit(int8_t sercomId, uint8_t beatSize) { if (_dmaConfigured) return DmaStatus::Ok; - _dmaTxTrigger = txTrigger; - _dmaRxTrigger = rxTrigger; + // Validate beat size: 0=byte, 1=halfword, 2=word + if (beatSize > DMA_BEAT_SIZE_WORD) + beatSize = DMA_BEAT_SIZE_BYTE; + + // Look up DMA triggers from sercomData table +#ifdef SERCOM0_DMAC_ID_TX + if (sercomId >= 0 && sercomId < (int8_t)kSercomCount && sercomData[sercomId].dmaTxTrigger != 0) + { + _dmaTxTrigger = sercomData[sercomId].dmaTxTrigger; + _dmaRxTrigger = sercomData[sercomId].dmaRxTrigger; + } + else +#endif + { + // Fallback: calculate triggers if table lookup unavailable + _dmaTxTrigger = SERCOM0_DMAC_ID_TX + (sercomId * 2); + _dmaRxTrigger = SERCOM0_DMAC_ID_RX + (sercomId * 2); + } + + // DATA register is at the same offset (0x28) for all protocols (I2C, SPI, UART). + // Access via any union member is transparent—just use I2CM as the canonical reference. + void* dataReg = (void*)&sercom->I2CM.DATA.reg; if (!_dmaTx) _dmaTx = new Adafruit_ZeroDMA(); @@ -1088,9 +1050,9 @@ SERCOM::DmaStatus SERCOM::dmaInit(uint8_t txTrigger, uint8_t rxTrigger) _dmaRx->setCallback(_dmaRxCb); if (_dmaTxDesc == nullptr) - _dmaTxDesc = _dmaTx->addDescriptor(&_dmaDummy, (void*)&sercom->I2CM.DATA.reg, 1, DMA_BEAT_SIZE_BYTE, true, false); + _dmaTxDesc = _dmaTx->addDescriptor(&_dmaDummy, dataReg, 0, (dma_beat_size)beatSize, true, false); if (_dmaRxDesc == nullptr) - _dmaRxDesc = _dmaRx->addDescriptor((void*)&sercom->I2CM.DATA.reg, &_dmaDummy, 1, DMA_BEAT_SIZE_BYTE, false, true); + _dmaRxDesc = _dmaRx->addDescriptor(dataReg, &_dmaDummy, 0, (dma_beat_size)beatSize, false, true); if (_dmaTxDesc == nullptr || _dmaRxDesc == nullptr) { _dmaLastError = DmaStatus::DescriptorFailed; @@ -1117,7 +1079,74 @@ void SERCOM::dmaSetCallbacks(DmaCallback txCb, DmaCallback rxCb) } } -SERCOM::DmaStatus SERCOM::dmaStartDuplex(const void* txSrc, void* rxDst, void* txReg, void* rxReg, size_t len, +SERCOM::DmaStatus SERCOM::dmaStartTx(const void* src, volatile void* dstReg, size_t len) +{ + if (!_dmaConfigured || !_dmaTx) { + _dmaLastError = DmaStatus::NotConfigured; + return _dmaLastError; + } + if (src == nullptr || dstReg == nullptr) { + _dmaLastError = DmaStatus::NullPtr; + return _dmaLastError; + } + if (len == 0) { + _dmaLastError = DmaStatus::ZeroLen; + return _dmaLastError; + } + if (_dmaTxDesc == nullptr) { + _dmaLastError = DmaStatus::DescriptorFailed; + return _dmaLastError; + } + + _dmaTx->changeDescriptor(_dmaTxDesc, const_cast(src), + const_cast(dstReg), len); + + if (_dmaTx->startJob() != DMA_STATUS_OK) { + _dmaTx->abort(); + _dmaLastError = DmaStatus::StartFailed; + return _dmaLastError; + } + + _dmaTxActive = true; + _dmaLastError = DmaStatus::Ok; + return _dmaLastError; +} + +SERCOM::DmaStatus SERCOM::dmaStartRx(void* dst, volatile void* srcReg, size_t len) +{ + if (!_dmaConfigured || !_dmaRx) { + _dmaLastError = DmaStatus::NotConfigured; + return _dmaLastError; + } + if (dst == nullptr || srcReg == nullptr) { + _dmaLastError = DmaStatus::NullPtr; + return _dmaLastError; + } + if (len == 0) { + _dmaLastError = DmaStatus::ZeroLen; + return _dmaLastError; + } + if (_dmaRxDesc == nullptr) { + _dmaLastError = DmaStatus::DescriptorFailed; + return _dmaLastError; + } + + _dmaRx->changeDescriptor(_dmaRxDesc, + const_cast(srcReg), + dst, len); + + if (_dmaRx->startJob() != DMA_STATUS_OK) { + _dmaRx->abort(); + _dmaLastError = DmaStatus::StartFailed; + return _dmaLastError; + } + + _dmaRxActive = true; + _dmaLastError = DmaStatus::Ok; + return _dmaLastError; +} + +SERCOM::DmaStatus SERCOM::dmaStartDuplex(const void* txSrc, void* rxDst, volatile void* txReg, volatile void* rxReg, size_t len, const uint8_t* dummyTx) { if (len == 0) diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index f3b797d3b..6d29e021c 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -180,6 +180,14 @@ class SERCOM { public: SERCOM(Sercom* s) ; + void resetSERCOM( void ) ; + inline void enableSERCOM( void ) ; + inline void disableSERCOM( void ) ; + inline void disableInterrupts(uint8_t mask) { sercom->I2CM.INTENCLR.reg = mask; } + inline void enableInterrupts(uint8_t mask) { sercom->I2CM.INTENSET.reg = mask; } + inline uint8_t getINTFLAG( void ) const { return sercom->I2CM.INTFLAG.reg; } + inline uint16_t getSTATUS( void ) const { return sercom->I2CM.STATUS.reg; } + inline void clearINTFLAG( void ) { sercom->I2CM.INTFLAG.reg = 0xFF; } /* ========== UART ========== */ void initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint32_t baudrate=0) ; @@ -187,7 +195,8 @@ class SERCOM void initPads(SercomUartTXPad txPad, SercomRXPad rxPad) ; void resetUART( void ) ; - void enableUART( void ) ; + void enableUART( void ) { enableSERCOM(); } + void disableUART( void ) { disableSERCOM(); } void flushUART( void ) ; void clearStatusUART( void ) ; bool availableDataUART( void ) ; @@ -212,8 +221,8 @@ class SERCOM void initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize charSize, SercomDataOrder dataOrder) ; void initSPIClock(SercomSpiClockMode clockMode, uint32_t baudrate) ; void resetSPI( void ) ; - void enableSPI( void ) ; - void disableSPI( void ) ; + void enableSPI( void ) { enableSERCOM(); } + void disableSPI( void ) { disableSERCOM(); } void setDataOrderSPI(SercomDataOrder dataOrder) ; SercomDataOrder getDataOrderSPI( void ) ; void setBaudrateSPI(uint8_t divider) ; @@ -225,10 +234,18 @@ class SERCOM bool isReceiveCompleteSPI( void ) ; bool enqueueSPI(SercomTxn* txn); bool startTransmissionSPI(void); - void serviceSPI(void); void deferStopSPI(SercomSpiError error); SercomTxn* stopTransmissionSPI(void); SercomTxn* stopTransmissionSPI(SercomSpiError error); + inline SercomTxn* getCurrentTxnSPI(void) { return _spi.currentTxn; } + inline const SercomTxn* getCurrentTxnSPI(void) const { return _spi.currentTxn; } + inline size_t getTxnIndexSPI(void) const { return _spi.index; } + inline size_t getTxnLengthSPI(void) const { return _spi.length; } + inline bool isActiveSPI(void) const { return _spi.active; } + inline void setTxnIndexSPI(size_t index) { _spi.index = index; } + inline void setReturnValueSPI(SercomSpiError err) { _spi.returnValue = err; } + inline bool sendDataSPI(void); + inline bool readDataSPI(void); /* ========== WIRE ========== */ void initSlaveWIRE(uint8_t address, bool enableGeneralCall = false, uint8_t speed = 0x0) ; @@ -294,11 +311,10 @@ class SERCOM void setClockSource(int8_t idx, SercomClockSource src, bool core) { (void)idx; (void)src; (void)core; }; SercomClockSource getClockSource(void) { return SERCOM_CLOCK_SOURCE_FCPU; }; uint32_t getFreqRef(void) { return F_CPU; }; -#endif +#endif // SAMD51 vs 21 - // --- Async SERCOM scaffolding (Phase 1) --- enum class Role : uint8_t { None = 0, UART, SPI, I2C }; - using ServiceFn = void (SERCOM::*)(); + using ServiceFn = SercomTxn* (SERCOM::*)(); static bool claim(uint8_t sercomId, Role role); static void release(uint8_t sercomId); @@ -307,7 +323,6 @@ class SERCOM static void dispatchPending(void); #ifdef USE_ZERODMA - // --- SERCOM ZeroDMA helpers (Phase 1) --- using DmaCallback = void (*)(Adafruit_ZeroDMA*); enum class DmaStatus : uint8_t { Ok = 0, @@ -319,11 +334,11 @@ class SERCOM StartFailed }; - DmaStatus dmaInit(uint8_t txTrigger, uint8_t rxTrigger); + DmaStatus dmaInit(int8_t sercomId, uint8_t beatSize = 0); // beatSize: 0=byte (default), 1=halfword, 2=word void dmaSetCallbacks(DmaCallback txCb, DmaCallback rxCb); - inline DmaStatus dmaStartTx(const void* src, void* dstReg, size_t len); - inline DmaStatus dmaStartRx(void* dst, void* srcReg, size_t len); - DmaStatus dmaStartDuplex(const void* txSrc, void* rxDst, void* txReg, void* rxReg, size_t len, + DmaStatus dmaStartTx(const void* src, volatile void* dstReg, size_t len); + DmaStatus dmaStartRx(void* dst, volatile void* srcReg, size_t len); + DmaStatus dmaStartDuplex(const void* txSrc, void* rxDst, volatile void* txReg, volatile void* rxReg, size_t len, const uint8_t* dummyTx = nullptr); void dmaRelease(); void dmaResetDescriptors(); @@ -390,14 +405,10 @@ class SERCOM static std::array s_states; static std::array s_instances; static volatile uint32_t s_pendingMask; - uint8_t calculateBaudrateSynchronous(uint32_t baudrate); + uint8_t calculateBaudrateSynchronous(uint32_t baudrate) ; uint32_t division(uint32_t dividend, uint32_t divisor) ; void initClockNVIC( void ) ; -#ifdef USE_ZERODMA - void initWireDma(void); - void initUartDma(void); -#endif - void initWIRE(void); + void initWIRE(void) ; // Cached I2C master/slave configuration for fast role switching. // This can be expanded to support additional configuration options diff --git a/cores/arduino/SERCOM_Txn.h b/cores/arduino/SERCOM_Txn.h index d109abecc..ca6365c52 100644 --- a/cores/arduino/SERCOM_Txn.h +++ b/cores/arduino/SERCOM_Txn.h @@ -41,15 +41,28 @@ enum class SercomSpiError : uint8_t UNKNOWN_ERROR = 2 }; +// UART error reporting (async callbacks) +enum class SercomUartError : uint8_t +{ + SUCCESS = 0, + UNKNOWN_ERROR = 1 +}; + // I2C config flags and helpers enum : uint16_t { I2C_CFG_READ = 1u << 0, I2C_CFG_STOP = 1u << 1, I2C_CFG_CRC = 1u << 2, I2C_CFG_10BIT = 1u << 3, - I2C_CFG_RESTART = 1u << 4, + I2C_CFG_NODMA = 1u << 4, }; +// I2C STATUS register error-clear masks (write to I2CM.STATUS or I2CS.STATUS) +// Master: LENERR|SEXTTOUT|MEXTTOUT|LOWTOUT|BUSSTATE_IDLE|ARBLOST|BUSERR +static constexpr uint16_t I2CM_STATUS_ERR_CLEAR = 0x753u; +// Slave: SEXTTOUT|LOWTOUT|COLL|BUSERR (preserves HS high-speed detection) +static constexpr uint16_t I2CS_STATUS_ERR_CLEAR = 0x243u; + static inline uint16_t I2C_ADDR(uint16_t addr10) { return addr10 & 0x03FFu; } static inline uint8_t I2C_ADDR7(uint16_t addr) { return (uint8_t)(addr & 0x7Fu); } diff --git a/cores/arduino/SERCOM_inline.h b/cores/arduino/SERCOM_inline.h index 76a79a69a..4215e8f50 100644 --- a/cores/arduino/SERCOM_inline.h +++ b/cores/arduino/SERCOM_inline.h @@ -3,22 +3,33 @@ #ifdef __cplusplus -inline void SERCOM::enableWIRE( void ) +inline void SERCOM::enableSERCOM( void ) { - // I2C Master and Slave modes share the ENABLE bit function. + // UART, SPI, I2CS, and I2CM use the same enable bit sercom->I2CM.CTRLA.bit.ENABLE = 1; while (sercom->I2CM.SYNCBUSY.bit.ENABLE != 0) ; +} + +inline void SERCOM::disableSERCOM( void ) +{ + // UART, SPI, I2CS, and I2CM use the same enable bit + sercom->I2CM.CTRLA.bit.ENABLE = 0; + while (sercom->I2CM.SYNCBUSY.bit.ENABLE != 0) ; +} + +inline void SERCOM::enableWIRE( void ) +{ + enableSERCOM(); // Setting bus idle mode sercom->I2CM.STATUS.bit.BUSSTATE = 1; while (sercom->I2CM.SYNCBUSY.bit.SYSOP != 0) ; } -inline void SERCOM::disableWIRE( void ) +inline void SERCOM::deferStopWIRE(SercomWireError error) { - // I2C Master and Slave modes share the ENABLE bit function. - sercom->I2CM.CTRLA.bit.ENABLE = 0; - while (sercom->I2CM.SYNCBUSY.bit.ENABLE != 0) ; + _wire.returnValue = error; + setPending((uint8_t)getSercomIndex()); } inline bool SERCOM::sendDataWIRE( void ) @@ -99,7 +110,7 @@ inline uint8_t SERCOM::getINTFLAG( void ) const { return sercom->I2CM.INTFLAG.re inline uint16_t SERCOM::getSTATUS( void ) const { return sercom->I2CM.STATUS.reg; } inline void SERCOM::clearINTFLAG( void ) { sercom->I2CM.INTFLAG.reg = 0xFF; } -inline void SERCOM::setWireTxn(SercomTxn* txn, size_t length, bool useDma) +inline void SERCOM::setTxnWIRE(SercomTxn* txn, size_t length, bool useDma) { _wire.currentTxn = txn; _wire.txnLength = length; @@ -107,31 +118,11 @@ inline void SERCOM::setWireTxn(SercomTxn* txn, size_t length, bool useDma) _wire.useDma = useDma; } -inline void SERCOM::setWireDma(bool useDma) -{ - _wire.useDma = useDma; -} - -inline bool SERCOM::isWireDma(void) const -{ - return _wire.useDma; -} - -inline bool SERCOM::isMasterReadOperationWIRE( void ) -{ - return sercom->I2CS.STATUS.bit.DIR; -} - -inline bool SERCOM::isRXNackReceivedWIRE( void ) -{ - return sercom->I2CM.STATUS.bit.RXNACK; -} - #ifdef USE_ZERODMA inline SERCOM* SERCOM::findDmaOwner(Adafruit_ZeroDMA* dma, bool tx) { - if (dma == nullptr) - return nullptr; + if (dma == nullptr) return nullptr; + for (size_t i = 0; i < kSercomCount; ++i) { SERCOM* inst = s_instances[i]; @@ -148,71 +139,8 @@ inline SERCOM* SERCOM::findDmaOwner(Adafruit_ZeroDMA* dma, bool tx) return inst; } } - return nullptr; -} - -inline SERCOM::DmaStatus SERCOM::dmaStartTx(const void* src, void* dstReg, size_t len) -{ - if (!_dmaConfigured || !_dmaTx) { - _dmaLastError = DmaStatus::NotConfigured; - return _dmaLastError; - } - if (src == nullptr || dstReg == nullptr) { - _dmaLastError = DmaStatus::NullPtr; - return _dmaLastError; - } - if (len == 0) { - _dmaLastError = DmaStatus::ZeroLen; - return _dmaLastError; - } - if (_dmaTxDesc == nullptr) { - _dmaLastError = DmaStatus::DescriptorFailed; - return _dmaLastError; - } - - _dmaTx->changeDescriptor(_dmaTxDesc, (void*)src, dstReg, len); - - if (_dmaTx->startJob() != DMA_STATUS_OK) { - _dmaTx->abort(); - _dmaLastError = DmaStatus::StartFailed; - return _dmaLastError; - } - _dmaTxActive = true; - _dmaLastError = DmaStatus::Ok; - return _dmaLastError; -} - -inline SERCOM::DmaStatus SERCOM::dmaStartRx(void* dst, void* srcReg, size_t len) -{ - if (!_dmaConfigured || !_dmaRx) { - _dmaLastError = DmaStatus::NotConfigured; - return _dmaLastError; - } - if (dst == nullptr || srcReg == nullptr) { - _dmaLastError = DmaStatus::NullPtr; - return _dmaLastError; - } - if (len == 0) { - _dmaLastError = DmaStatus::ZeroLen; - return _dmaLastError; - } - if (_dmaRxDesc == nullptr) { - _dmaLastError = DmaStatus::DescriptorFailed; - return _dmaLastError; - } - - _dmaRx->changeDescriptor(_dmaRxDesc, srcReg, dst, len); - - if (_dmaRx->startJob() != DMA_STATUS_OK) { - _dmaRx->abort(); - _dmaLastError = DmaStatus::StartFailed; - return _dmaLastError; - } - - _dmaRxActive = true; - _dmaLastError = DmaStatus::Ok; - return _dmaLastError; + return nullptr; } inline void SERCOM::dmaTxCallbackWIRE(Adafruit_ZeroDMA* dma) diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 6b1cbfd21..4c748a969 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -59,7 +59,7 @@ void SPIClass::begin() } #ifdef USE_ZERODMA - _p_sercom->dmaInit(getDMAC_ID_TX(), getDMAC_ID_RX()); + _p_sercom->dmaInit(_p_sercom->getSercomIndex()); #endif // PIO init @@ -270,7 +270,39 @@ void SPIClass::transfer(const void *txbuf, void *rxbuf, size_t count, void SPIClass::onService(void) { - _p_sercom->serviceSPI(); + // SPI interrupt service handler - moved from SERCOM::serviceSPI() + if (!_p_sercom->isActiveSPI() || _p_sercom->getCurrentTxnSPI() == nullptr) + return; + + uint8_t flags = _p_sercom->getINTFLAG(); + + if (flags & SERCOM_SPI_INTFLAG_ERROR) + { + _p_sercom->setReturnValueSPI(SercomSpiError::BUF_OVERFLOW); + _p_sercom->clearINTFLAG(); + _p_sercom->deferStopSPI(SercomSpiError::BUF_OVERFLOW); + return; + } + + SercomTxn* txn = _p_sercom->getCurrentTxnSPI(); + + if (flags & SERCOM_SPI_INTFLAG_RXC) { + // Read completes after write, so read previous byte + bool hasMore = _p_sercom->readDataSPI(); + + if (!hasMore) { + _p_sercom->disableInterrupts(SERCOM_SPI_INTENCLR_DRE | SERCOM_SPI_INTENCLR_RXC | SERCOM_SPI_INTENCLR_ERROR); + _p_sercom->setReturnValueSPI(SercomSpiError::SUCCESS); + _p_sercom->deferStopSPI(SercomSpiError::SUCCESS); + return; + } + } + + if (flags & SERCOM_SPI_INTFLAG_DRE) { + bool hasMore = _p_sercom->sendDataSPI(); + if (!hasMore) + _p_sercom->disableInterrupts(SERCOM_SPI_INTENCLR_DRE); + } } void SPIClass::onTxnComplete(void* user, int status) @@ -303,61 +335,6 @@ void SPIClass::detachInterrupt() { // Should be disableInterrupt() } -// SPI DMA lookup works on both SAMD21 and SAMD51 - -static const struct { - volatile uint32_t *data_reg; - int dmac_id_tx; - int dmac_id_rx; -} sercomData[] = { - { &SERCOM0->SPI.DATA.reg, SERCOM0_DMAC_ID_TX, SERCOM0_DMAC_ID_RX }, - { &SERCOM1->SPI.DATA.reg, SERCOM1_DMAC_ID_TX, SERCOM1_DMAC_ID_RX }, - { &SERCOM2->SPI.DATA.reg, SERCOM2_DMAC_ID_TX, SERCOM2_DMAC_ID_RX }, - { &SERCOM3->SPI.DATA.reg, SERCOM3_DMAC_ID_TX, SERCOM3_DMAC_ID_RX }, -#if defined(SERCOM4) - { &SERCOM4->SPI.DATA.reg, SERCOM4_DMAC_ID_TX, SERCOM4_DMAC_ID_RX }, -#endif -#if defined(SERCOM5) - { &SERCOM5->SPI.DATA.reg, SERCOM5_DMAC_ID_TX, SERCOM5_DMAC_ID_RX }, -#endif -#if defined(SERCOM6) - { &SERCOM6->SPI.DATA.reg, SERCOM6_DMAC_ID_TX, SERCOM6_DMAC_ID_RX }, -#endif -#if defined(SERCOM7) - { &SERCOM7->SPI.DATA.reg, SERCOM7_DMAC_ID_TX, SERCOM7_DMAC_ID_RX }, -#endif -}; - -volatile uint32_t *SPIClass::getDataRegister(void) { - int8_t idx = _p_sercom->getSercomIndex(); - return (idx >= 0) ? sercomData[idx].data_reg: NULL; -} - -int SPIClass::getDMAC_ID_TX(void) { - int8_t idx = _p_sercom->getSercomIndex(); - return (idx >= 0) ? sercomData[idx].dmac_id_tx : -1; -} - -int SPIClass::getDMAC_ID_RX(void) { - int8_t idx = _p_sercom->getSercomIndex(); - return (idx >= 0) ? sercomData[idx].dmac_id_rx : -1; -} - -#if defined(__SAMD51__) - -// Set the SPI device's SERCOM clock CORE and SLOW clock sources. -// SercomClockSource values are an enumeration in SERCOM.h. -// This works on SAMD51 only. On SAMD21, a dummy function is declared -// in SPI.h which compiles to nothing, so user code doesn't need to check -// and conditionally compile lines for different architectures. -void SPIClass::setClockSource(SercomClockSource clk) { - int8_t idx = _p_sercom->getSercomIndex(); - _p_sercom->setClockSource(idx, clk, true); // true = set core clock - _p_sercom->setClockSource(idx, clk, false); // false = set slow clock -} - -#endif // end __SAMD51__ - #if SPI_INTERFACES_COUNT > 0 /* In case new variant doesn't define these macros, * we put here the ones for Arduino Zero. diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index 794436e3b..444f1b9e0 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -42,8 +42,8 @@ // SAMD51 has configurable MAX_SPI, else use peripheral clock default. // Update: changing MAX_SPI via compiler flags is DEPRECATED, because // this affects ALL SPI peripherals including some that should NOT be - // changed (e.g. anything using SD card). Use the setClockSource() - // function instead. This is left here for compatibility with interim code. + // changed (e.g. anything using SD card). Configure SERCOM clock source + // directly via the SERCOM API instead. This is left here for compatibility. #if !defined(MAX_SPI) #define MAX_SPI 24000000 #endif From fedf6db7ef47f75940f2c34d38f523984afe393c Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sun, 15 Feb 2026 00:11:49 -0600 Subject: [PATCH 17/79] get sercom's wire protocol working in master mode --- cores/arduino/SERCOM.cpp | 279 +++++++++++++++++++--------------- cores/arduino/SERCOM.h | 83 +++++----- cores/arduino/SERCOM_inline.h | 185 ++++++++++++++-------- 3 files changed, 322 insertions(+), 225 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 56497287f..ea8022395 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -569,49 +569,53 @@ uint8_t SERCOM::calculateBaudrateSynchronous(uint32_t baudrate) */ void SERCOM::resetWIRE() { - //I2CM OR I2CS, no matter SWRST is the same bit. + resetSERCOM(); + _wire = WireConfig{}; +} - //Setting the Software bit to 1 - sercom->I2CM.CTRLA.bit.SWRST = 1; +void SERCOM::clearQueueWIRE(void) +{ + // Drain all pending transactions from the queue without invoking callbacks + // This is needed for test teardown to ensure no stale transactions carry over + SercomTxn* txn = nullptr; + int drained = 0; + while (_txnQueue.read(txn)) { + drained++; + // Just discard - don't invoke callbacks during reset + } - //Wait both bits Software Reset from CTRLA and SYNCBUSY are equal to 0 - while(sercom->I2CM.CTRLA.bit.SWRST || sercom->I2CM.SYNCBUSY.bit.SWRST); + // Ensure wire state is completely clean + _wire.active = false; + _wire.currentTxn = nullptr; + _wire.txnIndex = 0; + _wire.txnLength = 0; + _wire.returnValue = SercomWireError::SUCCESS; + _wire.retryCount = 0; - _wire = WireConfig{}; + // Clear deferred callbacks (from slave/receive operations) + _wireDeferredCb = nullptr; + _wireDeferredUser = nullptr; + _wireDeferredLength = 0; + _wireDeferredPending = false; } + void SERCOM::initWIRE(void) { - if (!_wire.inited) { + if (_wire.inited) // If already initialized, return + return; + uint8_t idx = getSercomIndex(); initClockNVIC(); registerService(idx, static_cast(&SERCOM::stopTransmissionWIRE)); - _wire.inited = true; - } -#ifdef USE_ZERODMA - initWireDma(); -#endif -} #ifdef USE_ZERODMA -void SERCOM::initWireDma(void) -{ -#ifdef SERCOM0_DMAC_ID_TX - if (_dmaConfigured) - return; - - int8_t id = getSercomIndex(); - if (id < 0) - return; - - _dmaTxTrigger = SERCOM0_DMAC_ID_TX + (id * 2); - _dmaRxTrigger = SERCOM0_DMAC_ID_RX + (id * 2); dmaSetCallbacks(SERCOM::dmaTxCallbackWIRE, SERCOM::dmaRxCallbackWIRE); - dmaInit(_dmaTxTrigger, _dmaRxTrigger); -#else - (void)0; -#endif + if (idx >= 0) + dmaInit(idx); +#endif // USE_ZERODMA + + _wire.inited = true; // Mark as initialized last } -#endif void SERCOM::initSlaveWIRE( uint8_t ucAddress, bool enableGeneralCall, uint8_t speed ) { @@ -639,13 +643,13 @@ void SERCOM::initMasterWIRE( uint32_t baudrate ) setMasterWIRE(); } -void SERCOM::registerWireReceive(void (*cb)(void* user, int length), void* user) +void SERCOM::registerReceiveWIRE(void (*cb)(void* user, int length), void* user) { _wireDeferredCb = cb; _wireDeferredUser = user; } -void SERCOM::deferWireReceive(int length) +void SERCOM::deferReceiveWIRE(int length) { _wireDeferredLength = length; _wireDeferredPending = true; @@ -654,50 +658,36 @@ void SERCOM::deferWireReceive(int length) void SERCOM::setMasterWIRE(void) { + // Errata: do not enable QCEN when SCLSM=1 (bus error). Hs-mode requires SCLSM=1, + // so master Hs-mode must be DMA-only and STOP-only (no repeated starts). disableWIRE(); - - sercom->I2CM.CTRLB.reg = _wire.ctrlb |SERCOM_I2CM_CTRLB_QCEN; - sercom->I2CM.BAUD.reg = _wire.baud; bool sclsm = (_wire.masterSpeed == 0x2); - - // Set master mode and clock settings - sercom->I2CM.CTRLA.reg = _wire.ctrla | + sercom->I2CM.CTRLA.reg = _wire.ctrla | SERCOM_I2CM_CTRLA_MODE(I2C_MASTER_OPERATION) | - SERCOM_I2CM_CTRLA_SPEED(_wire.masterSpeed) | + SERCOM_I2CM_CTRLA_SPEED(_wire.masterSpeed) | (sclsm ? SERCOM_I2CM_CTRLA_SCLSM : 0 ); - - while (sercom->I2CM.SYNCBUSY.bit.ENABLE != 0) ; - - // Setting bus idle mode - sercom->I2CM.STATUS.bit.BUSSTATE = 1 ; - while (sercom->I2CM.SYNCBUSY.bit.SYSOP != 0) ; - - // Disable slave interrupts: address match, data ready, stop/restart. - sercom->I2CS.INTENCLR.reg = SERCOM_I2CS_INTENSET_AMATCH | - SERCOM_I2CS_INTENSET_DRDY | + sercom->I2CM.CTRLB.reg = _wire.ctrlb; + sercom->I2CM.BAUD.reg = _wire.baud; + enableWIRE(); + // Disable slave interrupts. + // Master interrupts are set in startTransmissionWIRE() when the transaction is enqueued, + // so we don't want to enable them here. + sercom->I2CS.INTENCLR.reg = SERCOM_I2CS_INTENSET_ERROR | + SERCOM_I2CS_INTENSET_AMATCH | + SERCOM_I2CS_INTENSET_DRDY | SERCOM_I2CS_INTENSET_PREC; } void SERCOM::setSlaveWIRE(void) { disableWIRE(); - - sercom->I2CS.ADDR.reg = _wire.addr; - sercom->I2CS.CTRLB.reg = _wire.ctrlb; bool sclsm = (_wire.slaveSpeed == 0x2); - - // Set master mode and clock settings - sercom->I2CS.CTRLA.reg = _wire.ctrla | - SERCOM_I2CS_CTRLA_MODE(I2C_SLAVE_OPERATION) | - SERCOM_I2CS_CTRLA_SPEED(_wire.slaveSpeed) | + sercom->I2CS.CTRLA.reg = SERCOM_I2CS_CTRLA_MODE(I2C_SLAVE_OPERATION) | + SERCOM_I2CS_CTRLA_SPEED(_wire.slaveSpeed) | (sclsm ? SERCOM_I2CS_CTRLA_SCLSM : 0 ); - - while (sercom->I2CS.SYNCBUSY.bit.ENABLE != 0) ; - - // Setting bus idle mode - sercom->I2CS.STATUS.bit.BUSSTATE = 1 ; - while (sercom->I2CS.SYNCBUSY.bit.SYSOP != 0) ; - + sercom->I2CS.CTRLB.reg = _wire.ctrlb | SERCOM_I2CS_CTRLB_AACKEN; + sercom->I2CS.ADDR.reg = _wire.addr; + enableWIRE(); // Enable slave interrupts: address match, data ready, stop/restart. sercom->I2CS.INTENSET.reg = SERCOM_I2CS_INTENSET_ERROR | // Error SERCOM_I2CS_INTENSET_PREC | // Stop @@ -710,19 +700,13 @@ void SERCOM::setBaudrateWIRE(uint32_t baudrate) // Determine speed mode based on requested baudrate const uint32_t topSpeeds[3] = {400000, 1000000, 3400000}; // {(sm/fm), (fm+), (hs)} uint8_t speedBit; - uint8_t clockStretchMode; // See: 28.6.2.4.6 (SERCOM I2C Highspeed mode) - if (baudrate <= topSpeeds[0]) { + if (baudrate <= topSpeeds[0]) speedBit = 0; // Standard/Fast mode up to 400 khz - clockStretchMode = 0; - } else if (baudrate <= topSpeeds[1]) { + else if (baudrate <= topSpeeds[1]) speedBit = 1; // Fast mode+ up to 1 Mhz - clockStretchMode = 0; - } else { - // High speed up to 3.4 Mhz - speedBit = 2; - clockStretchMode = 1; - } + else + speedBit = 2; // High speed up to 3.4 Mhz _wire.masterSpeed = speedBit; @@ -742,48 +726,73 @@ void SERCOM::setBaudrateWIRE(uint32_t baudrate) } -SercomTxn* SERCOM::startTransmissionWIRE(void) +SercomTxn* SERCOM::startTransmissionWIRE( void ) { + // Writing ADDR.ADDR drives different behavior based on BUSSTATE: + // UNKNOWN: MB and BUSERR assert and the transfer aborts. + // BUSY: The host waits until the bus is IDLE. + // IDLE: A START is generated, the address is sent, and on ACK the host holds SCL low with CLKHOLD + // set and MB asserted. + // OWNER: A repeated START is generated; if the prior transaction was a read, the ACK/NACK for the + // read is sent before the repeated START. The repeated START ADDR write must occur while MB or SB + // is set. + // Writing ADDR also clears BUSERR, ARBLOST, MB, and SB. + + if (isBusUnknownWIRE()) { + stopTransmissionWIRE(SercomWireError::BUS_STATE_UNKNOWN); + return nullptr; + } + SercomTxn* txn = nullptr; - if (!_txnQueue.peek(txn) || txn == nullptr) + + if (!_txnQueue.peek(txn)) return nullptr; + if (txn != _wire.currentTxn) + _wire.retryCount = 0; + _wire.currentTxn = txn; _wire.txnIndex = 0; _wire.txnLength = txn->length; + setDmaWIRE(false); // Reset DMA mode - let code below decide if DMA is used const bool read = (txn->config & I2C_CFG_READ) != 0; uint16_t addr = (txn->config & I2C_CFG_10BIT) ? I2C_ADDR(txn->address) : I2C_ADDR7(txn->address); addr = (uint16_t)((addr << 1) | (read ? 1u : 0u)); + bool hsMode = (_wire.masterSpeed == 0x2); + uint32_t addrReg = SERCOM_I2CM_ADDR_ADDR(addr) | + ((txn->config & I2C_CFG_10BIT) ? SERCOM_I2CM_ADDR_TENBITEN : 0) | + (hsMode ? SERCOM_I2CM_ADDR_HS : 0); - // If another master owns the bus or the last bus owner has not properly - // sent a stop, return failure early. This will prevent some misbehaved - // devices from deadlocking here at the cost of the caller being responsible - // for retrying the failed transmission. See SercomWireBusState for the - // possible bus states. - if(!isBusOwnerWIRE()) - { - if (isArbLostWIRE() && !isBusIdleWIRE()) { - stopTransmissionWIRE(SercomWireError::ARBITRATION_LOST); + if (hsMode || sercom->I2CM.CTRLA.bit.SCLSM) { +#ifndef USE_ZERODMA + stopTransmissionWIRE(SercomWireError::OTHER); + return nullptr; +#endif + if (txn->length >255) { + stopTransmissionWIRE(SercomWireError::DATA_TOO_LONG); return nullptr; } - if (isBusUnknownWIRE()) { - stopTransmissionWIRE(SercomWireError::BUS_STATE_UNKNOWN); + + if (sercom->I2CM.CTRLB.bit.QCEN) { + stopTransmissionWIRE(SercomWireError::OTHER); return nullptr; } - } - - uint32_t addrReg = SERCOM_I2CM_ADDR_ADDR(addr) | - ((txn->config & I2C_CFG_10BIT) ? SERCOM_I2CM_ADDR_TENBITEN : 0) | - ((_wire.masterSpeed == 0x2) ? SERCOM_I2CM_ADDR_HS : 0); + txn->config |= I2C_CFG_STOP; + setDmaWIRE(true); + } #ifdef USE_ZERODMA - setWireDma(txn->length > 0 && txn->length < 256 && (txn->config & I2C_CFG_STOP)); + else { + setDmaWIRE(txn->length > 0 && txn->length < 256 && + (txn->config & I2C_CFG_STOP) && + !(txn->config & I2C_CFG_NODMA)); + } - if (isWireDma()) + if (isDmaWIRE()) { if (!_dmaConfigured) - dmaInit(_dmaTxTrigger, _dmaRxTrigger); + dmaInit(getSercomIndex()); if (!_dmaConfigured || !_dmaTx || !_dmaRx) { stopTransmissionWIRE(SercomWireError::OTHER); @@ -794,10 +803,10 @@ SercomTxn* SERCOM::startTransmissionWIRE(void) } #endif - // Send start and address (non-blocking; ISR handles MB/SB) + // Send address (non-blocking; ISR handles ERROR/MB/SB) _wire.active = true; - sercom->I2CM.INTENSET.reg = SERCOM_I2CM_INTENSET_ERROR | SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; - sercom->I2CM.ADDR.reg = addrReg; + sercom->I2CM.INTENSET.reg = SERCOM_I2CM_INTENSET_ERROR | SERCOM_I2CM_INTENSET_SB | SERCOM_I2CM_INTENSET_MB; + sercom->I2CM.ADDR.reg = addrReg; // ADDR is write synchronized so just wait for the MB/SB to know when synced return txn; } @@ -813,12 +822,6 @@ bool SERCOM::enqueueWIRE(SercomTxn* txn) return true; } -void SERCOM::deferStopWIRE(SercomWireError error) -{ - _wire.returnValue = error; - setPending((uint8_t)getSercomIndex()); -} - SercomTxn* SERCOM::stopTransmissionWIRE( void ) { return stopTransmissionWIRE( _wire.returnValue ); @@ -831,46 +834,76 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) // Retry/backoff policy is intentionally deferred; a future change may add // a retry budget or tick-based delay if needed. - SercomTxn* txn = nullptr; - _txnQueue.peek(txn); + SercomTxn* txn = _wire.currentTxn; + SercomTxn* next = nullptr; + + constexpr uint8_t kMaxWireRetries = 3; if (error == SercomWireError::BUS_STATE_UNKNOWN) { + if (_wire.retryCount < kMaxWireRetries) { + ++_wire.retryCount; sercom->I2CM.STATUS.bit.BUSSTATE = 1; - while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; - - if (txn) startTransmissionWIRE(); - return txn; + } } if (error == SercomWireError::ARBITRATION_LOST || error == SercomWireError::BUS_ERROR) { + if (_wire.retryCount < kMaxWireRetries) { + ++_wire.retryCount; sercom->I2CM.STATUS.bit.ARBLOST = 1; // Clear arbitration lost flag sercom->I2CM.INTFLAG.reg = SERCOM_I2CM_INTFLAG_ERROR; - - if (txn) startTransmissionWIRE(); - return txn; + } } - // Callbacks are expected to run in non-ISR context (main loop/PendSV). - if (_txnQueue.read(txn) && txn != nullptr) { - _wire.active = false; - _wire.currentTxn = nullptr; - if (txn->onComplete) - txn->onComplete(txn->user, static_cast(error)); + if (error == SercomWireError::BUS_STATE_UNKNOWN || + error == SercomWireError::ARBITRATION_LOST || + error == SercomWireError::BUS_ERROR) { + _wire.retryCount = 0; } - SercomTxn* next = nullptr; - - if (_txnQueue.peek(next) && next) - startTransmissionWIRE(); + // Callbacks are expected to run in non-ISR context (main loop/PendSV). + if (txn &&txn->onComplete) + txn->onComplete(txn->user, static_cast(error)); + if(isMasterWIRE()) + _txnQueue.read(txn); // remove the completed transaction from the queue + else { + // Deliver deferred WIRE callback outside the SERCOM ISR (from PendSV). + // This avoids running user code in the hardware interrupt context. if (_wireDeferredPending && _wireDeferredCb) { _wireDeferredPending = false; _wireDeferredCb(_wireDeferredUser, _wireDeferredLength); + } + } + + _wire.retryCount = 0; + _wire.active = false; + _wire.currentTxn = nullptr; + + bool isMaster = isMasterWIRE(); + + if (_txnQueue.peek(next) && isMaster) { + // Undocumented HW limitation: DMA transfers must terminate with STOP and bus release. + // After a DMA write, the host holds the bus ~7.33 us before the next transfer (Sr window). + // Writing ADDR during that window leaves the bus in an undefined state and breaks + // subsequent DMA/non-DMA transactions. To avoid this, we must wait for BUSSTATE + // to return to IDLE after a STOP returning the hardware to a known state. + // At the tested 48 MHz, this busy-wait is ~350 cycles corresponding to a 3.5 us delay + // at 100 MHz. Thus, we only wait when needed for the next transaction and not on every STOP. + if (txn) { + if (txn->config & I2C_CFG_STOP) + while (sercom->I2CM.STATUS.bit.BUSSTATE > 0x1) ; + } + + startTransmissionWIRE(); + } else if (isMaster) { + sercom->I2CM.INTENCLR.reg = SERCOM_I2CM_INTENCLR_ERROR | + SERCOM_I2CM_INTENCLR_MB | + SERCOM_I2CM_INTENCLR_SB; } return txn; diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index 6d29e021c..d489339f0 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -251,49 +251,53 @@ class SERCOM void initSlaveWIRE(uint8_t address, bool enableGeneralCall = false, uint8_t speed = 0x0) ; void initSlaveWIRE(uint16_t address, bool enableGeneralCall = false, uint8_t speed = 0x0, bool enable10Bit = false) ; void initMasterWIRE(uint32_t baudrate) ; - inline void setWireTxn(SercomTxn* txn, size_t length, bool useDma); - inline void setWireDma(bool useDma); - inline bool isWireDma(void) const; - void registerWireReceive(void (*cb)(void* user, int length), void* user); - void deferWireReceive(int length); + inline void setTxnWIRE(SercomTxn* txn, size_t length, bool useDma); + inline void setDmaWIRE(bool useDma) { _wire.useDma = useDma; } + inline bool isDmaWIRE(void) const { return _wire.useDma; } + void registerReceiveWIRE(void (*cb)(void* user, int length), void* user); + void deferReceiveWIRE(int length); void setSlaveWIRE( void ) ; void setMasterWIRE( void ) ; void resetWIRE( void ) ; + void clearQueueWIRE( void ) ; inline void enableWIRE( void ) ; - inline void disableWIRE( void ) ; + inline void disableWIRE( void ) { disableSERCOM(); } void setBaudrateWIRE(uint32_t baudrate) ; - inline void prepareNackBitWIRE( void ) ; - inline void prepareAckBitWIRE( void ) ; - inline void prepareCommandBitsWire(uint8_t cmd) ; - bool startTransmissionWIRE( void ) ; + inline void prepareNackBitWIRE( void ) { sercom->I2CM.CTRLB.bit.ACKACT = 1; } + inline void prepareAckBitWIRE( void ) { sercom->I2CM.CTRLB.bit.ACKACT = 0; } + inline void prepareCommandBitsWIRE(uint8_t cmd) ; + SercomTxn* startTransmissionWIRE( void ) ; bool startTransmissionWIRE( uint8_t address, SercomWireReadWriteFlag flag ) = delete ; SercomTxn* stopTransmissionWIRE( void ) ; SercomTxn* stopTransmissionWIRE( SercomWireError error ) ; bool enqueueWIRE(SercomTxn* txn); void deferStopWIRE(SercomWireError error); - inline uint8_t getINTFLAG( void ) const; - inline uint16_t getSTATUS( void ) const; - inline void clearINTFLAG( void ); + inline bool sendDataWIRE( void ) ; - inline bool isMasterWIRE( void ) ; - inline bool isSlaveWIRE( void ) ; - inline bool isBusIdleWIRE( void ) ; - inline bool isBusOwnerWIRE( void ) ; - inline bool isBusUnknownWIRE( void ) ; - inline bool isArbLostWIRE( void ) ; - inline bool isBusBusyWIRE( void ) ; - inline bool isDataReadyWIRE( void ) ; - inline bool isStopDetectedWIRE( void ) ; - inline bool isRestartDetectedWIRE( void ) ; - inline bool isAddressMatch( void ) ; - inline bool isMasterReadOperationWIRE( void ) ; - inline bool isRXNackReceivedWIRE( void ) ; - inline int availableWIRE( void ) ; + inline bool isMasterWIRE( void ) { return sercom->I2CM.CTRLA.bit.MODE == I2C_MASTER_OPERATION; } + inline bool isSlaveWIRE( void ) { return sercom->I2CS.CTRLA.bit.MODE == I2C_SLAVE_OPERATION; } + inline bool isBusIdleWIRE( void ) { return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_IDLE_STATE; } + inline bool isBusOwnerWIRE( void ) { return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_OWNER_STATE; } + inline bool isBusUnknownWIRE( void ) { return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_UNKNOWN_STATE; } + inline bool isArbLostWIRE( void ) { return sercom->I2CM.STATUS.bit.ARBLOST == 1; } + inline bool isBusBusyWIRE( void ) { return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_BUSY_STATE; } + inline bool isDataReadyWIRE( void ) { return sercom->I2CS.INTFLAG.bit.DRDY; } + inline bool isStopDetectedWIRE( void ) { return sercom->I2CS.INTFLAG.bit.PREC; } + inline bool isRestartDetectedWIRE( void ) { return sercom->I2CS.STATUS.bit.SR; } + inline bool isAddressMatch( void ) { return sercom->I2CS.INTFLAG.bit.AMATCH; } + inline bool isMasterReadOperationWIRE( void ) { return sercom->I2CS.STATUS.bit.DIR; } + inline bool isRXNackReceivedWIRE( void ) { return sercom->I2CM.STATUS.bit.RXNACK; } + inline int availableWIRE( void ) { return isMasterWIRE() ? sercom->I2CM.INTFLAG.bit.SB : sercom->I2CS.INTFLAG.bit.DRDY; } inline bool readDataWIRE( void ); - inline void writeDataWIRE(uint8_t data); - inline uint8_t readDataWIREByte(void); - + inline SercomTxn* getCurrentTxnWIRE(void) { return _wire.currentTxn; } + inline const SercomTxn* getCurrentTxnWIRE(void) const { return _wire.currentTxn; } + inline size_t getTxnIndexWIRE(void) const { return _wire.txnIndex; } + inline size_t getTxnLengthWIRE(void) const { return _wire.txnLength; } + + inline bool isDBGSTOP( void ) const { return sercom->I2CM.DBGCTRL.bit.DBGSTOP; } + inline void setDBGSTOP( bool stop ) { sercom->I2CM.DBGCTRL.bit.DBGSTOP = stop; } + inline Sercom* getSercom() const { return sercom; } int8_t getSercomIndex(void) ; uint32_t getSercomFreqRef(void) ; @@ -415,15 +419,16 @@ class SERCOM // as needed in the future. For now, it just provides default support // for (hs) mode and DMA. struct WireConfig { - uint32_t ctrla = 0x00000002; // default CTRLA value: auto ENABLE - uint32_t ctrlb = 0x00000500; // default CTRLB value: SMEN (both) | AACKEN (Slave only) - uint32_t baud = 0x000000FF; // default to lowest supported speed - uint32_t addr = 0x00000000; // default address no GCEN, no ADDRMASK, 7-bit address only - uint8_t masterSpeed = 0x0; // default to lowest speed - uint8_t slaveSpeed = 0x0; // default to lowest speed - bool inited = false; // whether initMaster/SlaveWIRE has been called - bool useDma = false; // per transaction DMA use flag for Host/Client modes - bool active = false; // active transaction in progress + uint32_t ctrla = 0x00000000; // default CTRLA value: auto ENABLE + uint32_t ctrlb = SERCOM_I2CM_CTRLB_SMEN; // default CTRLB value: SMEN + uint32_t baud = 0x000000FF; // default to lowest supported speed + uint32_t addr = 0x00000000; // default address no GCEN, no ADDRMASK, 7-bit address only + uint8_t masterSpeed = 0x0; // default to lowest speed + uint8_t slaveSpeed = 0x0; // default to lowest speed + bool inited = false; // whether initMaster/SlaveWIRE has been called + bool useDma = false; // per transaction DMA use flag for Host/Client modes + bool active = false; // active transaction in progress + uint8_t retryCount = 0; // retry count for recoverable bus errors SercomWireError returnValue = SercomWireError::SUCCESS; SercomTxn* currentTxn = nullptr; size_t txnIndex = 0; diff --git a/cores/arduino/SERCOM_inline.h b/cores/arduino/SERCOM_inline.h index 4215e8f50..c278d7090 100644 --- a/cores/arduino/SERCOM_inline.h +++ b/cores/arduino/SERCOM_inline.h @@ -35,80 +35,111 @@ inline void SERCOM::deferStopWIRE(SercomWireError error) inline bool SERCOM::sendDataWIRE( void ) { SercomTxn* txn = _wire.currentTxn; - if (txn && txn->txPtr == nullptr) return false; + if (txn == nullptr || txn->txPtr == nullptr) return false; #ifdef USE_ZERODMA - if (isWireDma()) { - if (!_dmaTxActive) - dmaStartTx(txn->txPtr, &sercom->I2CM.DATA.reg, _wire.txnLength); - return true; + if (isDmaWIRE()) { + DmaStatus value = DmaStatus::StartFailed; + if (!_dmaTxActive && !_dmaRxActive) + value = dmaStartTx(txn->txPtr, &sercom->I2CM.DATA.reg, _wire.txnLength); + return value == DmaStatus::Ok; } #endif - if (_wire.txnIndex < _wire.txnLength) { - sercom->I2CM.DATA.bit.DATA = txn->txPtr[_wire.txnIndex++]; - return true; - } - - return false; -} -inline bool SERCOM::isMasterWIRE( void ) { return sercom->I2CM.CTRLA.bit.MODE == I2C_MASTER_OPERATION; } -inline bool SERCOM::isSlaveWIRE( void ) { return sercom->I2CS.CTRLA.bit.MODE == I2C_SLAVE_OPERATION; } + sercom->I2CM.DATA.reg = txn->txPtr[_wire.txnIndex++]; -inline bool SERCOM::isBusIdleWIRE( void ) { return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_IDLE_STATE; } -inline bool SERCOM::isBusOwnerWIRE( void ) { return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_OWNER_STATE; } -inline bool SERCOM::isBusUnknownWIRE( void ) { return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_UNKNOWN_STATE; } -inline bool SERCOM::isArbLostWIRE( void ) { return sercom->I2CM.STATUS.bit.ARBLOST == 1; } -inline bool SERCOM::isBusBusyWIRE( void ) { return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_BUSY_STATE; } -inline bool SERCOM::isDataReadyWIRE( void ) { return sercom->I2CS.INTFLAG.bit.DRDY; } -inline bool SERCOM::isStopDetectedWIRE( void ) { return sercom->I2CS.INTFLAG.bit.PREC; } -inline bool SERCOM::isRestartDetectedWIRE( void ) { return sercom->I2CS.INTFLAG.bit.SR; } -inline bool SERCOM::isAddressMatch( void ) { return sercom->I2CS.INTFLAG.bit.AMATCH; } + if(isMasterWIRE()) + while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; // Wait for DATA to sync and clear MB + + // Return false when the last byte has been consumed so the caller can + // issue STOP / complete the transaction without waiting for another SB. + return (_wire.txnIndex < _wire.txnLength); +} -inline void SERCOM::prepareNackBitWIRE( void ) { sercom->I2CM.CTRLB.bit.ACKACT = 1; } -inline void SERCOM::prepareAckBitWIRE( void ) { sercom->I2CM.CTRLB.bit.ACKACT = 0; } -inline void SERCOM::prepareCommandBitsWire(uint8_t cmd) +inline void SERCOM::prepareCommandBitsWIRE(uint8_t cmd) { - if (isMasterWIRE()) { sercom->I2CM.CTRLB.bit.CMD = cmd; - while (sercom->I2CM.SYNCBUSY.bit.SYSOP) - { - // Waiting for synchronization + if (isMasterWIRE()) + while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; // Waiting for synchronization } - } else { - sercom->I2CS.CTRLB.bit.CMD = cmd; - } -} -inline int SERCOM::availableWIRE( void ) { return isMasterWIRE() ? sercom->I2CM.INTFLAG.bit.SB : sercom->I2CS.INTFLAG.bit.DRDY; } inline bool SERCOM::readDataWIRE( void ) { SercomTxn* txn = _wire.currentTxn; - if (txn && txn->rxPtr == nullptr) return false; + if (txn == nullptr || txn->rxPtr == nullptr) return false; + +#ifdef USE_ZERODMA + if (isDmaWIRE()) { + DmaStatus value = DmaStatus::StartFailed; + if (!_dmaRxActive && !_dmaTxActive) + value = dmaStartRx(txn->rxPtr, &sercom->I2CM.DATA.reg, _wire.txnLength); + return value == DmaStatus::Ok; + } +#endif + + bool isMaster = isMasterWIRE(); + + if (isMaster) { + if (_wire.txnIndex == (_wire.txnLength - 1)) { + uint8_t cmd = txn->config & I2C_CFG_STOP ? WIRE_MASTER_ACT_STOP : WIRE_MASTER_ACT_NO_ACTION; + sercom->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | SERCOM_I2CM_CTRLB_CMD(cmd); // NACK the last byte and send STOP if requested + if (cmd == WIRE_MASTER_ACT_STOP) + while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; // Wait for CMD to sync and clear SB + } + else + prepareAckBitWIRE(); // ACK bytes otherwise for non-SCLSM mode + } + + // Read DATA register (clears SB in Smart Mode) + txn->rxPtr[_wire.txnIndex++] = sercom->I2CM.DATA.reg; - if (isMasterWIRE() && (_wire.txnIndex + 1 >= _wire.txnLength)) - sercom->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; - else - sercom->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + if(isMaster) + while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; // Wait for DATA to sync and clear SB + + return (_wire.txnIndex < _wire.txnLength); +} + +inline bool SERCOM::sendDataSPI(void) +{ + SercomTxn* txn = _spi.currentTxn; + if (txn == nullptr || txn->txPtr == nullptr) return false; #ifdef USE_ZERODMA - if (isWireDma()) { - if (!_dmaRxActive) - dmaStartRx(txn->rxPtr, &sercom->I2CM.DATA.reg, _wire.txnLength); - return true; + if (_spi.useDma) { + DmaStatus value = DmaStatus::StartFailed; + if (!_dmaTxActive && !_dmaRxActive) + value = dmaStartTx(txn->txPtr, &sercom->SPI.DATA.reg, _spi.length); + return value == DmaStatus::Ok; } #endif - if ( _wire.txnIndex >= _wire.txnLength) - return false; + // Byte-by-byte: Write DATA register + sercom->SPI.DATA.bit.DATA = txn->txPtr[_spi.index++]; - txn->rxPtr[_wire.txnIndex++] = sercom->I2CM.DATA.bit.DATA; - return true; + // Return false when last byte consumed so caller can complete transaction + return (_spi.index < _spi.length); } -inline uint8_t SERCOM::getINTFLAG( void ) const { return sercom->I2CM.INTFLAG.reg; } -inline uint16_t SERCOM::getSTATUS( void ) const { return sercom->I2CM.STATUS.reg; } -inline void SERCOM::clearINTFLAG( void ) { sercom->I2CM.INTFLAG.reg = 0xFF; } +inline bool SERCOM::readDataSPI(void) +{ + SercomTxn* txn = _spi.currentTxn; + if (txn == nullptr || txn->rxPtr == nullptr) return false; + +#ifdef USE_ZERODMA + if (_spi.useDma) { + DmaStatus value = DmaStatus::StartFailed; + if (!_dmaRxActive && !_dmaTxActive) + value = dmaStartRx(txn->rxPtr, &sercom->SPI.DATA.reg, _spi.length); + return value == DmaStatus::Ok; + } +#endif + + // Byte-by-byte: Read DATA register + txn->rxPtr[_spi.index - 1] = sercom->SPI.DATA.bit.DATA; + + // Return false when all bytes consumed + return (_spi.index < _spi.length); +} inline void SERCOM::setTxnWIRE(SercomTxn* txn, size_t length, bool useDma) { @@ -148,13 +179,15 @@ inline void SERCOM::dmaTxCallbackWIRE(Adafruit_ZeroDMA* dma) SERCOM* inst = findDmaOwner(dma, true); if (!inst) return; - if (inst->isMasterWIRE()) - inst->sercom->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(WIRE_MASTER_ACT_STOP); - else - inst->sercom->I2CS.CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); - inst->_wire.returnValue = SercomWireError::SUCCESS; + // When using ADDR.LENEN mode, the hardware automatically generates STOP + // after ADDR.LEN bytes are transferred (datasheet §28.6.4.1.2). + // If a NACK TOPis received by the client for a host write transaction before + // ADDR.LEN bytes, a STOP will be automatically generated and the length error + // (STATUS.LENERR) will be raised along with the INTFLAG.ERROR interrupt.re + + inst->_wire.txnIndex = inst->_wire.txnLength; inst->_dmaTxActive = false; - SERCOM::setPending((uint8_t)inst->getSercomIndex()); + inst->deferStopWIRE(SercomWireError::SUCCESS); } inline void SERCOM::dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma) @@ -162,14 +195,14 @@ inline void SERCOM::dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma) SERCOM* inst = findDmaOwner(dma, false); if (!inst) return; - if (inst->isMasterWIRE()) - inst->sercom->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(WIRE_MASTER_ACT_STOP); - else - inst->sercom->I2CS.CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); + // When using ADDR.LENEN mode, the hardware automatically generates NACK+STOP + // after ADDR.LEN bytes are transferred (datasheet §28.6.4.1.2). + // Do NOT issue a manual STOP command - it conflicts with the automatic sequence. + // The STOP is generated by hardware, not by software CMD write. + inst->_wire.txnIndex = inst->_wire.txnLength; - inst->_wire.returnValue = SercomWireError::SUCCESS; inst->_dmaRxActive = false; - SERCOM::setPending((uint8_t)inst->getSercomIndex()); + inst->deferStopWIRE(SercomWireError::SUCCESS); } inline void SERCOM::dmaTxCallbackSPI(Adafruit_ZeroDMA* dma) @@ -179,6 +212,7 @@ inline void SERCOM::dmaTxCallbackSPI(Adafruit_ZeroDMA* dma) inst->_spi.dmaTxDone = true; if (inst->_spi.dmaNeedRx && !inst->_spi.dmaRxDone) return; + inst->_dmaTxActive = false; inst->_spi.returnValue = SercomSpiError::SUCCESS; SERCOM::setPending((uint8_t)inst->getSercomIndex()); } @@ -190,9 +224,34 @@ inline void SERCOM::dmaRxCallbackSPI(Adafruit_ZeroDMA* dma) inst->_spi.dmaRxDone = true; if (inst->_spi.dmaNeedTx && !inst->_spi.dmaTxDone) return; + inst->_dmaRxActive = false; inst->_spi.returnValue = SercomSpiError::SUCCESS; SERCOM::setPending((uint8_t)inst->getSercomIndex()); } + +inline void SERCOM::dmaTxCallbackUART(Adafruit_ZeroDMA* dma) +{ + SERCOM* inst = findDmaOwner(dma, true); + if (!inst) return; + inst->_uart.dmaTxDone = true; + if (inst->_uart.dmaNeedRx && !inst->_uart.dmaRxDone) + return; + inst->_dmaTxActive = false; + inst->_uart.returnValue = SercomUartError::SUCCESS; + SERCOM::setPending((uint8_t)inst->getSercomIndex()); +} + +inline void SERCOM::dmaRxCallbackUART(Adafruit_ZeroDMA* dma) +{ + SERCOM* inst = findDmaOwner(dma, false); + if (!inst) return; + inst->_uart.dmaRxDone = true; + if (inst->_uart.dmaNeedTx && !inst->_uart.dmaTxDone) + return; + inst->_dmaRxActive = false; + inst->_uart.returnValue = SercomUartError::SUCCESS; + SERCOM::setPending((uint8_t)inst->getSercomIndex()); +} #endif #endif // __cplusplus From f1ffbccace7a4c37a5259496e2d4f7181acf3864 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sun, 15 Feb 2026 00:12:51 -0600 Subject: [PATCH 18/79] clean up SPI --- cores/arduino/SERCOM_inline.h | 8 ++++---- libraries/SPI/SPI.h | 14 -------------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/cores/arduino/SERCOM_inline.h b/cores/arduino/SERCOM_inline.h index c278d7090..0a713614d 100644 --- a/cores/arduino/SERCOM_inline.h +++ b/cores/arduino/SERCOM_inline.h @@ -58,10 +58,10 @@ inline bool SERCOM::sendDataWIRE( void ) inline void SERCOM::prepareCommandBitsWIRE(uint8_t cmd) { - sercom->I2CM.CTRLB.bit.CMD = cmd; + sercom->I2CM.CTRLB.bit.CMD = cmd; if (isMasterWIRE()) while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; // Waiting for synchronization - } +} inline bool SERCOM::readDataWIRE( void ) { @@ -193,8 +193,8 @@ inline void SERCOM::dmaTxCallbackWIRE(Adafruit_ZeroDMA* dma) inline void SERCOM::dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma) { SERCOM* inst = findDmaOwner(dma, false); - if (!inst) return; - + if (!inst) return; + // When using ADDR.LENEN mode, the hardware automatically generates NACK+STOP // after ADDR.LEN bytes are transferred (datasheet §28.6.4.1.2). // Do NOT issue a manual STOP command - it conflicts with the automatic sequence. diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index 444f1b9e0..d0afa90b9 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -142,20 +142,6 @@ class SPIClass { void setDataMode(uint8_t uc_mode); void setClockDivider(uint8_t uc_div); - // SERCOM lookup functions are available on both SAMD51 and 21. - volatile uint32_t *getDataRegister(void); - int getDMAC_ID_TX(void); - int getDMAC_ID_RX(void); - uint8_t getSercomIndex(void) { return _p_sercom->getSercomIndex(); }; -#if defined(__SAMD51__) - // SERCOM clock source override is available only on SAMD51. - void setClockSource(SercomClockSource clk); -#else - // On SAMD21, this compiles to nothing, so user code doesn't need to - // check and conditionally compile lines for different architectures. - void setClockSource(SercomClockSource clk) { (void)clk; }; -#endif // end __SAMD51__ - private: void config(SPISettings settings); From d186cabecb8bff236877fb307caca9570d93813b Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sun, 15 Feb 2026 14:25:53 -0600 Subject: [PATCH 19/79] finish refactor of Wire to fully use async SERCOM --- cores/arduino/SERCOM.cpp | 3 + cores/arduino/SERCOM.h | 2 +- cores/arduino/SERCOM_inline.h | 41 +++-- libraries/Wire/Wire.cpp | 293 +++++++++------------------------- libraries/Wire/Wire.h | 181 ++++++++++++++++++++- 5 files changed, 287 insertions(+), 233 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index ea8022395..37337432b 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -865,6 +865,9 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) _wire.retryCount = 0; } + if(isMasterWIRE()) + while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; // Wait for DATA to sync from last transaction + // Callbacks are expected to run in non-ISR context (main loop/PendSV). if (txn &&txn->onComplete) txn->onComplete(txn->user, static_cast(error)); diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index d489339f0..17d54f9a5 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -251,7 +251,7 @@ class SERCOM void initSlaveWIRE(uint8_t address, bool enableGeneralCall = false, uint8_t speed = 0x0) ; void initSlaveWIRE(uint16_t address, bool enableGeneralCall = false, uint8_t speed = 0x0, bool enable10Bit = false) ; void initMasterWIRE(uint32_t baudrate) ; - inline void setTxnWIRE(SercomTxn* txn, size_t length, bool useDma); + inline void setTxnWIRE(SercomTxn* txn); inline void setDmaWIRE(bool useDma) { _wire.useDma = useDma; } inline bool isDmaWIRE(void) const { return _wire.useDma; } void registerReceiveWIRE(void (*cb)(void* user, int length), void* user); diff --git a/cores/arduino/SERCOM_inline.h b/cores/arduino/SERCOM_inline.h index 0a713614d..9f6502ff9 100644 --- a/cores/arduino/SERCOM_inline.h +++ b/cores/arduino/SERCOM_inline.h @@ -46,10 +46,8 @@ inline bool SERCOM::sendDataWIRE( void ) } #endif + // Wait for DATA to sync out of the ISR and clear MB sercom->I2CM.DATA.reg = txn->txPtr[_wire.txnIndex++]; - - if(isMasterWIRE()) - while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; // Wait for DATA to sync and clear MB // Return false when the last byte has been consumed so the caller can // issue STOP / complete the transaction without waiting for another SB. @@ -83,19 +81,21 @@ inline bool SERCOM::readDataWIRE( void ) if (_wire.txnIndex == (_wire.txnLength - 1)) { uint8_t cmd = txn->config & I2C_CFG_STOP ? WIRE_MASTER_ACT_STOP : WIRE_MASTER_ACT_NO_ACTION; sercom->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | SERCOM_I2CM_CTRLB_CMD(cmd); // NACK the last byte and send STOP if requested - if (cmd == WIRE_MASTER_ACT_STOP) - while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; // Wait for CMD to sync and clear SB } else prepareAckBitWIRE(); // ACK bytes otherwise for non-SCLSM mode } + else { + // Slave mode: set ACKACT BEFORE reading DATA (SMEN auto-receives based on ACKACT) + if (_wire.txnIndex >= _wire.txnLength) + prepareNackBitWIRE(); // NACK if buffer full + else + prepareAckBitWIRE(); // ACK if room available + } - // Read DATA register (clears SB in Smart Mode) + // Read DATA register (accesses auto-trigger bus operation based on ACKACT/SMEN) txn->rxPtr[_wire.txnIndex++] = sercom->I2CM.DATA.reg; - if(isMaster) - while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; // Wait for DATA to sync and clear SB - return (_wire.txnIndex < _wire.txnLength); } @@ -141,12 +141,29 @@ inline bool SERCOM::readDataSPI(void) return (_spi.index < _spi.length); } -inline void SERCOM::setTxnWIRE(SercomTxn* txn, size_t length, bool useDma) +inline void SERCOM::setTxnWIRE(SercomTxn* txn) { _wire.currentTxn = txn; - _wire.txnLength = length; _wire.txnIndex = 0; - _wire.useDma = useDma; + + if (txn) + _wire.txnLength = txn->length; +#ifdef USE_ZERODMA + setDmaWIRE((txn && txn->length > 0 && txn->length < 256) || sercom->I2CM.CTRLA.bit.SCLSM); + + if (isDmaWIRE()) + _wire.txnLength = (_wire.txnLength < 255u) ? _wire.txnLength : 255u; +#else + _wire.useDma = false; + + if (sercom->I2CM.CTRLA.bit.SCLSM) + _wire.currentTxn = nullptr; // SCLSM requires DMA for proper operation (true for master; I think for slave) +#endif + if (!_wire.active) + _wire.retryCount = 0; + + _wire.returnValue = SercomWireError::SUCCESS; + _wire.active = true; } #ifdef USE_ZERODMA diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index 9066f4a97..9b33ab869 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -41,6 +41,9 @@ TwoWire::TwoWire(SERCOM * s, uint8_t pinSDA, uint8_t pinSCL) rxIndex = 0; txLength = 0; txIndex = 0; + txExternalPtr = nullptr; + txExternalLength = 0; + txExternalActive = false; masterIndex = 0; awaitingAddressAck = false; txnDone = false; @@ -53,40 +56,37 @@ TwoWire::TwoWire(SERCOM * s, uint8_t pinSDA, uint8_t pinSCL) void TwoWire::begin(void) { //Master Mode + pinPeripheral(_uc_pinSDA, g_APinDescription[_uc_pinSDA].ulPinType); + pinPeripheral(_uc_pinSCL, g_APinDescription[_uc_pinSCL].ulPinType); + sercom->initMasterWIRE(TWI_CLOCK); sercom->enableWIRE(); - sercom->registerWireReceive(&TwoWire::onDeferredReceive, this); +} +void TwoWire::begin(uint16_t address, bool enableGeneralCall, uint8_t speed, bool enable10Bit) { + //Slave mode pinPeripheral(_uc_pinSDA, g_APinDescription[_uc_pinSDA].ulPinType); pinPeripheral(_uc_pinSCL, g_APinDescription[_uc_pinSCL].ulPinType); -} -void TwoWire::begin(uint8_t address, bool enableGeneralCall) { - //Slave mode - sercom->initSlaveWIRE(address, enableGeneralCall); + sercom->initSlaveWIRE(address, enableGeneralCall, speed, enable10Bit); sercom->enableWIRE(); - sercom->registerWireReceive(&TwoWire::onDeferredReceive, this); + sercom->registerReceiveWIRE(&TwoWire::onDeferredReceive, this); +} - pinPeripheral(_uc_pinSDA, g_APinDescription[_uc_pinSDA].ulPinType); - pinPeripheral(_uc_pinSCL, g_APinDescription[_uc_pinSCL].ulPinType); +void TwoWire::begin(uint8_t address, bool enableGeneralCall) { + begin(static_cast(address), enableGeneralCall); } void TwoWire::setClock(uint32_t baudrate) { - sercom->disableWIRE(); - sercom->initMasterWIRE(baudrate); - sercom->enableWIRE(); + sercom->setBaudrateWIRE(baudrate); } void TwoWire::end() { sercom->disableWIRE(); } -uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit) -{ - return requestFrom(address, quantity, static_cast(nullptr), stopBit); -} - -uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, uint8_t* rxBuffer, bool stopBit) +uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, uint8_t* rxBuffer, bool stopBit, + void (*onComplete)(void* user, int status), void* user) { if(quantity == 0) { @@ -111,8 +111,8 @@ uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, uint8_t* rxBuffer txn.length = quantity; txn.txPtr = nullptr; txn.rxPtr = rxBufferPtr; - txn.onComplete = &TwoWire::onTxnComplete; - txn.user = this; + txn.onComplete = onComplete ? onComplete : &TwoWire::onTxnComplete; + txn.user = onComplete ? user : this; txnDone = false; txnStatus = static_cast(SercomWireError::SUCCESS); masterIndex = 0; @@ -121,20 +121,24 @@ uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, uint8_t* rxBuffer if (!sercom->enqueueWIRE(&txn)) return 0; - while (!txnDone) { - yield(); - } + if (!onComplete) { + while (!txnDone) { + yield(); + } - if (txnStatus != static_cast(SercomWireError::SUCCESS)) - return 0; + if (txnStatus != static_cast(SercomWireError::SUCCESS)) + return 0; - rxLength = quantity; - return rxLength; + rxLength = quantity; + return rxLength; + } + + return quantity; } uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity) { - return requestFrom(address, quantity, true); + return requestFrom(address, quantity, nullptr, true); } void TwoWire::beginTransmission(uint8_t address) { @@ -142,6 +146,9 @@ void TwoWire::beginTransmission(uint8_t address) { txAddress = address; txLength = 0; txIndex = 0; + txExternalPtr = nullptr; + txExternalLength = 0; + txExternalActive = false; transmissionBegun = true; } @@ -152,17 +159,17 @@ void TwoWire::beginTransmission(uint8_t address) { // 2 : NACK on transmit of address // 3 : NACK on transmit of data // 4 : Other error -uint8_t TwoWire::endTransmission(bool stopBit) +uint8_t TwoWire::endTransmission(bool stopBit, void (*onComplete)(void* user, int status), void* user) { transmissionBegun = false ; txn.config = stopBit ? I2C_CFG_STOP : 0; txn.address = txAddress; - txn.length = txLength; - txn.txPtr = txBuffer; + txn.length = txExternalActive ? txExternalLength : txLength; + txn.txPtr = txExternalActive ? txExternalPtr : txBuffer; txn.rxPtr = nullptr; - txn.onComplete = &TwoWire::onTxnComplete; - txn.user = this; + txn.onComplete = onComplete ? onComplete : &TwoWire::onTxnComplete; + txn.user = onComplete ? user : this; txnDone = false; txnStatus = static_cast(SercomWireError::SUCCESS); masterIndex = 0; @@ -171,23 +178,31 @@ uint8_t TwoWire::endTransmission(bool stopBit) if (!sercom->enqueueWIRE(&txn)) return static_cast(SercomWireError::QUEUE_FULL); - while (!txnDone) { - yield(); - } + txExternalPtr = nullptr; + txExternalLength = 0; + txExternalActive = false; - SercomWireError err = static_cast(txnStatus); - switch (err) { - case SercomWireError::SUCCESS: - return 0; - case SercomWireError::DATA_TOO_LONG: - return 1; - case SercomWireError::NACK_ON_ADDRESS: - return 2; - case SercomWireError::NACK_ON_DATA: - return 3; - default: - return 4; + if (!onComplete) { + while (!txnDone) { + yield(); + } + + SercomWireError err = static_cast(txnStatus); + switch (err) { + case SercomWireError::SUCCESS: + return 0; + case SercomWireError::DATA_TOO_LONG: + return 1; + case SercomWireError::NACK_ON_ADDRESS: + return 2; + case SercomWireError::NACK_ON_DATA: + return 3; + default: + return 4; + } } + + return 0; } uint8_t TwoWire::endTransmission() @@ -198,7 +213,7 @@ uint8_t TwoWire::endTransmission() size_t TwoWire::write(uint8_t ucData) { // No writing, without begun transmission or a full buffer - if ( !transmissionBegun || txLength >= WIRE_TX_BUFFER_LENGTH ) + if ( !transmissionBegun || txExternalActive || txLength >= WIRE_TX_BUFFER_LENGTH ) { return 0 ; } @@ -210,6 +225,20 @@ size_t TwoWire::write(uint8_t ucData) size_t TwoWire::write(const uint8_t *data, size_t quantity) { + if (!transmissionBegun) + return 0; + + if (txExternalActive) + return 0; + + if (txLength == 0 && quantity > 0) { + txExternalPtr = data; + txExternalLength = quantity; + txExternalActive = true; + txLength = quantity; + return quantity; + } + //Try to store all data for(size_t i = 0; i < quantity; ++i) { @@ -293,174 +322,6 @@ size_t TwoWire::getRxLength(void) const return rxLength; } -void TwoWire::onService(void) -{ - SercomTxn* t = &txn; - uint8_t flags = (uint8_t)sercom->getINTFLAG(); - - if (flags == 0) - return; - - if (sercom->isRXNackReceivedWIRE()) - { - sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); - SercomWireError err = awaitingAddressAck ? SercomWireError::NACK_ON_ADDRESS - : SercomWireError::NACK_ON_DATA; - sercom->deferStopWIRE(err); - return; - } - - if (sercom->isMasterWIRE()) - { - if (flags & SERCOM_I2CM_INTFLAG_ERROR) - { - sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); - uint16_t status = (uint16_t)sercom->getSTATUS(); - uint8_t busState = (status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> SERCOM_I2CM_STATUS_BUSSTATE_Pos; - SercomWireError err; - - if (status & SERCOM_I2CM_STATUS_ARBLOST) - err = SercomWireError::ARBITRATION_LOST; - else if (status & SERCOM_I2CM_STATUS_BUSERR) - err = SercomWireError::BUS_ERROR; - else if (status & SERCOM_I2CM_STATUS_MEXTTOUT) - err = SercomWireError::MASTER_TIMEOUT; - else if (status & SERCOM_I2CM_STATUS_SEXTTOUT) - err = SercomWireError::SLAVE_TIMEOUT; - else if (status & SERCOM_I2CM_STATUS_LENERR) - err = SercomWireError::LENGTH_ERROR; - else if (busState == 0x0) - err = SercomWireError::BUS_STATE_UNKNOWN; - else - err = SercomWireError::UNKNOWN_ERROR; - - sercom->clearINTFLAG(); - sercom->deferStopWIRE(err); - return; - } - - bool ok = (t->config & I2C_CFG_READ) ? sercom->readDataWIRE() - : sercom->sendDataWIRE(); - - if (ok){ - awaitingAddressAck = false; -#ifdef USE_ZERODMA - if (t->length > 0 && t->length < 256 && (t->config & I2C_CFG_STOP)) - return; -#endif - sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_READ); - return; - } - - // Transaction complete - if (t->config & I2C_CFG_STOP) - sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); - else - sercom->clearINTFLAG(); - - sercom->deferStopWIRE(SercomWireError::SUCCESS); - return; - } - - if ( sercom->isSlaveWIRE() ) - { - if (flags & SERCOM_I2CS_INTFLAG_ERROR) - { - uint16_t status = (uint16_t)sercom->getSTATUS(); - SercomWireError err; - - if (status & SERCOM_I2CS_STATUS_BUSERR) - err = SercomWireError::BUS_ERROR; - else if (status & SERCOM_I2CS_STATUS_COLL) - err = SercomWireError::ARBITRATION_LOST; - else if (status & SERCOM_I2CS_STATUS_SEXTTOUT) - err = SercomWireError::SLAVE_TIMEOUT; - else if (status & SERCOM_I2CS_STATUS_LOWTOUT) - err = SercomWireError::SLAVE_TIMEOUT; - else - err = SercomWireError::UNKNOWN_ERROR; - - sercom->clearINTFLAG(); - sercom->deferStopWIRE(err); - return; - } - - if(sercom->isStopDetectedWIRE() || - (sercom->isAddressMatch() && sercom->isRestartDetectedWIRE() && !sercom->isMasterReadOperationWIRE())) //Stop or Restart detected - { - sercom->prepareAckBitWIRE(); - sercom->prepareCommandBitsWire(0x03); - - pendingReceive = true; - pendingReceiveLength = available(); - sercom->deferWireReceive(pendingReceiveLength); - } - else if(sercom->isAddressMatch()) //Address Match - { - sercom->prepareAckBitWIRE(); - sercom->prepareCommandBitsWire(0x03); - - if(sercom->isMasterReadOperationWIRE()) //Is a request ? - { - txLength = 0; - txIndex = 0; - - transmissionBegun = true; - - // onRequestCallback runs in ISR context here. Deferring to PendSV - // would require stalling DRDY or returning 0xFF until the buffer is filled. - if(onRequestCallback) - onRequestCallback(); - - t->txPtr = txBuffer; - t->rxPtr = nullptr; - t->length = txLength; - t->config = I2C_CFG_READ; - bool useDma = (txLength > 0 && txLength < 256); - sercom->setWireTxn(t, txLength, useDma); - } else { - rxLength = 0; - rxIndex = 0; - t->txPtr = nullptr; - t->rxPtr = rxBufferPtr; - t->length = rxBufferCapacity; - t->config = 0; - bool useDma = (rxBufferPtr != rxBuffer) && (rxBufferCapacity > 0 && rxBufferCapacity < 256); - sercom->setWireTxn(t, rxBufferCapacity, useDma); - if (useDma) - rxLength = rxBufferCapacity; - } - } - else if(sercom->isDataReadyWIRE()) - { - bool ok = sercom->isMasterReadOperationWIRE() - ? sercom->sendDataWIRE() - : sercom->readDataWIRE(); - - if (ok) { -#ifdef USE_ZERODMA - if (sercom->isWireDma()) - return; -#endif - if (!sercom->isMasterReadOperationWIRE()) - rxLength++; - sercom->prepareCommandBitsWire(0x03); - return; - } - - if (!sercom->isMasterReadOperationWIRE()) { - sercom->prepareNackBitWIRE(); - sercom->prepareCommandBitsWire(0x03); - return; - } - - sercom->I2CS.DATA.reg = 0xFF; - sercom->prepareCommandBitsWire(0x03); - return; - } - } -} - void TwoWire::onTxnComplete(void* user, int status) { if (!user) diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index da2834358..f313c5e99 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -30,21 +30,37 @@ // WIRE_HAS_END means Wire has end() #define WIRE_HAS_END 1 +// NOTE: SAMD21/SAMD51 silicon errata: when I2C master uses SCLSM=1, CTRLB.CMD +// (STOP/RESTART) is ignored, so interrupt-driven byte mode cannot reliably end +// transfers or issue repeated starts. Hs-mode requires SCLSM=1, therefore Hs-mode +// is DMA-only and STOP-only (no repeated starts). The non-DMA Wire path should +// not enable Hs-mode. Also per errata, do not enable QCEN when SCLSM=1 (bus error). + class TwoWire : public Stream { public: TwoWire(SERCOM *s, uint8_t pinSDA, uint8_t pinSCL); void begin(); + void begin(uint16_t, bool enableGeneralCall = false, uint8_t speed = 0x0, bool enable10Bit = false); void begin(uint8_t, bool enableGeneralCall = false); void end(); void setClock(uint32_t); void beginTransmission(uint8_t); - uint8_t endTransmission(bool stopBit); + // If onComplete is nullptr, this blocks for legacy sync behavior. + // If onComplete is non-null, this enqueues and returns immediately (async). + uint8_t endTransmission(bool stopBit = true, + void (*onComplete)(void* user, int status) = nullptr, + void* user = nullptr); uint8_t endTransmission(void); - uint8_t requestFrom(uint8_t address, size_t quantity, bool stopBit); - uint8_t requestFrom(uint8_t address, size_t quantity, uint8_t* rxBuffer, bool stopBit = true); + // If onComplete is nullptr, this blocks for legacy sync behavior. + // If onComplete is non-null, this enqueues and returns immediately (async). + // If rxBuffer is nullptr, the internal buffer is used; otherwise rxBuffer is used. + uint8_t requestFrom(uint8_t address, size_t quantity, uint8_t* rxBuffer = nullptr, + bool stopBit = true, + void (*onComplete)(void* user, int status) = nullptr, + void* user = nullptr); uint8_t requestFrom(uint8_t address, size_t quantity); size_t write(uint8_t data); @@ -68,7 +84,7 @@ class TwoWire : public Stream inline size_t write(int n) { return write((uint8_t)n); } using Print::write; - void onService(void); + inline void onService(void); private: SERCOM * sercom; @@ -88,6 +104,9 @@ class TwoWire : public Stream size_t rxIndex; size_t txLength; size_t txIndex; + const uint8_t* txExternalPtr; + size_t txExternalLength; + bool txExternalActive; size_t masterIndex; bool awaitingAddressAck; uint8_t txAddress; @@ -127,4 +146,158 @@ class TwoWire : public Stream extern TwoWire Wire5; #endif +inline void TwoWire::onService(void) +{ + uint8_t flags = (uint8_t)sercom->getINTFLAG(); + uint16_t status = (uint16_t)sercom->getSTATUS(); + bool isMaster = sercom->isMasterWIRE(); + + if ((!isMaster && !sercom->isSlaveWIRE()) || flags == 0) { + sercom->clearINTFLAG(); + return; + } + + if (status & SERCOM_I2CM_STATUS_RXNACK) { + sercom->prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); + SercomWireError err = awaitingAddressAck ? SercomWireError::NACK_ON_ADDRESS + : SercomWireError::NACK_ON_DATA; + sercom->deferStopWIRE(err); + return; + } + + if (isMaster) { + SercomTxn* txn = sercom->getCurrentTxnWIRE(); + if (!txn) { + sercom->clearINTFLAG(); + return; + } + + if (flags & SERCOM_I2CM_INTFLAG_ERROR) { + sercom->prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); + uint8_t busState = (status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> SERCOM_I2CM_STATUS_BUSSTATE_Pos; + SercomWireError err = SercomWireError::UNKNOWN_ERROR; + + if (status & SERCOM_I2CM_STATUS_ARBLOST) + err = SercomWireError::ARBITRATION_LOST; + if (status & SERCOM_I2CM_STATUS_BUSERR) + err = SercomWireError::BUS_ERROR; + if (status & SERCOM_I2CM_STATUS_MEXTTOUT) + err = SercomWireError::MASTER_TIMEOUT; + if (status & SERCOM_I2CM_STATUS_SEXTTOUT) + err = SercomWireError::SLAVE_TIMEOUT; + if (status & SERCOM_I2CM_STATUS_LENERR) + err = SercomWireError::LENGTH_ERROR; + if (busState == 0x0) + err = SercomWireError::BUS_STATE_UNKNOWN; + + sercom->clearINTFLAG(); + sercom->deferStopWIRE(err); + return; + } + + bool isRead = (txn->config & I2C_CFG_READ); + + if (sercom->getTxnIndexWIRE() < sercom->getTxnLengthWIRE()) { + isRead ? sercom->readDataWIRE() : sercom->sendDataWIRE(); + awaitingAddressAck = false; + return; + } + + if ((txn->config & I2C_CFG_STOP) && !isRead) + sercom->prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); + else + sercom->clearINTFLAG(); + + awaitingAddressAck = true; + sercom->deferStopWIRE(SercomWireError::SUCCESS); + return; + } + else { + if (flags & SERCOM_I2CS_INTFLAG_ERROR) { + SercomWireError err = SercomWireError::UNKNOWN_ERROR;; + + if (status & SERCOM_I2CS_STATUS_BUSERR) + err = SercomWireError::BUS_ERROR; + if (status & SERCOM_I2CS_STATUS_COLL) + err = SercomWireError::ARBITRATION_LOST; + if (status & SERCOM_I2CS_STATUS_SEXTTOUT) + err = SercomWireError::SLAVE_TIMEOUT; + if (status & SERCOM_I2CS_STATUS_LOWTOUT) + err = SercomWireError::SLAVE_TIMEOUT; + + sercom->clearINTFLAG(); + sercom->deferStopWIRE(err); + return; + } + + // To avoid unnecessary clock cycles for register reads, avoid using inline getters + bool isMasterRead = (status & SERCOM_I2CS_STATUS_DIR); // Master Read / Slave Transmit + bool sr = (status & SERCOM_I2CS_STATUS_SR); // Repeated Start detected + bool prec = (flags & SERCOM_I2CS_INTFLAG_PREC); // Stop detected + bool amatch = (flags & SERCOM_I2CS_INTFLAG_AMATCH); // Address Match detected + bool drdy = (flags & SERCOM_I2CS_INTFLAG_DRDY); // Data Ready detected + + // Stop or Restart detected - defer receive callback + if(prec || (amatch && sr && !isMasterRead)) + { + pendingReceive = true; + pendingReceiveLength = available(); + sercom->deferReceiveWIRE(pendingReceiveLength); + return; + } + + // Address Match - setup transaction + // AACKEN enabled: address ACK is automatic, no manual ACK/clear needed + else if(amatch) + { + if(isMasterRead) // Master Read / Slave TX + { + // onRequestCallback runs in ISR context here. Deferring to PendSV + // would require stalling DRDY or returning 0xFF until the buffer is filled. + // onRequestCallback is what will set TwoWire::txn for the transaction. + if(onRequestCallback) + onRequestCallback(); + + // Ensure callback actually set txn.length; if not, stall with 0-length txn + if (txn.length == 0) + return; + + if (!(txn.config & I2C_CFG_READ)) + txn.config |= I2C_CFG_READ; + } + else // Master Write / Slave RX + { + // rxLength needs to be set in the rxCallback so the user has runtime control + // setting rxLength to 0 will default to the internal buffer + rxIndex = 0; + txn.txPtr = nullptr; + txn.rxPtr = rxLength ? rxBufferPtr : rxBuffer; + txn.length = rxLength ? rxLength : WIRE_RX_BUFFER_LENGTH; + txn.config = 0; + } + + sercom->setTxnWIRE(&txn); + + // SCLSM=0 (Smart Mode disabled): AMATCH and DRDY never fire together + // → return now, DRDY will fire in next interrupt + // SCLSM=1 (Smart Mode enabled) + Master Read: AMATCH+DRDY fire together + // → fall through to handle data immediately + // SCLSM=1 + Master Write: DRDY not set yet + // → return now, DRDY fires later + if (!drdy) + return; + // else: DRDY is set (SCLSM=1 Master Read case), fall through + } + + // Data Ready - handle byte transfer + if(drdy) + { + isMasterRead ? sercom->sendDataWIRE() : sercom->readDataWIRE(); + + if (!isMasterRead) + rxLength = sercom->getTxnIndexWIRE(); + } + } +} + #endif From b81a58a65464c4e85adb9fb9f41602d92f2abad2 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 16 Feb 2026 19:41:06 -0600 Subject: [PATCH 20/79] master wire fully tested sync/async --- cores/arduino/SERCOM.cpp | 60 +++++----- cores/arduino/SERCOM.h | 3 +- libraries/Wire/Wire.cpp | 234 +++++++++++++++++++++++++-------------- libraries/Wire/Wire.h | 58 +++++----- 4 files changed, 215 insertions(+), 140 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 37337432b..5855eb6a5 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -82,7 +82,7 @@ void SERCOM::initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint #ifdef USE_ZERODMA int8_t id = getSercomIndex(); if (id >= 0) { - dmaSetCallbacks(SERCOM::dmaTxCallbackUART, SERCOM::dmaRxCallbackUART); + dmaSetCallbacks(SERCOM::dmaTxCallbackUART, SERCOM::dmaRxCallbackUART); dmaInit(id); } #endif // USE_ZERODMA @@ -342,7 +342,7 @@ void SERCOM::initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize ch #ifdef USE_ZERODMA int8_t id = getSercomIndex(); if (id >= 0) { - dmaSetCallbacks(SERCOM::dmaTxCallbackSPI, SERCOM::dmaRxCallbackSPI); + dmaSetCallbacks(SERCOM::dmaTxCallbackSPI, SERCOM::dmaRxCallbackSPI); dmaInit(id); } #endif // USE_ZERODMA @@ -569,8 +569,9 @@ uint8_t SERCOM::calculateBaudrateSynchronous(uint32_t baudrate) */ void SERCOM::resetWIRE() { - resetSERCOM(); - _wire = WireConfig{}; + clearQueueWIRE(); // Drain pending transactions from queue + resetSERCOM(); // SWRST: hardware reset to default state + _wire = WireConfig{}; // Reset software state } void SERCOM::clearQueueWIRE(void) @@ -604,10 +605,10 @@ void SERCOM::initWIRE(void) if (_wire.inited) // If already initialized, return return; - uint8_t idx = getSercomIndex(); - initClockNVIC(); - registerService(idx, static_cast(&SERCOM::stopTransmissionWIRE)); - + uint8_t idx = getSercomIndex(); + initClockNVIC(); + registerService(idx, static_cast(&SERCOM::stopTransmissionWIRE)); + #ifdef USE_ZERODMA dmaSetCallbacks(SERCOM::dmaTxCallbackWIRE, SERCOM::dmaRxCallbackWIRE); if (idx >= 0) @@ -842,9 +843,11 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) if (error == SercomWireError::BUS_STATE_UNKNOWN) { if (_wire.retryCount < kMaxWireRetries) { ++_wire.retryCount; + sercom->I2CM.STATUS.bit.BUSSTATE = 1; while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; startTransmissionWIRE(); + return txn; } } @@ -852,9 +855,11 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) if (error == SercomWireError::ARBITRATION_LOST || error == SercomWireError::BUS_ERROR) { if (_wire.retryCount < kMaxWireRetries) { ++_wire.retryCount; + sercom->I2CM.STATUS.bit.ARBLOST = 1; // Clear arbitration lost flag sercom->I2CM.INTFLAG.reg = SERCOM_I2CM_INTFLAG_ERROR; startTransmissionWIRE(); + return txn; } } @@ -868,18 +873,30 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) if(isMasterWIRE()) while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; // Wait for DATA to sync from last transaction + // Undocumented HW limitation: DMA transfers must terminate with STOP and bus release. + // After a DMA write, the host holds the bus ~7.33 us before the next transfer (Sr window). + // Writing ADDR during that window leaves the bus in an undefined state and breaks + // subsequent DMA/non-DMA transactions. To avoid this, we must wait for BUSSTATE + // to return to IDLE after a STOP returning the hardware to a known state. + // At the tested 48 MHz, this busy-wait is ~350 cycles corresponding to a 3.5 us delay + // at 100 MHz. This wait must occur BEFORE the callback to ensure the bus is stable + // before user code can enqueue the next transaction. + if (isMasterWIRE() && txn && (txn->config & I2C_CFG_STOP)) { + while (sercom->I2CM.STATUS.bit.BUSSTATE > 0x1) ; + } + // Callbacks are expected to run in non-ISR context (main loop/PendSV). - if (txn &&txn->onComplete) - txn->onComplete(txn->user, static_cast(error)); + if (txn && txn->onComplete) + txn->onComplete(txn->user, static_cast(error)); if(isMasterWIRE()) _txnQueue.read(txn); // remove the completed transaction from the queue else { // Deliver deferred WIRE callback outside the SERCOM ISR (from PendSV). // This avoids running user code in the hardware interrupt context. - if (_wireDeferredPending && _wireDeferredCb) { - _wireDeferredPending = false; - _wireDeferredCb(_wireDeferredUser, _wireDeferredLength); + if (_wireDeferredPending && _wireDeferredCb) { + _wireDeferredPending = false; + _wireDeferredCb(_wireDeferredUser, _wireDeferredLength); } } @@ -889,25 +906,12 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) bool isMaster = isMasterWIRE(); - if (_txnQueue.peek(next) && isMaster) { - // Undocumented HW limitation: DMA transfers must terminate with STOP and bus release. - // After a DMA write, the host holds the bus ~7.33 us before the next transfer (Sr window). - // Writing ADDR during that window leaves the bus in an undefined state and breaks - // subsequent DMA/non-DMA transactions. To avoid this, we must wait for BUSSTATE - // to return to IDLE after a STOP returning the hardware to a known state. - // At the tested 48 MHz, this busy-wait is ~350 cycles corresponding to a 3.5 us delay - // at 100 MHz. Thus, we only wait when needed for the next transaction and not on every STOP. - if (txn) { - if (txn->config & I2C_CFG_STOP) - while (sercom->I2CM.STATUS.bit.BUSSTATE > 0x1) ; - } - + if (_txnQueue.peek(next) && isMaster) startTransmissionWIRE(); - } else if (isMaster) { + else if (isMaster) sercom->I2CM.INTENCLR.reg = SERCOM_I2CM_INTENCLR_ERROR | SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; - } return txn; } diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index 17d54f9a5..eeadf05f4 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -294,6 +294,7 @@ class SERCOM inline const SercomTxn* getCurrentTxnWIRE(void) const { return _wire.currentTxn; } inline size_t getTxnIndexWIRE(void) const { return _wire.txnIndex; } inline size_t getTxnLengthWIRE(void) const { return _wire.txnLength; } + inline bool isActiveWIRE(void) const { return _wire.active; } inline bool isDBGSTOP( void ) const { return sercom->I2CM.DBGCTRL.bit.DBGSTOP; } inline void setDBGSTOP( bool stop ) { sercom->I2CM.DBGCTRL.bit.DBGSTOP = stop; } @@ -343,7 +344,7 @@ class SERCOM DmaStatus dmaStartTx(const void* src, volatile void* dstReg, size_t len); DmaStatus dmaStartRx(void* dst, volatile void* srcReg, size_t len); DmaStatus dmaStartDuplex(const void* txSrc, void* rxDst, volatile void* txReg, volatile void* rxReg, size_t len, - const uint8_t* dummyTx = nullptr); + const uint8_t* dummyTx = nullptr); void dmaRelease(); void dmaResetDescriptors(); void dmaAbortTx(); diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index 9b33ab869..2fdf8d0a8 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -39,19 +39,16 @@ TwoWire::TwoWire(SERCOM * s, uint8_t pinSDA, uint8_t pinSCL) transmissionBegun = false; rxLength = 0; rxIndex = 0; - txLength = 0; - txIndex = 0; - txExternalPtr = nullptr; - txExternalLength = 0; - txExternalActive = false; masterIndex = 0; awaitingAddressAck = false; txnDone = false; txnStatus = 0; rxBufferPtr = rxBuffer; - rxBufferCapacity = WIRE_RX_BUFFER_LENGTH; + rxBufferCapacity = WIRE_BUFFER_LENGTH; + txBufferCapacity = WIRE_BUFFER_LENGTH; pendingReceive = false; pendingReceiveLength = 0; + txnPoolHead = 0; } void TwoWire::begin(void) { @@ -82,74 +79,92 @@ void TwoWire::setClock(uint32_t baudrate) { } void TwoWire::end() { - sercom->disableWIRE(); + sercom->resetWIRE(); // SWRST: resets hardware + clears state + drains queue } -uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, uint8_t* rxBuffer, bool stopBit, +uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit, uint8_t* rxBuffer, void (*onComplete)(void* user, int status), void* user) { if(quantity == 0) - { return 0; - } + + loader = SercomTxn{}; if (rxBuffer != nullptr) { - rxBufferPtr = rxBuffer; - rxBufferCapacity = quantity; + loader.rxPtr = rxBuffer; + loader.length = quantity; } else { - rxBufferPtr = this->rxBuffer; - rxBufferCapacity = WIRE_RX_BUFFER_LENGTH; - if (quantity > rxBufferCapacity) - quantity = rxBufferCapacity; + loader.rxPtr = this->rxBuffer; + loader.length = ( quantity > WIRE_BUFFER_LENGTH) ? WIRE_BUFFER_LENGTH : quantity; } - rxLength = 0; - rxIndex = 0; - - txn.config = I2C_CFG_READ | (stopBit ? I2C_CFG_STOP : 0); - txn.address = address; - txn.length = quantity; - txn.txPtr = nullptr; - txn.rxPtr = rxBufferPtr; - txn.onComplete = onComplete ? onComplete : &TwoWire::onTxnComplete; - txn.user = onComplete ? user : this; - txnDone = false; - txnStatus = static_cast(SercomWireError::SUCCESS); - masterIndex = 0; + loader.config = I2C_CFG_READ | (stopBit ? I2C_CFG_STOP : 0); + loader.address = address; + loader.onComplete = onComplete ? onComplete : &TwoWire::onTxnComplete; + + // Allocate fresh transaction from pool and copy loader data + SercomTxn* txn = allocateTxn(); + *txn = loader; + + // For async callbacks, pass txn as user so callback can access txn->rxPtr/length directly + // For sync calls, pass 'this' so onTxnComplete can update txnStatus/txnDone + if (onComplete) + txn->user = (user == nullptr) ? txn : user; + else + txn->user = this; + awaitingAddressAck = true; + txnDone = false; - if (!sercom->enqueueWIRE(&txn)) + // Enqueue the pool transaction, not the loader + if (!sercom->enqueueWIRE(txn)) return 0; if (!onComplete) { + // Wait for transaction to complete (onTxnComplete sets txnDone) with timeout + uint32_t startMillis = millis(); + const uint32_t timeout = 1000; // 1 second timeout while (!txnDone) { + if (millis() - startMillis > timeout) + return 0; + yield(); } if (txnStatus != static_cast(SercomWireError::SUCCESS)) return 0; + // Set up pointers for Wire.available()/read() to access the data + // txn->rxPtr already points to this->rxBuffer (from loader copy) + rxBufferPtr = loader.rxPtr; rxLength = quantity; + rxIndex = 0; return rxLength; } return quantity; } -uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity) -{ - return requestFrom(address, quantity, nullptr, true); +SercomTxn* TwoWire::allocateTxn() { + // Simple round-robin allocation from pool + SercomTxn* txn = &txnPool[txnPoolHead]; + txnPoolHead = (txnPoolHead + 1) % TXN_POOL_SIZE; + *txn = SercomTxn{}; // Clear the transaction + return txn; } -void TwoWire::beginTransmission(uint8_t address) { - // save address of target and clear buffer - txAddress = address; - txLength = 0; - txIndex = 0; - txExternalPtr = nullptr; - txExternalLength = 0; - txExternalActive = false; +void TwoWire::freeTxn(SercomTxn* txn) { + // Transactions are freed when removed from SERCOM queue + // Pool allocation is round-robin, so no explicit free needed + (void)txn; +} +void TwoWire::beginTransmission(uint8_t address) { + // Initialize loader as staging area for building transaction + loader = SercomTxn{}; + loader.txPtr = nullptr; + loader.address = address; + txBufferCapacity = WIRE_BUFFER_LENGTH; transmissionBegun = true; } @@ -163,27 +178,30 @@ uint8_t TwoWire::endTransmission(bool stopBit, void (*onComplete)(void* user, in { transmissionBegun = false ; - txn.config = stopBit ? I2C_CFG_STOP : 0; - txn.address = txAddress; - txn.length = txExternalActive ? txExternalLength : txLength; - txn.txPtr = txExternalActive ? txExternalPtr : txBuffer; - txn.rxPtr = nullptr; - txn.onComplete = onComplete ? onComplete : &TwoWire::onTxnComplete; - txn.user = onComplete ? user : this; - txnDone = false; - txnStatus = static_cast(SercomWireError::SUCCESS); - masterIndex = 0; + // Allocate a fresh transaction from the pool and copy staged data from loader + SercomTxn* txn = allocateTxn(); + *txn = loader; // Copy staged transaction data + + // Set parameters that weren't known during beginTransmission/write + txn->config = stopBit ? I2C_CFG_STOP : 0; + txn->onComplete = onComplete ? onComplete : &TwoWire::onTxnComplete; + txn->user = (user == nullptr) ? this : user; + awaitingAddressAck = true; + txnDone = false; - if (!sercom->enqueueWIRE(&txn)) + // Enqueue the pool transaction, not the loader + if (!sercom->enqueueWIRE(txn)) return static_cast(SercomWireError::QUEUE_FULL); - txExternalPtr = nullptr; - txExternalLength = 0; - txExternalActive = false; - if (!onComplete) { + // Wait for transaction to complete (onTxnComplete sets txnDone) with timeout + uint32_t startMillis = millis(); + const uint32_t timeout = 1000; // 1 second timeout while (!txnDone) { + if (millis() - startMillis > timeout) + return 4; // OTHER error + yield(); } @@ -197,7 +215,7 @@ uint8_t TwoWire::endTransmission(bool stopBit, void (*onComplete)(void* user, in return 2; case SercomWireError::NACK_ON_DATA: return 3; - default: + default: // OTHER return 4; } } @@ -205,49 +223,81 @@ uint8_t TwoWire::endTransmission(bool stopBit, void (*onComplete)(void* user, in return 0; } -uint8_t TwoWire::endTransmission() -{ - return endTransmission(true); -} - size_t TwoWire::write(uint8_t ucData) { - // No writing, without begun transmission or a full buffer - if ( !transmissionBegun || txExternalActive || txLength >= WIRE_TX_BUFFER_LENGTH ) - { - return 0 ; - } + if (!transmissionBegun) + return 0; + + // Check buffer full + if (loader.length >= txBufferCapacity) + return 0; + + // Initialize to internal buffer if first write + if (loader.txPtr == nullptr) + loader.txPtr = txBuffer; - txBuffer[txLength++] = ucData; + // Append to current buffer (internal or external) + if (loader.txPtr == txBuffer) + txBuffer[loader.length++] = ucData; + else + const_cast(loader.txPtr)[loader.length++] = ucData; - return 1 ; + return 1; } -size_t TwoWire::write(const uint8_t *data, size_t quantity) +size_t TwoWire::write(const uint8_t *data, size_t quantity, bool setExternal) { if (!transmissionBegun) return 0; - if (txExternalActive) + if (quantity == 0) return 0; - if (txLength == 0 && quantity > 0) { - txExternalPtr = data; - txExternalLength = quantity; - txExternalActive = true; - txLength = quantity; - return quantity; + // External path: require external buffer (zero-copy) + if (setExternal) { + if (loader.txPtr == nullptr) { + loader.txPtr = data; + txBufferCapacity = quantity; // Treat quantity as both length and capacity + loader.length = quantity; + return quantity; + } + + // Prevent switching from internal buffer to external mid-transaction + if (loader.txPtr == txBuffer) + return 0; } - //Try to store all data - for(size_t i = 0; i < quantity; ++i) - { - //Return the number of data stored, when the buffer is full (if write return 0) - if(!write(data[i])) - return i; + // Sync path: prefer internal buffer unless it overflows + if (loader.txPtr == nullptr) { + if (quantity <= WIRE_BUFFER_LENGTH) { + loader.txPtr = txBuffer; + txBufferCapacity = WIRE_BUFFER_LENGTH; + memcpy(txBuffer, data, quantity); + loader.length = quantity; + return quantity; + } + + // Large write: require external buffer + loader.txPtr = data; + txBufferCapacity = quantity; + loader.length = quantity; + return quantity; } - //All data stored + // Appending to existing buffer + size_t available = txBufferCapacity - loader.length; + if (quantity > available) + quantity = available; + + if (quantity == 0) + return 0; + + if (loader.txPtr == txBuffer) + memcpy(txBuffer + loader.length, data, quantity); + else + memcpy(const_cast(loader.txPtr) + loader.length, data, quantity); + + loader.length += quantity; return quantity; } @@ -297,6 +347,20 @@ void TwoWire::setRxBuffer(uint8_t* buffer, size_t length) rxBufferCapacity = length; } +void TwoWire::setTxBuffer(uint8_t* buffer, size_t length) +{ + if (buffer == nullptr || length == 0) { + loader.txPtr = nullptr; + txBufferCapacity = WIRE_BUFFER_LENGTH; + loader.length = 0; + return; + } + + loader.txPtr = buffer; + txBufferCapacity = length; + loader.length = 0; +} + void TwoWire::clearRxBuffer(void) { if (rxBufferPtr && rxBufferCapacity > 0) @@ -308,7 +372,7 @@ void TwoWire::clearRxBuffer(void) void TwoWire::resetRxBuffer(void) { rxBufferPtr = rxBuffer; - rxBufferCapacity = WIRE_RX_BUFFER_LENGTH; + rxBufferCapacity = WIRE_BUFFER_LENGTH; clearRxBuffer(); } diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index f313c5e99..02636fafa 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -52,19 +52,21 @@ class TwoWire : public Stream uint8_t endTransmission(bool stopBit = true, void (*onComplete)(void* user, int status) = nullptr, void* user = nullptr); - uint8_t endTransmission(void); // If onComplete is nullptr, this blocks for legacy sync behavior. // If onComplete is non-null, this enqueues and returns immediately (async). // If rxBuffer is nullptr, the internal buffer is used; otherwise rxBuffer is used. - uint8_t requestFrom(uint8_t address, size_t quantity, uint8_t* rxBuffer = nullptr, - bool stopBit = true, + uint8_t requestFrom(uint8_t address, size_t quantity, bool stopBit = true, + uint8_t* rxBuffer = nullptr, void (*onComplete)(void* user, int status) = nullptr, void* user = nullptr); - uint8_t requestFrom(uint8_t address, size_t quantity); size_t write(uint8_t data); - size_t write(const uint8_t * data, size_t quantity); + // 3-arg write: when setExternal=true, data is used directly (zero-copy) and + // quantity is treated as both length and capacity; subsequent write() calls return 0. + // For streaming > WIRE_BUFFER_LENGTH or async usage, call setTxBuffer() before write() + // on every transaction. + size_t write(const uint8_t * data, size_t quantity, bool setExternal = false); virtual int available(void); virtual int read(void); @@ -73,6 +75,7 @@ class TwoWire : public Stream void onReceive(void(*)(int)); void onRequest(void(*)(void)); void setRxBuffer(uint8_t* buffer, size_t length); + void setTxBuffer(uint8_t* buffer, size_t length); void clearRxBuffer(void); void resetRxBuffer(void); uint8_t* getRxBuffer(void); @@ -94,27 +97,30 @@ class TwoWire : public Stream bool transmissionBegun; // RX/TX buffers (sync compatibility, async staging) - static constexpr size_t WIRE_TX_BUFFER_LENGTH = 255; - static constexpr size_t WIRE_RX_BUFFER_LENGTH = SERIAL_BUFFER_SIZE; - uint8_t rxBuffer[WIRE_RX_BUFFER_LENGTH]; - uint8_t txBuffer[WIRE_TX_BUFFER_LENGTH]; + static constexpr size_t WIRE_BUFFER_LENGTH = 255; + uint8_t rxBuffer[WIRE_BUFFER_LENGTH]; + uint8_t txBuffer[WIRE_BUFFER_LENGTH]; uint8_t* rxBufferPtr; size_t rxBufferCapacity; size_t rxLength; size_t rxIndex; - size_t txLength; - size_t txIndex; - const uint8_t* txExternalPtr; - size_t txExternalLength; - bool txExternalActive; + size_t txBufferCapacity; size_t masterIndex; bool awaitingAddressAck; - uint8_t txAddress; volatile bool txnDone; volatile int txnStatus; bool pendingReceive; int pendingReceiveLength; - SercomTxn txn; + SercomTxn slaveTxn; + SercomTxn loader; // Staging area for building transactions + + // Transaction pool for async operations (matches SERCOM queue depth) + static constexpr size_t TXN_POOL_SIZE = 8; + SercomTxn txnPool[TXN_POOL_SIZE]; + uint8_t txnPoolHead; + + SercomTxn* allocateTxn(); + void freeTxn(SercomTxn* txn); // Callback user functions void (*onRequestCallback)(void); @@ -254,29 +260,29 @@ inline void TwoWire::onService(void) { // onRequestCallback runs in ISR context here. Deferring to PendSV // would require stalling DRDY or returning 0xFF until the buffer is filled. - // onRequestCallback is what will set TwoWire::txn for the transaction. + // onRequestCallback is what will set TwoWire::slaveTxn for the transaction. if(onRequestCallback) onRequestCallback(); - // Ensure callback actually set txn.length; if not, stall with 0-length txn - if (txn.length == 0) + // Ensure callback actually set slaveTxn.length; if not, stall with 0-length txn + if (slaveTxn.length == 0) return; - if (!(txn.config & I2C_CFG_READ)) - txn.config |= I2C_CFG_READ; + if (!(slaveTxn.config & I2C_CFG_READ)) + slaveTxn.config |= I2C_CFG_READ; } else // Master Write / Slave RX { // rxLength needs to be set in the rxCallback so the user has runtime control // setting rxLength to 0 will default to the internal buffer rxIndex = 0; - txn.txPtr = nullptr; - txn.rxPtr = rxLength ? rxBufferPtr : rxBuffer; - txn.length = rxLength ? rxLength : WIRE_RX_BUFFER_LENGTH; - txn.config = 0; + slaveTxn.txPtr = nullptr; + slaveTxn.rxPtr = rxLength ? rxBufferPtr : rxBuffer; + slaveTxn.length = rxLength ? rxLength : WIRE_BUFFER_LENGTH; + slaveTxn.config = 0; } - sercom->setTxnWIRE(&txn); + sercom->setTxnWIRE(&slaveTxn); // SCLSM=0 (Smart Mode disabled): AMATCH and DRDY never fire together // → return now, DRDY will fire in next interrupt From 8decc12f54ea455b121ddf1045b996c4d7ee73a5 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 16 Feb 2026 20:10:07 -0600 Subject: [PATCH 21/79] test SPI sync and async operations --- libraries/SPI/SPI.cpp | 23 +++++++++++++++++------ libraries/SPI/SPI.h | 8 +++++++- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 4c748a969..be50ba042 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -47,6 +47,9 @@ SPIClass::SPIClass(SERCOM *p_sercom, uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint // SERCOM pads _padTx=PadTx; _padRx=PadRx; + + // Transaction pool initialization + txnPoolHead = 0; } void SPIClass::begin() @@ -249,15 +252,16 @@ void SPIClass::transfer(const void *txbuf, void *rxbuf, size_t count, // OK to assume now that txbuf and/or rxbuf are non-NULL, an if/else is // often sufficient, don't need else-ifs for everything buffer related. - _txn.txPtr = static_cast(txbuf); - _txn.rxPtr = static_cast(rxbuf); - _txn.length = count; - _txn.onComplete = onComplete ? onComplete : &SPIClass::onTxnComplete; - _txn.user = onComplete ? user : this; + SercomTxn* txn = allocateTxn(); + txn->txPtr = static_cast(txbuf); + txn->rxPtr = static_cast(rxbuf); + txn->length = count; + txn->onComplete = onComplete ? onComplete : &SPIClass::onTxnComplete; + txn->user = onComplete ? user : this; txnDone = false; txnStatus = 0; - if (!_p_sercom->enqueueSPI(&_txn)) { + if (!_p_sercom->enqueueSPI(txn)) { if (onComplete) onComplete(user, static_cast(SercomSpiError::UNKNOWN_ERROR)); return; @@ -305,6 +309,13 @@ void SPIClass::onService(void) } } +SercomTxn* SPIClass::allocateTxn() { + // Simple round-robin allocation from pool + SercomTxn* txn = &txnPool[txnPoolHead]; + txnPoolHead = (txnPoolHead + 1) % TXN_POOL_SIZE; + return txn; +} + void SPIClass::onTxnComplete(void* user, int status) { if (!user) diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index d0afa90b9..1991fb9d2 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -160,7 +160,13 @@ class SPIClass { volatile bool txnDone = false; volatile int txnStatus = 0; - SercomTxn _txn; + + // Transaction pool for async operations (matches SERCOM queue depth) + static constexpr size_t TXN_POOL_SIZE = 8; + SercomTxn txnPool[TXN_POOL_SIZE]; + uint8_t txnPoolHead; + + SercomTxn* allocateTxn(); static void onTxnComplete(void* user, int status); }; From cc3657af466273db65dd02d6541c1d3b4c99ecb3 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 16 Feb 2026 20:57:41 -0600 Subject: [PATCH 22/79] high level testing no hardware testing done on UART --- cores/arduino/Uart.cpp | 107 +++++++++++++++++++++++------------------ cores/arduino/Uart.h | 15 ++++-- 2 files changed, 73 insertions(+), 49 deletions(-) diff --git a/cores/arduino/Uart.cpp b/cores/arduino/Uart.cpp index 6d0262ace..8e8e03796 100644 --- a/cores/arduino/Uart.cpp +++ b/cores/arduino/Uart.cpp @@ -38,6 +38,7 @@ Uart::Uart(SERCOM *_s, uint8_t _pinRX, uint8_t _pinTX, SercomRXPad _padRX, Serco uc_padTX = _padTX; uc_pinRTS = _pinRTS; uc_pinCTS = _pinCTS; + txnPoolHead = 0; } void Uart::begin(unsigned long baudrate) @@ -190,56 +191,58 @@ size_t Uart::write(const uint8_t data) return 1; } -size_t Uart::write(const uint8_t* buffer, size_t size) +size_t Uart::write(const uint8_t* buffer, size_t size, + void (*onComplete)(void* user, int status), + void* user) { if (buffer == nullptr || size == 0) return 0; + if (onComplete == nullptr) { + // Synchronous path: block until complete #ifdef USE_ZERODMA - _txn.txPtr = buffer; - _txn.rxPtr = nullptr; - _txn.length = size; - _txn.onComplete = &Uart::onTxnComplete; - _txn.user = this; - txnDone = false; - txnStatus = 0; - - if (sercom->enqueueUART(&_txn)) { - while (!txnDone) ; - return size; - } + SercomTxn* txn = allocateTxn(); + txn->txPtr = buffer; + txn->rxPtr = nullptr; + txn->length = size; + txn->onComplete = &Uart::onTxnComplete; + txn->user = this; + txnDone = false; + txnStatus = 0; + + if (sercom->enqueueUART(txn)) { + while (!txnDone) ; + return size; + } #endif - - for (size_t i = 0; i < size; ++i) - write(buffer[i]); - return size; -} - -size_t Uart::writeAsync(const uint8_t* buffer, size_t size, void (*onComplete)(void* user, int status), void* user) -{ - if (buffer == nullptr || size == 0) - return 0; - + // Fallback: byte-by-byte + for (size_t i = 0; i < size; ++i) + write(buffer[i]); + return size; + } else { + // Asynchronous path: enqueue and return immediately #ifdef USE_ZERODMA - _txn.txPtr = buffer; - _txn.rxPtr = nullptr; - _txn.length = size; - _txn.onComplete = onComplete ? onComplete : &Uart::onTxnComplete; - _txn.user = onComplete ? user : this; - txnDone = false; - txnStatus = 0; + SercomTxn* txn = allocateTxn(); + txn->txPtr = buffer; + txn->rxPtr = nullptr; + txn->length = size; + txn->onComplete = onComplete; + txn->user = user; + txnDone = false; + txnStatus = 0; + + if (!sercom->enqueueUART(txn)) + return 0; - if (!sercom->enqueueUART(&_txn)) - return 0; - - return size; + return size; #else - (void)onComplete; - (void)user; - for (size_t i = 0; i < size; ++i) - write(buffer[i]); - return size; + (void)onComplete; + (void)user; + for (size_t i = 0; i < size; ++i) + write(buffer[i]); + return size; #endif + } } size_t Uart::read(uint8_t* buffer, size_t size, void (*onComplete)(void* user, int status), void* user) @@ -248,6 +251,7 @@ size_t Uart::read(uint8_t* buffer, size_t size, void (*onComplete)(void* user, i return 0; if (onComplete == nullptr) { + // Synchronous path: read from ring buffer size_t readCount = 0; while (readCount < size) { int c = read(); @@ -258,21 +262,25 @@ size_t Uart::read(uint8_t* buffer, size_t size, void (*onComplete)(void* user, i } #ifdef USE_ZERODMA + // Asynchronous path: use DMA pendingRxCb = onComplete; pendingRxUser = user; rxExternalActive = true; + // Disable RXC interrupt; DMA takes over sercom->getSercom()->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_RXC; - _txn.txPtr = nullptr; - _txn.rxPtr = buffer; - _txn.length = size; - _txn.onComplete = &Uart::onTxnComplete; - _txn.user = this; + SercomTxn* txn = allocateTxn(); + txn->txPtr = nullptr; + txn->rxPtr = buffer; + txn->length = size; + txn->onComplete = &Uart::onTxnComplete; + txn->user = this; txnDone = false; txnStatus = 0; - if (!sercom->enqueueUART(&_txn)) { + if (!sercom->enqueueUART(txn)) { + // Enqueue failed; restore RXC interrupt and clear pending state sercom->getSercom()->USART.INTENSET.reg = SERCOM_USART_INTENSET_RXC; rxExternalActive = false; pendingRxCb = nullptr; @@ -288,6 +296,13 @@ size_t Uart::read(uint8_t* buffer, size_t size, void (*onComplete)(void* user, i #endif } +SercomTxn* Uart::allocateTxn() { + // Simple round-robin allocation from pool + SercomTxn* txn = &txnPool[txnPoolHead]; + txnPoolHead = (txnPoolHead + 1) % TXN_POOL_SIZE; + return txn; +} + void Uart::onTxnComplete(void* user, int status) { if (!user) diff --git a/cores/arduino/Uart.h b/cores/arduino/Uart.h index 9687dbbbb..6f59fddf9 100644 --- a/cores/arduino/Uart.h +++ b/cores/arduino/Uart.h @@ -41,8 +41,11 @@ class Uart : public HardwareSerial void* user = nullptr); void flush(); size_t write(const uint8_t data); - size_t write(const uint8_t* buffer, size_t size); - size_t writeAsync(const uint8_t* buffer, size_t size, void (*onComplete)(void* user, int status), void* user); + // If onComplete is nullptr, blocks (sync); otherwise enqueues and returns (async) + // Transparently uses DMA when available, falls back to byte-by-byte otherwise + size_t write(const uint8_t* buffer, size_t size, + void (*onComplete)(void* user, int status) = nullptr, + void* user = nullptr); using Print::write; // pull in write(str) and write(buf, size) from Print void IrqHandler(); @@ -66,7 +69,13 @@ class Uart : public HardwareSerial volatile bool txnDone = false; volatile int txnStatus = 0; - SercomTxn _txn; + + // Transaction pool for async operations (matches SERCOM queue depth) + static constexpr size_t TXN_POOL_SIZE = 8; + SercomTxn txnPool[TXN_POOL_SIZE]; + uint8_t txnPoolHead = 0; + + SercomTxn* allocateTxn(); static void onTxnComplete(void* user, int status); bool rxExternalActive = false; void (*pendingRxCb)(void* user, int status) = nullptr; From fdd11fdaf030a456e38334b9ec499b7840a89f7b Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 16 Feb 2026 22:10:27 -0600 Subject: [PATCH 23/79] refactor to use async sercom api's --- variants/mkrgsm1400/variant.cpp | 33 +++++++++++++++++++--------- variants/mkrnb1500/variant.cpp | 37 +++++++++++++++++++++----------- variants/mkrwifi1010/variant.cpp | 35 ++++++++++++++++++++---------- 3 files changed, 72 insertions(+), 33 deletions(-) diff --git a/variants/mkrgsm1400/variant.cpp b/variants/mkrgsm1400/variant.cpp index c534367c2..d137b4aab 100644 --- a/variants/mkrgsm1400/variant.cpp +++ b/variants/mkrgsm1400/variant.cpp @@ -174,6 +174,7 @@ SERCOM sercom5(SERCOM5); #if defined(USE_BQ24195L_PMIC) #include "wiring_private.h" +#include "SERCOM_Txn.h" #define PMIC_ADDRESS 0x6B #define PMIC_REG01 0x01 @@ -185,12 +186,17 @@ static inline void enable_battery_charging() { pinPeripheral(PIN_WIRE_SDA, g_APinDescription[PIN_WIRE_SDA].ulPinType); pinPeripheral(PIN_WIRE_SCL, g_APinDescription[PIN_WIRE_SCL].ulPinType); - PERIPH_WIRE.startTransmissionWIRE( PMIC_ADDRESS, WIRE_WRITE_FLAG ); - PERIPH_WIRE.sendDataMasterWIRE(PMIC_REG01); - PERIPH_WIRE.sendDataMasterWIRE(0x1B); // Charge Battery + Minimum System Voltage 3.5V - PERIPH_WIRE.prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); + static SercomTxn txn; + static uint8_t txData[2] = {PMIC_REG01, 0x1B}; // Charge Battery + Minimum System Voltage 3.5V - PERIPH_WIRE.disableWIRE(); + txn = SercomTxn{}; + txn.config = I2C_CFG_STOP; + txn.address = PMIC_ADDRESS; + txn.length = 2; + txn.txPtr = txData; + + PERIPH_WIRE.enqueueWIRE(&txn); + PERIPH_WIRE.startTransmissionWIRE(); } static inline void disable_battery_fet(bool disabled) { @@ -199,14 +205,21 @@ static inline void disable_battery_fet(bool disabled) { pinPeripheral(PIN_WIRE_SDA, g_APinDescription[PIN_WIRE_SDA].ulPinType); pinPeripheral(PIN_WIRE_SCL, g_APinDescription[PIN_WIRE_SCL].ulPinType); - PERIPH_WIRE.startTransmissionWIRE( PMIC_ADDRESS, WIRE_WRITE_FLAG ); - PERIPH_WIRE.sendDataMasterWIRE(PMIC_REG07); + static SercomTxn txn; + static uint8_t txData[2]; + txData[0] = PMIC_REG07; // No D+/D– detection + Safety timer not slowed by 2X during input DPM or thermal regulation + // BAT fet disabled/enabled + charge and bat fault INT - PERIPH_WIRE.sendDataMasterWIRE(0x0B | (disabled ? 0x20 : 0x00)); - PERIPH_WIRE.prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); + txData[1] = 0x0B | (disabled ? 0x20 : 0x00); - PERIPH_WIRE.disableWIRE(); + txn = SercomTxn{}; + txn.config = I2C_CFG_STOP; + txn.address = PMIC_ADDRESS; + txn.length = 2; + txn.txPtr = txData; + + PERIPH_WIRE.enqueueWIRE(&txn); + PERIPH_WIRE.startTransmissionWIRE(); } #endif diff --git a/variants/mkrnb1500/variant.cpp b/variants/mkrnb1500/variant.cpp index 3cc0a5921..0a45fe372 100644 --- a/variants/mkrnb1500/variant.cpp +++ b/variants/mkrnb1500/variant.cpp @@ -174,23 +174,29 @@ SERCOM sercom5(SERCOM5); #if defined(USE_BQ24195L_PMIC) #include "wiring_private.h" +#include "SERCOM_Txn.h" #define PMIC_ADDRESS 0x6B #define PMIC_REG01 0x01 #define PMIC_REG07 0x07 -static inline void enable_battery_charging() { +static inline void enable_battery_charging() { PERIPH_WIRE.initMasterWIRE(100000); PERIPH_WIRE.enableWIRE(); pinPeripheral(PIN_WIRE_SDA, g_APinDescription[PIN_WIRE_SDA].ulPinType); pinPeripheral(PIN_WIRE_SCL, g_APinDescription[PIN_WIRE_SCL].ulPinType); - PERIPH_WIRE.startTransmissionWIRE( PMIC_ADDRESS, WIRE_WRITE_FLAG ); - PERIPH_WIRE.sendDataMasterWIRE(PMIC_REG01); - PERIPH_WIRE.sendDataMasterWIRE(0x1B); // Charge Battery + Minimum System Voltage 3.5V - PERIPH_WIRE.prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); + static SercomTxn txn; + static uint8_t txData[2] = {PMIC_REG01, 0x1B}; // Charge Battery + Minimum System Voltage 3.5V - PERIPH_WIRE.disableWIRE(); + txn = SercomTxn{}; + txn.config = I2C_CFG_STOP; + txn.address = PMIC_ADDRESS; + txn.length = 2; + txn.txPtr = txData; + + PERIPH_WIRE.enqueueWIRE(&txn); + PERIPH_WIRE.startTransmissionWIRE(); } static inline void disable_battery_fet(bool disabled) { @@ -199,14 +205,21 @@ static inline void disable_battery_fet(bool disabled) { pinPeripheral(PIN_WIRE_SDA, g_APinDescription[PIN_WIRE_SDA].ulPinType); pinPeripheral(PIN_WIRE_SCL, g_APinDescription[PIN_WIRE_SCL].ulPinType); - PERIPH_WIRE.startTransmissionWIRE( PMIC_ADDRESS, WIRE_WRITE_FLAG ); - PERIPH_WIRE.sendDataMasterWIRE(PMIC_REG07); + static SercomTxn txn; + static uint8_t txData[2]; + txData[0] = PMIC_REG07; // No D+/D– detection + Safety timer not slowed by 2X during input DPM or thermal regulation + // BAT fet disabled/enabled + charge and bat fault INT - PERIPH_WIRE.sendDataMasterWIRE(0x0B | (disabled ? 0x20 : 0x00)); - PERIPH_WIRE.prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); - - PERIPH_WIRE.disableWIRE(); + txData[1] = 0x0B | (disabled ? 0x20 : 0x00); + + txn = SercomTxn{}; + txn.config = I2C_CFG_STOP; + txn.address = PMIC_ADDRESS; + txn.length = 2; + txn.txPtr = txData; + + PERIPH_WIRE.enqueueWIRE(&txn); + PERIPH_WIRE.startTransmissionWIRE(); } #endif diff --git a/variants/mkrwifi1010/variant.cpp b/variants/mkrwifi1010/variant.cpp index f1938506b..7634df70a 100644 --- a/variants/mkrwifi1010/variant.cpp +++ b/variants/mkrwifi1010/variant.cpp @@ -174,6 +174,7 @@ SERCOM sercom5(SERCOM5); #if defined(USE_BQ24195L_PMIC) #include "wiring_private.h" +#include "SERCOM_Txn.h" #define PMIC_ADDRESS 0x6B #define PMIC_REG01 0x01 @@ -185,12 +186,17 @@ static inline void enable_battery_charging() { pinPeripheral(PIN_WIRE_SDA, g_APinDescription[PIN_WIRE_SDA].ulPinType); pinPeripheral(PIN_WIRE_SCL, g_APinDescription[PIN_WIRE_SCL].ulPinType); - PERIPH_WIRE.startTransmissionWIRE( PMIC_ADDRESS, WIRE_WRITE_FLAG ); - PERIPH_WIRE.sendDataMasterWIRE(PMIC_REG01); - PERIPH_WIRE.sendDataMasterWIRE(0x1B); // Charge Battery + Minimum System Voltage 3.5V - PERIPH_WIRE.prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); + static SercomTxn txn; + static uint8_t txData[2] = {PMIC_REG01, 0x1B}; // Charge Battery + Minimum System Voltage 3.5V - PERIPH_WIRE.disableWIRE(); + txn = SercomTxn{}; + txn.config = I2C_CFG_STOP; + txn.address = PMIC_ADDRESS; + txn.length = 2; + txn.txPtr = txData; + + PERIPH_WIRE.enqueueWIRE(&txn); + PERIPH_WIRE.startTransmissionWIRE(); } static inline void disable_battery_fet(bool disabled) { @@ -199,14 +205,21 @@ static inline void disable_battery_fet(bool disabled) { pinPeripheral(PIN_WIRE_SDA, g_APinDescription[PIN_WIRE_SDA].ulPinType); pinPeripheral(PIN_WIRE_SCL, g_APinDescription[PIN_WIRE_SCL].ulPinType); - PERIPH_WIRE.startTransmissionWIRE( PMIC_ADDRESS, WIRE_WRITE_FLAG ); - PERIPH_WIRE.sendDataMasterWIRE(PMIC_REG07); + static SercomTxn txn; + static uint8_t txData[2]; + txData[0] = PMIC_REG07; // No D+/D– detection + Safety timer not slowed by 2X during input DPM or thermal regulation + // BAT fet disabled/enabled + charge and bat fault INT - PERIPH_WIRE.sendDataMasterWIRE(0x0B | (disabled ? 0x20 : 0x00)); - PERIPH_WIRE.prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); - - PERIPH_WIRE.disableWIRE(); + txData[1] = 0x0B | (disabled ? 0x20 : 0x00); + + txn = SercomTxn{}; + txn.config = I2C_CFG_STOP; + txn.address = PMIC_ADDRESS; + txn.length = 2; + txn.txPtr = txData; + + PERIPH_WIRE.enqueueWIRE(&txn); + PERIPH_WIRE.startTransmissionWIRE(); } #endif From 7ce665e863a99d1ccd6d79c0e2c10fbe227d015d Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 16 Feb 2026 22:25:25 -0600 Subject: [PATCH 24/79] samd datasheet multiplexing tables as csv --- .../wirepinmux/SAMD21_Device_I2C_Config.csv | 161 ++++++++++++++++++ extras/wirepinmux/SAMD21_Table_2-1.csv | 31 ++++ extras/wirepinmux/SAMD21_Table_2-2.csv | 11 ++ extras/wirepinmux/SAMD21_Table_7-1.csv | 53 ++++++ extras/wirepinmux/SAMD21_Table_7-2.csv | 39 +++++ extras/wirepinmux/SAMD21_Table_7-5.csv | 4 + .../wirepinmux/SAMD51_Device_I2C_Config.csv | 26 +++ extras/wirepinmux/SAMD51_Table_1-1.csv | 14 ++ extras/wirepinmux/SAMD51_Table_1-2.csv | 21 +++ extras/wirepinmux/SAMD51_Table_6-1.csv | 101 +++++++++++ extras/wirepinmux/SAMD51_Table_6-8.csv | 10 ++ 11 files changed, 471 insertions(+) create mode 100644 extras/wirepinmux/SAMD21_Device_I2C_Config.csv create mode 100644 extras/wirepinmux/SAMD21_Table_2-1.csv create mode 100644 extras/wirepinmux/SAMD21_Table_2-2.csv create mode 100644 extras/wirepinmux/SAMD21_Table_7-1.csv create mode 100644 extras/wirepinmux/SAMD21_Table_7-2.csv create mode 100644 extras/wirepinmux/SAMD21_Table_7-5.csv create mode 100644 extras/wirepinmux/SAMD51_Device_I2C_Config.csv create mode 100644 extras/wirepinmux/SAMD51_Table_1-1.csv create mode 100644 extras/wirepinmux/SAMD51_Table_1-2.csv create mode 100644 extras/wirepinmux/SAMD51_Table_6-1.csv create mode 100644 extras/wirepinmux/SAMD51_Table_6-8.csv diff --git a/extras/wirepinmux/SAMD21_Device_I2C_Config.csv b/extras/wirepinmux/SAMD21_Device_I2C_Config.csv new file mode 100644 index 000000000..93463cb34 --- /dev/null +++ b/extras/wirepinmux/SAMD21_Device_I2C_Config.csv @@ -0,0 +1,161 @@ +Device,Pin Count,Variant,I2C Pins,SERCOM Configs +SAMD21E14,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E14A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E14B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E14C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E14D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E14L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E15,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E15A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E15B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E15C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E15D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E15L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E16,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E16A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E16B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E16C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E16D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E16L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E17,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E17A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E17B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E17C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E17D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E17L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E18,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E18A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E18B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E18C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E18D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E18L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G14,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G14A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G14B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G14C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G14D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G14L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G15,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G15A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G15B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G15C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G15D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G15L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G16,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G16A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G16B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G16C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G16D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G16L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G17,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G17A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G17B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G17C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G17D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G17L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G18,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G18A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G18B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G18C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G18D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G18L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21J14,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J14A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J14B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J14C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J14D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J15,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J15A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J15B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J15C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J15D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J16,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J16A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J16B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J16C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J16D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J17,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J17A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J17B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J17C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J17D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J18,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J18A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J18B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J18C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J18D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1E14,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E14A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E14B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E14C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E14D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E15,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E15A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E15B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E15C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E15D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E16,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E16A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E16B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E16C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E16D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E17,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E17A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E17B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E17C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E17D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E18,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E18A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E18B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E18C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E18D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G14,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G14A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G14B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G14C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G14D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G15,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G15A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G15B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G15C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G15D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G16,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G16A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G16B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G16C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G16D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G17,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G17A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G17B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G17C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G17D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G18,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G18A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G18B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G18C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G18D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1J14,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J14A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J14B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J14C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J14D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J15,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J15A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J15B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J15C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J15D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J16,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J16A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J16B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J16C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J16D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J17,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J17A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J17B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J17C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J17D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J18,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J18A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J18B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J18C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J18D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" diff --git a/extras/wirepinmux/SAMD21_Table_2-1.csv b/extras/wirepinmux/SAMD21_Table_2-1.csv new file mode 100644 index 000000000..6b5f41f29 --- /dev/null +++ b/extras/wirepinmux/SAMD21_Table_2-1.csv @@ -0,0 +1,31 @@ +Table 2-1. SAM D21 E/G/J and SAM D21 EL/GL Product Family Features,,,,,,,,,,,,,,,,,,,,,, +,,,,,Oscillators,,Peripherals,,,,,,,,,,,,Analog,,, +Device,Program Memory (KB),Data Memory (KB),Pins,Packages,Internal,External,USB,SERCOM,TC-Waveform /PWM Output/Capture Input Channels per TCx Instance,TCC,Waveform /PWM Output Channels per TCCx Instance,I2S,DMA Channels,RTC,WDT,Event System (Channels),External Interrupt Lines,I/O Pins,ADC Channels,Analog Comparator,DAC,"PTC (Mutual/Self-capacitance Channels)""" +ATSAMD21E15A,32,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21E16A,64,8,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21E17A,128,16,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21E18A,256,32,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21E15B,32,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21E16B,64,8,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21E15C,32,4,35,WLCSP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21E16C,64,8,35,WLCSP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21G15A,32,4,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" +ATSAMD21G16A,64,8,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" +ATSAMD21G17A,128,16,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" +ATSAMD21G18A,256,32,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" +ATSAMD21G15B,32,4,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" +ATSAMD21G16B,64,8,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" +ATSAMD21J15A,32,4,64,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" +ATSAMD21J16A,64,8,64,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" +ATSAMD21J17A,128,16,64,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" +ATSAMD21J18A,256,32,64,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" +ATSAMD21J15B,32,4,64,TQFP/QFN/UFBGA,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" +ATSAMD21J16B,64,8,64,TQFP/QFN/UFBGA,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" +ATSAMD21E15L,32,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC,N,4,"3-2",3,"6/4/2",N,12,Y,Y,12,16,26,14,4,Y,N +ATSAMD21E16L,64,8,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC,N,4,"3-2",3,"6/4/2",N,12,Y,Y,12,16,26,14,4,Y,N +ATSAMD21G16L,64,8,48,QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC,Y,6,"5-2",3,"8/4/2",N,12,Y,Y,12,16,38,18,4,Y,N +ATSAMD21E17D,128,16,32,TQFP/QFN/WLCSP,OSC32K/OSCULP32K/OSC8M/DFLL48M,XOSC32K/XOSC,Y,4,"3-2",4,"6/4/2/6",Y,12,Y,Y,12,16,26,10,2,Y,"30/6" +ATSAMD21G17D,128,16,48,QFN/TQFP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",4,"8/4/2/8",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" +ATSAMD21J17D,128,16,64,QFN/TQFP/UFBGA,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",4,"8/4/2/8",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" +ATSAMD21E17L,128,16,32,QFN/TQFP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC,N,4,"3-2",4,"6/4/2/6",N,12,Y,Y,12,16,26,14,4,Y,N +ATSAMD21G17L,128,16,48,QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC,N,6,"5-2",4,"8/4/2/8",N,12,Y,Y,12,16,38,18,4,Y,N \ No newline at end of file diff --git a/extras/wirepinmux/SAMD21_Table_2-2.csv b/extras/wirepinmux/SAMD21_Table_2-2.csv new file mode 100644 index 000000000..999c3f468 --- /dev/null +++ b/extras/wirepinmux/SAMD21_Table_2-2.csv @@ -0,0 +1,11 @@ +Table 2-2. SAM DA1,E/G/J Product Family Features,,,,,,,,,,,,,,,,,,,,, +Device,Program Memory (KB),Data Memory (KB),Pins,Packages,Internal,External,USB,SERCOM,TC-Waveform/PWM Output/Capture Input Channels per TCx Instance,TCC,Waveform/PWM Output Channels per TCCx Instance,I2S,DMA Channels,RTC,WDT,EventSystem (Channels),External Interrupt Lines,I/O Pins,ADC Channels,2,1,PTC (Mutual/Self-capacitance Channels) +ATSAMDA1E14B,16,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,4,"3-2",3,"6/4/2",1,12,Y,Y,12,16,26,10,2,1,"60/6" +ATSAMDA1E15B,32,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,4,"3-2",3,"6/4/2",1,12,Y,Y,12,16,26,10,2,1,"60/6" +ATSAMDA1E16B,64,8,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,4,"3-2",3,"6/4/2",1,12,Y,Y,12,16,26,10,2,1,"60/6" +ATSAMDA1G14B,16,4,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"3-2",3,"8/4/2",1,12,Y,Y,12,16,38,14,2,1,"120/10" +ATSAMDA1G15B,32,4,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"3-2",3,"8/4/2",1,12,Y,Y,12,16,38,14,2,1,"120/10" +ATSAMDA1G16B,64,8,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"3-2",3,"8/4/2",1,12,Y,Y,12,16,38,14,2,1,"120/10" +ATSAMDA1J14B,16,4,64,TQFP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"5-2",3,"8/4/2",1,12,Y,Y,12,16,52,20,2,1,"256/16" +ATSAMDA1J15B,32,4,64,TQFP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"5-2",3,"8/4/2",1,12,Y,Y,12,16,52,20,2,1,"256/16" +ATSAMDA1J16B,64,8,64,TQFP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"5-2",3,"8/4/2",1,12,Y,Y,12,16,52,20,2,1,"256/16" \ No newline at end of file diff --git a/extras/wirepinmux/SAMD21_Table_7-1.csv b/extras/wirepinmux/SAMD21_Table_7-1.csv new file mode 100644 index 000000000..6c4c1b681 --- /dev/null +++ b/extras/wirepinmux/SAMD21_Table_7-1.csv @@ -0,0 +1,53 @@ +SAMD2xE,SAMD2xG,SAMD2xJ,I/O Pin,Supply,A,B1,B2,B3,B4,B5,C,D,E,F,G,H +1,1,1,PA00,VDDANA,EXTINT[0],,,,,,,SERCOM1/PAD[0],TCC2/WO[0],,, +2,2,2,PA01,VDDANA,EXTINT[1],,,,,,,SERCOM1/PAD[1],TCC2/WO[1],,, +3,3,3,PA02,VDDANA,EXTINT[2],,AIN[0],,Y[0],VOUT,,,,TCC3/WO[0],, +4,4,4,PA03,VDDANA,EXTINT[3],ADC/VREFA DAC/VREFA,AIN[1],,Y[1],,,,,TCC3/WO[1],, +,,5,PB04,VDDANA,EXTINT[4],,AIN[12],,Y[10],,,,,,, +,,6,PB05,VDDANA,EXTINT[5],,AIN[13],,Y[11],,,,,,, +,,9,PB06,VDDANA,EXTINT[6],,AIN[14],,Y[12],,,,,,, +,,10,PB07,VDDANA,EXTINT[7],,AIN[15],,Y[13],,,,,,, +,7,11,PB08,VDDANA,EXTINT[8],,AIN[2],,Y[14],,,SERCOM4/PAD[0],TC4/WO[0],TCC3/WO[6],, +,8,12,PB09,VDDANA,EXTINT[9],,AIN[3],,Y[15],,,SERCOM4/PAD[1],TC4/WO[1],TCC3/WO[7],, +5,9,13,PA04,VDDANA,EXTINT[4],ADC/VREFB,AIN[4],AIN[0],Y[2],,,SERCOM0/PAD[0],TCC0/WO[0],TCC3/WO[2],, +6,10,14,PA05,VDDANA,EXTINT[5],,AIN[5],AIN[1],Y[3],,,SERCOM0/PAD[1],TCC0/WO[1],TCC3/WO[3],, +7,11,15,PA06,VDDANA,EXTINT[6],,AIN[6],AIN[2],Y[4],,,SERCOM0/PAD[2],TCC1/WO[0],TCC3/WO[4],, +8,12,16,PA07,VDDANA,EXTINT[7],,AIN[7],AIN[3],Y[5],,,SERCOM0/PAD[3],TCC1/WO[1],TCC3/WO[5],I2S/SD[0], +11,13,17,PA08,VDDIO,NMI,,AIN[16],,X[0],,SERCOM0/PAD[0],SERCOM2/PAD[0],TCC0/WO[0],TCC1/WO[2],I2S/SD[1], +12,14,18,PA09,VDDIO,EXTINT[9],,AIN[17],,X[1],,SERCOM0/PAD[1],SERCOM2/PAD[1],TCC0/WO[1],TCC1/WO[3],I2S/MCK[0], +13,15,19,PA10,VDDIO,EXTINT[10],,AIN[18],,X[2],,SERCOM0/PAD[2],SERCOM2/PAD[2],TCC1/WO[0],TCC0/WO[2],I2S/SCK[0],GCLK_IO[4] +14,16,20,PA11,VDDIO,EXTINT[11],,AIN[19],,X[3],,SERCOM0/PAD[3],SERCOM2/PAD[3],TCC1/WO[1],TCC0/WO[3],I2S/FS[0],GCLK_IO[5] +,19,23,PB10,VDDIO,EXTINT[10],,,,,,,SERCOM4/PAD[2],TC5/WO[0],TCC0/WO[4],I2S/MCK[1],GCLK_IO[4] +,20,24,PB11,VDDIO,EXTINT[11],,,,,,,SERCOM4/PAD[3],TC5/WO[1],TCC0/WO[5],I2S/SCK[1],GCLK_IO[5] +,,25,PB12,VDDIO,EXTINT[12],,,,X[12],,SERCOM4/PAD[0],,TC4/WO[0],TCC0/WO[6],I2S/FS[1],GCLK_IO[6] +,,26,PB13,VDDIO,EXTINT[13],,,,X[13],,SERCOM4/PAD[1],,TC4/WO[1],TCC0/WO[7],,GCLK_IO[7] +,,27,PB14,VDDIO,EXTINT[14],,,,X[14],,SERCOM4/PAD[2],,TC5/WO[0],,,GCLK_IO[0] +,,28,PB15,VDDIO,EXTINT[15],,,,X[15],,SERCOM4/PAD[3],,TC5/WO[1],,,GCLK_IO[1] +,21,29,PA12,VDDIO,EXTINT[12],,,,,,SERCOM2/PAD[0],SERCOM4/PAD[0],TCC2/WO[0],TCC0/WO[6],,AC/CMP[0] +,22,30,PA13,VDDIO,EXTINT[13],,,,,,SERCOM2/PAD[1],SERCOM4/PAD[1],TCC2/WO[1],TCC0/WO[7],,AC/CMP[1] +15,23,31,PA14,VDDIO,EXTINT[14],,,,,,SERCOM2/PAD[2],SERCOM4/PAD[2],TC3/WO[0],TCC0/WO[4],,GCLK_IO[0] +16,24,32,PA15,VDDIO,EXTINT[15],,,,,,SERCOM2/PAD[3],SERCOM4/PAD[3],TC3/WO[1],TCC0/WO[5],,GCLK_IO[1] +17,25,35,PA16,VDDIO,EXTINT[0],,,,X[4],,SERCOM1/PAD[0],SERCOM3/PAD[0],TCC2/WO[0],TCC0/WO[6],,GCLK_IO[2] +18,26,36,PA17,VDDIO,EXTINT[1],,,,X[5],,SERCOM1/PAD[1],SERCOM3/PAD[1],TCC2/WO[1],TCC0/WO[7],,GCLK_IO[3] +19,27,37,PA18,VDDIO,EXTINT[2],,,,X[6],,SERCOM1/PAD[2],SERCOM3/PAD[2],TC3/WO[0],TCC0/WO[2],,AC/CMP[0] +20,28,38,PA19,VDDIO,EXTINT[3],,,,X[7],,SERCOM1/PAD[3],SERCOM3/PAD[3],TC3/WO[1],TCC0/WO[3],I2S/SD[0],AC/CMP[1] +,,39,PB16,VDDIO,EXTINT[0],,,,,,SERCOM5/PAD[0],,TC6/WO[0],TCC0/WO[4],I2S/SD[1],GCLK_IO[2] +,,40,PB17,VDDIO,EXTINT[1],,,,,,SERCOM5/PAD[1],,TC6/WO[1],TCC0/WO[5],I2S/MCK[0],GCLK_IO[3] +,29,41,PA20,VDDIO,EXTINT[4],,,,X[8],,SERCOM5/PAD[2],SERCOM3/PAD[2],TC7/WO[0],TCC0/WO[6],I2S/SCK[0],GCLK_IO[4] +,30,42,PA21,VDDIO,EXTINT[5],,,,X[9],,SERCOM5/PAD[3],SERCOM3/PAD[3],TC7/WO[1],TCC0/WO[7],I2S/FS[0],GCLK_IO[5] +21,31,43,PA22,VDDIO,EXTINT[6],,,,X[10],,SERCOM3/PAD[0],SERCOM5/PAD[0],TC4/WO[0],TCC0/WO[4],,GCLK_IO[6] +22,32,44,PA23,VDDIO,EXTINT[7],,,,X[11],,SERCOM3/PAD[1],SERCOM5/PAD[1],TC4/WO[1],TCC0/WO[5],USB/SOF 1kHz,GCLK_IO[7] +23,33,45,PA24,VDDIO,EXTINT[12],,,,,,SERCOM3/PAD[2],SERCOM5/PAD[2],TC5/WO[0],TCC1/WO[2],USB/DM, +24,34,46,PA25,VDDIO,EXTINT[13],,,,,,SERCOM3/PAD[3],SERCOM5/PAD[3],TC5/WO[1],TCC1/WO[3],USB/DP, +,37,49,PB22,VDDIO,EXTINT[6],,,,,,,SERCOM5/PAD[2],TC7/WO[0],TCC3/WO[0],,GCLK_IO[0] +,38,50,PB23,VDDIO,EXTINT[7],,,,,,,SERCOM5/PAD[3],TC7/WO[1],TCC3/WO[1],,GCLK_IO[1] +25,39,51,PA27,VDDIO,EXTINT[15],,,,,,,,,TCC3/WO[6],,GCLK_IO[0] +27,41,53,PA28,VDDIO,EXTINT[8],,,,,,,,,TCC3/WO[7],,GCLK_IO[0] +31,45,57,PA30,VDDIO,EXTINT[10],,,,,,,SERCOM1/PAD[2],TCC1/WO[0],TCC3/WO[4],SWCLK,GCLK_IO[0] +32,46,58,PA31,VDDIO,EXTINT[11],,,,,,,SERCOM1/PAD[3],TCC1/WO[1],TCC3/WO[5],SWDIO, +,,59,PB30,VDDIO,EXTINT[14],,,,,,,SERCOM5/PAD[0],TCC0/WO[0],TCC1/WO[2],, +,,60,PB31,VDDIO,EXTINT[15],,,,,,,SERCOM5/PAD[1],TCC0/WO[1],TCC1/WO[3],, +,,61,PB00,VDDANA,EXTINT[0],,AIN[8],,Y[6],,,SERCOM5/PAD[2],TC7/WO[0],,, +,,62,PB01,VDDANA,EXTINT[1],,AIN[9],,Y[7],,,SERCOM5/PAD[3],TC7/WO[1],,, +,47,63,PB02,VDDANA,EXTINT[2],,AIN[10],,Y[8],,,SERCOM5/PAD[0],TC6/WO[0],TCC3/WO[2],, +,48,64,PB03,VDDANA,EXTINT[3],,AIN[11],,Y[9],,,SERCOM5/PAD[1],TC6/WO[1],TCC3/WO[3],, \ No newline at end of file diff --git a/extras/wirepinmux/SAMD21_Table_7-2.csv b/extras/wirepinmux/SAMD21_Table_7-2.csv new file mode 100644 index 000000000..e6029598a --- /dev/null +++ b/extras/wirepinmux/SAMD21_Table_7-2.csv @@ -0,0 +1,39 @@ +SAMD21ExL,SAMD21GxL,I/O Pin,Supply,A,B1,B2,B3,B4,B5,C,D,E,F,G,H +1,1,PA02,VDDANA,EXTINT[2],,AIN[0],,,VOUT,,,,TCC3/WO[0],, +2,2,PA03,VDDANA,EXTINT[3],DAC/VREFA,AIN[1],,,,,,,TCC3/WO[1],, +3,3,PB04,VDDANA,EXTINT[4],,AIN[12],,AIN[0],,,,,,, +4,4,PB05,VDDANA,EXTINT[5],,AIN[13],,AIN[1],,,,,,, +,7,PB08,VDDANA,EXTINT[8],,AIN[2],,,,,SERCOM4/PAD[0],TC4/WO[0],TCC3/WO[6],, +,8,PB09,VDDANA,EXTINT[9],,AIN[3],,,,,SERCOM4/PAD[1],TC4/WO[1],TCC3/WO[7],, +5,9,PA04,VDDANA,EXTINT[4],ADC/VREFB,AIN[4],AIN[0],,,,SERCOM0/PAD[0],TCC0/WO[0],TCC3/WO[2],, +6,10,PA05,VDDANA,EXTINT[5],,AIN[5],AIN[1],,,,SERCOM0/PAD[1],TCC0/WO[1],TCC3/WO[3],, +7,11,PA06,VDDANA,EXTINT[6],,AIN[6],AIN[2],,,,SERCOM0/PAD[2],TCC1/WO[0],TCC3/WO[4],, +8,12,PA07,VDDANA,EXTINT[7],,AIN[7],AIN[3],,,,SERCOM0/PAD[3],TCC1/WO[1],TCC3/WO[5],, +11,13,PA08,VDDIO,NMI,,AIN[16],,,,SERCOM0/PAD[0],SERCOM2/PAD[0],TCC0/WO[0],TCC1/WO[2],, +12,14,PA09,VDDIO,EXTINT[9],,AIN[17],,,,SERCOM0/PAD[1],SERCOM2/PAD[1],TCC0/WO[1],TCC1/WO[3],, +13,15,PA10,VDDIO,EXTINT[10],,AIN[18],,,,SERCOM0/PAD[2],SERCOM2/PAD[2],TCC1/WO[0],TCC0/WO[2],,GCLK_IO[4] +14,16,PA11,VDDIO,EXTINT[11],,AIN[19],,,,SERCOM0/PAD[3],SERCOM2/PAD[3],TCC1/WO[1],TCC0/WO[3],,GCLK_IO[5] +,19,PB10,VDDIO,EXTINT[10],,,,,,,SERCOM4/PAD[2],TC5/WO[0],TCC0/WO[4],,GCLK_IO[4] +,20,PB11,VDDIO,EXTINT[11],,,,,,,SERCOM4/PAD[3],TC5/WO[1],TCC0/WO[5],,GCLK_IO[5] +,21,PA12,VDDIO,EXTINT[12],,,,,,SERCOM2/PAD[0],SERCOM4/PAD[0],TCC2/WO[0],TCC0/WO[6],,AC/CMP[0] +,22,PA13,VDDIO,EXTINT[13],,,,,,SERCOM2/PAD[1],SERCOM4/PAD[1],TCC2/WO[1],TCC0/WO[7],,AC/CMP[1] +15,23,PA14,VDDIO,EXTINT[14],,,,,,SERCOM2/PAD[2],SERCOM4/PAD[2],TC3/WO[0],TCC0/WO[4],,GCLK_IO[0] +16,24,PA15,VDDIO,EXTINT[15],,,,,,SERCOM2/PAD[3],SERCOM4/PAD[3],TC3/WO[1],TCC0/WO[5],,GCLK_IO[1] +17,25,PA16,VDDIO,EXTINT[0],,,,,,SERCOM1/PAD[0],SERCOM3/PAD[0],TCC2/WO[0],TCC0/WO[6],,GCLK_IO[2] +18,26,PA17,VDDIO,EXTINT[1],,,,,,SERCOM1/PAD[1],SERCOM3/PAD[1],TCC2/WO[1],TCC0/WO[7],,GCLK_IO[3] +19,27,PA18,VDDIO,EXTINT[2],,,,,,SERCOM1/PAD[2],SERCOM3/PAD[2],TC3/WO[0],TCC0/WO[2],,AC/CMP[0] +20,28,PA19,VDDIO,EXTINT[3],,,,,,SERCOM1/PAD[3],SERCOM3/PAD[3],TC3/WO[1],TCC0/WO[3],,AC/CMP[1] +,29,PA20,VDDIO,EXTINT[4],,,,,,SERCOM5/PAD[2],SERCOM3/PAD[2],TC7/WO[0],TCC0/WO[6],,GCLK_IO[4] +,30,PA21,VDDIO,EXTINT[5],,,,,,SERCOM5/PAD[3],SERCOM3/PAD[3],TC7/WO[1],TCC0/WO[7],,GCLK_IO[5] +21,31,PA22,VDDIO,EXTINT[6],,,,,,SERCOM3/PAD[0],SERCOM5/PAD[0],TC4/WO[0],TCC0/WO[4],,GCLK_IO[6] +22,32,PA23,VDDIO,EXTINT[7],,,,,,SERCOM3/PAD[1],SERCOM5/PAD[1],TC4/WO[1],TCC0/WO[5],,GCLK_IO[7] +23,33,PA24,VDDIO,EXTINT[12],,,,,,SERCOM3/PAD[2],SERCOM5/PAD[2],TC5/WO[0],TCC1/WO[2],,AC1/CMP[0] +24,34,PA25,VDDIO,EXTINT[13],,,,,,SERCOM3/PAD[3],SERCOM5/PAD[3],TC5/WO[1],TCC1/WO[3],,AC1/CMP[1] +,37,PA27,VDDIO,EXTINT[15],,,,,,,,,TCC3/WO[6],,GCLK_IO[0] +,39,PA28,VDDIO,EXTINT[8],,,,,,,,,TCC3/WO[7],,GCLK_IO[0] +29,43,PA30,VDDIO,EXTINT[10],,,,,,,SERCOM1/PAD[2],TCC1/WO[0],TCC3/WO[4],SWCLK,GCLK_IO[0] +30,44,PA31,VDDIO,EXTINT[11],,,,,,,SERCOM1/PAD[3],TCC1/WO[1],TCC3/WO[5],SWDIO, +,45,PB00,,,,AIN[8],,,,,,,,, +,46,PB01,,,,AIN[9],,,,,,,,, +31,47,PB02,VDDANA,EXTINT[2],,AIN[10],,AIN[2],,,SERCOM5/PAD[0],TC6/WO[0],TCC3/WO[2],, +32,48,PB03,VDDANA,EXTINT[3],,AIN[11],,AIN[3],,,SERCOM5/PAD[1],TC6/WO[1],TCC3/WO[3],, \ No newline at end of file diff --git a/extras/wirepinmux/SAMD21_Table_7-5.csv b/extras/wirepinmux/SAMD21_Table_7-5.csv new file mode 100644 index 000000000..4344f607f --- /dev/null +++ b/extras/wirepinmux/SAMD21_Table_7-5.csv @@ -0,0 +1,4 @@ +Device,I/O Pin +32, PA08, PA09, PA16, PA17, PA22, PA23 +48, PA08, PA09, PA12, PA13, PA16, PA17, PA22, PA23 +64, PA08, PA09, PA12, PA13, PA16, PA17, PA22, PA23, PB12, PB13, PB16, PB17, PB30, PB31 \ No newline at end of file diff --git a/extras/wirepinmux/SAMD51_Device_I2C_Config.csv b/extras/wirepinmux/SAMD51_Device_I2C_Config.csv new file mode 100644 index 000000000..cbf74e6b5 --- /dev/null +++ b/extras/wirepinmux/SAMD51_Device_I2C_Config.csv @@ -0,0 +1,26 @@ +Device,Pin Count,I2C Pins,SERCOM Configs +SAMD51G18,48,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAMD51G19,48,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAMD51J18,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAMD51J19,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAMD51J20,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAMD51N19,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAMD51N20,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAMD51P19,120,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PD08,PD09","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]|PD08:[SERCOM7/PAD[0],SERCOM6/PAD[1]]|PD09:[SERCOM7/PAD[1],SERCOM6/PAD[0]]" +SAMD51P20,120,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PD08,PD09","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]|PD08:[SERCOM7/PAD[0],SERCOM6/PAD[1]]|PD09:[SERCOM7/PAD[1],SERCOM6/PAD[0]]" +SAME51G18,48,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME51G19,48,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME51J18,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME51J19,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME51J20,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME51N19,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME51N20,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME53J18,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME53J19,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME53J20,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME53N19,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME53N20,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME54N19,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME54N20,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME54P19,120,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PD08,PD09","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]|PD08:[SERCOM7/PAD[0],SERCOM6/PAD[1]]|PD09:[SERCOM7/PAD[1],SERCOM6/PAD[0]]" +SAME54P20,120,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PD08,PD09","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]|PD08:[SERCOM7/PAD[0],SERCOM6/PAD[1]]|PD09:[SERCOM7/PAD[1],SERCOM6/PAD[0]]" diff --git a/extras/wirepinmux/SAMD51_Table_1-1.csv b/extras/wirepinmux/SAMD51_Table_1-1.csv new file mode 100644 index 000000000..490edd9a4 --- /dev/null +++ b/extras/wirepinmux/SAMD51_Table_1-1.csv @@ -0,0 +1,14 @@ +Table 1-1. SAM E53/E54 Family Features with Ethernet,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,Peripherals,,,,,,,,,,,,,,,,,,,Analog,,,,,Security,,,, +Device,Flash (KB),SRAM (KB) / Backup RAM (KB),Pins,Packages,Ethernet Controller,CAN-FD,SERCOM,TC/Compare,TCC (24-bit/16-bit),I2S,USB,QSPI,SDHC,DMA Channels,PCC (data size),CCL,Position Decoder,RTC,WDT,Frequency Measurement,Event System (Channels),External Interrupt Lines,I/O Pins,ADC (Channels ADC0/ADC1),Analog Comparators (Channels),DAC (Channels),PTC (Mutual/Self-capacitance Channels),Temperature Sensor,AES,TRNG,Public Key Cryptography (PUKCC),Integrity Check Monitor,Tamper Pins +SAME53N20,1024,256 / 8,100,TQFP,Y,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,"256/32",Y,Y,Y,Y,Y,5 +SAME53N19,512,192 / 8,100,TQFP,Y,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,"256/32",Y,Y,Y,Y,Y,5 +SAME53J20,1024,256 / 8,64,"TQFP,VQFN",Y,N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,"256/32",Y,Y,Y,Y,Y,3 +SAME53J19,512,192 / 8,64,"TQFP,VQFN",Y,N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,"256/32",Y,Y,Y,Y,Y,3 +SAME53J18,256,128 / 8,64,"TQFP,VQFN",Y,N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,"256/32",Y,Y,Y,Y,Y,3 +SAME54P20,1024,256 / 8,128,TQFP,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,"256/32",Y,Y,Y,Y,Y,5 +SAME54P20,1024,256 / 8,120,TFBGA,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,"256/32",Y,Y,Y,Y,Y,5 +SAME54P19,512,192 / 8,128,TQFP,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,"256/32",Y,Y,Y,Y,Y,5 +SAME54P19,512,192 / 8,120,TFBGA,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,"256/32",Y,Y,Y,Y,Y,5 +SAME54N20,1024,256 / 8,100,TQFP,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,"256/32",Y,Y,Y,Y,Y,5 +SAME54N19,512,192 / 8,100,TQFP,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,"256/32",Y,Y,Y,Y,Y,5 \ No newline at end of file diff --git a/extras/wirepinmux/SAMD51_Table_1-2.csv b/extras/wirepinmux/SAMD51_Table_1-2.csv new file mode 100644 index 000000000..800a7bd2f --- /dev/null +++ b/extras/wirepinmux/SAMD51_Table_1-2.csv @@ -0,0 +1,21 @@ +Table 1-2. SAM D51/E51 Family Features without Ethernet,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,Peripherals,,,,,,,,,,,,,,,,,,Analog,,,,,Security,,,, +Device,Flash (KB),SRAM (KB) / Backup RAM (KB),Pins,Packages,CAN-FD,SERCOM,TC/Compare,TCC (24-bit/16-bit),I2S,USB,QSPI,SDHC,DMA Channels,PCC (data size),CCL,Position Decoder,RTC,WDT,Frequency Measurement,Event System (Channels),External Interrupt Lines,I/O Pins,ADC (Channels ADC0/ADC1),Analog Comparators (Channels),DAC (Channels),PTC (Mutual/Self-capacitance Channels),Temperature Sensor,AES,TRNG,Public Key Cryptography (PUKCC),Integrity Check Monitor,Tamper Pins +SAMD51P20,1024,256,128,TQFP,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,256/32,Y,Y,Y,Y,Y,5 +SAMD51P20,1024,256,120,TFBGA,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,256/32,Y,Y,Y,Y,Y,5 +SAMD51P19,512,192,128,TQFP,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,256/32,Y,Y,Y,Y,Y,5 +SAMD51P19,512,192,120,TFBGA,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,256/32,Y,Y,Y,Y,Y,5 +SAMD51N20,1024,256,100,TQFP,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,256/32,Y,Y,Y,Y,Y,5 +SAMD51N19,512,192,100,TQFP,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,256/32,Y,Y,Y,Y,Y,5 +SAMD51J20,1024,256,64,"TQFP,VQFN,WLCSP",N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 +SAMD51J19,512,192,64,"TQFP,VQFN,WLCSP",N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 +SAMD51J18,256,128,64,"TQFP,VQFN",N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 +SAMD51G19,512,192,48,VQFN,N,6,"4/2","2/1",N,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,37,"16/4",4,2,121/22,Y,Y,Y,Y,Y,2 +SAMD51G18,256,128,48,VQFN,N,6,"4/2","2/1",N,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,37,"16/4",4,2,121/22,Y,Y,Y,Y,Y,2 +SAME51N20,1024,256,100,TQFP,Y,8,"8/2","2/3",Y,Y,Y,1,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,256/32,Y,Y,Y,Y,Y,5 +SAME51N19,512,192,100,TQFP,Y,8,"8/2","2/3",Y,Y,Y,1,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,256/32,Y,Y,Y,Y,Y,5 +SAME51J20,1024,256,64,"TQFP,VQFN",Y,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 +SAME51J19,512,192,64,"TQFP,VQFN",Y,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 +SAME51J18,256,128,64,"TQFP,VQFN",Y,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 +SAME51G18,256,128,48,VQFN,Y,6,"4/2","2/1",N,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,37,"16/4",4,2,121/22,Y,Y,Y,Y,Y,2 +SAME51G19,512,192,48,VQFN,Y,6,"4/2","2/1",N,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,37,"16/4",4,2,121/22,Y,Y,Y,Y,Y,2 \ No newline at end of file diff --git a/extras/wirepinmux/SAMD51_Table_6-1.csv b/extras/wirepinmux/SAMD51_Table_6-1.csv new file mode 100644 index 000000000..b9171d493 --- /dev/null +++ b/extras/wirepinmux/SAMD51_Table_6-1.csv @@ -0,0 +1,101 @@ +48,64,100,120,128,Pad Name,A,B1,B2,B3,B4,B5,B6,C,D,E,F,G,H,I,J,K,L,M,N +48,64/C6,100,B2,128,PB03,EIC/EXTINT[3],-,ADC0/AIN[15],-,-,-,X21/Y21,-,SERCOM5/PAD[1],TC6/WO[1],-,-,-,-,-,-,-,-,- +1,01/B8,1,A1,1,PA00,EIC/EXTINT[0],-,-,-,-,-,,-,SERCOM1/PAD[0],TC2/WO[0],-,-,-,-,-,-,-,-,- +2,02/C8,2,B1,2,PA01,EIC/EXTINT[1],-,-,-,-,-,,-,SERCOM1/PAD[1],TC2/WO[1],-,-,-,-,-,-,-,-,- +,,3,C1,3,PC00,EIC/EXTINT[0],-,-,ADC1/AIN[10],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +,,4,C2,4,PC01,EIC/EXTINT[1],-,-,ADC1/AIN[11],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +,,5,D1,7,PC02,EIC/EXTINT[2],-,-,ADC1/AIN[4],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +,,6,E2,8,PC03,EIC/EXTINT[3],-,-,ADC1/AIN[5],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +3,03/C7,7,E1,9,PA02,EIC/EXTINT[2],-,ADC0/AIN[0],-,-,DAC/VOUT[0],,-,-,-,-,-,-,-,-,-,-,-,- +4,04/D6,8,F2,10,PA03,EIC/EXTINT[3],ANAREF/VREFA,ADC0/AIN[1],-,-,-,X0/Y0,-,-,-,-,-,-,-,-,-,-,-,- +,05/D7,9,F1,11,PB04,EIC/EXTINT[4],-,-,ADC1/AIN[6],-,-,X22/Y22,-,-,-,-,-,-,-,-,-,-,-,- +,06/D8,10,G1,12,PB05,EIC/EXTINT[5],-,-,ADC1/AIN[7],-,-,X23/Y23,-,-,-,-,-,-,-,-,-,-,-,- +,,-,G2,13,PD00,EIC/EXTINT[0],-,-,ADC1/AIN[14],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +,,-,H1,16,PD01,EIC/EXTINT[1],-,-,ADC1/AIN[15],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +,09/E7,13,H2,17,PB06,EIC/EXTINT[6],-,-,ADC1/AIN[8],-,-,X24/Y24,-,-,-,-,-,-,-,-,-,-,-,CCL/IN[6] +,10/E6,14,J1,18,PB07,EIC/EXTINT[7],-,-,ADC1/AIN[9],-,-,X25/Y25,-,-,-,-,-,-,-,-,-,-,-,CCL/IN[7] +7,11/F5,15,J2,19,PB08,EIC/EXTINT[8],-,ADC0/AIN[2],ADC1/AIN[0],-,-,X1/Y1,-,SERCOM4/PAD[0],TC4/WO[0],-,-,-,-,-,-,-,-,CCL/IN[8] +8,12/F8,16,K1,20,PB09,EIC/EXTINT[9],-,ADC0/AIN[3],ADC1/AIN[1],-,-,X2/Y2,-,SERCOM4/PAD[1],TC4/WO[1],-,-,-,-,-,-,-,-,CCL/OUT[2] +9,13/F7,17,K2,21,PA04,EIC/EXTINT[4],ANAREF/VREFB,ADC0/AIN[4],-,AC/AIN[0],-,X3/Y3,-,SERCOM0/PAD[0],TC0/WO[0],-,-,-,-,-,-,-,-,CCL/IN[0] +10,14/F6,18,L1,22,PA05,EIC/EXTINT[5],-,ADC0/AIN[5],-,AC/AIN[1],DAC/VOUT[1],,-,SERCOM0/PAD[1],TC0/WO[1],-,-,-,-,-,-,-,-,CCL/IN[1] +11,15/G7,19,L2,23,PA06,EIC/EXTINT[6],ANAREF/VREFC,ADC0/AIN[6],-,AC/AIN[2],-,X4/Y4,-,SERCOM0/PAD[2],TC1/WO[0],-,-,-,SDHC0/SDCD,-,-,-,-,CCL/IN[2] +12,16/G8,20,M1,24,PA07,EIC/EXTINT[7],-,ADC0/AIN[7],-,AC/AIN[3],-,X5/Y5,-,SERCOM0/PAD[3],TC1/WO[1],-,-,-,SDHC0/SDWP,-,-,-,-,CCL/OUT[0] +,,-,N1,27,PC04,EIC/EXTINT[4],-,-,-,-,-,,SERCOM6/PAD[0],-,-,TCC0/WO[0],-,-,-,-,-,-,-,- +,,21,N2,28,PC05,EIC/EXTINT[5],-,-,-,-,-,,SERCOM6/PAD[1],-,-,-,-,-,-,-,-,-,-,- +,,22,P1,29,PC06,EIC/EXTINT[6],-,-,-,-,-,,SERCOM6/PAD[2],-,-,-,-,-,SDHC0/SDCD,-,-,-,-,- +,,23,P2,30,PC07,EIC/EXTINT[9],-,-,-,-,-,,SERCOM6/PAD[3],-,-,-,-,-,SDHC0/SDWP,-,-,-,-,- +13,17/H8,26,R1,33,PA08,EIC/NMI,-,ADC0/AIN[8],ADC1/AIN[2],-,-,X6/Y6,SERCOM0/PAD[0],SERCOM2/PAD[1],TC0/WO[0],TCC0/WO[0],TCC1/WO[4],QSPI/DATA[0],SDHC0/SDCMD,I2S/MCK[0],-,-,-,CCL/IN[3] +14,18/G6,27,P3,34,PA09,EIC/EXTINT[9],-,ADC0/AIN[9],ADC1/AIN[3],-,-,X7/Y7,SERCOM0/PAD[1],SERCOM2/PAD[0],TC0/WO[1],TCC0/WO[1],TCC1/WO[5],QSPI/DATA[1],SDHC0/SDDAT[0],I2S/FS[0],-,-,-,CCL/IN[4] +15,19/H7,28,R2,35,PA10,EIC/EXTINT[10],-,ADC0/-AIN[10],,-,-,X8/Y8,SERCOM0/PAD[2],SERCOM2/PAD[2],TC1/WO[0],TCC0/WO[2],TCC1/WO[6],QSPI/DATA[2],SDHC0/SDDAT[1],I2S/SCK[0],-,-,GCLK/IO[4],CCL/IN[5] +16,20/G5,29,P4,36,PA11,EIC/EXTINT[11],-,ADC0/-AIN[11],,-,-,X9/Y9,SERCOM0/PAD[3],SERCOM2/PAD[3],TC1/WO[1],TCC0/WO[3],TCC1/WO[7],QSPI/DATA[3],SDHC0/SDDAT[2],I2S/SDO,-,-,GCLK/IO[5],CCL/OUT[1] +19,23/H6,32,R3,39,PB10,EIC/EXTINT[10],-,-,-,-,-,,-,SERCOM4/PAD[2],TC5/WO[0],TCC0/WO[4],TCC1/WO[0],QSPI/SCK,SDHC0/SDDAT[3],I2S/SDI,-,-,GCLK/IO[4],CCL/IN[11] +20,24/G4,33,P5,40,PB11,EIC/EXTINT[11],-,-,-,-,-,,-,SERCOM4/PAD[3],TC5/WO[1],TCC0/WO[5],TCC1/WO[1],QSPI/CS,SDHC0/SDCK,I2S/FS[1],-,-,GCLK/IO[5],CCL/OUT[1] +,25/H5,34,R4,41,PB12,EIC/EXTINT[12],-,-,-,-,-,X26/Y26,SERCOM4/PAD[0],-,TC4/WO[0],TCC3/WO[0],TCC0/WO[0],CAN1/TX,SDHC0/SDCD,I2S/SCK[1],-,-,GCLK/IO[6],- +,26/H4,35,P6,42,PB13,EIC/EXTINT[13],-,-,-,-,-,X27/Y27,SERCOM4/PAD[1],-,TC4/WO[1],TCC3/WO[1],TCC0/WO[1],CAN1/RX,SDHC0/SDWP,I2S/MCK[1],-,-,GCLK/IO[7],- +,27/G3,36,R5,43,PB14,EIC/EXTINT[14],-,-,-,-,-,X28/Y28,SERCOM4/PAD[2],-,TC5/WO[0],TCC4/WO[0],TCC0/WO[2],CAN1/TX,-,-,PCC/DATA[8],GMAC/GMDC,GCLK/IO[0],CCL/IN[9] +,28/H3,37,P7,44,PB15,EIC/EXTINT[15],-,-,-,-,-,X29/Y29,SERCOM4/PAD[3],-,TC5/WO[1],TCC4/WO[1],TCC0/WO[3],CAN1/RX,-,-,PCC/DATA[9],GMAC/GMDIO,GCLK/IO[1],CCL/IN[10] +,,-,R6,47,PD08,EIC/EXTINT[3],-,-,-,-,-,,SERCOM7/PAD[0],SERCOM6/PAD[1],-,TCC0/WO[1],-,-,-,-,-,-,-,- +,,-,P8,48,PD09,EIC/EXTINT[4],-,-,-,-,-,,SERCOM7/PAD[1],SERCOM6/PAD[0],-,TCC0/WO[2],-,-,-,-,-,-,-,- +,,-,R7,49,PD10,EIC/EXTINT[5],-,-,-,-,-,,SERCOM7/PAD[2],SERCOM6/PAD[2],-,TCC0/WO[3],-,-,-,-,-,-,-,- +,,-,P9,50,PD11,EIC/EXTINT[6],-,-,-,-,-,,SERCOM7/PAD[3],SERCOM6/PAD[3],-,TCC0/WO[4],-,-,-,-,-,-,-,- +,,-,R8,51,PD12,EIC/EXTINT[7],-,-,-,-,-,,-,-,-,TCC0/WO[5],-,-,-,-,-,-,-,- +,,40,P10,52,PC10,EIC/EXTINT[10],-,-,-,-,-,,SERCOM6/PAD[2],SERCOM7/PAD[2],-,TCC0/WO[0],TCC1/WO[4],-,-,-,-,-,-,- +,,41,R9,55,PC11,EIC/EXTINT[11],-,-,-,-,-,,SERCOM6/PAD[3],SERCOM7/PAD[3],-,TCC0/WO[1],TCC1/WO[5],-,-,-,-,GMAC/GMDC,-,- +,,42,R10,56,PC12,EIC/EXTINT[12],-,-,-,-,-,,SERCOM7/PAD[0],SERCOM6/PAD[1],-,TCC0/WO[2],TCC1/WO[6],-,-,-,PCC/DATA[10],GMAC/GMDIO,-,- +,,43,P11,57,PC13,EIC/EXTINT[13],-,-,-,-,-,,SERCOM7/PAD[1],SERCOM6/PAD[0],-,TCC0/WO[3],TCC1/WO[7],-,-,-,PCC/DATA[11],-,-,- +,,44,R11,58,PC14,EIC/EXTINT[14],-,-,-,-,-,,SERCOM7/PAD[2],SERCOM6/PAD[2],-,TCC0/WO[4],TCC1/WO[0],-,-,-,PCC/DATA[12],GMAC/GRX[3],-,- +,,45,P12,59,PC15,EIC/EXTINT[15],-,-,-,-,-,,SERCOM7/PAD[3],SERCOM6/PAD[3],-,TCC0/WO[5],TCC1/WO[1],-,-,-,PCC/DATA[13],GMAC/GRX[2],-,- +21,29/F2,46,R12,60,PA12,EIC/EXTINT[12],-,-,-,-,-,,SERCOM2/PAD[0],SERCOM4/PAD[1],TC2/WO[0],TCC0/WO[6],TCC1/WO[2],-,SDHC0/SDCD,-,PCC/DEN1,GMAC/GRX[1],AC/CMP[0],- +22,30/G2,47,P13,61,PA13,EIC/EXTINT[13],-,-,-,-,-,,SERCOM2/PAD[1],SERCOM4/PAD[0],TC2/WO[1],TCC0/WO[7],TCC1/WO[3],-,SDHC0/SDWP,-,PCC/DEN2,GMAC/GRX[0],AC/CMP[1],- +23,31/H1,48,R13,62,PA14,EIC/EXTINT[14],-,-,-,-,-,,SERCOM2/PAD[2],SERCOM4/PAD[2],TC3/WO[0],TCC2/WO[0],TCC1/WO[2],-,-,-,PCC/CLK,GMAC/GTXCK,GCLK/IO[0],- +24,32/H2,49,R14,63,PA15,EIC/EXTINT[15],-,-,-,-,-,,SERCOM2/PAD[3],SERCOM4/PAD[3],TC3/WO[1],TCC2/WO[1],TCC1/WO[3],-,-,-,-,GMAC/GRXER,GCLK/IO[1],- +25,35/G1,52,R15,66,PA16,EIC/EXTINT[0],-,-,-,-,-,X10/Y10,SERCOM1/PAD[0],SERCOM3/PAD[1],TC2/WO[0],TCC1/WO[0],TCC0/WO[4],-,-,-,PCC/DATA[0],GMAC/GCRS/GRXDV(6),GCLK/IO[2],CCL/IN[0] +26,36/F1,53,P14,67,PA17,EIC/EXTINT[1],-,-,-,-,-,X11/Y11,SERCOM1/PAD[1],SERCOM3/PAD[0],TC2/WO[1],TCC1/WO[1],TCC0/WO[5],-,-,-,PCC/DATA[1],GMAC/GTXEN,GCLK/IO[3],CCL/IN[1] +27,37/E1,54,P15,68,PA18,EIC/EXTINT[2],-,-,-,-,-,X12/Y12,SERCOM1/PAD[2],SERCOM3/PAD[2],TC3/WO[0],TCC1/WO[2],TCC0/WO[6],-,-,-,PCC/DATA[2],GMAC/GTX[0],AC/CMP[0],CCL/IN[2] +28,38/E2,55,N14,69,PA19,EIC/EXTINT[3],-,-,-,-,-,X13/Y13,SERCOM1/PAD[3],SERCOM3/PAD[3],TC3/WO[1],TCC1/WO[3],TCC0/WO[7],-,-,-,PCC/DATA[3],GMAC/GTX[1],AC/CMP[1],CCL/OUT[0] +,,56,N15,70,PC16,EIC/EXTINT[0],-,-,-,-,-,,SERCOM6/PAD[0],SERCOM0/PAD[1],-,TCC0/WO[0],PDEC/PDEC[0],-,-,-,-,GMAC/GTX[2],-,- +,,57,M14,71,PC17,EIC/EXTINT[1],-,-,-,-,-,,SERCOM6/PAD[1],SERCOM0/PAD[0],-,TCC0/WO[1],PDEC/PDEC[1],-,-,-,-,GMAC/GTX[3],-,- +,,58,M15,72,PC18,EIC/EXTINT[2],-,-,-,-,-,,SERCOM6/PAD[2],SERCOM0/PAD[2],-,TCC0/WO[2],PDEC/PDEC[2],-,-,-,-,GMAC/GRXCK,-,- +,,59,L14,73,PC19,EIC/EXTINT[3],-,-,-,-,-,,SERCOM6/PAD[3],SERCOM0/PAD[3],-,TCC0/WO[3],-,-,-,-,-,GMAC/GTXER,-,- +,,60,L15,74,PC20,EIC/EXTINT[4],-,-,-,-,-,,-,-,-,TCC0/WO[4],-,-,SDHC1/SDCD,-,-,GMAC/GRXDV,-,CCL/IN[9] +,,61,K14,75,PC21,EIC/EXTINT[5],-,-,-,-,-,,-,-,-,TCC0/WO[5],-,-,SDHC1/SDWP,-,-,GMAC/GCOL,-,CCL/IN[10] +,,-,K15,76,PC22,EIC/EXTINT[6],-,-,-,-,-,,SERCOM1/PAD[0],SERCOM3/PAD[1],-,TCC0/WO[6],-,-,-,-,-,GMAC/GMDC,-,- +,,-,J14,77,PC23,EIC/EXTINT[7],-,-,-,-,-,,SERCOM1/PAD[1],SERCOM3/PAD[0],-,TCC0/WO[7],-,-,-,-,-,GMAC/GMDIO,-,- +,,-,J15,80,PD20,EIC/EXTINT[10],-,-,-,-,-,,SERCOM1/PAD[2],SERCOM3/PAD[2],-,TCC1/WO[0],-,-,SDHC1/SDCD,-,-,-,-,- +,,-,H14,81,PD21,EIC/EXTINT[11],-,-,-,-,-,,SERCOM1/PAD[3],SERCOM3/PAD[3],-,TCC1/WO[1],-,-,SDHC1/SDWP,-,-,-,-,- +,39/D4,64,H15,82,PB16,EIC/EXTINT[0],-,-,-,-,-,,SERCOM5/PAD[0],-,TC6/WO[0],TCC3/WO[0],TCC0/WO[4],-,SDHC1/SDCD,I2S/SCK[0],-,-,GCLK/IO[2],CCL/IN[11] +,40/D1,65,G15,83,PB17,EIC/EXTINT[1],-,-,-,-,-,,SERCOM5/PAD[1],-,TC6/WO[1],TCC3/WO[1],TCC0/WO[5],-,SDHC1/SDWP,I2S/MCK[0],-,-,GCLK/IO[3],CCL/OUT[3] +,,66,G14,84,PB18,EIC/EXTINT[2],-,-,-,-,-,,SERCOM5/PAD[2],SERCOM7/PAD[2],-,TCC1/WO[0],PDEC/PDEC[0],-,SDHC1/SDDAT[0],-,-,-,GCLK/IO[4],- +,,67,F15,85,PB19,EIC/EXTINT[3],-,-,-,-,-,,SERCOM5/PAD[3],SERCOM7/PAD[3],-,TCC1/WO[1],PDEC/PDEC[1],-,SDHC1/SDDAT[1],-,-,-,GCLK/IO[5],- +,,68,F14,86,PB20,EIC/EXTINT[4],-,-,-,-,-,,SERCOM3/PAD[0],SERCOM7/PAD[1],-,TCC1/WO[2],PDEC/PDEC[2],-,SDHC1/SDDAT[2],-,-,-,GCLK/IO[6],- +,,69,E15,87,PB21,EIC/EXTINT[5],-,-,-,-,-,,SERCOM3/PAD[1],SERCOM7/PAD[0],-,TCC1/WO[3],-,-,SDHC1/SDDAT[3],-,-,-,GCLK/IO[7],- +29,41/D2,70,E14,88,PA20,EIC/EXTINT[4],-,-,-,-,-,X14/Y14,SERCOM5/PAD[2],SERCOM3/PAD[2],TC7/WO[0],TCC1/WO[4],TCC0/WO[0],-,SDHC1/SDCMD,I2S/FS[0],PCC/DATA[4],GMAC/GMDC,-,- +30,42/D3,71,D15,89,PA21,EIC/EXTINT[5],-,-,-,-,-,X15/Y15,SERCOM5/PAD[3],SERCOM3/PAD[3],TC7/WO[1],TCC1/WO[5],TCC0/WO[1],-,SDHC1/SDCK,I2S/SDO,PCC/DATA[5],GMAC/GMDIO,-,- +31,43/C1,72,D14,92,PA22,EIC/EXTINT[6],-,-,-,-,-,X16/Y16,SERCOM3/PAD[0],SERCOM5/PAD[1],TC4/WO[0],TCC1/WO[6],TCC0/WO[2],-,CAN0/TX,I2S/SDI,PCC/DATA[6],-,-,CCL/IN[6] +32,44/C2,73,C14,93,PA23,EIC/EXTINT[7],-,-,-,-,-,X17/Y17,SERCOM3/PAD[1],SERCOM5/PAD[0],TC4/WO[1],TCC1/WO[7],TCC0/WO[3],USB/SOF_1KHZ,CAN0/RX,I2S/FS[1],PCC/DATA[7],-,-,CCL/IN[7] +33,45/B1,74,C15,94,PA24,EIC/EXTINT[8],-,-,-,-,-,,SERCOM3/PAD[2],SERCOM5/PAD[2],TC5/WO[0],TCC2/WO[2],PDEC/PDEC[0],USB/DM,CAN0/TX,-,-,-,-,CCL/IN[8] +34,46/A1,75,B15,95,PA25,EIC/EXTINT[9],-,-,-,-,-,,SERCOM3/PAD[3],SERCOM5/PAD[3],TC5/WO[1],-,PDEC/PDEC[1],USB/DP,CAN0/RX,-,-,-,-,CCL/OUT[2] +37,49/A2,78,A15,98,PB22,EIC/EXTINT[6],-,-,-,-,-,,SERCOM1/PAD[2],SERCOM5/PAD[2],TC7/WO[0],-,PDEC/PDEC[2],USB/SOF_1KHZ,-,-,-,-,GCLK/IO[0],CCL/IN[0] +38,50/A3,79,A14,99,PB23,EIC/EXTINT[7],-,-,-,-,-,,SERCOM1/PAD[3],SERCOM5/PAD[3],TC7/WO[1],-,PDEC/PDEC[0],-,-,-,-,-,GCLK/IO[1],CCL/OUT[0] +,,80,B14,100,PB24,EIC/EXTINT[8],-,-,-,-,-,,SERCOM0/PAD[0],SERCOM2/PAD[1],-,-,PDEC/PDEC[1],-,-,-,-,-,AC/CMP[0],- +,,81,B13,101,PB25,EIC/EXTINT[9],-,-,-,-,-,,SERCOM0/PAD[1],SERCOM2/PAD[0],-,-,PDEC/PDEC[2],-,-,-,-,-,AC/CMP[1],- +,,-,A13,102,PB26,EIC/EXTINT[12],-,-,-,-,-,,SERCOM2/PAD[0],SERCOM4/PAD[1],-,TCC1/WO[2],-,-,-,-,-,-,-,- +,,-,B12,103,PB27,EIC/EXTINT[13],-,-,-,-,-,,SERCOM2/PAD[1],SERCOM4/PAD[0],-,TCC1/WO[3],-,-,-,-,-,-,-,- +,,-,A12,104,PB28,EIC/EXTINT[14],-,-,-,-,-,,SERCOM2/PAD[2],SERCOM4/PAD[2],-,TCC1/WO[4],-,-,-,I2S/SCK[1],-,-,-,- +,,-,B11,105,PB29,EIC/EXTINT[15],-,-,-,-,-,,SERCOM2/PAD[3],SERCOM4/PAD[3],-,TCC1/WO[5],-,-,-,I2S/MCK[1],-,-,-,- +,,82,A11,108,PC24,EIC/EXTINT[8],-,-,-,-,-,,SERCOM0/PAD[2],SERCOM2/PAD[2],-,-,-,CORTEX_CM4/TRACEDATA[3],-,-,-,-,-,- +,,83,B10,109,PC25,EIC/EXTINT[9],-,-,-,-,-,,SERCOM0/PAD[3],SERCOM2/PAD[3],-,-,-,CORTEX_CM4/TRACEDATA[2],-,-,-,-,-,- +,,84,A10,110,PC26,EIC/EXTINT[10],-,-,-,-,-,,-,-,-,-,-,CORTEX_CM4/TRACEDATA[1],-,-,-,-,-,- +,,85,A9,111,PC27,EIC/EXTINT[11],-,-,-,-,-,,SERCOM1/PAD[0],-,-,-,-,CORTEX_CM4/TRACECLK,-,-,-,-,CORTEX_M4/SWO,CCL/IN[4] +,,86,B9,112,PC28,EIC/EXTINT[12],-,-,-,-,-,,SERCOM1/PAD[1],-,-,-,-,CORTEX_CM4/TRACEDATA[0],-,-,-,-,-,CCL/IN[5] +39,51/B3,87,B8,113,PA27,EIC/EXTINT[11],-,-,-,-,-,X18/Y18,-,-,-,-,-,-,-,-,-,GCLK/IO[1],-, +40,52/B4,88,A8,114,RESET_N,-,-,-,-,-,-,,-,-,-,-,-,-,-,-,-,-,-,- +45,57/C5,93,B7,119,PA30,EIC/EXTINT[14],-,-,-,-,-,X19/Y19,SERCOM7/PAD[2],SERCOM1/PAD[2],TC6/WO[0],TCC2/WO[0],-,CORTEX_CM4/SWCLK,-,-,-,-,GCLK/IO[0],CCL/IN[3] +46,58/D5,94,B6,120,PA31,EIC/EXTINT[15],-,-,-,-,-,,SERCOM7/PAD[3],SERCOM1/PAD[3],TC6/WO[1],TCC2/WO[1],--,CORTEX_CM4/SWDIO,-,-,-,-,-,CCL/OUT[1] +,59/A6,95,A5,121,PB30,EIC/EXTINT[14],-,-,-,-,-,,SERCOM7/PAD[0],SERCOM5/PAD[1],TC0/WO[0],TCC4/WO[0],TCC0/WO[6],CORTEX_CM4/SWO,-,-,-,-,-,- +,60/B6,96,B5,122,PB31,EIC/EXTINT[15],-,-,-,-,-,,SERCOM7/PAD[1],SERCOM5/PAD[0],TC0/WO[1],TCC4/WO[1],TCC0/WO[7],,-,-,-,-,-,- +,,-,A4,123,PC30,EIC/EXTINT[14],-,-,ADC1/AIN[12],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +,,-,B4,124,PC31,EIC/EXTINT[15],-,-,ADC1/AIN[13],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +,61/A7,97,A3,125,PB00,EIC/EXTINT[0],-,ADC0/AIN[12],-,-,-,X30/Y30,-,SERCOM5/PAD[2],TC7/WO[0],-,-,-,-,-,-,-,-,CCL/IN[1] +,62/B7,98,B3,126,PB01,EIC/EXTINT[1],-,ADC0/AIN[13],-,-,-,X31/Y31,-,SERCOM5/PAD[3],TC7/WO[1],-,-,-,-,-,-,-,-,CCL/IN[2] +47,63/A8,99,A2,127,PB02,EIC/EXTINT[2],-,ADC0/AIN[14],-,-,-,X20/Y20,-,SERCOM5/PAD[0],TC6/WO[0],TCC2/WO[2],-,-,-,-,-,-,-,CCL/OUT[0] \ No newline at end of file diff --git a/extras/wirepinmux/SAMD51_Table_6-8.csv b/extras/wirepinmux/SAMD51_Table_6-8.csv new file mode 100644 index 000000000..85923703f --- /dev/null +++ b/extras/wirepinmux/SAMD51_Table_6-8.csv @@ -0,0 +1,10 @@ +Device,Supply,I/O Pin +128,VDDIOB, PA08, PA09 +128,VDDIO,PA12, PA13, PA16, PA17, PA22, PA23, PD08, PD09 +120,VDDIOB, PA08, PA09 +120,VDDIO, PA12, PA13, PA16, PA17, PA22, PA23, PD08, PD09 +100,VDDIOB, PA08, PA09 +100,VDDIO, PA12, PA13, PA16, PA17, PA22, PA23 +64,VDDIOB, PA08, PA09 +64,VDDIO, PA12, PA13, PA16, PA17, PA22, PA23 +48,VDDIO, PA08, PA09, PA12, PA13, PA16, PA17, PA22, PA23 \ No newline at end of file From 32284889e1b17455749b09f0e087e15572afe831 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 16 Feb 2026 22:27:28 -0600 Subject: [PATCH 25/79] script to generate wire sercom specific pin assignments can be used for pin assignment validation --- tools/generate_wirepinmux_inc.py | 570 +++++++++++++++++++++++++++++++ 1 file changed, 570 insertions(+) create mode 100644 tools/generate_wirepinmux_inc.py diff --git a/tools/generate_wirepinmux_inc.py b/tools/generate_wirepinmux_inc.py new file mode 100644 index 000000000..f47df3f38 --- /dev/null +++ b/tools/generate_wirepinmux_inc.py @@ -0,0 +1,570 @@ +#!/usr/bin/env python3 +""" +Generate WirePinMux.inc from SAMD21/DA1 and SAMD51/E5x device tables. + +Workflow: +1. Load device-to-package mappings from Tables 2-1, 2-2 (SAMD21/DA1) and 1-1, 1-2 (SAMD51/E5x) +2. Load I2C pin configurations from Tables 7-5 (SAMD21/DA1) and 6-8 (SAMD5x/E5x) +3. For each pin, parse columns C and D to extract primary/alternate SERCOM+PAD +4. Group devices by series and emit guard blocks with I2C_PIN macros + +Emits: + libraries/Wire/WirePinMux.inc + +Each pin entry: + I2C_PIN(PA08, 0, 0, 2, 1) +where arguments are (pin, primary_sercom, primary_pad, alt_sercom, alt_pad). +If an alternate SERCOM is missing, alt_sercom/pad are set to 255. +""" +from __future__ import annotations + +import csv +import re +from pathlib import Path +from typing import Dict, List, Set, Tuple + +ROOT = Path(__file__).resolve().parent.parent +DATA_DIR = ROOT / "extras" / "wirepinmux" +OUTPUT = ROOT / "libraries" / "Wire" / "WirePinMux.inc" + +# Device-to-package mapping CSVs +SAMD21_TABLE_2_1 = DATA_DIR / "SAMD21_Table_2-1.csv" +SAMD21_TABLE_2_2 = DATA_DIR / "SAMD21_Table_2-2.csv" +SAMD51_TABLE_1_1 = DATA_DIR / "SAMD51_Table_1-1.csv" +SAMD51_TABLE_1_2 = DATA_DIR / "SAMD51_Table_1-2.csv" + +# I2C pin configuration CSVs +SAMD21_TABLE_7_5 = DATA_DIR / "SAMD21_Table_7-5.csv" +SAMD51_TABLE_6_8 = DATA_DIR / "SAMD51_Table_6-8.csv" + +# Regex patterns +SERCOM_PAD_RE = re.compile(r"SERCOM(\d+)/PAD\[(\d+)\]") + +# Fallback when alt is missing +NO_ALT = (255, 255) + + +def load_csv(path: Path, skip_rows: int = 0) -> List[Dict[str, str]]: + """Load CSV file and return list of row dictionaries.""" + with path.open(newline="", encoding="utf-8-sig") as f: # utf-8-sig removes BOM + # Skip header rows if needed + for _ in range(skip_rows): + next(f) + return list(csv.DictReader(f)) + + +def parse_sercom_pad(text: str) -> Tuple[int, int] | None: + """Parse 'SERCOM0/PAD[0]' -> (0, 0), return None if not found.""" + if not text or text.strip() == "": + return None + m = SERCOM_PAD_RE.search(text) + if m: + return (int(m.group(1)), int(m.group(2))) + return None + + +def load_device_pin_map( + csv_paths: List[Tuple[Path, int]], +) -> Dict[str, Set[str]]: + """ + Load device-to-package tables and return device -> set of pins mapping. + + For SAMD21/DA1 (Tables 2-1, 2-2): 'Pins' column indicates package size (32, 48, 64). + For SAMD51/E5x (Tables 1-1, 1-2): 'Pins' column indicates package size (48, 64, 100, 120/128). + + Args: + csv_paths: List of (path, skip_rows) tuples + + We'll cross-reference with the I2C pin tables (7-5, 6-8) which list pins by package size. + """ + device_to_pins: Dict[str, Set[str]] = {} + + for csv_path, skip_rows in csv_paths: + rows = load_csv(csv_path, skip_rows=skip_rows) + for row in rows: + device = row.get("Device", "").strip() + pins_str = row.get("Pins", "").strip() + + if not device or not pins_str: + continue + + # Extract numeric pin count + try: + pin_count = int(pins_str) + except ValueError: + continue + + device_to_pins[device] = {str(pin_count)} + + return device_to_pins + + +def load_i2c_pins_samd21(csv_path: Path) -> Dict[str, List[str]]: + """Load I2C pin list by package from Table 7-5 (SAMD21/DA1).""" + package_to_pins: Dict[str, List[str]] = {} + + rows = load_csv(csv_path) + for row in rows: + package = row.get("Device", "").strip() + if not package: + continue + + pin_tokens: List[str] = [] + for key, val in row.items(): + if key == "Device": + continue + if val is None: + continue + values = val if isinstance(val, list) else [val] + for v in values: + token = v.strip() + if not token: + continue + pin_tokens.extend(t.strip() for t in token.split(",") if t.strip()) + + if not pin_tokens: + continue + + package_to_pins[package] = pin_tokens + + return package_to_pins + + +def load_i2c_pins_samd51(csv_path: Path) -> Dict[str, List[str]]: + """ + Load I2C pin list by package from Table 6-8 (SAMD5x/E5x). + + Each device can have multiple rows (different supplies). Merge pin lists per device: + - VDDIOB rows (if any) come first + - then other supplies (e.g., VDDIO) + Pins are de-duplicated preserving first-seen order. + Returns: package_size -> [pin_names] + """ + package_to_pins: Dict[str, List[str]] = {} + rows = load_csv(csv_path) + + # Collect per-device rows keyed by (device, supply_order) + per_device: Dict[str, List[Tuple[int, List[str]]]] = {} + for row in rows: + device = row.get("Device", "").strip() + supply = row.get("Supply", "").strip().upper() + # Gather pin tokens from all remaining columns (some rows spread pins across columns) + pin_tokens: List[str] = [] + for key, val in row.items(): + if key in {"Device", "Supply"}: + continue + if val is None: + continue + values = val if isinstance(val, list) else [val] + for v in values: + token = v.strip() + if not token: + continue + pin_tokens.extend(t.strip() for t in token.split(",") if t.strip()) + + if not device or not pin_tokens: + continue + pin_names = pin_tokens + + # Order supplies: VDDIOB first (0), others after (1) + supply_order = 0 if supply == "VDDIOB" else 1 + per_device.setdefault(device, []).append((supply_order, pin_names)) + + # Merge per device + for device, pin_lists in per_device.items(): + # Sort by supply order so VDDIOB pins precede + pin_lists.sort(key=lambda x: x[0]) + merged: List[str] = [] + seen: Set[str] = set() + for _, pins in pin_lists: + for pin in pins: + if pin not in seen: + seen.add(pin) + merged.append(pin) + package_to_pins[device] = merged + + return package_to_pins + + +def load_pin_mux_configs( + csv_path: Path, is_samd51: bool = False +) -> Dict[str, Tuple[int, int, int, int]]: + """ + Load pin multiplexing configuration from Table 7-1 (SAMD21) or 6-1 (SAMD51). + + Returns: pin_name -> (primary_sercom, primary_pad, alt_sercom, alt_pad) + + For SAMD21 Table 7-1: columns are organized with column C (primary) and D (alt) for SERCOM + For SAMD51 Table 6-1: columns C (primary) and D (alt) for SERCOM + + Special case: Some pins only have alternate SERCOM (column D only, no column C). + For these, we store them with 255,255 as primary and the SERCOM from D as alternate. + """ + pin_configs: Dict[str, Tuple[int, int, int, int]] = {} + + rows = load_csv(csv_path) + for row in rows: + # Pin name is in 'I/O Pin' column for SAMD21, 'Pad Name' for SAMD51 + pin_name = row.get("Pad Name" if is_samd51 else "I/O Pin", "").strip() + + if not pin_name or not pin_name.startswith("P"): + continue + + # Column C is primary SERCOM, column D is alternate SERCOM + col_c = row.get("C", "").strip() + col_d = row.get("D", "").strip() + + primary = parse_sercom_pad(col_c) + alt = parse_sercom_pad(col_d) + + if primary: + # Pin has primary SERCOM (column C) + primary_sercom, primary_pad = primary + alt_sercom, alt_pad = alt if alt else NO_ALT + pin_configs[pin_name] = (primary_sercom, primary_pad, alt_sercom, alt_pad) + elif alt: + # Pin only has alternate SERCOM (column D only, no column C) + # Store as: no primary (255,255), alternate from column D + alt_sercom, alt_pad = alt + pin_configs[pin_name] = (NO_ALT[0], NO_ALT[1], alt_sercom, alt_pad) + + return pin_configs + + +def macro_candidates(dev: str) -> List[str]: + """ + Return the canonical sam.h device macro for a device name. + + sam.h defines exactly one macro per device with pattern __DEVICENAME__ + where DEVICENAME is the datasheet name with "AT" prefix stripped. + + Examples: + ATSAMD21E18A → __SAMD21E18A__ + ATSAMDA1E14B → __SAMDA1E14B__ + SAMD51J19A → __SAMD51J19A__ + SAME53N20 → __SAME53N20__ + """ + dev_upper = dev.upper() + # Strip "AT" prefix if present (ATSAMD21E18A → SAMD21E18A) + if dev_upper.startswith("AT"): + dev_upper = dev_upper[2:] + return [f"__{dev_upper}__"] + + +def classify_device_series(device: str, pin_count: str) -> str: + """ + Classify device into series based on family and package. + + Returns series macro name like "SAMD21E_SERIES", "SAMD51_120_SERIES", etc. + """ + dev_upper = device.upper() + + # Remove "AT" prefix if present + if dev_upper.startswith("AT"): + dev_upper = dev_upper[2:] + + # SAMD21/DA1 classification by letter (E, G, J) + if dev_upper.startswith("SAMD21E"): + return "SAMD21E_SERIES" + elif dev_upper.startswith("SAMD21G"): + return "SAMD21G_SERIES" + elif dev_upper.startswith("SAMD21J"): + return "SAMD21J_SERIES" + elif dev_upper.startswith("SAMDA1"): + return "SAMDA1_SERIES" + + # SAMD51/E5x classification by pin count + # 120/128-pin packages have 5 SERCOMs (with PD08/PD09) + # Others have 4 SERCOMs + pin_num = int(pin_count) if pin_count.isdigit() else 0 + + if dev_upper.startswith("SAMD51"): + if pin_num >= 120: + return "SAMD51_120_SERIES" + else: + return "SAMD51_SERIES" + elif dev_upper.startswith("SAME51"): + return "SAME51_SERIES" + elif dev_upper.startswith("SAME53"): + return "SAME53_SERIES" + elif dev_upper.startswith("SAME54"): + if pin_num >= 120: + return "SAME54_120_SERIES" + else: + return "SAME54_SERIES" + + return "UNKNOWN_SERIES" + + +def build_series_map() -> ( + Dict[str, Tuple[List[str], List[Tuple[str, Tuple[int, int, int, int]]]]] +): + """ + Build series -> (devices, pins) mapping. + + Returns: { + "SAMD21E_SERIES": ([device_names], [(pin, (s0, p0, s1, p1))]) + } + """ + series_map: Dict[ + str, Tuple[List[str], List[Tuple[str, Tuple[int, int, int, int]]]] + ] = {} + + # Load device-to-package mappings (with skip_rows for header lines) + device_pins_samd21 = load_device_pin_map( + [ + (SAMD21_TABLE_2_1, 2), # Skip 2 header rows + (SAMD21_TABLE_2_2, 1), # Skip 1 header row + ] + ) + device_pins_samd51 = load_device_pin_map( + [ + (SAMD51_TABLE_1_1, 2), # Skip 2 header rows (table title + subheader) + (SAMD51_TABLE_1_2, 2), # Skip 2 header rows (table title + subheader) + ] + ) + + # Load I2C pin configurations by package + i2c_pins_samd21 = load_i2c_pins_samd21(SAMD21_TABLE_7_5) + i2c_pins_samd51 = load_i2c_pins_samd51(SAMD51_TABLE_6_8) + + # Load pin mux configurations (SERCOM mappings from columns C/D) + pin_mux_samd21 = load_pin_mux_configs( + ROOT / "docs" / "SAMD21_Table_7-1.csv", is_samd51=False + ) + pin_mux_samd51 = load_pin_mux_configs( + ROOT / "docs" / "SAMD51_Table_6-1.csv", is_samd51=True + ) + + # Process SAMD21/DA1 devices + for device, pin_counts in device_pins_samd21.items(): + pin_count = list(pin_counts)[0] # Get the pin count + series = classify_device_series(device, pin_count) + + if series not in series_map: + series_map[series] = ([], []) + + series_map[series][0].append(device) + + # Get I2C pins for this package size + if pin_count in i2c_pins_samd21: + pin_list = i2c_pins_samd21[pin_count] + # Resolve SERCOM configs from mux table + resolved_pins = [] + for pin_name in pin_list: + if pin_name in pin_mux_samd21: + resolved_pins.append((pin_name, pin_mux_samd21[pin_name])) + + # Only set pins for the first device in series (all same series share pins) + if not series_map[series][1]: + series_map[series] = (series_map[series][0], resolved_pins) + + # Process SAMD51/E5x devices + for device, pin_counts in device_pins_samd51.items(): + pin_count = list(pin_counts)[0] + series = classify_device_series(device, pin_count) + + if series not in series_map: + series_map[series] = ([], []) + + series_map[series][0].append(device) + + # Get I2C pins for this package size + if pin_count in i2c_pins_samd51: + pin_list = i2c_pins_samd51[pin_count] + # Resolve SERCOM configs from mux table + resolved_pins = [] + for pin_name in pin_list: + if pin_name in pin_mux_samd51: + resolved_pins.append((pin_name, pin_mux_samd51[pin_name])) + + # Only set pins for the first device in series + if not series_map[series][1]: + series_map[series] = (series_map[series][0], resolved_pins) + + return series_map + + +def format_series_macro(series_name: str, device_macros: List[str]) -> List[str]: + """ + Format a series macro with intelligent grouping. + + For SAMD21, group by numeric suffix (15, 16, 17, 18). + For SAMD51/E5x, group by letter prefix (G, J, N, P). + + Returns list of lines including #ifndef, #define, content lines, #endif. + """ + lines: List[str] = [] + lines.append(f"#ifndef {series_name}") + + if len(device_macros) == 1: + lines.append(f"#define {series_name} (defined({device_macros[0]}))") + else: + # Group devices by their variant (numeric for SAMD21, letter for SAMD51/E5x) + groups: Dict[str, List[str]] = {} + + for macro in sorted(device_macros): + # Extract the device name from __DEVICENAME__ + device_name = macro.strip("_") + + # For SAMD21/DA1: extract numeric suffix (15, 16, 17, 18, 14, etc.) + # Examples: SAMD21E15A -> "15", SAMDA1E14B -> "14", SAMD21J18A -> "18" + # Match: (SAMD21|SAMDA1)([DEGJ]?)(\d+) + if "SAMD21" in device_name or "SAMDA1" in device_name: + # Extract the number that appears after the family letter (E, G, J) + match = re.search(r"(SAMD21|SAMDA1)([DEGJ]?)(\d+)", device_name) + if match: + suffix = match.group(3) # "15", "16", "14", etc. + else: + suffix = "unknown" + # For SAMD51/E5x: extract letter prefix (G, J, N, P) + # Examples: SAMD51G18 -> "G", SAME51J19 -> "J" + else: + match = re.search(r"(SAM[DE]\d+)([A-Z])\d+", device_name) + if match: + suffix = match.group(2) # "G", "J", "N", etc. + else: + suffix = "unknown" + + if suffix not in groups: + groups[suffix] = [] + groups[suffix].append(macro) + + # Sort groups by their key (numeric or letter order) + sorted_groups = sorted(groups.items(), key=lambda x: x[0]) + + # Build macro output with proper line continuations + output_lines = [] + for group_idx, (suffix, macros) in enumerate(sorted_groups): + is_last = group_idx == len(sorted_groups) - 1 + group_line = " || ".join(f"defined({m})" for m in macros) + + if group_idx == 0: + # First group: start the macro + if is_last: + # Only one group + output_lines.append(f"#define {series_name} ({group_line})") + else: + # Multiple groups + output_lines.append(f"#define {series_name} ({group_line} || \\") + else: + # Continuation lines + if is_last: + # Last group + output_lines.append(f" {group_line})") + else: + # Middle group + output_lines.append(f" {group_line} || \\") + + lines.extend(output_lines) + + lines.append("#endif") + return lines + + +def emit_inc( + series_map: Dict[ + str, Tuple[List[str], List[Tuple[str, Tuple[int, int, int, int]]]] + ], +) -> str: + """Generate the WirePinMux.inc file content.""" + lines: List[str] = [] + lines.append( + "// Auto-generated from SAMD21/SAMD51 device tables. Do not edit manually.\n" + ) + + # Define custom ordering for series (moved here to use in sorting) + series_order = { + "SAMD21E_SERIES": 0, + "SAMD21G_SERIES": 1, + "SAMD21J_SERIES": 2, + "SAMDA1_SERIES": 3, + "SAMD51_120_SERIES": 4, + "SAMD51_SERIES": 5, + "SAME51_SERIES": 5, + "SAME53_SERIES": 5, + "SAME54_SERIES": 5, + "SAME54_120_SERIES": 6, + } + + # Emit series macro definitions in custom order + for series_name in sorted( + series_map.keys(), key=lambda s: (series_order.get(s, 999), s) + ): + devices, _ = series_map[series_name] + + # Build the macro definition + device_checks = [] + for dev in sorted(set(devices)): + device_checks.extend(macro_candidates(dev)) + + # Remove duplicates while preserving order + seen = set() + unique_checks = [] + for check in device_checks: + if check not in seen: + seen.add(check) + unique_checks.append(check) + + # Use the intelligent grouping formatter + lines.extend(format_series_macro(series_name, unique_checks)) + lines.append("") # Add blank line between series + + lines.append("") + + # Fallback: if a TARGET_* flag is set but no series macro matches, default to the base series + lines.append( + "#if defined(TARGET_SAMD21) && !(SAMD21E_SERIES || SAMD21G_SERIES || SAMD21J_SERIES || SAMDA1_SERIES)" + ) + lines.append("#define SAMD21E_SERIES 1") + lines.append("#endif") + lines.append( + "#if defined(TARGET_SAMD51) && !(SAMD51_SERIES || SAMD51_120_SERIES || SAME51_SERIES || SAME53_SERIES || SAME54_SERIES || SAME54_120_SERIES)" + ) + lines.append("#define SAMD51_SERIES 1") + lines.append("#endif") + lines.append("") + + # Group series by their pin configuration + pin_config_groups: Dict[Tuple, List[str]] = {} + for series_name, (_, pins) in series_map.items(): + pin_tuple = tuple((p, s0, p0, s1, p1) for p, (s0, p0, s1, p1) in pins) + if pin_tuple not in pin_config_groups: + pin_config_groups[pin_tuple] = [] + pin_config_groups[pin_tuple].append(series_name) + + # Sort by series order + sorted_groups = sorted( + pin_config_groups.items(), + key=lambda x: (series_order.get(sorted(x[1])[0], 999), sorted(x[1])[0]), + ) + + # Emit pin configurations with guards + for pin_config, series_list in sorted_groups: + # Build guard expression + guard_expr = " || ".join(sorted(series_list)) + lines.append(f"#if {guard_expr}") + + # Emit I2C_PIN macros + for pin_name, s0, p0, s1, p1 in pin_config: + lines.append(f"I2C_PIN({pin_name}, {s0}, {p0}, {s1}, {p1})") + + lines.append("#endif\n") + + return "\n".join(lines) + + +def main() -> None: + """Main entry point.""" + series_map = build_series_map() + inc_text = emit_inc(series_map) + OUTPUT.write_text(inc_text) + + total_devices = sum(len(devices) for devices, _ in series_map.values()) + print( + f"Wrote {len(series_map)} series macros covering {total_devices} devices to {OUTPUT}" + ) + + +if __name__ == "__main__": + main() From cb94be40391079b98eb270dfffe3011590dc7352 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 16 Feb 2026 23:33:44 -0600 Subject: [PATCH 26/79] Fix CI build errors - Fix misleading indentation in retry logic (lines 847, 857) - Remove ambiguous overload for Wire.begin() with integer literals (uint16_t version now requires explicit enableGeneralCall parameter) - Remove unused variable in SPI.cpp - Remove redundant unsigned < 0 check in setPending() --- cores/arduino/SERCOM.cpp | 14 +++++++------- libraries/SPI/SPI.cpp | 2 -- libraries/Wire/Wire.h | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 5855eb6a5..c870cd04f 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -844,11 +844,11 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) if (_wire.retryCount < kMaxWireRetries) { ++_wire.retryCount; - sercom->I2CM.STATUS.bit.BUSSTATE = 1; - while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; + sercom->I2CM.STATUS.bit.BUSSTATE = 1; + while (sercom->I2CM.SYNCBUSY.bit.SYSOP) ; startTransmissionWIRE(); - return txn; + return txn; } } @@ -856,11 +856,11 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) if (_wire.retryCount < kMaxWireRetries) { ++_wire.retryCount; - sercom->I2CM.STATUS.bit.ARBLOST = 1; // Clear arbitration lost flag - sercom->I2CM.INTFLAG.reg = SERCOM_I2CM_INTFLAG_ERROR; + sercom->I2CM.STATUS.bit.ARBLOST = 1; // Clear arbitration lost flag + sercom->I2CM.INTFLAG.reg = SERCOM_I2CM_INTFLAG_ERROR; startTransmissionWIRE(); - return txn; + return txn; } } @@ -1338,7 +1338,7 @@ void SERCOM::clearPads(uint8_t sercomId) void SERCOM::setPending(uint8_t sercomId) { - if (sercomId >= kSercomCount || sercomId < 0) + if (sercomId >= kSercomCount) return; __disable_irq(); diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index be50ba042..b9d646ac2 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -288,8 +288,6 @@ void SPIClass::onService(void) return; } - SercomTxn* txn = _p_sercom->getCurrentTxnSPI(); - if (flags & SERCOM_SPI_INTFLAG_RXC) { // Read completes after write, so read previous byte bool hasMore = _p_sercom->readDataSPI(); diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 02636fafa..730bf17c0 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -41,8 +41,8 @@ class TwoWire : public Stream public: TwoWire(SERCOM *s, uint8_t pinSDA, uint8_t pinSCL); void begin(); - void begin(uint16_t, bool enableGeneralCall = false, uint8_t speed = 0x0, bool enable10Bit = false); void begin(uint8_t, bool enableGeneralCall = false); + void begin(uint16_t, bool enableGeneralCall, uint8_t speed = 0x0, bool enable10Bit = false); void end(); void setClock(uint32_t); From c543bb2af5d351bf88e0b2691c3d116498e2ffc6 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 16 Feb 2026 23:40:05 -0600 Subject: [PATCH 27/79] Make SPI interrupt handlers weak and fix example ambiguity - Add __attribute__((weak)) to all SPI interrupt handlers (SERCOM4, SPI1, etc) This allows variants to override them when SERCOM is used for other peripherals (e.g., MKR variants use SERCOM4 for Serial2/UART) - Explicitly cast slave addresses to uint8_t in Wire examples to avoid any potential overload resolution issues on different compiler versions --- libraries/SPI/SPI.cpp | 10 ++++++++++ .../Wire/examples/slave_receiver/slave_receiver.ino | 2 +- libraries/Wire/examples/slave_sender/slave_sender.ino | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index b9d646ac2..216941e66 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -364,6 +364,7 @@ void SPIClass::detachInterrupt() { #ifndef SPI_IT_HANDLER #define SPI_IT_HANDLER SERCOM4_Handler #endif + void SPI_IT_HANDLER(void) __attribute__ ((weak)); void SPI_IT_HANDLER(void) { SPI.onService(); } #if defined(__SAMD51__) @@ -373,6 +374,10 @@ void SPIClass::detachInterrupt() { #define SPI_IT_HANDLER_2 SERCOM4_2_Handler #define SPI_IT_HANDLER_3 SERCOM4_3_Handler #endif + void SPI_IT_HANDLER_0(void) __attribute__ ((weak)); + void SPI_IT_HANDLER_1(void) __attribute__ ((weak)); + void SPI_IT_HANDLER_2(void) __attribute__ ((weak)); + void SPI_IT_HANDLER_3(void) __attribute__ ((weak)); void SPI_IT_HANDLER_0(void) { SPI.onService(); } void SPI_IT_HANDLER_1(void) { SPI.onService(); } void SPI_IT_HANDLER_2(void) { SPI.onService(); } @@ -383,10 +388,15 @@ void SPIClass::detachInterrupt() { SPIClass SPI1(&PERIPH_SPI1, PIN_SPI1_MISO, PIN_SPI1_SCK, PIN_SPI1_MOSI, PAD_SPI1_TX, PAD_SPI1_RX); #if defined(SPI1_IT_HANDLER) + void SPI1_IT_HANDLER(void) __attribute__ ((weak)); void SPI1_IT_HANDLER(void) { SPI1.onService(); } #endif #if defined(__SAMD51__) && defined(SPI1_IT_HANDLER_0) + void SPI1_IT_HANDLER_0(void) __attribute__ ((weak)); + void SPI1_IT_HANDLER_1(void) __attribute__ ((weak)); + void SPI1_IT_HANDLER_2(void) __attribute__ ((weak)); + void SPI1_IT_HANDLER_3(void) __attribute__ ((weak)); void SPI1_IT_HANDLER_0(void) { SPI1.onService(); } void SPI1_IT_HANDLER_1(void) { SPI1.onService(); } void SPI1_IT_HANDLER_2(void) { SPI1.onService(); } diff --git a/libraries/Wire/examples/slave_receiver/slave_receiver.ino b/libraries/Wire/examples/slave_receiver/slave_receiver.ino index a3103f8b9..5660ac678 100644 --- a/libraries/Wire/examples/slave_receiver/slave_receiver.ino +++ b/libraries/Wire/examples/slave_receiver/slave_receiver.ino @@ -14,7 +14,7 @@ void setup() { - Wire.begin(4); // join i2c bus with address #4 + Wire.begin((uint8_t)4); // join i2c bus with address #4 Wire.onReceive(receiveEvent); // register event Serial.begin(9600); // start serial for output } diff --git a/libraries/Wire/examples/slave_sender/slave_sender.ino b/libraries/Wire/examples/slave_sender/slave_sender.ino index d3b238af9..34f44be9d 100644 --- a/libraries/Wire/examples/slave_sender/slave_sender.ino +++ b/libraries/Wire/examples/slave_sender/slave_sender.ino @@ -14,7 +14,7 @@ void setup() { - Wire.begin(2); // join i2c bus with address #2 + Wire.begin((uint8_t)2); // join i2c bus with address #2 Wire.onRequest(requestEvent); // register event } From b841afb83a2e8b06681649564668093de5277f93 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 20 Feb 2026 10:17:29 -0500 Subject: [PATCH 28/79] add getters for sercom class object --- libraries/Wire/Wire.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 730bf17c0..8170fb86a 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -80,6 +80,8 @@ class TwoWire : public Stream void resetRxBuffer(void); uint8_t* getRxBuffer(void); size_t getRxLength(void) const; + inline SERCOM *getSercom(void) { return sercom; } + inline const SERCOM *getSercom(void) const { return sercom; } inline size_t write(unsigned long n) { return write((uint8_t)n); } inline size_t write(long n) { return write((uint8_t)n); } From edac0609f1b36bad64692137ad2655183ddf8d58 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sat, 21 Feb 2026 15:04:25 -0500 Subject: [PATCH 29/79] add support to allow transaction chaining for uart/spi/i2c --- cores/arduino/SERCOM.cpp | 59 +++++++++++++++++++++++++++++++------- cores/arduino/SERCOM_Txn.h | 1 + 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index c870cd04f..7769b4432 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -284,9 +284,9 @@ bool SERCOM::enqueueUART(SercomTxn* txn) #ifdef USE_ZERODMA if (!_dmaConfigured) return false; -#else - return false; #endif + if (_txnQueue.isFull()) + return false; // Queue full; caller must retry at runtime if (!_txnQueue.store(txn)) return false; if (!_uart.active) { @@ -315,14 +315,31 @@ SercomTxn* SERCOM::stopTransmissionUART(void) SercomTxn* SERCOM::stopTransmissionUART(SercomUartError error) { SercomTxn* txn = nullptr; - if (_txnQueue.read(txn) && txn != nullptr) - { - _uart.active = false; - _uart.currentTxn = nullptr; + if (!_txnQueue.peek(txn) || txn == nullptr) + return nullptr; + + // Call completion callback before deciding to dequeue if (txn->onComplete) txn->onComplete(txn->user, static_cast(error)); + + // Check if callback wants to chain another phase + if (txn->chainNext) { + txn->chainNext = false; // reset for next iteration + if (!startTransmissionUART()) { + // Hardware start failed, force dequeue + _txnQueue.read(txn); + _uart.active = false; + _uart.currentTxn = nullptr; + return txn; + } + return txn; } + // Normal completion: dequeue and start next transaction + _txnQueue.read(txn); + _uart.active = false; + _uart.currentTxn = nullptr; + SercomTxn* next = nullptr; if (_txnQueue.peek(next) && next) startTransmissionUART(); @@ -421,6 +438,8 @@ bool SERCOM::enqueueSPI(SercomTxn* txn) { if (txn == nullptr) return false; + if (_txnQueue.isFull()) + return false; // Queue full; caller must retry at runtime if (!_txnQueue.store(txn)) return false; if (!_spi.active) { @@ -443,14 +462,25 @@ SercomTxn* SERCOM::stopTransmissionSPI(void) SercomTxn* SERCOM::stopTransmissionSPI(SercomSpiError error) { SercomTxn* txn = nullptr; - if (_txnQueue.read(txn) && txn != nullptr) - { - _spi.active = false; - _spi.currentTxn = nullptr; + if (!_txnQueue.peek(txn) || txn == nullptr) + return nullptr; + + // Call completion callback before deciding to dequeue if (txn->onComplete) txn->onComplete(txn->user, static_cast(error)); + + // Check if callback wants to chain another phase + if (txn->chainNext) { + txn->chainNext = false; // reset for next iteration + startTransmissionSPI(); // restart with updated context, same queue slot + return txn; } + // Normal completion: dequeue and start next transaction + _txnQueue.read(txn); + _spi.active = false; + _spi.currentTxn = nullptr; + SercomTxn* next = nullptr; if (_txnQueue.peek(next) && next) startTransmissionSPI(); @@ -816,6 +846,8 @@ bool SERCOM::enqueueWIRE(SercomTxn* txn) { if (txn == nullptr) return false; + if (_txnQueue.isFull()) + return false; // Queue full; caller must retry at runtime if (!_txnQueue.store(txn)) return false; if (!_wire.active) @@ -889,6 +921,13 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) if (txn && txn->onComplete) txn->onComplete(txn->user, static_cast(error)); + // Allow multi-phase I2C transactions to chain without dequeuing. + if (isMasterWIRE() && txn && txn->chainNext) { + txn->chainNext = false; // reset for next iteration + startTransmissionWIRE(); + return txn; + } + if(isMasterWIRE()) _txnQueue.read(txn); // remove the completed transaction from the queue else { diff --git a/cores/arduino/SERCOM_Txn.h b/cores/arduino/SERCOM_Txn.h index ca6365c52..f72aae241 100644 --- a/cores/arduino/SERCOM_Txn.h +++ b/cores/arduino/SERCOM_Txn.h @@ -12,6 +12,7 @@ struct SercomTxn { uint8_t* rxPtr; void (*onComplete)(void* user, int status); void* user; + bool chainNext; // callback sets true to continue transaction with updated context }; // Mirrors WireDMA I2CError values for SERCOM-level reporting. From e97d102eebb19fb3a140809e58fbb9d7a9f3006e Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sun, 22 Feb 2026 14:01:42 -0500 Subject: [PATCH 30/79] finish support for chaining transactions and formatting cleanup --- cores/arduino/SERCOM.cpp | 8 +++--- cores/arduino/Uart.h | 3 ++ libraries/SPI/SPI.cpp | 1 + libraries/SPI/SPI.h | 1 + libraries/Wire/Wire.cpp | 7 ++++- libraries/Wire/Wire.h | 60 ++++++++++++++++++++-------------------- 6 files changed, 45 insertions(+), 35 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 7769b4432..b239c8389 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -319,8 +319,8 @@ SercomTxn* SERCOM::stopTransmissionUART(SercomUartError error) return nullptr; // Call completion callback before deciding to dequeue - if (txn->onComplete) - txn->onComplete(txn->user, static_cast(error)); + if (txn->onComplete) + txn->onComplete(txn->user, static_cast(error)); // Check if callback wants to chain another phase if (txn->chainNext) { @@ -466,8 +466,8 @@ SercomTxn* SERCOM::stopTransmissionSPI(SercomSpiError error) return nullptr; // Call completion callback before deciding to dequeue - if (txn->onComplete) - txn->onComplete(txn->user, static_cast(error)); + if (txn->onComplete) + txn->onComplete(txn->user, static_cast(error)); // Check if callback wants to chain another phase if (txn->chainNext) { diff --git a/cores/arduino/Uart.h b/cores/arduino/Uart.h index 6f59fddf9..5e6ff9df2 100644 --- a/cores/arduino/Uart.h +++ b/cores/arduino/Uart.h @@ -52,6 +52,9 @@ class Uart : public HardwareSerial operator bool() { return true; } + // Accessor for SERCOM peripheral (for direct async transaction enqueuing) + SERCOM* getSercom(void) const { return sercom; } + private: SERCOM *sercom; RingBuffer rxBuffer; diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 216941e66..0501dfb72 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -258,6 +258,7 @@ void SPIClass::transfer(const void *txbuf, void *rxbuf, size_t count, txn->length = count; txn->onComplete = onComplete ? onComplete : &SPIClass::onTxnComplete; txn->user = onComplete ? user : this; + txn->chainNext = false; txnDone = false; txnStatus = 0; diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index 1991fb9d2..533714a0f 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -124,6 +124,7 @@ class SPIClass { void onService(void); void waitForTransfer(void); bool isBusy(void); + SERCOM* getSercom(void) const { return _p_sercom; } // Transaction Functions void usingInterrupt(int interruptNumber); diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index 2fdf8d0a8..750548932 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -105,6 +105,7 @@ uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit, uin // Allocate fresh transaction from pool and copy loader data SercomTxn* txn = allocateTxn(); *txn = loader; + txn->chainNext = false; // For async callbacks, pass txn as user so callback can access txn->rxPtr/length directly // For sync calls, pass 'this' so onTxnComplete can update txnStatus/txnDone @@ -181,11 +182,15 @@ uint8_t TwoWire::endTransmission(bool stopBit, void (*onComplete)(void* user, in // Allocate a fresh transaction from the pool and copy staged data from loader SercomTxn* txn = allocateTxn(); *txn = loader; // Copy staged transaction data + txn->chainNext = false; // Set parameters that weren't known during beginTransmission/write txn->config = stopBit ? I2C_CFG_STOP : 0; txn->onComplete = onComplete ? onComplete : &TwoWire::onTxnComplete; - txn->user = (user == nullptr) ? this : user; + if (onComplete) + txn->user = (user == nullptr) ? txn : user; + else + txn->user = this; awaitingAddressAck = true; txnDone = false; diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 8170fb86a..d66bc9d73 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -50,35 +50,35 @@ class TwoWire : public Stream // If onComplete is nullptr, this blocks for legacy sync behavior. // If onComplete is non-null, this enqueues and returns immediately (async). uint8_t endTransmission(bool stopBit = true, - void (*onComplete)(void* user, int status) = nullptr, - void* user = nullptr); + void (*onComplete)(void *user, int status) = nullptr, + void *user = nullptr); // If onComplete is nullptr, this blocks for legacy sync behavior. // If onComplete is non-null, this enqueues and returns immediately (async). // If rxBuffer is nullptr, the internal buffer is used; otherwise rxBuffer is used. uint8_t requestFrom(uint8_t address, size_t quantity, bool stopBit = true, - uint8_t* rxBuffer = nullptr, - void (*onComplete)(void* user, int status) = nullptr, - void* user = nullptr); + uint8_t *rxBuffer = nullptr, + void (*onComplete)(void *user, int status) = nullptr, + void *user = nullptr); size_t write(uint8_t data); // 3-arg write: when setExternal=true, data is used directly (zero-copy) and // quantity is treated as both length and capacity; subsequent write() calls return 0. // For streaming > WIRE_BUFFER_LENGTH or async usage, call setTxBuffer() before write() // on every transaction. - size_t write(const uint8_t * data, size_t quantity, bool setExternal = false); + size_t write(const uint8_t *data, size_t quantity, bool setExternal = false); virtual int available(void); virtual int read(void); virtual int peek(void); virtual void flush(void); - void onReceive(void(*)(int)); - void onRequest(void(*)(void)); - void setRxBuffer(uint8_t* buffer, size_t length); - void setTxBuffer(uint8_t* buffer, size_t length); + void onReceive(void (*)(int)); + void onRequest(void (*)(void)); + void setRxBuffer(uint8_t *buffer, size_t length); + void setTxBuffer(uint8_t *buffer, size_t length); void clearRxBuffer(void); void resetRxBuffer(void); - uint8_t* getRxBuffer(void); + uint8_t *getRxBuffer(void); size_t getRxLength(void) const; inline SERCOM *getSercom(void) { return sercom; } inline const SERCOM *getSercom(void) const { return sercom; } @@ -92,7 +92,7 @@ class TwoWire : public Stream inline void onService(void); private: - SERCOM * sercom; + SERCOM *sercom; uint8_t _uc_pinSDA; uint8_t _uc_pinSCL; @@ -102,7 +102,7 @@ class TwoWire : public Stream static constexpr size_t WIRE_BUFFER_LENGTH = 255; uint8_t rxBuffer[WIRE_BUFFER_LENGTH]; uint8_t txBuffer[WIRE_BUFFER_LENGTH]; - uint8_t* rxBufferPtr; + uint8_t *rxBufferPtr; size_t rxBufferCapacity; size_t rxLength; size_t rxIndex; @@ -114,22 +114,22 @@ class TwoWire : public Stream bool pendingReceive; int pendingReceiveLength; SercomTxn slaveTxn; - SercomTxn loader; // Staging area for building transactions + SercomTxn loader; // Staging area for building transactions // Transaction pool for async operations (matches SERCOM queue depth) static constexpr size_t TXN_POOL_SIZE = 8; SercomTxn txnPool[TXN_POOL_SIZE]; uint8_t txnPoolHead; - SercomTxn* allocateTxn(); - void freeTxn(SercomTxn* txn); + SercomTxn *allocateTxn(); + void freeTxn(SercomTxn *txn); // Callback user functions void (*onRequestCallback)(void); void (*onReceiveCallback)(int); - static void onTxnComplete(void* user, int status); - static void onDeferredReceive(void* user, int length); + static void onTxnComplete(void *user, int status); + static void onDeferredReceive(void *user, int length); // TWI clock frequency static const uint32_t TWI_CLOCK = 100000; @@ -174,7 +174,7 @@ inline void TwoWire::onService(void) } if (isMaster) { - SercomTxn* txn = sercom->getCurrentTxnWIRE(); + SercomTxn *txn = sercom->getCurrentTxnWIRE(); if (!txn) { sercom->clearINTFLAG(); return; @@ -222,7 +222,7 @@ inline void TwoWire::onService(void) } else { if (flags & SERCOM_I2CS_INTFLAG_ERROR) { - SercomWireError err = SercomWireError::UNKNOWN_ERROR;; + SercomWireError err = SercomWireError::UNKNOWN_ERROR; if (status & SERCOM_I2CS_STATUS_BUSERR) err = SercomWireError::BUS_ERROR; @@ -239,14 +239,14 @@ inline void TwoWire::onService(void) } // To avoid unnecessary clock cycles for register reads, avoid using inline getters - bool isMasterRead = (status & SERCOM_I2CS_STATUS_DIR); // Master Read / Slave Transmit - bool sr = (status & SERCOM_I2CS_STATUS_SR); // Repeated Start detected - bool prec = (flags & SERCOM_I2CS_INTFLAG_PREC); // Stop detected - bool amatch = (flags & SERCOM_I2CS_INTFLAG_AMATCH); // Address Match detected - bool drdy = (flags & SERCOM_I2CS_INTFLAG_DRDY); // Data Ready detected + bool isMasterRead = (status & SERCOM_I2CS_STATUS_DIR); // Master Read / Slave Transmit + bool sr = (status & SERCOM_I2CS_STATUS_SR); // Repeated Start detected + bool prec = (flags & SERCOM_I2CS_INTFLAG_PREC); // Stop detected + bool amatch = (flags & SERCOM_I2CS_INTFLAG_AMATCH); // Address Match detected + bool drdy = (flags & SERCOM_I2CS_INTFLAG_DRDY); // Data Ready detected // Stop or Restart detected - defer receive callback - if(prec || (amatch && sr && !isMasterRead)) + if (prec || (amatch && sr && !isMasterRead)) { pendingReceive = true; pendingReceiveLength = available(); @@ -256,14 +256,14 @@ inline void TwoWire::onService(void) // Address Match - setup transaction // AACKEN enabled: address ACK is automatic, no manual ACK/clear needed - else if(amatch) + else if (amatch) { - if(isMasterRead) // Master Read / Slave TX + if (isMasterRead) // Master Read / Slave TX { // onRequestCallback runs in ISR context here. Deferring to PendSV // would require stalling DRDY or returning 0xFF until the buffer is filled. // onRequestCallback is what will set TwoWire::slaveTxn for the transaction. - if(onRequestCallback) + if (onRequestCallback) onRequestCallback(); // Ensure callback actually set slaveTxn.length; if not, stall with 0-length txn @@ -298,7 +298,7 @@ inline void TwoWire::onService(void) } // Data Ready - handle byte transfer - if(drdy) + if (drdy) { isMasterRead ? sercom->sendDataWIRE() : sercom->readDataWIRE(); From 29728a6edd27381abb20859960bf90d69617c817 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Thu, 2 Apr 2026 18:13:04 -0400 Subject: [PATCH 31/79] allow readDataWIRE() to allow passing deferStopWIRE() on the final read. --- libraries/Wire/Wire.h | 128 +++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 57 deletions(-) diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index d66bc9d73..0eb758325 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -27,7 +27,7 @@ #include "RingBuffer.h" #include - // WIRE_HAS_END means Wire has end() +// WIRE_HAS_END means Wire has end() #define WIRE_HAS_END 1 // NOTE: SAMD21/SAMD51 silicon errata: when I2C master uses SCLSM=1, CTRLB.CMD @@ -38,35 +38,37 @@ class TwoWire : public Stream { - public: - TwoWire(SERCOM *s, uint8_t pinSDA, uint8_t pinSCL); - void begin(); - void begin(uint8_t, bool enableGeneralCall = false); - void begin(uint16_t, bool enableGeneralCall, uint8_t speed = 0x0, bool enable10Bit = false); - void end(); - void setClock(uint32_t); - - void beginTransmission(uint8_t); - // If onComplete is nullptr, this blocks for legacy sync behavior. - // If onComplete is non-null, this enqueues and returns immediately (async). - uint8_t endTransmission(bool stopBit = true, - void (*onComplete)(void *user, int status) = nullptr, - void *user = nullptr); - - // If onComplete is nullptr, this blocks for legacy sync behavior. - // If onComplete is non-null, this enqueues and returns immediately (async). - // If rxBuffer is nullptr, the internal buffer is used; otherwise rxBuffer is used. - uint8_t requestFrom(uint8_t address, size_t quantity, bool stopBit = true, - uint8_t *rxBuffer = nullptr, - void (*onComplete)(void *user, int status) = nullptr, - void *user = nullptr); - - size_t write(uint8_t data); - // 3-arg write: when setExternal=true, data is used directly (zero-copy) and - // quantity is treated as both length and capacity; subsequent write() calls return 0. - // For streaming > WIRE_BUFFER_LENGTH or async usage, call setTxBuffer() before write() - // on every transaction. - size_t write(const uint8_t *data, size_t quantity, bool setExternal = false); +public: + TwoWire(SERCOM *s, uint8_t pinSDA, uint8_t pinSCL); + void begin(); + void begin(uint8_t, bool enableGeneralCall = false); + void begin(uint16_t, bool enableGeneralCall, uint8_t speed = 0x0, + bool enable10Bit = false); + void end(); + void setClock(uint32_t); + + void beginTransmission(uint8_t); + // If onComplete is nullptr, this blocks for legacy sync behavior. + // If onComplete is non-null, this enqueues and returns immediately (async). + uint8_t endTransmission(bool stopBit = true, + void (*onComplete)(void *user, int status) = nullptr, + void *user = nullptr); + + // If onComplete is nullptr, this blocks for legacy sync behavior. + // If onComplete is non-null, this enqueues and returns immediately (async). + // If rxBuffer is nullptr, the internal buffer is used; otherwise rxBuffer is + // used. + uint8_t requestFrom(uint8_t address, size_t quantity, bool stopBit = true, + uint8_t *rxBuffer = nullptr, + void (*onComplete)(void *user, int status) = nullptr, + void *user = nullptr); + + size_t write(uint8_t data); + // 3-arg write: when setExternal=true, data is used directly (zero-copy) and + // quantity is treated as both length and capacity; subsequent write() calls + // return 0. For streaming > WIRE_BUFFER_LENGTH or async usage, call + // setTxBuffer() before write() on every transaction. + size_t write(const uint8_t *data, size_t quantity, bool setExternal = false); virtual int available(void); virtual int read(void); @@ -91,8 +93,21 @@ class TwoWire : public Stream inline void onService(void); - private: - SERCOM *sercom; +#ifdef _DEBUG_ + inline SercomTxn *getSlaveTxn(void) { return &slaveTxn; } + inline const SercomTxn *getSlaveTxn(void) const { return &slaveTxn; } + inline SercomTxn *getLoaderTxn(void) { return &loader; } + inline const SercomTxn *getLoaderTxn(void) const { return &loader; } + inline SercomTxn *getActiveTxn(void) { + return sercom ? sercom->getCurrentTxnWIRE() : nullptr; + } + inline const SercomTxn *getActiveTxn(void) const { + return sercom ? sercom->getCurrentTxnWIRE() : nullptr; + } +#endif // _DEBUG_ + +private: + SERCOM *sercom; uint8_t _uc_pinSDA; uint8_t _uc_pinSCL; @@ -102,7 +117,7 @@ class TwoWire : public Stream static constexpr size_t WIRE_BUFFER_LENGTH = 255; uint8_t rxBuffer[WIRE_BUFFER_LENGTH]; uint8_t txBuffer[WIRE_BUFFER_LENGTH]; - uint8_t *rxBufferPtr; + uint8_t *rxBufferPtr; size_t rxBufferCapacity; size_t rxLength; size_t rxIndex; @@ -115,12 +130,12 @@ class TwoWire : public Stream int pendingReceiveLength; SercomTxn slaveTxn; SercomTxn loader; // Staging area for building transactions - + // Transaction pool for async operations (matches SERCOM queue depth) static constexpr size_t TXN_POOL_SIZE = 8; SercomTxn txnPool[TXN_POOL_SIZE]; uint8_t txnPoolHead; - + SercomTxn *allocateTxn(); void freeTxn(SercomTxn *txn); @@ -136,22 +151,22 @@ class TwoWire : public Stream }; #if WIRE_INTERFACES_COUNT > 0 - extern TwoWire Wire; +extern TwoWire Wire; #endif #if WIRE_INTERFACES_COUNT > 1 - extern TwoWire Wire1; +extern TwoWire Wire1; #endif #if WIRE_INTERFACES_COUNT > 2 - extern TwoWire Wire2; +extern TwoWire Wire2; #endif #if WIRE_INTERFACES_COUNT > 3 - extern TwoWire Wire3; +extern TwoWire Wire3; #endif #if WIRE_INTERFACES_COUNT > 4 - extern TwoWire Wire4; +extern TwoWire Wire4; #endif #if WIRE_INTERFACES_COUNT > 5 - extern TwoWire Wire5; +extern TwoWire Wire5; #endif inline void TwoWire::onService(void) @@ -161,9 +176,9 @@ inline void TwoWire::onService(void) bool isMaster = sercom->isMasterWIRE(); if ((!isMaster && !sercom->isSlaveWIRE()) || flags == 0) { - sercom->clearINTFLAG(); - return; - } + sercom->clearINTFLAG(); + return; + } if (status & SERCOM_I2CM_STATUS_RXNACK) { sercom->prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); @@ -197,7 +212,7 @@ inline void TwoWire::onService(void) err = SercomWireError::LENGTH_ERROR; if (busState == 0x0) err = SercomWireError::BUS_STATE_UNKNOWN; - + sercom->clearINTFLAG(); sercom->deferStopWIRE(err); return; @@ -206,9 +221,9 @@ inline void TwoWire::onService(void) bool isRead = (txn->config & I2C_CFG_READ); if (sercom->getTxnIndexWIRE() < sercom->getTxnLengthWIRE()) { - isRead ? sercom->readDataWIRE() : sercom->sendDataWIRE(); + bool more = isRead ? sercom->readDataWIRE() : sercom->sendDataWIRE(); awaitingAddressAck = false; - return; + if (!isRead || more) return; } if ((txn->config & I2C_CFG_STOP) && !isRead) @@ -244,7 +259,7 @@ inline void TwoWire::onService(void) bool prec = (flags & SERCOM_I2CS_INTFLAG_PREC); // Stop detected bool amatch = (flags & SERCOM_I2CS_INTFLAG_AMATCH); // Address Match detected bool drdy = (flags & SERCOM_I2CS_INTFLAG_DRDY); // Data Ready detected - + // Stop or Restart detected - defer receive callback if (prec || (amatch && sr && !isMasterRead)) { @@ -253,11 +268,10 @@ inline void TwoWire::onService(void) sercom->deferReceiveWIRE(pendingReceiveLength); return; } - + // Address Match - setup transaction // AACKEN enabled: address ACK is automatic, no manual ACK/clear needed - else if (amatch) - { + else if (amatch) { if (isMasterRead) // Master Read / Slave TX { // onRequestCallback runs in ISR context here. Deferring to PendSV @@ -265,12 +279,12 @@ inline void TwoWire::onService(void) // onRequestCallback is what will set TwoWire::slaveTxn for the transaction. if (onRequestCallback) onRequestCallback(); - + // Ensure callback actually set slaveTxn.length; if not, stall with 0-length txn - if (slaveTxn.length == 0) + if (slaveTxn.length == 0) return; - if (!(slaveTxn.config & I2C_CFG_READ)) + if (!(slaveTxn.config & I2C_CFG_READ)) slaveTxn.config |= I2C_CFG_READ; } else // Master Write / Slave RX @@ -288,12 +302,12 @@ inline void TwoWire::onService(void) // SCLSM=0 (Smart Mode disabled): AMATCH and DRDY never fire together // → return now, DRDY will fire in next interrupt - // SCLSM=1 (Smart Mode enabled) + Master Read: AMATCH+DRDY fire together + // SCLSM=1 (Smart Mode enabled) + Master Read: AMATCH+DRDY fire together // → fall through to handle data immediately // SCLSM=1 + Master Write: DRDY not set yet // → return now, DRDY fires later if (!drdy) - return; + return; // else: DRDY is set (SCLSM=1 Master Read case), fall through } @@ -303,7 +317,7 @@ inline void TwoWire::onService(void) isMasterRead ? sercom->sendDataWIRE() : sercom->readDataWIRE(); if (!isMasterRead) - rxLength = sercom->getTxnIndexWIRE(); + rxLength = sercom->getTxnIndexWIRE(); } } } From 0d615d883c326975fbd91fa3550b2ed249ba8dc1 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 6 Apr 2026 08:08:35 -0500 Subject: [PATCH 32/79] Revert "allow readDataWIRE() to allow passing deferStopWIRE() on the final read." This reverts commit ff93e56090ae50c8905d8b51f90c953f33a50202. --- libraries/Wire/Wire.h | 128 +++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 71 deletions(-) diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 0eb758325..d66bc9d73 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -27,7 +27,7 @@ #include "RingBuffer.h" #include -// WIRE_HAS_END means Wire has end() + // WIRE_HAS_END means Wire has end() #define WIRE_HAS_END 1 // NOTE: SAMD21/SAMD51 silicon errata: when I2C master uses SCLSM=1, CTRLB.CMD @@ -38,37 +38,35 @@ class TwoWire : public Stream { -public: - TwoWire(SERCOM *s, uint8_t pinSDA, uint8_t pinSCL); - void begin(); - void begin(uint8_t, bool enableGeneralCall = false); - void begin(uint16_t, bool enableGeneralCall, uint8_t speed = 0x0, - bool enable10Bit = false); - void end(); - void setClock(uint32_t); - - void beginTransmission(uint8_t); - // If onComplete is nullptr, this blocks for legacy sync behavior. - // If onComplete is non-null, this enqueues and returns immediately (async). - uint8_t endTransmission(bool stopBit = true, - void (*onComplete)(void *user, int status) = nullptr, - void *user = nullptr); - - // If onComplete is nullptr, this blocks for legacy sync behavior. - // If onComplete is non-null, this enqueues and returns immediately (async). - // If rxBuffer is nullptr, the internal buffer is used; otherwise rxBuffer is - // used. - uint8_t requestFrom(uint8_t address, size_t quantity, bool stopBit = true, - uint8_t *rxBuffer = nullptr, - void (*onComplete)(void *user, int status) = nullptr, - void *user = nullptr); - - size_t write(uint8_t data); - // 3-arg write: when setExternal=true, data is used directly (zero-copy) and - // quantity is treated as both length and capacity; subsequent write() calls - // return 0. For streaming > WIRE_BUFFER_LENGTH or async usage, call - // setTxBuffer() before write() on every transaction. - size_t write(const uint8_t *data, size_t quantity, bool setExternal = false); + public: + TwoWire(SERCOM *s, uint8_t pinSDA, uint8_t pinSCL); + void begin(); + void begin(uint8_t, bool enableGeneralCall = false); + void begin(uint16_t, bool enableGeneralCall, uint8_t speed = 0x0, bool enable10Bit = false); + void end(); + void setClock(uint32_t); + + void beginTransmission(uint8_t); + // If onComplete is nullptr, this blocks for legacy sync behavior. + // If onComplete is non-null, this enqueues and returns immediately (async). + uint8_t endTransmission(bool stopBit = true, + void (*onComplete)(void *user, int status) = nullptr, + void *user = nullptr); + + // If onComplete is nullptr, this blocks for legacy sync behavior. + // If onComplete is non-null, this enqueues and returns immediately (async). + // If rxBuffer is nullptr, the internal buffer is used; otherwise rxBuffer is used. + uint8_t requestFrom(uint8_t address, size_t quantity, bool stopBit = true, + uint8_t *rxBuffer = nullptr, + void (*onComplete)(void *user, int status) = nullptr, + void *user = nullptr); + + size_t write(uint8_t data); + // 3-arg write: when setExternal=true, data is used directly (zero-copy) and + // quantity is treated as both length and capacity; subsequent write() calls return 0. + // For streaming > WIRE_BUFFER_LENGTH or async usage, call setTxBuffer() before write() + // on every transaction. + size_t write(const uint8_t *data, size_t quantity, bool setExternal = false); virtual int available(void); virtual int read(void); @@ -93,21 +91,8 @@ class TwoWire : public Stream inline void onService(void); -#ifdef _DEBUG_ - inline SercomTxn *getSlaveTxn(void) { return &slaveTxn; } - inline const SercomTxn *getSlaveTxn(void) const { return &slaveTxn; } - inline SercomTxn *getLoaderTxn(void) { return &loader; } - inline const SercomTxn *getLoaderTxn(void) const { return &loader; } - inline SercomTxn *getActiveTxn(void) { - return sercom ? sercom->getCurrentTxnWIRE() : nullptr; - } - inline const SercomTxn *getActiveTxn(void) const { - return sercom ? sercom->getCurrentTxnWIRE() : nullptr; - } -#endif // _DEBUG_ - -private: - SERCOM *sercom; + private: + SERCOM *sercom; uint8_t _uc_pinSDA; uint8_t _uc_pinSCL; @@ -117,7 +102,7 @@ class TwoWire : public Stream static constexpr size_t WIRE_BUFFER_LENGTH = 255; uint8_t rxBuffer[WIRE_BUFFER_LENGTH]; uint8_t txBuffer[WIRE_BUFFER_LENGTH]; - uint8_t *rxBufferPtr; + uint8_t *rxBufferPtr; size_t rxBufferCapacity; size_t rxLength; size_t rxIndex; @@ -130,12 +115,12 @@ class TwoWire : public Stream int pendingReceiveLength; SercomTxn slaveTxn; SercomTxn loader; // Staging area for building transactions - + // Transaction pool for async operations (matches SERCOM queue depth) static constexpr size_t TXN_POOL_SIZE = 8; SercomTxn txnPool[TXN_POOL_SIZE]; uint8_t txnPoolHead; - + SercomTxn *allocateTxn(); void freeTxn(SercomTxn *txn); @@ -151,22 +136,22 @@ class TwoWire : public Stream }; #if WIRE_INTERFACES_COUNT > 0 -extern TwoWire Wire; + extern TwoWire Wire; #endif #if WIRE_INTERFACES_COUNT > 1 -extern TwoWire Wire1; + extern TwoWire Wire1; #endif #if WIRE_INTERFACES_COUNT > 2 -extern TwoWire Wire2; + extern TwoWire Wire2; #endif #if WIRE_INTERFACES_COUNT > 3 -extern TwoWire Wire3; + extern TwoWire Wire3; #endif #if WIRE_INTERFACES_COUNT > 4 -extern TwoWire Wire4; + extern TwoWire Wire4; #endif #if WIRE_INTERFACES_COUNT > 5 -extern TwoWire Wire5; + extern TwoWire Wire5; #endif inline void TwoWire::onService(void) @@ -176,9 +161,9 @@ inline void TwoWire::onService(void) bool isMaster = sercom->isMasterWIRE(); if ((!isMaster && !sercom->isSlaveWIRE()) || flags == 0) { - sercom->clearINTFLAG(); - return; - } + sercom->clearINTFLAG(); + return; + } if (status & SERCOM_I2CM_STATUS_RXNACK) { sercom->prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); @@ -212,7 +197,7 @@ inline void TwoWire::onService(void) err = SercomWireError::LENGTH_ERROR; if (busState == 0x0) err = SercomWireError::BUS_STATE_UNKNOWN; - + sercom->clearINTFLAG(); sercom->deferStopWIRE(err); return; @@ -221,9 +206,9 @@ inline void TwoWire::onService(void) bool isRead = (txn->config & I2C_CFG_READ); if (sercom->getTxnIndexWIRE() < sercom->getTxnLengthWIRE()) { - bool more = isRead ? sercom->readDataWIRE() : sercom->sendDataWIRE(); + isRead ? sercom->readDataWIRE() : sercom->sendDataWIRE(); awaitingAddressAck = false; - if (!isRead || more) return; + return; } if ((txn->config & I2C_CFG_STOP) && !isRead) @@ -259,7 +244,7 @@ inline void TwoWire::onService(void) bool prec = (flags & SERCOM_I2CS_INTFLAG_PREC); // Stop detected bool amatch = (flags & SERCOM_I2CS_INTFLAG_AMATCH); // Address Match detected bool drdy = (flags & SERCOM_I2CS_INTFLAG_DRDY); // Data Ready detected - + // Stop or Restart detected - defer receive callback if (prec || (amatch && sr && !isMasterRead)) { @@ -268,10 +253,11 @@ inline void TwoWire::onService(void) sercom->deferReceiveWIRE(pendingReceiveLength); return; } - + // Address Match - setup transaction // AACKEN enabled: address ACK is automatic, no manual ACK/clear needed - else if (amatch) { + else if (amatch) + { if (isMasterRead) // Master Read / Slave TX { // onRequestCallback runs in ISR context here. Deferring to PendSV @@ -279,12 +265,12 @@ inline void TwoWire::onService(void) // onRequestCallback is what will set TwoWire::slaveTxn for the transaction. if (onRequestCallback) onRequestCallback(); - + // Ensure callback actually set slaveTxn.length; if not, stall with 0-length txn - if (slaveTxn.length == 0) + if (slaveTxn.length == 0) return; - if (!(slaveTxn.config & I2C_CFG_READ)) + if (!(slaveTxn.config & I2C_CFG_READ)) slaveTxn.config |= I2C_CFG_READ; } else // Master Write / Slave RX @@ -302,12 +288,12 @@ inline void TwoWire::onService(void) // SCLSM=0 (Smart Mode disabled): AMATCH and DRDY never fire together // → return now, DRDY will fire in next interrupt - // SCLSM=1 (Smart Mode enabled) + Master Read: AMATCH+DRDY fire together + // SCLSM=1 (Smart Mode enabled) + Master Read: AMATCH+DRDY fire together // → fall through to handle data immediately // SCLSM=1 + Master Write: DRDY not set yet // → return now, DRDY fires later if (!drdy) - return; + return; // else: DRDY is set (SCLSM=1 Master Read case), fall through } @@ -317,7 +303,7 @@ inline void TwoWire::onService(void) isMasterRead ? sercom->sendDataWIRE() : sercom->readDataWIRE(); if (!isMasterRead) - rxLength = sercom->getTxnIndexWIRE(); + rxLength = sercom->getTxnIndexWIRE(); } } } From b84405fadfe2c5bf3c3b698b7cddd8d612b9ba9a Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 13 Apr 2026 11:36:03 -0400 Subject: [PATCH 33/79] Reapply "allow readDataWIRE() to allow passing deferStopWIRE() on the final read." This reverts commit 7f91df10d6f6eb9ad7ec3f7b42a7b468647ede22. --- libraries/Wire/Wire.h | 128 +++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 57 deletions(-) diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index d66bc9d73..0eb758325 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -27,7 +27,7 @@ #include "RingBuffer.h" #include - // WIRE_HAS_END means Wire has end() +// WIRE_HAS_END means Wire has end() #define WIRE_HAS_END 1 // NOTE: SAMD21/SAMD51 silicon errata: when I2C master uses SCLSM=1, CTRLB.CMD @@ -38,35 +38,37 @@ class TwoWire : public Stream { - public: - TwoWire(SERCOM *s, uint8_t pinSDA, uint8_t pinSCL); - void begin(); - void begin(uint8_t, bool enableGeneralCall = false); - void begin(uint16_t, bool enableGeneralCall, uint8_t speed = 0x0, bool enable10Bit = false); - void end(); - void setClock(uint32_t); - - void beginTransmission(uint8_t); - // If onComplete is nullptr, this blocks for legacy sync behavior. - // If onComplete is non-null, this enqueues and returns immediately (async). - uint8_t endTransmission(bool stopBit = true, - void (*onComplete)(void *user, int status) = nullptr, - void *user = nullptr); - - // If onComplete is nullptr, this blocks for legacy sync behavior. - // If onComplete is non-null, this enqueues and returns immediately (async). - // If rxBuffer is nullptr, the internal buffer is used; otherwise rxBuffer is used. - uint8_t requestFrom(uint8_t address, size_t quantity, bool stopBit = true, - uint8_t *rxBuffer = nullptr, - void (*onComplete)(void *user, int status) = nullptr, - void *user = nullptr); - - size_t write(uint8_t data); - // 3-arg write: when setExternal=true, data is used directly (zero-copy) and - // quantity is treated as both length and capacity; subsequent write() calls return 0. - // For streaming > WIRE_BUFFER_LENGTH or async usage, call setTxBuffer() before write() - // on every transaction. - size_t write(const uint8_t *data, size_t quantity, bool setExternal = false); +public: + TwoWire(SERCOM *s, uint8_t pinSDA, uint8_t pinSCL); + void begin(); + void begin(uint8_t, bool enableGeneralCall = false); + void begin(uint16_t, bool enableGeneralCall, uint8_t speed = 0x0, + bool enable10Bit = false); + void end(); + void setClock(uint32_t); + + void beginTransmission(uint8_t); + // If onComplete is nullptr, this blocks for legacy sync behavior. + // If onComplete is non-null, this enqueues and returns immediately (async). + uint8_t endTransmission(bool stopBit = true, + void (*onComplete)(void *user, int status) = nullptr, + void *user = nullptr); + + // If onComplete is nullptr, this blocks for legacy sync behavior. + // If onComplete is non-null, this enqueues and returns immediately (async). + // If rxBuffer is nullptr, the internal buffer is used; otherwise rxBuffer is + // used. + uint8_t requestFrom(uint8_t address, size_t quantity, bool stopBit = true, + uint8_t *rxBuffer = nullptr, + void (*onComplete)(void *user, int status) = nullptr, + void *user = nullptr); + + size_t write(uint8_t data); + // 3-arg write: when setExternal=true, data is used directly (zero-copy) and + // quantity is treated as both length and capacity; subsequent write() calls + // return 0. For streaming > WIRE_BUFFER_LENGTH or async usage, call + // setTxBuffer() before write() on every transaction. + size_t write(const uint8_t *data, size_t quantity, bool setExternal = false); virtual int available(void); virtual int read(void); @@ -91,8 +93,21 @@ class TwoWire : public Stream inline void onService(void); - private: - SERCOM *sercom; +#ifdef _DEBUG_ + inline SercomTxn *getSlaveTxn(void) { return &slaveTxn; } + inline const SercomTxn *getSlaveTxn(void) const { return &slaveTxn; } + inline SercomTxn *getLoaderTxn(void) { return &loader; } + inline const SercomTxn *getLoaderTxn(void) const { return &loader; } + inline SercomTxn *getActiveTxn(void) { + return sercom ? sercom->getCurrentTxnWIRE() : nullptr; + } + inline const SercomTxn *getActiveTxn(void) const { + return sercom ? sercom->getCurrentTxnWIRE() : nullptr; + } +#endif // _DEBUG_ + +private: + SERCOM *sercom; uint8_t _uc_pinSDA; uint8_t _uc_pinSCL; @@ -102,7 +117,7 @@ class TwoWire : public Stream static constexpr size_t WIRE_BUFFER_LENGTH = 255; uint8_t rxBuffer[WIRE_BUFFER_LENGTH]; uint8_t txBuffer[WIRE_BUFFER_LENGTH]; - uint8_t *rxBufferPtr; + uint8_t *rxBufferPtr; size_t rxBufferCapacity; size_t rxLength; size_t rxIndex; @@ -115,12 +130,12 @@ class TwoWire : public Stream int pendingReceiveLength; SercomTxn slaveTxn; SercomTxn loader; // Staging area for building transactions - + // Transaction pool for async operations (matches SERCOM queue depth) static constexpr size_t TXN_POOL_SIZE = 8; SercomTxn txnPool[TXN_POOL_SIZE]; uint8_t txnPoolHead; - + SercomTxn *allocateTxn(); void freeTxn(SercomTxn *txn); @@ -136,22 +151,22 @@ class TwoWire : public Stream }; #if WIRE_INTERFACES_COUNT > 0 - extern TwoWire Wire; +extern TwoWire Wire; #endif #if WIRE_INTERFACES_COUNT > 1 - extern TwoWire Wire1; +extern TwoWire Wire1; #endif #if WIRE_INTERFACES_COUNT > 2 - extern TwoWire Wire2; +extern TwoWire Wire2; #endif #if WIRE_INTERFACES_COUNT > 3 - extern TwoWire Wire3; +extern TwoWire Wire3; #endif #if WIRE_INTERFACES_COUNT > 4 - extern TwoWire Wire4; +extern TwoWire Wire4; #endif #if WIRE_INTERFACES_COUNT > 5 - extern TwoWire Wire5; +extern TwoWire Wire5; #endif inline void TwoWire::onService(void) @@ -161,9 +176,9 @@ inline void TwoWire::onService(void) bool isMaster = sercom->isMasterWIRE(); if ((!isMaster && !sercom->isSlaveWIRE()) || flags == 0) { - sercom->clearINTFLAG(); - return; - } + sercom->clearINTFLAG(); + return; + } if (status & SERCOM_I2CM_STATUS_RXNACK) { sercom->prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); @@ -197,7 +212,7 @@ inline void TwoWire::onService(void) err = SercomWireError::LENGTH_ERROR; if (busState == 0x0) err = SercomWireError::BUS_STATE_UNKNOWN; - + sercom->clearINTFLAG(); sercom->deferStopWIRE(err); return; @@ -206,9 +221,9 @@ inline void TwoWire::onService(void) bool isRead = (txn->config & I2C_CFG_READ); if (sercom->getTxnIndexWIRE() < sercom->getTxnLengthWIRE()) { - isRead ? sercom->readDataWIRE() : sercom->sendDataWIRE(); + bool more = isRead ? sercom->readDataWIRE() : sercom->sendDataWIRE(); awaitingAddressAck = false; - return; + if (!isRead || more) return; } if ((txn->config & I2C_CFG_STOP) && !isRead) @@ -244,7 +259,7 @@ inline void TwoWire::onService(void) bool prec = (flags & SERCOM_I2CS_INTFLAG_PREC); // Stop detected bool amatch = (flags & SERCOM_I2CS_INTFLAG_AMATCH); // Address Match detected bool drdy = (flags & SERCOM_I2CS_INTFLAG_DRDY); // Data Ready detected - + // Stop or Restart detected - defer receive callback if (prec || (amatch && sr && !isMasterRead)) { @@ -253,11 +268,10 @@ inline void TwoWire::onService(void) sercom->deferReceiveWIRE(pendingReceiveLength); return; } - + // Address Match - setup transaction // AACKEN enabled: address ACK is automatic, no manual ACK/clear needed - else if (amatch) - { + else if (amatch) { if (isMasterRead) // Master Read / Slave TX { // onRequestCallback runs in ISR context here. Deferring to PendSV @@ -265,12 +279,12 @@ inline void TwoWire::onService(void) // onRequestCallback is what will set TwoWire::slaveTxn for the transaction. if (onRequestCallback) onRequestCallback(); - + // Ensure callback actually set slaveTxn.length; if not, stall with 0-length txn - if (slaveTxn.length == 0) + if (slaveTxn.length == 0) return; - if (!(slaveTxn.config & I2C_CFG_READ)) + if (!(slaveTxn.config & I2C_CFG_READ)) slaveTxn.config |= I2C_CFG_READ; } else // Master Write / Slave RX @@ -288,12 +302,12 @@ inline void TwoWire::onService(void) // SCLSM=0 (Smart Mode disabled): AMATCH and DRDY never fire together // → return now, DRDY will fire in next interrupt - // SCLSM=1 (Smart Mode enabled) + Master Read: AMATCH+DRDY fire together + // SCLSM=1 (Smart Mode enabled) + Master Read: AMATCH+DRDY fire together // → fall through to handle data immediately // SCLSM=1 + Master Write: DRDY not set yet // → return now, DRDY fires later if (!drdy) - return; + return; // else: DRDY is set (SCLSM=1 Master Read case), fall through } @@ -303,7 +317,7 @@ inline void TwoWire::onService(void) isMasterRead ? sercom->sendDataWIRE() : sercom->readDataWIRE(); if (!isMasterRead) - rxLength = sercom->getTxnIndexWIRE(); + rxLength = sercom->getTxnIndexWIRE(); } } } From 6b5dd111f1f81f0a162c32ed4176771b69b6916e Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 13 Apr 2026 21:51:25 -0400 Subject: [PATCH 34/79] generate SPI and Wire pin mux arrays --- cores/arduino/SercomPinMux.inc | 730 ++++++++++++++++++ ...repinmux_inc.py => generate_pinmux_inc.py} | 183 +++-- 2 files changed, 845 insertions(+), 68 deletions(-) create mode 100644 cores/arduino/SercomPinMux.inc rename tools/{generate_wirepinmux_inc.py => generate_pinmux_inc.py} (77%) diff --git a/cores/arduino/SercomPinMux.inc b/cores/arduino/SercomPinMux.inc new file mode 100644 index 000000000..1dd6d95ef --- /dev/null +++ b/cores/arduino/SercomPinMux.inc @@ -0,0 +1,730 @@ +// Auto-generated from SAMD21/SAMD51 device tables. Do not edit manually. +// Define SERCOM_PINMUX_EMIT_I2C and/or SERCOM_PINMUX_EMIT_SPI before including. + +#ifndef SAMD21E_SERIES +#define SAMD21E_SERIES (defined(__SAMD21E15A__) || defined(__SAMD21E15B__) || defined(__SAMD21E15C__) || defined(__SAMD21E15L__) || \ + defined(__SAMD21E16A__) || defined(__SAMD21E16B__) || defined(__SAMD21E16C__) || defined(__SAMD21E16L__) || \ + defined(__SAMD21E17A__) || defined(__SAMD21E17D__) || defined(__SAMD21E17L__) || \ + defined(__SAMD21E18A__)) +#endif + +#ifndef SAMD21G_SERIES +#define SAMD21G_SERIES (defined(__SAMD21G15A__) || defined(__SAMD21G15B__) || \ + defined(__SAMD21G16A__) || defined(__SAMD21G16B__) || defined(__SAMD21G16L__) || \ + defined(__SAMD21G17A__) || defined(__SAMD21G17D__) || defined(__SAMD21G17L__) || \ + defined(__SAMD21G18A__)) +#endif + +#ifndef SAMD21J_SERIES +#define SAMD21J_SERIES (defined(__SAMD21J15A__) || defined(__SAMD21J15B__) || \ + defined(__SAMD21J16A__) || defined(__SAMD21J16B__) || \ + defined(__SAMD21J17A__) || defined(__SAMD21J17D__) || \ + defined(__SAMD21J18A__)) +#endif + +#ifndef SAMDA1_SERIES +#define SAMDA1_SERIES (defined(__SAMDA1E14B__) || defined(__SAMDA1G14B__) || defined(__SAMDA1J14B__) || \ + defined(__SAMDA1E15B__) || defined(__SAMDA1G15B__) || defined(__SAMDA1J15B__) || \ + defined(__SAMDA1E16B__) || defined(__SAMDA1G16B__) || defined(__SAMDA1J16B__)) +#endif + +#ifndef SAMD51_120_SERIES +#define SAMD51_120_SERIES (defined(__SAMD51P19__) || defined(__SAMD51P20__)) +#endif + +#ifndef SAMD51_SERIES +#define SAMD51_SERIES (defined(__SAMD51G18__) || defined(__SAMD51G19__) || \ + defined(__SAMD51J18__) || defined(__SAMD51J19__) || defined(__SAMD51J20__) || \ + defined(__SAMD51N19__) || defined(__SAMD51N20__)) +#endif + +#ifndef SAME51_SERIES +#define SAME51_SERIES (defined(__SAME51G18__) || defined(__SAME51G19__) || \ + defined(__SAME51J18__) || defined(__SAME51J19__) || defined(__SAME51J20__) || \ + defined(__SAME51N19__) || defined(__SAME51N20__)) +#endif + +#ifndef SAME53_SERIES +#define SAME53_SERIES (defined(__SAME53J18__) || defined(__SAME53J19__) || defined(__SAME53J20__) || \ + defined(__SAME53N19__) || defined(__SAME53N20__)) +#endif + +#ifndef SAME54_SERIES +#define SAME54_SERIES (defined(__SAME54N19__) || defined(__SAME54N20__)) +#endif + +#ifndef SAME54_120_SERIES +#define SAME54_120_SERIES (defined(__SAME54P19__) || defined(__SAME54P20__)) +#endif + +#ifdef FAMILY_SAMD2X +#if !(SAMD21E_SERIES || SAMD21G_SERIES || SAMD21J_SERIES || SAMDA1_SERIES) +#define SAMD21E_SERIES 1 +#endif +#endif +#ifdef FAMILY_SAMD5X +#if !(SAMD51_SERIES || SAMD51_120_SERIES || SAME51_SERIES || SAME53_SERIES || SAME54_SERIES || SAME54_120_SERIES) +#define SAMD51_SERIES 1 +#endif +#endif + +#if defined(SERCOM_PINMUX_EMIT_I2C) +#if SAMD21E_SERIES || SAMDA1_SERIES +SERCOM_I2C_PIN('A', 8, 0, 0, 2, 0) +SERCOM_I2C_PIN('A', 9, 0, 1, 2, 1) +SERCOM_I2C_PIN('A', 16, 1, 0, 3, 0) +SERCOM_I2C_PIN('A', 17, 1, 1, 3, 1) +SERCOM_I2C_PIN('A', 22, 3, 0, 5, 0) +SERCOM_I2C_PIN('A', 23, 3, 1, 5, 1) +#endif + +#if SAMD21G_SERIES +SERCOM_I2C_PIN('A', 8, 0, 0, 2, 0) +SERCOM_I2C_PIN('A', 9, 0, 1, 2, 1) +SERCOM_I2C_PIN('A', 12, 2, 0, 4, 0) +SERCOM_I2C_PIN('A', 13, 2, 1, 4, 1) +SERCOM_I2C_PIN('A', 16, 1, 0, 3, 0) +SERCOM_I2C_PIN('A', 17, 1, 1, 3, 1) +SERCOM_I2C_PIN('A', 22, 3, 0, 5, 0) +SERCOM_I2C_PIN('A', 23, 3, 1, 5, 1) +#endif + +#if SAMD21J_SERIES +SERCOM_I2C_PIN('A', 8, 0, 0, 2, 0) +SERCOM_I2C_PIN('A', 9, 0, 1, 2, 1) +SERCOM_I2C_PIN('A', 12, 2, 0, 4, 0) +SERCOM_I2C_PIN('A', 13, 2, 1, 4, 1) +SERCOM_I2C_PIN('A', 16, 1, 0, 3, 0) +SERCOM_I2C_PIN('A', 17, 1, 1, 3, 1) +SERCOM_I2C_PIN('A', 22, 3, 0, 5, 0) +SERCOM_I2C_PIN('A', 23, 3, 1, 5, 1) +SERCOM_I2C_PIN('B', 12, 4, 0, 255, 255) +SERCOM_I2C_PIN('B', 13, 4, 1, 255, 255) +SERCOM_I2C_PIN('B', 16, 5, 0, 255, 255) +SERCOM_I2C_PIN('B', 17, 5, 1, 255, 255) +SERCOM_I2C_PIN('B', 30, 255, 255, 5, 0) +SERCOM_I2C_PIN('B', 31, 255, 255, 5, 1) +#endif + +#if SAMD51_120_SERIES || SAME54_120_SERIES +SERCOM_I2C_PIN('A', 8, 0, 0, 2, 1) +SERCOM_I2C_PIN('A', 9, 0, 1, 2, 0) +SERCOM_I2C_PIN('A', 12, 2, 0, 4, 1) +SERCOM_I2C_PIN('A', 13, 2, 1, 4, 0) +SERCOM_I2C_PIN('A', 16, 1, 0, 3, 1) +SERCOM_I2C_PIN('A', 17, 1, 1, 3, 0) +SERCOM_I2C_PIN('A', 22, 3, 0, 5, 1) +SERCOM_I2C_PIN('A', 23, 3, 1, 5, 0) +SERCOM_I2C_PIN('D', 8, 7, 0, 6, 1) +SERCOM_I2C_PIN('D', 9, 7, 1, 6, 0) +#endif + +#if SAMD51_SERIES || SAME51_SERIES || SAME53_SERIES || SAME54_SERIES +SERCOM_I2C_PIN('A', 8, 0, 0, 2, 1) +SERCOM_I2C_PIN('A', 9, 0, 1, 2, 0) +SERCOM_I2C_PIN('A', 12, 2, 0, 4, 1) +SERCOM_I2C_PIN('A', 13, 2, 1, 4, 0) +SERCOM_I2C_PIN('A', 16, 1, 0, 3, 1) +SERCOM_I2C_PIN('A', 17, 1, 1, 3, 0) +SERCOM_I2C_PIN('A', 22, 3, 0, 5, 1) +SERCOM_I2C_PIN('A', 23, 3, 1, 5, 0) +#endif + +#endif + +#if defined(SERCOM_PINMUX_EMIT_SPI) +#if SAMD21E_SERIES +SERCOM_SPI_PIN('A', 0, 255, 255, 1, 0) +SERCOM_SPI_PIN('A', 4, 255, 255, 0, 0) +SERCOM_SPI_PIN('A', 6, 255, 255, 0, 2) +SERCOM_SPI_PIN('A', 7, 255, 255, 0, 3) +SERCOM_SPI_PIN('A', 8, 0, 0, 2, 0) +SERCOM_SPI_PIN('A', 10, 0, 2, 2, 2) +SERCOM_SPI_PIN('A', 11, 0, 3, 2, 3) +SERCOM_SPI_PIN('A', 12, 2, 0, 4, 0) +SERCOM_SPI_PIN('A', 14, 2, 2, 4, 2) +SERCOM_SPI_PIN('A', 15, 2, 3, 4, 3) +SERCOM_SPI_PIN('A', 16, 1, 0, 3, 0) +SERCOM_SPI_PIN('A', 18, 1, 2, 3, 2) +SERCOM_SPI_PIN('A', 19, 1, 3, 3, 3) +SERCOM_SPI_PIN('A', 20, 5, 2, 3, 2) +SERCOM_SPI_PIN('A', 21, 5, 3, 3, 3) +SERCOM_SPI_PIN('A', 22, 3, 0, 5, 0) +SERCOM_SPI_PIN('A', 24, 3, 2, 5, 2) +SERCOM_SPI_PIN('A', 25, 3, 3, 5, 3) +SERCOM_SPI_PIN('A', 30, 255, 255, 1, 2) +SERCOM_SPI_PIN('A', 31, 255, 255, 1, 3) +SERCOM_SPI_PIN('B', 0, 255, 255, 5, 2) +SERCOM_SPI_PIN('B', 1, 255, 255, 5, 3) +SERCOM_SPI_PIN('B', 2, 255, 255, 5, 0) +SERCOM_SPI_PIN('B', 8, 255, 255, 4, 0) +SERCOM_SPI_PIN('B', 10, 255, 255, 4, 2) +SERCOM_SPI_PIN('B', 11, 255, 255, 4, 3) +SERCOM_SPI_PIN('B', 12, 4, 0, 255, 255) +SERCOM_SPI_PIN('B', 14, 4, 2, 255, 255) +SERCOM_SPI_PIN('B', 15, 4, 3, 255, 255) +SERCOM_SPI_PIN('B', 16, 5, 0, 255, 255) +SERCOM_SPI_PIN('B', 22, 255, 255, 5, 2) +SERCOM_SPI_PIN('B', 23, 255, 255, 5, 3) +SERCOM_SPI_PIN('B', 30, 255, 255, 5, 0) +#endif + +#if SAMD21G_SERIES +SERCOM_SPI_PIN('A', 0, 255, 255, 1, 0) +SERCOM_SPI_PIN('A', 4, 255, 255, 0, 0) +SERCOM_SPI_PIN('A', 6, 255, 255, 0, 2) +SERCOM_SPI_PIN('A', 7, 255, 255, 0, 3) +SERCOM_SPI_PIN('A', 8, 0, 0, 2, 0) +SERCOM_SPI_PIN('A', 10, 0, 2, 2, 2) +SERCOM_SPI_PIN('A', 11, 0, 3, 2, 3) +SERCOM_SPI_PIN('A', 12, 2, 0, 4, 0) +SERCOM_SPI_PIN('A', 14, 2, 2, 4, 2) +SERCOM_SPI_PIN('A', 15, 2, 3, 4, 3) +SERCOM_SPI_PIN('A', 16, 1, 0, 3, 0) +SERCOM_SPI_PIN('A', 18, 1, 2, 3, 2) +SERCOM_SPI_PIN('A', 19, 1, 3, 3, 3) +SERCOM_SPI_PIN('A', 20, 5, 2, 3, 2) +SERCOM_SPI_PIN('A', 21, 5, 3, 3, 3) +SERCOM_SPI_PIN('A', 22, 3, 0, 5, 0) +SERCOM_SPI_PIN('A', 24, 3, 2, 5, 2) +SERCOM_SPI_PIN('A', 25, 3, 3, 5, 3) +SERCOM_SPI_PIN('A', 30, 255, 255, 1, 2) +SERCOM_SPI_PIN('A', 31, 255, 255, 1, 3) +SERCOM_SPI_PIN('B', 0, 255, 255, 5, 2) +SERCOM_SPI_PIN('B', 1, 255, 255, 5, 3) +SERCOM_SPI_PIN('B', 2, 255, 255, 5, 0) +SERCOM_SPI_PIN('B', 8, 255, 255, 4, 0) +SERCOM_SPI_PIN('B', 10, 255, 255, 4, 2) +SERCOM_SPI_PIN('B', 11, 255, 255, 4, 3) +SERCOM_SPI_PIN('B', 12, 4, 0, 255, 255) +SERCOM_SPI_PIN('B', 14, 4, 2, 255, 255) +SERCOM_SPI_PIN('B', 15, 4, 3, 255, 255) +SERCOM_SPI_PIN('B', 16, 5, 0, 255, 255) +SERCOM_SPI_PIN('B', 22, 255, 255, 5, 2) +SERCOM_SPI_PIN('B', 23, 255, 255, 5, 3) +SERCOM_SPI_PIN('B', 30, 255, 255, 5, 0) +#endif + +#if SAMD21J_SERIES +SERCOM_SPI_PIN('A', 0, 255, 255, 1, 0) +SERCOM_SPI_PIN('A', 4, 255, 255, 0, 0) +SERCOM_SPI_PIN('A', 6, 255, 255, 0, 2) +SERCOM_SPI_PIN('A', 7, 255, 255, 0, 3) +SERCOM_SPI_PIN('A', 8, 0, 0, 2, 0) +SERCOM_SPI_PIN('A', 10, 0, 2, 2, 2) +SERCOM_SPI_PIN('A', 11, 0, 3, 2, 3) +SERCOM_SPI_PIN('A', 12, 2, 0, 4, 0) +SERCOM_SPI_PIN('A', 14, 2, 2, 4, 2) +SERCOM_SPI_PIN('A', 15, 2, 3, 4, 3) +SERCOM_SPI_PIN('A', 16, 1, 0, 3, 0) +SERCOM_SPI_PIN('A', 18, 1, 2, 3, 2) +SERCOM_SPI_PIN('A', 19, 1, 3, 3, 3) +SERCOM_SPI_PIN('A', 20, 5, 2, 3, 2) +SERCOM_SPI_PIN('A', 21, 5, 3, 3, 3) +SERCOM_SPI_PIN('A', 22, 3, 0, 5, 0) +SERCOM_SPI_PIN('A', 24, 3, 2, 5, 2) +SERCOM_SPI_PIN('A', 25, 3, 3, 5, 3) +SERCOM_SPI_PIN('A', 30, 255, 255, 1, 2) +SERCOM_SPI_PIN('A', 31, 255, 255, 1, 3) +SERCOM_SPI_PIN('B', 0, 255, 255, 5, 2) +SERCOM_SPI_PIN('B', 1, 255, 255, 5, 3) +SERCOM_SPI_PIN('B', 2, 255, 255, 5, 0) +SERCOM_SPI_PIN('B', 8, 255, 255, 4, 0) +SERCOM_SPI_PIN('B', 10, 255, 255, 4, 2) +SERCOM_SPI_PIN('B', 11, 255, 255, 4, 3) +SERCOM_SPI_PIN('B', 12, 4, 0, 255, 255) +SERCOM_SPI_PIN('B', 14, 4, 2, 255, 255) +SERCOM_SPI_PIN('B', 15, 4, 3, 255, 255) +SERCOM_SPI_PIN('B', 16, 5, 0, 255, 255) +SERCOM_SPI_PIN('B', 22, 255, 255, 5, 2) +SERCOM_SPI_PIN('B', 23, 255, 255, 5, 3) +SERCOM_SPI_PIN('B', 30, 255, 255, 5, 0) +#endif + +#if SAMDA1_SERIES +SERCOM_SPI_PIN('A', 0, 255, 255, 1, 0) +SERCOM_SPI_PIN('A', 4, 255, 255, 0, 0) +SERCOM_SPI_PIN('A', 6, 255, 255, 0, 2) +SERCOM_SPI_PIN('A', 7, 255, 255, 0, 3) +SERCOM_SPI_PIN('A', 8, 0, 0, 2, 0) +SERCOM_SPI_PIN('A', 10, 0, 2, 2, 2) +SERCOM_SPI_PIN('A', 11, 0, 3, 2, 3) +SERCOM_SPI_PIN('A', 12, 2, 0, 4, 0) +SERCOM_SPI_PIN('A', 14, 2, 2, 4, 2) +SERCOM_SPI_PIN('A', 15, 2, 3, 4, 3) +SERCOM_SPI_PIN('A', 16, 1, 0, 3, 0) +SERCOM_SPI_PIN('A', 18, 1, 2, 3, 2) +SERCOM_SPI_PIN('A', 19, 1, 3, 3, 3) +SERCOM_SPI_PIN('A', 20, 5, 2, 3, 2) +SERCOM_SPI_PIN('A', 21, 5, 3, 3, 3) +SERCOM_SPI_PIN('A', 22, 3, 0, 5, 0) +SERCOM_SPI_PIN('A', 24, 3, 2, 5, 2) +SERCOM_SPI_PIN('A', 25, 3, 3, 5, 3) +SERCOM_SPI_PIN('A', 30, 255, 255, 1, 2) +SERCOM_SPI_PIN('A', 31, 255, 255, 1, 3) +SERCOM_SPI_PIN('B', 0, 255, 255, 5, 2) +SERCOM_SPI_PIN('B', 1, 255, 255, 5, 3) +SERCOM_SPI_PIN('B', 2, 255, 255, 5, 0) +SERCOM_SPI_PIN('B', 8, 255, 255, 4, 0) +SERCOM_SPI_PIN('B', 10, 255, 255, 4, 2) +SERCOM_SPI_PIN('B', 11, 255, 255, 4, 3) +SERCOM_SPI_PIN('B', 12, 4, 0, 255, 255) +SERCOM_SPI_PIN('B', 14, 4, 2, 255, 255) +SERCOM_SPI_PIN('B', 15, 4, 3, 255, 255) +SERCOM_SPI_PIN('B', 16, 5, 0, 255, 255) +SERCOM_SPI_PIN('B', 22, 255, 255, 5, 2) +SERCOM_SPI_PIN('B', 23, 255, 255, 5, 3) +SERCOM_SPI_PIN('B', 30, 255, 255, 5, 0) +#endif + +#if SAMD51_120_SERIES +SERCOM_SPI_PIN('A', 0, 255, 255, 1, 0) +SERCOM_SPI_PIN('A', 4, 255, 255, 0, 0) +SERCOM_SPI_PIN('A', 6, 255, 255, 0, 2) +SERCOM_SPI_PIN('A', 7, 255, 255, 0, 3) +SERCOM_SPI_PIN('A', 8, 0, 0, 2, 1) +SERCOM_SPI_PIN('A', 9, 0, 1, 2, 0) +SERCOM_SPI_PIN('A', 10, 0, 2, 2, 2) +SERCOM_SPI_PIN('A', 11, 0, 3, 2, 3) +SERCOM_SPI_PIN('A', 12, 2, 0, 4, 1) +SERCOM_SPI_PIN('A', 13, 2, 1, 4, 0) +SERCOM_SPI_PIN('A', 14, 2, 2, 4, 2) +SERCOM_SPI_PIN('A', 15, 2, 3, 4, 3) +SERCOM_SPI_PIN('A', 16, 1, 0, 3, 1) +SERCOM_SPI_PIN('A', 17, 1, 1, 3, 0) +SERCOM_SPI_PIN('A', 18, 1, 2, 3, 2) +SERCOM_SPI_PIN('A', 19, 1, 3, 3, 3) +SERCOM_SPI_PIN('A', 20, 5, 2, 3, 2) +SERCOM_SPI_PIN('A', 21, 5, 3, 3, 3) +SERCOM_SPI_PIN('A', 22, 3, 0, 5, 1) +SERCOM_SPI_PIN('A', 23, 3, 1, 5, 0) +SERCOM_SPI_PIN('A', 24, 3, 2, 5, 2) +SERCOM_SPI_PIN('A', 25, 3, 3, 5, 3) +SERCOM_SPI_PIN('A', 30, 7, 2, 1, 2) +SERCOM_SPI_PIN('A', 31, 7, 3, 1, 3) +SERCOM_SPI_PIN('B', 0, 255, 255, 5, 2) +SERCOM_SPI_PIN('B', 1, 255, 255, 5, 3) +SERCOM_SPI_PIN('B', 2, 255, 255, 5, 0) +SERCOM_SPI_PIN('B', 8, 255, 255, 4, 0) +SERCOM_SPI_PIN('B', 10, 255, 255, 4, 2) +SERCOM_SPI_PIN('B', 11, 255, 255, 4, 3) +SERCOM_SPI_PIN('B', 12, 4, 0, 255, 255) +SERCOM_SPI_PIN('B', 14, 4, 2, 255, 255) +SERCOM_SPI_PIN('B', 15, 4, 3, 255, 255) +SERCOM_SPI_PIN('B', 16, 5, 0, 255, 255) +SERCOM_SPI_PIN('B', 18, 5, 2, 7, 2) +SERCOM_SPI_PIN('B', 19, 5, 3, 7, 3) +SERCOM_SPI_PIN('B', 20, 3, 0, 7, 1) +SERCOM_SPI_PIN('B', 21, 3, 1, 7, 0) +SERCOM_SPI_PIN('B', 22, 1, 2, 5, 2) +SERCOM_SPI_PIN('B', 23, 1, 3, 5, 3) +SERCOM_SPI_PIN('B', 24, 0, 0, 2, 1) +SERCOM_SPI_PIN('B', 25, 0, 1, 2, 0) +SERCOM_SPI_PIN('B', 26, 2, 0, 4, 1) +SERCOM_SPI_PIN('B', 27, 2, 1, 4, 0) +SERCOM_SPI_PIN('B', 28, 2, 2, 4, 2) +SERCOM_SPI_PIN('B', 29, 2, 3, 4, 3) +SERCOM_SPI_PIN('B', 30, 7, 0, 5, 1) +SERCOM_SPI_PIN('B', 31, 7, 1, 5, 0) +SERCOM_SPI_PIN('C', 4, 6, 0, 255, 255) +SERCOM_SPI_PIN('C', 6, 6, 2, 255, 255) +SERCOM_SPI_PIN('C', 7, 6, 3, 255, 255) +SERCOM_SPI_PIN('C', 10, 6, 2, 7, 2) +SERCOM_SPI_PIN('C', 11, 6, 3, 7, 3) +SERCOM_SPI_PIN('C', 12, 7, 0, 6, 1) +SERCOM_SPI_PIN('C', 13, 7, 1, 6, 0) +SERCOM_SPI_PIN('C', 14, 7, 2, 6, 2) +SERCOM_SPI_PIN('C', 15, 7, 3, 6, 3) +SERCOM_SPI_PIN('C', 16, 6, 0, 0, 1) +SERCOM_SPI_PIN('C', 17, 6, 1, 0, 0) +SERCOM_SPI_PIN('C', 18, 6, 2, 0, 2) +SERCOM_SPI_PIN('C', 19, 6, 3, 0, 3) +SERCOM_SPI_PIN('C', 22, 1, 0, 3, 1) +SERCOM_SPI_PIN('C', 23, 1, 1, 3, 0) +SERCOM_SPI_PIN('C', 24, 0, 2, 2, 2) +SERCOM_SPI_PIN('C', 25, 0, 3, 2, 3) +SERCOM_SPI_PIN('C', 27, 1, 0, 255, 255) +SERCOM_SPI_PIN('D', 8, 7, 0, 6, 1) +SERCOM_SPI_PIN('D', 9, 7, 1, 6, 0) +SERCOM_SPI_PIN('D', 10, 7, 2, 6, 2) +SERCOM_SPI_PIN('D', 11, 7, 3, 6, 3) +SERCOM_SPI_PIN('D', 20, 1, 2, 3, 2) +SERCOM_SPI_PIN('D', 21, 1, 3, 3, 3) +#endif + +#if SAMD51_SERIES +SERCOM_SPI_PIN('A', 0, 255, 255, 1, 0) +SERCOM_SPI_PIN('A', 4, 255, 255, 0, 0) +SERCOM_SPI_PIN('A', 6, 255, 255, 0, 2) +SERCOM_SPI_PIN('A', 7, 255, 255, 0, 3) +SERCOM_SPI_PIN('A', 8, 0, 0, 2, 1) +SERCOM_SPI_PIN('A', 9, 0, 1, 2, 0) +SERCOM_SPI_PIN('A', 10, 0, 2, 2, 2) +SERCOM_SPI_PIN('A', 11, 0, 3, 2, 3) +SERCOM_SPI_PIN('A', 12, 2, 0, 4, 1) +SERCOM_SPI_PIN('A', 13, 2, 1, 4, 0) +SERCOM_SPI_PIN('A', 14, 2, 2, 4, 2) +SERCOM_SPI_PIN('A', 15, 2, 3, 4, 3) +SERCOM_SPI_PIN('A', 16, 1, 0, 3, 1) +SERCOM_SPI_PIN('A', 17, 1, 1, 3, 0) +SERCOM_SPI_PIN('A', 18, 1, 2, 3, 2) +SERCOM_SPI_PIN('A', 19, 1, 3, 3, 3) +SERCOM_SPI_PIN('A', 20, 5, 2, 3, 2) +SERCOM_SPI_PIN('A', 21, 5, 3, 3, 3) +SERCOM_SPI_PIN('A', 22, 3, 0, 5, 1) +SERCOM_SPI_PIN('A', 23, 3, 1, 5, 0) +SERCOM_SPI_PIN('A', 24, 3, 2, 5, 2) +SERCOM_SPI_PIN('A', 25, 3, 3, 5, 3) +SERCOM_SPI_PIN('A', 30, 7, 2, 1, 2) +SERCOM_SPI_PIN('A', 31, 7, 3, 1, 3) +SERCOM_SPI_PIN('B', 0, 255, 255, 5, 2) +SERCOM_SPI_PIN('B', 1, 255, 255, 5, 3) +SERCOM_SPI_PIN('B', 2, 255, 255, 5, 0) +SERCOM_SPI_PIN('B', 8, 255, 255, 4, 0) +SERCOM_SPI_PIN('B', 10, 255, 255, 4, 2) +SERCOM_SPI_PIN('B', 11, 255, 255, 4, 3) +SERCOM_SPI_PIN('B', 12, 4, 0, 255, 255) +SERCOM_SPI_PIN('B', 14, 4, 2, 255, 255) +SERCOM_SPI_PIN('B', 15, 4, 3, 255, 255) +SERCOM_SPI_PIN('B', 16, 5, 0, 255, 255) +SERCOM_SPI_PIN('B', 18, 5, 2, 7, 2) +SERCOM_SPI_PIN('B', 19, 5, 3, 7, 3) +SERCOM_SPI_PIN('B', 20, 3, 0, 7, 1) +SERCOM_SPI_PIN('B', 21, 3, 1, 7, 0) +SERCOM_SPI_PIN('B', 22, 1, 2, 5, 2) +SERCOM_SPI_PIN('B', 23, 1, 3, 5, 3) +SERCOM_SPI_PIN('B', 24, 0, 0, 2, 1) +SERCOM_SPI_PIN('B', 25, 0, 1, 2, 0) +SERCOM_SPI_PIN('B', 26, 2, 0, 4, 1) +SERCOM_SPI_PIN('B', 27, 2, 1, 4, 0) +SERCOM_SPI_PIN('B', 28, 2, 2, 4, 2) +SERCOM_SPI_PIN('B', 29, 2, 3, 4, 3) +SERCOM_SPI_PIN('B', 30, 7, 0, 5, 1) +SERCOM_SPI_PIN('B', 31, 7, 1, 5, 0) +SERCOM_SPI_PIN('C', 4, 6, 0, 255, 255) +SERCOM_SPI_PIN('C', 6, 6, 2, 255, 255) +SERCOM_SPI_PIN('C', 7, 6, 3, 255, 255) +SERCOM_SPI_PIN('C', 10, 6, 2, 7, 2) +SERCOM_SPI_PIN('C', 11, 6, 3, 7, 3) +SERCOM_SPI_PIN('C', 12, 7, 0, 6, 1) +SERCOM_SPI_PIN('C', 13, 7, 1, 6, 0) +SERCOM_SPI_PIN('C', 14, 7, 2, 6, 2) +SERCOM_SPI_PIN('C', 15, 7, 3, 6, 3) +SERCOM_SPI_PIN('C', 16, 6, 0, 0, 1) +SERCOM_SPI_PIN('C', 17, 6, 1, 0, 0) +SERCOM_SPI_PIN('C', 18, 6, 2, 0, 2) +SERCOM_SPI_PIN('C', 19, 6, 3, 0, 3) +SERCOM_SPI_PIN('C', 22, 1, 0, 3, 1) +SERCOM_SPI_PIN('C', 23, 1, 1, 3, 0) +SERCOM_SPI_PIN('C', 24, 0, 2, 2, 2) +SERCOM_SPI_PIN('C', 25, 0, 3, 2, 3) +SERCOM_SPI_PIN('C', 27, 1, 0, 255, 255) +SERCOM_SPI_PIN('D', 8, 7, 0, 6, 1) +SERCOM_SPI_PIN('D', 9, 7, 1, 6, 0) +SERCOM_SPI_PIN('D', 10, 7, 2, 6, 2) +SERCOM_SPI_PIN('D', 11, 7, 3, 6, 3) +SERCOM_SPI_PIN('D', 20, 1, 2, 3, 2) +SERCOM_SPI_PIN('D', 21, 1, 3, 3, 3) +#endif + +#if SAME51_SERIES +SERCOM_SPI_PIN('A', 0, 255, 255, 1, 0) +SERCOM_SPI_PIN('A', 4, 255, 255, 0, 0) +SERCOM_SPI_PIN('A', 6, 255, 255, 0, 2) +SERCOM_SPI_PIN('A', 7, 255, 255, 0, 3) +SERCOM_SPI_PIN('A', 8, 0, 0, 2, 1) +SERCOM_SPI_PIN('A', 9, 0, 1, 2, 0) +SERCOM_SPI_PIN('A', 10, 0, 2, 2, 2) +SERCOM_SPI_PIN('A', 11, 0, 3, 2, 3) +SERCOM_SPI_PIN('A', 12, 2, 0, 4, 1) +SERCOM_SPI_PIN('A', 13, 2, 1, 4, 0) +SERCOM_SPI_PIN('A', 14, 2, 2, 4, 2) +SERCOM_SPI_PIN('A', 15, 2, 3, 4, 3) +SERCOM_SPI_PIN('A', 16, 1, 0, 3, 1) +SERCOM_SPI_PIN('A', 17, 1, 1, 3, 0) +SERCOM_SPI_PIN('A', 18, 1, 2, 3, 2) +SERCOM_SPI_PIN('A', 19, 1, 3, 3, 3) +SERCOM_SPI_PIN('A', 20, 5, 2, 3, 2) +SERCOM_SPI_PIN('A', 21, 5, 3, 3, 3) +SERCOM_SPI_PIN('A', 22, 3, 0, 5, 1) +SERCOM_SPI_PIN('A', 23, 3, 1, 5, 0) +SERCOM_SPI_PIN('A', 24, 3, 2, 5, 2) +SERCOM_SPI_PIN('A', 25, 3, 3, 5, 3) +SERCOM_SPI_PIN('A', 30, 7, 2, 1, 2) +SERCOM_SPI_PIN('A', 31, 7, 3, 1, 3) +SERCOM_SPI_PIN('B', 0, 255, 255, 5, 2) +SERCOM_SPI_PIN('B', 1, 255, 255, 5, 3) +SERCOM_SPI_PIN('B', 2, 255, 255, 5, 0) +SERCOM_SPI_PIN('B', 8, 255, 255, 4, 0) +SERCOM_SPI_PIN('B', 10, 255, 255, 4, 2) +SERCOM_SPI_PIN('B', 11, 255, 255, 4, 3) +SERCOM_SPI_PIN('B', 12, 4, 0, 255, 255) +SERCOM_SPI_PIN('B', 14, 4, 2, 255, 255) +SERCOM_SPI_PIN('B', 15, 4, 3, 255, 255) +SERCOM_SPI_PIN('B', 16, 5, 0, 255, 255) +SERCOM_SPI_PIN('B', 18, 5, 2, 7, 2) +SERCOM_SPI_PIN('B', 19, 5, 3, 7, 3) +SERCOM_SPI_PIN('B', 20, 3, 0, 7, 1) +SERCOM_SPI_PIN('B', 21, 3, 1, 7, 0) +SERCOM_SPI_PIN('B', 22, 1, 2, 5, 2) +SERCOM_SPI_PIN('B', 23, 1, 3, 5, 3) +SERCOM_SPI_PIN('B', 24, 0, 0, 2, 1) +SERCOM_SPI_PIN('B', 25, 0, 1, 2, 0) +SERCOM_SPI_PIN('B', 26, 2, 0, 4, 1) +SERCOM_SPI_PIN('B', 27, 2, 1, 4, 0) +SERCOM_SPI_PIN('B', 28, 2, 2, 4, 2) +SERCOM_SPI_PIN('B', 29, 2, 3, 4, 3) +SERCOM_SPI_PIN('B', 30, 7, 0, 5, 1) +SERCOM_SPI_PIN('B', 31, 7, 1, 5, 0) +SERCOM_SPI_PIN('C', 4, 6, 0, 255, 255) +SERCOM_SPI_PIN('C', 6, 6, 2, 255, 255) +SERCOM_SPI_PIN('C', 7, 6, 3, 255, 255) +SERCOM_SPI_PIN('C', 10, 6, 2, 7, 2) +SERCOM_SPI_PIN('C', 11, 6, 3, 7, 3) +SERCOM_SPI_PIN('C', 12, 7, 0, 6, 1) +SERCOM_SPI_PIN('C', 13, 7, 1, 6, 0) +SERCOM_SPI_PIN('C', 14, 7, 2, 6, 2) +SERCOM_SPI_PIN('C', 15, 7, 3, 6, 3) +SERCOM_SPI_PIN('C', 16, 6, 0, 0, 1) +SERCOM_SPI_PIN('C', 17, 6, 1, 0, 0) +SERCOM_SPI_PIN('C', 18, 6, 2, 0, 2) +SERCOM_SPI_PIN('C', 19, 6, 3, 0, 3) +SERCOM_SPI_PIN('C', 22, 1, 0, 3, 1) +SERCOM_SPI_PIN('C', 23, 1, 1, 3, 0) +SERCOM_SPI_PIN('C', 24, 0, 2, 2, 2) +SERCOM_SPI_PIN('C', 25, 0, 3, 2, 3) +SERCOM_SPI_PIN('C', 27, 1, 0, 255, 255) +SERCOM_SPI_PIN('D', 8, 7, 0, 6, 1) +SERCOM_SPI_PIN('D', 9, 7, 1, 6, 0) +SERCOM_SPI_PIN('D', 10, 7, 2, 6, 2) +SERCOM_SPI_PIN('D', 11, 7, 3, 6, 3) +SERCOM_SPI_PIN('D', 20, 1, 2, 3, 2) +SERCOM_SPI_PIN('D', 21, 1, 3, 3, 3) +#endif + +#if SAME53_SERIES +SERCOM_SPI_PIN('A', 0, 255, 255, 1, 0) +SERCOM_SPI_PIN('A', 4, 255, 255, 0, 0) +SERCOM_SPI_PIN('A', 6, 255, 255, 0, 2) +SERCOM_SPI_PIN('A', 7, 255, 255, 0, 3) +SERCOM_SPI_PIN('A', 8, 0, 0, 2, 1) +SERCOM_SPI_PIN('A', 9, 0, 1, 2, 0) +SERCOM_SPI_PIN('A', 10, 0, 2, 2, 2) +SERCOM_SPI_PIN('A', 11, 0, 3, 2, 3) +SERCOM_SPI_PIN('A', 12, 2, 0, 4, 1) +SERCOM_SPI_PIN('A', 13, 2, 1, 4, 0) +SERCOM_SPI_PIN('A', 14, 2, 2, 4, 2) +SERCOM_SPI_PIN('A', 15, 2, 3, 4, 3) +SERCOM_SPI_PIN('A', 16, 1, 0, 3, 1) +SERCOM_SPI_PIN('A', 17, 1, 1, 3, 0) +SERCOM_SPI_PIN('A', 18, 1, 2, 3, 2) +SERCOM_SPI_PIN('A', 19, 1, 3, 3, 3) +SERCOM_SPI_PIN('A', 20, 5, 2, 3, 2) +SERCOM_SPI_PIN('A', 21, 5, 3, 3, 3) +SERCOM_SPI_PIN('A', 22, 3, 0, 5, 1) +SERCOM_SPI_PIN('A', 23, 3, 1, 5, 0) +SERCOM_SPI_PIN('A', 24, 3, 2, 5, 2) +SERCOM_SPI_PIN('A', 25, 3, 3, 5, 3) +SERCOM_SPI_PIN('A', 30, 7, 2, 1, 2) +SERCOM_SPI_PIN('A', 31, 7, 3, 1, 3) +SERCOM_SPI_PIN('B', 0, 255, 255, 5, 2) +SERCOM_SPI_PIN('B', 1, 255, 255, 5, 3) +SERCOM_SPI_PIN('B', 2, 255, 255, 5, 0) +SERCOM_SPI_PIN('B', 8, 255, 255, 4, 0) +SERCOM_SPI_PIN('B', 10, 255, 255, 4, 2) +SERCOM_SPI_PIN('B', 11, 255, 255, 4, 3) +SERCOM_SPI_PIN('B', 12, 4, 0, 255, 255) +SERCOM_SPI_PIN('B', 14, 4, 2, 255, 255) +SERCOM_SPI_PIN('B', 15, 4, 3, 255, 255) +SERCOM_SPI_PIN('B', 16, 5, 0, 255, 255) +SERCOM_SPI_PIN('B', 18, 5, 2, 7, 2) +SERCOM_SPI_PIN('B', 19, 5, 3, 7, 3) +SERCOM_SPI_PIN('B', 20, 3, 0, 7, 1) +SERCOM_SPI_PIN('B', 21, 3, 1, 7, 0) +SERCOM_SPI_PIN('B', 22, 1, 2, 5, 2) +SERCOM_SPI_PIN('B', 23, 1, 3, 5, 3) +SERCOM_SPI_PIN('B', 24, 0, 0, 2, 1) +SERCOM_SPI_PIN('B', 25, 0, 1, 2, 0) +SERCOM_SPI_PIN('B', 26, 2, 0, 4, 1) +SERCOM_SPI_PIN('B', 27, 2, 1, 4, 0) +SERCOM_SPI_PIN('B', 28, 2, 2, 4, 2) +SERCOM_SPI_PIN('B', 29, 2, 3, 4, 3) +SERCOM_SPI_PIN('B', 30, 7, 0, 5, 1) +SERCOM_SPI_PIN('B', 31, 7, 1, 5, 0) +SERCOM_SPI_PIN('C', 4, 6, 0, 255, 255) +SERCOM_SPI_PIN('C', 6, 6, 2, 255, 255) +SERCOM_SPI_PIN('C', 7, 6, 3, 255, 255) +SERCOM_SPI_PIN('C', 10, 6, 2, 7, 2) +SERCOM_SPI_PIN('C', 11, 6, 3, 7, 3) +SERCOM_SPI_PIN('C', 12, 7, 0, 6, 1) +SERCOM_SPI_PIN('C', 13, 7, 1, 6, 0) +SERCOM_SPI_PIN('C', 14, 7, 2, 6, 2) +SERCOM_SPI_PIN('C', 15, 7, 3, 6, 3) +SERCOM_SPI_PIN('C', 16, 6, 0, 0, 1) +SERCOM_SPI_PIN('C', 17, 6, 1, 0, 0) +SERCOM_SPI_PIN('C', 18, 6, 2, 0, 2) +SERCOM_SPI_PIN('C', 19, 6, 3, 0, 3) +SERCOM_SPI_PIN('C', 22, 1, 0, 3, 1) +SERCOM_SPI_PIN('C', 23, 1, 1, 3, 0) +SERCOM_SPI_PIN('C', 24, 0, 2, 2, 2) +SERCOM_SPI_PIN('C', 25, 0, 3, 2, 3) +SERCOM_SPI_PIN('C', 27, 1, 0, 255, 255) +SERCOM_SPI_PIN('D', 8, 7, 0, 6, 1) +SERCOM_SPI_PIN('D', 9, 7, 1, 6, 0) +SERCOM_SPI_PIN('D', 10, 7, 2, 6, 2) +SERCOM_SPI_PIN('D', 11, 7, 3, 6, 3) +SERCOM_SPI_PIN('D', 20, 1, 2, 3, 2) +SERCOM_SPI_PIN('D', 21, 1, 3, 3, 3) +#endif + +#if SAME54_SERIES +SERCOM_SPI_PIN('A', 0, 255, 255, 1, 0) +SERCOM_SPI_PIN('A', 4, 255, 255, 0, 0) +SERCOM_SPI_PIN('A', 6, 255, 255, 0, 2) +SERCOM_SPI_PIN('A', 7, 255, 255, 0, 3) +SERCOM_SPI_PIN('A', 8, 0, 0, 2, 1) +SERCOM_SPI_PIN('A', 9, 0, 1, 2, 0) +SERCOM_SPI_PIN('A', 10, 0, 2, 2, 2) +SERCOM_SPI_PIN('A', 11, 0, 3, 2, 3) +SERCOM_SPI_PIN('A', 12, 2, 0, 4, 1) +SERCOM_SPI_PIN('A', 13, 2, 1, 4, 0) +SERCOM_SPI_PIN('A', 14, 2, 2, 4, 2) +SERCOM_SPI_PIN('A', 15, 2, 3, 4, 3) +SERCOM_SPI_PIN('A', 16, 1, 0, 3, 1) +SERCOM_SPI_PIN('A', 17, 1, 1, 3, 0) +SERCOM_SPI_PIN('A', 18, 1, 2, 3, 2) +SERCOM_SPI_PIN('A', 19, 1, 3, 3, 3) +SERCOM_SPI_PIN('A', 20, 5, 2, 3, 2) +SERCOM_SPI_PIN('A', 21, 5, 3, 3, 3) +SERCOM_SPI_PIN('A', 22, 3, 0, 5, 1) +SERCOM_SPI_PIN('A', 23, 3, 1, 5, 0) +SERCOM_SPI_PIN('A', 24, 3, 2, 5, 2) +SERCOM_SPI_PIN('A', 25, 3, 3, 5, 3) +SERCOM_SPI_PIN('A', 30, 7, 2, 1, 2) +SERCOM_SPI_PIN('A', 31, 7, 3, 1, 3) +SERCOM_SPI_PIN('B', 0, 255, 255, 5, 2) +SERCOM_SPI_PIN('B', 1, 255, 255, 5, 3) +SERCOM_SPI_PIN('B', 2, 255, 255, 5, 0) +SERCOM_SPI_PIN('B', 8, 255, 255, 4, 0) +SERCOM_SPI_PIN('B', 10, 255, 255, 4, 2) +SERCOM_SPI_PIN('B', 11, 255, 255, 4, 3) +SERCOM_SPI_PIN('B', 12, 4, 0, 255, 255) +SERCOM_SPI_PIN('B', 14, 4, 2, 255, 255) +SERCOM_SPI_PIN('B', 15, 4, 3, 255, 255) +SERCOM_SPI_PIN('B', 16, 5, 0, 255, 255) +SERCOM_SPI_PIN('B', 18, 5, 2, 7, 2) +SERCOM_SPI_PIN('B', 19, 5, 3, 7, 3) +SERCOM_SPI_PIN('B', 20, 3, 0, 7, 1) +SERCOM_SPI_PIN('B', 21, 3, 1, 7, 0) +SERCOM_SPI_PIN('B', 22, 1, 2, 5, 2) +SERCOM_SPI_PIN('B', 23, 1, 3, 5, 3) +SERCOM_SPI_PIN('B', 24, 0, 0, 2, 1) +SERCOM_SPI_PIN('B', 25, 0, 1, 2, 0) +SERCOM_SPI_PIN('B', 26, 2, 0, 4, 1) +SERCOM_SPI_PIN('B', 27, 2, 1, 4, 0) +SERCOM_SPI_PIN('B', 28, 2, 2, 4, 2) +SERCOM_SPI_PIN('B', 29, 2, 3, 4, 3) +SERCOM_SPI_PIN('B', 30, 7, 0, 5, 1) +SERCOM_SPI_PIN('B', 31, 7, 1, 5, 0) +SERCOM_SPI_PIN('C', 4, 6, 0, 255, 255) +SERCOM_SPI_PIN('C', 6, 6, 2, 255, 255) +SERCOM_SPI_PIN('C', 7, 6, 3, 255, 255) +SERCOM_SPI_PIN('C', 10, 6, 2, 7, 2) +SERCOM_SPI_PIN('C', 11, 6, 3, 7, 3) +SERCOM_SPI_PIN('C', 12, 7, 0, 6, 1) +SERCOM_SPI_PIN('C', 13, 7, 1, 6, 0) +SERCOM_SPI_PIN('C', 14, 7, 2, 6, 2) +SERCOM_SPI_PIN('C', 15, 7, 3, 6, 3) +SERCOM_SPI_PIN('C', 16, 6, 0, 0, 1) +SERCOM_SPI_PIN('C', 17, 6, 1, 0, 0) +SERCOM_SPI_PIN('C', 18, 6, 2, 0, 2) +SERCOM_SPI_PIN('C', 19, 6, 3, 0, 3) +SERCOM_SPI_PIN('C', 22, 1, 0, 3, 1) +SERCOM_SPI_PIN('C', 23, 1, 1, 3, 0) +SERCOM_SPI_PIN('C', 24, 0, 2, 2, 2) +SERCOM_SPI_PIN('C', 25, 0, 3, 2, 3) +SERCOM_SPI_PIN('C', 27, 1, 0, 255, 255) +SERCOM_SPI_PIN('D', 8, 7, 0, 6, 1) +SERCOM_SPI_PIN('D', 9, 7, 1, 6, 0) +SERCOM_SPI_PIN('D', 10, 7, 2, 6, 2) +SERCOM_SPI_PIN('D', 11, 7, 3, 6, 3) +SERCOM_SPI_PIN('D', 20, 1, 2, 3, 2) +SERCOM_SPI_PIN('D', 21, 1, 3, 3, 3) +#endif + +#if SAME54_120_SERIES +SERCOM_SPI_PIN('A', 0, 255, 255, 1, 0) +SERCOM_SPI_PIN('A', 4, 255, 255, 0, 0) +SERCOM_SPI_PIN('A', 6, 255, 255, 0, 2) +SERCOM_SPI_PIN('A', 7, 255, 255, 0, 3) +SERCOM_SPI_PIN('A', 8, 0, 0, 2, 1) +SERCOM_SPI_PIN('A', 9, 0, 1, 2, 0) +SERCOM_SPI_PIN('A', 10, 0, 2, 2, 2) +SERCOM_SPI_PIN('A', 11, 0, 3, 2, 3) +SERCOM_SPI_PIN('A', 12, 2, 0, 4, 1) +SERCOM_SPI_PIN('A', 13, 2, 1, 4, 0) +SERCOM_SPI_PIN('A', 14, 2, 2, 4, 2) +SERCOM_SPI_PIN('A', 15, 2, 3, 4, 3) +SERCOM_SPI_PIN('A', 16, 1, 0, 3, 1) +SERCOM_SPI_PIN('A', 17, 1, 1, 3, 0) +SERCOM_SPI_PIN('A', 18, 1, 2, 3, 2) +SERCOM_SPI_PIN('A', 19, 1, 3, 3, 3) +SERCOM_SPI_PIN('A', 20, 5, 2, 3, 2) +SERCOM_SPI_PIN('A', 21, 5, 3, 3, 3) +SERCOM_SPI_PIN('A', 22, 3, 0, 5, 1) +SERCOM_SPI_PIN('A', 23, 3, 1, 5, 0) +SERCOM_SPI_PIN('A', 24, 3, 2, 5, 2) +SERCOM_SPI_PIN('A', 25, 3, 3, 5, 3) +SERCOM_SPI_PIN('A', 30, 7, 2, 1, 2) +SERCOM_SPI_PIN('A', 31, 7, 3, 1, 3) +SERCOM_SPI_PIN('B', 0, 255, 255, 5, 2) +SERCOM_SPI_PIN('B', 1, 255, 255, 5, 3) +SERCOM_SPI_PIN('B', 2, 255, 255, 5, 0) +SERCOM_SPI_PIN('B', 8, 255, 255, 4, 0) +SERCOM_SPI_PIN('B', 10, 255, 255, 4, 2) +SERCOM_SPI_PIN('B', 11, 255, 255, 4, 3) +SERCOM_SPI_PIN('B', 12, 4, 0, 255, 255) +SERCOM_SPI_PIN('B', 14, 4, 2, 255, 255) +SERCOM_SPI_PIN('B', 15, 4, 3, 255, 255) +SERCOM_SPI_PIN('B', 16, 5, 0, 255, 255) +SERCOM_SPI_PIN('B', 18, 5, 2, 7, 2) +SERCOM_SPI_PIN('B', 19, 5, 3, 7, 3) +SERCOM_SPI_PIN('B', 20, 3, 0, 7, 1) +SERCOM_SPI_PIN('B', 21, 3, 1, 7, 0) +SERCOM_SPI_PIN('B', 22, 1, 2, 5, 2) +SERCOM_SPI_PIN('B', 23, 1, 3, 5, 3) +SERCOM_SPI_PIN('B', 24, 0, 0, 2, 1) +SERCOM_SPI_PIN('B', 25, 0, 1, 2, 0) +SERCOM_SPI_PIN('B', 26, 2, 0, 4, 1) +SERCOM_SPI_PIN('B', 27, 2, 1, 4, 0) +SERCOM_SPI_PIN('B', 28, 2, 2, 4, 2) +SERCOM_SPI_PIN('B', 29, 2, 3, 4, 3) +SERCOM_SPI_PIN('B', 30, 7, 0, 5, 1) +SERCOM_SPI_PIN('B', 31, 7, 1, 5, 0) +SERCOM_SPI_PIN('C', 4, 6, 0, 255, 255) +SERCOM_SPI_PIN('C', 6, 6, 2, 255, 255) +SERCOM_SPI_PIN('C', 7, 6, 3, 255, 255) +SERCOM_SPI_PIN('C', 10, 6, 2, 7, 2) +SERCOM_SPI_PIN('C', 11, 6, 3, 7, 3) +SERCOM_SPI_PIN('C', 12, 7, 0, 6, 1) +SERCOM_SPI_PIN('C', 13, 7, 1, 6, 0) +SERCOM_SPI_PIN('C', 14, 7, 2, 6, 2) +SERCOM_SPI_PIN('C', 15, 7, 3, 6, 3) +SERCOM_SPI_PIN('C', 16, 6, 0, 0, 1) +SERCOM_SPI_PIN('C', 17, 6, 1, 0, 0) +SERCOM_SPI_PIN('C', 18, 6, 2, 0, 2) +SERCOM_SPI_PIN('C', 19, 6, 3, 0, 3) +SERCOM_SPI_PIN('C', 22, 1, 0, 3, 1) +SERCOM_SPI_PIN('C', 23, 1, 1, 3, 0) +SERCOM_SPI_PIN('C', 24, 0, 2, 2, 2) +SERCOM_SPI_PIN('C', 25, 0, 3, 2, 3) +SERCOM_SPI_PIN('C', 27, 1, 0, 255, 255) +SERCOM_SPI_PIN('D', 8, 7, 0, 6, 1) +SERCOM_SPI_PIN('D', 9, 7, 1, 6, 0) +SERCOM_SPI_PIN('D', 10, 7, 2, 6, 2) +SERCOM_SPI_PIN('D', 11, 7, 3, 6, 3) +SERCOM_SPI_PIN('D', 20, 1, 2, 3, 2) +SERCOM_SPI_PIN('D', 21, 1, 3, 3, 3) +#endif + +#endif diff --git a/tools/generate_wirepinmux_inc.py b/tools/generate_pinmux_inc.py similarity index 77% rename from tools/generate_wirepinmux_inc.py rename to tools/generate_pinmux_inc.py index f47df3f38..6434b669b 100644 --- a/tools/generate_wirepinmux_inc.py +++ b/tools/generate_pinmux_inc.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -Generate WirePinMux.inc from SAMD21/DA1 and SAMD51/E5x device tables. +Generate SercomPinMux.inc from SAMD21/DA1 and SAMD51/E5x device tables. Workflow: 1. Load device-to-package mappings from Tables 2-1, 2-2 (SAMD21/DA1) and 1-1, 1-2 (SAMD51/E5x) @@ -9,10 +9,10 @@ 4. Group devices by series and emit guard blocks with I2C_PIN macros Emits: - libraries/Wire/WirePinMux.inc + cores/arduino/SercomPinMux.inc Each pin entry: - I2C_PIN(PA08, 0, 0, 2, 1) + SERCOM_I2C_PIN(PA08, 0, 0, 2, 1) where arguments are (pin, primary_sercom, primary_pad, alt_sercom, alt_pad). If an alternate SERCOM is missing, alt_sercom/pad are set to 255. """ @@ -25,7 +25,7 @@ ROOT = Path(__file__).resolve().parent.parent DATA_DIR = ROOT / "extras" / "wirepinmux" -OUTPUT = ROOT / "libraries" / "Wire" / "WirePinMux.inc" +SERCOM_OUTPUT = ROOT / "cores" / "arduino" / "SercomPinMux.inc" # Device-to-package mapping CSVs SAMD21_TABLE_2_1 = DATA_DIR / "SAMD21_Table_2-1.csv" @@ -37,6 +37,10 @@ SAMD21_TABLE_7_5 = DATA_DIR / "SAMD21_Table_7-5.csv" SAMD51_TABLE_6_8 = DATA_DIR / "SAMD51_Table_6-8.csv" +# Full pin mux tables (SERCOM column C/D) +SAMD21_TABLE_7_1 = DATA_DIR / "SAMD21_Table_7-1.csv" +SAMD51_TABLE_6_1 = DATA_DIR / "SAMD51_Table_6-1.csv" + # Regex patterns SERCOM_PAD_RE = re.compile(r"SERCOM(\d+)/PAD\[(\d+)\]") @@ -231,6 +235,14 @@ def load_pin_mux_configs( return pin_configs +def pin_name_to_port_pin(pin_name: str) -> Tuple[str, int]: + """Convert pin label like PA08 -> ('A', 8).""" + pin_name = pin_name.strip().upper() + if len(pin_name) < 4 or pin_name[0] != "P": + raise ValueError(f"Unsupported pin format: {pin_name}") + return pin_name[1], int(pin_name[2:]) + + def macro_candidates(dev: str) -> List[str]: """ Return the canonical sam.h device macro for a device name. @@ -329,12 +341,8 @@ def build_series_map() -> ( i2c_pins_samd51 = load_i2c_pins_samd51(SAMD51_TABLE_6_8) # Load pin mux configurations (SERCOM mappings from columns C/D) - pin_mux_samd21 = load_pin_mux_configs( - ROOT / "docs" / "SAMD21_Table_7-1.csv", is_samd51=False - ) - pin_mux_samd51 = load_pin_mux_configs( - ROOT / "docs" / "SAMD51_Table_6-1.csv", is_samd51=True - ) + pin_mux_samd21 = load_pin_mux_configs(SAMD21_TABLE_7_1, is_samd51=False) + pin_mux_samd51 = load_pin_mux_configs(SAMD51_TABLE_6_1, is_samd51=True) # Process SAMD21/DA1 devices for device, pin_counts in device_pins_samd21.items(): @@ -462,94 +470,131 @@ def format_series_macro(series_name: str, device_macros: List[str]) -> List[str] return lines -def emit_inc( - series_map: Dict[ - str, Tuple[List[str], List[Tuple[str, Tuple[int, int, int, int]]]] - ], -) -> str: - """Generate the WirePinMux.inc file content.""" - lines: List[str] = [] - lines.append( - "// Auto-generated from SAMD21/SAMD51 device tables. Do not edit manually.\n" - ) +SERIES_ORDER = { + "SAMD21E_SERIES": 0, + "SAMD21G_SERIES": 1, + "SAMD21J_SERIES": 2, + "SAMDA1_SERIES": 3, + "SAMD51_120_SERIES": 4, + "SAMD51_SERIES": 5, + "SAME51_SERIES": 5, + "SAME53_SERIES": 5, + "SAME54_SERIES": 5, + "SAME54_120_SERIES": 6, +} - # Define custom ordering for series (moved here to use in sorting) - series_order = { - "SAMD21E_SERIES": 0, - "SAMD21G_SERIES": 1, - "SAMD21J_SERIES": 2, - "SAMDA1_SERIES": 3, - "SAMD51_120_SERIES": 4, - "SAMD51_SERIES": 5, - "SAME51_SERIES": 5, - "SAME53_SERIES": 5, - "SAME54_SERIES": 5, - "SAME54_120_SERIES": 6, - } - # Emit series macro definitions in custom order - for series_name in sorted( - series_map.keys(), key=lambda s: (series_order.get(s, 999), s) - ): +def emit_series_macros( + series_map: Dict[str, Tuple[List[str], List[Tuple[str, Tuple[int, int, int, int]]]]], +) -> List[str]: + lines: List[str] = [] + for series_name in sorted(series_map.keys(), key=lambda s: (SERIES_ORDER.get(s, 999), s)): devices, _ = series_map[series_name] - - # Build the macro definition - device_checks = [] + device_checks: List[str] = [] for dev in sorted(set(devices)): device_checks.extend(macro_candidates(dev)) - # Remove duplicates while preserving order - seen = set() - unique_checks = [] + seen: Set[str] = set() + unique_checks: List[str] = [] for check in device_checks: if check not in seen: seen.add(check) unique_checks.append(check) - # Use the intelligent grouping formatter lines.extend(format_series_macro(series_name, unique_checks)) - lines.append("") # Add blank line between series - - lines.append("") + lines.append("") - # Fallback: if a TARGET_* flag is set but no series macro matches, default to the base series - lines.append( - "#if defined(TARGET_SAMD21) && !(SAMD21E_SERIES || SAMD21G_SERIES || SAMD21J_SERIES || SAMDA1_SERIES)" - ) + lines.append("#ifdef FAMILY_SAMD2X") + lines.append("#if !(SAMD21E_SERIES || SAMD21G_SERIES || SAMD21J_SERIES || SAMDA1_SERIES)") lines.append("#define SAMD21E_SERIES 1") lines.append("#endif") - lines.append( - "#if defined(TARGET_SAMD51) && !(SAMD51_SERIES || SAMD51_120_SERIES || SAME51_SERIES || SAME53_SERIES || SAME54_SERIES || SAME54_120_SERIES)" - ) + lines.append("#endif") + lines.append("#ifdef FAMILY_SAMD5X") + lines.append("#if !(SAMD51_SERIES || SAMD51_120_SERIES || SAME51_SERIES || SAME53_SERIES || SAME54_SERIES || SAME54_120_SERIES)") lines.append("#define SAMD51_SERIES 1") lines.append("#endif") + lines.append("#endif") lines.append("") + return lines - # Group series by their pin configuration + +def emit_i2c_entries( + series_map: Dict[str, Tuple[List[str], List[Tuple[str, Tuple[int, int, int, int]]]]], +) -> List[str]: + lines: List[str] = [] pin_config_groups: Dict[Tuple, List[str]] = {} for series_name, (_, pins) in series_map.items(): pin_tuple = tuple((p, s0, p0, s1, p1) for p, (s0, p0, s1, p1) in pins) - if pin_tuple not in pin_config_groups: - pin_config_groups[pin_tuple] = [] - pin_config_groups[pin_tuple].append(series_name) + pin_config_groups.setdefault(pin_tuple, []).append(series_name) - # Sort by series order sorted_groups = sorted( pin_config_groups.items(), - key=lambda x: (series_order.get(sorted(x[1])[0], 999), sorted(x[1])[0]), + key=lambda x: (SERIES_ORDER.get(sorted(x[1])[0], 999), sorted(x[1])[0]), ) - # Emit pin configurations with guards for pin_config, series_list in sorted_groups: - # Build guard expression guard_expr = " || ".join(sorted(series_list)) lines.append(f"#if {guard_expr}") - - # Emit I2C_PIN macros for pin_name, s0, p0, s1, p1 in pin_config: - lines.append(f"I2C_PIN({pin_name}, {s0}, {p0}, {s1}, {p1})") + port, pin = pin_name_to_port_pin(pin_name) + lines.append(f"SERCOM_I2C_PIN('{port}', {pin}, {s0}, {p0}, {s1}, {p1})") + lines.append("#endif\n") + return lines + +def build_spi_series_map( + series_map: Dict[str, Tuple[List[str], List[Tuple[str, Tuple[int, int, int, int]]]]], +) -> Dict[str, List[Tuple[str, Tuple[int, int, int, int]]]]: + """Build SPI MOSI-capable pin configs per series.""" + pin_mux_samd21 = load_pin_mux_configs(SAMD21_TABLE_7_1, is_samd51=False) + pin_mux_samd51 = load_pin_mux_configs(SAMD51_TABLE_6_1, is_samd51=True) + + result: Dict[str, List[Tuple[str, Tuple[int, int, int, int]]]] = {} + for series_name in series_map.keys(): + family_mux = pin_mux_samd51 if ("SAMD51" in series_name or "SAME" in series_name) else pin_mux_samd21 + pins: List[Tuple[str, Tuple[int, int, int, int]]] = [] + for pin_name, cfg in sorted(family_mux.items()): + s0, p0, s1, p1 = cfg + # MOSI can be PAD 0, 2, or 3. Keep entries where at least one route is MOSI-capable. + if ((s0 != 255 and p0 in (0, 2, 3)) or (s1 != 255 and p1 in (0, 2, 3))): + pins.append((pin_name, cfg)) + result[series_name] = pins + return result + + +def emit_spi_entries( + series_map: Dict[str, Tuple[List[str], List[Tuple[str, Tuple[int, int, int, int]]]]], + spi_series_map: Dict[str, List[Tuple[str, Tuple[int, int, int, int]]]], +) -> List[str]: + lines: List[str] = [] + for series_name in sorted(spi_series_map.keys(), key=lambda s: (SERIES_ORDER.get(s, 999), s)): + lines.append(f"#if {series_name}") + for pin_name, (s0, p0, s1, p1) in spi_series_map[series_name]: + port, pin = pin_name_to_port_pin(pin_name) + lines.append(f"SERCOM_SPI_PIN('{port}', {pin}, {s0}, {p0}, {s1}, {p1})") lines.append("#endif\n") + return lines + + +def emit_sercom_inc( + series_map: Dict[str, Tuple[List[str], List[Tuple[str, Tuple[int, int, int, int]]]]], + spi_series_map: Dict[str, List[Tuple[str, Tuple[int, int, int, int]]]], +) -> str: + lines: List[str] = [] + lines.append("// Auto-generated from SAMD21/SAMD51 device tables. Do not edit manually.") + lines.append("// Define SERCOM_PINMUX_EMIT_I2C and/or SERCOM_PINMUX_EMIT_SPI before including.") + lines.append("") + lines.extend(emit_series_macros(series_map)) + + lines.append("#if defined(SERCOM_PINMUX_EMIT_I2C)") + lines.extend(emit_i2c_entries(series_map)) + lines.append("#endif") + lines.append("") + + lines.append("#if defined(SERCOM_PINMUX_EMIT_SPI)") + lines.extend(emit_spi_entries(series_map, spi_series_map)) + lines.append("#endif") + lines.append("") return "\n".join(lines) @@ -557,12 +602,14 @@ def emit_inc( def main() -> None: """Main entry point.""" series_map = build_series_map() - inc_text = emit_inc(series_map) - OUTPUT.write_text(inc_text) + spi_series_map = build_spi_series_map(series_map) + sercom_inc_text = emit_sercom_inc(series_map, spi_series_map) + + SERCOM_OUTPUT.write_text(sercom_inc_text) total_devices = sum(len(devices) for devices, _ in series_map.values()) print( - f"Wrote {len(series_map)} series macros covering {total_devices} devices to {OUTPUT}" + f"Wrote SERCOM pinmux for {len(series_map)} series covering {total_devices} devices to {SERCOM_OUTPUT}" ) From 260da0b2c1c8e39ef9b94fa0f2962218095c5cfe Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 13 Apr 2026 21:53:19 -0400 Subject: [PATCH 35/79] document sercom_txn.h purpose more clearly --- cores/arduino/SERCOM_Txn.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/cores/arduino/SERCOM_Txn.h b/cores/arduino/SERCOM_Txn.h index f72aae241..29c08c662 100644 --- a/cores/arduino/SERCOM_Txn.h +++ b/cores/arduino/SERCOM_Txn.h @@ -4,6 +4,28 @@ #include #include +// SercomTxn represents a single bus transaction that occupies one queue slot. +// For multi-phase operations (e.g., TMC2130 read phase1 then phase2, or is31fl3733 +// multi-frame sequences), callbacks can set chainNext=true to hold the queue slot +// across phases without dequeuing. This provides bus atomicity: the transaction +// remains active, preventing other queued transactions from interleaving. +// +// Example multi-phase flow: +// 1. allocateTxn() -> sets chainNext=false +// 2. Phase1: enqueueSPI(txn) -> txn occupies queue slot +// 3. Phase1 DMA completes -> onComplete() callback runs +// 4. Callback updates txPtr/rxPtr/length for phase2, sets chainNext=true +// 5. stopTransmissionSPI() sees chainNext, calls startTransmissionSPI() without dequeue +// 6. Phase2 DMA completes -> onComplete() callback runs +// 7. Callback clears chainNext (or callback already cleared it) +// 8. stopTransmissionSPI() dequeues txn, starts next queued transaction +// +// This design: +// - Avoids explicit claim/release calls (no NVIC priority dance) +// - Keeps atomicity logic in one place (the callback) +// - Allows other devices on different queue slots to proceed normally +// - Prevents queue-full failure during multi-phase chains (slot remains reserved) + struct SercomTxn { uint16_t config; // common + protocol-specific flags/fields uint16_t address; // I2C addr, SPI CS/baud, UART RTS/CTS From dec082ee1c284611b192a18891c168867e65fdbf Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 13 Apr 2026 22:01:42 -0400 Subject: [PATCH 36/79] warn user if SPI or Wire pins are not properly assigned with a boolean begin() return --- cores/arduino/SERCOM.h | 1 + cores/arduino/SERCOM_PinMux.h | 44 +++++++++++++ libraries/SPI/SPI.cpp | 113 +++++++++++++++++++++++++--------- libraries/SPI/SPI.h | 30 ++++----- libraries/Wire/Wire.cpp | 106 +++++++++++++++++++------------ libraries/Wire/Wire.h | 6 +- 6 files changed, 214 insertions(+), 86 deletions(-) create mode 100644 cores/arduino/SERCOM_PinMux.h diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index eeadf05f4..0669216fa 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -20,6 +20,7 @@ #define _SERCOM_CLASS_ #include "sam.h" +#include "SERCOM_PinMux.h" #include "SERCOM_Txn.h" #include "RingBuffer.h" #include diff --git a/cores/arduino/SERCOM_PinMux.h b/cores/arduino/SERCOM_PinMux.h new file mode 100644 index 000000000..6dbe2e98a --- /dev/null +++ b/cores/arduino/SERCOM_PinMux.h @@ -0,0 +1,44 @@ +#pragma once + +/** + * @file SERCOM_PinMux.h + * @brief Family-selection flags and SERCOM pin-route validation APIs. + * + * This header exposes small, shared declarations used by SPI/Wire to validate + * that variant-assigned pins can be routed to the selected SERCOM instance. + * + * @note Function definitions live in each library translation unit: + * - libraries/SPI/SPI.cpp + * - libraries/Wire/Wire.cpp + */ + +#include + +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) +#define FAMILY_SAMD5X ///< Presence flag for SAMD5x/SAME5x family builds. +#else +#define FAMILY_SAMD2X ///< Presence flag for SAMD2x family builds. +#endif + +/** @brief Shared helpers for validating SERCOM pin routing. */ +namespace sercomPinMux { + +/** + * @brief Check whether a variant pin can be used for I2C on a SERCOM instance. + * @param arduinoPin Arduino pin index into g_APinDescription. + * @param sercomIndex Zero-based SERCOM instance index. + * @return true if the pin has an I2C mapping for the requested SERCOM. + */ +bool wirePinValidForSercom(uint8_t arduinoPin, uint8_t sercomIndex); + +/** + * @brief Check whether a MOSI pin matches an SPI route for SERCOM+pad. + * @param arduinoPin Arduino pin index into g_APinDescription. + * @param sercomIndex Zero-based SERCOM instance index. + * @param mosiPad SERCOM MOSI pad index (0, 2, or 3 depending on TX pad mode). + * @return true if the pin supports the requested SERCOM/pad route. + */ +bool spiMosiPinValidForRoute(uint8_t arduinoPin, uint8_t sercomIndex, + uint8_t mosiPad); + +} // namespace sercomPinMux \ No newline at end of file diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 0501dfb72..5cfcb1090 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -33,6 +33,47 @@ const SPISettings DEFAULT_SPI_SETTINGS = SPISettings(); +namespace sercomPinMux { +bool spiMosiPinValidForRoute(uint8_t arduinoPin, uint8_t sercomIndex, + uint8_t mosiPad) { + if (arduinoPin >= PINS_COUNT || mosiPad > 3) + return false; + + const char portLetter = + static_cast('A' + g_APinDescription[arduinoPin].ulPort); + const uint8_t portPin = + static_cast(g_APinDescription[arduinoPin].ulPin); + +#define SERCOM_PINMUX_EMIT_SPI 1 +#define SERCOM_SPI_PIN(PORT_LETTER, PORT_PIN, S0, P0, S1, P1) \ + if ((portLetter == (PORT_LETTER)) && (portPin == (PORT_PIN)) && \ + (((S0) == sercomIndex && (P0) == mosiPad) || \ + ((S1) == sercomIndex && (P1) == mosiPad))) \ + return true; +#include +#undef SERCOM_SPI_PIN +#undef SERCOM_PINMUX_EMIT_SPI + + return false; +} +} // namespace sercomPinMux + +namespace { +static uint8_t spiMosiPadFromTxPad(SercomSpiTXPad txPad) { + switch (txPad) { + case SPI_PAD_0_SCK_1: + case SPI_PAD_0_SCK_3: + return 0; + case SPI_PAD_2_SCK_3: + return 2; + case SPI_PAD_3_SCK_1: + return 3; + default: + return 255; + } +} +} // namespace + SPIClass::SPIClass(SERCOM *p_sercom, uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint8_t uc_pinMOSI, SercomSpiTXPad PadTx, SercomRXPad PadRx) { initialized = false; @@ -52,7 +93,7 @@ SPIClass::SPIClass(SERCOM *p_sercom, uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint txnPoolHead = 0; } -void SPIClass::begin() +bool SPIClass::begin() { if(!initialized) { interruptMode = SPI_IMODE_NONE; @@ -66,11 +107,17 @@ void SPIClass::begin() #endif // PIO init + const uint8_t sercomIndex = static_cast(_p_sercom->getSercomIndex()); + const uint8_t mosiPad = spiMosiPadFromTxPad(_padTx); + if (!sercomPinMux::spiMosiPinValidForRoute(_uc_pinMosi, sercomIndex, mosiPad)) + return false; + pinPeripheral(_uc_pinMiso, g_APinDescription[_uc_pinMiso].ulPinType); pinPeripheral(_uc_pinSCK, g_APinDescription[_uc_pinSCK].ulPinType); pinPeripheral(_uc_pinMosi, g_APinDescription[_uc_pinMosi].ulPinType); config(DEFAULT_SPI_SETTINGS); + return true; } void SPIClass::config(SPISettings settings) @@ -368,7 +415,7 @@ void SPIClass::detachInterrupt() { void SPI_IT_HANDLER(void) __attribute__ ((weak)); void SPI_IT_HANDLER(void) { SPI.onService(); } - #if defined(__SAMD51__) + #ifdef FAMILY_SAMD5X #ifndef SPI_IT_HANDLER_0 #define SPI_IT_HANDLER_0 SERCOM4_0_Handler #define SPI_IT_HANDLER_1 SERCOM4_1_Handler @@ -388,12 +435,7 @@ void SPIClass::detachInterrupt() { #if SPI_INTERFACES_COUNT > 1 SPIClass SPI1(&PERIPH_SPI1, PIN_SPI1_MISO, PIN_SPI1_SCK, PIN_SPI1_MOSI, PAD_SPI1_TX, PAD_SPI1_RX); - #if defined(SPI1_IT_HANDLER) - void SPI1_IT_HANDLER(void) __attribute__ ((weak)); - void SPI1_IT_HANDLER(void) { SPI1.onService(); } - #endif - - #if defined(__SAMD51__) && defined(SPI1_IT_HANDLER_0) + #ifdef FAMILY_SAMD5X void SPI1_IT_HANDLER_0(void) __attribute__ ((weak)); void SPI1_IT_HANDLER_1(void) __attribute__ ((weak)); void SPI1_IT_HANDLER_2(void) __attribute__ ((weak)); @@ -402,61 +444,76 @@ void SPIClass::detachInterrupt() { void SPI1_IT_HANDLER_1(void) { SPI1.onService(); } void SPI1_IT_HANDLER_2(void) { SPI1.onService(); } void SPI1_IT_HANDLER_3(void) { SPI1.onService(); } + #else + void SPI1_IT_HANDLER(void) __attribute__ ((weak)); + void SPI1_IT_HANDLER(void) { SPI1.onService(); } #endif #endif #if SPI_INTERFACES_COUNT > 2 SPIClass SPI2(&PERIPH_SPI2, PIN_SPI2_MISO, PIN_SPI2_SCK, PIN_SPI2_MOSI, PAD_SPI2_TX, PAD_SPI2_RX); - #if defined(SPI2_IT_HANDLER) - void SPI2_IT_HANDLER(void) { SPI2.onService(); } - #endif - - #if defined(__SAMD51__) && defined(SPI2_IT_HANDLER_0) + #ifdef FAMILY_SAMD5X + void SPI2_IT_HANDLER_0(void) __attribute__ ((weak)); + void SPI2_IT_HANDLER_1(void) __attribute__ ((weak)); + void SPI2_IT_HANDLER_2(void) __attribute__ ((weak)); + void SPI2_IT_HANDLER_3(void) __attribute__ ((weak)); void SPI2_IT_HANDLER_0(void) { SPI2.onService(); } void SPI2_IT_HANDLER_1(void) { SPI2.onService(); } void SPI2_IT_HANDLER_2(void) { SPI2.onService(); } void SPI2_IT_HANDLER_3(void) { SPI2.onService(); } + #else + void SPI2_IT_HANDLER(void) __attribute__ ((weak)); + void SPI2_IT_HANDLER(void) { SPI2.onService(); } #endif #endif #if SPI_INTERFACES_COUNT > 3 SPIClass SPI3(&PERIPH_SPI3, PIN_SPI3_MISO, PIN_SPI3_SCK, PIN_SPI3_MOSI, PAD_SPI3_TX, PAD_SPI3_RX); - #if defined(SPI3_IT_HANDLER) - void SPI3_IT_HANDLER(void) { SPI3.onService(); } - #endif - - #if defined(__SAMD51__) && defined(SPI3_IT_HANDLER_0) + #ifdef FAMILY_SAMD5X + void SPI3_IT_HANDLER_0(void) __attribute__ ((weak)); + void SPI3_IT_HANDLER_1(void) __attribute__ ((weak)); + void SPI3_IT_HANDLER_2(void) __attribute__ ((weak)); + void SPI3_IT_HANDLER_3(void) __attribute__ ((weak)); void SPI3_IT_HANDLER_0(void) { SPI3.onService(); } void SPI3_IT_HANDLER_1(void) { SPI3.onService(); } void SPI3_IT_HANDLER_2(void) { SPI3.onService(); } void SPI3_IT_HANDLER_3(void) { SPI3.onService(); } + #else + void SPI3_IT_HANDLER(void) __attribute__ ((weak)); + void SPI3_IT_HANDLER(void) { SPI3.onService(); } #endif #endif #if SPI_INTERFACES_COUNT > 4 SPIClass SPI4(&PERIPH_SPI4, PIN_SPI4_MISO, PIN_SPI4_SCK, PIN_SPI4_MOSI, PAD_SPI4_TX, PAD_SPI4_RX); - #if defined(SPI4_IT_HANDLER) - void SPI4_IT_HANDLER(void) { SPI4.onService(); } - #endif - - #if defined(__SAMD51__) && defined(SPI4_IT_HANDLER_0) + #ifdef FAMILY_SAMD5X + void SPI4_IT_HANDLER_0(void) __attribute__ ((weak)); + void SPI4_IT_HANDLER_1(void) __attribute__ ((weak)); + void SPI4_IT_HANDLER_2(void) __attribute__ ((weak)); + void SPI4_IT_HANDLER_3(void) __attribute__ ((weak)); void SPI4_IT_HANDLER_0(void) { SPI4.onService(); } void SPI4_IT_HANDLER_1(void) { SPI4.onService(); } void SPI4_IT_HANDLER_2(void) { SPI4.onService(); } void SPI4_IT_HANDLER_3(void) { SPI4.onService(); } + #else + void SPI4_IT_HANDLER(void) __attribute__ ((weak)); + void SPI4_IT_HANDLER(void) { SPI4.onService(); } #endif #endif #if SPI_INTERFACES_COUNT > 5 SPIClass SPI5(&PERIPH_SPI5, PIN_SPI5_MISO, PIN_SPI5_SCK, PIN_SPI5_MOSI, PAD_SPI5_TX, PAD_SPI5_RX); - #if defined(SPI5_IT_HANDLER) - void SPI5_IT_HANDLER(void) { SPI5.onService(); } - #endif - - #if defined(__SAMD51__) && defined(SPI5_IT_HANDLER_0) + #ifdef FAMILY_SAMD5X + void SPI5_IT_HANDLER_0(void) __attribute__ ((weak)); + void SPI5_IT_HANDLER_1(void) __attribute__ ((weak)); + void SPI5_IT_HANDLER_2(void) __attribute__ ((weak)); + void SPI5_IT_HANDLER_3(void) __attribute__ ((weak)); void SPI5_IT_HANDLER_0(void) { SPI5.onService(); } void SPI5_IT_HANDLER_1(void) { SPI5.onService(); } void SPI5_IT_HANDLER_2(void) { SPI5.onService(); } void SPI5_IT_HANDLER_3(void) { SPI5.onService(); } + #else + void SPI5_IT_HANDLER(void) __attribute__ ((weak)); + void SPI5_IT_HANDLER(void) { SPI5.onService(); } #endif #endif diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index 533714a0f..0415d22d8 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -38,16 +38,16 @@ #define SPI_MODE2 0x03 #define SPI_MODE3 0x01 -#if defined(__SAMD51__) - // SAMD51 has configurable MAX_SPI, else use peripheral clock default. - // Update: changing MAX_SPI via compiler flags is DEPRECATED, because - // this affects ALL SPI peripherals including some that should NOT be - // changed (e.g. anything using SD card). Configure SERCOM clock source - // directly via the SERCOM API instead. This is left here for compatibility. - #if !defined(MAX_SPI) - #define MAX_SPI 24000000 - #endif - #define SPI_MIN_CLOCK_DIVIDER 1 +#ifdef FAMILY_SAMD5X +// SAMD51 has configurable MAX_SPI, else use peripheral clock default. +// Update: changing MAX_SPI via compiler flags is DEPRECATED, because +// this affects ALL SPI peripherals including some that should NOT be +// changed (e.g. anything using SD card). Configure SERCOM clock source +// directly via the SERCOM API instead. This is left here for compatibility. +#if !defined(MAX_SPI) +#define MAX_SPI 24000000 +#endif +#define SPI_MIN_CLOCK_DIVIDER 1 #else // The datasheet specifies a typical SPI SCK period (tSCK) of 42 ns, // see "Table 36-48. SPI Timing Characteristics and Requirements", @@ -80,7 +80,7 @@ class SPISettings { } void init_AlwaysInline(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) __attribute__((__always_inline__)) { -#if defined(__SAMD51__) +#ifdef FAMILY_SAMD5X this->clockFreq = clock; // Clipping handled in SERCOM.cpp #else this->clockFreq = clock >= MAX_SPI ? MAX_SPI : clock; @@ -136,7 +136,7 @@ class SPIClass { void attachInterrupt(); void detachInterrupt(); - void begin(); + bool begin(); void end(); void setBitOrder(BitOrder order); @@ -159,8 +159,8 @@ class SPIClass { char interruptSave; uint32_t interruptMask; - volatile bool txnDone = false; - volatile int txnStatus = 0; + volatile bool txnDone = false; + volatile int txnStatus = 0; // Transaction pool for async operations (matches SERCOM queue depth) static constexpr size_t TXN_POOL_SIZE = 8; @@ -168,7 +168,7 @@ class SPIClass { uint8_t txnPoolHead; SercomTxn* allocateTxn(); - static void onTxnComplete(void* user, int status); + static void onTxnComplete(void* user, int status); }; #if SPI_INTERFACES_COUNT > 0 diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index 750548932..3cd7c3e82 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -31,6 +31,29 @@ extern "C" { #include "Wire.h" +namespace sercomPinMux { +bool wirePinValidForSercom(uint8_t arduinoPin, uint8_t sercomIndex) { + if (arduinoPin >= PINS_COUNT) + return false; + + const char portLetter = + static_cast('A' + g_APinDescription[arduinoPin].ulPort); + const uint8_t portPin = + static_cast(g_APinDescription[arduinoPin].ulPin); + +#define SERCOM_PINMUX_EMIT_I2C 1 +#define SERCOM_I2C_PIN(PORT_LETTER, PORT_PIN, S0, P0, S1, P1) \ + if ((portLetter == (PORT_LETTER)) && (portPin == (PORT_PIN)) && \ + (((S0) == sercomIndex) || ((S1) == sercomIndex))) \ + return true; +#include +#undef SERCOM_I2C_PIN +#undef SERCOM_PINMUX_EMIT_I2C + + return false; +} +} // namespace sercomPinMux + TwoWire::TwoWire(SERCOM * s, uint8_t pinSDA, uint8_t pinSCL) { this->sercom = s; @@ -51,27 +74,40 @@ TwoWire::TwoWire(SERCOM * s, uint8_t pinSDA, uint8_t pinSCL) txnPoolHead = 0; } -void TwoWire::begin(void) { +bool TwoWire::begin(void) { //Master Mode + const uint8_t sercomIndex = static_cast(sercom->getSercomIndex()); + if (!sercomPinMux::wirePinValidForSercom(_uc_pinSDA, sercomIndex) || + !sercomPinMux::wirePinValidForSercom(_uc_pinSCL, sercomIndex)) + return false; + pinPeripheral(_uc_pinSDA, g_APinDescription[_uc_pinSDA].ulPinType); pinPeripheral(_uc_pinSCL, g_APinDescription[_uc_pinSCL].ulPinType); sercom->initMasterWIRE(TWI_CLOCK); sercom->enableWIRE(); + return true; } -void TwoWire::begin(uint16_t address, bool enableGeneralCall, uint8_t speed, bool enable10Bit) { +bool TwoWire::begin(uint16_t address, bool enableGeneralCall, uint8_t speed, + bool enable10Bit) { //Slave mode + const uint8_t sercomIndex = static_cast(sercom->getSercomIndex()); + if (!sercomPinMux::wirePinValidForSercom(_uc_pinSDA, sercomIndex) || + !sercomPinMux::wirePinValidForSercom(_uc_pinSCL, sercomIndex)) + return false; + pinPeripheral(_uc_pinSDA, g_APinDescription[_uc_pinSDA].ulPinType); pinPeripheral(_uc_pinSCL, g_APinDescription[_uc_pinSCL].ulPinType); sercom->initSlaveWIRE(address, enableGeneralCall, speed, enable10Bit); sercom->enableWIRE(); sercom->registerReceiveWIRE(&TwoWire::onDeferredReceive, this); + return true; } -void TwoWire::begin(uint8_t address, bool enableGeneralCall) { - begin(static_cast(address), enableGeneralCall); +bool TwoWire::begin(uint8_t address, bool enableGeneralCall) { + return begin(static_cast(address), enableGeneralCall); } void TwoWire::setClock(uint32_t baudrate) { @@ -431,85 +467,75 @@ void TwoWire::onDeferredReceive(void* user, int length) Wire.onService(); } - #if defined(__SAMD51__) - void WIRE_IT_HANDLER_0(void) { Wire.onService(); } - void WIRE_IT_HANDLER_1(void) { Wire.onService(); } - void WIRE_IT_HANDLER_2(void) { Wire.onService(); } - void WIRE_IT_HANDLER_3(void) { Wire.onService(); } - #endif // __SAMD51__ +#ifdef FAMILY_SAMD5X + void WIRE_IT_HANDLER_0(void) { Wire.onService(); } + void WIRE_IT_HANDLER_1(void) { Wire.onService(); } + void WIRE_IT_HANDLER_2(void) { Wire.onService(); } + void WIRE_IT_HANDLER_3(void) { Wire.onService(); } +#endif // FAMILY_SAMD5X #endif #if WIRE_INTERFACES_COUNT > 1 - TwoWire Wire1(&PERIPH_WIRE1, PIN_WIRE1_SDA, PIN_WIRE1_SCL); + TwoWire Wire1(&PERIPH_WIRE1, PIN_WIRE1_SDA, PIN_WIRE1_SCL); - void WIRE1_IT_HANDLER(void) { - Wire1.onService(); - } + void WIRE1_IT_HANDLER(void) { Wire1.onService(); } - #if defined(__SAMD51__) +#ifdef FAMILY_SAMD5X void WIRE1_IT_HANDLER_0(void) { Wire1.onService(); } void WIRE1_IT_HANDLER_1(void) { Wire1.onService(); } void WIRE1_IT_HANDLER_2(void) { Wire1.onService(); } void WIRE1_IT_HANDLER_3(void) { Wire1.onService(); } - #endif // __SAMD51__ +#endif // FAMILY_SAMD5X #endif #if WIRE_INTERFACES_COUNT > 2 - TwoWire Wire2(&PERIPH_WIRE2, PIN_WIRE2_SDA, PIN_WIRE2_SCL); + TwoWire Wire2(&PERIPH_WIRE2, PIN_WIRE2_SDA, PIN_WIRE2_SCL); - void WIRE2_IT_HANDLER(void) { - Wire2.onService(); - } + void WIRE2_IT_HANDLER(void) { Wire2.onService(); } - #if defined(__SAMD51__) +#ifdef FAMILY_SAMD5X void WIRE2_IT_HANDLER_0(void) { Wire2.onService(); } void WIRE2_IT_HANDLER_1(void) { Wire2.onService(); } void WIRE2_IT_HANDLER_2(void) { Wire2.onService(); } void WIRE2_IT_HANDLER_3(void) { Wire2.onService(); } - #endif // __SAMD51__ +#endif // FAMILY_SAMD5X #endif #if WIRE_INTERFACES_COUNT > 3 - TwoWire Wire3(&PERIPH_WIRE3, PIN_WIRE3_SDA, PIN_WIRE3_SCL); + TwoWire Wire3(&PERIPH_WIRE3, PIN_WIRE3_SDA, PIN_WIRE3_SCL); - void WIRE3_IT_HANDLER(void) { - Wire3.onService(); - } + void WIRE3_IT_HANDLER(void) { Wire3.onService(); } - #if defined(__SAMD51__) +#ifdef FAMILY_SAMD5X void WIRE3_IT_HANDLER_0(void) { Wire3.onService(); } void WIRE3_IT_HANDLER_1(void) { Wire3.onService(); } void WIRE3_IT_HANDLER_2(void) { Wire3.onService(); } void WIRE3_IT_HANDLER_3(void) { Wire3.onService(); } - #endif // __SAMD51__ +#endif // FAMILY_SAMD5X #endif #if WIRE_INTERFACES_COUNT > 4 - TwoWire Wire4(&PERIPH_WIRE4, PIN_WIRE4_SDA, PIN_WIRE4_SCL); + TwoWire Wire4(&PERIPH_WIRE4, PIN_WIRE4_SDA, PIN_WIRE4_SCL); - void WIRE4_IT_HANDLER(void) { - Wire4.onService(); - } + void WIRE4_IT_HANDLER(void) { Wire4.onService(); } - #if defined(__SAMD51__) +#ifdef FAMILY_SAMD5X void WIRE4_IT_HANDLER_0(void) { Wire4.onService(); } void WIRE4_IT_HANDLER_1(void) { Wire4.onService(); } void WIRE4_IT_HANDLER_2(void) { Wire4.onService(); } void WIRE4_IT_HANDLER_3(void) { Wire4.onService(); } - #endif // __SAMD51__ +#endif // FAMILY_SAMD5X #endif #if WIRE_INTERFACES_COUNT > 5 - TwoWire Wire5(&PERIPH_WIRE5, PIN_WIRE5_SDA, PIN_WIRE5_SCL); + TwoWire Wire5(&PERIPH_WIRE5, PIN_WIRE5_SDA, PIN_WIRE5_SCL); - void WIRE5_IT_HANDLER(void) { - Wire5.onService(); - } + void WIRE5_IT_HANDLER(void) { Wire5.onService(); } - #if defined(__SAMD51__) +#ifdef FAMILY_SAMD5X void WIRE5_IT_HANDLER_0(void) { Wire5.onService(); } void WIRE5_IT_HANDLER_1(void) { Wire5.onService(); } void WIRE5_IT_HANDLER_2(void) { Wire5.onService(); } void WIRE5_IT_HANDLER_3(void) { Wire5.onService(); } - #endif // __SAMD51__ +#endif // FAMILY_SAMD5X #endif diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 0eb758325..fdc838b4b 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -40,9 +40,9 @@ class TwoWire : public Stream { public: TwoWire(SERCOM *s, uint8_t pinSDA, uint8_t pinSCL); - void begin(); - void begin(uint8_t, bool enableGeneralCall = false); - void begin(uint16_t, bool enableGeneralCall, uint8_t speed = 0x0, + bool begin(); + bool begin(uint8_t, bool enableGeneralCall = false); + bool begin(uint16_t, bool enableGeneralCall, uint8_t speed = 0x0, bool enable10Bit = false); void end(); void setClock(uint32_t); From fff3c38605a1d29b511477cc89971a05f5daa04c Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 13 Apr 2026 22:02:44 -0400 Subject: [PATCH 37/79] fix UART test regression --- cores/arduino/SERCOM.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index b239c8389..947a13f96 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -143,6 +143,22 @@ void SERCOM::initPads(SercomUartTXPad txPad, SercomRXPad rxPad) void SERCOM::resetUART() { + _txnQueue.clear(); + _uart.currentTxn = nullptr; + _uart.index = 0; + _uart.length = 0; + _uart.active = false; + _uart.returnValue = SercomUartError::SUCCESS; + +#ifdef USE_ZERODMA + _uart.dmaNeedTx = false; + _uart.dmaNeedRx = false; + _uart.dmaTxDone = false; + _uart.dmaRxDone = false; + dmaAbortTx(); + dmaAbortRx(); +#endif + resetSERCOM(); } @@ -243,6 +259,7 @@ bool SERCOM::startTransmissionUART(void) _uart.index = 0; _uart.length = txn->length; _uart.active = true; + _uart.returnValue = SercomUartError::SUCCESS; #ifdef USE_ZERODMA _uart.useDma = _dmaConfigured; @@ -251,7 +268,12 @@ bool SERCOM::startTransmissionUART(void) #endif if (!_uart.useDma) + { + _uart.active = false; + _uart.currentTxn = nullptr; + _uart.returnValue = SercomUartError::UNKNOWN_ERROR; return false; + } #ifdef USE_ZERODMA void* dataReg = (void*)&sercom->USART.DATA.reg; @@ -267,8 +289,9 @@ bool SERCOM::startTransmissionUART(void) st = dmaStartRx(txn->rxPtr, dataReg, txn->length); if (st != DmaStatus::Ok) { + _uart.active = false; + _uart.currentTxn = nullptr; _uart.returnValue = SercomUartError::UNKNOWN_ERROR; - deferStopUART(_uart.returnValue); return false; } return true; From 5beb170d02a51142418013681c411ab1dc1c5ceb Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 17 Apr 2026 15:48:20 -0400 Subject: [PATCH 38/79] fix(spi): derive IRQ handlers from PERIPH_SPIx and add SPI6/SPI7 support --- libraries/SPI/SPI.cpp | 302 +++++++++++++++++++++++++++++++----------- libraries/SPI/SPI.h | 26 ++-- 2 files changed, 244 insertions(+), 84 deletions(-) diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 5cfcb1090..e8df6a7e0 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -31,6 +31,50 @@ #define SPI_IMODE_EXTINT 1 #define SPI_IMODE_GLOBAL 2 +// Derive SERCOM ISR symbols from PERIPH_SPI* tokens (e.g. sercom4 -> +// SERCOM4_Handler) so variants don't need to duplicate SPI_IT_HANDLER +// definitions. +#define SPI_SERCOM_INDEX_sercom0 0 +#define SPI_SERCOM_INDEX_sercom1 1 +#define SPI_SERCOM_INDEX_sercom2 2 +#define SPI_SERCOM_INDEX_sercom3 3 +#define SPI_SERCOM_INDEX_sercom4 4 +#define SPI_SERCOM_INDEX_sercom5 5 +#if defined(SERCOM6) +#define SPI_SERCOM_INDEX_sercom6 6 +#endif +#if defined(SERCOM7) +#define SPI_SERCOM_INDEX_sercom7 7 +#endif + +#define SPI_SERCOM_INDEX(token) SPI_SERCOM_INDEX_##token +#define SPI_SERCOM_HANDLER_FROM_INDEX(idx) SPI_SERCOM_HANDLER_FROM_INDEX_2(idx) +#define SPI_SERCOM_HANDLER_FROM_INDEX_2(idx) SERCOM##idx##_Handler +#define SPI_SERCOM_HANDLER_FROM_TOKEN(token) \ + SPI_SERCOM_HANDLER_FROM_INDEX(SPI_SERCOM_INDEX(token)) + +#define SPI_SERCOM_HANDLER0_FROM_INDEX(idx) \ + SPI_SERCOM_HANDLER0_FROM_INDEX_2(idx) +#define SPI_SERCOM_HANDLER0_FROM_INDEX_2(idx) SERCOM##idx##_0_Handler +#define SPI_SERCOM_HANDLER1_FROM_INDEX(idx) \ + SPI_SERCOM_HANDLER1_FROM_INDEX_2(idx) +#define SPI_SERCOM_HANDLER1_FROM_INDEX_2(idx) SERCOM##idx##_1_Handler +#define SPI_SERCOM_HANDLER2_FROM_INDEX(idx) \ + SPI_SERCOM_HANDLER2_FROM_INDEX_2(idx) +#define SPI_SERCOM_HANDLER2_FROM_INDEX_2(idx) SERCOM##idx##_2_Handler +#define SPI_SERCOM_HANDLER3_FROM_INDEX(idx) \ + SPI_SERCOM_HANDLER3_FROM_INDEX_2(idx) +#define SPI_SERCOM_HANDLER3_FROM_INDEX_2(idx) SERCOM##idx##_3_Handler + +#define SPI_SERCOM_HANDLER0_FROM_TOKEN(token) \ + SPI_SERCOM_HANDLER0_FROM_INDEX(SPI_SERCOM_INDEX(token)) +#define SPI_SERCOM_HANDLER1_FROM_TOKEN(token) \ + SPI_SERCOM_HANDLER1_FROM_INDEX(SPI_SERCOM_INDEX(token)) +#define SPI_SERCOM_HANDLER2_FROM_TOKEN(token) \ + SPI_SERCOM_HANDLER2_FROM_INDEX(SPI_SERCOM_INDEX(token)) +#define SPI_SERCOM_HANDLER3_FROM_TOKEN(token) \ + SPI_SERCOM_HANDLER3_FROM_INDEX(SPI_SERCOM_INDEX(token)) + const SPISettings DEFAULT_SPI_SETTINGS = SPISettings(); namespace sercomPinMux { @@ -410,110 +454,216 @@ void SPIClass::detachInterrupt() { SPIClass SPI (&PERIPH_SPI, PIN_SPI_MISO, PIN_SPI_SCK, PIN_SPI_MOSI, PAD_SPI_TX, PAD_SPI_RX); #ifndef SPI_IT_HANDLER - #define SPI_IT_HANDLER SERCOM4_Handler - #endif +#define SPI_IT_HANDLER SPI_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SPI) +#endif void SPI_IT_HANDLER(void) __attribute__ ((weak)); void SPI_IT_HANDLER(void) { SPI.onService(); } #ifdef FAMILY_SAMD5X #ifndef SPI_IT_HANDLER_0 - #define SPI_IT_HANDLER_0 SERCOM4_0_Handler - #define SPI_IT_HANDLER_1 SERCOM4_1_Handler - #define SPI_IT_HANDLER_2 SERCOM4_2_Handler - #define SPI_IT_HANDLER_3 SERCOM4_3_Handler - #endif - void SPI_IT_HANDLER_0(void) __attribute__ ((weak)); - void SPI_IT_HANDLER_1(void) __attribute__ ((weak)); - void SPI_IT_HANDLER_2(void) __attribute__ ((weak)); - void SPI_IT_HANDLER_3(void) __attribute__ ((weak)); - void SPI_IT_HANDLER_0(void) { SPI.onService(); } - void SPI_IT_HANDLER_1(void) { SPI.onService(); } - void SPI_IT_HANDLER_2(void) { SPI.onService(); } - void SPI_IT_HANDLER_3(void) { SPI.onService(); } - #endif +#define SPI_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI) +#define SPI_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI) +#define SPI_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI) +#define SPI_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI) +#endif + void SPI_IT_HANDLER_0(void) __attribute__((weak)); + void SPI_IT_HANDLER_1(void) __attribute__((weak)); + void SPI_IT_HANDLER_2(void) __attribute__((weak)); + void SPI_IT_HANDLER_3(void) __attribute__((weak)); + void SPI_IT_HANDLER_0(void) { SPI.onService(); } + void SPI_IT_HANDLER_1(void) { SPI.onService(); } + void SPI_IT_HANDLER_2(void) { SPI.onService(); } + void SPI_IT_HANDLER_3(void) { SPI.onService(); } +#endif #endif #if SPI_INTERFACES_COUNT > 1 SPIClass SPI1(&PERIPH_SPI1, PIN_SPI1_MISO, PIN_SPI1_SCK, PIN_SPI1_MOSI, PAD_SPI1_TX, PAD_SPI1_RX); - #ifdef FAMILY_SAMD5X - void SPI1_IT_HANDLER_0(void) __attribute__ ((weak)); - void SPI1_IT_HANDLER_1(void) __attribute__ ((weak)); - void SPI1_IT_HANDLER_2(void) __attribute__ ((weak)); - void SPI1_IT_HANDLER_3(void) __attribute__ ((weak)); - void SPI1_IT_HANDLER_0(void) { SPI1.onService(); } - void SPI1_IT_HANDLER_1(void) { SPI1.onService(); } - void SPI1_IT_HANDLER_2(void) { SPI1.onService(); } - void SPI1_IT_HANDLER_3(void) { SPI1.onService(); } - #else - void SPI1_IT_HANDLER(void) __attribute__ ((weak)); - void SPI1_IT_HANDLER(void) { SPI1.onService(); } +#ifndef SPI1_IT_HANDLER +#define SPI1_IT_HANDLER SPI_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SPI1) +#endif + +#ifdef FAMILY_SAMD5X +#ifndef SPI1_IT_HANDLER_0 +#define SPI1_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI1) +#define SPI1_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI1) +#define SPI1_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI1) +#define SPI1_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI1) +#endif + void SPI1_IT_HANDLER_0(void) __attribute__((weak)); + void SPI1_IT_HANDLER_1(void) __attribute__((weak)); + void SPI1_IT_HANDLER_2(void) __attribute__((weak)); + void SPI1_IT_HANDLER_3(void) __attribute__((weak)); + void SPI1_IT_HANDLER_0(void) { SPI1.onService(); } + void SPI1_IT_HANDLER_1(void) { SPI1.onService(); } + void SPI1_IT_HANDLER_2(void) { SPI1.onService(); } + void SPI1_IT_HANDLER_3(void) { SPI1.onService(); } +#else + void SPI1_IT_HANDLER(void) __attribute__((weak)); + void SPI1_IT_HANDLER(void) { SPI1.onService(); } #endif #endif #if SPI_INTERFACES_COUNT > 2 SPIClass SPI2(&PERIPH_SPI2, PIN_SPI2_MISO, PIN_SPI2_SCK, PIN_SPI2_MOSI, PAD_SPI2_TX, PAD_SPI2_RX); - #ifdef FAMILY_SAMD5X - void SPI2_IT_HANDLER_0(void) __attribute__ ((weak)); - void SPI2_IT_HANDLER_1(void) __attribute__ ((weak)); - void SPI2_IT_HANDLER_2(void) __attribute__ ((weak)); - void SPI2_IT_HANDLER_3(void) __attribute__ ((weak)); - void SPI2_IT_HANDLER_0(void) { SPI2.onService(); } - void SPI2_IT_HANDLER_1(void) { SPI2.onService(); } - void SPI2_IT_HANDLER_2(void) { SPI2.onService(); } - void SPI2_IT_HANDLER_3(void) { SPI2.onService(); } - #else - void SPI2_IT_HANDLER(void) __attribute__ ((weak)); - void SPI2_IT_HANDLER(void) { SPI2.onService(); } +#ifndef SPI2_IT_HANDLER +#define SPI2_IT_HANDLER SPI_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SPI2) +#endif + +#ifdef FAMILY_SAMD5X +#ifndef SPI2_IT_HANDLER_0 +#define SPI2_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI2) +#define SPI2_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI2) +#define SPI2_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI2) +#define SPI2_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI2) +#endif + void SPI2_IT_HANDLER_0(void) __attribute__((weak)); + void SPI2_IT_HANDLER_1(void) __attribute__((weak)); + void SPI2_IT_HANDLER_2(void) __attribute__((weak)); + void SPI2_IT_HANDLER_3(void) __attribute__((weak)); + void SPI2_IT_HANDLER_0(void) { SPI2.onService(); } + void SPI2_IT_HANDLER_1(void) { SPI2.onService(); } + void SPI2_IT_HANDLER_2(void) { SPI2.onService(); } + void SPI2_IT_HANDLER_3(void) { SPI2.onService(); } +#else + void SPI2_IT_HANDLER(void) __attribute__((weak)); + void SPI2_IT_HANDLER(void) { SPI2.onService(); } #endif #endif #if SPI_INTERFACES_COUNT > 3 SPIClass SPI3(&PERIPH_SPI3, PIN_SPI3_MISO, PIN_SPI3_SCK, PIN_SPI3_MOSI, PAD_SPI3_TX, PAD_SPI3_RX); - #ifdef FAMILY_SAMD5X - void SPI3_IT_HANDLER_0(void) __attribute__ ((weak)); - void SPI3_IT_HANDLER_1(void) __attribute__ ((weak)); - void SPI3_IT_HANDLER_2(void) __attribute__ ((weak)); - void SPI3_IT_HANDLER_3(void) __attribute__ ((weak)); - void SPI3_IT_HANDLER_0(void) { SPI3.onService(); } - void SPI3_IT_HANDLER_1(void) { SPI3.onService(); } - void SPI3_IT_HANDLER_2(void) { SPI3.onService(); } - void SPI3_IT_HANDLER_3(void) { SPI3.onService(); } - #else - void SPI3_IT_HANDLER(void) __attribute__ ((weak)); - void SPI3_IT_HANDLER(void) { SPI3.onService(); } +#ifndef SPI3_IT_HANDLER +#define SPI3_IT_HANDLER SPI_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SPI3) +#endif + +#ifdef FAMILY_SAMD5X +#ifndef SPI3_IT_HANDLER_0 +#define SPI3_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI3) +#define SPI3_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI3) +#define SPI3_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI3) +#define SPI3_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI3) +#endif + void SPI3_IT_HANDLER_0(void) __attribute__((weak)); + void SPI3_IT_HANDLER_1(void) __attribute__((weak)); + void SPI3_IT_HANDLER_2(void) __attribute__((weak)); + void SPI3_IT_HANDLER_3(void) __attribute__((weak)); + void SPI3_IT_HANDLER_0(void) { SPI3.onService(); } + void SPI3_IT_HANDLER_1(void) { SPI3.onService(); } + void SPI3_IT_HANDLER_2(void) { SPI3.onService(); } + void SPI3_IT_HANDLER_3(void) { SPI3.onService(); } +#else + void SPI3_IT_HANDLER(void) __attribute__((weak)); + void SPI3_IT_HANDLER(void) { SPI3.onService(); } #endif #endif #if SPI_INTERFACES_COUNT > 4 SPIClass SPI4(&PERIPH_SPI4, PIN_SPI4_MISO, PIN_SPI4_SCK, PIN_SPI4_MOSI, PAD_SPI4_TX, PAD_SPI4_RX); - #ifdef FAMILY_SAMD5X - void SPI4_IT_HANDLER_0(void) __attribute__ ((weak)); - void SPI4_IT_HANDLER_1(void) __attribute__ ((weak)); - void SPI4_IT_HANDLER_2(void) __attribute__ ((weak)); - void SPI4_IT_HANDLER_3(void) __attribute__ ((weak)); - void SPI4_IT_HANDLER_0(void) { SPI4.onService(); } - void SPI4_IT_HANDLER_1(void) { SPI4.onService(); } - void SPI4_IT_HANDLER_2(void) { SPI4.onService(); } - void SPI4_IT_HANDLER_3(void) { SPI4.onService(); } - #else - void SPI4_IT_HANDLER(void) __attribute__ ((weak)); - void SPI4_IT_HANDLER(void) { SPI4.onService(); } +#ifndef SPI4_IT_HANDLER +#define SPI4_IT_HANDLER SPI_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SPI4) +#endif + +#ifdef FAMILY_SAMD5X +#ifndef SPI4_IT_HANDLER_0 +#define SPI4_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI4) +#define SPI4_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI4) +#define SPI4_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI4) +#define SPI4_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI4) +#endif + void SPI4_IT_HANDLER_0(void) __attribute__((weak)); + void SPI4_IT_HANDLER_1(void) __attribute__((weak)); + void SPI4_IT_HANDLER_2(void) __attribute__((weak)); + void SPI4_IT_HANDLER_3(void) __attribute__((weak)); + void SPI4_IT_HANDLER_0(void) { SPI4.onService(); } + void SPI4_IT_HANDLER_1(void) { SPI4.onService(); } + void SPI4_IT_HANDLER_2(void) { SPI4.onService(); } + void SPI4_IT_HANDLER_3(void) { SPI4.onService(); } +#else + void SPI4_IT_HANDLER(void) __attribute__((weak)); + void SPI4_IT_HANDLER(void) { SPI4.onService(); } #endif #endif #if SPI_INTERFACES_COUNT > 5 SPIClass SPI5(&PERIPH_SPI5, PIN_SPI5_MISO, PIN_SPI5_SCK, PIN_SPI5_MOSI, PAD_SPI5_TX, PAD_SPI5_RX); - #ifdef FAMILY_SAMD5X - void SPI5_IT_HANDLER_0(void) __attribute__ ((weak)); - void SPI5_IT_HANDLER_1(void) __attribute__ ((weak)); - void SPI5_IT_HANDLER_2(void) __attribute__ ((weak)); - void SPI5_IT_HANDLER_3(void) __attribute__ ((weak)); - void SPI5_IT_HANDLER_0(void) { SPI5.onService(); } - void SPI5_IT_HANDLER_1(void) { SPI5.onService(); } - void SPI5_IT_HANDLER_2(void) { SPI5.onService(); } - void SPI5_IT_HANDLER_3(void) { SPI5.onService(); } - #else - void SPI5_IT_HANDLER(void) __attribute__ ((weak)); - void SPI5_IT_HANDLER(void) { SPI5.onService(); } +#ifndef SPI5_IT_HANDLER +#define SPI5_IT_HANDLER SPI_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SPI5) +#endif + +#ifdef FAMILY_SAMD5X +#ifndef SPI5_IT_HANDLER_0 +#define SPI5_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI5) +#define SPI5_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI5) +#define SPI5_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI5) +#define SPI5_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI5) +#endif + void SPI5_IT_HANDLER_0(void) __attribute__((weak)); + void SPI5_IT_HANDLER_1(void) __attribute__((weak)); + void SPI5_IT_HANDLER_2(void) __attribute__((weak)); + void SPI5_IT_HANDLER_3(void) __attribute__((weak)); + void SPI5_IT_HANDLER_0(void) { SPI5.onService(); } + void SPI5_IT_HANDLER_1(void) { SPI5.onService(); } + void SPI5_IT_HANDLER_2(void) { SPI5.onService(); } + void SPI5_IT_HANDLER_3(void) { SPI5.onService(); } +#else + void SPI5_IT_HANDLER(void) __attribute__((weak)); + void SPI5_IT_HANDLER(void) { SPI5.onService(); } #endif #endif +#if SPI_INTERFACES_COUNT > 6 + SPIClass SPI6(&PERIPH_SPI6, PIN_SPI6_MISO, PIN_SPI6_SCK, PIN_SPI6_MOSI, + PAD_SPI6_TX, PAD_SPI6_RX); + +#ifndef SPI6_IT_HANDLER +#define SPI6_IT_HANDLER SPI_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SPI6) +#endif + +#ifdef FAMILY_SAMD5X +#ifndef SPI6_IT_HANDLER_0 +#define SPI6_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI6) +#define SPI6_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI6) +#define SPI6_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI6) +#define SPI6_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI6) +#endif + void SPI6_IT_HANDLER_0(void) __attribute__((weak)); + void SPI6_IT_HANDLER_1(void) __attribute__((weak)); + void SPI6_IT_HANDLER_2(void) __attribute__((weak)); + void SPI6_IT_HANDLER_3(void) __attribute__((weak)); + void SPI6_IT_HANDLER_0(void) { SPI6.onService(); } + void SPI6_IT_HANDLER_1(void) { SPI6.onService(); } + void SPI6_IT_HANDLER_2(void) { SPI6.onService(); } + void SPI6_IT_HANDLER_3(void) { SPI6.onService(); } +#else + void SPI6_IT_HANDLER(void) __attribute__((weak)); + void SPI6_IT_HANDLER(void) { SPI6.onService(); } +#endif +#endif +#if SPI_INTERFACES_COUNT > 7 + SPIClass SPI7(&PERIPH_SPI7, PIN_SPI7_MISO, PIN_SPI7_SCK, PIN_SPI7_MOSI, + PAD_SPI7_TX, PAD_SPI7_RX); + +#ifndef SPI7_IT_HANDLER +#define SPI7_IT_HANDLER SPI_SERCOM_HANDLER_FROM_TOKEN(PERIPH_SPI7) +#endif + +#ifdef FAMILY_SAMD5X +#ifndef SPI7_IT_HANDLER_0 +#define SPI7_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI7) +#define SPI7_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI7) +#define SPI7_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI7) +#define SPI7_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI7) +#endif + void SPI7_IT_HANDLER_0(void) __attribute__((weak)); + void SPI7_IT_HANDLER_1(void) __attribute__((weak)); + void SPI7_IT_HANDLER_2(void) __attribute__((weak)); + void SPI7_IT_HANDLER_3(void) __attribute__((weak)); + void SPI7_IT_HANDLER_0(void) { SPI7.onService(); } + void SPI7_IT_HANDLER_1(void) { SPI7.onService(); } + void SPI7_IT_HANDLER_2(void) { SPI7.onService(); } + void SPI7_IT_HANDLER_3(void) { SPI7.onService(); } +#else + void SPI7_IT_HANDLER(void) __attribute__((weak)); + void SPI7_IT_HANDLER(void) { SPI7.onService(); } +#endif +#endif diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index 0415d22d8..c2e33d9bc 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -23,6 +23,10 @@ #include #include "SERCOM.h" +#if defined(SERCOM_INST_NUM) && (SPI_INTERFACES_COUNT > SERCOM_INST_NUM) +#error "SPI_INTERFACES_COUNT exceeds available SERCOM instances for this MCU" +#endif + // SPI_HAS_TRANSACTION means SPI has // - beginTransaction() // - endTransaction() @@ -189,15 +193,21 @@ class SPIClass { #if SPI_INTERFACES_COUNT > 5 extern SPIClass SPI5; #endif +#if SPI_INTERFACES_COUNT > 6 + extern SPIClass SPI6; +#endif +#if SPI_INTERFACES_COUNT > 7 + extern SPIClass SPI7; +#endif -// For compatibility with sketches designed for AVR @ 16 MHz -// New programs should use SPI.beginTransaction to set the SPI clock -#define SPI_CLOCK_DIV2 (MAX_SPI * 2 / 8000000) -#define SPI_CLOCK_DIV4 (MAX_SPI * 2 / 4000000) -#define SPI_CLOCK_DIV8 (MAX_SPI * 2 / 2000000) -#define SPI_CLOCK_DIV16 (MAX_SPI * 2 / 1000000) -#define SPI_CLOCK_DIV32 (MAX_SPI * 2 / 500000) -#define SPI_CLOCK_DIV64 (MAX_SPI * 2 / 250000) + // For compatibility with sketches designed for AVR @ 16 MHz + // New programs should use SPI.beginTransaction to set the SPI clock +#define SPI_CLOCK_DIV2 (MAX_SPI * 2 / 8000000) +#define SPI_CLOCK_DIV4 (MAX_SPI * 2 / 4000000) +#define SPI_CLOCK_DIV8 (MAX_SPI * 2 / 2000000) +#define SPI_CLOCK_DIV16 (MAX_SPI * 2 / 1000000) +#define SPI_CLOCK_DIV32 (MAX_SPI * 2 / 500000) +#define SPI_CLOCK_DIV64 (MAX_SPI * 2 / 250000) #define SPI_CLOCK_DIV128 (MAX_SPI * 2 / 125000) #endif From eff051156c3ffcf1e6926111ce3a242b2ca0287d Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sun, 17 May 2026 13:39:57 -0500 Subject: [PATCH 39/79] Preserve Wire sync compatibility while tightening async transaction handling. --- .gitignore | 3 ++ cores/arduino/SERCOM.cpp | 9 ++++-- libraries/Wire/Wire.cpp | 61 +++++++++++++++++----------------------- libraries/Wire/Wire.h | 21 ++++---------- 4 files changed, 41 insertions(+), 53 deletions(-) diff --git a/.gitignore b/.gitignore index ef8f29944..82515546a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ bootloaders/*/build/ *~ /libraries/**/build/ +.ci-home +.ci_home +*.log \ No newline at end of file diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 947a13f96..b1fcebda6 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -951,9 +951,12 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) return txn; } - if(isMasterWIRE()) - _txnQueue.read(txn); // remove the completed transaction from the queue - else { + bool dequeued = false; + SercomTxn* headTxn = nullptr; + if (_txnQueue.peek(headTxn) && headTxn == txn) + dequeued = _txnQueue.read(txn); // remove completed master txn when it is queue head + + if (!dequeued) { // Deliver deferred WIRE callback outside the SERCOM ISR (from PendSV). // This avoids running user code in the hardware interrupt context. if (_wireDeferredPending && _wireDeferredCb) { diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index 3cd7c3e82..ea596d032 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -136,20 +136,22 @@ uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit, uin loader.config = I2C_CFG_READ | (stopBit ? I2C_CFG_STOP : 0); loader.address = address; - loader.onComplete = onComplete ? onComplete : &TwoWire::onTxnComplete; - + // Allocate fresh transaction from pool and copy loader data SercomTxn* txn = allocateTxn(); *txn = loader; txn->chainNext = false; - - // For async callbacks, pass txn as user so callback can access txn->rxPtr/length directly - // For sync calls, pass 'this' so onTxnComplete can update txnStatus/txnDone - if (onComplete) + + // For caller callbacks, pass txn as user so callback can access + // txn->rxPtr/length directly. + if (onComplete) { + txn->onComplete = onComplete; txn->user = (user == nullptr) ? txn : user; - else + } else { + txn->onComplete = &TwoWire::onTxnComplete; txn->user = this; - + } + awaitingAddressAck = true; txnDone = false; @@ -158,28 +160,19 @@ uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit, uin return 0; if (!onComplete) { - // Wait for transaction to complete (onTxnComplete sets txnDone) with timeout - uint32_t startMillis = millis(); - const uint32_t timeout = 1000; // 1 second timeout - while (!txnDone) { - if (millis() - startMillis > timeout) - return 0; - + while (!txnDone) yield(); - } if (txnStatus != static_cast(SercomWireError::SUCCESS)) return 0; - // Set up pointers for Wire.available()/read() to access the data - // txn->rxPtr already points to this->rxBuffer (from loader copy) rxBufferPtr = loader.rxPtr; - rxLength = quantity; + rxLength = loader.length; rxIndex = 0; return rxLength; } - return quantity; + return loader.length; } SercomTxn* TwoWire::allocateTxn() { @@ -222,29 +215,27 @@ uint8_t TwoWire::endTransmission(bool stopBit, void (*onComplete)(void* user, in // Set parameters that weren't known during beginTransmission/write txn->config = stopBit ? I2C_CFG_STOP : 0; - txn->onComplete = onComplete ? onComplete : &TwoWire::onTxnComplete; - if (onComplete) + if (onComplete) { + txn->onComplete = onComplete; txn->user = (user == nullptr) ? txn : user; - else + } else { + txn->onComplete = &TwoWire::onTxnComplete; txn->user = this; - + } + awaitingAddressAck = true; txnDone = false; // Enqueue the pool transaction, not the loader - if (!sercom->enqueueWIRE(txn)) - return static_cast(SercomWireError::QUEUE_FULL); + if (!sercom->enqueueWIRE(txn)) { + if (onComplete) + return static_cast(SercomWireError::QUEUE_FULL); + return 4; + } if (!onComplete) { - // Wait for transaction to complete (onTxnComplete sets txnDone) with timeout - uint32_t startMillis = millis(); - const uint32_t timeout = 1000; // 1 second timeout - while (!txnDone) { - if (millis() - startMillis > timeout) - return 4; // OTHER error - + while (!txnDone) yield(); - } SercomWireError err = static_cast(txnStatus); switch (err) { @@ -256,7 +247,7 @@ uint8_t TwoWire::endTransmission(bool stopBit, void (*onComplete)(void* user, in return 2; case SercomWireError::NACK_ON_DATA: return 3; - default: // OTHER + default: return 4; } } diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index fdc838b4b..81efc69a6 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -93,19 +93,6 @@ class TwoWire : public Stream inline void onService(void); -#ifdef _DEBUG_ - inline SercomTxn *getSlaveTxn(void) { return &slaveTxn; } - inline const SercomTxn *getSlaveTxn(void) const { return &slaveTxn; } - inline SercomTxn *getLoaderTxn(void) { return &loader; } - inline const SercomTxn *getLoaderTxn(void) const { return &loader; } - inline SercomTxn *getActiveTxn(void) { - return sercom ? sercom->getCurrentTxnWIRE() : nullptr; - } - inline const SercomTxn *getActiveTxn(void) const { - return sercom ? sercom->getCurrentTxnWIRE() : nullptr; - } -#endif // _DEBUG_ - private: SERCOM *sercom; uint8_t _uc_pinSDA; @@ -175,11 +162,14 @@ inline void TwoWire::onService(void) uint16_t status = (uint16_t)sercom->getSTATUS(); bool isMaster = sercom->isMasterWIRE(); - if ((!isMaster && !sercom->isSlaveWIRE()) || flags == 0) { + if (!isMaster && !sercom->isSlaveWIRE()) { sercom->clearINTFLAG(); return; } + if (flags == 0) + return; + if (status & SERCOM_I2CM_STATUS_RXNACK) { sercom->prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); SercomWireError err = awaitingAddressAck ? SercomWireError::NACK_ON_ADDRESS @@ -223,7 +213,8 @@ inline void TwoWire::onService(void) if (sercom->getTxnIndexWIRE() < sercom->getTxnLengthWIRE()) { bool more = isRead ? sercom->readDataWIRE() : sercom->sendDataWIRE(); awaitingAddressAck = false; - if (!isRead || more) return; + if (!isRead || more) + return; } if ((txn->config & I2C_CFG_STOP) && !isRead) From ef295ff75cec4864126890fb74c09cb0eb8ae0fc Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sun, 21 Jun 2026 08:15:00 -0400 Subject: [PATCH 40/79] Normalize async branch whitespace --- cores/arduino/SERCOM.cpp | 4 +- cores/arduino/SERCOM_Txn.h | 2 +- cores/arduino/SERCOM_inline.h | 10 +- cores/arduino/Uart.cpp | 2 +- cores/arduino/Uart.h | 4 +- .../wirepinmux/SAMD21_Device_I2C_Config.csv | 322 +++++++++--------- extras/wirepinmux/SAMD21_Table_2-1.csv | 60 ++-- extras/wirepinmux/SAMD21_Table_2-2.csv | 20 +- extras/wirepinmux/SAMD21_Table_7-1.csv | 104 +++--- extras/wirepinmux/SAMD21_Table_7-2.csv | 76 ++--- extras/wirepinmux/SAMD21_Table_7-5.csv | 6 +- .../wirepinmux/SAMD51_Device_I2C_Config.csv | 52 +-- extras/wirepinmux/SAMD51_Table_1-1.csv | 26 +- extras/wirepinmux/SAMD51_Table_1-2.csv | 40 +-- extras/wirepinmux/SAMD51_Table_6-1.csv | 200 +++++------ extras/wirepinmux/SAMD51_Table_6-8.csv | 18 +- libraries/SPI/SPI.cpp | 4 +- libraries/SPI/SPI.h | 4 +- libraries/Wire/Wire.cpp | 4 +- .../slave_receiver/slave_receiver.ino | 2 +- variants/mkrgsm1400/variant.cpp | 6 +- variants/mkrnb1500/variant.cpp | 10 +- variants/mkrwifi1010/variant.cpp | 8 +- 23 files changed, 492 insertions(+), 492 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index b1fcebda6..724b2a4b2 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -67,7 +67,7 @@ void SERCOM::resetSERCOM() // DBGCTRL is not affected by SWRST, so explicitly clear it here to ensure debug behavior is // consistent after reset - sercom->USART.DBGCTRL.bit.DBGSTOP = 0; + sercom->USART.DBGCTRL.bit.DBGSTOP = 0; } /* ========================= @@ -661,7 +661,7 @@ void SERCOM::initWIRE(void) uint8_t idx = getSercomIndex(); initClockNVIC(); registerService(idx, static_cast(&SERCOM::stopTransmissionWIRE)); - + #ifdef USE_ZERODMA dmaSetCallbacks(SERCOM::dmaTxCallbackWIRE, SERCOM::dmaRxCallbackWIRE); if (idx >= 0) diff --git a/cores/arduino/SERCOM_Txn.h b/cores/arduino/SERCOM_Txn.h index 29c08c662..d3334e0e4 100644 --- a/cores/arduino/SERCOM_Txn.h +++ b/cores/arduino/SERCOM_Txn.h @@ -19,7 +19,7 @@ // 6. Phase2 DMA completes -> onComplete() callback runs // 7. Callback clears chainNext (or callback already cleared it) // 8. stopTransmissionSPI() dequeues txn, starts next queued transaction -// +// // This design: // - Avoids explicit claim/release calls (no NVIC priority dance) // - Keeps atomicity logic in one place (the callback) diff --git a/cores/arduino/SERCOM_inline.h b/cores/arduino/SERCOM_inline.h index 9f6502ff9..0432b420f 100644 --- a/cores/arduino/SERCOM_inline.h +++ b/cores/arduino/SERCOM_inline.h @@ -36,7 +36,7 @@ inline bool SERCOM::sendDataWIRE( void ) { SercomTxn* txn = _wire.currentTxn; if (txn == nullptr || txn->txPtr == nullptr) return false; - + #ifdef USE_ZERODMA if (isDmaWIRE()) { DmaStatus value = DmaStatus::StartFailed; @@ -48,7 +48,7 @@ inline bool SERCOM::sendDataWIRE( void ) // Wait for DATA to sync out of the ISR and clear MB sercom->I2CM.DATA.reg = txn->txPtr[_wire.txnIndex++]; - + // Return false when the last byte has been consumed so the caller can // issue STOP / complete the transaction without waiting for another SB. return (_wire.txnIndex < _wire.txnLength); @@ -89,7 +89,7 @@ inline bool SERCOM::readDataWIRE( void ) // Slave mode: set ACKACT BEFORE reading DATA (SMEN auto-receives based on ACKACT) if (_wire.txnIndex >= _wire.txnLength) prepareNackBitWIRE(); // NACK if buffer full - else + else prepareAckBitWIRE(); // ACK if room available } @@ -211,12 +211,12 @@ inline void SERCOM::dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma) { SERCOM* inst = findDmaOwner(dma, false); if (!inst) return; - + // When using ADDR.LENEN mode, the hardware automatically generates NACK+STOP // after ADDR.LEN bytes are transferred (datasheet §28.6.4.1.2). // Do NOT issue a manual STOP command - it conflicts with the automatic sequence. // The STOP is generated by hardware, not by software CMD write. - + inst->_wire.txnIndex = inst->_wire.txnLength; inst->_dmaRxActive = false; inst->deferStopWIRE(SercomWireError::SUCCESS); diff --git a/cores/arduino/Uart.cpp b/cores/arduino/Uart.cpp index 8e8e03796..2af5a2be3 100644 --- a/cores/arduino/Uart.cpp +++ b/cores/arduino/Uart.cpp @@ -51,7 +51,7 @@ void Uart::begin(unsigned long baudrate, uint16_t config) pinPeripheral(uc_pinRX, g_APinDescription[uc_pinRX].ulPinType); pinPeripheral(uc_pinTX, g_APinDescription[uc_pinTX].ulPinType); - if (uc_padTX == UART_TX_RTS_CTS_PAD_0_2_3) { + if (uc_padTX == UART_TX_RTS_CTS_PAD_0_2_3) { if (uc_pinCTS != NO_CTS_PIN) { pinPeripheral(uc_pinCTS, g_APinDescription[uc_pinCTS].ulPinType); } diff --git a/cores/arduino/Uart.h b/cores/arduino/Uart.h index 5e6ff9df2..cebf72dec 100644 --- a/cores/arduino/Uart.h +++ b/cores/arduino/Uart.h @@ -72,12 +72,12 @@ class Uart : public HardwareSerial volatile bool txnDone = false; volatile int txnStatus = 0; - + // Transaction pool for async operations (matches SERCOM queue depth) static constexpr size_t TXN_POOL_SIZE = 8; SercomTxn txnPool[TXN_POOL_SIZE]; uint8_t txnPoolHead = 0; - + SercomTxn* allocateTxn(); static void onTxnComplete(void* user, int status); bool rxExternalActive = false; diff --git a/extras/wirepinmux/SAMD21_Device_I2C_Config.csv b/extras/wirepinmux/SAMD21_Device_I2C_Config.csv index 93463cb34..91ec36e5d 100644 --- a/extras/wirepinmux/SAMD21_Device_I2C_Config.csv +++ b/extras/wirepinmux/SAMD21_Device_I2C_Config.csv @@ -1,161 +1,161 @@ -Device,Pin Count,Variant,I2C Pins,SERCOM Configs -SAMD21E14,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E14A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E14B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E14C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E14D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E14L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E15,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E15A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E15B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E15C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E15D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E15L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E16,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E16A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E16B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E16C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E16D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E16L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E17,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E17A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E17B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E17C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E17D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E17L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E18,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E18A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E18B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E18C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E18D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21E18L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G14,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G14A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G14B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G14C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G14D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G14L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G15,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G15A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G15B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G15C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G15D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G15L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G16,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G16A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G16B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G16C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G16D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G16L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G17,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G17A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G17B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G17C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G17D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G17L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G18,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G18A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G18B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G18C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G18D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21G18L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMD21J14,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J14A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J14B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J14C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J14D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J15,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J15A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J15B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J15C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J15D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J16,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J16A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J16B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J16C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J16D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J17,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J17A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J17B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J17C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J17D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J18,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J18A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J18B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J18C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMD21J18D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1E14,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E14A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E14B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E14C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E14D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E15,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E15A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E15B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E15C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E15D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E16,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E16A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E16B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E16C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E16D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E17,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E17A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E17B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E17C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E17D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E18,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E18A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E18B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E18C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1E18D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G14,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G14A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G14B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G14C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G14D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G15,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G15A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G15B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G15C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G15D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G16,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G16A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G16B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G16C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G16D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G17,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G17A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G17B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G17C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G17D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G18,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G18A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G18B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G18C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1G18D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" -SAMDA1J14,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J14A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J14B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J14C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J14D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J15,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J15A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J15B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J15C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J15D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J16,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J16A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J16B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J16C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J16D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J17,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J17A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J17B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J17C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J17D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J18,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J18A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J18B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J18C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" -SAMDA1J18D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +Device,Pin Count,Variant,I2C Pins,SERCOM Configs +SAMD21E14,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E14A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E14B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E14C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E14D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E14L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E15,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E15A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E15B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E15C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E15D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E15L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E16,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E16A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E16B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E16C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E16D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E16L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E17,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E17A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E17B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E17C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E17D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E17L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E18,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E18A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E18B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E18C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E18D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21E18L,32,L,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G14,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G14A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G14B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G14C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G14D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G14L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G15,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G15A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G15B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G15C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G15D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G15L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G16,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G16A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G16B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G16C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G16D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G16L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G17,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G17A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G17B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G17C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G17D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G17L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G18,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G18A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G18B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G18C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G18D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21G18L,48,L,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMD21J14,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J14A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J14B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J14C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J14D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J15,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J15A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J15B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J15C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J15D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J16,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J16A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J16B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J16C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J16D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J17,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J17A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J17B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J17C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J17D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J18,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J18A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J18B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J18C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMD21J18D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1E14,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E14A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E14B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E14C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E14D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E15,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E15A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E15B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E15C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E15D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E16,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E16A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E16B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E16C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E16D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E17,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E17A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E17B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E17C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E17D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E18,32,base,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E18A,32,A,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E18B,32,B,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E18C,32,C,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1E18D,32,D,"PA08,PA09,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G14,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G14A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G14B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G14C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G14D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G15,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G15A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G15B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G15C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G15D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G16,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G16A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G16B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G16C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G16D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G17,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G17A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G17B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G17C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G17D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G18,48,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G18A,48,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G18B,48,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G18C,48,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1G18D,48,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]" +SAMDA1J14,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J14A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J14B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J14C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J14D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J15,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J15A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J15B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J15C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J15D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J16,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J16A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J16B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J16C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J16D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J17,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J17A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J17B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J17C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J17D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J18,64,base,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J18A,64,A,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J18B,64,B,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J18C,64,C,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" +SAMDA1J18D,64,D,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PB12,PB13,PB16,PB17,PB30,PB31","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[0]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[1]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[0]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[1]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[0]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[1]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[0]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[1]]|PB12:[SERCOM4/PAD[0]]|PB13:[SERCOM4/PAD[1]]|PB16:[SERCOM5/PAD[0]]|PB17:[SERCOM5/PAD[1]]|PB30:[SERCOM5/PAD[0]]|PB31:[SERCOM5/PAD[1]]" diff --git a/extras/wirepinmux/SAMD21_Table_2-1.csv b/extras/wirepinmux/SAMD21_Table_2-1.csv index 6b5f41f29..050df920d 100644 --- a/extras/wirepinmux/SAMD21_Table_2-1.csv +++ b/extras/wirepinmux/SAMD21_Table_2-1.csv @@ -1,31 +1,31 @@ -Table 2-1. SAM D21 E/G/J and SAM D21 EL/GL Product Family Features,,,,,,,,,,,,,,,,,,,,,, -,,,,,Oscillators,,Peripherals,,,,,,,,,,,,Analog,,, -Device,Program Memory (KB),Data Memory (KB),Pins,Packages,Internal,External,USB,SERCOM,TC-Waveform /PWM Output/Capture Input Channels per TCx Instance,TCC,Waveform /PWM Output Channels per TCCx Instance,I2S,DMA Channels,RTC,WDT,Event System (Channels),External Interrupt Lines,I/O Pins,ADC Channels,Analog Comparator,DAC,"PTC (Mutual/Self-capacitance Channels)""" -ATSAMD21E15A,32,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" -ATSAMD21E16A,64,8,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" -ATSAMD21E17A,128,16,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" -ATSAMD21E18A,256,32,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" -ATSAMD21E15B,32,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" -ATSAMD21E16B,64,8,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" -ATSAMD21E15C,32,4,35,WLCSP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" -ATSAMD21E16C,64,8,35,WLCSP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" -ATSAMD21G15A,32,4,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" -ATSAMD21G16A,64,8,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" -ATSAMD21G17A,128,16,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" -ATSAMD21G18A,256,32,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" -ATSAMD21G15B,32,4,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" -ATSAMD21G16B,64,8,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" -ATSAMD21J15A,32,4,64,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" -ATSAMD21J16A,64,8,64,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" -ATSAMD21J17A,128,16,64,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" -ATSAMD21J18A,256,32,64,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" -ATSAMD21J15B,32,4,64,TQFP/QFN/UFBGA,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" -ATSAMD21J16B,64,8,64,TQFP/QFN/UFBGA,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" -ATSAMD21E15L,32,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC,N,4,"3-2",3,"6/4/2",N,12,Y,Y,12,16,26,14,4,Y,N -ATSAMD21E16L,64,8,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC,N,4,"3-2",3,"6/4/2",N,12,Y,Y,12,16,26,14,4,Y,N -ATSAMD21G16L,64,8,48,QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC,Y,6,"5-2",3,"8/4/2",N,12,Y,Y,12,16,38,18,4,Y,N -ATSAMD21E17D,128,16,32,TQFP/QFN/WLCSP,OSC32K/OSCULP32K/OSC8M/DFLL48M,XOSC32K/XOSC,Y,4,"3-2",4,"6/4/2/6",Y,12,Y,Y,12,16,26,10,2,Y,"30/6" -ATSAMD21G17D,128,16,48,QFN/TQFP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",4,"8/4/2/8",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" -ATSAMD21J17D,128,16,64,QFN/TQFP/UFBGA,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",4,"8/4/2/8",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" -ATSAMD21E17L,128,16,32,QFN/TQFP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC,N,4,"3-2",4,"6/4/2/6",N,12,Y,Y,12,16,26,14,4,Y,N +Table 2-1. SAM D21 E/G/J and SAM D21 EL/GL Product Family Features,,,,,,,,,,,,,,,,,,,,,, +,,,,,Oscillators,,Peripherals,,,,,,,,,,,,Analog,,, +Device,Program Memory (KB),Data Memory (KB),Pins,Packages,Internal,External,USB,SERCOM,TC-Waveform /PWM Output/Capture Input Channels per TCx Instance,TCC,Waveform /PWM Output Channels per TCCx Instance,I2S,DMA Channels,RTC,WDT,Event System (Channels),External Interrupt Lines,I/O Pins,ADC Channels,Analog Comparator,DAC,"PTC (Mutual/Self-capacitance Channels)""" +ATSAMD21E15A,32,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21E16A,64,8,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21E17A,128,16,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21E18A,256,32,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21E15B,32,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21E16B,64,8,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21E15C,32,4,35,WLCSP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21E16C,64,8,35,WLCSP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,4,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,26,10,2,Y,"60/6" +ATSAMD21G15A,32,4,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" +ATSAMD21G16A,64,8,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" +ATSAMD21G17A,128,16,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" +ATSAMD21G18A,256,32,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" +ATSAMD21G15B,32,4,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" +ATSAMD21G16B,64,8,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",3,"8/4/2",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" +ATSAMD21J15A,32,4,64,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" +ATSAMD21J16A,64,8,64,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" +ATSAMD21J17A,128,16,64,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" +ATSAMD21J18A,256,32,64,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" +ATSAMD21J15B,32,4,64,TQFP/QFN/UFBGA,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" +ATSAMD21J16B,64,8,64,TQFP/QFN/UFBGA,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",3,"8/4/2",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" +ATSAMD21E15L,32,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC,N,4,"3-2",3,"6/4/2",N,12,Y,Y,12,16,26,14,4,Y,N +ATSAMD21E16L,64,8,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC,N,4,"3-2",3,"6/4/2",N,12,Y,Y,12,16,26,14,4,Y,N +ATSAMD21G16L,64,8,48,QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC,Y,6,"5-2",3,"8/4/2",N,12,Y,Y,12,16,38,18,4,Y,N +ATSAMD21E17D,128,16,32,TQFP/QFN/WLCSP,OSC32K/OSCULP32K/OSC8M/DFLL48M,XOSC32K/XOSC,Y,4,"3-2",4,"6/4/2/6",Y,12,Y,Y,12,16,26,10,2,Y,"30/6" +ATSAMD21G17D,128,16,48,QFN/TQFP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"3-2",4,"8/4/2/8",Y,12,Y,Y,12,16,38,14,2,Y,"120/10" +ATSAMD21J17D,128,16,64,QFN/TQFP/UFBGA,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,Y,6,"5-2",4,"8/4/2/8",Y,12,Y,Y,12,16,52,20,2,Y,"256/16" +ATSAMD21E17L,128,16,32,QFN/TQFP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC,N,4,"3-2",4,"6/4/2/6",N,12,Y,Y,12,16,26,14,4,Y,N ATSAMD21G17L,128,16,48,QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC,N,6,"5-2",4,"8/4/2/8",N,12,Y,Y,12,16,38,18,4,Y,N \ No newline at end of file diff --git a/extras/wirepinmux/SAMD21_Table_2-2.csv b/extras/wirepinmux/SAMD21_Table_2-2.csv index 999c3f468..06081a952 100644 --- a/extras/wirepinmux/SAMD21_Table_2-2.csv +++ b/extras/wirepinmux/SAMD21_Table_2-2.csv @@ -1,11 +1,11 @@ -Table 2-2. SAM DA1,E/G/J Product Family Features,,,,,,,,,,,,,,,,,,,,, -Device,Program Memory (KB),Data Memory (KB),Pins,Packages,Internal,External,USB,SERCOM,TC-Waveform/PWM Output/Capture Input Channels per TCx Instance,TCC,Waveform/PWM Output Channels per TCCx Instance,I2S,DMA Channels,RTC,WDT,EventSystem (Channels),External Interrupt Lines,I/O Pins,ADC Channels,2,1,PTC (Mutual/Self-capacitance Channels) -ATSAMDA1E14B,16,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,4,"3-2",3,"6/4/2",1,12,Y,Y,12,16,26,10,2,1,"60/6" -ATSAMDA1E15B,32,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,4,"3-2",3,"6/4/2",1,12,Y,Y,12,16,26,10,2,1,"60/6" -ATSAMDA1E16B,64,8,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,4,"3-2",3,"6/4/2",1,12,Y,Y,12,16,26,10,2,1,"60/6" -ATSAMDA1G14B,16,4,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"3-2",3,"8/4/2",1,12,Y,Y,12,16,38,14,2,1,"120/10" -ATSAMDA1G15B,32,4,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"3-2",3,"8/4/2",1,12,Y,Y,12,16,38,14,2,1,"120/10" -ATSAMDA1G16B,64,8,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"3-2",3,"8/4/2",1,12,Y,Y,12,16,38,14,2,1,"120/10" -ATSAMDA1J14B,16,4,64,TQFP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"5-2",3,"8/4/2",1,12,Y,Y,12,16,52,20,2,1,"256/16" -ATSAMDA1J15B,32,4,64,TQFP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"5-2",3,"8/4/2",1,12,Y,Y,12,16,52,20,2,1,"256/16" +Table 2-2. SAM DA1,E/G/J Product Family Features,,,,,,,,,,,,,,,,,,,,, +Device,Program Memory (KB),Data Memory (KB),Pins,Packages,Internal,External,USB,SERCOM,TC-Waveform/PWM Output/Capture Input Channels per TCx Instance,TCC,Waveform/PWM Output Channels per TCCx Instance,I2S,DMA Channels,RTC,WDT,EventSystem (Channels),External Interrupt Lines,I/O Pins,ADC Channels,2,1,PTC (Mutual/Self-capacitance Channels) +ATSAMDA1E14B,16,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,4,"3-2",3,"6/4/2",1,12,Y,Y,12,16,26,10,2,1,"60/6" +ATSAMDA1E15B,32,4,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,4,"3-2",3,"6/4/2",1,12,Y,Y,12,16,26,10,2,1,"60/6" +ATSAMDA1E16B,64,8,32,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,4,"3-2",3,"6/4/2",1,12,Y,Y,12,16,26,10,2,1,"60/6" +ATSAMDA1G14B,16,4,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"3-2",3,"8/4/2",1,12,Y,Y,12,16,38,14,2,1,"120/10" +ATSAMDA1G15B,32,4,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"3-2",3,"8/4/2",1,12,Y,Y,12,16,38,14,2,1,"120/10" +ATSAMDA1G16B,64,8,48,TQFP/QFN,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"3-2",3,"8/4/2",1,12,Y,Y,12,16,38,14,2,1,"120/10" +ATSAMDA1J14B,16,4,64,TQFP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"5-2",3,"8/4/2",1,12,Y,Y,12,16,52,20,2,1,"256/16" +ATSAMDA1J15B,32,4,64,TQFP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"5-2",3,"8/4/2",1,12,Y,Y,12,16,52,20,2,1,"256/16" ATSAMDA1J16B,64,8,64,TQFP,OSC32K/OSCULP32K/OSC8M/DFLL48M/FDPLL96M,XOSC32K/XOSC,1,6,"5-2",3,"8/4/2",1,12,Y,Y,12,16,52,20,2,1,"256/16" \ No newline at end of file diff --git a/extras/wirepinmux/SAMD21_Table_7-1.csv b/extras/wirepinmux/SAMD21_Table_7-1.csv index 6c4c1b681..c50b4113f 100644 --- a/extras/wirepinmux/SAMD21_Table_7-1.csv +++ b/extras/wirepinmux/SAMD21_Table_7-1.csv @@ -1,53 +1,53 @@ -SAMD2xE,SAMD2xG,SAMD2xJ,I/O Pin,Supply,A,B1,B2,B3,B4,B5,C,D,E,F,G,H -1,1,1,PA00,VDDANA,EXTINT[0],,,,,,,SERCOM1/PAD[0],TCC2/WO[0],,, -2,2,2,PA01,VDDANA,EXTINT[1],,,,,,,SERCOM1/PAD[1],TCC2/WO[1],,, -3,3,3,PA02,VDDANA,EXTINT[2],,AIN[0],,Y[0],VOUT,,,,TCC3/WO[0],, -4,4,4,PA03,VDDANA,EXTINT[3],ADC/VREFA DAC/VREFA,AIN[1],,Y[1],,,,,TCC3/WO[1],, -,,5,PB04,VDDANA,EXTINT[4],,AIN[12],,Y[10],,,,,,, -,,6,PB05,VDDANA,EXTINT[5],,AIN[13],,Y[11],,,,,,, -,,9,PB06,VDDANA,EXTINT[6],,AIN[14],,Y[12],,,,,,, -,,10,PB07,VDDANA,EXTINT[7],,AIN[15],,Y[13],,,,,,, -,7,11,PB08,VDDANA,EXTINT[8],,AIN[2],,Y[14],,,SERCOM4/PAD[0],TC4/WO[0],TCC3/WO[6],, -,8,12,PB09,VDDANA,EXTINT[9],,AIN[3],,Y[15],,,SERCOM4/PAD[1],TC4/WO[1],TCC3/WO[7],, -5,9,13,PA04,VDDANA,EXTINT[4],ADC/VREFB,AIN[4],AIN[0],Y[2],,,SERCOM0/PAD[0],TCC0/WO[0],TCC3/WO[2],, -6,10,14,PA05,VDDANA,EXTINT[5],,AIN[5],AIN[1],Y[3],,,SERCOM0/PAD[1],TCC0/WO[1],TCC3/WO[3],, -7,11,15,PA06,VDDANA,EXTINT[6],,AIN[6],AIN[2],Y[4],,,SERCOM0/PAD[2],TCC1/WO[0],TCC3/WO[4],, -8,12,16,PA07,VDDANA,EXTINT[7],,AIN[7],AIN[3],Y[5],,,SERCOM0/PAD[3],TCC1/WO[1],TCC3/WO[5],I2S/SD[0], -11,13,17,PA08,VDDIO,NMI,,AIN[16],,X[0],,SERCOM0/PAD[0],SERCOM2/PAD[0],TCC0/WO[0],TCC1/WO[2],I2S/SD[1], -12,14,18,PA09,VDDIO,EXTINT[9],,AIN[17],,X[1],,SERCOM0/PAD[1],SERCOM2/PAD[1],TCC0/WO[1],TCC1/WO[3],I2S/MCK[0], -13,15,19,PA10,VDDIO,EXTINT[10],,AIN[18],,X[2],,SERCOM0/PAD[2],SERCOM2/PAD[2],TCC1/WO[0],TCC0/WO[2],I2S/SCK[0],GCLK_IO[4] -14,16,20,PA11,VDDIO,EXTINT[11],,AIN[19],,X[3],,SERCOM0/PAD[3],SERCOM2/PAD[3],TCC1/WO[1],TCC0/WO[3],I2S/FS[0],GCLK_IO[5] -,19,23,PB10,VDDIO,EXTINT[10],,,,,,,SERCOM4/PAD[2],TC5/WO[0],TCC0/WO[4],I2S/MCK[1],GCLK_IO[4] -,20,24,PB11,VDDIO,EXTINT[11],,,,,,,SERCOM4/PAD[3],TC5/WO[1],TCC0/WO[5],I2S/SCK[1],GCLK_IO[5] -,,25,PB12,VDDIO,EXTINT[12],,,,X[12],,SERCOM4/PAD[0],,TC4/WO[0],TCC0/WO[6],I2S/FS[1],GCLK_IO[6] -,,26,PB13,VDDIO,EXTINT[13],,,,X[13],,SERCOM4/PAD[1],,TC4/WO[1],TCC0/WO[7],,GCLK_IO[7] -,,27,PB14,VDDIO,EXTINT[14],,,,X[14],,SERCOM4/PAD[2],,TC5/WO[0],,,GCLK_IO[0] -,,28,PB15,VDDIO,EXTINT[15],,,,X[15],,SERCOM4/PAD[3],,TC5/WO[1],,,GCLK_IO[1] -,21,29,PA12,VDDIO,EXTINT[12],,,,,,SERCOM2/PAD[0],SERCOM4/PAD[0],TCC2/WO[0],TCC0/WO[6],,AC/CMP[0] -,22,30,PA13,VDDIO,EXTINT[13],,,,,,SERCOM2/PAD[1],SERCOM4/PAD[1],TCC2/WO[1],TCC0/WO[7],,AC/CMP[1] -15,23,31,PA14,VDDIO,EXTINT[14],,,,,,SERCOM2/PAD[2],SERCOM4/PAD[2],TC3/WO[0],TCC0/WO[4],,GCLK_IO[0] -16,24,32,PA15,VDDIO,EXTINT[15],,,,,,SERCOM2/PAD[3],SERCOM4/PAD[3],TC3/WO[1],TCC0/WO[5],,GCLK_IO[1] -17,25,35,PA16,VDDIO,EXTINT[0],,,,X[4],,SERCOM1/PAD[0],SERCOM3/PAD[0],TCC2/WO[0],TCC0/WO[6],,GCLK_IO[2] -18,26,36,PA17,VDDIO,EXTINT[1],,,,X[5],,SERCOM1/PAD[1],SERCOM3/PAD[1],TCC2/WO[1],TCC0/WO[7],,GCLK_IO[3] -19,27,37,PA18,VDDIO,EXTINT[2],,,,X[6],,SERCOM1/PAD[2],SERCOM3/PAD[2],TC3/WO[0],TCC0/WO[2],,AC/CMP[0] -20,28,38,PA19,VDDIO,EXTINT[3],,,,X[7],,SERCOM1/PAD[3],SERCOM3/PAD[3],TC3/WO[1],TCC0/WO[3],I2S/SD[0],AC/CMP[1] -,,39,PB16,VDDIO,EXTINT[0],,,,,,SERCOM5/PAD[0],,TC6/WO[0],TCC0/WO[4],I2S/SD[1],GCLK_IO[2] -,,40,PB17,VDDIO,EXTINT[1],,,,,,SERCOM5/PAD[1],,TC6/WO[1],TCC0/WO[5],I2S/MCK[0],GCLK_IO[3] -,29,41,PA20,VDDIO,EXTINT[4],,,,X[8],,SERCOM5/PAD[2],SERCOM3/PAD[2],TC7/WO[0],TCC0/WO[6],I2S/SCK[0],GCLK_IO[4] -,30,42,PA21,VDDIO,EXTINT[5],,,,X[9],,SERCOM5/PAD[3],SERCOM3/PAD[3],TC7/WO[1],TCC0/WO[7],I2S/FS[0],GCLK_IO[5] -21,31,43,PA22,VDDIO,EXTINT[6],,,,X[10],,SERCOM3/PAD[0],SERCOM5/PAD[0],TC4/WO[0],TCC0/WO[4],,GCLK_IO[6] -22,32,44,PA23,VDDIO,EXTINT[7],,,,X[11],,SERCOM3/PAD[1],SERCOM5/PAD[1],TC4/WO[1],TCC0/WO[5],USB/SOF 1kHz,GCLK_IO[7] -23,33,45,PA24,VDDIO,EXTINT[12],,,,,,SERCOM3/PAD[2],SERCOM5/PAD[2],TC5/WO[0],TCC1/WO[2],USB/DM, -24,34,46,PA25,VDDIO,EXTINT[13],,,,,,SERCOM3/PAD[3],SERCOM5/PAD[3],TC5/WO[1],TCC1/WO[3],USB/DP, -,37,49,PB22,VDDIO,EXTINT[6],,,,,,,SERCOM5/PAD[2],TC7/WO[0],TCC3/WO[0],,GCLK_IO[0] -,38,50,PB23,VDDIO,EXTINT[7],,,,,,,SERCOM5/PAD[3],TC7/WO[1],TCC3/WO[1],,GCLK_IO[1] -25,39,51,PA27,VDDIO,EXTINT[15],,,,,,,,,TCC3/WO[6],,GCLK_IO[0] -27,41,53,PA28,VDDIO,EXTINT[8],,,,,,,,,TCC3/WO[7],,GCLK_IO[0] -31,45,57,PA30,VDDIO,EXTINT[10],,,,,,,SERCOM1/PAD[2],TCC1/WO[0],TCC3/WO[4],SWCLK,GCLK_IO[0] -32,46,58,PA31,VDDIO,EXTINT[11],,,,,,,SERCOM1/PAD[3],TCC1/WO[1],TCC3/WO[5],SWDIO, -,,59,PB30,VDDIO,EXTINT[14],,,,,,,SERCOM5/PAD[0],TCC0/WO[0],TCC1/WO[2],, -,,60,PB31,VDDIO,EXTINT[15],,,,,,,SERCOM5/PAD[1],TCC0/WO[1],TCC1/WO[3],, -,,61,PB00,VDDANA,EXTINT[0],,AIN[8],,Y[6],,,SERCOM5/PAD[2],TC7/WO[0],,, -,,62,PB01,VDDANA,EXTINT[1],,AIN[9],,Y[7],,,SERCOM5/PAD[3],TC7/WO[1],,, -,47,63,PB02,VDDANA,EXTINT[2],,AIN[10],,Y[8],,,SERCOM5/PAD[0],TC6/WO[0],TCC3/WO[2],, +SAMD2xE,SAMD2xG,SAMD2xJ,I/O Pin,Supply,A,B1,B2,B3,B4,B5,C,D,E,F,G,H +1,1,1,PA00,VDDANA,EXTINT[0],,,,,,,SERCOM1/PAD[0],TCC2/WO[0],,, +2,2,2,PA01,VDDANA,EXTINT[1],,,,,,,SERCOM1/PAD[1],TCC2/WO[1],,, +3,3,3,PA02,VDDANA,EXTINT[2],,AIN[0],,Y[0],VOUT,,,,TCC3/WO[0],, +4,4,4,PA03,VDDANA,EXTINT[3],ADC/VREFA DAC/VREFA,AIN[1],,Y[1],,,,,TCC3/WO[1],, +,,5,PB04,VDDANA,EXTINT[4],,AIN[12],,Y[10],,,,,,, +,,6,PB05,VDDANA,EXTINT[5],,AIN[13],,Y[11],,,,,,, +,,9,PB06,VDDANA,EXTINT[6],,AIN[14],,Y[12],,,,,,, +,,10,PB07,VDDANA,EXTINT[7],,AIN[15],,Y[13],,,,,,, +,7,11,PB08,VDDANA,EXTINT[8],,AIN[2],,Y[14],,,SERCOM4/PAD[0],TC4/WO[0],TCC3/WO[6],, +,8,12,PB09,VDDANA,EXTINT[9],,AIN[3],,Y[15],,,SERCOM4/PAD[1],TC4/WO[1],TCC3/WO[7],, +5,9,13,PA04,VDDANA,EXTINT[4],ADC/VREFB,AIN[4],AIN[0],Y[2],,,SERCOM0/PAD[0],TCC0/WO[0],TCC3/WO[2],, +6,10,14,PA05,VDDANA,EXTINT[5],,AIN[5],AIN[1],Y[3],,,SERCOM0/PAD[1],TCC0/WO[1],TCC3/WO[3],, +7,11,15,PA06,VDDANA,EXTINT[6],,AIN[6],AIN[2],Y[4],,,SERCOM0/PAD[2],TCC1/WO[0],TCC3/WO[4],, +8,12,16,PA07,VDDANA,EXTINT[7],,AIN[7],AIN[3],Y[5],,,SERCOM0/PAD[3],TCC1/WO[1],TCC3/WO[5],I2S/SD[0], +11,13,17,PA08,VDDIO,NMI,,AIN[16],,X[0],,SERCOM0/PAD[0],SERCOM2/PAD[0],TCC0/WO[0],TCC1/WO[2],I2S/SD[1], +12,14,18,PA09,VDDIO,EXTINT[9],,AIN[17],,X[1],,SERCOM0/PAD[1],SERCOM2/PAD[1],TCC0/WO[1],TCC1/WO[3],I2S/MCK[0], +13,15,19,PA10,VDDIO,EXTINT[10],,AIN[18],,X[2],,SERCOM0/PAD[2],SERCOM2/PAD[2],TCC1/WO[0],TCC0/WO[2],I2S/SCK[0],GCLK_IO[4] +14,16,20,PA11,VDDIO,EXTINT[11],,AIN[19],,X[3],,SERCOM0/PAD[3],SERCOM2/PAD[3],TCC1/WO[1],TCC0/WO[3],I2S/FS[0],GCLK_IO[5] +,19,23,PB10,VDDIO,EXTINT[10],,,,,,,SERCOM4/PAD[2],TC5/WO[0],TCC0/WO[4],I2S/MCK[1],GCLK_IO[4] +,20,24,PB11,VDDIO,EXTINT[11],,,,,,,SERCOM4/PAD[3],TC5/WO[1],TCC0/WO[5],I2S/SCK[1],GCLK_IO[5] +,,25,PB12,VDDIO,EXTINT[12],,,,X[12],,SERCOM4/PAD[0],,TC4/WO[0],TCC0/WO[6],I2S/FS[1],GCLK_IO[6] +,,26,PB13,VDDIO,EXTINT[13],,,,X[13],,SERCOM4/PAD[1],,TC4/WO[1],TCC0/WO[7],,GCLK_IO[7] +,,27,PB14,VDDIO,EXTINT[14],,,,X[14],,SERCOM4/PAD[2],,TC5/WO[0],,,GCLK_IO[0] +,,28,PB15,VDDIO,EXTINT[15],,,,X[15],,SERCOM4/PAD[3],,TC5/WO[1],,,GCLK_IO[1] +,21,29,PA12,VDDIO,EXTINT[12],,,,,,SERCOM2/PAD[0],SERCOM4/PAD[0],TCC2/WO[0],TCC0/WO[6],,AC/CMP[0] +,22,30,PA13,VDDIO,EXTINT[13],,,,,,SERCOM2/PAD[1],SERCOM4/PAD[1],TCC2/WO[1],TCC0/WO[7],,AC/CMP[1] +15,23,31,PA14,VDDIO,EXTINT[14],,,,,,SERCOM2/PAD[2],SERCOM4/PAD[2],TC3/WO[0],TCC0/WO[4],,GCLK_IO[0] +16,24,32,PA15,VDDIO,EXTINT[15],,,,,,SERCOM2/PAD[3],SERCOM4/PAD[3],TC3/WO[1],TCC0/WO[5],,GCLK_IO[1] +17,25,35,PA16,VDDIO,EXTINT[0],,,,X[4],,SERCOM1/PAD[0],SERCOM3/PAD[0],TCC2/WO[0],TCC0/WO[6],,GCLK_IO[2] +18,26,36,PA17,VDDIO,EXTINT[1],,,,X[5],,SERCOM1/PAD[1],SERCOM3/PAD[1],TCC2/WO[1],TCC0/WO[7],,GCLK_IO[3] +19,27,37,PA18,VDDIO,EXTINT[2],,,,X[6],,SERCOM1/PAD[2],SERCOM3/PAD[2],TC3/WO[0],TCC0/WO[2],,AC/CMP[0] +20,28,38,PA19,VDDIO,EXTINT[3],,,,X[7],,SERCOM1/PAD[3],SERCOM3/PAD[3],TC3/WO[1],TCC0/WO[3],I2S/SD[0],AC/CMP[1] +,,39,PB16,VDDIO,EXTINT[0],,,,,,SERCOM5/PAD[0],,TC6/WO[0],TCC0/WO[4],I2S/SD[1],GCLK_IO[2] +,,40,PB17,VDDIO,EXTINT[1],,,,,,SERCOM5/PAD[1],,TC6/WO[1],TCC0/WO[5],I2S/MCK[0],GCLK_IO[3] +,29,41,PA20,VDDIO,EXTINT[4],,,,X[8],,SERCOM5/PAD[2],SERCOM3/PAD[2],TC7/WO[0],TCC0/WO[6],I2S/SCK[0],GCLK_IO[4] +,30,42,PA21,VDDIO,EXTINT[5],,,,X[9],,SERCOM5/PAD[3],SERCOM3/PAD[3],TC7/WO[1],TCC0/WO[7],I2S/FS[0],GCLK_IO[5] +21,31,43,PA22,VDDIO,EXTINT[6],,,,X[10],,SERCOM3/PAD[0],SERCOM5/PAD[0],TC4/WO[0],TCC0/WO[4],,GCLK_IO[6] +22,32,44,PA23,VDDIO,EXTINT[7],,,,X[11],,SERCOM3/PAD[1],SERCOM5/PAD[1],TC4/WO[1],TCC0/WO[5],USB/SOF 1kHz,GCLK_IO[7] +23,33,45,PA24,VDDIO,EXTINT[12],,,,,,SERCOM3/PAD[2],SERCOM5/PAD[2],TC5/WO[0],TCC1/WO[2],USB/DM, +24,34,46,PA25,VDDIO,EXTINT[13],,,,,,SERCOM3/PAD[3],SERCOM5/PAD[3],TC5/WO[1],TCC1/WO[3],USB/DP, +,37,49,PB22,VDDIO,EXTINT[6],,,,,,,SERCOM5/PAD[2],TC7/WO[0],TCC3/WO[0],,GCLK_IO[0] +,38,50,PB23,VDDIO,EXTINT[7],,,,,,,SERCOM5/PAD[3],TC7/WO[1],TCC3/WO[1],,GCLK_IO[1] +25,39,51,PA27,VDDIO,EXTINT[15],,,,,,,,,TCC3/WO[6],,GCLK_IO[0] +27,41,53,PA28,VDDIO,EXTINT[8],,,,,,,,,TCC3/WO[7],,GCLK_IO[0] +31,45,57,PA30,VDDIO,EXTINT[10],,,,,,,SERCOM1/PAD[2],TCC1/WO[0],TCC3/WO[4],SWCLK,GCLK_IO[0] +32,46,58,PA31,VDDIO,EXTINT[11],,,,,,,SERCOM1/PAD[3],TCC1/WO[1],TCC3/WO[5],SWDIO, +,,59,PB30,VDDIO,EXTINT[14],,,,,,,SERCOM5/PAD[0],TCC0/WO[0],TCC1/WO[2],, +,,60,PB31,VDDIO,EXTINT[15],,,,,,,SERCOM5/PAD[1],TCC0/WO[1],TCC1/WO[3],, +,,61,PB00,VDDANA,EXTINT[0],,AIN[8],,Y[6],,,SERCOM5/PAD[2],TC7/WO[0],,, +,,62,PB01,VDDANA,EXTINT[1],,AIN[9],,Y[7],,,SERCOM5/PAD[3],TC7/WO[1],,, +,47,63,PB02,VDDANA,EXTINT[2],,AIN[10],,Y[8],,,SERCOM5/PAD[0],TC6/WO[0],TCC3/WO[2],, ,48,64,PB03,VDDANA,EXTINT[3],,AIN[11],,Y[9],,,SERCOM5/PAD[1],TC6/WO[1],TCC3/WO[3],, \ No newline at end of file diff --git a/extras/wirepinmux/SAMD21_Table_7-2.csv b/extras/wirepinmux/SAMD21_Table_7-2.csv index e6029598a..c24707fae 100644 --- a/extras/wirepinmux/SAMD21_Table_7-2.csv +++ b/extras/wirepinmux/SAMD21_Table_7-2.csv @@ -1,39 +1,39 @@ -SAMD21ExL,SAMD21GxL,I/O Pin,Supply,A,B1,B2,B3,B4,B5,C,D,E,F,G,H -1,1,PA02,VDDANA,EXTINT[2],,AIN[0],,,VOUT,,,,TCC3/WO[0],, -2,2,PA03,VDDANA,EXTINT[3],DAC/VREFA,AIN[1],,,,,,,TCC3/WO[1],, -3,3,PB04,VDDANA,EXTINT[4],,AIN[12],,AIN[0],,,,,,, -4,4,PB05,VDDANA,EXTINT[5],,AIN[13],,AIN[1],,,,,,, -,7,PB08,VDDANA,EXTINT[8],,AIN[2],,,,,SERCOM4/PAD[0],TC4/WO[0],TCC3/WO[6],, -,8,PB09,VDDANA,EXTINT[9],,AIN[3],,,,,SERCOM4/PAD[1],TC4/WO[1],TCC3/WO[7],, -5,9,PA04,VDDANA,EXTINT[4],ADC/VREFB,AIN[4],AIN[0],,,,SERCOM0/PAD[0],TCC0/WO[0],TCC3/WO[2],, -6,10,PA05,VDDANA,EXTINT[5],,AIN[5],AIN[1],,,,SERCOM0/PAD[1],TCC0/WO[1],TCC3/WO[3],, -7,11,PA06,VDDANA,EXTINT[6],,AIN[6],AIN[2],,,,SERCOM0/PAD[2],TCC1/WO[0],TCC3/WO[4],, -8,12,PA07,VDDANA,EXTINT[7],,AIN[7],AIN[3],,,,SERCOM0/PAD[3],TCC1/WO[1],TCC3/WO[5],, -11,13,PA08,VDDIO,NMI,,AIN[16],,,,SERCOM0/PAD[0],SERCOM2/PAD[0],TCC0/WO[0],TCC1/WO[2],, -12,14,PA09,VDDIO,EXTINT[9],,AIN[17],,,,SERCOM0/PAD[1],SERCOM2/PAD[1],TCC0/WO[1],TCC1/WO[3],, -13,15,PA10,VDDIO,EXTINT[10],,AIN[18],,,,SERCOM0/PAD[2],SERCOM2/PAD[2],TCC1/WO[0],TCC0/WO[2],,GCLK_IO[4] -14,16,PA11,VDDIO,EXTINT[11],,AIN[19],,,,SERCOM0/PAD[3],SERCOM2/PAD[3],TCC1/WO[1],TCC0/WO[3],,GCLK_IO[5] -,19,PB10,VDDIO,EXTINT[10],,,,,,,SERCOM4/PAD[2],TC5/WO[0],TCC0/WO[4],,GCLK_IO[4] -,20,PB11,VDDIO,EXTINT[11],,,,,,,SERCOM4/PAD[3],TC5/WO[1],TCC0/WO[5],,GCLK_IO[5] -,21,PA12,VDDIO,EXTINT[12],,,,,,SERCOM2/PAD[0],SERCOM4/PAD[0],TCC2/WO[0],TCC0/WO[6],,AC/CMP[0] -,22,PA13,VDDIO,EXTINT[13],,,,,,SERCOM2/PAD[1],SERCOM4/PAD[1],TCC2/WO[1],TCC0/WO[7],,AC/CMP[1] -15,23,PA14,VDDIO,EXTINT[14],,,,,,SERCOM2/PAD[2],SERCOM4/PAD[2],TC3/WO[0],TCC0/WO[4],,GCLK_IO[0] -16,24,PA15,VDDIO,EXTINT[15],,,,,,SERCOM2/PAD[3],SERCOM4/PAD[3],TC3/WO[1],TCC0/WO[5],,GCLK_IO[1] -17,25,PA16,VDDIO,EXTINT[0],,,,,,SERCOM1/PAD[0],SERCOM3/PAD[0],TCC2/WO[0],TCC0/WO[6],,GCLK_IO[2] -18,26,PA17,VDDIO,EXTINT[1],,,,,,SERCOM1/PAD[1],SERCOM3/PAD[1],TCC2/WO[1],TCC0/WO[7],,GCLK_IO[3] -19,27,PA18,VDDIO,EXTINT[2],,,,,,SERCOM1/PAD[2],SERCOM3/PAD[2],TC3/WO[0],TCC0/WO[2],,AC/CMP[0] -20,28,PA19,VDDIO,EXTINT[3],,,,,,SERCOM1/PAD[3],SERCOM3/PAD[3],TC3/WO[1],TCC0/WO[3],,AC/CMP[1] -,29,PA20,VDDIO,EXTINT[4],,,,,,SERCOM5/PAD[2],SERCOM3/PAD[2],TC7/WO[0],TCC0/WO[6],,GCLK_IO[4] -,30,PA21,VDDIO,EXTINT[5],,,,,,SERCOM5/PAD[3],SERCOM3/PAD[3],TC7/WO[1],TCC0/WO[7],,GCLK_IO[5] -21,31,PA22,VDDIO,EXTINT[6],,,,,,SERCOM3/PAD[0],SERCOM5/PAD[0],TC4/WO[0],TCC0/WO[4],,GCLK_IO[6] -22,32,PA23,VDDIO,EXTINT[7],,,,,,SERCOM3/PAD[1],SERCOM5/PAD[1],TC4/WO[1],TCC0/WO[5],,GCLK_IO[7] -23,33,PA24,VDDIO,EXTINT[12],,,,,,SERCOM3/PAD[2],SERCOM5/PAD[2],TC5/WO[0],TCC1/WO[2],,AC1/CMP[0] -24,34,PA25,VDDIO,EXTINT[13],,,,,,SERCOM3/PAD[3],SERCOM5/PAD[3],TC5/WO[1],TCC1/WO[3],,AC1/CMP[1] -,37,PA27,VDDIO,EXTINT[15],,,,,,,,,TCC3/WO[6],,GCLK_IO[0] -,39,PA28,VDDIO,EXTINT[8],,,,,,,,,TCC3/WO[7],,GCLK_IO[0] -29,43,PA30,VDDIO,EXTINT[10],,,,,,,SERCOM1/PAD[2],TCC1/WO[0],TCC3/WO[4],SWCLK,GCLK_IO[0] -30,44,PA31,VDDIO,EXTINT[11],,,,,,,SERCOM1/PAD[3],TCC1/WO[1],TCC3/WO[5],SWDIO, -,45,PB00,,,,AIN[8],,,,,,,,, -,46,PB01,,,,AIN[9],,,,,,,,, -31,47,PB02,VDDANA,EXTINT[2],,AIN[10],,AIN[2],,,SERCOM5/PAD[0],TC6/WO[0],TCC3/WO[2],, +SAMD21ExL,SAMD21GxL,I/O Pin,Supply,A,B1,B2,B3,B4,B5,C,D,E,F,G,H +1,1,PA02,VDDANA,EXTINT[2],,AIN[0],,,VOUT,,,,TCC3/WO[0],, +2,2,PA03,VDDANA,EXTINT[3],DAC/VREFA,AIN[1],,,,,,,TCC3/WO[1],, +3,3,PB04,VDDANA,EXTINT[4],,AIN[12],,AIN[0],,,,,,, +4,4,PB05,VDDANA,EXTINT[5],,AIN[13],,AIN[1],,,,,,, +,7,PB08,VDDANA,EXTINT[8],,AIN[2],,,,,SERCOM4/PAD[0],TC4/WO[0],TCC3/WO[6],, +,8,PB09,VDDANA,EXTINT[9],,AIN[3],,,,,SERCOM4/PAD[1],TC4/WO[1],TCC3/WO[7],, +5,9,PA04,VDDANA,EXTINT[4],ADC/VREFB,AIN[4],AIN[0],,,,SERCOM0/PAD[0],TCC0/WO[0],TCC3/WO[2],, +6,10,PA05,VDDANA,EXTINT[5],,AIN[5],AIN[1],,,,SERCOM0/PAD[1],TCC0/WO[1],TCC3/WO[3],, +7,11,PA06,VDDANA,EXTINT[6],,AIN[6],AIN[2],,,,SERCOM0/PAD[2],TCC1/WO[0],TCC3/WO[4],, +8,12,PA07,VDDANA,EXTINT[7],,AIN[7],AIN[3],,,,SERCOM0/PAD[3],TCC1/WO[1],TCC3/WO[5],, +11,13,PA08,VDDIO,NMI,,AIN[16],,,,SERCOM0/PAD[0],SERCOM2/PAD[0],TCC0/WO[0],TCC1/WO[2],, +12,14,PA09,VDDIO,EXTINT[9],,AIN[17],,,,SERCOM0/PAD[1],SERCOM2/PAD[1],TCC0/WO[1],TCC1/WO[3],, +13,15,PA10,VDDIO,EXTINT[10],,AIN[18],,,,SERCOM0/PAD[2],SERCOM2/PAD[2],TCC1/WO[0],TCC0/WO[2],,GCLK_IO[4] +14,16,PA11,VDDIO,EXTINT[11],,AIN[19],,,,SERCOM0/PAD[3],SERCOM2/PAD[3],TCC1/WO[1],TCC0/WO[3],,GCLK_IO[5] +,19,PB10,VDDIO,EXTINT[10],,,,,,,SERCOM4/PAD[2],TC5/WO[0],TCC0/WO[4],,GCLK_IO[4] +,20,PB11,VDDIO,EXTINT[11],,,,,,,SERCOM4/PAD[3],TC5/WO[1],TCC0/WO[5],,GCLK_IO[5] +,21,PA12,VDDIO,EXTINT[12],,,,,,SERCOM2/PAD[0],SERCOM4/PAD[0],TCC2/WO[0],TCC0/WO[6],,AC/CMP[0] +,22,PA13,VDDIO,EXTINT[13],,,,,,SERCOM2/PAD[1],SERCOM4/PAD[1],TCC2/WO[1],TCC0/WO[7],,AC/CMP[1] +15,23,PA14,VDDIO,EXTINT[14],,,,,,SERCOM2/PAD[2],SERCOM4/PAD[2],TC3/WO[0],TCC0/WO[4],,GCLK_IO[0] +16,24,PA15,VDDIO,EXTINT[15],,,,,,SERCOM2/PAD[3],SERCOM4/PAD[3],TC3/WO[1],TCC0/WO[5],,GCLK_IO[1] +17,25,PA16,VDDIO,EXTINT[0],,,,,,SERCOM1/PAD[0],SERCOM3/PAD[0],TCC2/WO[0],TCC0/WO[6],,GCLK_IO[2] +18,26,PA17,VDDIO,EXTINT[1],,,,,,SERCOM1/PAD[1],SERCOM3/PAD[1],TCC2/WO[1],TCC0/WO[7],,GCLK_IO[3] +19,27,PA18,VDDIO,EXTINT[2],,,,,,SERCOM1/PAD[2],SERCOM3/PAD[2],TC3/WO[0],TCC0/WO[2],,AC/CMP[0] +20,28,PA19,VDDIO,EXTINT[3],,,,,,SERCOM1/PAD[3],SERCOM3/PAD[3],TC3/WO[1],TCC0/WO[3],,AC/CMP[1] +,29,PA20,VDDIO,EXTINT[4],,,,,,SERCOM5/PAD[2],SERCOM3/PAD[2],TC7/WO[0],TCC0/WO[6],,GCLK_IO[4] +,30,PA21,VDDIO,EXTINT[5],,,,,,SERCOM5/PAD[3],SERCOM3/PAD[3],TC7/WO[1],TCC0/WO[7],,GCLK_IO[5] +21,31,PA22,VDDIO,EXTINT[6],,,,,,SERCOM3/PAD[0],SERCOM5/PAD[0],TC4/WO[0],TCC0/WO[4],,GCLK_IO[6] +22,32,PA23,VDDIO,EXTINT[7],,,,,,SERCOM3/PAD[1],SERCOM5/PAD[1],TC4/WO[1],TCC0/WO[5],,GCLK_IO[7] +23,33,PA24,VDDIO,EXTINT[12],,,,,,SERCOM3/PAD[2],SERCOM5/PAD[2],TC5/WO[0],TCC1/WO[2],,AC1/CMP[0] +24,34,PA25,VDDIO,EXTINT[13],,,,,,SERCOM3/PAD[3],SERCOM5/PAD[3],TC5/WO[1],TCC1/WO[3],,AC1/CMP[1] +,37,PA27,VDDIO,EXTINT[15],,,,,,,,,TCC3/WO[6],,GCLK_IO[0] +,39,PA28,VDDIO,EXTINT[8],,,,,,,,,TCC3/WO[7],,GCLK_IO[0] +29,43,PA30,VDDIO,EXTINT[10],,,,,,,SERCOM1/PAD[2],TCC1/WO[0],TCC3/WO[4],SWCLK,GCLK_IO[0] +30,44,PA31,VDDIO,EXTINT[11],,,,,,,SERCOM1/PAD[3],TCC1/WO[1],TCC3/WO[5],SWDIO, +,45,PB00,,,,AIN[8],,,,,,,,, +,46,PB01,,,,AIN[9],,,,,,,,, +31,47,PB02,VDDANA,EXTINT[2],,AIN[10],,AIN[2],,,SERCOM5/PAD[0],TC6/WO[0],TCC3/WO[2],, 32,48,PB03,VDDANA,EXTINT[3],,AIN[11],,AIN[3],,,SERCOM5/PAD[1],TC6/WO[1],TCC3/WO[3],, \ No newline at end of file diff --git a/extras/wirepinmux/SAMD21_Table_7-5.csv b/extras/wirepinmux/SAMD21_Table_7-5.csv index 4344f607f..3b0f7183e 100644 --- a/extras/wirepinmux/SAMD21_Table_7-5.csv +++ b/extras/wirepinmux/SAMD21_Table_7-5.csv @@ -1,4 +1,4 @@ -Device,I/O Pin -32, PA08, PA09, PA16, PA17, PA22, PA23 -48, PA08, PA09, PA12, PA13, PA16, PA17, PA22, PA23 +Device,I/O Pin +32, PA08, PA09, PA16, PA17, PA22, PA23 +48, PA08, PA09, PA12, PA13, PA16, PA17, PA22, PA23 64, PA08, PA09, PA12, PA13, PA16, PA17, PA22, PA23, PB12, PB13, PB16, PB17, PB30, PB31 \ No newline at end of file diff --git a/extras/wirepinmux/SAMD51_Device_I2C_Config.csv b/extras/wirepinmux/SAMD51_Device_I2C_Config.csv index cbf74e6b5..93ab09455 100644 --- a/extras/wirepinmux/SAMD51_Device_I2C_Config.csv +++ b/extras/wirepinmux/SAMD51_Device_I2C_Config.csv @@ -1,26 +1,26 @@ -Device,Pin Count,I2C Pins,SERCOM Configs -SAMD51G18,48,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAMD51G19,48,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAMD51J18,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAMD51J19,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAMD51J20,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAMD51N19,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAMD51N20,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAMD51P19,120,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PD08,PD09","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]|PD08:[SERCOM7/PAD[0],SERCOM6/PAD[1]]|PD09:[SERCOM7/PAD[1],SERCOM6/PAD[0]]" -SAMD51P20,120,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PD08,PD09","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]|PD08:[SERCOM7/PAD[0],SERCOM6/PAD[1]]|PD09:[SERCOM7/PAD[1],SERCOM6/PAD[0]]" -SAME51G18,48,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAME51G19,48,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAME51J18,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAME51J19,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAME51J20,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAME51N19,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAME51N20,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAME53J18,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAME53J19,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAME53J20,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAME53N19,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAME53N20,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAME54N19,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAME54N20,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" -SAME54P19,120,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PD08,PD09","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]|PD08:[SERCOM7/PAD[0],SERCOM6/PAD[1]]|PD09:[SERCOM7/PAD[1],SERCOM6/PAD[0]]" -SAME54P20,120,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PD08,PD09","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]|PD08:[SERCOM7/PAD[0],SERCOM6/PAD[1]]|PD09:[SERCOM7/PAD[1],SERCOM6/PAD[0]]" +Device,Pin Count,I2C Pins,SERCOM Configs +SAMD51G18,48,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAMD51G19,48,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAMD51J18,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAMD51J19,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAMD51J20,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAMD51N19,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAMD51N20,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAMD51P19,120,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PD08,PD09","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]|PD08:[SERCOM7/PAD[0],SERCOM6/PAD[1]]|PD09:[SERCOM7/PAD[1],SERCOM6/PAD[0]]" +SAMD51P20,120,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PD08,PD09","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]|PD08:[SERCOM7/PAD[0],SERCOM6/PAD[1]]|PD09:[SERCOM7/PAD[1],SERCOM6/PAD[0]]" +SAME51G18,48,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME51G19,48,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME51J18,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME51J19,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME51J20,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME51N19,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME51N20,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME53J18,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME53J19,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME53J20,64,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME53N19,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME53N20,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME54N19,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME54N20,100,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]" +SAME54P19,120,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PD08,PD09","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]|PD08:[SERCOM7/PAD[0],SERCOM6/PAD[1]]|PD09:[SERCOM7/PAD[1],SERCOM6/PAD[0]]" +SAME54P20,120,"PA08,PA09,PA12,PA13,PA16,PA17,PA22,PA23,PD08,PD09","PA08:[SERCOM0/PAD[0],SERCOM2/PAD[1]]|PA09:[SERCOM0/PAD[1],SERCOM2/PAD[0]]|PA12:[SERCOM2/PAD[0],SERCOM4/PAD[1]]|PA13:[SERCOM2/PAD[1],SERCOM4/PAD[0]]|PA16:[SERCOM1/PAD[0],SERCOM3/PAD[1]]|PA17:[SERCOM1/PAD[1],SERCOM3/PAD[0]]|PA22:[SERCOM3/PAD[0],SERCOM5/PAD[1]]|PA23:[SERCOM3/PAD[1],SERCOM5/PAD[0]]|PD08:[SERCOM7/PAD[0],SERCOM6/PAD[1]]|PD09:[SERCOM7/PAD[1],SERCOM6/PAD[0]]" diff --git a/extras/wirepinmux/SAMD51_Table_1-1.csv b/extras/wirepinmux/SAMD51_Table_1-1.csv index 490edd9a4..78e57a570 100644 --- a/extras/wirepinmux/SAMD51_Table_1-1.csv +++ b/extras/wirepinmux/SAMD51_Table_1-1.csv @@ -1,14 +1,14 @@ -Table 1-1. SAM E53/E54 Family Features with Ethernet,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -,,,,,Peripherals,,,,,,,,,,,,,,,,,,,Analog,,,,,Security,,,, -Device,Flash (KB),SRAM (KB) / Backup RAM (KB),Pins,Packages,Ethernet Controller,CAN-FD,SERCOM,TC/Compare,TCC (24-bit/16-bit),I2S,USB,QSPI,SDHC,DMA Channels,PCC (data size),CCL,Position Decoder,RTC,WDT,Frequency Measurement,Event System (Channels),External Interrupt Lines,I/O Pins,ADC (Channels ADC0/ADC1),Analog Comparators (Channels),DAC (Channels),PTC (Mutual/Self-capacitance Channels),Temperature Sensor,AES,TRNG,Public Key Cryptography (PUKCC),Integrity Check Monitor,Tamper Pins -SAME53N20,1024,256 / 8,100,TQFP,Y,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,"256/32",Y,Y,Y,Y,Y,5 -SAME53N19,512,192 / 8,100,TQFP,Y,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,"256/32",Y,Y,Y,Y,Y,5 -SAME53J20,1024,256 / 8,64,"TQFP,VQFN",Y,N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,"256/32",Y,Y,Y,Y,Y,3 -SAME53J19,512,192 / 8,64,"TQFP,VQFN",Y,N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,"256/32",Y,Y,Y,Y,Y,3 -SAME53J18,256,128 / 8,64,"TQFP,VQFN",Y,N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,"256/32",Y,Y,Y,Y,Y,3 -SAME54P20,1024,256 / 8,128,TQFP,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,"256/32",Y,Y,Y,Y,Y,5 -SAME54P20,1024,256 / 8,120,TFBGA,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,"256/32",Y,Y,Y,Y,Y,5 -SAME54P19,512,192 / 8,128,TQFP,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,"256/32",Y,Y,Y,Y,Y,5 -SAME54P19,512,192 / 8,120,TFBGA,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,"256/32",Y,Y,Y,Y,Y,5 -SAME54N20,1024,256 / 8,100,TQFP,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,"256/32",Y,Y,Y,Y,Y,5 +Table 1-1. SAM E53/E54 Family Features with Ethernet,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,Peripherals,,,,,,,,,,,,,,,,,,,Analog,,,,,Security,,,, +Device,Flash (KB),SRAM (KB) / Backup RAM (KB),Pins,Packages,Ethernet Controller,CAN-FD,SERCOM,TC/Compare,TCC (24-bit/16-bit),I2S,USB,QSPI,SDHC,DMA Channels,PCC (data size),CCL,Position Decoder,RTC,WDT,Frequency Measurement,Event System (Channels),External Interrupt Lines,I/O Pins,ADC (Channels ADC0/ADC1),Analog Comparators (Channels),DAC (Channels),PTC (Mutual/Self-capacitance Channels),Temperature Sensor,AES,TRNG,Public Key Cryptography (PUKCC),Integrity Check Monitor,Tamper Pins +SAME53N20,1024,256 / 8,100,TQFP,Y,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,"256/32",Y,Y,Y,Y,Y,5 +SAME53N19,512,192 / 8,100,TQFP,Y,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,"256/32",Y,Y,Y,Y,Y,5 +SAME53J20,1024,256 / 8,64,"TQFP,VQFN",Y,N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,"256/32",Y,Y,Y,Y,Y,3 +SAME53J19,512,192 / 8,64,"TQFP,VQFN",Y,N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,"256/32",Y,Y,Y,Y,Y,3 +SAME53J18,256,128 / 8,64,"TQFP,VQFN",Y,N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,"256/32",Y,Y,Y,Y,Y,3 +SAME54P20,1024,256 / 8,128,TQFP,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,"256/32",Y,Y,Y,Y,Y,5 +SAME54P20,1024,256 / 8,120,TFBGA,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,"256/32",Y,Y,Y,Y,Y,5 +SAME54P19,512,192 / 8,128,TQFP,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,"256/32",Y,Y,Y,Y,Y,5 +SAME54P19,512,192 / 8,120,TFBGA,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,"256/32",Y,Y,Y,Y,Y,5 +SAME54N20,1024,256 / 8,100,TQFP,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,"256/32",Y,Y,Y,Y,Y,5 SAME54N19,512,192 / 8,100,TQFP,Y,Y,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,"256/32",Y,Y,Y,Y,Y,5 \ No newline at end of file diff --git a/extras/wirepinmux/SAMD51_Table_1-2.csv b/extras/wirepinmux/SAMD51_Table_1-2.csv index 800a7bd2f..881339354 100644 --- a/extras/wirepinmux/SAMD51_Table_1-2.csv +++ b/extras/wirepinmux/SAMD51_Table_1-2.csv @@ -1,21 +1,21 @@ -Table 1-2. SAM D51/E51 Family Features without Ethernet,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -,,,,,Peripherals,,,,,,,,,,,,,,,,,,Analog,,,,,Security,,,, -Device,Flash (KB),SRAM (KB) / Backup RAM (KB),Pins,Packages,CAN-FD,SERCOM,TC/Compare,TCC (24-bit/16-bit),I2S,USB,QSPI,SDHC,DMA Channels,PCC (data size),CCL,Position Decoder,RTC,WDT,Frequency Measurement,Event System (Channels),External Interrupt Lines,I/O Pins,ADC (Channels ADC0/ADC1),Analog Comparators (Channels),DAC (Channels),PTC (Mutual/Self-capacitance Channels),Temperature Sensor,AES,TRNG,Public Key Cryptography (PUKCC),Integrity Check Monitor,Tamper Pins -SAMD51P20,1024,256,128,TQFP,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,256/32,Y,Y,Y,Y,Y,5 -SAMD51P20,1024,256,120,TFBGA,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,256/32,Y,Y,Y,Y,Y,5 -SAMD51P19,512,192,128,TQFP,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,256/32,Y,Y,Y,Y,Y,5 -SAMD51P19,512,192,120,TFBGA,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,256/32,Y,Y,Y,Y,Y,5 -SAMD51N20,1024,256,100,TQFP,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,256/32,Y,Y,Y,Y,Y,5 -SAMD51N19,512,192,100,TQFP,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,256/32,Y,Y,Y,Y,Y,5 -SAMD51J20,1024,256,64,"TQFP,VQFN,WLCSP",N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 -SAMD51J19,512,192,64,"TQFP,VQFN,WLCSP",N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 -SAMD51J18,256,128,64,"TQFP,VQFN",N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 -SAMD51G19,512,192,48,VQFN,N,6,"4/2","2/1",N,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,37,"16/4",4,2,121/22,Y,Y,Y,Y,Y,2 -SAMD51G18,256,128,48,VQFN,N,6,"4/2","2/1",N,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,37,"16/4",4,2,121/22,Y,Y,Y,Y,Y,2 -SAME51N20,1024,256,100,TQFP,Y,8,"8/2","2/3",Y,Y,Y,1,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,256/32,Y,Y,Y,Y,Y,5 -SAME51N19,512,192,100,TQFP,Y,8,"8/2","2/3",Y,Y,Y,1,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,256/32,Y,Y,Y,Y,Y,5 -SAME51J20,1024,256,64,"TQFP,VQFN",Y,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 -SAME51J19,512,192,64,"TQFP,VQFN",Y,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 -SAME51J18,256,128,64,"TQFP,VQFN",Y,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 -SAME51G18,256,128,48,VQFN,Y,6,"4/2","2/1",N,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,37,"16/4",4,2,121/22,Y,Y,Y,Y,Y,2 +Table 1-2. SAM D51/E51 Family Features without Ethernet,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,Peripherals,,,,,,,,,,,,,,,,,,Analog,,,,,Security,,,, +Device,Flash (KB),SRAM (KB) / Backup RAM (KB),Pins,Packages,CAN-FD,SERCOM,TC/Compare,TCC (24-bit/16-bit),I2S,USB,QSPI,SDHC,DMA Channels,PCC (data size),CCL,Position Decoder,RTC,WDT,Frequency Measurement,Event System (Channels),External Interrupt Lines,I/O Pins,ADC (Channels ADC0/ADC1),Analog Comparators (Channels),DAC (Channels),PTC (Mutual/Self-capacitance Channels),Temperature Sensor,AES,TRNG,Public Key Cryptography (PUKCC),Integrity Check Monitor,Tamper Pins +SAMD51P20,1024,256,128,TQFP,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,256/32,Y,Y,Y,Y,Y,5 +SAMD51P20,1024,256,120,TFBGA,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,256/32,Y,Y,Y,Y,Y,5 +SAMD51P19,512,192,128,TQFP,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,256/32,Y,Y,Y,Y,Y,5 +SAMD51P19,512,192,120,TFBGA,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,99,"16/16",4,2,256/32,Y,Y,Y,Y,Y,5 +SAMD51N20,1024,256,100,TQFP,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,256/32,Y,Y,Y,Y,Y,5 +SAMD51N19,512,192,100,TQFP,N,8,"8/2","2/3",Y,Y,Y,2,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,256/32,Y,Y,Y,Y,Y,5 +SAMD51J20,1024,256,64,"TQFP,VQFN,WLCSP",N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 +SAMD51J19,512,192,64,"TQFP,VQFN,WLCSP",N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 +SAMD51J18,256,128,64,"TQFP,VQFN",N,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 +SAMD51G19,512,192,48,VQFN,N,6,"4/2","2/1",N,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,37,"16/4",4,2,121/22,Y,Y,Y,Y,Y,2 +SAMD51G18,256,128,48,VQFN,N,6,"4/2","2/1",N,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,37,"16/4",4,2,121/22,Y,Y,Y,Y,Y,2 +SAME51N20,1024,256,100,TQFP,Y,8,"8/2","2/3",Y,Y,Y,1,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,256/32,Y,Y,Y,Y,Y,5 +SAME51N19,512,192,100,TQFP,Y,8,"8/2","2/3",Y,Y,Y,1,32,14,4,Y,Y,Y,Y,32,16,81,"16/12",4,2,256/32,Y,Y,Y,Y,Y,5 +SAME51J20,1024,256,64,"TQFP,VQFN",Y,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 +SAME51J19,512,192,64,"TQFP,VQFN",Y,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 +SAME51J18,256,128,64,"TQFP,VQFN",Y,6,"6/2","2/3",Y,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,51,"16/8",4,2,256/32,Y,Y,Y,Y,Y,3 +SAME51G18,256,128,48,VQFN,Y,6,"4/2","2/1",N,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,37,"16/4",4,2,121/22,Y,Y,Y,Y,Y,2 SAME51G19,512,192,48,VQFN,Y,6,"4/2","2/1",N,Y,Y,1,32,10,4,Y,Y,Y,Y,32,16,37,"16/4",4,2,121/22,Y,Y,Y,Y,Y,2 \ No newline at end of file diff --git a/extras/wirepinmux/SAMD51_Table_6-1.csv b/extras/wirepinmux/SAMD51_Table_6-1.csv index b9171d493..15969cb2f 100644 --- a/extras/wirepinmux/SAMD51_Table_6-1.csv +++ b/extras/wirepinmux/SAMD51_Table_6-1.csv @@ -1,101 +1,101 @@ -48,64,100,120,128,Pad Name,A,B1,B2,B3,B4,B5,B6,C,D,E,F,G,H,I,J,K,L,M,N -48,64/C6,100,B2,128,PB03,EIC/EXTINT[3],-,ADC0/AIN[15],-,-,-,X21/Y21,-,SERCOM5/PAD[1],TC6/WO[1],-,-,-,-,-,-,-,-,- -1,01/B8,1,A1,1,PA00,EIC/EXTINT[0],-,-,-,-,-,,-,SERCOM1/PAD[0],TC2/WO[0],-,-,-,-,-,-,-,-,- -2,02/C8,2,B1,2,PA01,EIC/EXTINT[1],-,-,-,-,-,,-,SERCOM1/PAD[1],TC2/WO[1],-,-,-,-,-,-,-,-,- -,,3,C1,3,PC00,EIC/EXTINT[0],-,-,ADC1/AIN[10],-,-,,-,-,-,-,-,-,-,-,-,-,-,- -,,4,C2,4,PC01,EIC/EXTINT[1],-,-,ADC1/AIN[11],-,-,,-,-,-,-,-,-,-,-,-,-,-,- -,,5,D1,7,PC02,EIC/EXTINT[2],-,-,ADC1/AIN[4],-,-,,-,-,-,-,-,-,-,-,-,-,-,- -,,6,E2,8,PC03,EIC/EXTINT[3],-,-,ADC1/AIN[5],-,-,,-,-,-,-,-,-,-,-,-,-,-,- -3,03/C7,7,E1,9,PA02,EIC/EXTINT[2],-,ADC0/AIN[0],-,-,DAC/VOUT[0],,-,-,-,-,-,-,-,-,-,-,-,- -4,04/D6,8,F2,10,PA03,EIC/EXTINT[3],ANAREF/VREFA,ADC0/AIN[1],-,-,-,X0/Y0,-,-,-,-,-,-,-,-,-,-,-,- -,05/D7,9,F1,11,PB04,EIC/EXTINT[4],-,-,ADC1/AIN[6],-,-,X22/Y22,-,-,-,-,-,-,-,-,-,-,-,- -,06/D8,10,G1,12,PB05,EIC/EXTINT[5],-,-,ADC1/AIN[7],-,-,X23/Y23,-,-,-,-,-,-,-,-,-,-,-,- -,,-,G2,13,PD00,EIC/EXTINT[0],-,-,ADC1/AIN[14],-,-,,-,-,-,-,-,-,-,-,-,-,-,- -,,-,H1,16,PD01,EIC/EXTINT[1],-,-,ADC1/AIN[15],-,-,,-,-,-,-,-,-,-,-,-,-,-,- -,09/E7,13,H2,17,PB06,EIC/EXTINT[6],-,-,ADC1/AIN[8],-,-,X24/Y24,-,-,-,-,-,-,-,-,-,-,-,CCL/IN[6] -,10/E6,14,J1,18,PB07,EIC/EXTINT[7],-,-,ADC1/AIN[9],-,-,X25/Y25,-,-,-,-,-,-,-,-,-,-,-,CCL/IN[7] -7,11/F5,15,J2,19,PB08,EIC/EXTINT[8],-,ADC0/AIN[2],ADC1/AIN[0],-,-,X1/Y1,-,SERCOM4/PAD[0],TC4/WO[0],-,-,-,-,-,-,-,-,CCL/IN[8] -8,12/F8,16,K1,20,PB09,EIC/EXTINT[9],-,ADC0/AIN[3],ADC1/AIN[1],-,-,X2/Y2,-,SERCOM4/PAD[1],TC4/WO[1],-,-,-,-,-,-,-,-,CCL/OUT[2] -9,13/F7,17,K2,21,PA04,EIC/EXTINT[4],ANAREF/VREFB,ADC0/AIN[4],-,AC/AIN[0],-,X3/Y3,-,SERCOM0/PAD[0],TC0/WO[0],-,-,-,-,-,-,-,-,CCL/IN[0] -10,14/F6,18,L1,22,PA05,EIC/EXTINT[5],-,ADC0/AIN[5],-,AC/AIN[1],DAC/VOUT[1],,-,SERCOM0/PAD[1],TC0/WO[1],-,-,-,-,-,-,-,-,CCL/IN[1] -11,15/G7,19,L2,23,PA06,EIC/EXTINT[6],ANAREF/VREFC,ADC0/AIN[6],-,AC/AIN[2],-,X4/Y4,-,SERCOM0/PAD[2],TC1/WO[0],-,-,-,SDHC0/SDCD,-,-,-,-,CCL/IN[2] -12,16/G8,20,M1,24,PA07,EIC/EXTINT[7],-,ADC0/AIN[7],-,AC/AIN[3],-,X5/Y5,-,SERCOM0/PAD[3],TC1/WO[1],-,-,-,SDHC0/SDWP,-,-,-,-,CCL/OUT[0] -,,-,N1,27,PC04,EIC/EXTINT[4],-,-,-,-,-,,SERCOM6/PAD[0],-,-,TCC0/WO[0],-,-,-,-,-,-,-,- -,,21,N2,28,PC05,EIC/EXTINT[5],-,-,-,-,-,,SERCOM6/PAD[1],-,-,-,-,-,-,-,-,-,-,- -,,22,P1,29,PC06,EIC/EXTINT[6],-,-,-,-,-,,SERCOM6/PAD[2],-,-,-,-,-,SDHC0/SDCD,-,-,-,-,- -,,23,P2,30,PC07,EIC/EXTINT[9],-,-,-,-,-,,SERCOM6/PAD[3],-,-,-,-,-,SDHC0/SDWP,-,-,-,-,- -13,17/H8,26,R1,33,PA08,EIC/NMI,-,ADC0/AIN[8],ADC1/AIN[2],-,-,X6/Y6,SERCOM0/PAD[0],SERCOM2/PAD[1],TC0/WO[0],TCC0/WO[0],TCC1/WO[4],QSPI/DATA[0],SDHC0/SDCMD,I2S/MCK[0],-,-,-,CCL/IN[3] -14,18/G6,27,P3,34,PA09,EIC/EXTINT[9],-,ADC0/AIN[9],ADC1/AIN[3],-,-,X7/Y7,SERCOM0/PAD[1],SERCOM2/PAD[0],TC0/WO[1],TCC0/WO[1],TCC1/WO[5],QSPI/DATA[1],SDHC0/SDDAT[0],I2S/FS[0],-,-,-,CCL/IN[4] -15,19/H7,28,R2,35,PA10,EIC/EXTINT[10],-,ADC0/-AIN[10],,-,-,X8/Y8,SERCOM0/PAD[2],SERCOM2/PAD[2],TC1/WO[0],TCC0/WO[2],TCC1/WO[6],QSPI/DATA[2],SDHC0/SDDAT[1],I2S/SCK[0],-,-,GCLK/IO[4],CCL/IN[5] -16,20/G5,29,P4,36,PA11,EIC/EXTINT[11],-,ADC0/-AIN[11],,-,-,X9/Y9,SERCOM0/PAD[3],SERCOM2/PAD[3],TC1/WO[1],TCC0/WO[3],TCC1/WO[7],QSPI/DATA[3],SDHC0/SDDAT[2],I2S/SDO,-,-,GCLK/IO[5],CCL/OUT[1] -19,23/H6,32,R3,39,PB10,EIC/EXTINT[10],-,-,-,-,-,,-,SERCOM4/PAD[2],TC5/WO[0],TCC0/WO[4],TCC1/WO[0],QSPI/SCK,SDHC0/SDDAT[3],I2S/SDI,-,-,GCLK/IO[4],CCL/IN[11] -20,24/G4,33,P5,40,PB11,EIC/EXTINT[11],-,-,-,-,-,,-,SERCOM4/PAD[3],TC5/WO[1],TCC0/WO[5],TCC1/WO[1],QSPI/CS,SDHC0/SDCK,I2S/FS[1],-,-,GCLK/IO[5],CCL/OUT[1] -,25/H5,34,R4,41,PB12,EIC/EXTINT[12],-,-,-,-,-,X26/Y26,SERCOM4/PAD[0],-,TC4/WO[0],TCC3/WO[0],TCC0/WO[0],CAN1/TX,SDHC0/SDCD,I2S/SCK[1],-,-,GCLK/IO[6],- -,26/H4,35,P6,42,PB13,EIC/EXTINT[13],-,-,-,-,-,X27/Y27,SERCOM4/PAD[1],-,TC4/WO[1],TCC3/WO[1],TCC0/WO[1],CAN1/RX,SDHC0/SDWP,I2S/MCK[1],-,-,GCLK/IO[7],- -,27/G3,36,R5,43,PB14,EIC/EXTINT[14],-,-,-,-,-,X28/Y28,SERCOM4/PAD[2],-,TC5/WO[0],TCC4/WO[0],TCC0/WO[2],CAN1/TX,-,-,PCC/DATA[8],GMAC/GMDC,GCLK/IO[0],CCL/IN[9] -,28/H3,37,P7,44,PB15,EIC/EXTINT[15],-,-,-,-,-,X29/Y29,SERCOM4/PAD[3],-,TC5/WO[1],TCC4/WO[1],TCC0/WO[3],CAN1/RX,-,-,PCC/DATA[9],GMAC/GMDIO,GCLK/IO[1],CCL/IN[10] -,,-,R6,47,PD08,EIC/EXTINT[3],-,-,-,-,-,,SERCOM7/PAD[0],SERCOM6/PAD[1],-,TCC0/WO[1],-,-,-,-,-,-,-,- -,,-,P8,48,PD09,EIC/EXTINT[4],-,-,-,-,-,,SERCOM7/PAD[1],SERCOM6/PAD[0],-,TCC0/WO[2],-,-,-,-,-,-,-,- -,,-,R7,49,PD10,EIC/EXTINT[5],-,-,-,-,-,,SERCOM7/PAD[2],SERCOM6/PAD[2],-,TCC0/WO[3],-,-,-,-,-,-,-,- -,,-,P9,50,PD11,EIC/EXTINT[6],-,-,-,-,-,,SERCOM7/PAD[3],SERCOM6/PAD[3],-,TCC0/WO[4],-,-,-,-,-,-,-,- -,,-,R8,51,PD12,EIC/EXTINT[7],-,-,-,-,-,,-,-,-,TCC0/WO[5],-,-,-,-,-,-,-,- -,,40,P10,52,PC10,EIC/EXTINT[10],-,-,-,-,-,,SERCOM6/PAD[2],SERCOM7/PAD[2],-,TCC0/WO[0],TCC1/WO[4],-,-,-,-,-,-,- -,,41,R9,55,PC11,EIC/EXTINT[11],-,-,-,-,-,,SERCOM6/PAD[3],SERCOM7/PAD[3],-,TCC0/WO[1],TCC1/WO[5],-,-,-,-,GMAC/GMDC,-,- -,,42,R10,56,PC12,EIC/EXTINT[12],-,-,-,-,-,,SERCOM7/PAD[0],SERCOM6/PAD[1],-,TCC0/WO[2],TCC1/WO[6],-,-,-,PCC/DATA[10],GMAC/GMDIO,-,- -,,43,P11,57,PC13,EIC/EXTINT[13],-,-,-,-,-,,SERCOM7/PAD[1],SERCOM6/PAD[0],-,TCC0/WO[3],TCC1/WO[7],-,-,-,PCC/DATA[11],-,-,- -,,44,R11,58,PC14,EIC/EXTINT[14],-,-,-,-,-,,SERCOM7/PAD[2],SERCOM6/PAD[2],-,TCC0/WO[4],TCC1/WO[0],-,-,-,PCC/DATA[12],GMAC/GRX[3],-,- -,,45,P12,59,PC15,EIC/EXTINT[15],-,-,-,-,-,,SERCOM7/PAD[3],SERCOM6/PAD[3],-,TCC0/WO[5],TCC1/WO[1],-,-,-,PCC/DATA[13],GMAC/GRX[2],-,- -21,29/F2,46,R12,60,PA12,EIC/EXTINT[12],-,-,-,-,-,,SERCOM2/PAD[0],SERCOM4/PAD[1],TC2/WO[0],TCC0/WO[6],TCC1/WO[2],-,SDHC0/SDCD,-,PCC/DEN1,GMAC/GRX[1],AC/CMP[0],- -22,30/G2,47,P13,61,PA13,EIC/EXTINT[13],-,-,-,-,-,,SERCOM2/PAD[1],SERCOM4/PAD[0],TC2/WO[1],TCC0/WO[7],TCC1/WO[3],-,SDHC0/SDWP,-,PCC/DEN2,GMAC/GRX[0],AC/CMP[1],- -23,31/H1,48,R13,62,PA14,EIC/EXTINT[14],-,-,-,-,-,,SERCOM2/PAD[2],SERCOM4/PAD[2],TC3/WO[0],TCC2/WO[0],TCC1/WO[2],-,-,-,PCC/CLK,GMAC/GTXCK,GCLK/IO[0],- -24,32/H2,49,R14,63,PA15,EIC/EXTINT[15],-,-,-,-,-,,SERCOM2/PAD[3],SERCOM4/PAD[3],TC3/WO[1],TCC2/WO[1],TCC1/WO[3],-,-,-,-,GMAC/GRXER,GCLK/IO[1],- -25,35/G1,52,R15,66,PA16,EIC/EXTINT[0],-,-,-,-,-,X10/Y10,SERCOM1/PAD[0],SERCOM3/PAD[1],TC2/WO[0],TCC1/WO[0],TCC0/WO[4],-,-,-,PCC/DATA[0],GMAC/GCRS/GRXDV(6),GCLK/IO[2],CCL/IN[0] -26,36/F1,53,P14,67,PA17,EIC/EXTINT[1],-,-,-,-,-,X11/Y11,SERCOM1/PAD[1],SERCOM3/PAD[0],TC2/WO[1],TCC1/WO[1],TCC0/WO[5],-,-,-,PCC/DATA[1],GMAC/GTXEN,GCLK/IO[3],CCL/IN[1] -27,37/E1,54,P15,68,PA18,EIC/EXTINT[2],-,-,-,-,-,X12/Y12,SERCOM1/PAD[2],SERCOM3/PAD[2],TC3/WO[0],TCC1/WO[2],TCC0/WO[6],-,-,-,PCC/DATA[2],GMAC/GTX[0],AC/CMP[0],CCL/IN[2] -28,38/E2,55,N14,69,PA19,EIC/EXTINT[3],-,-,-,-,-,X13/Y13,SERCOM1/PAD[3],SERCOM3/PAD[3],TC3/WO[1],TCC1/WO[3],TCC0/WO[7],-,-,-,PCC/DATA[3],GMAC/GTX[1],AC/CMP[1],CCL/OUT[0] -,,56,N15,70,PC16,EIC/EXTINT[0],-,-,-,-,-,,SERCOM6/PAD[0],SERCOM0/PAD[1],-,TCC0/WO[0],PDEC/PDEC[0],-,-,-,-,GMAC/GTX[2],-,- -,,57,M14,71,PC17,EIC/EXTINT[1],-,-,-,-,-,,SERCOM6/PAD[1],SERCOM0/PAD[0],-,TCC0/WO[1],PDEC/PDEC[1],-,-,-,-,GMAC/GTX[3],-,- -,,58,M15,72,PC18,EIC/EXTINT[2],-,-,-,-,-,,SERCOM6/PAD[2],SERCOM0/PAD[2],-,TCC0/WO[2],PDEC/PDEC[2],-,-,-,-,GMAC/GRXCK,-,- -,,59,L14,73,PC19,EIC/EXTINT[3],-,-,-,-,-,,SERCOM6/PAD[3],SERCOM0/PAD[3],-,TCC0/WO[3],-,-,-,-,-,GMAC/GTXER,-,- -,,60,L15,74,PC20,EIC/EXTINT[4],-,-,-,-,-,,-,-,-,TCC0/WO[4],-,-,SDHC1/SDCD,-,-,GMAC/GRXDV,-,CCL/IN[9] -,,61,K14,75,PC21,EIC/EXTINT[5],-,-,-,-,-,,-,-,-,TCC0/WO[5],-,-,SDHC1/SDWP,-,-,GMAC/GCOL,-,CCL/IN[10] -,,-,K15,76,PC22,EIC/EXTINT[6],-,-,-,-,-,,SERCOM1/PAD[0],SERCOM3/PAD[1],-,TCC0/WO[6],-,-,-,-,-,GMAC/GMDC,-,- -,,-,J14,77,PC23,EIC/EXTINT[7],-,-,-,-,-,,SERCOM1/PAD[1],SERCOM3/PAD[0],-,TCC0/WO[7],-,-,-,-,-,GMAC/GMDIO,-,- -,,-,J15,80,PD20,EIC/EXTINT[10],-,-,-,-,-,,SERCOM1/PAD[2],SERCOM3/PAD[2],-,TCC1/WO[0],-,-,SDHC1/SDCD,-,-,-,-,- -,,-,H14,81,PD21,EIC/EXTINT[11],-,-,-,-,-,,SERCOM1/PAD[3],SERCOM3/PAD[3],-,TCC1/WO[1],-,-,SDHC1/SDWP,-,-,-,-,- -,39/D4,64,H15,82,PB16,EIC/EXTINT[0],-,-,-,-,-,,SERCOM5/PAD[0],-,TC6/WO[0],TCC3/WO[0],TCC0/WO[4],-,SDHC1/SDCD,I2S/SCK[0],-,-,GCLK/IO[2],CCL/IN[11] -,40/D1,65,G15,83,PB17,EIC/EXTINT[1],-,-,-,-,-,,SERCOM5/PAD[1],-,TC6/WO[1],TCC3/WO[1],TCC0/WO[5],-,SDHC1/SDWP,I2S/MCK[0],-,-,GCLK/IO[3],CCL/OUT[3] -,,66,G14,84,PB18,EIC/EXTINT[2],-,-,-,-,-,,SERCOM5/PAD[2],SERCOM7/PAD[2],-,TCC1/WO[0],PDEC/PDEC[0],-,SDHC1/SDDAT[0],-,-,-,GCLK/IO[4],- -,,67,F15,85,PB19,EIC/EXTINT[3],-,-,-,-,-,,SERCOM5/PAD[3],SERCOM7/PAD[3],-,TCC1/WO[1],PDEC/PDEC[1],-,SDHC1/SDDAT[1],-,-,-,GCLK/IO[5],- -,,68,F14,86,PB20,EIC/EXTINT[4],-,-,-,-,-,,SERCOM3/PAD[0],SERCOM7/PAD[1],-,TCC1/WO[2],PDEC/PDEC[2],-,SDHC1/SDDAT[2],-,-,-,GCLK/IO[6],- -,,69,E15,87,PB21,EIC/EXTINT[5],-,-,-,-,-,,SERCOM3/PAD[1],SERCOM7/PAD[0],-,TCC1/WO[3],-,-,SDHC1/SDDAT[3],-,-,-,GCLK/IO[7],- -29,41/D2,70,E14,88,PA20,EIC/EXTINT[4],-,-,-,-,-,X14/Y14,SERCOM5/PAD[2],SERCOM3/PAD[2],TC7/WO[0],TCC1/WO[4],TCC0/WO[0],-,SDHC1/SDCMD,I2S/FS[0],PCC/DATA[4],GMAC/GMDC,-,- -30,42/D3,71,D15,89,PA21,EIC/EXTINT[5],-,-,-,-,-,X15/Y15,SERCOM5/PAD[3],SERCOM3/PAD[3],TC7/WO[1],TCC1/WO[5],TCC0/WO[1],-,SDHC1/SDCK,I2S/SDO,PCC/DATA[5],GMAC/GMDIO,-,- -31,43/C1,72,D14,92,PA22,EIC/EXTINT[6],-,-,-,-,-,X16/Y16,SERCOM3/PAD[0],SERCOM5/PAD[1],TC4/WO[0],TCC1/WO[6],TCC0/WO[2],-,CAN0/TX,I2S/SDI,PCC/DATA[6],-,-,CCL/IN[6] -32,44/C2,73,C14,93,PA23,EIC/EXTINT[7],-,-,-,-,-,X17/Y17,SERCOM3/PAD[1],SERCOM5/PAD[0],TC4/WO[1],TCC1/WO[7],TCC0/WO[3],USB/SOF_1KHZ,CAN0/RX,I2S/FS[1],PCC/DATA[7],-,-,CCL/IN[7] -33,45/B1,74,C15,94,PA24,EIC/EXTINT[8],-,-,-,-,-,,SERCOM3/PAD[2],SERCOM5/PAD[2],TC5/WO[0],TCC2/WO[2],PDEC/PDEC[0],USB/DM,CAN0/TX,-,-,-,-,CCL/IN[8] -34,46/A1,75,B15,95,PA25,EIC/EXTINT[9],-,-,-,-,-,,SERCOM3/PAD[3],SERCOM5/PAD[3],TC5/WO[1],-,PDEC/PDEC[1],USB/DP,CAN0/RX,-,-,-,-,CCL/OUT[2] -37,49/A2,78,A15,98,PB22,EIC/EXTINT[6],-,-,-,-,-,,SERCOM1/PAD[2],SERCOM5/PAD[2],TC7/WO[0],-,PDEC/PDEC[2],USB/SOF_1KHZ,-,-,-,-,GCLK/IO[0],CCL/IN[0] -38,50/A3,79,A14,99,PB23,EIC/EXTINT[7],-,-,-,-,-,,SERCOM1/PAD[3],SERCOM5/PAD[3],TC7/WO[1],-,PDEC/PDEC[0],-,-,-,-,-,GCLK/IO[1],CCL/OUT[0] -,,80,B14,100,PB24,EIC/EXTINT[8],-,-,-,-,-,,SERCOM0/PAD[0],SERCOM2/PAD[1],-,-,PDEC/PDEC[1],-,-,-,-,-,AC/CMP[0],- -,,81,B13,101,PB25,EIC/EXTINT[9],-,-,-,-,-,,SERCOM0/PAD[1],SERCOM2/PAD[0],-,-,PDEC/PDEC[2],-,-,-,-,-,AC/CMP[1],- -,,-,A13,102,PB26,EIC/EXTINT[12],-,-,-,-,-,,SERCOM2/PAD[0],SERCOM4/PAD[1],-,TCC1/WO[2],-,-,-,-,-,-,-,- -,,-,B12,103,PB27,EIC/EXTINT[13],-,-,-,-,-,,SERCOM2/PAD[1],SERCOM4/PAD[0],-,TCC1/WO[3],-,-,-,-,-,-,-,- -,,-,A12,104,PB28,EIC/EXTINT[14],-,-,-,-,-,,SERCOM2/PAD[2],SERCOM4/PAD[2],-,TCC1/WO[4],-,-,-,I2S/SCK[1],-,-,-,- -,,-,B11,105,PB29,EIC/EXTINT[15],-,-,-,-,-,,SERCOM2/PAD[3],SERCOM4/PAD[3],-,TCC1/WO[5],-,-,-,I2S/MCK[1],-,-,-,- -,,82,A11,108,PC24,EIC/EXTINT[8],-,-,-,-,-,,SERCOM0/PAD[2],SERCOM2/PAD[2],-,-,-,CORTEX_CM4/TRACEDATA[3],-,-,-,-,-,- -,,83,B10,109,PC25,EIC/EXTINT[9],-,-,-,-,-,,SERCOM0/PAD[3],SERCOM2/PAD[3],-,-,-,CORTEX_CM4/TRACEDATA[2],-,-,-,-,-,- -,,84,A10,110,PC26,EIC/EXTINT[10],-,-,-,-,-,,-,-,-,-,-,CORTEX_CM4/TRACEDATA[1],-,-,-,-,-,- -,,85,A9,111,PC27,EIC/EXTINT[11],-,-,-,-,-,,SERCOM1/PAD[0],-,-,-,-,CORTEX_CM4/TRACECLK,-,-,-,-,CORTEX_M4/SWO,CCL/IN[4] -,,86,B9,112,PC28,EIC/EXTINT[12],-,-,-,-,-,,SERCOM1/PAD[1],-,-,-,-,CORTEX_CM4/TRACEDATA[0],-,-,-,-,-,CCL/IN[5] -39,51/B3,87,B8,113,PA27,EIC/EXTINT[11],-,-,-,-,-,X18/Y18,-,-,-,-,-,-,-,-,-,GCLK/IO[1],-, -40,52/B4,88,A8,114,RESET_N,-,-,-,-,-,-,,-,-,-,-,-,-,-,-,-,-,-,- -45,57/C5,93,B7,119,PA30,EIC/EXTINT[14],-,-,-,-,-,X19/Y19,SERCOM7/PAD[2],SERCOM1/PAD[2],TC6/WO[0],TCC2/WO[0],-,CORTEX_CM4/SWCLK,-,-,-,-,GCLK/IO[0],CCL/IN[3] -46,58/D5,94,B6,120,PA31,EIC/EXTINT[15],-,-,-,-,-,,SERCOM7/PAD[3],SERCOM1/PAD[3],TC6/WO[1],TCC2/WO[1],--,CORTEX_CM4/SWDIO,-,-,-,-,-,CCL/OUT[1] -,59/A6,95,A5,121,PB30,EIC/EXTINT[14],-,-,-,-,-,,SERCOM7/PAD[0],SERCOM5/PAD[1],TC0/WO[0],TCC4/WO[0],TCC0/WO[6],CORTEX_CM4/SWO,-,-,-,-,-,- -,60/B6,96,B5,122,PB31,EIC/EXTINT[15],-,-,-,-,-,,SERCOM7/PAD[1],SERCOM5/PAD[0],TC0/WO[1],TCC4/WO[1],TCC0/WO[7],,-,-,-,-,-,- -,,-,A4,123,PC30,EIC/EXTINT[14],-,-,ADC1/AIN[12],-,-,,-,-,-,-,-,-,-,-,-,-,-,- -,,-,B4,124,PC31,EIC/EXTINT[15],-,-,ADC1/AIN[13],-,-,,-,-,-,-,-,-,-,-,-,-,-,- -,61/A7,97,A3,125,PB00,EIC/EXTINT[0],-,ADC0/AIN[12],-,-,-,X30/Y30,-,SERCOM5/PAD[2],TC7/WO[0],-,-,-,-,-,-,-,-,CCL/IN[1] -,62/B7,98,B3,126,PB01,EIC/EXTINT[1],-,ADC0/AIN[13],-,-,-,X31/Y31,-,SERCOM5/PAD[3],TC7/WO[1],-,-,-,-,-,-,-,-,CCL/IN[2] +48,64,100,120,128,Pad Name,A,B1,B2,B3,B4,B5,B6,C,D,E,F,G,H,I,J,K,L,M,N +48,64/C6,100,B2,128,PB03,EIC/EXTINT[3],-,ADC0/AIN[15],-,-,-,X21/Y21,-,SERCOM5/PAD[1],TC6/WO[1],-,-,-,-,-,-,-,-,- +1,01/B8,1,A1,1,PA00,EIC/EXTINT[0],-,-,-,-,-,,-,SERCOM1/PAD[0],TC2/WO[0],-,-,-,-,-,-,-,-,- +2,02/C8,2,B1,2,PA01,EIC/EXTINT[1],-,-,-,-,-,,-,SERCOM1/PAD[1],TC2/WO[1],-,-,-,-,-,-,-,-,- +,,3,C1,3,PC00,EIC/EXTINT[0],-,-,ADC1/AIN[10],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +,,4,C2,4,PC01,EIC/EXTINT[1],-,-,ADC1/AIN[11],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +,,5,D1,7,PC02,EIC/EXTINT[2],-,-,ADC1/AIN[4],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +,,6,E2,8,PC03,EIC/EXTINT[3],-,-,ADC1/AIN[5],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +3,03/C7,7,E1,9,PA02,EIC/EXTINT[2],-,ADC0/AIN[0],-,-,DAC/VOUT[0],,-,-,-,-,-,-,-,-,-,-,-,- +4,04/D6,8,F2,10,PA03,EIC/EXTINT[3],ANAREF/VREFA,ADC0/AIN[1],-,-,-,X0/Y0,-,-,-,-,-,-,-,-,-,-,-,- +,05/D7,9,F1,11,PB04,EIC/EXTINT[4],-,-,ADC1/AIN[6],-,-,X22/Y22,-,-,-,-,-,-,-,-,-,-,-,- +,06/D8,10,G1,12,PB05,EIC/EXTINT[5],-,-,ADC1/AIN[7],-,-,X23/Y23,-,-,-,-,-,-,-,-,-,-,-,- +,,-,G2,13,PD00,EIC/EXTINT[0],-,-,ADC1/AIN[14],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +,,-,H1,16,PD01,EIC/EXTINT[1],-,-,ADC1/AIN[15],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +,09/E7,13,H2,17,PB06,EIC/EXTINT[6],-,-,ADC1/AIN[8],-,-,X24/Y24,-,-,-,-,-,-,-,-,-,-,-,CCL/IN[6] +,10/E6,14,J1,18,PB07,EIC/EXTINT[7],-,-,ADC1/AIN[9],-,-,X25/Y25,-,-,-,-,-,-,-,-,-,-,-,CCL/IN[7] +7,11/F5,15,J2,19,PB08,EIC/EXTINT[8],-,ADC0/AIN[2],ADC1/AIN[0],-,-,X1/Y1,-,SERCOM4/PAD[0],TC4/WO[0],-,-,-,-,-,-,-,-,CCL/IN[8] +8,12/F8,16,K1,20,PB09,EIC/EXTINT[9],-,ADC0/AIN[3],ADC1/AIN[1],-,-,X2/Y2,-,SERCOM4/PAD[1],TC4/WO[1],-,-,-,-,-,-,-,-,CCL/OUT[2] +9,13/F7,17,K2,21,PA04,EIC/EXTINT[4],ANAREF/VREFB,ADC0/AIN[4],-,AC/AIN[0],-,X3/Y3,-,SERCOM0/PAD[0],TC0/WO[0],-,-,-,-,-,-,-,-,CCL/IN[0] +10,14/F6,18,L1,22,PA05,EIC/EXTINT[5],-,ADC0/AIN[5],-,AC/AIN[1],DAC/VOUT[1],,-,SERCOM0/PAD[1],TC0/WO[1],-,-,-,-,-,-,-,-,CCL/IN[1] +11,15/G7,19,L2,23,PA06,EIC/EXTINT[6],ANAREF/VREFC,ADC0/AIN[6],-,AC/AIN[2],-,X4/Y4,-,SERCOM0/PAD[2],TC1/WO[0],-,-,-,SDHC0/SDCD,-,-,-,-,CCL/IN[2] +12,16/G8,20,M1,24,PA07,EIC/EXTINT[7],-,ADC0/AIN[7],-,AC/AIN[3],-,X5/Y5,-,SERCOM0/PAD[3],TC1/WO[1],-,-,-,SDHC0/SDWP,-,-,-,-,CCL/OUT[0] +,,-,N1,27,PC04,EIC/EXTINT[4],-,-,-,-,-,,SERCOM6/PAD[0],-,-,TCC0/WO[0],-,-,-,-,-,-,-,- +,,21,N2,28,PC05,EIC/EXTINT[5],-,-,-,-,-,,SERCOM6/PAD[1],-,-,-,-,-,-,-,-,-,-,- +,,22,P1,29,PC06,EIC/EXTINT[6],-,-,-,-,-,,SERCOM6/PAD[2],-,-,-,-,-,SDHC0/SDCD,-,-,-,-,- +,,23,P2,30,PC07,EIC/EXTINT[9],-,-,-,-,-,,SERCOM6/PAD[3],-,-,-,-,-,SDHC0/SDWP,-,-,-,-,- +13,17/H8,26,R1,33,PA08,EIC/NMI,-,ADC0/AIN[8],ADC1/AIN[2],-,-,X6/Y6,SERCOM0/PAD[0],SERCOM2/PAD[1],TC0/WO[0],TCC0/WO[0],TCC1/WO[4],QSPI/DATA[0],SDHC0/SDCMD,I2S/MCK[0],-,-,-,CCL/IN[3] +14,18/G6,27,P3,34,PA09,EIC/EXTINT[9],-,ADC0/AIN[9],ADC1/AIN[3],-,-,X7/Y7,SERCOM0/PAD[1],SERCOM2/PAD[0],TC0/WO[1],TCC0/WO[1],TCC1/WO[5],QSPI/DATA[1],SDHC0/SDDAT[0],I2S/FS[0],-,-,-,CCL/IN[4] +15,19/H7,28,R2,35,PA10,EIC/EXTINT[10],-,ADC0/-AIN[10],,-,-,X8/Y8,SERCOM0/PAD[2],SERCOM2/PAD[2],TC1/WO[0],TCC0/WO[2],TCC1/WO[6],QSPI/DATA[2],SDHC0/SDDAT[1],I2S/SCK[0],-,-,GCLK/IO[4],CCL/IN[5] +16,20/G5,29,P4,36,PA11,EIC/EXTINT[11],-,ADC0/-AIN[11],,-,-,X9/Y9,SERCOM0/PAD[3],SERCOM2/PAD[3],TC1/WO[1],TCC0/WO[3],TCC1/WO[7],QSPI/DATA[3],SDHC0/SDDAT[2],I2S/SDO,-,-,GCLK/IO[5],CCL/OUT[1] +19,23/H6,32,R3,39,PB10,EIC/EXTINT[10],-,-,-,-,-,,-,SERCOM4/PAD[2],TC5/WO[0],TCC0/WO[4],TCC1/WO[0],QSPI/SCK,SDHC0/SDDAT[3],I2S/SDI,-,-,GCLK/IO[4],CCL/IN[11] +20,24/G4,33,P5,40,PB11,EIC/EXTINT[11],-,-,-,-,-,,-,SERCOM4/PAD[3],TC5/WO[1],TCC0/WO[5],TCC1/WO[1],QSPI/CS,SDHC0/SDCK,I2S/FS[1],-,-,GCLK/IO[5],CCL/OUT[1] +,25/H5,34,R4,41,PB12,EIC/EXTINT[12],-,-,-,-,-,X26/Y26,SERCOM4/PAD[0],-,TC4/WO[0],TCC3/WO[0],TCC0/WO[0],CAN1/TX,SDHC0/SDCD,I2S/SCK[1],-,-,GCLK/IO[6],- +,26/H4,35,P6,42,PB13,EIC/EXTINT[13],-,-,-,-,-,X27/Y27,SERCOM4/PAD[1],-,TC4/WO[1],TCC3/WO[1],TCC0/WO[1],CAN1/RX,SDHC0/SDWP,I2S/MCK[1],-,-,GCLK/IO[7],- +,27/G3,36,R5,43,PB14,EIC/EXTINT[14],-,-,-,-,-,X28/Y28,SERCOM4/PAD[2],-,TC5/WO[0],TCC4/WO[0],TCC0/WO[2],CAN1/TX,-,-,PCC/DATA[8],GMAC/GMDC,GCLK/IO[0],CCL/IN[9] +,28/H3,37,P7,44,PB15,EIC/EXTINT[15],-,-,-,-,-,X29/Y29,SERCOM4/PAD[3],-,TC5/WO[1],TCC4/WO[1],TCC0/WO[3],CAN1/RX,-,-,PCC/DATA[9],GMAC/GMDIO,GCLK/IO[1],CCL/IN[10] +,,-,R6,47,PD08,EIC/EXTINT[3],-,-,-,-,-,,SERCOM7/PAD[0],SERCOM6/PAD[1],-,TCC0/WO[1],-,-,-,-,-,-,-,- +,,-,P8,48,PD09,EIC/EXTINT[4],-,-,-,-,-,,SERCOM7/PAD[1],SERCOM6/PAD[0],-,TCC0/WO[2],-,-,-,-,-,-,-,- +,,-,R7,49,PD10,EIC/EXTINT[5],-,-,-,-,-,,SERCOM7/PAD[2],SERCOM6/PAD[2],-,TCC0/WO[3],-,-,-,-,-,-,-,- +,,-,P9,50,PD11,EIC/EXTINT[6],-,-,-,-,-,,SERCOM7/PAD[3],SERCOM6/PAD[3],-,TCC0/WO[4],-,-,-,-,-,-,-,- +,,-,R8,51,PD12,EIC/EXTINT[7],-,-,-,-,-,,-,-,-,TCC0/WO[5],-,-,-,-,-,-,-,- +,,40,P10,52,PC10,EIC/EXTINT[10],-,-,-,-,-,,SERCOM6/PAD[2],SERCOM7/PAD[2],-,TCC0/WO[0],TCC1/WO[4],-,-,-,-,-,-,- +,,41,R9,55,PC11,EIC/EXTINT[11],-,-,-,-,-,,SERCOM6/PAD[3],SERCOM7/PAD[3],-,TCC0/WO[1],TCC1/WO[5],-,-,-,-,GMAC/GMDC,-,- +,,42,R10,56,PC12,EIC/EXTINT[12],-,-,-,-,-,,SERCOM7/PAD[0],SERCOM6/PAD[1],-,TCC0/WO[2],TCC1/WO[6],-,-,-,PCC/DATA[10],GMAC/GMDIO,-,- +,,43,P11,57,PC13,EIC/EXTINT[13],-,-,-,-,-,,SERCOM7/PAD[1],SERCOM6/PAD[0],-,TCC0/WO[3],TCC1/WO[7],-,-,-,PCC/DATA[11],-,-,- +,,44,R11,58,PC14,EIC/EXTINT[14],-,-,-,-,-,,SERCOM7/PAD[2],SERCOM6/PAD[2],-,TCC0/WO[4],TCC1/WO[0],-,-,-,PCC/DATA[12],GMAC/GRX[3],-,- +,,45,P12,59,PC15,EIC/EXTINT[15],-,-,-,-,-,,SERCOM7/PAD[3],SERCOM6/PAD[3],-,TCC0/WO[5],TCC1/WO[1],-,-,-,PCC/DATA[13],GMAC/GRX[2],-,- +21,29/F2,46,R12,60,PA12,EIC/EXTINT[12],-,-,-,-,-,,SERCOM2/PAD[0],SERCOM4/PAD[1],TC2/WO[0],TCC0/WO[6],TCC1/WO[2],-,SDHC0/SDCD,-,PCC/DEN1,GMAC/GRX[1],AC/CMP[0],- +22,30/G2,47,P13,61,PA13,EIC/EXTINT[13],-,-,-,-,-,,SERCOM2/PAD[1],SERCOM4/PAD[0],TC2/WO[1],TCC0/WO[7],TCC1/WO[3],-,SDHC0/SDWP,-,PCC/DEN2,GMAC/GRX[0],AC/CMP[1],- +23,31/H1,48,R13,62,PA14,EIC/EXTINT[14],-,-,-,-,-,,SERCOM2/PAD[2],SERCOM4/PAD[2],TC3/WO[0],TCC2/WO[0],TCC1/WO[2],-,-,-,PCC/CLK,GMAC/GTXCK,GCLK/IO[0],- +24,32/H2,49,R14,63,PA15,EIC/EXTINT[15],-,-,-,-,-,,SERCOM2/PAD[3],SERCOM4/PAD[3],TC3/WO[1],TCC2/WO[1],TCC1/WO[3],-,-,-,-,GMAC/GRXER,GCLK/IO[1],- +25,35/G1,52,R15,66,PA16,EIC/EXTINT[0],-,-,-,-,-,X10/Y10,SERCOM1/PAD[0],SERCOM3/PAD[1],TC2/WO[0],TCC1/WO[0],TCC0/WO[4],-,-,-,PCC/DATA[0],GMAC/GCRS/GRXDV(6),GCLK/IO[2],CCL/IN[0] +26,36/F1,53,P14,67,PA17,EIC/EXTINT[1],-,-,-,-,-,X11/Y11,SERCOM1/PAD[1],SERCOM3/PAD[0],TC2/WO[1],TCC1/WO[1],TCC0/WO[5],-,-,-,PCC/DATA[1],GMAC/GTXEN,GCLK/IO[3],CCL/IN[1] +27,37/E1,54,P15,68,PA18,EIC/EXTINT[2],-,-,-,-,-,X12/Y12,SERCOM1/PAD[2],SERCOM3/PAD[2],TC3/WO[0],TCC1/WO[2],TCC0/WO[6],-,-,-,PCC/DATA[2],GMAC/GTX[0],AC/CMP[0],CCL/IN[2] +28,38/E2,55,N14,69,PA19,EIC/EXTINT[3],-,-,-,-,-,X13/Y13,SERCOM1/PAD[3],SERCOM3/PAD[3],TC3/WO[1],TCC1/WO[3],TCC0/WO[7],-,-,-,PCC/DATA[3],GMAC/GTX[1],AC/CMP[1],CCL/OUT[0] +,,56,N15,70,PC16,EIC/EXTINT[0],-,-,-,-,-,,SERCOM6/PAD[0],SERCOM0/PAD[1],-,TCC0/WO[0],PDEC/PDEC[0],-,-,-,-,GMAC/GTX[2],-,- +,,57,M14,71,PC17,EIC/EXTINT[1],-,-,-,-,-,,SERCOM6/PAD[1],SERCOM0/PAD[0],-,TCC0/WO[1],PDEC/PDEC[1],-,-,-,-,GMAC/GTX[3],-,- +,,58,M15,72,PC18,EIC/EXTINT[2],-,-,-,-,-,,SERCOM6/PAD[2],SERCOM0/PAD[2],-,TCC0/WO[2],PDEC/PDEC[2],-,-,-,-,GMAC/GRXCK,-,- +,,59,L14,73,PC19,EIC/EXTINT[3],-,-,-,-,-,,SERCOM6/PAD[3],SERCOM0/PAD[3],-,TCC0/WO[3],-,-,-,-,-,GMAC/GTXER,-,- +,,60,L15,74,PC20,EIC/EXTINT[4],-,-,-,-,-,,-,-,-,TCC0/WO[4],-,-,SDHC1/SDCD,-,-,GMAC/GRXDV,-,CCL/IN[9] +,,61,K14,75,PC21,EIC/EXTINT[5],-,-,-,-,-,,-,-,-,TCC0/WO[5],-,-,SDHC1/SDWP,-,-,GMAC/GCOL,-,CCL/IN[10] +,,-,K15,76,PC22,EIC/EXTINT[6],-,-,-,-,-,,SERCOM1/PAD[0],SERCOM3/PAD[1],-,TCC0/WO[6],-,-,-,-,-,GMAC/GMDC,-,- +,,-,J14,77,PC23,EIC/EXTINT[7],-,-,-,-,-,,SERCOM1/PAD[1],SERCOM3/PAD[0],-,TCC0/WO[7],-,-,-,-,-,GMAC/GMDIO,-,- +,,-,J15,80,PD20,EIC/EXTINT[10],-,-,-,-,-,,SERCOM1/PAD[2],SERCOM3/PAD[2],-,TCC1/WO[0],-,-,SDHC1/SDCD,-,-,-,-,- +,,-,H14,81,PD21,EIC/EXTINT[11],-,-,-,-,-,,SERCOM1/PAD[3],SERCOM3/PAD[3],-,TCC1/WO[1],-,-,SDHC1/SDWP,-,-,-,-,- +,39/D4,64,H15,82,PB16,EIC/EXTINT[0],-,-,-,-,-,,SERCOM5/PAD[0],-,TC6/WO[0],TCC3/WO[0],TCC0/WO[4],-,SDHC1/SDCD,I2S/SCK[0],-,-,GCLK/IO[2],CCL/IN[11] +,40/D1,65,G15,83,PB17,EIC/EXTINT[1],-,-,-,-,-,,SERCOM5/PAD[1],-,TC6/WO[1],TCC3/WO[1],TCC0/WO[5],-,SDHC1/SDWP,I2S/MCK[0],-,-,GCLK/IO[3],CCL/OUT[3] +,,66,G14,84,PB18,EIC/EXTINT[2],-,-,-,-,-,,SERCOM5/PAD[2],SERCOM7/PAD[2],-,TCC1/WO[0],PDEC/PDEC[0],-,SDHC1/SDDAT[0],-,-,-,GCLK/IO[4],- +,,67,F15,85,PB19,EIC/EXTINT[3],-,-,-,-,-,,SERCOM5/PAD[3],SERCOM7/PAD[3],-,TCC1/WO[1],PDEC/PDEC[1],-,SDHC1/SDDAT[1],-,-,-,GCLK/IO[5],- +,,68,F14,86,PB20,EIC/EXTINT[4],-,-,-,-,-,,SERCOM3/PAD[0],SERCOM7/PAD[1],-,TCC1/WO[2],PDEC/PDEC[2],-,SDHC1/SDDAT[2],-,-,-,GCLK/IO[6],- +,,69,E15,87,PB21,EIC/EXTINT[5],-,-,-,-,-,,SERCOM3/PAD[1],SERCOM7/PAD[0],-,TCC1/WO[3],-,-,SDHC1/SDDAT[3],-,-,-,GCLK/IO[7],- +29,41/D2,70,E14,88,PA20,EIC/EXTINT[4],-,-,-,-,-,X14/Y14,SERCOM5/PAD[2],SERCOM3/PAD[2],TC7/WO[0],TCC1/WO[4],TCC0/WO[0],-,SDHC1/SDCMD,I2S/FS[0],PCC/DATA[4],GMAC/GMDC,-,- +30,42/D3,71,D15,89,PA21,EIC/EXTINT[5],-,-,-,-,-,X15/Y15,SERCOM5/PAD[3],SERCOM3/PAD[3],TC7/WO[1],TCC1/WO[5],TCC0/WO[1],-,SDHC1/SDCK,I2S/SDO,PCC/DATA[5],GMAC/GMDIO,-,- +31,43/C1,72,D14,92,PA22,EIC/EXTINT[6],-,-,-,-,-,X16/Y16,SERCOM3/PAD[0],SERCOM5/PAD[1],TC4/WO[0],TCC1/WO[6],TCC0/WO[2],-,CAN0/TX,I2S/SDI,PCC/DATA[6],-,-,CCL/IN[6] +32,44/C2,73,C14,93,PA23,EIC/EXTINT[7],-,-,-,-,-,X17/Y17,SERCOM3/PAD[1],SERCOM5/PAD[0],TC4/WO[1],TCC1/WO[7],TCC0/WO[3],USB/SOF_1KHZ,CAN0/RX,I2S/FS[1],PCC/DATA[7],-,-,CCL/IN[7] +33,45/B1,74,C15,94,PA24,EIC/EXTINT[8],-,-,-,-,-,,SERCOM3/PAD[2],SERCOM5/PAD[2],TC5/WO[0],TCC2/WO[2],PDEC/PDEC[0],USB/DM,CAN0/TX,-,-,-,-,CCL/IN[8] +34,46/A1,75,B15,95,PA25,EIC/EXTINT[9],-,-,-,-,-,,SERCOM3/PAD[3],SERCOM5/PAD[3],TC5/WO[1],-,PDEC/PDEC[1],USB/DP,CAN0/RX,-,-,-,-,CCL/OUT[2] +37,49/A2,78,A15,98,PB22,EIC/EXTINT[6],-,-,-,-,-,,SERCOM1/PAD[2],SERCOM5/PAD[2],TC7/WO[0],-,PDEC/PDEC[2],USB/SOF_1KHZ,-,-,-,-,GCLK/IO[0],CCL/IN[0] +38,50/A3,79,A14,99,PB23,EIC/EXTINT[7],-,-,-,-,-,,SERCOM1/PAD[3],SERCOM5/PAD[3],TC7/WO[1],-,PDEC/PDEC[0],-,-,-,-,-,GCLK/IO[1],CCL/OUT[0] +,,80,B14,100,PB24,EIC/EXTINT[8],-,-,-,-,-,,SERCOM0/PAD[0],SERCOM2/PAD[1],-,-,PDEC/PDEC[1],-,-,-,-,-,AC/CMP[0],- +,,81,B13,101,PB25,EIC/EXTINT[9],-,-,-,-,-,,SERCOM0/PAD[1],SERCOM2/PAD[0],-,-,PDEC/PDEC[2],-,-,-,-,-,AC/CMP[1],- +,,-,A13,102,PB26,EIC/EXTINT[12],-,-,-,-,-,,SERCOM2/PAD[0],SERCOM4/PAD[1],-,TCC1/WO[2],-,-,-,-,-,-,-,- +,,-,B12,103,PB27,EIC/EXTINT[13],-,-,-,-,-,,SERCOM2/PAD[1],SERCOM4/PAD[0],-,TCC1/WO[3],-,-,-,-,-,-,-,- +,,-,A12,104,PB28,EIC/EXTINT[14],-,-,-,-,-,,SERCOM2/PAD[2],SERCOM4/PAD[2],-,TCC1/WO[4],-,-,-,I2S/SCK[1],-,-,-,- +,,-,B11,105,PB29,EIC/EXTINT[15],-,-,-,-,-,,SERCOM2/PAD[3],SERCOM4/PAD[3],-,TCC1/WO[5],-,-,-,I2S/MCK[1],-,-,-,- +,,82,A11,108,PC24,EIC/EXTINT[8],-,-,-,-,-,,SERCOM0/PAD[2],SERCOM2/PAD[2],-,-,-,CORTEX_CM4/TRACEDATA[3],-,-,-,-,-,- +,,83,B10,109,PC25,EIC/EXTINT[9],-,-,-,-,-,,SERCOM0/PAD[3],SERCOM2/PAD[3],-,-,-,CORTEX_CM4/TRACEDATA[2],-,-,-,-,-,- +,,84,A10,110,PC26,EIC/EXTINT[10],-,-,-,-,-,,-,-,-,-,-,CORTEX_CM4/TRACEDATA[1],-,-,-,-,-,- +,,85,A9,111,PC27,EIC/EXTINT[11],-,-,-,-,-,,SERCOM1/PAD[0],-,-,-,-,CORTEX_CM4/TRACECLK,-,-,-,-,CORTEX_M4/SWO,CCL/IN[4] +,,86,B9,112,PC28,EIC/EXTINT[12],-,-,-,-,-,,SERCOM1/PAD[1],-,-,-,-,CORTEX_CM4/TRACEDATA[0],-,-,-,-,-,CCL/IN[5] +39,51/B3,87,B8,113,PA27,EIC/EXTINT[11],-,-,-,-,-,X18/Y18,-,-,-,-,-,-,-,-,-,GCLK/IO[1],-, +40,52/B4,88,A8,114,RESET_N,-,-,-,-,-,-,,-,-,-,-,-,-,-,-,-,-,-,- +45,57/C5,93,B7,119,PA30,EIC/EXTINT[14],-,-,-,-,-,X19/Y19,SERCOM7/PAD[2],SERCOM1/PAD[2],TC6/WO[0],TCC2/WO[0],-,CORTEX_CM4/SWCLK,-,-,-,-,GCLK/IO[0],CCL/IN[3] +46,58/D5,94,B6,120,PA31,EIC/EXTINT[15],-,-,-,-,-,,SERCOM7/PAD[3],SERCOM1/PAD[3],TC6/WO[1],TCC2/WO[1],--,CORTEX_CM4/SWDIO,-,-,-,-,-,CCL/OUT[1] +,59/A6,95,A5,121,PB30,EIC/EXTINT[14],-,-,-,-,-,,SERCOM7/PAD[0],SERCOM5/PAD[1],TC0/WO[0],TCC4/WO[0],TCC0/WO[6],CORTEX_CM4/SWO,-,-,-,-,-,- +,60/B6,96,B5,122,PB31,EIC/EXTINT[15],-,-,-,-,-,,SERCOM7/PAD[1],SERCOM5/PAD[0],TC0/WO[1],TCC4/WO[1],TCC0/WO[7],,-,-,-,-,-,- +,,-,A4,123,PC30,EIC/EXTINT[14],-,-,ADC1/AIN[12],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +,,-,B4,124,PC31,EIC/EXTINT[15],-,-,ADC1/AIN[13],-,-,,-,-,-,-,-,-,-,-,-,-,-,- +,61/A7,97,A3,125,PB00,EIC/EXTINT[0],-,ADC0/AIN[12],-,-,-,X30/Y30,-,SERCOM5/PAD[2],TC7/WO[0],-,-,-,-,-,-,-,-,CCL/IN[1] +,62/B7,98,B3,126,PB01,EIC/EXTINT[1],-,ADC0/AIN[13],-,-,-,X31/Y31,-,SERCOM5/PAD[3],TC7/WO[1],-,-,-,-,-,-,-,-,CCL/IN[2] 47,63/A8,99,A2,127,PB02,EIC/EXTINT[2],-,ADC0/AIN[14],-,-,-,X20/Y20,-,SERCOM5/PAD[0],TC6/WO[0],TCC2/WO[2],-,-,-,-,-,-,-,CCL/OUT[0] \ No newline at end of file diff --git a/extras/wirepinmux/SAMD51_Table_6-8.csv b/extras/wirepinmux/SAMD51_Table_6-8.csv index 85923703f..a13b7c6bc 100644 --- a/extras/wirepinmux/SAMD51_Table_6-8.csv +++ b/extras/wirepinmux/SAMD51_Table_6-8.csv @@ -1,10 +1,10 @@ -Device,Supply,I/O Pin -128,VDDIOB, PA08, PA09 -128,VDDIO,PA12, PA13, PA16, PA17, PA22, PA23, PD08, PD09 -120,VDDIOB, PA08, PA09 -120,VDDIO, PA12, PA13, PA16, PA17, PA22, PA23, PD08, PD09 -100,VDDIOB, PA08, PA09 -100,VDDIO, PA12, PA13, PA16, PA17, PA22, PA23 -64,VDDIOB, PA08, PA09 -64,VDDIO, PA12, PA13, PA16, PA17, PA22, PA23 +Device,Supply,I/O Pin +128,VDDIOB, PA08, PA09 +128,VDDIO,PA12, PA13, PA16, PA17, PA22, PA23, PD08, PD09 +120,VDDIOB, PA08, PA09 +120,VDDIO, PA12, PA13, PA16, PA17, PA22, PA23, PD08, PD09 +100,VDDIOB, PA08, PA09 +100,VDDIO, PA12, PA13, PA16, PA17, PA22, PA23 +64,VDDIOB, PA08, PA09 +64,VDDIO, PA12, PA13, PA16, PA17, PA22, PA23 48,VDDIO, PA08, PA09, PA12, PA13, PA16, PA17, PA22, PA23 \ No newline at end of file diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index e8df6a7e0..5768e0069 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -132,7 +132,7 @@ SPIClass::SPIClass(SERCOM *p_sercom, uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint // SERCOM pads _padTx=PadTx; _padRx=PadRx; - + // Transaction pool initialization txnPoolHead = 0; } @@ -383,7 +383,7 @@ void SPIClass::onService(void) if (flags & SERCOM_SPI_INTFLAG_RXC) { // Read completes after write, so read previous byte bool hasMore = _p_sercom->readDataSPI(); - + if (!hasMore) { _p_sercom->disableInterrupts(SERCOM_SPI_INTENCLR_DRE | SERCOM_SPI_INTENCLR_RXC | SERCOM_SPI_INTENCLR_ERROR); _p_sercom->setReturnValueSPI(SercomSpiError::SUCCESS); diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index c2e33d9bc..ef99abb73 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -165,12 +165,12 @@ class SPIClass { volatile bool txnDone = false; volatile int txnStatus = 0; - + // Transaction pool for async operations (matches SERCOM queue depth) static constexpr size_t TXN_POOL_SIZE = 8; SercomTxn txnPool[TXN_POOL_SIZE]; uint8_t txnPoolHead; - + SercomTxn* allocateTxn(); static void onTxnComplete(void* user, int status); }; diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index ea596d032..15d7a38d2 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -124,7 +124,7 @@ uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit, uin if(quantity == 0) return 0; - loader = SercomTxn{}; + loader = SercomTxn{}; if (rxBuffer != nullptr) { loader.rxPtr = rxBuffer; @@ -212,7 +212,7 @@ uint8_t TwoWire::endTransmission(bool stopBit, void (*onComplete)(void* user, in SercomTxn* txn = allocateTxn(); *txn = loader; // Copy staged transaction data txn->chainNext = false; - + // Set parameters that weren't known during beginTransmission/write txn->config = stopBit ? I2C_CFG_STOP : 0; if (onComplete) { diff --git a/libraries/Wire/examples/slave_receiver/slave_receiver.ino b/libraries/Wire/examples/slave_receiver/slave_receiver.ino index 5660ac678..b1430051b 100644 --- a/libraries/Wire/examples/slave_receiver/slave_receiver.ino +++ b/libraries/Wire/examples/slave_receiver/slave_receiver.ino @@ -29,7 +29,7 @@ void loop() void receiveEvent(int howMany) { (void)howMany; // avoid compiler warning about unused parameter - + while(1 < Wire.available()) // loop through all but the last { char c = Wire.read(); // receive byte as a character diff --git a/variants/mkrgsm1400/variant.cpp b/variants/mkrgsm1400/variant.cpp index d137b4aab..3310aaabb 100644 --- a/variants/mkrgsm1400/variant.cpp +++ b/variants/mkrgsm1400/variant.cpp @@ -151,7 +151,7 @@ const PinDescription g_APinDescription[] = { { PORTA, 14, PIO_SERCOM_ALT, (PIN_ATTR_DIGITAL ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // SS: as GPIO { PORTA, 15, PIO_SERCOM_ALT, (PIN_ATTR_DIGITAL ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // MISO: SERCOM4/PAD[3] { PORTA, 27, PIO_DIGITAL, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 }, - + { PORTB, 8, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG ), ADC_Channel2, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, { PORTB, 9, PIO_ANALOG, (PIN_ATTR_PWM|PIN_ATTR_TIMER ), ADC_Channel3, PWM4_CH1, TC4_CH1, EXTERNAL_INT_9 }, @@ -194,7 +194,7 @@ static inline void enable_battery_charging() { txn.address = PMIC_ADDRESS; txn.length = 2; txn.txPtr = txData; - + PERIPH_WIRE.enqueueWIRE(&txn); PERIPH_WIRE.startTransmissionWIRE(); } @@ -217,7 +217,7 @@ static inline void disable_battery_fet(bool disabled) { txn.address = PMIC_ADDRESS; txn.length = 2; txn.txPtr = txData; - + PERIPH_WIRE.enqueueWIRE(&txn); PERIPH_WIRE.startTransmissionWIRE(); } diff --git a/variants/mkrnb1500/variant.cpp b/variants/mkrnb1500/variant.cpp index 0a45fe372..197dda414 100644 --- a/variants/mkrnb1500/variant.cpp +++ b/variants/mkrnb1500/variant.cpp @@ -151,7 +151,7 @@ const PinDescription g_APinDescription[] = { { PORTA, 14, PIO_SERCOM_ALT, (PIN_ATTR_DIGITAL ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // SS: as GPIO { PORTA, 15, PIO_SERCOM_ALT, (PIN_ATTR_DIGITAL ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // MISO: SERCOM4/PAD[3] { PORTA, 27, PIO_DIGITAL, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 }, - + { PORTB, 8, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG ), ADC_Channel2, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, { PORTB, 9, PIO_ANALOG, (PIN_ATTR_PWM|PIN_ATTR_TIMER ), ADC_Channel3, PWM4_CH1, TC4_CH1, EXTERNAL_INT_9 }, @@ -180,7 +180,7 @@ SERCOM sercom5(SERCOM5); #define PMIC_REG01 0x01 #define PMIC_REG07 0x07 -static inline void enable_battery_charging() { +static inline void enable_battery_charging() { PERIPH_WIRE.initMasterWIRE(100000); PERIPH_WIRE.enableWIRE(); pinPeripheral(PIN_WIRE_SDA, g_APinDescription[PIN_WIRE_SDA].ulPinType); @@ -194,7 +194,7 @@ static inline void enable_battery_charging() { txn.address = PMIC_ADDRESS; txn.length = 2; txn.txPtr = txData; - + PERIPH_WIRE.enqueueWIRE(&txn); PERIPH_WIRE.startTransmissionWIRE(); } @@ -211,13 +211,13 @@ static inline void disable_battery_fet(bool disabled) { // No D+/D– detection + Safety timer not slowed by 2X during input DPM or thermal regulation + // BAT fet disabled/enabled + charge and bat fault INT txData[1] = 0x0B | (disabled ? 0x20 : 0x00); - + txn = SercomTxn{}; txn.config = I2C_CFG_STOP; txn.address = PMIC_ADDRESS; txn.length = 2; txn.txPtr = txData; - + PERIPH_WIRE.enqueueWIRE(&txn); PERIPH_WIRE.startTransmissionWIRE(); } diff --git a/variants/mkrwifi1010/variant.cpp b/variants/mkrwifi1010/variant.cpp index 7634df70a..29f9dcc1a 100644 --- a/variants/mkrwifi1010/variant.cpp +++ b/variants/mkrwifi1010/variant.cpp @@ -151,7 +151,7 @@ const PinDescription g_APinDescription[] = { { PORTA, 14, PIO_DIGITAL, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // SS: as GPIO { PORTA, 15, PIO_SERCOM_ALT, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // MISO: SERCOM4/PAD[3] { PORTA, 27, PIO_DIGITAL, (PIN_ATTR_NONE ), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 }, - + { PORTB, 8, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_ANALOG ), ADC_Channel2, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, { PORTB, 9, PIO_ANALOG, (PIN_ATTR_PWM|PIN_ATTR_TIMER ), ADC_Channel3, PWM4_CH1, TC4_CH1, EXTERNAL_INT_9 }, @@ -194,7 +194,7 @@ static inline void enable_battery_charging() { txn.address = PMIC_ADDRESS; txn.length = 2; txn.txPtr = txData; - + PERIPH_WIRE.enqueueWIRE(&txn); PERIPH_WIRE.startTransmissionWIRE(); } @@ -211,13 +211,13 @@ static inline void disable_battery_fet(bool disabled) { // No D+/D– detection + Safety timer not slowed by 2X during input DPM or thermal regulation + // BAT fet disabled/enabled + charge and bat fault INT txData[1] = 0x0B | (disabled ? 0x20 : 0x00); - + txn = SercomTxn{}; txn.config = I2C_CFG_STOP; txn.address = PMIC_ADDRESS; txn.length = 2; txn.txPtr = txData; - + PERIPH_WIRE.enqueueWIRE(&txn); PERIPH_WIRE.startTransmissionWIRE(); } From f4dc420b4bc0bed5b9c5663364da5633152b62be Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sun, 21 Jun 2026 17:37:44 -0400 Subject: [PATCH 41/79] Route SERCOM deferred services through shared PendSV --- cores/arduino/SERCOM.cpp | 45 ++++++++++++++++++---------------------- cores/arduino/SERCOM.h | 2 +- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 724b2a4b2..9744814bb 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -20,6 +20,7 @@ #include "SERCOM.h" #include "variant.h" #include "Arduino.h" +#include "PendSV.h" #ifdef USE_ZERODMA #include @@ -1053,7 +1054,6 @@ static const SercomData sercomData[] = { std::array SERCOM::s_states = {}; std::array SERCOM::s_instances = {}; -volatile uint32_t SERCOM::s_pendingMask = 0; bool SERCOM::claim(uint8_t sercomId, Role role) { @@ -1076,10 +1076,10 @@ void SERCOM::release(uint8_t sercomId) SercomState &state = s_states[sercomId]; state.role = Role::None; state.service = nullptr; + PendSV::instance().clearService(sercomId); #ifdef SERCOM_STRICT_PADS clearPads(sercomId); #endif // SERCOM_STRICT_PADS - s_pendingMask &= ~(1u << sercomId); } bool SERCOM::registerService(uint8_t sercomId, ServiceFn fn) @@ -1088,6 +1088,12 @@ bool SERCOM::registerService(uint8_t sercomId, ServiceFn fn) return false; s_states[sercomId].service = fn; + if (!PendSV::instance().registerService(sercomId, &SERCOM::dispatchService, nullptr)) + { + s_states[sercomId].service = nullptr; + return false; + } + return true; } @@ -1406,37 +1412,26 @@ void SERCOM::setPending(uint8_t sercomId) if (sercomId >= kSercomCount) return; - __disable_irq(); - s_pendingMask |= (1u << sercomId); - __enable_irq(); - __DMB(); - SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; + PendSV::instance().setPending(sercomId); } -void SERCOM::dispatchPending(void) +void SERCOM::dispatchService(uint8_t sercomId, void *context) { - uint32_t pending; - - __disable_irq(); - pending = s_pendingMask; - s_pendingMask = 0; - __enable_irq(); + (void)context; - for (size_t i = 0; i < kSercomCount; ++i) - { - if ((pending & (1u << i)) == 0) - continue; + if (sercomId >= kSercomCount) + return; - ServiceFn fn = s_states[i].service; - SERCOM* inst = s_instances[i]; - if (fn && inst) - (inst->*fn)(); - } + ServiceFn fn = s_states[sercomId].service; + SERCOM* inst = s_instances[sercomId]; + if (fn && inst) + (inst->*fn)(); } -extern "C" void PendSV_Handler(void) +void SERCOM::dispatchPending(void) { - SERCOM::dispatchPending(); + for (uint8_t i = 0; i < kSercomCount; ++i) + dispatchService(i, nullptr); } int8_t SERCOM::getSercomIndex(void) { diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index 0669216fa..b6fa0a241 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -326,6 +326,7 @@ class SERCOM static void release(uint8_t sercomId); static bool registerService(uint8_t sercomId, ServiceFn fn); static void setPending(uint8_t sercomId); + static void dispatchService(uint8_t sercomId, void *context); static void dispatchPending(void); #ifdef USE_ZERODMA @@ -410,7 +411,6 @@ class SERCOM static std::array s_states; static std::array s_instances; - static volatile uint32_t s_pendingMask; uint8_t calculateBaudrateSynchronous(uint32_t baudrate) ; uint32_t division(uint32_t dividend, uint32_t divisor) ; void initClockNVIC( void ) ; From 8cfeeaf9001d3fdaa63950d3ff71606a88d0e3ec Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Tue, 23 Jun 2026 22:16:10 -0400 Subject: [PATCH 42/79] Add PendSV peripheral channel map --- cores/arduino/PendSV.h | 87 +++++++++++++++++++++++++++++++- cores/arduino/PendSVChannelMap.h | 57 +++++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 cores/arduino/PendSVChannelMap.h diff --git a/cores/arduino/PendSV.h b/cores/arduino/PendSV.h index ab22a43e6..36771a4df 100644 --- a/cores/arduino/PendSV.h +++ b/cores/arduino/PendSV.h @@ -1,14 +1,99 @@ #pragma once +#include "PendSVChannelMap.h" +#include "sam.h" + #include #include +#if defined(SERCOM0) || defined(SERCOM0_REGS) +#define PENDSV_HAS_SERCOM0 true +#else +#define PENDSV_HAS_SERCOM0 false +#endif +#if defined(SERCOM1) || defined(SERCOM1_REGS) +#define PENDSV_HAS_SERCOM1 true +#else +#define PENDSV_HAS_SERCOM1 false +#endif +#if defined(SERCOM2) || defined(SERCOM2_REGS) +#define PENDSV_HAS_SERCOM2 true +#else +#define PENDSV_HAS_SERCOM2 false +#endif +#if defined(SERCOM3) || defined(SERCOM3_REGS) +#define PENDSV_HAS_SERCOM3 true +#else +#define PENDSV_HAS_SERCOM3 false +#endif +#if defined(SERCOM4) || defined(SERCOM4_REGS) +#define PENDSV_HAS_SERCOM4 true +#else +#define PENDSV_HAS_SERCOM4 false +#endif +#if defined(SERCOM5) || defined(SERCOM5_REGS) +#define PENDSV_HAS_SERCOM5 true +#else +#define PENDSV_HAS_SERCOM5 false +#endif +#if defined(SERCOM6) || defined(SERCOM6_REGS) +#define PENDSV_HAS_SERCOM6 true +#else +#define PENDSV_HAS_SERCOM6 false +#endif +#if defined(SERCOM7) || defined(SERCOM7_REGS) +#define PENDSV_HAS_SERCOM7 true +#else +#define PENDSV_HAS_SERCOM7 false +#endif + +#if defined(USB) || defined(USB_REGS) +#define PENDSV_HAS_USB true +#else +#define PENDSV_HAS_USB false +#endif + +#if defined(GMAC) || defined(GMAC_REGS) +#define PENDSV_HAS_GMAC true +#else +#define PENDSV_HAS_GMAC false +#endif +#if defined(ADC) || defined(ADC_REGS) || defined(ADC0) || defined(ADC0_REGS) +#define PENDSV_HAS_ADC true +#else +#define PENDSV_HAS_ADC false +#endif +#if defined(AES) || defined(AES_REGS) +#define PENDSV_HAS_AES true +#else +#define PENDSV_HAS_AES false +#endif +#if defined(PUKCC) || defined(PUKCC_REGS) || defined(ID_PUKCC) || \ + defined(PUKCC_INSTANCE_ID) || defined(PUKCC_IRQn) +#define PENDSV_HAS_PUKCC true +#else +#define PENDSV_HAS_PUKCC false +#endif +#if defined(TRNG) || defined(TRNG_REGS) +#define PENDSV_HAS_TRNG true +#else +#define PENDSV_HAS_TRNG false +#endif + +using PendSVChannels = PendSVChannelMap< + PENDSV_HAS_SERCOM0, PENDSV_HAS_SERCOM1, PENDSV_HAS_SERCOM2, + PENDSV_HAS_SERCOM3, PENDSV_HAS_SERCOM4, PENDSV_HAS_SERCOM5, + PENDSV_HAS_SERCOM6, PENDSV_HAS_SERCOM7, PENDSV_HAS_USB, PENDSV_HAS_GMAC, + PENDSV_HAS_ADC, PENDSV_HAS_AES, PENDSV_HAS_PUKCC, PENDSV_HAS_TRNG>; + class PendSV { public: using ServiceFn = void (*)(uint8_t serviceId, void *context); - static constexpr uint8_t kMaxServices = 32; + static constexpr uint8_t kMaxServices = PendSVChannels::kMaxServices; static constexpr uint8_t kDispatchBudget = 16; static_assert(kMaxServices <= 32, "PendSV pending mask supports at most 32 services"); + static_assert(PendSVChannels::Count <= kMaxServices, + "PendSV channel allocation exceeds dispatcher capacity"); static PendSV &instance(); diff --git a/cores/arduino/PendSVChannelMap.h b/cores/arduino/PendSVChannelMap.h new file mode 100644 index 000000000..b97b2b2db --- /dev/null +++ b/cores/arduino/PendSVChannelMap.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +template +struct PendSVChannelMap { + static constexpr uint8_t kMaxServices = 32; + static constexpr uint8_t kUnavailable = 0xFF; + +private: + static constexpr uint8_t assign(bool present, uint8_t next) { + return present ? next : kUnavailable; + } + + static constexpr uint8_t advance(bool present, uint8_t next) { + return present ? static_cast(next + 1) : next; + } + + static constexpr uint8_t kBase = 0; + static constexpr uint8_t kAfterSercom0 = advance(HasSercom0, kBase); + static constexpr uint8_t kAfterSercom1 = advance(HasSercom1, kAfterSercom0); + static constexpr uint8_t kAfterSercom2 = advance(HasSercom2, kAfterSercom1); + static constexpr uint8_t kAfterSercom3 = advance(HasSercom3, kAfterSercom2); + static constexpr uint8_t kAfterSercom4 = advance(HasSercom4, kAfterSercom3); + static constexpr uint8_t kAfterSercom5 = advance(HasSercom5, kAfterSercom4); + static constexpr uint8_t kAfterSercom6 = advance(HasSercom6, kAfterSercom5); + static constexpr uint8_t kAfterSercom7 = advance(HasSercom7, kAfterSercom6); + static constexpr uint8_t kAfterUsb = advance(HasUsb, kAfterSercom7); + static constexpr uint8_t kAfterGmac = advance(HasGmac, kAfterUsb); + static constexpr uint8_t kAfterAdc = advance(HasAdc, kAfterGmac); + static constexpr uint8_t kAfterAes = advance(HasAes, kAfterAdc); + static constexpr uint8_t kAfterPukcc = advance(HasPukcc, kAfterAes); + +public: + static constexpr uint8_t Sercom0 = assign(HasSercom0, kBase); + static constexpr uint8_t Sercom1 = assign(HasSercom1, kAfterSercom0); + static constexpr uint8_t Sercom2 = assign(HasSercom2, kAfterSercom1); + static constexpr uint8_t Sercom3 = assign(HasSercom3, kAfterSercom2); + static constexpr uint8_t Sercom4 = assign(HasSercom4, kAfterSercom3); + static constexpr uint8_t Sercom5 = assign(HasSercom5, kAfterSercom4); + static constexpr uint8_t Sercom6 = assign(HasSercom6, kAfterSercom5); + static constexpr uint8_t Sercom7 = assign(HasSercom7, kAfterSercom6); + static constexpr uint8_t Usb = assign(HasUsb, kAfterSercom7); + static constexpr uint8_t Gmac = assign(HasGmac, kAfterUsb); + static constexpr uint8_t Adc = assign(HasAdc, kAfterGmac); + static constexpr uint8_t Aes = assign(HasAes, kAfterAdc); + static constexpr uint8_t Pukcc = assign(HasPukcc, kAfterAes); + static constexpr uint8_t Trng = assign(HasTrng, kAfterPukcc); + static constexpr uint8_t Count = advance(HasTrng, kAfterPukcc); + + static constexpr bool isAvailable(uint8_t channel) { + return channel != kUnavailable; + } +}; From 1a13c8f7b2a89565ac569bf9267b0054ee49d188 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 26 Jun 2026 14:13:01 -0400 Subject: [PATCH 43/79] Add PHY PendSV channel allocation --- cores/arduino/PendSV.h | 5 ++++- cores/arduino/PendSVChannelMap.h | 10 ++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/cores/arduino/PendSV.h b/cores/arduino/PendSV.h index 36771a4df..b1ca59a90 100644 --- a/cores/arduino/PendSV.h +++ b/cores/arduino/PendSV.h @@ -55,8 +55,10 @@ #if defined(GMAC) || defined(GMAC_REGS) #define PENDSV_HAS_GMAC true +#define PENDSV_HAS_PHY true #else #define PENDSV_HAS_GMAC false +#define PENDSV_HAS_PHY false #endif #if defined(ADC) || defined(ADC_REGS) || defined(ADC0) || defined(ADC0_REGS) #define PENDSV_HAS_ADC true @@ -84,7 +86,8 @@ using PendSVChannels = PendSVChannelMap< PENDSV_HAS_SERCOM0, PENDSV_HAS_SERCOM1, PENDSV_HAS_SERCOM2, PENDSV_HAS_SERCOM3, PENDSV_HAS_SERCOM4, PENDSV_HAS_SERCOM5, PENDSV_HAS_SERCOM6, PENDSV_HAS_SERCOM7, PENDSV_HAS_USB, PENDSV_HAS_GMAC, - PENDSV_HAS_ADC, PENDSV_HAS_AES, PENDSV_HAS_PUKCC, PENDSV_HAS_TRNG>; + PENDSV_HAS_PHY, PENDSV_HAS_ADC, PENDSV_HAS_AES, PENDSV_HAS_PUKCC, + PENDSV_HAS_TRNG>; class PendSV { public: diff --git a/cores/arduino/PendSVChannelMap.h b/cores/arduino/PendSVChannelMap.h index b97b2b2db..fcc6864f8 100644 --- a/cores/arduino/PendSVChannelMap.h +++ b/cores/arduino/PendSVChannelMap.h @@ -4,8 +4,8 @@ template + bool HasUsb, bool HasGmac, bool HasPhy, bool HasAdc, bool HasAes, + bool HasPukcc, bool HasTrng> struct PendSVChannelMap { static constexpr uint8_t kMaxServices = 32; static constexpr uint8_t kUnavailable = 0xFF; @@ -30,7 +30,8 @@ struct PendSVChannelMap { static constexpr uint8_t kAfterSercom7 = advance(HasSercom7, kAfterSercom6); static constexpr uint8_t kAfterUsb = advance(HasUsb, kAfterSercom7); static constexpr uint8_t kAfterGmac = advance(HasGmac, kAfterUsb); - static constexpr uint8_t kAfterAdc = advance(HasAdc, kAfterGmac); + static constexpr uint8_t kAfterPhy = advance(HasPhy, kAfterGmac); + static constexpr uint8_t kAfterAdc = advance(HasAdc, kAfterPhy); static constexpr uint8_t kAfterAes = advance(HasAes, kAfterAdc); static constexpr uint8_t kAfterPukcc = advance(HasPukcc, kAfterAes); @@ -45,7 +46,8 @@ struct PendSVChannelMap { static constexpr uint8_t Sercom7 = assign(HasSercom7, kAfterSercom6); static constexpr uint8_t Usb = assign(HasUsb, kAfterSercom7); static constexpr uint8_t Gmac = assign(HasGmac, kAfterUsb); - static constexpr uint8_t Adc = assign(HasAdc, kAfterGmac); + static constexpr uint8_t Phy = assign(HasPhy, kAfterGmac); + static constexpr uint8_t Adc = assign(HasAdc, kAfterPhy); static constexpr uint8_t Aes = assign(HasAes, kAfterAdc); static constexpr uint8_t Pukcc = assign(HasPukcc, kAfterAes); static constexpr uint8_t Trng = assign(HasTrng, kAfterPukcc); From 12eeb029ab57894e976f876f9c4f6eb81314ed6d Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 3 Jul 2026 21:51:43 -0400 Subject: [PATCH 44/79] Add RTC PendSV channel allocation Signed-off-by: Cal Abel --- cores/arduino/PendSV.h | 7 ++++++- cores/arduino/PendSVChannelMap.h | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/cores/arduino/PendSV.h b/cores/arduino/PendSV.h index b1ca59a90..6200c8de1 100644 --- a/cores/arduino/PendSV.h +++ b/cores/arduino/PendSV.h @@ -81,13 +81,18 @@ #else #define PENDSV_HAS_TRNG false #endif +#if defined(RTC) || defined(RTC_REGS) +#define PENDSV_HAS_RTC true +#else +#define PENDSV_HAS_RTC false +#endif using PendSVChannels = PendSVChannelMap< PENDSV_HAS_SERCOM0, PENDSV_HAS_SERCOM1, PENDSV_HAS_SERCOM2, PENDSV_HAS_SERCOM3, PENDSV_HAS_SERCOM4, PENDSV_HAS_SERCOM5, PENDSV_HAS_SERCOM6, PENDSV_HAS_SERCOM7, PENDSV_HAS_USB, PENDSV_HAS_GMAC, PENDSV_HAS_PHY, PENDSV_HAS_ADC, PENDSV_HAS_AES, PENDSV_HAS_PUKCC, - PENDSV_HAS_TRNG>; + PENDSV_HAS_TRNG, PENDSV_HAS_RTC>; class PendSV { public: diff --git a/cores/arduino/PendSVChannelMap.h b/cores/arduino/PendSVChannelMap.h index fcc6864f8..441bb92cb 100644 --- a/cores/arduino/PendSVChannelMap.h +++ b/cores/arduino/PendSVChannelMap.h @@ -5,7 +5,7 @@ template + bool HasPukcc, bool HasTrng, bool HasRtc> struct PendSVChannelMap { static constexpr uint8_t kMaxServices = 32; static constexpr uint8_t kUnavailable = 0xFF; @@ -34,6 +34,7 @@ struct PendSVChannelMap { static constexpr uint8_t kAfterAdc = advance(HasAdc, kAfterPhy); static constexpr uint8_t kAfterAes = advance(HasAes, kAfterAdc); static constexpr uint8_t kAfterPukcc = advance(HasPukcc, kAfterAes); + static constexpr uint8_t kAfterTrng = advance(HasTrng, kAfterPukcc); public: static constexpr uint8_t Sercom0 = assign(HasSercom0, kBase); @@ -51,7 +52,8 @@ struct PendSVChannelMap { static constexpr uint8_t Aes = assign(HasAes, kAfterAdc); static constexpr uint8_t Pukcc = assign(HasPukcc, kAfterAes); static constexpr uint8_t Trng = assign(HasTrng, kAfterPukcc); - static constexpr uint8_t Count = advance(HasTrng, kAfterPukcc); + static constexpr uint8_t Rtc = assign(HasRtc, kAfterTrng); + static constexpr uint8_t Count = advance(HasRtc, kAfterTrng); static constexpr bool isAvailable(uint8_t channel) { return channel != kUnavailable; From e9e81f7ed21dde64a68085cf677f559beaef8d3e Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Thu, 16 Jul 2026 12:17:31 -0400 Subject: [PATCH 45/79] fix(wire): restore multimaster role transitions --- cores/arduino/SERCOM.cpp | 15 +++++++++++++-- cores/arduino/SERCOM.h | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 9744814bb..741b7e768 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -687,6 +687,7 @@ void SERCOM::initSlaveWIRE( uint16_t ucAddress, bool enableGeneralCall, uint8_t SERCOM_I2CS_ADDR_ADDRMASK(0x00ul) | // 0x00, only match exact address (enable10Bit ? SERCOM_I2CS_ADDR_TENBITEN : 0) | // 10-bit addressing enableGeneralCall; // enable general call (address 0x00) + _wire.slaveConfigured = true; setSlaveWIRE(); } @@ -695,6 +696,7 @@ void SERCOM::initMasterWIRE( uint32_t baudrate ) initWIRE(); setBaudrateWIRE(baudrate); + _wire.slaveConfigured = false; setMasterWIRE(); } @@ -874,8 +876,14 @@ bool SERCOM::enqueueWIRE(SercomTxn* txn) return false; // Queue full; caller must retry at runtime if (!_txnQueue.store(txn)) return false; - if (!_wire.active) + if (!_wire.active) { + // dI2C uses a single SERCOM as both an addressed client and a host. A + // queued host operation is the role-transition point; callers must not + // tear down and re-begin Wire around every transaction. + if (!isMasterWIRE()) + setMasterWIRE(); return startTransmissionWIRE() != nullptr; + } return true; } @@ -974,10 +982,13 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) if (_txnQueue.peek(next) && isMaster) startTransmissionWIRE(); - else if (isMaster) + else if (isMaster) { sercom->I2CM.INTENCLR.reg = SERCOM_I2CM_INTENCLR_ERROR | SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; + if (_wire.slaveConfigured) + setSlaveWIRE(); + } return txn; } diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index b6fa0a241..a9fd36040 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -428,6 +428,7 @@ class SERCOM uint8_t masterSpeed = 0x0; // default to lowest speed uint8_t slaveSpeed = 0x0; // default to lowest speed bool inited = false; // whether initMaster/SlaveWIRE has been called + bool slaveConfigured = false; // return to client mode after queued host work bool useDma = false; // per transaction DMA use flag for Host/Client modes bool active = false; // active transaction in progress uint8_t retryCount = 0; // retry count for recoverable bus errors From 8d8022138454ddfe43064f50d75ea7e8df36ba72 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 17 Jul 2026 06:04:31 -0400 Subject: [PATCH 46/79] refactor(core): define SAME5x chip families --- cores/arduino/sam.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 cores/arduino/sam.h diff --git a/cores/arduino/sam.h b/cores/arduino/sam.h new file mode 100644 index 000000000..a4837e738 --- /dev/null +++ b/cores/arduino/sam.h @@ -0,0 +1,12 @@ +#pragma once + +// The board's build configuration selects the exact CMSIS device. Keep that +// selection authoritative, then expose only the family distinctions needed by +// the Arduino core. +#include_next + +#if defined(__SAMD51__) || defined(__SAME51__) +#define ARDUINO_SAMD51_E51 +#elif defined(__SAME53__) || defined(__SAME54__) +#define ARDUINO_SAME53_E54 +#endif From 4b63b95f7d9c19953195ee3bd640af2fc6bbc592 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 17 Jul 2026 06:45:07 -0400 Subject: [PATCH 47/79] feat(variant): add SAME54 Xplained Pro board --- .../xplained_m4/debug_scripts/variant.gdb | 31 ++ .../gcc/flash_with_bootloader.ld | 216 ++++++++++++ .../gcc/flash_without_bootloader.ld | 214 ++++++++++++ .../openocd_scripts/jlink_same54.cfg | 32 ++ variants/xplained_m4/pins_arduino.h | 20 ++ variants/xplained_m4/variant.cpp | 133 ++++++++ variants/xplained_m4/variant.h | 314 ++++++++++++++++++ 7 files changed, 960 insertions(+) create mode 100644 variants/xplained_m4/debug_scripts/variant.gdb create mode 100644 variants/xplained_m4/linker_scripts/gcc/flash_with_bootloader.ld create mode 100644 variants/xplained_m4/linker_scripts/gcc/flash_without_bootloader.ld create mode 100644 variants/xplained_m4/openocd_scripts/jlink_same54.cfg create mode 100644 variants/xplained_m4/pins_arduino.h create mode 100644 variants/xplained_m4/variant.cpp create mode 100644 variants/xplained_m4/variant.h diff --git a/variants/xplained_m4/debug_scripts/variant.gdb b/variants/xplained_m4/debug_scripts/variant.gdb new file mode 100644 index 000000000..17cecc0de --- /dev/null +++ b/variants/xplained_m4/debug_scripts/variant.gdb @@ -0,0 +1,31 @@ +# +# SimIO Device M4 OpenOCD/GDB script. +# +# Copyright (c) 2014-2015 Arduino LLC. All right reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +# Define 'reset' command +define reset + +info reg + +break main + +# End of 'reset' command +end + +target remote | openocd -c "interface jlink" -c "transport select swd" -c "set CHIPNAME same54" -c "set _FLASHDRIVER atsame5" -f target/atsame5x.cfg -c "gdb_port pipe; log_output openocd.log" diff --git a/variants/xplained_m4/linker_scripts/gcc/flash_with_bootloader.ld b/variants/xplained_m4/linker_scripts/gcc/flash_with_bootloader.ld new file mode 100644 index 000000000..34e0c24d9 --- /dev/null +++ b/variants/xplained_m4/linker_scripts/gcc/flash_with_bootloader.ld @@ -0,0 +1,216 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* Linker script to configure memory regions. + * Need modifying for a specific board. + * FLASH.ORIGIN: starting address of flash + * FLASH.LENGTH: length of flash + * RAM.ORIGIN: starting address of RAM bank 0 + * RAM.LENGTH: length of RAM bank 0 + */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000+0x4000, LENGTH = 0x00080000-0x4000 /* First 16KB used by bootloader */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00030000 +} + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + __text_start__ = .; + + KEEP(*(.sketch_boot)) + + . = ALIGN(0x4000); + KEEP(*(.isr_vector)) + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */ + /* + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + LONG (__etext) + LONG (__data_start__) + LONG (__data_end__ - __data_start__) + LONG (__etext2) + LONG (__data2_start__) + LONG (__data2_end__ - __data2_start__) + __copy_table_end__ = .; + } > FLASH + */ + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */ + /* + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + LONG (__bss2_start__) + LONG (__bss2_end__ - __bss2_start__) + __zero_table_end__ = .; + } > FLASH + */ + + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(16); + /* All data end */ + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + PROVIDE(end = .); + *(.heap*) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + *(.stack*) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + __ram_end__ = ORIGIN(RAM) + LENGTH(RAM) -1 ; + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/variants/xplained_m4/linker_scripts/gcc/flash_without_bootloader.ld b/variants/xplained_m4/linker_scripts/gcc/flash_without_bootloader.ld new file mode 100644 index 000000000..46e8d8846 --- /dev/null +++ b/variants/xplained_m4/linker_scripts/gcc/flash_without_bootloader.ld @@ -0,0 +1,214 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* Linker script to configure memory regions. + * Need modifying for a specific board. + * FLASH.ORIGIN: starting address of flash + * FLASH.LENGTH: length of flash + * RAM.ORIGIN: starting address of RAM bank 0 + * RAM.LENGTH: length of RAM bank 0 + */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00080000 + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00030000 +} + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __ram_end__ + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + __text_start__ = .; + + KEEP(*(.isr_vector)) + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */ + /* + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + LONG (__etext) + LONG (__data_start__) + LONG (__data_end__ - __data_start__) + LONG (__etext2) + LONG (__data2_start__) + LONG (__data2_end__ - __data2_start__) + __copy_table_end__ = .; + } > FLASH + */ + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */ + /* + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + LONG (__bss2_start__) + LONG (__bss2_end__ - __bss2_start__) + __zero_table_end__ = .; + } > FLASH + */ + + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(16); + /* All data end */ + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + PROVIDE(end = .); + *(.heap*) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + *(.stack*) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM) ; + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + __ram_end__ = ORIGIN(RAM) + LENGTH(RAM) -1 ; + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/variants/xplained_m4/openocd_scripts/jlink_same54.cfg b/variants/xplained_m4/openocd_scripts/jlink_same54.cfg new file mode 100644 index 000000000..67e059621 --- /dev/null +++ b/variants/xplained_m4/openocd_scripts/jlink_same54.cfg @@ -0,0 +1,32 @@ +# +# Arduino Zero OpenOCD script. +# +# Copyright (c) 2014-2015 Arduino LLC. All right reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +source [find interface/jlink.cfg] +transport select swd + +# chip name +set CHIPNAME same54 +set _FLASHDRIVER atsame5 +set ENDIAN little + +# choose a port here +set telnet_port 0 + +source [find target/atsame5x.cfg] diff --git a/variants/xplained_m4/pins_arduino.h b/variants/xplained_m4/pins_arduino.h new file mode 100644 index 000000000..dbc5a93c4 --- /dev/null +++ b/variants/xplained_m4/pins_arduino.h @@ -0,0 +1,20 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// API compatibility +#include "variant.h" diff --git a/variants/xplained_m4/variant.cpp b/variants/xplained_m4/variant.cpp new file mode 100644 index 000000000..0015e3c15 --- /dev/null +++ b/variants/xplained_m4/variant.cpp @@ -0,0 +1,133 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "variant.h" + +/* + * Pins descriptions + */ + +const PinDescription g_APinDescription[] = + { + // 0..15: EXT1. UART first, then GPIO/PWM/IRQ/SPI/ADC/I2C. + {PORTA, 5, PIO_SERCOM, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG, ADC_Channel5, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_5}, + {PORTA, 4, PIO_SERCOM, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG, ADC_Channel4, NOT_ON_PWM, TC0_CH0, EXTERNAL_INT_4}, + {PORTA, 6, PIO_DIGITAL, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG | PIN_ATTR_PWM_E, ADC_Channel6, TC1_CH0, TC1_CH0, EXTERNAL_INT_6}, + {PORTA, 7, PIO_DIGITAL, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG | PIN_ATTR_PWM_E, ADC_Channel7, TC1_CH1, TC1_CH1, EXTERNAL_INT_7}, + {PORTB, 8, PIO_TIMER, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG | PIN_ATTR_PWM_E, ADC_Channel2, TC4_CH0, TC4_CH0, EXTERNAL_INT_8}, + {PORTB, 9, PIO_TIMER, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG | PIN_ATTR_PWM_E, ADC_Channel3, TC4_CH1, TC4_CH1, EXTERNAL_INT_9}, + {PORTB, 7, PIO_DIGITAL, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG_ALT, ADC_Channel9, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_7}, + {PORTA, 27, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_11}, + {PORTB, 28, PIO_SERCOM_ALT, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_14}, + {PORTB, 27, PIO_SERCOM_ALT, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_13}, + {PORTB, 29, PIO_SERCOM_ALT, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15}, + {PORTB, 26, PIO_SERCOM_ALT, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_12}, + {PORTB, 4, PIO_ANALOG, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG_ALT, ADC_Channel6, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_4}, + {PORTB, 5, PIO_ANALOG, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG_ALT, ADC_Channel7, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_5}, + {PORTA, 22, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, TC4_CH0, EXTERNAL_INT_6}, + {PORTA, 23, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, TC4_CH1, EXTERNAL_INT_7}, + + // 16..31: EXT2, including the I2C/SPI lines shared with EXT3. + {PORTB, 17, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, TC6_CH1, EXTERNAL_INT_1}, + {PORTB, 16, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, TC6_CH0, EXTERNAL_INT_0}, + {PORTB, 1, PIO_DIGITAL, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG, ADC_Channel13, NOT_ON_PWM, TC7_CH1, EXTERNAL_INT_1}, + {PORTB, 6, PIO_DIGITAL, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG_ALT, ADC_Channel8, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_6}, + {PORTB, 14, PIO_TIMER_ALT, PIN_ATTR_DIGITAL | PIN_ATTR_PWM_F, No_ADC_Channel, TCC4_CH0, TC5_CH0, EXTERNAL_INT_14}, + {PORTB, 15, PIO_TIMER_ALT, PIN_ATTR_DIGITAL | PIN_ATTR_PWM_F, No_ADC_Channel, TCC4_CH1, TC5_CH1, EXTERNAL_INT_15}, + {PORTD, 0, PIO_DIGITAL, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG_ALT, ADC_Channel14, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_0}, + {PORTB, 2, PIO_DIGITAL, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG | PIN_ATTR_PWM_E, ADC_Channel14, TC6_CH0, TC6_CH0, EXTERNAL_INT_2}, + {PORTC, 6, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_6}, + {PORTC, 4, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_4}, + {PORTC, 7, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_9}, + {PORTC, 5, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_5}, + {PORTB, 0, PIO_ANALOG, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG, ADC_Channel12, NOT_ON_PWM, TC7_CH0, EXTERNAL_INT_0}, + {PORTA, 3, PIO_ANALOG, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG, ADC_Channel1, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_3}, + {PORTD, 8, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_3}, + {PORTD, 9, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_4}, + + // 32..42: EXT3 pins that are not duplicates of EXT2 shared buses. + {PORTC, 23, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_7}, + {PORTC, 22, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_6}, + {PORTC, 1, PIO_DIGITAL, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG_ALT, ADC_Channel11, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_1}, + {PORTC, 10, PIO_DIGITAL, PIN_ATTR_DIGITAL | PIN_ATTR_PWM_F, No_ADC_Channel, TCC0_CH0, NOT_ON_TIMER, EXTERNAL_INT_10}, + {PORTD, 10, PIO_TIMER_ALT, PIN_ATTR_DIGITAL | PIN_ATTR_PWM_F, No_ADC_Channel, TCC0_CH3, NOT_ON_TIMER, EXTERNAL_INT_5}, + {PORTD, 11, PIO_TIMER_ALT, PIN_ATTR_DIGITAL | PIN_ATTR_PWM_F, No_ADC_Channel, TCC0_CH4, NOT_ON_TIMER, EXTERNAL_INT_6}, + {PORTC, 30, PIO_DIGITAL, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG_ALT, ADC_Channel12, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_14}, + {PORTC, 31, PIO_DIGITAL, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG_ALT, ADC_Channel13, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15}, + {PORTC, 14, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_14}, + {PORTC, 2, PIO_ANALOG, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG_ALT, ADC_Channel4, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_2}, + {PORTC, 3, PIO_ANALOG, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG_ALT, ADC_Channel5, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_3}, + + // 43..46: dedicated ADC/DAC header and PDEC header. + {PORTA, 2, PIO_ANALOG, PIN_ATTR_DIGITAL | PIN_ATTR_ANALOG, ADC_Channel0, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_2}, + {PORTC, 16, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_0}, + {PORTC, 17, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_1}, + {PORTC, 18, PIO_DIGITAL, PIN_ATTR_DIGITAL | PIN_ATTR_PWM_F, No_ADC_Channel, TCC0_CH2, NOT_ON_TIMER, EXTERNAL_INT_2}, + + // 47..58: internal EDBG UART, target USB, and onboard QSPI flash. + {PORTB, 24, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_8}, + {PORTB, 25, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_9}, + {PORTC, 0, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_0}, + {PORTC, 19, PIO_COM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_3}, + {PORTA, 24, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_8}, + {PORTA, 25, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_9}, + {PORTB, 10, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_10}, + {PORTB, 11, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_11}, + {PORTA, 8, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NMI}, + {PORTA, 9, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_9}, + {PORTA, 10, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_10}, + {PORTA, 11, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_11}, + {PORTB, 31, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15}, +}; + +static_assert(sizeof(g_APinDescription) / sizeof(g_APinDescription[0]) == PINS_COUNT, + "xplained_m4 pin count does not match g_APinDescription"); + +const uint8_t g_AAnalogPinMap[NUM_ANALOG_INPUTS] = { + PIN_A0, PIN_A1, PIN_A2, PIN_A3, PIN_A4, PIN_A5, PIN_A6, PIN_A7, + PIN_A8, PIN_A9, PIN_A10, PIN_A11, PIN_A12, PIN_A13, PIN_A14, PIN_A15, +}; + +const void *g_apTCInstances[TCC_INST_NUM + TC_INST_NUM] = {TCC0, TCC1, TCC2, TCC3, TCC4, TC0, TC1, TC2, TC3, TC4, TC5}; +const uint32_t GCLK_CLKCTRL_IDs[TCC_INST_NUM + TC_INST_NUM] = {TCC0_GCLK_ID, TCC1_GCLK_ID, TCC2_GCLK_ID, TCC3_GCLK_ID, TCC4_GCLK_ID, TC0_GCLK_ID, TC1_GCLK_ID, TC2_GCLK_ID, TC3_GCLK_ID, TC4_GCLK_ID, TC5_GCLK_ID}; + +// Multi-serial objects instantiation +SERCOM sercom0(SERCOM0); +SERCOM sercom1(SERCOM1); +SERCOM sercom2(SERCOM2); +SERCOM sercom3(SERCOM3); +SERCOM sercom4(SERCOM4); +SERCOM sercom5(SERCOM5); +SERCOM sercom6(SERCOM6); +SERCOM sercom7(SERCOM7); + +Uart Serial1(&sercom0, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); +Uart Serial2(&sercom5, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX); +Uart Serial3(&sercom1, PIN_SERIAL3_RX, PIN_SERIAL3_TX, PAD_SERIAL3_RX, PAD_SERIAL3_TX); +Uart Serial4(&sercom2, PIN_SERIAL4_RX, PIN_SERIAL4_TX, PAD_SERIAL4_RX, PAD_SERIAL4_TX); + +#define UART_HANDLERS(n, serial) \ + void SERCOM##n##_0_Handler() { serial.IrqHandler(); } \ + void SERCOM##n##_1_Handler() { serial.IrqHandler(); } \ + void SERCOM##n##_2_Handler() { serial.IrqHandler(); } \ + void SERCOM##n##_3_Handler() { serial.IrqHandler(); } + +UART_HANDLERS(0, Serial1) +UART_HANDLERS(5, Serial2) +UART_HANDLERS(1, Serial3) +UART_HANDLERS(2, Serial4) diff --git a/variants/xplained_m4/variant.h b/variants/xplained_m4/variant.h new file mode 100644 index 000000000..288b89863 --- /dev/null +++ b/variants/xplained_m4/variant.h @@ -0,0 +1,314 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_XPLAINED_M4_ +#define _VARIANT_XPLAINED_M4_ + +// The definitions here needs a SAMD core >=1.6.10 +#define ARDUINO_SAMD_VARIANT_COMPLIANCE 10610 + +/*---------------------------------------------------------------------------- + * Definitions + *----------------------------------------------------------------------------*/ + +/** Frequency of the board main oscillator */ +#define VARIANT_MAINOSC (32768ul) + +/** Master clock frequency */ +#define VARIANT_MCK (120000000ul) + +#define VARIANT_GCLK0_FREQ (120000000UL) +#define VARIANT_GCLK1_FREQ (48000000UL) +#define VARIANT_GCLK2_FREQ (100000000UL) + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +#include "SERCOM.h" +#include "Uart.h" +#endif // __cplusplus + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +/*---------------------------------------------------------------------------- + * Pins + *----------------------------------------------------------------------------*/ + +// Number of pins defined in PinDescription array +#define PINS_COUNT (60u) +#define NUM_DIGITAL_PINS (47u) +#define NUM_ANALOG_INPUTS (16u) +#define NUM_ANALOG_OUTPUTS (1u) +#define analogInputToDigitalPin(p) ((p < NUM_ANALOG_INPUTS) ? g_AAnalogPinMap[(p)] : -1) + +extern const uint8_t g_AAnalogPinMap[NUM_ANALOG_INPUTS]; + +#define digitalPinToPort(P) (&(PORT->Group[g_APinDescription[P].ulPort])) +#define digitalPinToBitMask(P) (1 << g_APinDescription[P].ulPin) +// #define analogInPinToBit(P) ( ) +#define portOutputRegister(port) (&(port->OUT.reg)) +#define portInputRegister(port) (&(port->IN.reg)) +#define portModeRegister(port) (&(port->DIR.reg)) +#define digitalPinHasPWM(P) (g_APinDescription[P].ulPWMChannel != NOT_ON_PWM || g_APinDescription[P].ulTCChannel != NOT_ON_TIMER) + +/* + * digitalPinToTimer(..) is AVR-specific and is not defined for SAMD + * architecture. If you need to check if a pin supports PWM you must + * use digitalPinHasPWM(..). + * + * https://github.com/arduino/Arduino/issues/1833 + */ +// #define digitalPinToTimer(P) + +// LEDs +#define PIN_LED (46u) +#define LED_BUILTIN PIN_LED +#define PIN_BUTTON (59u) + +/* + * Analog pins + */ +#define PIN_A0 (43u) +#define PIN_A1 (12u) +#define PIN_A2 (13u) +#define PIN_A3 (28u) +#define PIN_A4 (29u) +#define PIN_A5 (41u) +#define PIN_A6 (42u) +#define PIN_A7 (2u) +#define PIN_A8 (3u) +#define PIN_A9 (4u) +#define PIN_A10 (5u) +#define PIN_A11 (6u) +#define PIN_A12 (18u) +#define PIN_A13 (19u) +#define PIN_A14 (22u) +#define PIN_A15 (34u) + +#define PIN_DAC0 PIN_A0 + + static const uint8_t A0 = PIN_A0; + static const uint8_t A1 = PIN_A1; + static const uint8_t A2 = PIN_A2; + static const uint8_t A3 = PIN_A3; + static const uint8_t A4 = PIN_A4; + static const uint8_t A5 = PIN_A5; + static const uint8_t A6 = PIN_A6; + static const uint8_t A7 = PIN_A7; + static const uint8_t A8 = PIN_A8; + static const uint8_t A9 = PIN_A9; + static const uint8_t A10 = PIN_A10; + static const uint8_t A11 = PIN_A11; + static const uint8_t A12 = PIN_A12; + static const uint8_t A13 = PIN_A13; + static const uint8_t A14 = PIN_A14; + static const uint8_t A15 = PIN_A15; + + static const uint8_t DAC0 = PIN_DAC0; + +#define ADC_RESOLUTION 12 + +// Other pins +#define PIN_EXT1_SDA (14u) +#define PIN_EXT1_SCL (15u) +#define PIN_EXT2_SDA (30u) +#define PIN_EXT2_SCL (31u) +#define PIN_EXT3_SDA PIN_EXT2_SDA +#define PIN_EXT3_SCL PIN_EXT2_SCL + +/* + * Serial interfaces + */ + +// Numbered Arduino UARTs: EXT1, EXT2, EXT3, then the EDBG virtual COM port. +#define PIN_SERIAL1_RX (0u) +#define PIN_SERIAL1_TX (1u) +#define PAD_SERIAL1_RX SERCOM_RX_PAD_1 +#define PAD_SERIAL1_TX UART_TX_PAD_0 + +#define PIN_SERIAL2_RX (16u) +#define PIN_SERIAL2_TX (17u) +#define PAD_SERIAL2_RX SERCOM_RX_PAD_1 +#define PAD_SERIAL2_TX UART_TX_PAD_0 + +#define PIN_SERIAL3_RX (32u) +#define PIN_SERIAL3_TX (33u) +#define PAD_SERIAL3_RX SERCOM_RX_PAD_1 +#define PAD_SERIAL3_TX UART_TX_PAD_0 + +#define PIN_SERIAL4_RX (47u) +#define PIN_SERIAL4_TX (48u) +#define PAD_SERIAL4_RX SERCOM_RX_PAD_1 +#define PAD_SERIAL4_TX UART_TX_PAD_0 + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 2 + +#define PIN_SPI_MISO (10u) +#define PIN_SPI_MOSI (9u) +#define PIN_SPI_SCK (11u) +#define PIN_SPI_SS (8u) +#define PERIPH_SPI sercom4 +#define PAD_SPI_TX SPI_PAD_0_SCK_1 +#define PAD_SPI_RX SERCOM_RX_PAD_3 +#define SPI_IT_HANDLER_0 SERCOM4_0_Handler +#define SPI_IT_HANDLER_1 SERCOM4_1_Handler +#define SPI_IT_HANDLER_2 SERCOM4_2_Handler +#define SPI_IT_HANDLER_3 SERCOM4_3_Handler + + static const uint8_t SS = PIN_SPI_SS; + static const uint8_t MOSI = PIN_SPI_MOSI; + static const uint8_t MISO = PIN_SPI_MISO; + static const uint8_t SCK = PIN_SPI_SCK; + +#define PIN_SPI1_MISO (26u) +#define PIN_SPI1_MOSI (25u) +#define PIN_SPI1_SCK (27u) +#define PIN_SPI1_SS (24u) +#define PERIPH_SPI1 sercom6 +#define PAD_SPI1_TX SPI_PAD_0_SCK_1 +#define PAD_SPI1_RX SERCOM_RX_PAD_3 +#define SPI1_IT_HANDLER_0 SERCOM6_0_Handler +#define SPI1_IT_HANDLER_1 SERCOM6_1_Handler +#define SPI1_IT_HANDLER_2 SERCOM6_2_Handler +#define SPI1_IT_HANDLER_3 SERCOM6_3_Handler + + static const uint8_t SS1 = PIN_SPI1_SS; + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 2 + +#define PIN_WIRE_SDA PIN_EXT1_SDA +#define PIN_WIRE_SCL PIN_EXT1_SCL +#define PERIPH_WIRE sercom3 +#define WIRE_IT_HANDLER SERCOM3_Handler +#define WIRE_IT_HANDLER_0 SERCOM3_0_Handler +#define WIRE_IT_HANDLER_1 SERCOM3_1_Handler +#define WIRE_IT_HANDLER_2 SERCOM3_2_Handler +#define WIRE_IT_HANDLER_3 SERCOM3_3_Handler + + static const uint8_t SDA = PIN_WIRE_SDA; + static const uint8_t SCL = PIN_WIRE_SCL; + +#define PIN_WIRE1_SDA PIN_EXT2_SDA +#define PIN_WIRE1_SCL PIN_EXT2_SCL +#define PERIPH_WIRE1 sercom7 +#define WIRE1_IT_HANDLER SERCOM7_Handler +#define WIRE1_IT_HANDLER_0 SERCOM7_0_Handler +#define WIRE1_IT_HANDLER_1 SERCOM7_1_Handler +#define WIRE1_IT_HANDLER_2 SERCOM7_2_Handler +#define WIRE1_IT_HANDLER_3 SERCOM7_3_Handler + + static const uint8_t SDA1 = PIN_WIRE1_SDA; + static const uint8_t SCL1 = PIN_WIRE1_SCL; + +/* + * USB + */ +#define PIN_USB_VBUS (49u) +#define PIN_USB_HOST_ENABLE (50u) +#define PIN_USB_DM (51u) +#define PIN_USB_DP (52u) + +/* + * I2S Interfaces + */ +#define I2S_INTERFACES_COUNT 1 + +#define I2S_DEVICE 0 +#define I2S_CLOCK_GENERATOR 3 + +#define PIN_I2S_SDO (14u) +#define PIN_I2S_SDI (15u) +#define PIN_I2S_SCK PIN_SERIAL2_TX +#define PIN_I2S_FS (33u) +#define PIN_I2S_MCK PIN_SERIAL2_RX +// Onboard Micron N25Q256A QSPI flash routes. +#define PIN_QSPI_SCK (53u) +#define PIN_QSPI_CS (54u) +#define PIN_QSPI_IO0 (55u) +#define PIN_QSPI_IO1 (56u) +#define PIN_QSPI_IO2 (57u) +#define PIN_QSPI_IO3 (58u) + + // TODO: meaningful value for this + // #define VARIANT_QSPI_BAUD_DEFAULT 5000000 + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#ifdef __cplusplus + +/* ========================= + * ===== SERCOM DEFINITION + * ========================= + */ +extern SERCOM sercom0; +extern SERCOM sercom1; +extern SERCOM sercom2; +extern SERCOM sercom3; +extern SERCOM sercom4; +extern SERCOM sercom5; +extern SERCOM sercom6; +extern SERCOM sercom7; + +extern Uart Serial1; +extern Uart Serial2; +extern Uart Serial3; +extern Uart Serial4; + +#endif + +// These serial port names are intended to allow libraries and architecture-neutral +// sketches to automatically default to the correct port name for a particular type +// of use. For example, a GPS module would normally connect to SERIAL_PORT_HARDWARE_OPEN, +// the first hardware serial port whose RX/TX pins are not dedicated to another use. +// +// SERIAL_PORT_MONITOR Port which normally prints to the Arduino Serial Monitor +// +// SERIAL_PORT_USBVIRTUAL Port which is USB virtual serial +// +// SERIAL_PORT_LINUXBRIDGE Port which connects to a Linux system via Bridge library +// +// SERIAL_PORT_HARDWARE Hardware serial port, physical RX & TX pins. +// +// SERIAL_PORT_HARDWARE_OPEN Hardware serial ports which are open for use. Their RX & TX +// pins are NOT connected to anything by default. +#define SERIAL_PORT_USBVIRTUAL Serial +#define SERIAL_PORT_MONITOR Serial +// Serial has no physical pins broken out, so it's not listed as HARDWARE port +#define SERIAL_PORT_HARDWARE Serial1 +#define SERIAL_PORT_HARDWARE_OPEN Serial1 + +#endif /* _VARIANT_XPLAINED_M4_ */ From 23d2515494915b7db69f840f61ea6b6ae4732651 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 17 Jul 2026 06:45:31 -0400 Subject: [PATCH 48/79] build(core): expose framework package metadata --- package.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 package.json diff --git a/package.json b/package.json new file mode 100644 index 000000000..b2a4a76b5 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "framework-arduino-samd-adafruit", + "version": "1.7.16", + "description": "Arduino Wiring-based framework for Microchip SAM D and SAME microcontrollers.", + "keywords": [ + "framework", + "arduino", + "microchip", + "samd", + "same" + ], + "license": "LGPL-2.1-or-later", + "repository": { + "type": "git", + "url": "https://github.com/adafruit/ArduinoCore-samd" + } +} From 533a33e1b0e422c5883fee348fbc6d91d775c23c Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 17 Jul 2026 11:35:35 -0400 Subject: [PATCH 49/79] feat(core): add native SAME53/E54 peripheral support --- cores/arduino/Reset.cpp | 8 +- cores/arduino/SERCOM.cpp | 832 ++++++++++++++++++++++++++++++- cores/arduino/SERCOM.h | 16 +- cores/arduino/Tone.cpp | 56 ++- cores/arduino/Uart.cpp | 5 + cores/arduino/WInterrupts.c | 144 ++++-- cores/arduino/WVariant.h | 49 +- cores/arduino/cortex_handlers.c | 176 ++++++- cores/arduino/pulse.c | 9 +- cores/arduino/startup.c | 118 ++++- cores/arduino/wiring.c | 60 ++- cores/arduino/wiring_analog.c | 211 ++++++-- cores/arduino/wiring_digital.c | 63 ++- cores/arduino/wiring_private.c | 19 +- libraries/Wire/Wire.cpp | 91 ++-- variants/xplained_m4/variant.cpp | 36 +- variants/xplained_m4/variant.h | 4 +- 17 files changed, 1717 insertions(+), 180 deletions(-) diff --git a/cores/arduino/Reset.cpp b/cores/arduino/Reset.cpp index 9bde4a8c0..159c4eeac 100644 --- a/cores/arduino/Reset.cpp +++ b/cores/arduino/Reset.cpp @@ -33,7 +33,7 @@ extern const uint32_t __text_start__; #else -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) #define APP_START 0x00004004 #else #define APP_START 0x00002004 @@ -42,8 +42,10 @@ extern const uint32_t __text_start__; #endif static inline bool nvmReady(void) { -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) return NVMCTRL->STATUS.reg & NVMCTRL_STATUS_READY; +#elif defined(ARDUINO_SAME53_E54) + return NVMCTRL_REGS->NVMCTRL_STATUS & NVMCTRL_STATUS_READY_Msk; #else return NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY; #endif @@ -54,7 +56,7 @@ static void banzai() { // Disable all interrupts __disable_irq(); -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) //THESE MUST MATCH THE BOOTLOADER #define DOUBLE_TAP_MAGIC 0xf01669efUL #define BOOT_DOUBLE_TAP_ADDRESS (HSRAM_ADDR + HSRAM_SIZE - 4) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index f945a847f..9ae1ee007 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -27,11 +27,17 @@ #define WIRE_RISE_TIME_NANOSECONDS 125 #endif +#if defined(ARDUINO_SAMD51_E51) SERCOM::SERCOM(Sercom* s) +#elif defined(ARDUINO_SAME53_E54) +SERCOM::SERCOM(sercom_registers_t* s) +#else +SERCOM::SERCOM(Sercom* s) +#endif { sercom = s; -#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) // A briefly-available but now deprecated feature had the SPI clock source // set via a compile-time setting (MAX_SPI)...problem was this affected // ALL SERCOMs, whereas some (anything read/write, e.g. SD cards) should @@ -47,7 +53,7 @@ SERCOM::SERCOM(Sercom* s) #elif SERCOM_SPI_FREQ_REF == 100000000 // 100 MHz clock = GCLK2 clockSource = SERCOM_CLOCK_SOURCE_100M; #endif -#endif // end __SAMD51__ +#endif // SAME5x } /* ========================= @@ -59,6 +65,7 @@ void SERCOM::initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint initClockNVIC(); resetUART(); +#if defined(ARDUINO_SAMD51_E51) //Setting the CTRLA register sercom->USART.CTRLA.reg = SERCOM_USART_CTRLA_MODE(mode) | SERCOM_USART_CTRLA_SAMPR(sampleRate); @@ -66,6 +73,20 @@ void SERCOM::initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint //Setting the Interrupt register sercom->USART.INTENSET.reg = SERCOM_USART_INTENSET_RXC | //Received complete SERCOM_USART_INTENSET_ERROR; //All others errors +#elif defined(ARDUINO_SAME53_E54) + sercom->USART_INT.SERCOM_CTRLA = SERCOM_USART_INT_CTRLA_MODE(mode) | + SERCOM_USART_INT_CTRLA_SAMPR(sampleRate); + sercom->USART_INT.SERCOM_INTENSET = SERCOM_USART_INT_INTENSET_RXC_Msk | + SERCOM_USART_INT_INTENSET_ERROR_Msk; +#else + //Setting the CTRLA register + sercom->USART.CTRLA.reg = SERCOM_USART_CTRLA_MODE(mode) | + SERCOM_USART_CTRLA_SAMPR(sampleRate); + + //Setting the Interrupt register + sercom->USART.INTENSET.reg = SERCOM_USART_INTENSET_RXC | //Received complete + SERCOM_USART_INTENSET_ERROR; //All others errors +#endif if ( mode == UART_INT_CLOCK ) { @@ -82,12 +103,22 @@ void SERCOM::initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint // (multiply by 8, to calculate fractional piece) uint32_t baudTimes8 = (freqRef * 8) / (sampleRateValue * baudrate); +#if defined(ARDUINO_SAMD51_E51) + sercom->USART.BAUD.FRAC.FP = (baudTimes8 % 8); + sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8); +#elif defined(ARDUINO_SAME53_E54) + sercom->USART_INT.SERCOM_BAUD = + SERCOM_USART_INT_BAUD_FRAC_FP(baudTimes8 % 8) | + SERCOM_USART_INT_BAUD_FRAC_BAUD(baudTimes8 / 8); +#else sercom->USART.BAUD.FRAC.FP = (baudTimes8 % 8); sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8); +#endif } } void SERCOM::initFrame(SercomUartCharSize charSize, SercomDataOrder dataOrder, SercomParityMode parityMode, SercomNumberStopBit nbStopBits) { +#if defined(ARDUINO_SAMD51_E51) //Setting the CTRLA register sercom->USART.CTRLA.reg |= SERCOM_USART_CTRLA_FORM((parityMode == SERCOM_NO_PARITY ? 0 : 1) ) | @@ -98,20 +129,68 @@ void SERCOM::initFrame(SercomUartCharSize charSize, SercomDataOrder dataOrder, S nbStopBits << SERCOM_USART_CTRLB_SBMODE_Pos | (parityMode == SERCOM_NO_PARITY ? 0 : parityMode) << SERCOM_USART_CTRLB_PMODE_Pos; //If no parity use default value +#elif defined(ARDUINO_SAME53_E54) + sercom->USART_INT.SERCOM_CTRLA |= + SERCOM_USART_INT_CTRLA_FORM(parityMode == SERCOM_NO_PARITY ? 0 : 1) | + SERCOM_USART_INT_CTRLA_DORD(dataOrder); + sercom->USART_INT.SERCOM_CTRLB |= SERCOM_USART_INT_CTRLB_CHSIZE(charSize) | + SERCOM_USART_INT_CTRLB_SBMODE(nbStopBits) | + SERCOM_USART_INT_CTRLB_PMODE( + parityMode == SERCOM_NO_PARITY ? 0 : parityMode); +#else + //Setting the CTRLA register + sercom->USART.CTRLA.reg |= + SERCOM_USART_CTRLA_FORM((parityMode == SERCOM_NO_PARITY ? 0 : 1) ) | + dataOrder << SERCOM_USART_CTRLA_DORD_Pos; + + //Setting the CTRLB register + sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_CHSIZE(charSize) | + nbStopBits << SERCOM_USART_CTRLB_SBMODE_Pos | + (parityMode == SERCOM_NO_PARITY ? 0 : parityMode) << + SERCOM_USART_CTRLB_PMODE_Pos; //If no parity use default value +#endif } void SERCOM::initPads(SercomUartTXPad txPad, SercomRXPad rxPad) { +#if defined(ARDUINO_SAMD51_E51) //Setting the CTRLA register sercom->USART.CTRLA.reg |= SERCOM_USART_CTRLA_TXPO(txPad) | SERCOM_USART_CTRLA_RXPO(rxPad); // Enable Transceiver and Receiver sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN ; +#elif defined(ARDUINO_SAME53_E54) + sercom->USART_INT.SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_TXPO(txPad) | + SERCOM_USART_INT_CTRLA_RXPO(rxPad); + sercom->USART_INT.SERCOM_CTRLB |= SERCOM_USART_INT_CTRLB_TXEN_Msk | + SERCOM_USART_INT_CTRLB_RXEN_Msk; +#else + //Setting the CTRLA register + sercom->USART.CTRLA.reg |= SERCOM_USART_CTRLA_TXPO(txPad) | + SERCOM_USART_CTRLA_RXPO(rxPad); + + // Enable Transceiver and Receiver + sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN ; +#endif } void SERCOM::resetUART() { +#if defined(ARDUINO_SAMD51_E51) + // Start the Software Reset + sercom->USART.CTRLA.bit.SWRST = 1 ; + + while ( sercom->USART.CTRLA.bit.SWRST || sercom->USART.SYNCBUSY.bit.SWRST ) + { + // Wait for both bits Software Reset from CTRLA and SYNCBUSY coming back to 0 + } +#elif defined(ARDUINO_SAME53_E54) + sercom->USART_INT.SERCOM_CTRLA = SERCOM_USART_INT_CTRLA_SWRST_Msk; + while ((sercom->USART_INT.SERCOM_CTRLA & SERCOM_USART_INT_CTRLA_SWRST_Msk) || + (sercom->USART_INT.SERCOM_SYNCBUSY & SERCOM_USART_INT_SYNCBUSY_SWRST_Msk)) { + } +#else // Start the Software Reset sercom->USART.CTRLA.bit.SWRST = 1 ; @@ -119,15 +198,28 @@ void SERCOM::resetUART() { // Wait for both bits Software Reset from CTRLA and SYNCBUSY coming back to 0 } +#endif } void SERCOM::enableUART() { +#if defined(ARDUINO_SAMD51_E51) + //Setting the enable bit to 1 + sercom->USART.CTRLA.bit.ENABLE = 0x1u; + + //Wait for then enable bit from SYNCBUSY is equal to 0; + while(sercom->USART.SYNCBUSY.bit.ENABLE); +#elif defined(ARDUINO_SAME53_E54) + sercom->USART_INT.SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_ENABLE_Msk; + while (sercom->USART_INT.SERCOM_SYNCBUSY & SERCOM_USART_INT_SYNCBUSY_ENABLE_Msk) { + } +#else //Setting the enable bit to 1 sercom->USART.CTRLA.bit.ENABLE = 0x1u; //Wait for then enable bit from SYNCBUSY is equal to 0; while(sercom->USART.SYNCBUSY.bit.ENABLE); +#endif } void SERCOM::flushUART() @@ -137,64 +229,132 @@ void SERCOM::flushUART() return; // Wait for transmission to complete +#if defined(ARDUINO_SAMD51_E51) while(!sercom->USART.INTFLAG.bit.TXC); +#elif defined(ARDUINO_SAME53_E54) + while ((sercom->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_TXC_Msk) == 0) { + } +#else + while(!sercom->USART.INTFLAG.bit.TXC); +#endif } void SERCOM::clearStatusUART() { +#if defined(ARDUINO_SAMD51_E51) //Reset (with 0) the STATUS register sercom->USART.STATUS.reg = SERCOM_USART_STATUS_RESETVALUE; +#elif defined(ARDUINO_SAME53_E54) + sercom->USART_INT.SERCOM_STATUS = SERCOM_USART_INT_STATUS_RESETVALUE; +#else + //Reset (with 0) the STATUS register + sercom->USART.STATUS.reg = SERCOM_USART_STATUS_RESETVALUE; +#endif } bool SERCOM::availableDataUART() { //RXC : Receive Complete +#if defined(ARDUINO_SAMD51_E51) + return sercom->USART.INTFLAG.bit.RXC; +#elif defined(ARDUINO_SAME53_E54) + return (sercom->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_RXC_Msk) != 0; +#else return sercom->USART.INTFLAG.bit.RXC; +#endif } bool SERCOM::isUARTError() { +#if defined(ARDUINO_SAMD51_E51) + return sercom->USART.INTFLAG.bit.ERROR; +#elif defined(ARDUINO_SAME53_E54) + return (sercom->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_ERROR_Msk) != 0; +#else return sercom->USART.INTFLAG.bit.ERROR; +#endif } void SERCOM::acknowledgeUARTError() { +#if defined(ARDUINO_SAMD51_E51) + sercom->USART.INTFLAG.bit.ERROR = 1; +#elif defined(ARDUINO_SAME53_E54) + sercom->USART_INT.SERCOM_INTFLAG = SERCOM_USART_INT_INTFLAG_ERROR_Msk; +#else sercom->USART.INTFLAG.bit.ERROR = 1; +#endif } bool SERCOM::isBufferOverflowErrorUART() { //BUFOVF : Buffer Overflow +#if defined(ARDUINO_SAMD51_E51) return sercom->USART.STATUS.bit.BUFOVF; +#elif defined(ARDUINO_SAME53_E54) + return (sercom->USART_INT.SERCOM_STATUS & SERCOM_USART_INT_STATUS_BUFOVF_Msk) != 0; +#else + return sercom->USART.STATUS.bit.BUFOVF; +#endif } bool SERCOM::isFrameErrorUART() { //FERR : Frame Error +#if defined(ARDUINO_SAMD51_E51) return sercom->USART.STATUS.bit.FERR; +#elif defined(ARDUINO_SAME53_E54) + return (sercom->USART_INT.SERCOM_STATUS & SERCOM_USART_INT_STATUS_FERR_Msk) != 0; +#else + return sercom->USART.STATUS.bit.FERR; +#endif } void SERCOM::clearFrameErrorUART() { // clear FERR bit writing 1 status bit +#if defined(ARDUINO_SAMD51_E51) sercom->USART.STATUS.bit.FERR = 1; +#elif defined(ARDUINO_SAME53_E54) + sercom->USART_INT.SERCOM_STATUS = SERCOM_USART_INT_STATUS_FERR_Msk; +#else + sercom->USART.STATUS.bit.FERR = 1; +#endif } bool SERCOM::isParityErrorUART() { //PERR : Parity Error +#if defined(ARDUINO_SAMD51_E51) + return sercom->USART.STATUS.bit.PERR; +#elif defined(ARDUINO_SAME53_E54) + return (sercom->USART_INT.SERCOM_STATUS & SERCOM_USART_INT_STATUS_PERR_Msk) != 0; +#else return sercom->USART.STATUS.bit.PERR; +#endif } bool SERCOM::isDataRegisterEmptyUART() { //DRE : Data Register Empty +#if defined(ARDUINO_SAMD51_E51) + return sercom->USART.INTFLAG.bit.DRE; +#elif defined(ARDUINO_SAME53_E54) + return (sercom->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_DRE_Msk) != 0; +#else return sercom->USART.INTFLAG.bit.DRE; +#endif } uint8_t SERCOM::readDataUART() { +#if defined(ARDUINO_SAMD51_E51) + return sercom->USART.DATA.bit.DATA; +#elif defined(ARDUINO_SAME53_E54) + return (uint8_t)sercom->USART_INT.SERCOM_DATA; +#else return sercom->USART.DATA.bit.DATA; +#endif } int SERCOM::writeDataUART(uint8_t data) @@ -203,18 +363,36 @@ int SERCOM::writeDataUART(uint8_t data) while(!isDataRegisterEmptyUART()); //Put data into DATA register +#if defined(ARDUINO_SAMD51_E51) + sercom->USART.DATA.reg = (uint16_t)data; +#elif defined(ARDUINO_SAME53_E54) + sercom->USART_INT.SERCOM_DATA = data; +#else sercom->USART.DATA.reg = (uint16_t)data; +#endif return 1; } void SERCOM::enableDataRegisterEmptyInterruptUART() { +#if defined(ARDUINO_SAMD51_E51) sercom->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE; +#elif defined(ARDUINO_SAME53_E54) + sercom->USART_INT.SERCOM_INTENSET = SERCOM_USART_INT_INTENSET_DRE_Msk; +#else + sercom->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE; +#endif } void SERCOM::disableDataRegisterEmptyInterruptUART() { +#if defined(ARDUINO_SAMD51_E51) sercom->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_DRE; +#elif defined(ARDUINO_SAME53_E54) + sercom->USART_INT.SERCOM_INTENCLR = SERCOM_USART_INT_INTENCLR_DRE_Msk; +#else + sercom->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_DRE; +#endif } /* ========================= @@ -226,11 +404,16 @@ void SERCOM::initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize ch resetSPI(); initClockNVIC(); -#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) +#if defined(ARDUINO_SAMD51_E51) sercom->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_MODE(0x3) | // master mode SERCOM_SPI_CTRLA_DOPO(mosi) | SERCOM_SPI_CTRLA_DIPO(miso) | dataOrder << SERCOM_SPI_CTRLA_DORD_Pos; +#elif defined(ARDUINO_SAME53_E54) + sercom->SPIM.SERCOM_CTRLA = SERCOM_SPIM_CTRLA_MODE_SPI_MASTER | + SERCOM_SPIM_CTRLA_DOPO(mosi) | + SERCOM_SPIM_CTRLA_DIPO(miso) | + SERCOM_SPIM_CTRLA_DORD(dataOrder); #else //Setting the CTRLA register sercom->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_MODE_SPI_MASTER | @@ -240,10 +423,21 @@ void SERCOM::initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize ch #endif //Setting the CTRLB register +#if defined(ARDUINO_SAMD51_E51) + sercom->SPI.CTRLB.reg = SERCOM_SPI_CTRLB_CHSIZE(charSize) | + SERCOM_SPI_CTRLB_RXEN; //Active the SPI receiver. + + while( sercom->SPI.SYNCBUSY.bit.CTRLB == 1 ); +#elif defined(ARDUINO_SAME53_E54) + sercom->SPIM.SERCOM_CTRLB = SERCOM_SPIM_CTRLB_CHSIZE(charSize) | + SERCOM_SPIM_CTRLB_RXEN_Msk; + while (sercom->SPIM.SERCOM_SYNCBUSY & SERCOM_SPIM_SYNCBUSY_CTRLB_Msk); +#else sercom->SPI.CTRLB.reg = SERCOM_SPI_CTRLB_CHSIZE(charSize) | SERCOM_SPI_CTRLB_RXEN; //Active the SPI receiver. while( sercom->SPI.SYNCBUSY.bit.CTRLB == 1 ); +#endif } void SERCOM::initSPIClock(SercomSpiClockMode clockMode, uint32_t baudrate) @@ -262,24 +456,49 @@ void SERCOM::initSPIClock(SercomSpiClockMode clockMode, uint32_t baudrate) cpol = 1; //Setting the CTRLA register +#if defined(ARDUINO_SAMD51_E51) + sercom->SPI.CTRLA.reg |= ( cpha << SERCOM_SPI_CTRLA_CPHA_Pos ) | + ( cpol << SERCOM_SPI_CTRLA_CPOL_Pos ); + + //Synchronous arithmetic + sercom->SPI.BAUD.reg = calculateBaudrateSynchronous(baudrate); +#elif defined(ARDUINO_SAME53_E54) + sercom->SPIM.SERCOM_CTRLA |= SERCOM_SPIM_CTRLA_CPHA(cpha) | + SERCOM_SPIM_CTRLA_CPOL(cpol); + sercom->SPIM.SERCOM_BAUD = SERCOM_SPIM_BAUD_BAUD(calculateBaudrateSynchronous(baudrate)); +#else sercom->SPI.CTRLA.reg |= ( cpha << SERCOM_SPI_CTRLA_CPHA_Pos ) | ( cpol << SERCOM_SPI_CTRLA_CPOL_Pos ); //Synchronous arithmetic sercom->SPI.BAUD.reg = calculateBaudrateSynchronous(baudrate); +#endif } void SERCOM::resetSPI() { +#if defined(ARDUINO_SAMD51_E51) + //Setting the Software Reset bit to 1 + sercom->SPI.CTRLA.bit.SWRST = 1; + + //Wait both bits Software Reset from CTRLA and SYNCBUSY are equal to 0 + while(sercom->SPI.CTRLA.bit.SWRST || sercom->SPI.SYNCBUSY.bit.SWRST); +#elif defined(ARDUINO_SAME53_E54) + sercom->SPIM.SERCOM_CTRLA |= SERCOM_SPIM_CTRLA_SWRST_Msk; + while ((sercom->SPIM.SERCOM_CTRLA & SERCOM_SPIM_CTRLA_SWRST_Msk) || + (sercom->SPIM.SERCOM_SYNCBUSY & SERCOM_SPIM_SYNCBUSY_SWRST_Msk)); +#else //Setting the Software Reset bit to 1 sercom->SPI.CTRLA.bit.SWRST = 1; //Wait both bits Software Reset from CTRLA and SYNCBUSY are equal to 0 while(sercom->SPI.CTRLA.bit.SWRST || sercom->SPI.SYNCBUSY.bit.SWRST); +#endif } void SERCOM::enableSPI() { +#if defined(ARDUINO_SAMD51_E51) //Setting the enable bit to 1 sercom->SPI.CTRLA.bit.ENABLE = 1; @@ -287,10 +506,34 @@ void SERCOM::enableSPI() { //Waiting then enable bit from SYNCBUSY is equal to 0; } +#elif defined(ARDUINO_SAME53_E54) + sercom->SPIM.SERCOM_CTRLA |= SERCOM_SPIM_CTRLA_ENABLE_Msk; + while (sercom->SPIM.SERCOM_SYNCBUSY & SERCOM_SPIM_SYNCBUSY_ENABLE_Msk); +#else + //Setting the enable bit to 1 + sercom->SPI.CTRLA.bit.ENABLE = 1; + + while(sercom->SPI.SYNCBUSY.bit.ENABLE) + { + //Waiting then enable bit from SYNCBUSY is equal to 0; + } +#endif } void SERCOM::disableSPI() { +#if defined(ARDUINO_SAMD51_E51) + while(sercom->SPI.SYNCBUSY.bit.ENABLE) + { + //Waiting then enable bit from SYNCBUSY is equal to 0; + } + + //Setting the enable bit to 0 + sercom->SPI.CTRLA.bit.ENABLE = 0; +#elif defined(ARDUINO_SAME53_E54) + while (sercom->SPIM.SERCOM_SYNCBUSY & SERCOM_SPIM_SYNCBUSY_ENABLE_Msk); + sercom->SPIM.SERCOM_CTRLA &= ~SERCOM_SPIM_CTRLA_ENABLE_Msk; +#else while(sercom->SPI.SYNCBUSY.bit.ENABLE) { //Waiting then enable bit from SYNCBUSY is equal to 0; @@ -298,6 +541,7 @@ void SERCOM::disableSPI() //Setting the enable bit to 0 sercom->SPI.CTRLA.bit.ENABLE = 0; +#endif } void SERCOM::setDataOrderSPI(SercomDataOrder dataOrder) @@ -305,20 +549,40 @@ void SERCOM::setDataOrderSPI(SercomDataOrder dataOrder) //Register enable-protected disableSPI(); +#if defined(ARDUINO_SAMD51_E51) sercom->SPI.CTRLA.bit.DORD = dataOrder; +#elif defined(ARDUINO_SAME53_E54) + sercom->SPIM.SERCOM_CTRLA = + (sercom->SPIM.SERCOM_CTRLA & ~SERCOM_SPIM_CTRLA_DORD_Msk) | + SERCOM_SPIM_CTRLA_DORD(dataOrder); +#else + sercom->SPI.CTRLA.bit.DORD = dataOrder; +#endif enableSPI(); } SercomDataOrder SERCOM::getDataOrderSPI() { +#if defined(ARDUINO_SAMD51_E51) return (sercom->SPI.CTRLA.bit.DORD ? LSB_FIRST : MSB_FIRST); +#elif defined(ARDUINO_SAME53_E54) + return (sercom->SPIM.SERCOM_CTRLA & SERCOM_SPIM_CTRLA_DORD_Msk) ? LSB_FIRST : MSB_FIRST; +#else + return (sercom->SPI.CTRLA.bit.DORD ? LSB_FIRST : MSB_FIRST); +#endif } void SERCOM::setBaudrateSPI(uint8_t divider) { disableSPI(); // Register is enable-protected +#if defined(ARDUINO_SAMD51_E51) + sercom->SPI.BAUD.reg = calculateBaudrateSynchronous(freqRef / divider); +#elif defined(ARDUINO_SAME53_E54) + sercom->SPIM.SERCOM_BAUD = SERCOM_SPIM_BAUD_BAUD(calculateBaudrateSynchronous(freqRef / divider)); +#else sercom->SPI.BAUD.reg = calculateBaudrateSynchronous(freqRef / divider); +#endif enableSPI(); } @@ -338,30 +602,63 @@ void SERCOM::setClockModeSPI(SercomSpiClockMode clockMode) //Register enable-protected disableSPI(); +#if defined(ARDUINO_SAMD51_E51) sercom->SPI.CTRLA.bit.CPOL = cpol; sercom->SPI.CTRLA.bit.CPHA = cpha; +#elif defined(ARDUINO_SAME53_E54) + sercom->SPIM.SERCOM_CTRLA = + (sercom->SPIM.SERCOM_CTRLA & ~(SERCOM_SPIM_CTRLA_CPOL_Msk | SERCOM_SPIM_CTRLA_CPHA_Msk)) | + SERCOM_SPIM_CTRLA_CPOL(cpol) | SERCOM_SPIM_CTRLA_CPHA(cpha); +#else + sercom->SPI.CTRLA.bit.CPOL = cpol; + sercom->SPI.CTRLA.bit.CPHA = cpha; +#endif enableSPI(); } uint8_t SERCOM::transferDataSPI(uint8_t data) { +#if defined(ARDUINO_SAMD51_E51) + sercom->SPI.DATA.bit.DATA = data; // Writing data into Data register + + while(sercom->SPI.INTFLAG.bit.RXC == 0); // Waiting Complete Reception + + return sercom->SPI.DATA.bit.DATA; // Reading data +#elif defined(ARDUINO_SAME53_E54) + sercom->SPIM.SERCOM_DATA = data; + while ((sercom->SPIM.SERCOM_INTFLAG & SERCOM_SPIM_INTFLAG_RXC_Msk) == 0); + return (uint8_t)sercom->SPIM.SERCOM_DATA; +#else sercom->SPI.DATA.bit.DATA = data; // Writing data into Data register while(sercom->SPI.INTFLAG.bit.RXC == 0); // Waiting Complete Reception return sercom->SPI.DATA.bit.DATA; // Reading data +#endif } bool SERCOM::isBufferOverflowErrorSPI() { +#if defined(ARDUINO_SAMD51_E51) + return sercom->SPI.STATUS.bit.BUFOVF; +#elif defined(ARDUINO_SAME53_E54) + return sercom->SPIM.SERCOM_STATUS & SERCOM_SPIM_STATUS_BUFOVF_Msk; +#else return sercom->SPI.STATUS.bit.BUFOVF; +#endif } bool SERCOM::isDataRegisterEmptySPI() { //DRE : Data Register Empty +#if defined(ARDUINO_SAMD51_E51) return sercom->SPI.INTFLAG.bit.DRE; +#elif defined(ARDUINO_SAME53_E54) + return sercom->SPIM.SERCOM_INTFLAG & SERCOM_SPIM_INTFLAG_DRE_Msk; +#else + return sercom->SPI.INTFLAG.bit.DRE; +#endif } //bool SERCOM::isTransmitCompleteSPI() @@ -393,17 +690,30 @@ void SERCOM::resetWIRE() { //I2CM OR I2CS, no matter SWRST is the same bit. +#if defined(ARDUINO_SAMD51_E51) //Setting the Software bit to 1 sercom->I2CM.CTRLA.bit.SWRST = 1; //Wait both bits Software Reset from CTRLA and SYNCBUSY are equal to 0 while(sercom->I2CM.CTRLA.bit.SWRST || sercom->I2CM.SYNCBUSY.bit.SWRST); +#elif defined(ARDUINO_SAME53_E54) + sercom->I2CM.SERCOM_CTRLA |= SERCOM_I2CM_CTRLA_SWRST_Msk; + while ((sercom->I2CM.SERCOM_CTRLA & SERCOM_I2CM_CTRLA_SWRST_Msk) || + (sercom->I2CM.SERCOM_SYNCBUSY & SERCOM_I2CM_SYNCBUSY_SWRST_Msk)); +#else + //Setting the Software bit to 1 + sercom->I2CM.CTRLA.bit.SWRST = 1; + + //Wait both bits Software Reset from CTRLA and SYNCBUSY are equal to 0 + while(sercom->I2CM.CTRLA.bit.SWRST || sercom->I2CM.SYNCBUSY.bit.SWRST); +#endif } void SERCOM::enableWIRE() { // I2C Master and Slave modes share the ENABLE bit function. +#if defined(ARDUINO_SAMD51_E51) // Enable the I2C master mode sercom->I2CM.CTRLA.bit.ENABLE = 1 ; @@ -419,12 +729,38 @@ void SERCOM::enableWIRE() { // Wait the SYSOP bit from SYNCBUSY coming back to 0 } +#elif defined(ARDUINO_SAME53_E54) + sercom->I2CM.SERCOM_CTRLA |= SERCOM_I2CM_CTRLA_ENABLE_Msk; + while (sercom->I2CM.SERCOM_SYNCBUSY & SERCOM_I2CM_SYNCBUSY_ENABLE_Msk); + + sercom->I2CM.SERCOM_STATUS = + (sercom->I2CM.SERCOM_STATUS & ~SERCOM_I2CM_STATUS_BUSSTATE_Msk) | + SERCOM_I2CM_STATUS_BUSSTATE_IDLE; + while (sercom->I2CM.SERCOM_SYNCBUSY & SERCOM_I2CM_SYNCBUSY_SYSOP_Msk); +#else + // Enable the I2C master mode + sercom->I2CM.CTRLA.bit.ENABLE = 1 ; + + while ( sercom->I2CM.SYNCBUSY.bit.ENABLE != 0 ) + { + // Waiting the enable bit from SYNCBUSY is equal to 0; + } + + // Setting bus idle mode + sercom->I2CM.STATUS.bit.BUSSTATE = 1 ; + + while ( sercom->I2CM.SYNCBUSY.bit.SYSOP != 0 ) + { + // Wait the SYSOP bit from SYNCBUSY coming back to 0 + } +#endif } void SERCOM::disableWIRE() { // I2C Master and Slave modes share the ENABLE bit function. +#if defined(ARDUINO_SAMD51_E51) // Enable the I2C master mode sercom->I2CM.CTRLA.bit.ENABLE = 0 ; @@ -432,6 +768,18 @@ void SERCOM::disableWIRE() { // Waiting the enable bit from SYNCBUSY is equal to 0; } +#elif defined(ARDUINO_SAME53_E54) + sercom->I2CM.SERCOM_CTRLA &= ~SERCOM_I2CM_CTRLA_ENABLE_Msk; + while (sercom->I2CM.SERCOM_SYNCBUSY & SERCOM_I2CM_SYNCBUSY_ENABLE_Msk); +#else + // Enable the I2C master mode + sercom->I2CM.CTRLA.bit.ENABLE = 0 ; + + while ( sercom->I2CM.SYNCBUSY.bit.ENABLE != 0 ) + { + // Waiting the enable bit from SYNCBUSY is equal to 0; + } +#endif } void SERCOM::initSlaveWIRE( uint8_t ucAddress, bool enableGeneralCall ) @@ -441,6 +789,36 @@ void SERCOM::initSlaveWIRE( uint8_t ucAddress, bool enableGeneralCall ) resetWIRE() ; // Set slave mode +#if defined(ARDUINO_SAMD51_E51) + sercom->I2CS.CTRLA.bit.MODE = I2C_SLAVE_OPERATION; + + sercom->I2CS.ADDR.reg = SERCOM_I2CS_ADDR_ADDR( ucAddress & 0x7Ful ) | // 0x7F, select only 7 bits + SERCOM_I2CS_ADDR_ADDRMASK( 0x00ul ); // 0x00, only match exact address + if (enableGeneralCall) { + sercom->I2CS.ADDR.reg |= SERCOM_I2CS_ADDR_GENCEN; // enable general call (address 0x00) + } + + // Set the interrupt register + sercom->I2CS.INTENSET.reg = SERCOM_I2CS_INTENSET_PREC | // Stop + SERCOM_I2CS_INTENSET_AMATCH | // Address Match + SERCOM_I2CS_INTENSET_DRDY ; // Data Ready + + while ( sercom->I2CM.SYNCBUSY.bit.SYSOP != 0 ) + { + // Wait the SYSOP bit from SYNCBUSY to come back to 0 + } +#elif defined(ARDUINO_SAME53_E54) + sercom->I2CS.SERCOM_CTRLA = SERCOM_I2CS_CTRLA_MODE_I2C_SLAVE; + sercom->I2CS.SERCOM_ADDR = SERCOM_I2CS_ADDR_ADDR(ucAddress & 0x7Ful) | + SERCOM_I2CS_ADDR_ADDRMASK(0x00ul); + if (enableGeneralCall) { + sercom->I2CS.SERCOM_ADDR |= SERCOM_I2CS_ADDR_GENCEN_Msk; + } + sercom->I2CS.SERCOM_INTENSET = SERCOM_I2CS_INTENSET_PREC_Msk | + SERCOM_I2CS_INTENSET_AMATCH_Msk | + SERCOM_I2CS_INTENSET_DRDY_Msk; + while (sercom->I2CM.SERCOM_SYNCBUSY & SERCOM_I2CM_SYNCBUSY_SYSOP_Msk); +#else sercom->I2CS.CTRLA.bit.MODE = I2C_SLAVE_OPERATION; sercom->I2CS.ADDR.reg = SERCOM_I2CS_ADDR_ADDR( ucAddress & 0x7Ful ) | // 0x7F, select only 7 bits @@ -458,6 +836,7 @@ void SERCOM::initSlaveWIRE( uint8_t ucAddress, bool enableGeneralCall ) { // Wait the SYSOP bit from SYNCBUSY to come back to 0 } +#endif } void SERCOM::initMasterWIRE( uint32_t baudrate ) @@ -468,8 +847,15 @@ void SERCOM::initMasterWIRE( uint32_t baudrate ) resetWIRE() ; // Set master mode and enable SCL Clock Stretch mode (stretch after ACK bit) +#if defined(ARDUINO_SAMD51_E51) sercom->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_MODE( I2C_MASTER_OPERATION )/* | SERCOM_I2CM_CTRLA_SCLSM*/ ; +#elif defined(ARDUINO_SAME53_E54) + sercom->I2CM.SERCOM_CTRLA = SERCOM_I2CM_CTRLA_MODE_I2C_MASTER; +#else + sercom->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_MODE( I2C_MASTER_OPERATION )/* | + SERCOM_I2CM_CTRLA_SCLSM*/ ; +#endif // Enable Smart mode and Quick Command //sercom->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN /*| SERCOM_I2CM_CTRLB_QCEN*/ ; @@ -495,42 +881,103 @@ void SERCOM::initMasterWIRE( uint32_t baudrate ) clockStretchMode = 1; } +#if defined(ARDUINO_SAMD51_E51) sercom->I2CM.CTRLA.bit.SPEED = speedBit; sercom->I2CM.CTRLA.bit.SCLSM = clockStretchMode; +#elif defined(ARDUINO_SAME53_E54) + sercom->I2CM.SERCOM_CTRLA = + (sercom->I2CM.SERCOM_CTRLA & + ~(SERCOM_I2CM_CTRLA_SPEED_Msk | SERCOM_I2CM_CTRLA_SCLSM_Msk)) | + SERCOM_I2CM_CTRLA_SPEED(speedBit) | + SERCOM_I2CM_CTRLA_SCLSM(clockStretchMode); +#else + sercom->I2CM.CTRLA.bit.SPEED = speedBit; + sercom->I2CM.CTRLA.bit.SCLSM = clockStretchMode; +#endif uint32_t minBaudrate = freqRef / 512; // BAUD = 255: SAMD51(@100MHz) ~195kHz, SAMD21 ~94kHz uint32_t maxBaudrate = topSpeeds[speedBit]; baudrate = max(minBaudrate, min(baudrate, maxBaudrate)); - if (speedBit == 0x2) + if (speedBit == 0x2) { +#if defined(ARDUINO_SAMD51_E51) sercom->I2CM.BAUD.bit.HSBAUD = freqRef / (2 * baudrate) - 1; - else +#elif defined(ARDUINO_SAME53_E54) + sercom->I2CM.SERCOM_BAUD = + (sercom->I2CM.SERCOM_BAUD & ~SERCOM_I2CM_BAUD_HSBAUD_Msk) | + SERCOM_I2CM_BAUD_HSBAUD(freqRef / (2 * baudrate) - 1); +#else + sercom->I2CM.BAUD.bit.HSBAUD = freqRef / (2 * baudrate) - 1; +#endif + } else { +#if defined(ARDUINO_SAMD51_E51) sercom->I2CM.BAUD.bit.BAUD = freqRef / (2 * baudrate) - 5 - (freqRef/1000000ul * WIRE_RISE_TIME_NANOSECONDS) / 2000; +#elif defined(ARDUINO_SAME53_E54) + sercom->I2CM.SERCOM_BAUD = + (sercom->I2CM.SERCOM_BAUD & ~SERCOM_I2CM_BAUD_BAUD_Msk) | + SERCOM_I2CM_BAUD_BAUD(freqRef / (2 * baudrate) - 5 - + (freqRef/1000000ul * WIRE_RISE_TIME_NANOSECONDS) / 2000); +#else + sercom->I2CM.BAUD.bit.BAUD = freqRef / (2 * baudrate) - 5 - + (freqRef/1000000ul * WIRE_RISE_TIME_NANOSECONDS) / 2000; +#endif + } } void SERCOM::prepareNackBitWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) if(isMasterWIRE()) { // Send a NACK sercom->I2CM.CTRLB.bit.ACKACT = 1; } else { sercom->I2CS.CTRLB.bit.ACKACT = 1; } +#elif defined(ARDUINO_SAME53_E54) + if (isMasterWIRE()) { + sercom->I2CM.SERCOM_CTRLB |= SERCOM_I2CM_CTRLB_ACKACT_Msk; + } else { + sercom->I2CS.SERCOM_CTRLB |= SERCOM_I2CS_CTRLB_ACKACT_Msk; + } +#else + if(isMasterWIRE()) { + // Send a NACK + sercom->I2CM.CTRLB.bit.ACKACT = 1; + } else { + sercom->I2CS.CTRLB.bit.ACKACT = 1; + } +#endif } void SERCOM::prepareAckBitWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) + if(isMasterWIRE()) { + // Send an ACK + sercom->I2CM.CTRLB.bit.ACKACT = 0; + } else { + sercom->I2CS.CTRLB.bit.ACKACT = 0; + } +#elif defined(ARDUINO_SAME53_E54) + if (isMasterWIRE()) { + sercom->I2CM.SERCOM_CTRLB &= ~SERCOM_I2CM_CTRLB_ACKACT_Msk; + } else { + sercom->I2CS.SERCOM_CTRLB &= ~SERCOM_I2CS_CTRLB_ACKACT_Msk; + } +#else if(isMasterWIRE()) { // Send an ACK sercom->I2CM.CTRLB.bit.ACKACT = 0; } else { sercom->I2CS.CTRLB.bit.ACKACT = 0; } +#endif } void SERCOM::prepareCommandBitsWire(uint8_t cmd) { +#if defined(ARDUINO_SAMD51_E51) if(isMasterWIRE()) { sercom->I2CM.CTRLB.bit.CMD = cmd; @@ -541,6 +988,29 @@ void SERCOM::prepareCommandBitsWire(uint8_t cmd) } else { sercom->I2CS.CTRLB.bit.CMD = cmd; } +#elif defined(ARDUINO_SAME53_E54) + if (isMasterWIRE()) { + sercom->I2CM.SERCOM_CTRLB = + (sercom->I2CM.SERCOM_CTRLB & ~SERCOM_I2CM_CTRLB_CMD_Msk) | + SERCOM_I2CM_CTRLB_CMD(cmd); + while (sercom->I2CM.SERCOM_SYNCBUSY & SERCOM_I2CM_SYNCBUSY_SYSOP_Msk); + } else { + sercom->I2CS.SERCOM_CTRLB = + (sercom->I2CS.SERCOM_CTRLB & ~SERCOM_I2CS_CTRLB_CMD_Msk) | + SERCOM_I2CS_CTRLB_CMD(cmd); + } +#else + if(isMasterWIRE()) { + sercom->I2CM.CTRLB.bit.CMD = cmd; + + while(sercom->I2CM.SYNCBUSY.bit.SYSOP) + { + // Waiting for synchronization + } + } else { + sercom->I2CS.CTRLB.bit.CMD = cmd; + } +#endif } bool SERCOM::startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag) @@ -561,6 +1031,80 @@ bool SERCOM::startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag } } +#if defined(ARDUINO_SAMD51_E51) + // Send start and address + sercom->I2CM.INTFLAG.bit.ERROR = 1; + sercom->I2CM.ADDR.reg = SERCOM_I2CM_ADDR_ADDR(address) | + ((sercom->I2CM.CTRLA.bit.SPEED == 0x2) ? SERCOM_I2CM_ADDR_HS : 0); + + // Address Transmitted + if ( flag == WIRE_WRITE_FLAG ) // Write mode + { + while( !sercom->I2CM.INTFLAG.bit.MB ) { + // Wait transmission complete + + // If certain errors occur, the MB bit may never be set (RFTM: SAMD21 sec:28.10.6; SAMD51 sec:36.10.7). + // The data transfer errors that can occur (including BUSERR) are all + // rolled up into INTFLAG.bit.ERROR from STATUS.reg + if (sercom->I2CM.INTFLAG.bit.ERROR) { + return false; + } + } + } + else // Read mode + { + while( !sercom->I2CM.INTFLAG.bit.SB ) { + // Wait transmission complete + + // If the slave NACKS the address, the MB bit will be set. + // A variety of errors in the STATUS register can set the ERROR bit in the INTFLAG register + // In that case, send a stop condition and return false. + if (sercom->I2CM.INTFLAG.bit.MB || sercom->I2CM.INTFLAG.bit.ERROR) { + sercom->I2CM.CTRLB.bit.CMD = 3; // Stop condition + return false; + } + } + + // Clean the 'Slave on Bus' flag, for further usage. + //sercom->I2CM.INTFLAG.bit.SB = 0x1ul; + } + + //ACK received (0: ACK, 1: NACK) + if(sercom->I2CM.STATUS.bit.RXNACK) + { + return false; + } + else + { + return true; + } +#elif defined(ARDUINO_SAME53_E54) + sercom->I2CM.SERCOM_INTFLAG = SERCOM_I2CM_INTFLAG_ERROR_Msk; + const bool highSpeed = + (sercom->I2CM.SERCOM_CTRLA & SERCOM_I2CM_CTRLA_SPEED_Msk) == + SERCOM_I2CM_CTRLA_SPEED_HIGH_SPEED_MODE; + sercom->I2CM.SERCOM_ADDR = SERCOM_I2CM_ADDR_ADDR(address) | + (highSpeed ? SERCOM_I2CM_ADDR_HS_Msk : 0); + + if (flag == WIRE_WRITE_FLAG) { + while ((sercom->I2CM.SERCOM_INTFLAG & SERCOM_I2CM_INTFLAG_MB_Msk) == 0) { + if (sercom->I2CM.SERCOM_INTFLAG & SERCOM_I2CM_INTFLAG_ERROR_Msk) { + return false; + } + } + } else { + while ((sercom->I2CM.SERCOM_INTFLAG & SERCOM_I2CM_INTFLAG_SB_Msk) == 0) { + if (sercom->I2CM.SERCOM_INTFLAG & + (SERCOM_I2CM_INTFLAG_MB_Msk | SERCOM_I2CM_INTFLAG_ERROR_Msk)) { + sercom->I2CM.SERCOM_CTRLB = + (sercom->I2CM.SERCOM_CTRLB & ~SERCOM_I2CM_CTRLB_CMD_Msk) | + SERCOM_I2CM_CTRLB_CMD(3); + return false; + } + } + } + return (sercom->I2CM.SERCOM_STATUS & SERCOM_I2CM_STATUS_RXNACK_Msk) == 0; +#else // Send start and address sercom->I2CM.INTFLAG.bit.ERROR = 1; sercom->I2CM.ADDR.reg = SERCOM_I2CM_ADDR_ADDR(address) | @@ -607,10 +1151,12 @@ bool SERCOM::startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag { return true; } +#endif } bool SERCOM::sendDataMasterWIRE(uint8_t data) { +#if defined(ARDUINO_SAMD51_E51) //Send data sercom->I2CM.INTFLAG.bit.ERROR = 1; sercom->I2CM.DATA.bit.DATA = data; @@ -631,10 +1177,56 @@ bool SERCOM::sendDataMasterWIRE(uint8_t data) return false; else return true; +#elif defined(ARDUINO_SAME53_E54) + sercom->I2CM.SERCOM_INTFLAG = SERCOM_I2CM_INTFLAG_ERROR_Msk; + sercom->I2CM.SERCOM_DATA = data; + + while ((sercom->I2CM.SERCOM_INTFLAG & SERCOM_I2CM_INTFLAG_MB_Msk) == 0) { + if (sercom->I2CM.SERCOM_INTFLAG & SERCOM_I2CM_INTFLAG_ERROR_Msk) { + return false; + } + } + return (sercom->I2CM.SERCOM_STATUS & SERCOM_I2CM_STATUS_RXNACK_Msk) == 0; +#else + //Send data + sercom->I2CM.INTFLAG.bit.ERROR = 1; + sercom->I2CM.DATA.bit.DATA = data; + + //Wait transmission successful + while(!sercom->I2CM.INTFLAG.bit.MB) { + // If a data transfer error occurs, the MB bit may never be set. + // Check the error bit and bail if it's set. + // The data transfer errors that can occur (including BUSERR) are all + // rolled up into INTFLAG.bit.ERROR from STATUS.reg + if (sercom->I2CM.INTFLAG.bit.ERROR) { + return false; + } + } + + //Problems on line? nack received? + if(sercom->I2CM.STATUS.bit.RXNACK) + return false; + else + return true; +#endif } bool SERCOM::sendDataSlaveWIRE(uint8_t data) { +#if defined(ARDUINO_SAMD51_E51) + //Send data + sercom->I2CS.DATA.bit.DATA = data; + + //Problems on line? nack received? + if(!sercom->I2CS.INTFLAG.bit.DRDY || sercom->I2CS.STATUS.bit.RXNACK) + return false; + else + return true; +#elif defined(ARDUINO_SAME53_E54) + sercom->I2CS.SERCOM_DATA = data; + return (sercom->I2CS.SERCOM_INTFLAG & SERCOM_I2CS_INTFLAG_DRDY_Msk) && + ((sercom->I2CS.SERCOM_STATUS & SERCOM_I2CS_STATUS_RXNACK_Msk) == 0); +#else //Send data sercom->I2CS.DATA.bit.DATA = data; @@ -643,83 +1235,181 @@ bool SERCOM::sendDataSlaveWIRE(uint8_t data) return false; else return true; +#endif } bool SERCOM::isMasterWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) return sercom->I2CS.CTRLA.bit.MODE == I2C_MASTER_OPERATION; +#elif defined(ARDUINO_SAME53_E54) + return (sercom->I2CS.SERCOM_CTRLA & SERCOM_I2CS_CTRLA_MODE_Msk) == + SERCOM_I2CS_CTRLA_MODE_I2C_MASTER; +#else + return sercom->I2CS.CTRLA.bit.MODE == I2C_MASTER_OPERATION; +#endif } bool SERCOM::isSlaveWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) return sercom->I2CS.CTRLA.bit.MODE == I2C_SLAVE_OPERATION; +#elif defined(ARDUINO_SAME53_E54) + return (sercom->I2CS.SERCOM_CTRLA & SERCOM_I2CS_CTRLA_MODE_Msk) == + SERCOM_I2CS_CTRLA_MODE_I2C_SLAVE; +#else + return sercom->I2CS.CTRLA.bit.MODE == I2C_SLAVE_OPERATION; +#endif } bool SERCOM::isBusIdleWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) + return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_IDLE_STATE; +#elif defined(ARDUINO_SAME53_E54) + return (sercom->I2CM.SERCOM_STATUS & SERCOM_I2CM_STATUS_BUSSTATE_Msk) == + SERCOM_I2CM_STATUS_BUSSTATE_IDLE; +#else return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_IDLE_STATE; +#endif } bool SERCOM::isBusOwnerWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) + return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_OWNER_STATE; +#elif defined(ARDUINO_SAME53_E54) + return (sercom->I2CM.SERCOM_STATUS & SERCOM_I2CM_STATUS_BUSSTATE_Msk) == + SERCOM_I2CM_STATUS_BUSSTATE_OWNER; +#else return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_OWNER_STATE; +#endif } bool SERCOM::isBusUnknownWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) + return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_UNKNOWN_STATE; +#elif defined(ARDUINO_SAME53_E54) + return (sercom->I2CM.SERCOM_STATUS & SERCOM_I2CM_STATUS_BUSSTATE_Msk) == + SERCOM_I2CM_STATUS_BUSSTATE_UNKNOWN; +#else return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_UNKNOWN_STATE; +#endif } bool SERCOM::isArbLostWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) return sercom->I2CM.STATUS.bit.ARBLOST == 1; +#elif defined(ARDUINO_SAME53_E54) + return sercom->I2CM.SERCOM_STATUS & SERCOM_I2CM_STATUS_ARBLOST_Msk; +#else + return sercom->I2CM.STATUS.bit.ARBLOST == 1; +#endif } bool SERCOM::isBusBusyWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_BUSY_STATE; +#elif defined(ARDUINO_SAME53_E54) + return (sercom->I2CM.SERCOM_STATUS & SERCOM_I2CM_STATUS_BUSSTATE_Msk) == + SERCOM_I2CM_STATUS_BUSSTATE_BUSY; +#else + return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_BUSY_STATE; +#endif } bool SERCOM::isDataReadyWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) return sercom->I2CS.INTFLAG.bit.DRDY; +#elif defined(ARDUINO_SAME53_E54) + return sercom->I2CS.SERCOM_INTFLAG & SERCOM_I2CS_INTFLAG_DRDY_Msk; +#else + return sercom->I2CS.INTFLAG.bit.DRDY; +#endif } bool SERCOM::isStopDetectedWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) + return sercom->I2CS.INTFLAG.bit.PREC; +#elif defined(ARDUINO_SAME53_E54) + return sercom->I2CS.SERCOM_INTFLAG & SERCOM_I2CS_INTFLAG_PREC_Msk; +#else return sercom->I2CS.INTFLAG.bit.PREC; +#endif } bool SERCOM::isRestartDetectedWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) + return sercom->I2CS.STATUS.bit.SR; +#elif defined(ARDUINO_SAME53_E54) + return sercom->I2CS.SERCOM_STATUS & SERCOM_I2CS_STATUS_SR_Msk; +#else return sercom->I2CS.STATUS.bit.SR; +#endif } bool SERCOM::isAddressMatch( void ) { +#if defined(ARDUINO_SAMD51_E51) return sercom->I2CS.INTFLAG.bit.AMATCH; +#elif defined(ARDUINO_SAME53_E54) + return sercom->I2CS.SERCOM_INTFLAG & SERCOM_I2CS_INTFLAG_AMATCH_Msk; +#else + return sercom->I2CS.INTFLAG.bit.AMATCH; +#endif } bool SERCOM::isMasterReadOperationWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) return sercom->I2CS.STATUS.bit.DIR; +#elif defined(ARDUINO_SAME53_E54) + return sercom->I2CS.SERCOM_STATUS & SERCOM_I2CS_STATUS_DIR_Msk; +#else + return sercom->I2CS.STATUS.bit.DIR; +#endif } bool SERCOM::isRXNackReceivedWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) return sercom->I2CM.STATUS.bit.RXNACK; +#elif defined(ARDUINO_SAME53_E54) + return sercom->I2CM.SERCOM_STATUS & SERCOM_I2CM_STATUS_RXNACK_Msk; +#else + return sercom->I2CM.STATUS.bit.RXNACK; +#endif } int SERCOM::availableWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) + if(isMasterWIRE()) + return sercom->I2CM.INTFLAG.bit.SB; + else + return sercom->I2CS.INTFLAG.bit.DRDY; +#elif defined(ARDUINO_SAME53_E54) + if (isMasterWIRE()) { + return (sercom->I2CM.SERCOM_INTFLAG & SERCOM_I2CM_INTFLAG_SB_Msk) != 0; + } + return (sercom->I2CS.SERCOM_INTFLAG & SERCOM_I2CS_INTFLAG_DRDY_Msk) != 0; +#else if(isMasterWIRE()) return sercom->I2CM.INTFLAG.bit.SB; else return sercom->I2CS.INTFLAG.bit.DRDY; +#endif } uint8_t SERCOM::readDataWIRE( void ) { +#if defined(ARDUINO_SAMD51_E51) if(isMasterWIRE()) { while (sercom->I2CM.INTFLAG.bit.SB == 0) { @@ -741,9 +1431,46 @@ uint8_t SERCOM::readDataWIRE( void ) { return sercom->I2CS.DATA.reg ; } +#elif defined(ARDUINO_SAME53_E54) + if (isMasterWIRE()) { + while ((sercom->I2CM.SERCOM_INTFLAG & SERCOM_I2CM_INTFLAG_SB_Msk) == 0) { + if (sercom->I2CM.SERCOM_INTFLAG & + (SERCOM_I2CM_INTFLAG_MB_Msk | SERCOM_I2CM_INTFLAG_ERROR_Msk)) { + sercom->I2CM.SERCOM_CTRLB = + (sercom->I2CM.SERCOM_CTRLB & ~SERCOM_I2CM_CTRLB_CMD_Msk) | + SERCOM_I2CM_CTRLB_CMD(3); + return 0xFF; + } + } + return (uint8_t)sercom->I2CM.SERCOM_DATA; + } + return (uint8_t)sercom->I2CS.SERCOM_DATA; +#else + if(isMasterWIRE()) + { + while (sercom->I2CM.INTFLAG.bit.SB == 0) { + // Waiting complete receive + // A variety of errors in the STATUS register can set the ERROR bit in the INTFLAG register + // In that case, send a stop condition and return false. + // readDataWIRE should really be able to indicate an error (which would never be used + // because the readDataWIRE callers (in Wire.cpp) should have checked availableWIRE() first and timed it + // out if the data never showed up + if (sercom->I2CM.INTFLAG.bit.MB || sercom->I2CM.INTFLAG.bit.ERROR) { + sercom->I2CM.CTRLB.bit.CMD = 3; // Stop condition + return 0xFF; + } + } + + return sercom->I2CM.DATA.bit.DATA ; + } + else + { + return sercom->I2CS.DATA.reg ; + } +#endif } -#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) +#if defined(ARDUINO_SAMD51_E51) static const struct { Sercom *sercomPtr; @@ -773,6 +1500,32 @@ static const struct { #endif }; +#elif defined(ARDUINO_SAME53_E54) + +static const struct { + sercom_registers_t *sercomPtr; + uint8_t id_core; + uint8_t id_slow; + IRQn_Type irq[4]; +} sercomData[] = { + { SERCOM0_REGS, SERCOM0_GCLK_ID_CORE, SERCOM0_GCLK_ID_SLOW, + SERCOM0_0_IRQn, SERCOM0_1_IRQn, SERCOM0_2_IRQn, SERCOM0_OTHER_IRQn }, + { SERCOM1_REGS, SERCOM1_GCLK_ID_CORE, SERCOM1_GCLK_ID_SLOW, + SERCOM1_0_IRQn, SERCOM1_1_IRQn, SERCOM1_2_IRQn, SERCOM1_OTHER_IRQn }, + { SERCOM2_REGS, SERCOM2_GCLK_ID_CORE, SERCOM2_GCLK_ID_SLOW, + SERCOM2_0_IRQn, SERCOM2_1_IRQn, SERCOM2_2_IRQn, SERCOM2_OTHER_IRQn }, + { SERCOM3_REGS, SERCOM3_GCLK_ID_CORE, SERCOM3_GCLK_ID_SLOW, + SERCOM3_0_IRQn, SERCOM3_1_IRQn, SERCOM3_2_IRQn, SERCOM3_OTHER_IRQn }, + { SERCOM4_REGS, SERCOM4_GCLK_ID_CORE, SERCOM4_GCLK_ID_SLOW, + SERCOM4_0_IRQn, SERCOM4_1_IRQn, SERCOM4_2_IRQn, SERCOM4_OTHER_IRQn }, + { SERCOM5_REGS, SERCOM5_GCLK_ID_CORE, SERCOM5_GCLK_ID_SLOW, + SERCOM5_0_IRQn, SERCOM5_1_IRQn, SERCOM5_2_IRQn, SERCOM5_OTHER_IRQn }, + { SERCOM6_REGS, SERCOM6_GCLK_ID_CORE, SERCOM6_GCLK_ID_SLOW, + SERCOM6_0_IRQn, SERCOM6_1_IRQn, SERCOM6_2_IRQn, SERCOM6_OTHER_IRQn }, + { SERCOM7_REGS, SERCOM7_GCLK_ID_CORE, SERCOM7_GCLK_ID_SLOW, + SERCOM7_0_IRQn, SERCOM7_1_IRQn, SERCOM7_2_IRQn, SERCOM7_OTHER_IRQn }, +}; + #else // end if SAMD51 (prob SAMD21) static const struct { @@ -803,14 +1556,21 @@ int8_t SERCOM::getSercomIndex(void) { uint32_t SERCOM::getSercomFreqRef(void) { -#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) int8_t idx = getSercomIndex(); uint8_t gen = 1; // default to GCLK1 (48 MHz) if we can't resolve if (idx >= 0) { uint8_t pch = sercomData[idx].id_core; +#if defined(ARDUINO_SAMD51_E51) + gen = GCLK->PCHCTRL[pch].bit.GEN; +#elif defined(ARDUINO_SAME53_E54) + gen = (GCLK_REGS->GCLK_PCHCTRL[pch] & GCLK_PCHCTRL_GEN_Msk) >> + GCLK_PCHCTRL_GEN_Pos; +#else gen = GCLK->PCHCTRL[pch].bit.GEN; +#endif } switch (gen) @@ -841,7 +1601,7 @@ uint32_t SERCOM::getSercomFreqRef(void) return freqRef; } -#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) // This is currently for overriding an SPI SERCOM's clock source only -- // NOT for UART or WIRE SERCOMs, where it will have unintended consequences. // It does not check. @@ -854,8 +1614,16 @@ void SERCOM::setClockSource(int8_t idx, SercomClockSource src, bool core) { uint8_t clk_id = core ? sercomData[idx].id_core : sercomData[idx].id_slow; +#if defined(ARDUINO_SAMD51_E51) + GCLK->PCHCTRL[clk_id].bit.CHEN = 0; // Disable timer + while(GCLK->PCHCTRL[clk_id].bit.CHEN); // Wait for disable +#elif defined(ARDUINO_SAME53_E54) + GCLK_REGS->GCLK_PCHCTRL[clk_id] &= ~GCLK_PCHCTRL_CHEN_Msk; + while (GCLK_REGS->GCLK_PCHCTRL[clk_id] & GCLK_PCHCTRL_CHEN_Msk); +#else GCLK->PCHCTRL[clk_id].bit.CHEN = 0; // Disable timer while(GCLK->PCHCTRL[clk_id].bit.CHEN); // Wait for disable +#endif if(core) clockSource = src; // Save SercomClockSource value @@ -866,31 +1634,77 @@ void SERCOM::setClockSource(int8_t idx, SercomClockSource src, bool core) { // GCLK3 = XOSC32K // GCLK4 = 12 MHz if(src == SERCOM_CLOCK_SOURCE_FCPU) { +#if defined(ARDUINO_SAMD51_E51) GCLK->PCHCTRL[clk_id].reg = GCLK_PCHCTRL_GEN_GCLK2_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); // Guard Sercom from exceeding 100 MHz maximum +#elif defined(ARDUINO_SAME53_E54) + GCLK_REGS->GCLK_PCHCTRL[clk_id] = GCLK_PCHCTRL_GEN_GCLK2 | + GCLK_PCHCTRL_CHEN_Msk; +#else + GCLK->PCHCTRL[clk_id].reg = + GCLK_PCHCTRL_GEN_GCLK2_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); // Guard Sercom from exceeding 100 MHz maximum +#endif if (core) freqRef = 100000000; // Save clock frequency value } else if (src == SERCOM_CLOCK_SOURCE_48M) { +#if defined(ARDUINO_SAMD51_E51) GCLK->PCHCTRL[clk_id].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); +#elif defined(ARDUINO_SAME53_E54) + GCLK_REGS->GCLK_PCHCTRL[clk_id] = GCLK_PCHCTRL_GEN_GCLK1 | + GCLK_PCHCTRL_CHEN_Msk; +#else + GCLK->PCHCTRL[clk_id].reg = + GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); +#endif if(core) freqRef = 48000000; } else if(src == SERCOM_CLOCK_SOURCE_100M) { +#if defined(ARDUINO_SAMD51_E51) GCLK->PCHCTRL[clk_id].reg = GCLK_PCHCTRL_GEN_GCLK2_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); +#elif defined(ARDUINO_SAME53_E54) + GCLK_REGS->GCLK_PCHCTRL[clk_id] = GCLK_PCHCTRL_GEN_GCLK2 | + GCLK_PCHCTRL_CHEN_Msk; +#else + GCLK->PCHCTRL[clk_id].reg = + GCLK_PCHCTRL_GEN_GCLK2_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); +#endif if(core) freqRef = 100000000; } else if(src == SERCOM_CLOCK_SOURCE_32K) { +#if defined(ARDUINO_SAMD51_E51) GCLK->PCHCTRL[clk_id].reg = GCLK_PCHCTRL_GEN_GCLK3_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); +#elif defined(ARDUINO_SAME53_E54) + GCLK_REGS->GCLK_PCHCTRL[clk_id] = GCLK_PCHCTRL_GEN_GCLK3 | + GCLK_PCHCTRL_CHEN_Msk; +#else + GCLK->PCHCTRL[clk_id].reg = + GCLK_PCHCTRL_GEN_GCLK3_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); +#endif if(core) freqRef = 32768; } else if(src == SERCOM_CLOCK_SOURCE_12M) { +#if defined(ARDUINO_SAMD51_E51) GCLK->PCHCTRL[clk_id].reg = GCLK_PCHCTRL_GEN_GCLK4_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); +#elif defined(ARDUINO_SAME53_E54) + GCLK_REGS->GCLK_PCHCTRL[clk_id] = GCLK_PCHCTRL_GEN_GCLK4 | + GCLK_PCHCTRL_CHEN_Msk; +#else + GCLK->PCHCTRL[clk_id].reg = + GCLK_PCHCTRL_GEN_GCLK4_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); +#endif if(core) freqRef = 12000000; } +#if defined(ARDUINO_SAMD51_E51) while(!GCLK->PCHCTRL[clk_id].bit.CHEN); // Wait for clock enable +#elif defined(ARDUINO_SAME53_E54) + while ((GCLK_REGS->GCLK_PCHCTRL[clk_id] & GCLK_PCHCTRL_CHEN_Msk) == 0); +#else + while(!GCLK->PCHCTRL[clk_id].bit.CHEN); // Wait for clock enable +#endif } #endif @@ -899,7 +1713,7 @@ void SERCOM::initClockNVIC( void ) int8_t idx = getSercomIndex(); if(idx < 0) return; // We got a problem here -#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) for(uint8_t i=0; i<4; i++) { NVIC_ClearPendingIRQ(sercomData[idx].irq[i]); diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index 35c90f1c4..a1114b43d 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -168,7 +168,13 @@ typedef enum { class SERCOM { public: +#if defined(ARDUINO_SAMD51_E51) SERCOM(Sercom* s) ; +#elif defined(ARDUINO_SAME53_E54) + SERCOM(sercom_registers_t* s) ; +#else + SERCOM(Sercom* s) ; +#endif /* ========== UART ========== */ void initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint32_t baudrate=0) ; @@ -238,7 +244,7 @@ class SERCOM uint8_t readDataWIRE( void ) ; int8_t getSercomIndex(void); uint32_t getSercomFreqRef(void); -#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) // SERCOM clock source override is only available on // SAMD51 (not 21) ... but these functions are declared // regardless so user code doesn't need ifdefs or lengthy @@ -255,9 +261,15 @@ class SERCOM #endif private: +#if defined(ARDUINO_SAMD51_E51) Sercom *sercom; +#elif defined(ARDUINO_SAME53_E54) + sercom_registers_t *sercom; +#else + Sercom *sercom; +#endif uint32_t freqRef = 48000000ul; // Frequency corresponding to clockSource -#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) SercomClockSource clockSource; #endif uint8_t calculateBaudrateSynchronous(uint32_t baudrate); diff --git a/cores/arduino/Tone.cpp b/cores/arduino/Tone.cpp index b47a1a278..6744edc7b 100644 --- a/cores/arduino/Tone.cpp +++ b/cores/arduino/Tone.cpp @@ -30,14 +30,20 @@ volatile int64_t toggleCount; volatile bool toneIsActive = false; volatile bool firstTimeRunning = false; -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) #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); +#elif defined(ARDUINO_SAME53_E54) + #define TONE_TC TC0_REGS + #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.TC_SYNCBUSY); #else #define TONE_TC TC5 #define TONE_TC_IRQn TC5_IRQn @@ -49,7 +55,18 @@ volatile bool firstTimeRunning = false; #define TONE_TC_TOP 0xFFFF #define TONE_TC_CHANNEL 0 -static inline void resetTC (Tc* TCx) +#if defined(ARDUINO_SAME53_E54) +static inline void resetTC(tc_registers_t *TCx) +{ + TCx->COUNT16.TC_CTRLA &= ~TC_CTRLA_ENABLE_Msk; + WAIT_TC16_REGS_SYNC(TCx) + + TCx->COUNT16.TC_CTRLA = TC_CTRLA_SWRST_Msk; + WAIT_TC16_REGS_SYNC(TCx) + while (TCx->COUNT16.TC_CTRLA & TC_CTRLA_SWRST_Msk); +} +#else +static inline void resetTC(Tc *TCx) { // Disable TCx TCx->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; @@ -60,6 +77,7 @@ static inline void resetTC (Tc* TCx) WAIT_TC16_REGS_SYNC(TCx) while (TCx->COUNT16.CTRLA.bit.SWRST); } +#endif void toneAccurateClock (uint32_t accurateSystemCoreClockFrequency) { @@ -86,8 +104,11 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration) NVIC_SetPriority(TONE_TC_IRQn, 5); -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) GCLK->PCHCTRL[TONE_TC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); +#elif defined(ARDUINO_SAME53_E54) + GCLK_REGS->GCLK_PCHCTRL[TONE_TC_GCLK_ID] = GCLK_PCHCTRL_GEN_GCLK0 | + GCLK_PCHCTRL_CHEN_Msk; #else // Enable GCLK for TC4 and TC5 (timer counter input clock) GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5)); @@ -150,24 +171,43 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration) uint16_t tmpReg = 0; tmpReg |= TC_CTRLA_MODE_COUNT16; // Set Timer counter Mode to 16 bits -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) TONE_TC->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ; // Set TONE_TC mode as match frequency +#elif defined(ARDUINO_SAME53_E54) + TONE_TC->COUNT16.TC_WAVE = TC_WAVE_WAVEGEN_MFRQ; #else tmpReg |= TC_CTRLA_WAVEGEN_MFRQ; // Set TONE_TC mode as match frequency #endif tmpReg |= prescalerConfigBits; +#if defined(ARDUINO_SAME53_E54) + TONE_TC->COUNT16.TC_CTRLA |= tmpReg; +#else TONE_TC->COUNT16.CTRLA.reg |= tmpReg; +#endif WAIT_TC16_REGS_SYNC(TONE_TC) +#if defined(ARDUINO_SAME53_E54) + TONE_TC->COUNT16.TC_CC[TONE_TC_CHANNEL] = (uint16_t)ccValue; +#else TONE_TC->COUNT16.CC[TONE_TC_CHANNEL].reg = (uint16_t) ccValue; +#endif WAIT_TC16_REGS_SYNC(TONE_TC) +#if defined(ARDUINO_SAME53_E54) + portToggleRegister = &PORT_REGS->GROUP[g_APinDescription[outputPin].ulPort].PORT_OUTTGL; + portClearRegister = &PORT_REGS->GROUP[g_APinDescription[outputPin].ulPort].PORT_OUTCLR; +#else portToggleRegister = &(PORT->Group[g_APinDescription[outputPin].ulPort].OUTTGL.reg); portClearRegister = &(PORT->Group[g_APinDescription[outputPin].ulPort].OUTCLR.reg); +#endif portBitMask = (1ul << g_APinDescription[outputPin].ulPin); // Enable the TONE_TC interrupt request +#if defined(ARDUINO_SAME53_E54) + TONE_TC->COUNT16.TC_INTENSET = TC_INTENSET_MC0_Msk; +#else TONE_TC->COUNT16.INTENSET.bit.MC0 = 1; +#endif if (outputPin != lastOutputPin) { @@ -178,7 +218,11 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration) } // Enable TONE_TC +#if defined(ARDUINO_SAME53_E54) + TONE_TC->COUNT16.TC_CTRLA |= TC_CTRLA_ENABLE_Msk; +#else TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; +#endif WAIT_TC16_REGS_SYNC(TONE_TC) NVIC_EnableIRQ(TONE_TC_IRQn); @@ -219,7 +263,11 @@ void Tone_Handler (void) --toggleCount; // Clear the interrupt +#if defined(ARDUINO_SAME53_E54) + TONE_TC->COUNT16.TC_INTFLAG = TC_INTFLAG_MC0_Msk; +#else TONE_TC->COUNT16.INTFLAG.bit.MC0 = 1; +#endif } else { diff --git a/cores/arduino/Uart.cpp b/cores/arduino/Uart.cpp index dd797da4d..9e2a4d824 100644 --- a/cores/arduino/Uart.cpp +++ b/cores/arduino/Uart.cpp @@ -60,8 +60,13 @@ void Uart::begin(unsigned long baudrate, uint16_t config) pinMode(uc_pinRTS, OUTPUT); EPortType rtsPort = g_APinDescription[uc_pinRTS].ulPort; +#if defined(ARDUINO_SAME53_E54) + pul_outsetRTS = &PORT_REGS->GROUP[rtsPort].PORT_OUTSET; + pul_outclrRTS = &PORT_REGS->GROUP[rtsPort].PORT_OUTCLR; +#else pul_outsetRTS = &PORT->Group[rtsPort].OUTSET.reg; pul_outclrRTS = &PORT->Group[rtsPort].OUTCLR.reg; +#endif ul_pinMaskRTS = (1ul << g_APinDescription[uc_pinRTS].ulPin); *pul_outclrRTS = ul_pinMaskRTS; diff --git a/cores/arduino/WInterrupts.c b/cores/arduino/WInterrupts.c index 85b744673..40fb96aed 100644 --- a/cores/arduino/WInterrupts.c +++ b/cores/arduino/WInterrupts.c @@ -34,7 +34,7 @@ static void __initialize() memset(ISRcallback, 0, sizeof(ISRcallback)); nints = 0; -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) ///EIC MCLK is enabled by default for (uint32_t i = 0; i <= 15; i++) // EIC_0_IRQn = 12 ... EIC_15_IRQn = 27 { @@ -46,6 +46,19 @@ static void __initialize() } GCLK->PCHCTRL[EIC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK2_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); +#elif defined(ARDUINO_SAME53_E54) + // EIC MCLK is enabled by default. + for (uint32_t i = 0; i <= 15; i++) + { + IRQn_Type irqn = (IRQn_Type)(EIC_EXTINT_0_IRQn + i); + NVIC_DisableIRQ(irqn); + NVIC_ClearPendingIRQ(irqn); + NVIC_SetPriority(irqn, 0); + NVIC_EnableIRQ(irqn); + } + + GCLK_REGS->GCLK_PCHCTRL[EIC_GCLK_ID] = GCLK_PCHCTRL_GEN_GCLK2 | + GCLK_PCHCTRL_CHEN_Msk; #else NVIC_DisableIRQ(EIC_IRQn); NVIC_ClearPendingIRQ(EIC_IRQn); @@ -63,9 +76,12 @@ static void __initialize() */ // Enable EIC -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) EIC->CTRLA.bit.ENABLE = 1; while (EIC->SYNCBUSY.bit.ENABLE == 1) { } +#elif defined(ARDUINO_SAME53_E54) + EIC_REGS->EIC_CTRLA |= EIC_CTRLA_ENABLE_Msk; + while (EIC_REGS->EIC_SYNCBUSY & EIC_SYNCBUSY_ENABLE_Msk) { } #else EIC->CTRL.bit.ENABLE = 1; while (EIC->STATUS.bit.SYNCBUSY == 1) { } @@ -95,7 +111,7 @@ void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode) } uint32_t inMask = (1UL << in); // Enable wakeup capability on pin in case being used during sleep - #if defined(__SAMD51__) + #if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) //I believe this is done automatically #else EIC->WAKEUP.reg |= (1 << in); @@ -107,29 +123,45 @@ void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode) if (callback) { if (in == EXTERNAL_INT_NMI) { + uint8_t sense; +#if defined(ARDUINO_SAME53_E54) + EIC_REGS->EIC_NMIFLAG = EIC_NMIFLAG_NMI_Msk; +#else EIC->NMIFLAG.bit.NMI = 1; // Clear flag +#endif switch (mode) { case LOW: - EIC->NMICTRL.bit.NMISENSE = EIC_NMICTRL_NMISENSE_LOW; + sense = EIC_NMICTRL_NMISENSE_LOW_Val; break; case HIGH: - EIC->NMICTRL.bit.NMISENSE = EIC_NMICTRL_NMISENSE_HIGH; + sense = EIC_NMICTRL_NMISENSE_HIGH_Val; break; case CHANGE: - EIC->NMICTRL.bit.NMISENSE = EIC_NMICTRL_NMISENSE_BOTH; + sense = EIC_NMICTRL_NMISENSE_BOTH_Val; break; case FALLING: - EIC->NMICTRL.bit.NMISENSE = EIC_NMICTRL_NMISENSE_FALL; + sense = EIC_NMICTRL_NMISENSE_FALL_Val; break; case RISING: - EIC->NMICTRL.bit.NMISENSE = EIC_NMICTRL_NMISENSE_RISE; + sense = EIC_NMICTRL_NMISENSE_RISE_Val; break; + + default: + return; } +#if defined(ARDUINO_SAME53_E54) + EIC_REGS->EIC_NMICTRL = + (EIC_REGS->EIC_NMICTRL & ~EIC_NMICTRL_NMISENSE_Msk) | + EIC_NMICTRL_NMISENSE(sense); +#else + EIC->NMICTRL.bit.NMISENSE = sense; +#endif + // Assign callback to interrupt ISRcallback[EXTERNAL_INT_NMI] = callback; @@ -164,42 +196,49 @@ void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode) pos = in << 2; } - #if defined (__SAMD51__) - EIC->CTRLA.bit.ENABLE = 0; - while (EIC->SYNCBUSY.bit.ENABLE == 1) { } - #endif - - EIC->CONFIG[config].reg &=~ (EIC_CONFIG_SENSE0_Msk << pos); // Reset sense mode, important when changing trigger mode during runtime + uint8_t sense; switch (mode) { - case LOW: - EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_LOW_Val << pos; - break; - - case HIGH: - EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_HIGH_Val << pos; - break; - - case CHANGE: - EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_BOTH_Val << pos; - break; + case LOW: sense = EIC_CONFIG_SENSE0_LOW_Val; break; + case HIGH: sense = EIC_CONFIG_SENSE0_HIGH_Val; break; + case CHANGE: sense = EIC_CONFIG_SENSE0_BOTH_Val; break; + case FALLING: sense = EIC_CONFIG_SENSE0_FALL_Val; break; + case RISING: sense = EIC_CONFIG_SENSE0_RISE_Val; break; + default: return; + } - case FALLING: - EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_FALL_Val << pos; - break; + #if defined(ARDUINO_SAMD51_E51) + EIC->CTRLA.bit.ENABLE = 0; + while (EIC->SYNCBUSY.bit.ENABLE == 1) { } + #elif defined(ARDUINO_SAME53_E54) + EIC_REGS->EIC_CTRLA &= ~EIC_CTRLA_ENABLE_Msk; + while (EIC_REGS->EIC_SYNCBUSY & EIC_SYNCBUSY_ENABLE_Msk) { } + #endif - case RISING: - EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_RISE_Val << pos; - break; - } +#if defined(ARDUINO_SAME53_E54) + EIC_REGS->EIC_CONFIG[config] = + (EIC_REGS->EIC_CONFIG[config] & ~(EIC_CONFIG_SENSE0_Msk << pos)) | + ((uint32_t)sense << pos); +#else + EIC->CONFIG[config].reg = + (EIC->CONFIG[config].reg & ~(EIC_CONFIG_SENSE0_Msk << pos)) | + ((uint32_t)sense << pos); +#endif } // Enable the interrupt +#if defined(ARDUINO_SAME53_E54) + EIC_REGS->EIC_INTENSET = EIC_INTENSET_EXTINT(1 << in); +#else EIC->INTENSET.reg = EIC_INTENSET_EXTINT(1 << in); +#endif } - #if defined (__SAMD51__) + #if defined(ARDUINO_SAMD51_E51) EIC->CTRLA.bit.ENABLE = 1; while (EIC->SYNCBUSY.bit.ENABLE == 1) { } + #elif defined(ARDUINO_SAME53_E54) + EIC_REGS->EIC_CTRLA |= EIC_CTRLA_ENABLE_Msk; + while (EIC_REGS->EIC_SYNCBUSY & EIC_SYNCBUSY_ENABLE_Msk) { } #endif } @@ -216,12 +255,20 @@ void detachInterrupt(uint32_t pin) if (in == NOT_AN_INTERRUPT) return; if(in == EXTERNAL_INT_NMI) { +#if defined(ARDUINO_SAME53_E54) + EIC_REGS->EIC_NMICTRL &= ~EIC_NMICTRL_NMISENSE_Msk; +#else EIC->NMICTRL.bit.NMISENSE = 0; // Turn off detection +#endif } else { +#if defined(ARDUINO_SAME53_E54) + EIC_REGS->EIC_INTENCLR = EIC_INTENCLR_EXTINT(1 << in); +#else EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT(1 << in); +#endif // Disable wakeup capability on pin during sleep -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) //I believe this is done automatically #else // Disable wakeup capability on pin during sleep @@ -249,7 +296,7 @@ void detachInterrupt(uint32_t pin) /* * External Interrupt Controller NVIC Interrupt Handler */ -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) void InterruptHandler(uint32_t unused_i) { (void)unused_i; @@ -259,16 +306,25 @@ void InterruptHandler(uint32_t unused_i) // Loop over all enabled interrupts in the list for (uint32_t i=0; iEIC_INTFLAG & ISRlist[i]) != 0) + #else if ((EIC->INTFLAG.reg & ISRlist[i]) != 0) + #endif { // Call the callback function ISRcallback[i](); // Clear the interrupt + #if defined(ARDUINO_SAME53_E54) + EIC_REGS->EIC_INTFLAG = ISRlist[i]; + #else EIC->INTFLAG.reg = ISRlist[i]; + #endif } } } +#if defined(ARDUINO_SAMD51_E51) void EIC_0_Handler(void) { InterruptHandler(EXTERNAL_INT_0); @@ -348,6 +404,24 @@ void EIC_15_Handler(void) { InterruptHandler(EXTERNAL_INT_15); } +#elif defined(ARDUINO_SAME53_E54) +void EIC_EXTINT_0_Handler(void) { InterruptHandler(EXTERNAL_INT_0); } +void EIC_EXTINT_1_Handler(void) { InterruptHandler(EXTERNAL_INT_1); } +void EIC_EXTINT_2_Handler(void) { InterruptHandler(EXTERNAL_INT_2); } +void EIC_EXTINT_3_Handler(void) { InterruptHandler(EXTERNAL_INT_3); } +void EIC_EXTINT_4_Handler(void) { InterruptHandler(EXTERNAL_INT_4); } +void EIC_EXTINT_5_Handler(void) { InterruptHandler(EXTERNAL_INT_5); } +void EIC_EXTINT_6_Handler(void) { InterruptHandler(EXTERNAL_INT_6); } +void EIC_EXTINT_7_Handler(void) { InterruptHandler(EXTERNAL_INT_7); } +void EIC_EXTINT_8_Handler(void) { InterruptHandler(EXTERNAL_INT_8); } +void EIC_EXTINT_9_Handler(void) { InterruptHandler(EXTERNAL_INT_9); } +void EIC_EXTINT_10_Handler(void) { InterruptHandler(EXTERNAL_INT_10); } +void EIC_EXTINT_11_Handler(void) { InterruptHandler(EXTERNAL_INT_11); } +void EIC_EXTINT_12_Handler(void) { InterruptHandler(EXTERNAL_INT_12); } +void EIC_EXTINT_13_Handler(void) { InterruptHandler(EXTERNAL_INT_13); } +void EIC_EXTINT_14_Handler(void) { InterruptHandler(EXTERNAL_INT_14); } +void EIC_EXTINT_15_Handler(void) { InterruptHandler(EXTERNAL_INT_15); } +#endif #else void EIC_Handler(void) diff --git a/cores/arduino/WVariant.h b/cores/arduino/WVariant.h index cb7202d84..47155957e 100644 --- a/cores/arduino/WVariant.h +++ b/cores/arduino/WVariant.h @@ -23,6 +23,23 @@ #include "sam.h" #include +#if defined(ARDUINO_SAMD51_E51) +enum { + ARDUINO_TCC_INSTANCE_COUNT = TCC_INST_NUM, + ARDUINO_TC_INSTANCE_COUNT = TC_INST_NUM, +}; +#elif defined(ARDUINO_SAME53_E54) +enum { + ARDUINO_TCC_INSTANCE_COUNT = 5, + ARDUINO_TC_INSTANCE_COUNT = 8, +}; +#else +enum { + ARDUINO_TCC_INSTANCE_COUNT = TCC_INST_NUM, + ARDUINO_TC_INSTANCE_COUNT = TC_INST_NUM, +}; +#endif + #ifdef __cplusplus extern "C" { #endif @@ -39,18 +56,30 @@ typedef enum _EAnalogChannel ADC_Channel5=5, ADC_Channel6=6, ADC_Channel7=7, -#if defined __SAMD21J18A__ || defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) ADC_Channel8=8, ADC_Channel9=9, -#endif // __SAMD21J18A__ +#else + #if defined(__SAMD21J18A__) + ADC_Channel8=8, + ADC_Channel9=9, + #endif +#endif ADC_Channel10=10, ADC_Channel11=11, -#if defined __SAMD21J18A__ || defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) ADC_Channel12=12, ADC_Channel13=13, ADC_Channel14=14, ADC_Channel15=15, -#endif // __SAMD21J18A__ +#else + #if defined(__SAMD21J18A__) + ADC_Channel12=12, + ADC_Channel13=13, + ADC_Channel14=14, + ADC_Channel15=15, + #endif +#endif ADC_Channel16=16, ADC_Channel17=17, ADC_Channel18=18, @@ -61,7 +90,7 @@ typedef enum _EAnalogChannel ADC_Channel_PTAT=0x1C, } EAnalogChannel ; -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) #if defined(__SAMD51G19A__) || defined(__SAME51G19A__) @@ -136,7 +165,7 @@ typedef enum _ETCChannel TC7_CH1 = (12<<8)|(1), } ETCChannel ; -#elif defined(__SAME53N20A__) || defined(__SAME53N19A__) || defined(__SAME54P20A__) || defined(__SAME54P19A__) || defined(__SAME54N20A__) || defined(__SAME54N19A__) || defined(__SAMD51P20A__) || defined(__SAMD51P19A__) || defined(__SAMD51N20A__) || defined(__SAMD51N19A__) || defined(__SAME51N20A__) || defined(__SAME51N19A__) +#else typedef enum _ETCChannel { @@ -179,7 +208,7 @@ typedef enum _ETCChannel #endif typedef ETCChannel EPWMChannel; -extern const uint32_t GCLK_CLKCTRL_IDs[TCC_INST_NUM+TC_INST_NUM]; +extern const uint32_t GCLK_CLKCTRL_IDs[ARDUINO_TCC_INSTANCE_COUNT + ARDUINO_TC_INSTANCE_COUNT]; #define NOT_ON_PWM NOT_ON_TIMER @@ -254,7 +283,7 @@ typedef enum _EPWMChannel #endif -extern const void* g_apTCInstances[TCC_INST_NUM+TC_INST_NUM] ; +extern const void* g_apTCInstances[ARDUINO_TCC_INSTANCE_COUNT + ARDUINO_TC_INSTANCE_COUNT] ; #define GetTCNumber( x ) ( (x) >> 8 ) #define GetTCChannelNumber( x ) ( (x) & 0xff ) @@ -307,7 +336,7 @@ typedef enum _EPioType PIO_SERCOM_ALT, /* The pin is controlled by the associated signal of peripheral D. */ PIO_TIMER, /* The pin is controlled by the associated signal of peripheral E. */ PIO_TIMER_ALT, /* The pin is controlled by the associated signal of peripheral F. */ -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) PIO_TCC_PDEC, /* The pin is controlled by the associated signal of peripheral G. */ PIO_COM, /* The pin is controlled by the associated signal of peripheral H. */ PIO_SDHC, /* The pin is controlled by the associated signal of peripheral I. */ @@ -341,7 +370,7 @@ typedef enum _EPioType #define PIN_ATTR_EXTINT (1UL<<6) #define PIN_ATTR_ANALOG_ALT (1UL<<7) -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) // these correspond to the mux table #define PIN_ATTR_PWM_E (1UL<<3) #define PIN_ATTR_PWM_F (1UL<<8) diff --git a/cores/arduino/cortex_handlers.c b/cores/arduino/cortex_handlers.c index 953d1a6e1..0ca844134 100644 --- a/cores/arduino/cortex_handlers.c +++ b/cores/arduino/cortex_handlers.c @@ -35,7 +35,7 @@ void Dummy_Handler(void) for (;;) { } } -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) /* Cortex-M4 processor handlers */ void Reset_Handler ( void ); @@ -62,6 +62,7 @@ void SUPC_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Han void SUPC_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void WDT_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void RTC_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#if defined(ARDUINO_SAMD51_E51) void EIC_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void EIC_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void EIC_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); @@ -78,6 +79,25 @@ void EIC_12_Handler ( void ) __attribute__ ((weak, alias("Dummy_Han void EIC_13_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void EIC_14_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void EIC_15_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#elif defined(ARDUINO_SAME53_E54) +void EIC_EXTINT_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_4_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_5_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_6_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_7_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_8_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_9_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_10_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_11_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_12_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_13_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_14_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void EIC_EXTINT_15_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#else +#endif void FREQM_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void NVMCTRL_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void NVMCTRL_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); @@ -98,41 +118,89 @@ void RAMECC_Handler ( void ) __attribute__ ((weak, alias("Dummy_Han void SERCOM0_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM0_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM0_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#if defined(ARDUINO_SAMD51_E51) void SERCOM0_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#elif defined(ARDUINO_SAME53_E54) +void SERCOM0_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#else +#endif void SERCOM1_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM1_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM1_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#if defined(ARDUINO_SAMD51_E51) void SERCOM1_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#elif defined(ARDUINO_SAME53_E54) +void SERCOM1_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#else +#endif void SERCOM2_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM2_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM2_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#if defined(ARDUINO_SAMD51_E51) void SERCOM2_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#elif defined(ARDUINO_SAME53_E54) +void SERCOM2_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#else +#endif void SERCOM3_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM3_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM3_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#if defined(ARDUINO_SAMD51_E51) void SERCOM3_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#elif defined(ARDUINO_SAME53_E54) +void SERCOM3_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#else +#endif void SERCOM4_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM4_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM4_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#if defined(ARDUINO_SAMD51_E51) void SERCOM4_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#elif defined(ARDUINO_SAME53_E54) +void SERCOM4_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#else +#endif void SERCOM5_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM5_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM5_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#if defined(ARDUINO_SAMD51_E51) void SERCOM5_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#elif defined(ARDUINO_SAME53_E54) +void SERCOM5_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#else +#endif void SERCOM6_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM6_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM6_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#if defined(ARDUINO_SAMD51_E51) void SERCOM6_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#elif defined(ARDUINO_SAME53_E54) +void SERCOM6_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#else +#endif void SERCOM7_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM7_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM7_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#if defined(ARDUINO_SAMD51_E51) void SERCOM7_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#elif defined(ARDUINO_SAME53_E54) +void SERCOM7_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#else +#endif void CAN0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void CAN1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#if defined(ARDUINO_SAMD51_E51) void USB_0_Handler ( void ) __attribute__ ((weak)); void USB_1_Handler ( void ) __attribute__ ((weak)); void USB_2_Handler ( void ) __attribute__ ((weak)); void USB_3_Handler ( void ) __attribute__ ((weak)); +#elif defined(ARDUINO_SAME53_E54) +void USB_OTHER_Handler ( void ) __attribute__ ((weak)); +void USB_SOF_HSOF_Handler ( void ) __attribute__ ((weak)); +void USB_TRCPT0_Handler ( void ) __attribute__ ((weak)); +void USB_TRCPT1_Handler ( void ) __attribute__ ((weak)); +#else +#endif void GMAC_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void TCC0_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void TCC0_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); @@ -232,6 +300,7 @@ __attribute__ ((section(".isr_vector"))) const DeviceVectors exception_table = (void*) SUPC_1_Handler, /* 9 Supply Controller IRQ 1 */ (void*) WDT_Handler, /* 10 Watchdog Timer */ (void*) RTC_Handler, /* 11 Real-Time Counter */ +#if defined(ARDUINO_SAMD51_E51) (void*) EIC_0_Handler, /* 12 External Interrupt Controller IRQ 0 */ (void*) EIC_1_Handler, /* 13 External Interrupt Controller IRQ 1 */ (void*) EIC_2_Handler, /* 14 External Interrupt Controller IRQ 2 */ @@ -248,6 +317,25 @@ __attribute__ ((section(".isr_vector"))) const DeviceVectors exception_table = (void*) EIC_13_Handler, /* 25 External Interrupt Controller IRQ 13 */ (void*) EIC_14_Handler, /* 26 External Interrupt Controller IRQ 14 */ (void*) EIC_15_Handler, /* 27 External Interrupt Controller IRQ 15 */ +#elif defined(ARDUINO_SAME53_E54) + (void*) EIC_EXTINT_0_Handler, /* 12 External Interrupt Controller IRQ 0 */ + (void*) EIC_EXTINT_1_Handler, /* 13 External Interrupt Controller IRQ 1 */ + (void*) EIC_EXTINT_2_Handler, /* 14 External Interrupt Controller IRQ 2 */ + (void*) EIC_EXTINT_3_Handler, /* 15 External Interrupt Controller IRQ 3 */ + (void*) EIC_EXTINT_4_Handler, /* 16 External Interrupt Controller IRQ 4 */ + (void*) EIC_EXTINT_5_Handler, /* 17 External Interrupt Controller IRQ 5 */ + (void*) EIC_EXTINT_6_Handler, /* 18 External Interrupt Controller IRQ 6 */ + (void*) EIC_EXTINT_7_Handler, /* 19 External Interrupt Controller IRQ 7 */ + (void*) EIC_EXTINT_8_Handler, /* 20 External Interrupt Controller IRQ 8 */ + (void*) EIC_EXTINT_9_Handler, /* 21 External Interrupt Controller IRQ 9 */ + (void*) EIC_EXTINT_10_Handler, /* 22 External Interrupt Controller IRQ 10 */ + (void*) EIC_EXTINT_11_Handler, /* 23 External Interrupt Controller IRQ 11 */ + (void*) EIC_EXTINT_12_Handler, /* 24 External Interrupt Controller IRQ 12 */ + (void*) EIC_EXTINT_13_Handler, /* 25 External Interrupt Controller IRQ 13 */ + (void*) EIC_EXTINT_14_Handler, /* 26 External Interrupt Controller IRQ 14 */ + (void*) EIC_EXTINT_15_Handler, /* 27 External Interrupt Controller IRQ 15 */ +#else +#endif (void*) FREQM_Handler, /* 28 Frequency Meter */ (void*) NVMCTRL_0_Handler, /* 29 Non-Volatile Memory Controller IRQ 0 */ (void*) NVMCTRL_1_Handler, /* 30 Non-Volatile Memory Controller IRQ 1 */ @@ -269,41 +357,89 @@ __attribute__ ((section(".isr_vector"))) const DeviceVectors exception_table = (void*) SERCOM0_0_Handler, /* 46 Serial Communication Interface 0 IRQ 0 */ (void*) SERCOM0_1_Handler, /* 47 Serial Communication Interface 0 IRQ 1 */ (void*) SERCOM0_2_Handler, /* 48 Serial Communication Interface 0 IRQ 2 */ +#if defined(ARDUINO_SAMD51_E51) (void*) SERCOM0_3_Handler, /* 49 Serial Communication Interface 0 IRQ 3 */ +#elif defined(ARDUINO_SAME53_E54) + (void*) SERCOM0_OTHER_Handler, /* Serial Communication Interface 0 other IRQ */ +#else +#endif (void*) SERCOM1_0_Handler, /* 50 Serial Communication Interface 1 IRQ 0 */ (void*) SERCOM1_1_Handler, /* 51 Serial Communication Interface 1 IRQ 1 */ (void*) SERCOM1_2_Handler, /* 52 Serial Communication Interface 1 IRQ 2 */ +#if defined(ARDUINO_SAMD51_E51) (void*) SERCOM1_3_Handler, /* 53 Serial Communication Interface 1 IRQ 3 */ +#elif defined(ARDUINO_SAME53_E54) + (void*) SERCOM1_OTHER_Handler, /* Serial Communication Interface 1 other IRQ */ +#else +#endif (void*) SERCOM2_0_Handler, /* 54 Serial Communication Interface 2 IRQ 0 */ (void*) SERCOM2_1_Handler, /* 55 Serial Communication Interface 2 IRQ 1 */ (void*) SERCOM2_2_Handler, /* 56 Serial Communication Interface 2 IRQ 2 */ +#if defined(ARDUINO_SAMD51_E51) (void*) SERCOM2_3_Handler, /* 57 Serial Communication Interface 2 IRQ 3 */ +#elif defined(ARDUINO_SAME53_E54) + (void*) SERCOM2_OTHER_Handler, /* Serial Communication Interface 2 other IRQ */ +#else +#endif (void*) SERCOM3_0_Handler, /* 58 Serial Communication Interface 3 IRQ 0 */ (void*) SERCOM3_1_Handler, /* 59 Serial Communication Interface 3 IRQ 1 */ (void*) SERCOM3_2_Handler, /* 60 Serial Communication Interface 3 IRQ 2 */ +#if defined(ARDUINO_SAMD51_E51) (void*) SERCOM3_3_Handler, /* 61 Serial Communication Interface 3 IRQ 3 */ +#elif defined(ARDUINO_SAME53_E54) + (void*) SERCOM3_OTHER_Handler, /* Serial Communication Interface 3 other IRQ */ +#else +#endif (void*) SERCOM4_0_Handler, /* 62 Serial Communication Interface 4 IRQ 0 */ (void*) SERCOM4_1_Handler, /* 63 Serial Communication Interface 4 IRQ 1 */ (void*) SERCOM4_2_Handler, /* 64 Serial Communication Interface 4 IRQ 2 */ +#if defined(ARDUINO_SAMD51_E51) (void*) SERCOM4_3_Handler, /* 65 Serial Communication Interface 4 IRQ 3 */ +#elif defined(ARDUINO_SAME53_E54) + (void*) SERCOM4_OTHER_Handler, /* Serial Communication Interface 4 other IRQ */ +#else +#endif (void*) SERCOM5_0_Handler, /* 66 Serial Communication Interface 5 IRQ 0 */ (void*) SERCOM5_1_Handler, /* 67 Serial Communication Interface 5 IRQ 1 */ (void*) SERCOM5_2_Handler, /* 68 Serial Communication Interface 5 IRQ 2 */ +#if defined(ARDUINO_SAMD51_E51) (void*) SERCOM5_3_Handler, /* 69 Serial Communication Interface 5 IRQ 3 */ +#elif defined(ARDUINO_SAME53_E54) + (void*) SERCOM5_OTHER_Handler, /* Serial Communication Interface 5 other IRQ */ +#else +#endif (void*) SERCOM6_0_Handler, /* 70 Serial Communication Interface 6 IRQ 0 */ (void*) SERCOM6_1_Handler, /* 71 Serial Communication Interface 6 IRQ 1 */ (void*) SERCOM6_2_Handler, /* 72 Serial Communication Interface 6 IRQ 2 */ +#if defined(ARDUINO_SAMD51_E51) (void*) SERCOM6_3_Handler, /* 73 Serial Communication Interface 6 IRQ 3 */ +#elif defined(ARDUINO_SAME53_E54) + (void*) SERCOM6_OTHER_Handler, /* Serial Communication Interface 6 other IRQ */ +#else +#endif (void*) SERCOM7_0_Handler, /* 74 Serial Communication Interface 7 IRQ 0 */ (void*) SERCOM7_1_Handler, /* 75 Serial Communication Interface 7 IRQ 1 */ (void*) SERCOM7_2_Handler, /* 76 Serial Communication Interface 7 IRQ 2 */ +#if defined(ARDUINO_SAMD51_E51) (void*) SERCOM7_3_Handler, /* 77 Serial Communication Interface 7 IRQ 3 */ +#elif defined(ARDUINO_SAME53_E54) + (void*) SERCOM7_OTHER_Handler, /* Serial Communication Interface 7 other IRQ */ +#else +#endif (void*) CAN0_Handler, /* 78 Control Area Network 0 (SAM E5x) */ (void*) CAN1_Handler, /* 79 Control Area Network 0 (SAM E5x) */ +#if defined(ARDUINO_SAMD51_E51) (void*) USB_0_Handler, /* 80 Universal Serial Bus IRQ 0 */ (void*) USB_1_Handler, /* 81 Universal Serial Bus IRQ 1 */ (void*) USB_2_Handler, /* 82 Universal Serial Bus IRQ 2 */ (void*) USB_3_Handler, /* 83 Universal Serial Bus IRQ 3 */ +#elif defined(ARDUINO_SAME53_E54) + (void*) USB_OTHER_Handler, /* 80 Universal Serial Bus other IRQ */ + (void*) USB_SOF_HSOF_Handler, /* 81 Universal Serial Bus SOF/HSOF IRQ */ + (void*) USB_TRCPT0_Handler, /* 82 Universal Serial Bus transfer complete 0 IRQ */ + (void*) USB_TRCPT1_Handler, /* 83 Universal Serial Bus transfer complete 1 IRQ */ +#else +#endif (void*) GMAC_Handler, /* 84 Ethernet MAC */ (void*) TCC0_0_Handler, /* 85 Timer Counter Control 0 IRQ 0 */ (void*) TCC0_1_Handler, /* 86 Timer Counter Control 0 IRQ 1 */ @@ -465,6 +601,7 @@ __attribute__ ((section(".isr_vector"))) const DeviceVectors exception_table = #endif extern int main(void); +extern void SystemInit(void); /* This is called on processor reset to initialize the device and call main() */ void Reset_Handler(void) @@ -486,7 +623,8 @@ void Reset_Handler(void) *pDest = 0; } -#if defined(__FPU_USED) && defined(__SAMD51__) +#if defined(__FPU_USED) && \ + (defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54)) /* Enable FPU */ SCB->CPACR |= (0xFu << 20); __DSB(); @@ -513,32 +651,26 @@ void SysTick_Handler(void) static void (*usb_isr)(void) = NULL; -#if defined(__SAMD51__) -void USB_0_Handler(void) -{ - if (usb_isr) - usb_isr(); -} -void USB_1_Handler(void) -{ - if (usb_isr) - usb_isr(); -} -void USB_2_Handler(void) -{ - if (usb_isr) - usb_isr(); -} -void USB_3_Handler(void) +static void dispatchUSBInterrupt(void) { if (usb_isr) - usb_isr(); + usb_isr(); } + +#if defined(ARDUINO_SAMD51_E51) +void USB_0_Handler(void) { dispatchUSBInterrupt(); } +void USB_1_Handler(void) { dispatchUSBInterrupt(); } +void USB_2_Handler(void) { dispatchUSBInterrupt(); } +void USB_3_Handler(void) { dispatchUSBInterrupt(); } +#elif defined(ARDUINO_SAME53_E54) +void USB_OTHER_Handler(void) { dispatchUSBInterrupt(); } +void USB_SOF_HSOF_Handler(void) { dispatchUSBInterrupt(); } +void USB_TRCPT0_Handler(void) { dispatchUSBInterrupt(); } +void USB_TRCPT1_Handler(void) { dispatchUSBInterrupt(); } #else void USB_Handler(void) { - if (usb_isr) - usb_isr(); + dispatchUSBInterrupt(); } #endif diff --git a/cores/arduino/pulse.c b/cores/arduino/pulse.c index 6ead704dd..20a6c687d 100644 --- a/cores/arduino/pulse.c +++ b/cores/arduino/pulse.c @@ -34,14 +34,18 @@ uint32_t pulseIn(uint32_t pin, uint32_t state, uint32_t timeout) uint32_t bit = 1 << p.ulPin; uint32_t stateMask = state ? bit : 0; -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) /* * The SAMD51 is fast enough to use really obvious code (similar to * what was used to produce pulse_asm.S, but using micros() for timing. * No assembly required, no conversion of loop counts to times (which is * worrisome in the presence of cache.) */ - const volatile uint32_t *port = &(PORT->Group[p.ulPort].IN.reg); +#if defined(ARDUINO_SAME53_E54) + const volatile uint32_t *port = &PORT_REGS->GROUP[p.ulPort].PORT_IN; +#else + const volatile uint32_t *port = &PORT->Group[p.ulPort].IN.reg; +#endif uint32_t usCallStart; // microseconds at start of call, for timeout. uint32_t usPulseStart; // microseconds at start of measured pulse. usCallStart = usPulseStart = micros(); @@ -80,4 +84,3 @@ uint32_t pulseIn(uint32_t pin, uint32_t state, uint32_t timeout) return 0; #endif // SAMD51 } - diff --git a/cores/arduino/startup.c b/cores/arduino/startup.c index 676844091..e0df923e5 100644 --- a/cores/arduino/startup.c +++ b/cores/arduino/startup.c @@ -22,10 +22,12 @@ #include +extern uint32_t SystemCoreClock; + // Constants for Clock generators #define GENERIC_CLOCK_GENERATOR_MAIN (0u) -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) #define GENERIC_CLOCK_GENERATOR_XOSC32K (3u) #define GENERIC_CLOCK_GENERATOR_48M (1u) #define GENERIC_CLOCK_GENERATOR_48M_SYNC GCLK_SYNCBUSY_GENCTRL1 @@ -40,6 +42,16 @@ #define GENERIC_CLOCK_GENERATOR_1M (5u) //#define CRYSTALLESS +#elif defined(ARDUINO_SAME53_E54) +#define GENERIC_CLOCK_GENERATOR_XOSC32K (3u) +#define GENERIC_CLOCK_GENERATOR_48M (1u) +#define GENERIC_CLOCK_GENERATOR_48M_SYNC GCLK_SYNCBUSY_GENCTRL_Msk +#define GENERIC_CLOCK_GENERATOR_100M (2u) +#define GENERIC_CLOCK_GENERATOR_100M_SYNC GCLK_SYNCBUSY_GENCTRL_Msk +#define GENERIC_CLOCK_GENERATOR_12M (4u) +#define GENERIC_CLOCK_GENERATOR_12M_SYNC GCLK_SYNCBUSY_GENCTRL_Msk +#define GENERIC_CLOCK_GENERATOR_1M (5u) + #else #define GENERIC_CLOCK_GENERATOR_XOSC32K (1u) @@ -55,7 +67,7 @@ void SystemInit( void ) { //***************** SAMD51 ************************// -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) NVMCTRL->CTRLA.reg |= NVMCTRL_CTRLA_RWS(0); #ifndef CRYSTALLESS @@ -303,7 +315,107 @@ void SystemInit( void ) | USB_PADCAL_TRANSP(usbtransp); //*************** END SAMD51 *************************// - + +#elif defined(ARDUINO_SAME53_E54) + NVMCTRL_REGS->NVMCTRL_CTRLA |= NVMCTRL_CTRLA_RWS(0); + +#ifndef CRYSTALLESS + OSC32KCTRL_REGS->OSC32KCTRL_XOSC32K = + OSC32KCTRL_XOSC32K_ENABLE_Msk | OSC32KCTRL_XOSC32K_EN1K_Msk | + OSC32KCTRL_XOSC32K_EN32K_Msk | OSC32KCTRL_XOSC32K_CGM_XT | + OSC32KCTRL_XOSC32K_XTALEN_Msk; + while ((OSC32KCTRL_REGS->OSC32KCTRL_STATUS & OSC32KCTRL_STATUS_XOSC32KRDY_Msk) == 0); +#endif + + GCLK_REGS->GCLK_CTRLA = GCLK_CTRLA_SWRST_Msk; + while (GCLK_REGS->GCLK_SYNCBUSY & GCLK_SYNCBUSY_SWRST_Msk); + +#ifndef CRYSTALLESS + GCLK_REGS->GCLK_GENCTRL[GENERIC_CLOCK_GENERATOR_XOSC32K] = + GCLK_GENCTRL_SRC_XOSC32K | GCLK_GENCTRL_GENEN_Msk; +#else + GCLK_REGS->GCLK_GENCTRL[GENERIC_CLOCK_GENERATOR_XOSC32K] = + GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_GENEN_Msk; +#endif + while (GCLK_REGS->GCLK_SYNCBUSY & GCLK_SYNCBUSY_GENCTRL_Msk); + + GCLK_REGS->GCLK_GENCTRL[GENERIC_CLOCK_GENERATOR_MAIN] = + GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_GENEN_Msk; + while (GCLK_REGS->GCLK_SYNCBUSY & GCLK_SYNCBUSY_GENCTRL_Msk); + + OSCCTRL_REGS->OSCCTRL_DFLLCTRLA = 0; + OSCCTRL_REGS->OSCCTRL_DFLLMUL = OSCCTRL_DFLLMUL_CSTEP(1) | + OSCCTRL_DFLLMUL_FSTEP(1) | + OSCCTRL_DFLLMUL_MUL(0); + while (OSCCTRL_REGS->OSCCTRL_DFLLSYNC & OSCCTRL_DFLLSYNC_DFLLMUL_Msk); + + OSCCTRL_REGS->OSCCTRL_DFLLCTRLB = 0; + while (OSCCTRL_REGS->OSCCTRL_DFLLSYNC & OSCCTRL_DFLLSYNC_DFLLCTRLB_Msk); + + OSCCTRL_REGS->OSCCTRL_DFLLCTRLA = OSCCTRL_DFLLCTRLA_ENABLE_Msk; + while (OSCCTRL_REGS->OSCCTRL_DFLLSYNC & OSCCTRL_DFLLSYNC_ENABLE_Msk); + + OSCCTRL_REGS->OSCCTRL_DFLLVAL = OSCCTRL_REGS->OSCCTRL_DFLLVAL; + while (OSCCTRL_REGS->OSCCTRL_DFLLSYNC & OSCCTRL_DFLLSYNC_DFLLVAL_Msk); + + OSCCTRL_REGS->OSCCTRL_DFLLCTRLB = OSCCTRL_DFLLCTRLB_WAITLOCK_Msk | + OSCCTRL_DFLLCTRLB_CCDIS_Msk | + OSCCTRL_DFLLCTRLB_USBCRM_Msk; + while ((OSCCTRL_REGS->OSCCTRL_STATUS & OSCCTRL_STATUS_DFLLRDY_Msk) == 0); + + GCLK_REGS->GCLK_GENCTRL[GENERIC_CLOCK_GENERATOR_1M] = + GCLK_GENCTRL_SRC_DFLL | GCLK_GENCTRL_GENEN_Msk | GCLK_GENCTRL_DIV(48); + while (GCLK_REGS->GCLK_SYNCBUSY & GCLK_SYNCBUSY_GENCTRL_Msk); + + GCLK_REGS->GCLK_PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0] = + GCLK_PCHCTRL_GEN_GCLK5 | GCLK_PCHCTRL_CHEN_Msk; + OSCCTRL_REGS->DPLL[0].OSCCTRL_DPLLRATIO = + OSCCTRL_DPLLRATIO_LDRFRAC(0) | OSCCTRL_DPLLRATIO_LDR((F_CPU - 500000) / 1000000); + while (OSCCTRL_REGS->DPLL[0].OSCCTRL_DPLLSYNCBUSY & OSCCTRL_DPLLSYNCBUSY_DPLLRATIO_Msk); + OSCCTRL_REGS->DPLL[0].OSCCTRL_DPLLCTRLB = + OSCCTRL_DPLLCTRLB_REFCLK_GCLK | OSCCTRL_DPLLCTRLB_LBYPASS_Msk; + OSCCTRL_REGS->DPLL[0].OSCCTRL_DPLLCTRLA = OSCCTRL_DPLLCTRLA_ENABLE_Msk; + while ((OSCCTRL_REGS->DPLL[0].OSCCTRL_DPLLSTATUS & + (OSCCTRL_DPLLSTATUS_CLKRDY_Msk | OSCCTRL_DPLLSTATUS_LOCK_Msk)) != + (OSCCTRL_DPLLSTATUS_CLKRDY_Msk | OSCCTRL_DPLLSTATUS_LOCK_Msk)); + + GCLK_REGS->GCLK_PCHCTRL[OSCCTRL_GCLK_ID_FDPLL1] = + GCLK_PCHCTRL_GEN_GCLK5 | GCLK_PCHCTRL_CHEN_Msk; + OSCCTRL_REGS->DPLL[1].OSCCTRL_DPLLRATIO = + OSCCTRL_DPLLRATIO_LDRFRAC(0) | OSCCTRL_DPLLRATIO_LDR(99); + while (OSCCTRL_REGS->DPLL[1].OSCCTRL_DPLLSYNCBUSY & OSCCTRL_DPLLSYNCBUSY_DPLLRATIO_Msk); + OSCCTRL_REGS->DPLL[1].OSCCTRL_DPLLCTRLB = + OSCCTRL_DPLLCTRLB_REFCLK_GCLK | OSCCTRL_DPLLCTRLB_LBYPASS_Msk; + OSCCTRL_REGS->DPLL[1].OSCCTRL_DPLLCTRLA = OSCCTRL_DPLLCTRLA_ENABLE_Msk; + while ((OSCCTRL_REGS->DPLL[1].OSCCTRL_DPLLSTATUS & + (OSCCTRL_DPLLSTATUS_CLKRDY_Msk | OSCCTRL_DPLLSTATUS_LOCK_Msk)) != + (OSCCTRL_DPLLSTATUS_CLKRDY_Msk | OSCCTRL_DPLLSTATUS_LOCK_Msk)); + + GCLK_REGS->GCLK_GENCTRL[GENERIC_CLOCK_GENERATOR_48M] = + GCLK_GENCTRL_SRC_DFLL | GCLK_GENCTRL_IDC_Msk | GCLK_GENCTRL_GENEN_Msk; + while (GCLK_REGS->GCLK_SYNCBUSY & GENERIC_CLOCK_GENERATOR_48M_SYNC); + + GCLK_REGS->GCLK_GENCTRL[GENERIC_CLOCK_GENERATOR_100M] = + GCLK_GENCTRL_SRC_DPLL1 | GCLK_GENCTRL_IDC_Msk | GCLK_GENCTRL_GENEN_Msk; + while (GCLK_REGS->GCLK_SYNCBUSY & GENERIC_CLOCK_GENERATOR_100M_SYNC); + + GCLK_REGS->GCLK_GENCTRL[GENERIC_CLOCK_GENERATOR_12M] = + GCLK_GENCTRL_SRC_DFLL | GCLK_GENCTRL_IDC_Msk | GCLK_GENCTRL_DIV(4) | + GCLK_GENCTRL_GENEN_Msk; + while (GCLK_REGS->GCLK_SYNCBUSY & GENERIC_CLOCK_GENERATOR_12M_SYNC); + + GCLK_REGS->GCLK_GENCTRL[GENERIC_CLOCK_GENERATOR_MAIN] = + GCLK_GENCTRL_SRC_DPLL0 | GCLK_GENCTRL_IDC_Msk | GCLK_GENCTRL_GENEN_Msk; + while (GCLK_REGS->GCLK_SYNCBUSY & GCLK_SYNCBUSY_GENCTRL_Msk); + + MCLK_REGS->MCLK_CPUDIV = MCLK_CPUDIV_DIV_DIV1; + SUPC_REGS->SUPC_VREG &= ~SUPC_VREG_SEL_Msk; + + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; + + SystemCoreClock = F_CPU; + #else //********************** SAMD21 *********************// diff --git a/cores/arduino/wiring.c b/cores/arduino/wiring.c index ad6c7982d..0f1d533c0 100644 --- a/cores/arduino/wiring.c +++ b/cores/arduino/wiring.c @@ -24,7 +24,7 @@ extern "C" { #endif -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) uint32_t SystemCoreClock=F_CPU; #else /* @@ -77,7 +77,7 @@ void init( void ) // // Clock EIC for I/O interrupts // PM->APBAMASK.reg |= PM_APBAMASK_EIC ; -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) MCLK->APBAMASK.reg |= MCLK_APBAMASK_SERCOM0 | MCLK_APBAMASK_SERCOM1 | MCLK_APBAMASK_TC0 | MCLK_APBAMASK_TC1; MCLK->APBBMASK.reg |= MCLK_APBBMASK_SERCOM2 | MCLK_APBBMASK_SERCOM3 | MCLK_APBBMASK_TCC0 | MCLK_APBBMASK_TCC1 | MCLK_APBBMASK_TC3 | MCLK_APBBMASK_TC2; @@ -87,6 +87,20 @@ void init( void ) MCLK->APBDMASK.reg |= MCLK_APBDMASK_DAC | MCLK_APBDMASK_SERCOM4 | MCLK_APBDMASK_SERCOM5 | MCLK_APBDMASK_ADC0 | MCLK_APBDMASK_ADC1 | MCLK_APBDMASK_TCC4 | MCLK_APBDMASK_TC6 | MCLK_APBDMASK_TC7 | MCLK_APBDMASK_SERCOM6 | MCLK_APBDMASK_SERCOM7; +#elif defined(ARDUINO_SAME53_E54) + MCLK_REGS->MCLK_APBAMASK |= MCLK_APBAMASK_SERCOM0_Msk | MCLK_APBAMASK_SERCOM1_Msk | + MCLK_APBAMASK_TC0_Msk | MCLK_APBAMASK_TC1_Msk; + MCLK_REGS->MCLK_APBBMASK |= MCLK_APBBMASK_SERCOM2_Msk | MCLK_APBBMASK_SERCOM3_Msk | + MCLK_APBBMASK_TCC0_Msk | MCLK_APBBMASK_TCC1_Msk | + MCLK_APBBMASK_TC2_Msk | MCLK_APBBMASK_TC3_Msk; + MCLK_REGS->MCLK_APBCMASK |= MCLK_APBCMASK_TCC2_Msk | MCLK_APBCMASK_TCC3_Msk | + MCLK_APBCMASK_TC4_Msk | MCLK_APBCMASK_TC5_Msk; + MCLK_REGS->MCLK_APBDMASK |= MCLK_APBDMASK_DAC_Msk | MCLK_APBDMASK_SERCOM4_Msk | + MCLK_APBDMASK_SERCOM5_Msk | MCLK_APBDMASK_ADC0_Msk | + MCLK_APBDMASK_ADC1_Msk | MCLK_APBDMASK_TCC4_Msk | + MCLK_APBDMASK_TC6_Msk | MCLK_APBDMASK_TC7_Msk | + MCLK_APBDMASK_SERCOM6_Msk | MCLK_APBDMASK_SERCOM7_Msk; + #else // Clock SERCOM for Serial PM->APBCMASK.reg |= PM_APBCMASK_SERCOM0 | PM_APBCMASK_SERCOM1 | PM_APBCMASK_SERCOM2 | PM_APBCMASK_SERCOM3 | PM_APBCMASK_SERCOM4 | PM_APBCMASK_SERCOM5 ; @@ -114,7 +128,7 @@ void init( void ) // Initialize Analog Controller // Setting clock -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) //set to 1/(1/(48000000/32) * 6) = 250000 SPS GCLK->PCHCTRL[ADC0_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); //use clock generator 1 (48Mhz) GCLK->PCHCTRL[ADC1_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); //use clock generator 1 (48Mhz) @@ -156,6 +170,46 @@ void init( void ) DAC->DACCTRL[0].bit.REFRESH = 2; DAC->DACCTRL[1].bit.REFRESH = 2; +#elif defined(ARDUINO_SAME53_E54) + GCLK_REGS->GCLK_PCHCTRL[ADC0_GCLK_ID] = GCLK_PCHCTRL_GEN_GCLK1 | + GCLK_PCHCTRL_CHEN_Msk; + GCLK_REGS->GCLK_PCHCTRL[ADC1_GCLK_ID] = GCLK_PCHCTRL_GEN_GCLK1 | + GCLK_PCHCTRL_CHEN_Msk; + + adc_registers_t *adcs[] = {ADC0_REGS, ADC1_REGS}; + for (uint32_t i = 0; i < 2; i++) { + adcs[i]->ADC_CTRLA = (adcs[i]->ADC_CTRLA & ~ADC_CTRLA_PRESCALER_Msk) | + ADC_CTRLA_PRESCALER_DIV32; + adcs[i]->ADC_CTRLB = (adcs[i]->ADC_CTRLB & ~ADC_CTRLB_RESSEL_Msk) | + ADC_CTRLB_RESSEL_10BIT; + while (adcs[i]->ADC_SYNCBUSY & ADC_SYNCBUSY_CTRLB_Msk); + + adcs[i]->ADC_SAMPCTRL = ADC_SAMPCTRL_SAMPLEN(5); + while (adcs[i]->ADC_SYNCBUSY & ADC_SYNCBUSY_SAMPCTRL_Msk); + + adcs[i]->ADC_INPUTCTRL = ADC_INPUTCTRL_MUXNEG_GND; + while (adcs[i]->ADC_SYNCBUSY & ADC_SYNCBUSY_INPUTCTRL_Msk); + + adcs[i]->ADC_AVGCTRL = ADC_AVGCTRL_SAMPLENUM_1 | ADC_AVGCTRL_ADJRES(0); + while (adcs[i]->ADC_SYNCBUSY & ADC_SYNCBUSY_AVGCTRL_Msk); + } + + analogReference(AR_DEFAULT); + + GCLK_REGS->GCLK_PCHCTRL[DAC_GCLK_ID] = GCLK_PCHCTRL_GEN_GCLK4 | + GCLK_PCHCTRL_CHEN_Msk; + while ((GCLK_REGS->GCLK_PCHCTRL[DAC_GCLK_ID] & GCLK_PCHCTRL_CHEN_Msk) == 0); + + while (DAC_REGS->DAC_SYNCBUSY & DAC_SYNCBUSY_SWRST_Msk); + DAC_REGS->DAC_CTRLA |= DAC_CTRLA_SWRST_Msk; + while (DAC_REGS->DAC_SYNCBUSY & DAC_SYNCBUSY_SWRST_Msk); + + DAC_REGS->DAC_CTRLB = DAC_CTRLB_REFSEL_VREFPU; + DAC_REGS->DAC_DACCTRL[0] = + (DAC_REGS->DAC_DACCTRL[0] & ~DAC_DACCTRL_REFRESH_Msk) | DAC_DACCTRL_REFRESH(2); + DAC_REGS->DAC_DACCTRL[1] = + (DAC_REGS->DAC_DACCTRL[1] & ~DAC_DACCTRL_REFRESH_Msk) | DAC_DACCTRL_REFRESH(2); + #else //set to 1/(1/(48000000/32) * 6) = 250000 SPS diff --git a/cores/arduino/wiring_analog.c b/cores/arduino/wiring_analog.c index 40f5a6424..f5562d9dc 100644 --- a/cores/arduino/wiring_analog.c +++ b/cores/arduino/wiring_analog.c @@ -27,7 +27,7 @@ extern "C" { static int _readResolution = 10; static int _ADCResolution = 10; -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) static int _writeResolution = 12; static int _dacResolution = 12; #else @@ -36,7 +36,7 @@ static int _writeResolution = 8; #endif -#if !defined(__SAMD51__) +#if !defined(ARDUINO_SAMD51_E51) && !defined(ARDUINO_SAME53_E54) // Wait for synchronization of registers between the clock domains static __inline__ void syncADC() __attribute__((always_inline, unused)); static void syncADC() { @@ -45,7 +45,7 @@ static void syncADC() { } // ATSAMR, for example, doesn't have a DAC -#ifdef DAC +#if defined(DAC) || defined(DAC_REGS) // Wait for synchronization of registers between the clock domains static __inline__ void syncDAC() __attribute__((always_inline, unused)); static void syncDAC() { @@ -73,7 +73,7 @@ static bool dacEnabled[2]; void analogReadResolution(int res) { _readResolution = res; -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) if (res > 10) { ADC0->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_12BIT_Val; @@ -92,6 +92,23 @@ void analogReadResolution(int res) while(ADC0->SYNCBUSY.reg & ADC_SYNCBUSY_CTRLB); //wait for sync while(ADC1->SYNCBUSY.reg & ADC_SYNCBUSY_CTRLB); //wait for sync +#elif defined(ARDUINO_SAME53_E54) + uint32_t resolution; + if (res > 10) { + resolution = ADC_CTRLB_RESSEL_12BIT; + _ADCResolution = 12; + } else if (res > 8) { + resolution = ADC_CTRLB_RESSEL_10BIT; + _ADCResolution = 10; + } else { + resolution = ADC_CTRLB_RESSEL_8BIT; + _ADCResolution = 8; + } + + ADC0_REGS->ADC_CTRLB = (ADC0_REGS->ADC_CTRLB & ~ADC_CTRLB_RESSEL_Msk) | resolution; + ADC1_REGS->ADC_CTRLB = (ADC1_REGS->ADC_CTRLB & ~ADC_CTRLB_RESSEL_Msk) | resolution; + while (ADC0_REGS->ADC_SYNCBUSY & ADC_SYNCBUSY_CTRLB_Msk); + while (ADC1_REGS->ADC_SYNCBUSY & ADC_SYNCBUSY_CTRLB_Msk); #else if (res > 10) { @@ -133,7 +150,7 @@ static inline uint32_t mapResolution(uint32_t value, uint32_t from, uint32_t to) */ void analogReference(eAnalogReference mode) { -#if defined(__SAMD51__) + #if defined(ARDUINO_SAMD51_E51) while(ADC0->SYNCBUSY.reg & ADC_SYNCBUSY_REFCTRL); //wait for sync while(ADC1->SYNCBUSY.reg & ADC_SYNCBUSY_REFCTRL); //wait for sync @@ -223,7 +240,34 @@ void analogReference(eAnalogReference mode) ADC1->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC1_Val; // break; } - + +#elif defined(ARDUINO_SAME53_E54) + uint32_t reference = ADC_REFCTRL_REFSEL_INTVCC1; + uint32_t vref = SUPC_REGS->SUPC_VREF; + + switch (mode) + { + case AR_INTERNAL1V0: vref = (vref & ~SUPC_VREF_SEL_Msk) | SUPC_VREF_SEL_1V0; reference = ADC_REFCTRL_REFSEL_INTREF; break; + case AR_INTERNAL1V1: vref = (vref & ~SUPC_VREF_SEL_Msk) | SUPC_VREF_SEL_1V1; reference = ADC_REFCTRL_REFSEL_INTREF; break; + case AR_INTERNAL1V2: vref = (vref & ~SUPC_VREF_SEL_Msk) | SUPC_VREF_SEL_1V2; reference = ADC_REFCTRL_REFSEL_INTREF; break; + case AR_INTERNAL1V25: vref = (vref & ~SUPC_VREF_SEL_Msk) | SUPC_VREF_SEL_1V25; reference = ADC_REFCTRL_REFSEL_INTREF; break; + case AR_INTERNAL2V0: vref = (vref & ~SUPC_VREF_SEL_Msk) | SUPC_VREF_SEL_2V0; reference = ADC_REFCTRL_REFSEL_INTREF; break; + case AR_INTERNAL2V2: vref = (vref & ~SUPC_VREF_SEL_Msk) | SUPC_VREF_SEL_2V2; reference = ADC_REFCTRL_REFSEL_INTREF; break; + case AR_INTERNAL2V4: vref = (vref & ~SUPC_VREF_SEL_Msk) | SUPC_VREF_SEL_2V4; reference = ADC_REFCTRL_REFSEL_INTREF; break; + case AR_INTERNAL2V5: vref = (vref & ~SUPC_VREF_SEL_Msk) | SUPC_VREF_SEL_2V5; reference = ADC_REFCTRL_REFSEL_INTREF; break; + case AR_EXTERNAL: reference = ADC_REFCTRL_REFSEL_AREFA; break; + case AR_INTERNAL1V65: reference = ADC_REFCTRL_REFSEL_INTVCC0; break; + case AR_DEFAULT: + default: break; + } + + if (reference == ADC_REFCTRL_REFSEL_INTREF) { + SUPC_REGS->SUPC_VREF = vref | SUPC_VREF_VREFOE_Msk; + } + ADC0_REGS->ADC_REFCTRL = reference; + ADC1_REGS->ADC_REFCTRL = reference; + while (ADC0_REGS->ADC_SYNCBUSY & ADC_SYNCBUSY_REFCTRL_Msk); + while (ADC1_REGS->ADC_SYNCBUSY & ADC_SYNCBUSY_REFCTRL_Msk); #else syncADC(); switch (mode) @@ -277,9 +321,9 @@ uint32_t analogRead(uint32_t pin) pinPeripheral(pin, PIO_ANALOG); //ATSAMR, for example, doesn't have a DAC -#ifdef DAC +#if defined(DAC) || defined(DAC_REGS) - #if defined(__SAMD51__) + #if defined(ARDUINO_SAMD51_E51) if (pin == PIN_DAC0 || pin == PIN_DAC1) { // Disable DAC, if analogWrite(A0,dval) used previously the DAC is enabled uint8_t channel = (pin == PIN_DAC0 ? 0 : 1); @@ -297,19 +341,28 @@ uint32_t analogRead(uint32_t pin) } while (DAC->SYNCBUSY.bit.ENABLE); - #else - if (pin == PIN_DAC0) { // Disable DAC, if analogWrite(A0,dval) used previously the DAC is enabled - syncDAC(); - - DAC->CTRLA.bit.ENABLE = 0x00; // Disable DAC - //DAC->CTRLB.bit.EOEN = 0x00; // The DAC output is turned off. - syncDAC(); - #endif + #elif defined(ARDUINO_SAME53_E54) + if (pin == PIN_DAC0) { + if (dacEnabled[0]) { + dacEnabled[0] = false; + while (DAC_REGS->DAC_SYNCBUSY & (DAC_SYNCBUSY_ENABLE_Msk | DAC_SYNCBUSY_SWRST_Msk)); + DAC_REGS->DAC_CTRLA &= ~DAC_CTRLA_ENABLE_Msk; + while (DAC_REGS->DAC_SYNCBUSY & DAC_SYNCBUSY_ENABLE_Msk); + DAC_REGS->DAC_DACCTRL[0] &= ~DAC_DACCTRL_ENABLE_Msk; + DAC_REGS->DAC_CTRLA |= DAC_CTRLA_ENABLE_Msk; + } + while (DAC_REGS->DAC_SYNCBUSY & DAC_SYNCBUSY_ENABLE_Msk); + #else + if (pin == PIN_DAC0) { + syncDAC(); + DAC->CTRLA.bit.ENABLE = 0x00; + syncDAC(); + #endif } #endif -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) Adc *adc; if(g_APinDescription[pin].ulPinAttribute & PIN_ATTR_ANALOG) adc = ADC0; else if(g_APinDescription[pin].ulPinAttribute & PIN_ATTR_ANALOG_ALT) adc = ADC1; @@ -352,6 +405,32 @@ uint32_t analogRead(uint32_t pin) adc->CTRLA.bit.ENABLE = 0x00; // Disable ADC while( adc->SYNCBUSY.reg & ADC_SYNCBUSY_ENABLE ); //wait for sync +#elif defined(ARDUINO_SAME53_E54) + adc_registers_t *adc; + if (g_APinDescription[pin].ulPinAttribute & PIN_ATTR_ANALOG) adc = ADC0_REGS; + else if (g_APinDescription[pin].ulPinAttribute & PIN_ATTR_ANALOG_ALT) adc = ADC1_REGS; + else return 0; + + while (adc->ADC_SYNCBUSY & ADC_SYNCBUSY_INPUTCTRL_Msk); + adc->ADC_INPUTCTRL = + (adc->ADC_INPUTCTRL & ~ADC_INPUTCTRL_MUXPOS_Msk) | + ADC_INPUTCTRL_MUXPOS(g_APinDescription[pin].ulADCChannelNumber); + + while (adc->ADC_SYNCBUSY & ADC_SYNCBUSY_ENABLE_Msk); + adc->ADC_CTRLA |= ADC_CTRLA_ENABLE_Msk; + while (adc->ADC_SYNCBUSY & ADC_SYNCBUSY_ENABLE_Msk); + + adc->ADC_SWTRIG = ADC_SWTRIG_START_Msk; + while ((adc->ADC_INTFLAG & ADC_INTFLAG_RESRDY_Msk) == 0); + adc->ADC_INTFLAG = ADC_INTFLAG_RESRDY_Msk; + + adc->ADC_SWTRIG = ADC_SWTRIG_START_Msk; + while ((adc->ADC_INTFLAG & ADC_INTFLAG_RESRDY_Msk) == 0); + valueRead = adc->ADC_RESULT; + + while (adc->ADC_SYNCBUSY & ADC_SYNCBUSY_ENABLE_Msk); + adc->ADC_CTRLA &= ~ADC_CTRLA_ENABLE_Msk; + while (adc->ADC_SYNCBUSY & ADC_SYNCBUSY_ENABLE_Msk); #else syncADC(); ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[pin].ulADCChannelNumber; // Selection for the positive ADC input @@ -408,17 +487,19 @@ void analogWrite(uint32_t pin, uint32_t value) uint32_t attr = pinDesc.ulPinAttribute; // ATSAMR, for example, doesn't have a DAC -#ifdef DAC +#if defined(DAC) || defined(DAC_REGS) if ((attr & PIN_ATTR_ANALOG) == PIN_ATTR_ANALOG) { // DAC handling code -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) if (pin == PIN_DAC0 || pin == PIN_DAC1) { // 2 DACs on A0 (PA02) and A1 (PA05) +#elif defined(ARDUINO_SAME53_E54) + if (pin == PIN_DAC0) { #else if (pin == PIN_DAC0) { // Only 1 DAC on A0 (PA02) #endif -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) value = mapResolution(value, _writeResolution, _dacResolution); @@ -473,25 +554,41 @@ void analogWrite(uint32_t pin, uint32_t value) DAC->DATA[1].reg = value; // DAC on 10 bits. } +#elif defined(ARDUINO_SAME53_E54) + value = mapResolution(value, _writeResolution, _dacResolution); + uint8_t channel = 0; + pinPeripheral(pin, PIO_ANALOG); + + if (!dacEnabled[channel]) { + dacEnabled[channel] = true; + while (DAC_REGS->DAC_SYNCBUSY & (DAC_SYNCBUSY_ENABLE_Msk | DAC_SYNCBUSY_SWRST_Msk)); + DAC_REGS->DAC_CTRLA &= ~DAC_CTRLA_ENABLE_Msk; + while (DAC_REGS->DAC_SYNCBUSY & DAC_SYNCBUSY_ENABLE_Msk); + DAC_REGS->DAC_DACCTRL[channel] |= DAC_DACCTRL_ENABLE_Msk; + DAC_REGS->DAC_CTRLA |= DAC_CTRLA_ENABLE_Msk; + } + while ((DAC_REGS->DAC_STATUS & (DAC_STATUS_READY0_Msk << channel)) == 0); + while (DAC_REGS->DAC_SYNCBUSY & (DAC_SYNCBUSY_DATA0_Msk << channel)); + DAC_REGS->DAC_DATA[channel] = value; #else syncDAC(); DAC->DATA.reg = value & 0x3FF; // DAC on 10 bits. syncDAC(); DAC->CTRLA.bit.ENABLE = 0x01; // Enable DAC syncDAC(); -#endif // __SAMD51__ +#endif return; } } #endif // DAC -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) if(attr & (PIN_ATTR_PWM_E|PIN_ATTR_PWM_F|PIN_ATTR_PWM_G)){ uint32_t tcNum = GetTCNumber(pinDesc.ulPWMChannel); uint8_t tcChannel = GetTCChannelNumber(pinDesc.ulPWMChannel); - static bool tcEnabled[TCC_INST_NUM+TC_INST_NUM]; + static bool tcEnabled[ARDUINO_TCC_INSTANCE_COUNT + ARDUINO_TC_INSTANCE_COUNT]; if(attr & PIN_ATTR_PWM_E) pinPeripheral(pin, PIO_TIMER); @@ -505,7 +602,7 @@ void analogWrite(uint32_t pin, uint32_t value) GCLK->PCHCTRL[GCLK_CLKCTRL_IDs[tcNum]].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); //use clock generator 0 // Set PORT - if (tcNum >= TCC_INST_NUM) { + if (tcNum >= ARDUINO_TCC_INSTANCE_COUNT) { // -- Configure TC Tc* TCx = (Tc*) GetTC(pinDesc.ulPWMChannel); @@ -560,7 +657,7 @@ void analogWrite(uint32_t pin, uint32_t value) } } else { - if (tcNum >= TCC_INST_NUM) { + if (tcNum >= ARDUINO_TCC_INSTANCE_COUNT) { Tc* TCx = (Tc*) GetTC(pinDesc.ulPWMChannel); TCx->COUNT8.CC[tcChannel].reg = (uint8_t) value; while (TCx->COUNT8.SYNCBUSY.bit.CC0 || TCx->COUNT8.SYNCBUSY.bit.CC1); @@ -577,7 +674,63 @@ void analogWrite(uint32_t pin, uint32_t value) return; } - + +#elif defined(ARDUINO_SAME53_E54) + if (attr & (PIN_ATTR_PWM_E | PIN_ATTR_PWM_F | PIN_ATTR_PWM_G)) { + uint32_t tcNum = GetTCNumber(pinDesc.ulPWMChannel); + uint8_t tcChannel = GetTCChannelNumber(pinDesc.ulPWMChannel); + static bool tcEnabled[ARDUINO_TCC_INSTANCE_COUNT + ARDUINO_TC_INSTANCE_COUNT]; + + if (attr & PIN_ATTR_PWM_E) pinPeripheral(pin, PIO_TIMER); + else if (attr & PIN_ATTR_PWM_F) pinPeripheral(pin, PIO_TIMER_ALT); + else pinPeripheral(pin, PIO_TCC_PDEC); + + if (!tcEnabled[tcNum]) { + tcEnabled[tcNum] = true; + GCLK_REGS->GCLK_PCHCTRL[GCLK_CLKCTRL_IDs[tcNum]] = + GCLK_PCHCTRL_GEN_GCLK0 | GCLK_PCHCTRL_CHEN_Msk; + + if (tcNum >= ARDUINO_TCC_INSTANCE_COUNT) { + tc_registers_t *TCx = (tc_registers_t *)GetTC(pinDesc.ulPWMChannel); + TCx->COUNT8.TC_CTRLA = TC_CTRLA_SWRST_Msk; + while (TCx->COUNT8.TC_SYNCBUSY & TC_SYNCBUSY_SWRST_Msk); + TCx->COUNT8.TC_CTRLA = TC_CTRLA_MODE_COUNT8 | TC_CTRLA_PRESCALER_DIV256; + TCx->COUNT8.TC_WAVE = TC_WAVE_WAVEGEN_NPWM; + TCx->COUNT8.TC_CC[tcChannel] = (uint8_t)value; + while (TCx->COUNT8.TC_SYNCBUSY & (TC_SYNCBUSY_CC0_Msk << tcChannel)); + TCx->COUNT8.TC_PER = 0xff; + while (TCx->COUNT8.TC_SYNCBUSY & TC_SYNCBUSY_PER_Msk); + TCx->COUNT8.TC_CTRLA |= TC_CTRLA_ENABLE_Msk; + while (TCx->COUNT8.TC_SYNCBUSY & TC_SYNCBUSY_ENABLE_Msk); + } else { + tcc_registers_t *TCCx = (tcc_registers_t *)GetTC(pinDesc.ulPWMChannel); + TCCx->TCC_CTRLA = TCC_CTRLA_SWRST_Msk; + while (TCCx->TCC_SYNCBUSY & TCC_SYNCBUSY_SWRST_Msk); + TCCx->TCC_CTRLA = TCC_CTRLA_PRESCALER_DIV256 | TCC_CTRLA_PRESCSYNC_GCLK; + TCCx->TCC_WAVE = TCC_WAVE_WAVEGEN_NPWM; + while (TCCx->TCC_SYNCBUSY & TCC_SYNCBUSY_WAVE_Msk); + TCCx->TCC_CC[tcChannel] = value; + while (TCCx->TCC_SYNCBUSY & (TCC_SYNCBUSY_CC0_Msk << tcChannel)); + TCCx->TCC_PER = 0xff; + while (TCCx->TCC_SYNCBUSY & TCC_SYNCBUSY_PER_Msk); + TCCx->TCC_CTRLA |= TCC_CTRLA_ENABLE_Msk; + while (TCCx->TCC_SYNCBUSY & TCC_SYNCBUSY_ENABLE_Msk); + } + } else if (tcNum >= ARDUINO_TCC_INSTANCE_COUNT) { + tc_registers_t *TCx = (tc_registers_t *)GetTC(pinDesc.ulPWMChannel); + TCx->COUNT8.TC_CC[tcChannel] = (uint8_t)value; + while (TCx->COUNT8.TC_SYNCBUSY & (TC_SYNCBUSY_CC0_Msk << tcChannel)); + } else { + tcc_registers_t *TCCx = (tcc_registers_t *)GetTC(pinDesc.ulPWMChannel); + while (TCCx->TCC_SYNCBUSY & TCC_SYNCBUSY_CTRLB_Msk); + TCCx->TCC_CCBUF[tcChannel] = value; + while (TCCx->TCC_SYNCBUSY & (TCC_SYNCBUSY_CC0_Msk << tcChannel)); + TCCx->TCC_CTRLBCLR = TCC_CTRLBCLR_LUPD_Msk; + while (TCCx->TCC_SYNCBUSY & TCC_SYNCBUSY_CTRLB_Msk); + } + + return; + } #else if ((attr & PIN_ATTR_PWM) == PIN_ATTR_PWM) @@ -586,7 +739,7 @@ void analogWrite(uint32_t pin, uint32_t value) uint32_t tcNum = GetTCNumber(pinDesc.ulPWMChannel); uint8_t tcChannel = GetTCChannelNumber(pinDesc.ulPWMChannel); - static bool tcEnabled[TCC_INST_NUM+TC_INST_NUM]; + static bool tcEnabled[ARDUINO_TCC_INSTANCE_COUNT + ARDUINO_TC_INSTANCE_COUNT]; if (attr & PIN_ATTR_TIMER) { #if !(ARDUINO_SAMD_VARIANT_COMPLIANCE >= 10603) @@ -622,7 +775,7 @@ void analogWrite(uint32_t pin, uint32_t value) while (GCLK->STATUS.bit.SYNCBUSY == 1); // Set PORT - if (tcNum >= TCC_INST_NUM) { + if (tcNum >= ARDUINO_TCC_INSTANCE_COUNT) { // -- Configure TC Tc* TCx = (Tc*) GetTC(pinDesc.ulPWMChannel); // Disable TCx @@ -657,7 +810,7 @@ void analogWrite(uint32_t pin, uint32_t value) syncTCC(TCCx); } } else { - if (tcNum >= TCC_INST_NUM) { + if (tcNum >= ARDUINO_TCC_INSTANCE_COUNT) { Tc* TCx = (Tc*) GetTC(pinDesc.ulPWMChannel); TCx->COUNT16.CC[tcChannel].reg = (uint32_t) value; syncTC_16(TCx); diff --git a/cores/arduino/wiring_digital.c b/cores/arduino/wiring_digital.c index c62fdebfb..d030ae974 100644 --- a/cores/arduino/wiring_digital.c +++ b/cores/arduino/wiring_digital.c @@ -22,6 +22,67 @@ extern "C" { #endif +#if defined(ARDUINO_SAME53_E54) +void pinMode(uint32_t ulPin, uint32_t ulMode) +{ + if (g_APinDescription[ulPin].ulPinType == PIO_NOT_A_PIN) return; + + port_group_registers_t *group = &PORT_REGS->GROUP[g_APinDescription[ulPin].ulPort]; + uint32_t pin = g_APinDescription[ulPin].ulPin; + uint32_t pinMask = 1UL << pin; + + switch (ulMode) + { + case INPUT: + group->PORT_PINCFG[pin] = PORT_PINCFG_INEN_Msk; + group->PORT_DIRCLR = pinMask; + break; + case INPUT_PULLUP: + group->PORT_PINCFG[pin] = PORT_PINCFG_INEN_Msk | PORT_PINCFG_PULLEN_Msk; + group->PORT_DIRCLR = pinMask; + group->PORT_OUTSET = pinMask; + break; + case INPUT_PULLDOWN: + group->PORT_PINCFG[pin] = PORT_PINCFG_INEN_Msk | PORT_PINCFG_PULLEN_Msk; + group->PORT_DIRCLR = pinMask; + group->PORT_OUTCLR = pinMask; + break; + case OUTPUT: + group->PORT_PINCFG[pin] = PORT_PINCFG_INEN_Msk | PORT_PINCFG_DRVSTR_Msk; + group->PORT_DIRSET = pinMask; + break; + default: + break; + } +} + +void digitalWrite(uint32_t ulPin, uint32_t ulVal) +{ + if (g_APinDescription[ulPin].ulPinType == PIO_NOT_A_PIN) return; + + port_group_registers_t *group = &PORT_REGS->GROUP[g_APinDescription[ulPin].ulPort]; + uint32_t pin = g_APinDescription[ulPin].ulPin; + uint32_t pinMask = 1UL << pin; + + if ((group->PORT_DIRSET & pinMask) == 0) { + if (ulVal == LOW) group->PORT_PINCFG[pin] &= ~PORT_PINCFG_PULLEN_Msk; + else group->PORT_PINCFG[pin] |= PORT_PINCFG_PULLEN_Msk; + } + + if (ulVal == LOW) group->PORT_OUTCLR = pinMask; + else group->PORT_OUTSET = pinMask; +} + +int digitalRead(uint32_t ulPin) +{ + if (g_APinDescription[ulPin].ulPinType == PIO_NOT_A_PIN) return LOW; + + port_group_registers_t *group = &PORT_REGS->GROUP[g_APinDescription[ulPin].ulPort]; + uint32_t pinMask = 1UL << g_APinDescription[ulPin].ulPin; + return (group->PORT_IN & pinMask) ? HIGH : LOW; +} + +#else void pinMode( uint32_t ulPin, uint32_t ulMode ) { // Handle the case the pin isn't usable as PIO @@ -121,8 +182,8 @@ int digitalRead( uint32_t ulPin ) return LOW ; } +#endif #ifdef __cplusplus } #endif - diff --git a/cores/arduino/wiring_private.c b/cores/arduino/wiring_private.c index 954f854af..18bb643c3 100644 --- a/cores/arduino/wiring_private.c +++ b/cores/arduino/wiring_private.c @@ -68,7 +68,7 @@ int pinPeripheral( uint32_t ulPin, EPioType ulPeripheral ) case PIO_TIMER: case PIO_TIMER_ALT: case PIO_EXTINT: -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) case PIO_TCC_PDEC: case PIO_COM: case PIO_SDHC: @@ -81,6 +81,7 @@ int pinPeripheral( uint32_t ulPin, EPioType ulPeripheral ) case PIO_COM: case PIO_AC_CLK: #endif + { #if 0 // Is the pio pin in the lower 16 ones? // The WRCONFIG register allows update of only 16 pin max out of 32 @@ -97,6 +98,19 @@ int pinPeripheral( uint32_t ulPin, EPioType ulPeripheral ) PORT_WRCONFIG_WRPINCFG | PORT_WRCONFIG_PINMASK( g_APinDescription[ulPin].ulPin - 16 ) ; } +#else +#if defined(ARDUINO_SAME53_E54) + port_group_registers_t *group = &PORT_REGS->GROUP[g_APinDescription[ulPin].ulPort]; + uint32_t pin = g_APinDescription[ulPin].ulPin; + uint32_t pmux = group->PORT_PMUX[pin >> 1]; + + if (pin & 1) { + pmux = (pmux & PORT_PMUX_PMUXE_Msk) | PORT_PMUX_PMUXO(ulPeripheral); + } else { + pmux = (pmux & PORT_PMUX_PMUXO_Msk) | PORT_PMUX_PMUXE(ulPeripheral); + } + group->PORT_PMUX[pin >> 1] = pmux; + group->PORT_PINCFG[pin] |= PORT_PINCFG_PMUXEN_Msk | PORT_PINCFG_DRVSTR_Msk; #else if ( g_APinDescription[ulPin].ulPin & 1 ) // is pin odd? { @@ -118,6 +132,8 @@ int pinPeripheral( uint32_t ulPin, EPioType ulPeripheral ) PORT->Group[g_APinDescription[ulPin].ulPort].PINCFG[g_APinDescription[ulPin].ulPin].reg |= PORT_PINCFG_PMUXEN | PORT_PINCFG_DRVSTR ; // Enable port mux } #endif +#endif + } break ; case PIO_NOT_A_PIN: @@ -127,4 +143,3 @@ int pinPeripheral( uint32_t ulPin, EPioType ulPeripheral ) return 0l ; } - diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index da9732c59..5e8b6c581 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -289,90 +289,107 @@ void TwoWire::onService(void) #endif // PERIPH_WIRE TwoWire Wire(&PERIPH_WIRE, PIN_WIRE_SDA, PIN_WIRE_SCL); - void WIRE_IT_HANDLER(void) { - Wire.onService(); - } - - #if defined(__SAMD51__) + #if defined(ARDUINO_SAMD51_E51) void WIRE_IT_HANDLER_0(void) { Wire.onService(); } void WIRE_IT_HANDLER_1(void) { Wire.onService(); } void WIRE_IT_HANDLER_2(void) { Wire.onService(); } void WIRE_IT_HANDLER_3(void) { Wire.onService(); } - #endif // __SAMD51__ + #elif defined(ARDUINO_SAME53_E54) + void WIRE_IT_HANDLER_0(void) { Wire.onService(); } + void WIRE_IT_HANDLER_1(void) { Wire.onService(); } + void WIRE_IT_HANDLER_2(void) { Wire.onService(); } + void WIRE_IT_HANDLER_OTHER(void) { Wire.onService(); } + #else + void WIRE_IT_HANDLER(void) { Wire.onService(); } + #endif #endif #if WIRE_INTERFACES_COUNT > 1 TwoWire Wire1(&PERIPH_WIRE1, PIN_WIRE1_SDA, PIN_WIRE1_SCL); - void WIRE1_IT_HANDLER(void) { - Wire1.onService(); - } - - #if defined(__SAMD51__) + #if defined(ARDUINO_SAMD51_E51) void WIRE1_IT_HANDLER_0(void) { Wire1.onService(); } void WIRE1_IT_HANDLER_1(void) { Wire1.onService(); } void WIRE1_IT_HANDLER_2(void) { Wire1.onService(); } void WIRE1_IT_HANDLER_3(void) { Wire1.onService(); } - #endif // __SAMD51__ + #elif defined(ARDUINO_SAME53_E54) + void WIRE1_IT_HANDLER_0(void) { Wire1.onService(); } + void WIRE1_IT_HANDLER_1(void) { Wire1.onService(); } + void WIRE1_IT_HANDLER_2(void) { Wire1.onService(); } + void WIRE1_IT_HANDLER_OTHER(void) { Wire1.onService(); } + #else + void WIRE1_IT_HANDLER(void) { Wire1.onService(); } + #endif #endif #if WIRE_INTERFACES_COUNT > 2 TwoWire Wire2(&PERIPH_WIRE2, PIN_WIRE2_SDA, PIN_WIRE2_SCL); - void WIRE2_IT_HANDLER(void) { - Wire2.onService(); - } - - #if defined(__SAMD51__) + #if defined(ARDUINO_SAMD51_E51) void WIRE2_IT_HANDLER_0(void) { Wire2.onService(); } void WIRE2_IT_HANDLER_1(void) { Wire2.onService(); } void WIRE2_IT_HANDLER_2(void) { Wire2.onService(); } void WIRE2_IT_HANDLER_3(void) { Wire2.onService(); } - #endif // __SAMD51__ + #elif defined(ARDUINO_SAME53_E54) + void WIRE2_IT_HANDLER_0(void) { Wire2.onService(); } + void WIRE2_IT_HANDLER_1(void) { Wire2.onService(); } + void WIRE2_IT_HANDLER_2(void) { Wire2.onService(); } + void WIRE2_IT_HANDLER_OTHER(void) { Wire2.onService(); } + #else + void WIRE2_IT_HANDLER(void) { Wire2.onService(); } + #endif #endif #if WIRE_INTERFACES_COUNT > 3 TwoWire Wire3(&PERIPH_WIRE3, PIN_WIRE3_SDA, PIN_WIRE3_SCL); - void WIRE3_IT_HANDLER(void) { - Wire3.onService(); - } - - #if defined(__SAMD51__) + #if defined(ARDUINO_SAMD51_E51) void WIRE3_IT_HANDLER_0(void) { Wire3.onService(); } void WIRE3_IT_HANDLER_1(void) { Wire3.onService(); } void WIRE3_IT_HANDLER_2(void) { Wire3.onService(); } void WIRE3_IT_HANDLER_3(void) { Wire3.onService(); } - #endif // __SAMD51__ + #elif defined(ARDUINO_SAME53_E54) + void WIRE3_IT_HANDLER_0(void) { Wire3.onService(); } + void WIRE3_IT_HANDLER_1(void) { Wire3.onService(); } + void WIRE3_IT_HANDLER_2(void) { Wire3.onService(); } + void WIRE3_IT_HANDLER_OTHER(void) { Wire3.onService(); } + #else + void WIRE3_IT_HANDLER(void) { Wire3.onService(); } + #endif #endif #if WIRE_INTERFACES_COUNT > 4 TwoWire Wire4(&PERIPH_WIRE4, PIN_WIRE4_SDA, PIN_WIRE4_SCL); - void WIRE4_IT_HANDLER(void) { - Wire4.onService(); - } - - #if defined(__SAMD51__) + #if defined(ARDUINO_SAMD51_E51) void WIRE4_IT_HANDLER_0(void) { Wire4.onService(); } void WIRE4_IT_HANDLER_1(void) { Wire4.onService(); } void WIRE4_IT_HANDLER_2(void) { Wire4.onService(); } void WIRE4_IT_HANDLER_3(void) { Wire4.onService(); } - #endif // __SAMD51__ + #elif defined(ARDUINO_SAME53_E54) + void WIRE4_IT_HANDLER_0(void) { Wire4.onService(); } + void WIRE4_IT_HANDLER_1(void) { Wire4.onService(); } + void WIRE4_IT_HANDLER_2(void) { Wire4.onService(); } + void WIRE4_IT_HANDLER_OTHER(void) { Wire4.onService(); } + #else + void WIRE4_IT_HANDLER(void) { Wire4.onService(); } + #endif #endif #if WIRE_INTERFACES_COUNT > 5 TwoWire Wire5(&PERIPH_WIRE5, PIN_WIRE5_SDA, PIN_WIRE5_SCL); - void WIRE5_IT_HANDLER(void) { - Wire5.onService(); - } - - #if defined(__SAMD51__) + #if defined(ARDUINO_SAMD51_E51) void WIRE5_IT_HANDLER_0(void) { Wire5.onService(); } void WIRE5_IT_HANDLER_1(void) { Wire5.onService(); } void WIRE5_IT_HANDLER_2(void) { Wire5.onService(); } void WIRE5_IT_HANDLER_3(void) { Wire5.onService(); } - #endif // __SAMD51__ + #elif defined(ARDUINO_SAME53_E54) + void WIRE5_IT_HANDLER_0(void) { Wire5.onService(); } + void WIRE5_IT_HANDLER_1(void) { Wire5.onService(); } + void WIRE5_IT_HANDLER_2(void) { Wire5.onService(); } + void WIRE5_IT_HANDLER_OTHER(void) { Wire5.onService(); } + #else + void WIRE5_IT_HANDLER(void) { Wire5.onService(); } + #endif #endif - diff --git a/variants/xplained_m4/variant.cpp b/variants/xplained_m4/variant.cpp index 0015e3c15..33d161b4a 100644 --- a/variants/xplained_m4/variant.cpp +++ b/variants/xplained_m4/variant.cpp @@ -103,29 +103,35 @@ const uint8_t g_AAnalogPinMap[NUM_ANALOG_INPUTS] = { PIN_A8, PIN_A9, PIN_A10, PIN_A11, PIN_A12, PIN_A13, PIN_A14, PIN_A15, }; -const void *g_apTCInstances[TCC_INST_NUM + TC_INST_NUM] = {TCC0, TCC1, TCC2, TCC3, TCC4, TC0, TC1, TC2, TC3, TC4, TC5}; -const uint32_t GCLK_CLKCTRL_IDs[TCC_INST_NUM + TC_INST_NUM] = {TCC0_GCLK_ID, TCC1_GCLK_ID, TCC2_GCLK_ID, TCC3_GCLK_ID, TCC4_GCLK_ID, TC0_GCLK_ID, TC1_GCLK_ID, TC2_GCLK_ID, TC3_GCLK_ID, TC4_GCLK_ID, TC5_GCLK_ID}; +const void *g_apTCInstances[ARDUINO_TCC_INSTANCE_COUNT + ARDUINO_TC_INSTANCE_COUNT] = { + TCC0_REGS, TCC1_REGS, TCC2_REGS, TCC3_REGS, TCC4_REGS, + TC0_REGS, TC1_REGS, TC2_REGS, TC3_REGS, TC4_REGS, TC5_REGS, + TC6_REGS, TC7_REGS}; +const uint32_t GCLK_CLKCTRL_IDs[ARDUINO_TCC_INSTANCE_COUNT + ARDUINO_TC_INSTANCE_COUNT] = { + TCC0_GCLK_ID, TCC1_GCLK_ID, TCC2_GCLK_ID, TCC3_GCLK_ID, TCC4_GCLK_ID, + TC0_GCLK_ID, TC1_GCLK_ID, TC2_GCLK_ID, TC3_GCLK_ID, TC4_GCLK_ID, + TC5_GCLK_ID, TC6_GCLK_ID, TC7_GCLK_ID}; // Multi-serial objects instantiation -SERCOM sercom0(SERCOM0); -SERCOM sercom1(SERCOM1); -SERCOM sercom2(SERCOM2); -SERCOM sercom3(SERCOM3); -SERCOM sercom4(SERCOM4); -SERCOM sercom5(SERCOM5); -SERCOM sercom6(SERCOM6); -SERCOM sercom7(SERCOM7); +SERCOM sercom0(SERCOM0_REGS); +SERCOM sercom1(SERCOM1_REGS); +SERCOM sercom2(SERCOM2_REGS); +SERCOM sercom3(SERCOM3_REGS); +SERCOM sercom4(SERCOM4_REGS); +SERCOM sercom5(SERCOM5_REGS); +SERCOM sercom6(SERCOM6_REGS); +SERCOM sercom7(SERCOM7_REGS); Uart Serial1(&sercom0, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX); Uart Serial2(&sercom5, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX); Uart Serial3(&sercom1, PIN_SERIAL3_RX, PIN_SERIAL3_TX, PAD_SERIAL3_RX, PAD_SERIAL3_TX); Uart Serial4(&sercom2, PIN_SERIAL4_RX, PIN_SERIAL4_TX, PAD_SERIAL4_RX, PAD_SERIAL4_TX); -#define UART_HANDLERS(n, serial) \ - void SERCOM##n##_0_Handler() { serial.IrqHandler(); } \ - void SERCOM##n##_1_Handler() { serial.IrqHandler(); } \ - void SERCOM##n##_2_Handler() { serial.IrqHandler(); } \ - void SERCOM##n##_3_Handler() { serial.IrqHandler(); } +#define UART_HANDLERS(n, serial) \ + void SERCOM##n##_0_Handler() { serial.IrqHandler(); } \ + void SERCOM##n##_1_Handler() { serial.IrqHandler(); } \ + void SERCOM##n##_2_Handler() { serial.IrqHandler(); } \ + void SERCOM##n##_OTHER_Handler() { serial.IrqHandler(); } UART_HANDLERS(0, Serial1) UART_HANDLERS(5, Serial2) diff --git a/variants/xplained_m4/variant.h b/variants/xplained_m4/variant.h index 288b89863..5d87f9032 100644 --- a/variants/xplained_m4/variant.h +++ b/variants/xplained_m4/variant.h @@ -211,7 +211,7 @@ extern const uint8_t g_AAnalogPinMap[NUM_ANALOG_INPUTS]; #define WIRE_IT_HANDLER_0 SERCOM3_0_Handler #define WIRE_IT_HANDLER_1 SERCOM3_1_Handler #define WIRE_IT_HANDLER_2 SERCOM3_2_Handler -#define WIRE_IT_HANDLER_3 SERCOM3_3_Handler +#define WIRE_IT_HANDLER_OTHER SERCOM3_OTHER_Handler static const uint8_t SDA = PIN_WIRE_SDA; static const uint8_t SCL = PIN_WIRE_SCL; @@ -223,7 +223,7 @@ extern const uint8_t g_AAnalogPinMap[NUM_ANALOG_INPUTS]; #define WIRE1_IT_HANDLER_0 SERCOM7_0_Handler #define WIRE1_IT_HANDLER_1 SERCOM7_1_Handler #define WIRE1_IT_HANDLER_2 SERCOM7_2_Handler -#define WIRE1_IT_HANDLER_3 SERCOM7_3_Handler +#define WIRE1_IT_HANDLER_OTHER SERCOM7_OTHER_Handler static const uint8_t SDA1 = PIN_WIRE1_SDA; static const uint8_t SCL1 = PIN_WIRE1_SCL; From 8344356d3cc58b0248d3a0b61f1a60b9b7448c66 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 17 Jul 2026 11:35:45 -0400 Subject: [PATCH 50/79] feat(usb): add native SAME53/E54 legacy USB support --- cores/arduino/Arduino.h | 6 +- cores/arduino/USB/SAMD21_USBDevice.cpp | 23 +++ cores/arduino/USB/SAMD21_USBDevice.h | 195 ++++++++++++++++++++--- cores/arduino/USB/USBCore.cpp | 47 ++++-- cores/arduino/USB/samd21_host.c | 207 ++++++++++++++++++++++++- cores/arduino/USB/samd21_host.h | 14 ++ 6 files changed, 446 insertions(+), 46 deletions(-) diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index 175dcf705..ebe2fe4a8 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -48,6 +48,8 @@ extern "C"{ #include "sam.h" #include "wiring_constants.h" +extern uint32_t SystemCoreClock; + #define clockCyclesPerMicrosecond() ( SystemCoreClock / 1000000L ) #define clockCyclesToMicroseconds(a) ( ((a) * 1000L) / (SystemCoreClock / 1000L) ) #define microsecondsToClockCycles(a) ( (a) * (SystemCoreClock / 1000000L) ) @@ -156,7 +158,9 @@ void loop( void ) ; #include "USB/USBDesc.h" #include "USB/USBCore.h" #include "USB/USBAPI.h" - #include "USB/USB_host.h" + #if !defined(ARDUINO_SAME53_E54) + #include "USB/USB_host.h" + #endif #endif #endif // Arduino_h diff --git a/cores/arduino/USB/SAMD21_USBDevice.cpp b/cores/arduino/USB/SAMD21_USBDevice.cpp index f45d256dc..da0e7b4d4 100644 --- a/cores/arduino/USB/SAMD21_USBDevice.cpp +++ b/cores/arduino/USB/SAMD21_USBDevice.cpp @@ -10,14 +10,30 @@ #include "SAMD21_USBDevice.h" void USBDevice_SAMD21G18x::reset() { +#if defined(ARDUINO_SAME53_E54) + usb.USB_CTRLA |= USB_CTRLA_SWRST_Msk; + memset(EP, 0, sizeof(EP)); + while (usb.USB_SYNCBUSY & (USB_SYNCBUSY_SWRST_Msk | USB_SYNCBUSY_ENABLE_Msk)) {} + usb.USB_DESCADD = (uint32_t)(&EP); +#else usb.CTRLA.bit.SWRST = 1; memset(EP, 0, sizeof(EP)); while (usb.SYNCBUSY.bit.SWRST || usb.SYNCBUSY.bit.ENABLE) {} usb.DESCADD.reg = (uint32_t)(&EP); +#endif } void USBDevice_SAMD21G18x::calibrate() { // Load Pad Calibration data from non-volatile memory +#if defined(ARDUINO_SAME53_E54) + const uint32_t calibration = *(const uint32_t *)(SW0_ADDR + 4u); + uint32_t pad_transn = (calibration & FUSES_SW0_WORD_1_USB_TRANSN_Msk) >> + FUSES_SW0_WORD_1_USB_TRANSN_Pos; + uint32_t pad_transp = (calibration & FUSES_SW0_WORD_1_USB_TRANSP_Msk) >> + FUSES_SW0_WORD_1_USB_TRANSP_Pos; + uint32_t pad_trim = (calibration & FUSES_SW0_WORD_1_USB_TRIM_Msk) >> + FUSES_SW0_WORD_1_USB_TRIM_Pos; +#else uint32_t *pad_transn_p = (uint32_t *) USB_FUSES_TRANSN_ADDR; uint32_t *pad_transp_p = (uint32_t *) USB_FUSES_TRANSP_ADDR; uint32_t *pad_trim_p = (uint32_t *) USB_FUSES_TRIM_ADDR; @@ -25,6 +41,7 @@ void USBDevice_SAMD21G18x::calibrate() { uint32_t pad_transn = (*pad_transn_p & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos; uint32_t pad_transp = (*pad_transp_p & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos; uint32_t pad_trim = (*pad_trim_p & USB_FUSES_TRIM_Msk ) >> USB_FUSES_TRIM_Pos; +#endif if (pad_transn == 0x1F) // maximum value (31) pad_transn = 5; @@ -33,9 +50,15 @@ void USBDevice_SAMD21G18x::calibrate() { if (pad_trim == 0x7) // maximum value (7) pad_trim = 3; +#if defined(ARDUINO_SAME53_E54) + usb.USB_PADCAL = USB_PADCAL_TRANSN(pad_transn) | + USB_PADCAL_TRANSP(pad_transp) | + USB_PADCAL_TRIM(pad_trim); +#else usb.PADCAL.bit.TRANSN = pad_transn; usb.PADCAL.bit.TRANSP = pad_transp; usb.PADCAL.bit.TRIM = pad_trim; +#endif } #endif // USE_TINYUSB diff --git a/cores/arduino/USB/SAMD21_USBDevice.h b/cores/arduino/USB/SAMD21_USBDevice.h index 00da069db..825166711 100644 --- a/cores/arduino/USB/SAMD21_USBDevice.h +++ b/cores/arduino/USB/SAMD21_USBDevice.h @@ -29,6 +29,64 @@ typedef uint8_t ep_t; class USBDevice_SAMD21G18x { public: +#if defined(ARDUINO_SAME53_E54) + USBDevice_SAMD21G18x() : usb(USB_REGS->DEVICE) { + // Empty + } + + // USB Device function mapping + // --------------------------- + + // Reset USB Device + void reset(); + + // Enable + inline void enable() { + usb.USB_CTRLA |= USB_CTRLA_ENABLE_Msk; + while (usb.USB_SYNCBUSY & USB_SYNCBUSY_ENABLE_Msk); + } + inline void disable() { + usb.USB_CTRLA &= ~USB_CTRLA_ENABLE_Msk; + while (usb.USB_SYNCBUSY & USB_SYNCBUSY_ENABLE_Msk); + } + + // USB mode (device/host) + inline void setUSBDeviceMode() { usb.USB_CTRLA &= ~USB_CTRLA_MODE_Msk; } + inline void setUSBHostMode() { usb.USB_CTRLA |= USB_CTRLA_MODE_Msk; } + + inline void runInStandby() { usb.USB_CTRLA |= USB_CTRLA_RUNSTDBY_Msk; } + inline void noRunInStandby() { usb.USB_CTRLA &= ~USB_CTRLA_RUNSTDBY_Msk; } + inline void wakeupHost() { usb.USB_CTRLB |= USB_DEVICE_CTRLB_UPRSM_Msk; } + + // USB speed + inline void setFullSpeed() { setSpeedConfiguration(USB_DEVICE_CTRLB_SPDCONF_FS_Val); } + inline void setLowSpeed() { setSpeedConfiguration(USB_DEVICE_CTRLB_SPDCONF_LS_Val); } + inline void setHiSpeed() { setFullSpeed(); } + inline void setHiSpeedTestMode() { setFullSpeed(); } + + // Authorize attach if Vbus is present + inline void attach() { usb.USB_CTRLB &= ~USB_DEVICE_CTRLB_DETACH_Msk; } + inline void detach() { usb.USB_CTRLB |= USB_DEVICE_CTRLB_DETACH_Msk; } + + // USB Interrupts + inline bool isEndOfResetInterrupt() { return usb.USB_INTFLAG & USB_DEVICE_INTFLAG_EORST_Msk; } + inline void ackEndOfResetInterrupt() { usb.USB_INTFLAG = USB_DEVICE_INTFLAG_EORST_Msk; } + inline void enableEndOfResetInterrupt() { usb.USB_INTENSET = USB_DEVICE_INTENSET_EORST_Msk; } + inline void disableEndOfResetInterrupt() { usb.USB_INTENCLR = USB_DEVICE_INTENCLR_EORST_Msk; } + + inline bool isStartOfFrameInterrupt() { return usb.USB_INTFLAG & USB_DEVICE_INTFLAG_SOF_Msk; } + inline void ackStartOfFrameInterrupt() { usb.USB_INTFLAG = USB_DEVICE_INTFLAG_SOF_Msk; } + inline void enableStartOfFrameInterrupt() { usb.USB_INTENSET = USB_DEVICE_INTENSET_SOF_Msk; } + inline void disableStartOfFrameInterrupt() { usb.USB_INTENCLR = USB_DEVICE_INTENCLR_SOF_Msk; } + + // USB Address + inline void setAddress(uint32_t addr) { usb.USB_DADD = USB_DEVICE_DADD_DADD(addr) | USB_DEVICE_DADD_ADDEN_Msk; } + inline void unsetAddress() { usb.USB_DADD = 0; } + + // Frame number + inline uint16_t frameNumber() { return (usb.USB_FNUM & USB_DEVICE_FNUM_FNUM_Msk) >> USB_DEVICE_FNUM_FNUM_Pos; } + +#else USBDevice_SAMD21G18x() : usb(USB->DEVICE) { // Empty } @@ -40,54 +98,55 @@ class USBDevice_SAMD21G18x { void reset(); // Enable - inline void enable() { + inline void enable() { usb.CTRLA.bit.ENABLE = 1; -#if defined(__SAMD51__) - while( usb.SYNCBUSY.reg & USB_SYNCBUSY_ENABLE ); //wait for sync +#if defined(ARDUINO_SAMD51_E51) + while (usb.SYNCBUSY.reg & USB_SYNCBUSY_ENABLE); // Wait for sync #endif } - inline void disable() { + inline void disable() { usb.CTRLA.bit.ENABLE = 0; -#if defined(__SAMD51__) - while( usb.SYNCBUSY.reg & USB_SYNCBUSY_ENABLE ); //wait for sync +#if defined(ARDUINO_SAMD51_E51) + while (usb.SYNCBUSY.reg & USB_SYNCBUSY_ENABLE); // Wait for sync #endif } // USB mode (device/host) inline void setUSBDeviceMode() { usb.CTRLA.bit.MODE = USB_CTRLA_MODE_DEVICE_Val; } - inline void setUSBHostMode() { usb.CTRLA.bit.MODE = USB_CTRLA_MODE_HOST_Val; } + inline void setUSBHostMode() { usb.CTRLA.bit.MODE = USB_CTRLA_MODE_HOST_Val; } - inline void runInStandby() { usb.CTRLA.bit.RUNSTDBY = 1; } + inline void runInStandby() { usb.CTRLA.bit.RUNSTDBY = 1; } inline void noRunInStandby() { usb.CTRLA.bit.RUNSTDBY = 0; } - inline void wakeupHost() { usb.CTRLB.bit.UPRSM = 1; } + inline void wakeupHost() { usb.CTRLB.bit.UPRSM = 1; } // USB speed - inline void setFullSpeed() { usb.CTRLB.bit.SPDCONF = USB_DEVICE_CTRLB_SPDCONF_FS_Val; } - inline void setLowSpeed() { usb.CTRLB.bit.SPDCONF = USB_DEVICE_CTRLB_SPDCONF_LS_Val; } - inline void setHiSpeed() { usb.CTRLB.bit.SPDCONF = USB_DEVICE_CTRLB_SPDCONF_HS_Val; } - inline void setHiSpeedTestMode() { usb.CTRLB.bit.SPDCONF = USB_DEVICE_CTRLB_SPDCONF_HSTM_Val; } + inline void setFullSpeed() { setSpeedConfiguration(USB_DEVICE_CTRLB_SPDCONF_FS_Val); } + inline void setLowSpeed() { setSpeedConfiguration(USB_DEVICE_CTRLB_SPDCONF_LS_Val); } + inline void setHiSpeed() { setSpeedConfiguration(USB_DEVICE_CTRLB_SPDCONF_HS_Val); } + inline void setHiSpeedTestMode() { setSpeedConfiguration(USB_DEVICE_CTRLB_SPDCONF_HSTM_Val); } // Authorize attach if Vbus is present inline void attach() { usb.CTRLB.bit.DETACH = 0; } inline void detach() { usb.CTRLB.bit.DETACH = 1; } // USB Interrupts - inline bool isEndOfResetInterrupt() { return usb.INTFLAG.bit.EORST; } - inline void ackEndOfResetInterrupt() { usb.INTFLAG.reg = USB_DEVICE_INTFLAG_EORST; } - inline void enableEndOfResetInterrupt() { usb.INTENSET.bit.EORST = 1; } - inline void disableEndOfResetInterrupt() { usb.INTENCLR.bit.EORST = 1; } - - inline bool isStartOfFrameInterrupt() { return usb.INTFLAG.bit.SOF; } - inline void ackStartOfFrameInterrupt() { usb.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF; } - inline void enableStartOfFrameInterrupt() { usb.INTENSET.bit.SOF = 1; } + inline bool isEndOfResetInterrupt() { return usb.INTFLAG.bit.EORST; } + inline void ackEndOfResetInterrupt() { usb.INTFLAG.reg = USB_DEVICE_INTFLAG_EORST; } + inline void enableEndOfResetInterrupt() { usb.INTENSET.bit.EORST = 1; } + inline void disableEndOfResetInterrupt() { usb.INTENCLR.bit.EORST = 1; } + + inline bool isStartOfFrameInterrupt() { return usb.INTFLAG.bit.SOF; } + inline void ackStartOfFrameInterrupt() { usb.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF; } + inline void enableStartOfFrameInterrupt() { usb.INTENSET.bit.SOF = 1; } inline void disableStartOfFrameInterrupt() { usb.INTENCLR.bit.SOF = 1; } // USB Address - inline void setAddress(uint32_t addr) { usb.DADD.bit.DADD = addr; usb.DADD.bit.ADDEN = 1; } - inline void unsetAddress() { usb.DADD.bit.DADD = 0; usb.DADD.bit.ADDEN = 0; } + inline void setAddress(uint32_t addr) { usb.DADD.bit.DADD = addr; usb.DADD.bit.ADDEN = 1; } + inline void unsetAddress() { usb.DADD.bit.DADD = 0; usb.DADD.bit.ADDEN = 0; } // Frame number inline uint16_t frameNumber() { return usb.FNUM.bit.FNUM; } +#endif // Load calibration values void calibrate(); @@ -95,6 +154,77 @@ class USBDevice_SAMD21G18x { // USB Device Endpoints function mapping // ------------------------------------- +#if defined(ARDUINO_SAME53_E54) + // Config + inline void epBank0SetType(ep_t ep, uint8_t type) { + usb.DEVICE_ENDPOINT[ep].USB_EPCFG = + (usb.DEVICE_ENDPOINT[ep].USB_EPCFG & ~USB_DEVICE_EPCFG_EPTYPE0_Msk) | + USB_DEVICE_EPCFG_EPTYPE0(type); + } + inline void epBank1SetType(ep_t ep, uint8_t type) { + usb.DEVICE_ENDPOINT[ep].USB_EPCFG = + (usb.DEVICE_ENDPOINT[ep].USB_EPCFG & ~USB_DEVICE_EPCFG_EPTYPE1_Msk) | + USB_DEVICE_EPCFG_EPTYPE1(type); + } + + // Interrupts + inline uint16_t epInterruptSummary() { return usb.USB_EPINTSMRY; } + inline bool epBank0IsSetupReceived(ep_t ep) { return usb.DEVICE_ENDPOINT[ep].USB_EPINTFLAG & USB_DEVICE_EPINTFLAG_RXSTP_Msk; } + inline bool epBank0IsStalled(ep_t ep) { return usb.DEVICE_ENDPOINT[ep].USB_EPINTFLAG & USB_DEVICE_EPINTFLAG_STALL0_Msk; } + inline bool epBank1IsStalled(ep_t ep) { return usb.DEVICE_ENDPOINT[ep].USB_EPINTFLAG & USB_DEVICE_EPINTFLAG_STALL1_Msk; } + inline bool epBank0IsTransferComplete(ep_t ep) { return usb.DEVICE_ENDPOINT[ep].USB_EPINTFLAG & USB_DEVICE_EPINTFLAG_TRCPT0_Msk; } + inline bool epBank1IsTransferComplete(ep_t ep) { return usb.DEVICE_ENDPOINT[ep].USB_EPINTFLAG & USB_DEVICE_EPINTFLAG_TRCPT1_Msk; } + inline void epBank0AckSetupReceived(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTFLAG = USB_DEVICE_EPINTFLAG_RXSTP_Msk; } + inline void epBank0AckStalled(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTFLAG = USB_DEVICE_EPINTFLAG_STALL0_Msk; } + inline void epBank1AckStalled(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTFLAG = USB_DEVICE_EPINTFLAG_STALL1_Msk; } + inline void epBank0AckTransferComplete(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTFLAG = USB_DEVICE_EPINTFLAG_TRCPT0_Msk; } + inline void epBank1AckTransferComplete(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTFLAG = USB_DEVICE_EPINTFLAG_TRCPT1_Msk; } + inline void epBank0EnableSetupReceived(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTENSET = USB_DEVICE_EPINTENSET_RXSTP_Msk; } + inline void epBank0EnableStalled(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTENSET = USB_DEVICE_EPINTENSET_STALL0_Msk; } + inline void epBank1EnableStalled(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTENSET = USB_DEVICE_EPINTENSET_STALL1_Msk; } + inline void epBank0EnableTransferComplete(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTENSET = USB_DEVICE_EPINTENSET_TRCPT0_Msk; } + inline void epBank1EnableTransferComplete(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTENSET = USB_DEVICE_EPINTENSET_TRCPT1_Msk; } + inline void epBank0DisableSetupReceived(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTENCLR = USB_DEVICE_EPINTENCLR_RXSTP_Msk; } + inline void epBank0DisableStalled(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTENCLR = USB_DEVICE_EPINTENCLR_STALL0_Msk; } + inline void epBank1DisableStalled(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTENCLR = USB_DEVICE_EPINTENCLR_STALL1_Msk; } + inline void epBank0DisableTransferComplete(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTENCLR = USB_DEVICE_EPINTENCLR_TRCPT0_Msk; } + inline void epBank1DisableTransferComplete(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPINTENCLR = USB_DEVICE_EPINTENCLR_TRCPT1_Msk; } + + // Status + inline bool epBank0IsReady(ep_t ep) { return usb.DEVICE_ENDPOINT[ep].USB_EPSTATUS & USB_DEVICE_EPSTATUS_BK0RDY_Msk; } + inline bool epBank1IsReady(ep_t ep) { return usb.DEVICE_ENDPOINT[ep].USB_EPSTATUS & USB_DEVICE_EPSTATUS_BK1RDY_Msk; } + inline void epBank0SetReady(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPSTATUSSET = USB_DEVICE_EPSTATUSSET_BK0RDY_Msk; } + inline void epBank1SetReady(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPSTATUSSET = USB_DEVICE_EPSTATUSSET_BK1RDY_Msk; } + inline void epBank0ResetReady(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPSTATUSCLR = USB_DEVICE_EPSTATUSCLR_BK0RDY_Msk; } + inline void epBank1ResetReady(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPSTATUSCLR = USB_DEVICE_EPSTATUSCLR_BK1RDY_Msk; } + inline void epBank0SetStallReq(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPSTATUSSET = USB_DEVICE_EPSTATUSSET_STALLRQ0_Msk; } + inline void epBank1SetStallReq(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPSTATUSSET = USB_DEVICE_EPSTATUSSET_STALLRQ1_Msk; } + inline void epBank0ResetStallReq(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPSTATUSCLR = USB_DEVICE_EPSTATUSCLR_STALLRQ0_Msk; } + inline void epBank1ResetStallReq(ep_t ep) { usb.DEVICE_ENDPOINT[ep].USB_EPSTATUSCLR = USB_DEVICE_EPSTATUSCLR_STALLRQ1_Msk; } + + // Packet + inline uint16_t epBank0ByteCount(ep_t ep) { return (EP[ep].DEVICE_DESC_BANK[0].USB_PCKSIZE & USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk) >> USB_DEVICE_PCKSIZE_BYTE_COUNT_Pos; } + inline uint16_t epBank1ByteCount(ep_t ep) { return (EP[ep].DEVICE_DESC_BANK[1].USB_PCKSIZE & USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk) >> USB_DEVICE_PCKSIZE_BYTE_COUNT_Pos; } + inline void epBank0SetByteCount(ep_t ep, uint16_t bc) { EP[ep].DEVICE_DESC_BANK[0].USB_PCKSIZE = (EP[ep].DEVICE_DESC_BANK[0].USB_PCKSIZE & ~USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk) | USB_DEVICE_PCKSIZE_BYTE_COUNT(bc); } + inline void epBank1SetByteCount(ep_t ep, uint16_t bc) { EP[ep].DEVICE_DESC_BANK[1].USB_PCKSIZE = (EP[ep].DEVICE_DESC_BANK[1].USB_PCKSIZE & ~USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk) | USB_DEVICE_PCKSIZE_BYTE_COUNT(bc); } + inline void epBank0SetMultiPacketSize(ep_t ep, uint16_t s) { EP[ep].DEVICE_DESC_BANK[0].USB_PCKSIZE = (EP[ep].DEVICE_DESC_BANK[0].USB_PCKSIZE & ~USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk) | USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE(s); } + inline void epBank1SetMultiPacketSize(ep_t ep, uint16_t s) { EP[ep].DEVICE_DESC_BANK[1].USB_PCKSIZE = (EP[ep].DEVICE_DESC_BANK[1].USB_PCKSIZE & ~USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk) | USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE(s); } + inline void epBank0SetAddress(ep_t ep, void *addr) { EP[ep].DEVICE_DESC_BANK[0].USB_ADDR = (uint32_t)addr; } + inline void epBank1SetAddress(ep_t ep, void *addr) { EP[ep].DEVICE_DESC_BANK[1].USB_ADDR = (uint32_t)addr; } + inline void epBank0SetSize(ep_t ep, uint16_t size) { EP[ep].DEVICE_DESC_BANK[0].USB_PCKSIZE = (EP[ep].DEVICE_DESC_BANK[0].USB_PCKSIZE & ~USB_DEVICE_PCKSIZE_SIZE_Msk) | USB_DEVICE_PCKSIZE_SIZE(EP_PCKSIZE_SIZE(size)); } + inline void epBank1SetSize(ep_t ep, uint16_t size) { EP[ep].DEVICE_DESC_BANK[1].USB_PCKSIZE = (EP[ep].DEVICE_DESC_BANK[1].USB_PCKSIZE & ~USB_DEVICE_PCKSIZE_SIZE_Msk) | USB_DEVICE_PCKSIZE_SIZE(EP_PCKSIZE_SIZE(size)); } + inline uint8_t EP_PCKSIZE_SIZE(uint16_t size) { + switch (size) { + case 8: return 0; case 16: return 1; case 32: return 2; case 64: return 3; + case 128: return 4; case 256: return 5; case 512: return 6; case 1023: return 7; + default: return 0; + } + } + inline void epBank0DisableAutoZLP(ep_t ep) { EP[ep].DEVICE_DESC_BANK[0].USB_PCKSIZE &= ~USB_DEVICE_PCKSIZE_AUTO_ZLP_Msk; } + inline void epBank1DisableAutoZLP(ep_t ep) { EP[ep].DEVICE_DESC_BANK[1].USB_PCKSIZE &= ~USB_DEVICE_PCKSIZE_AUTO_ZLP_Msk; } + inline void epBank0EnableAutoZLP(ep_t ep) { EP[ep].DEVICE_DESC_BANK[0].USB_PCKSIZE |= USB_DEVICE_PCKSIZE_AUTO_ZLP_Msk; } + inline void epBank1EnableAutoZLP(ep_t ep) { EP[ep].DEVICE_DESC_BANK[1].USB_PCKSIZE |= USB_DEVICE_PCKSIZE_AUTO_ZLP_Msk; } +#else // Config inline void epBank0SetType(ep_t ep, uint8_t type) { usb.DeviceEndpoint[ep].EPCFG.bit.EPTYPE0 = type; } inline void epBank1SetType(ep_t ep, uint8_t type) { usb.DeviceEndpoint[ep].EPCFG.bit.EPTYPE1 = type; } @@ -169,13 +299,30 @@ class USBDevice_SAMD21G18x { inline void epBank1DisableAutoZLP(ep_t ep) { EP[ep].DeviceDescBank[1].PCKSIZE.bit.AUTO_ZLP = 0; } inline void epBank0EnableAutoZLP(ep_t ep) { EP[ep].DeviceDescBank[0].PCKSIZE.bit.AUTO_ZLP = 1; } inline void epBank1EnableAutoZLP(ep_t ep) { EP[ep].DeviceDescBank[1].PCKSIZE.bit.AUTO_ZLP = 1; } - +#endif private: +#if defined(ARDUINO_SAME53_E54) + inline void setSpeedConfiguration(uint8_t speed) { + usb.USB_CTRLB = (usb.USB_CTRLB & ~USB_DEVICE_CTRLB_SPDCONF_Msk) | + USB_DEVICE_CTRLB_SPDCONF(speed); + } + + // USB Device registers + usb_device_registers_t &usb; + + // Endpoints descriptors table + __attribute__((__aligned__(4))) usb_descriptor_device_registers_t EP[USB_EPT_NUM]; +#else + inline void setSpeedConfiguration(uint8_t speed) { + usb.CTRLB.bit.SPDCONF = speed; + } + // USB Device registers UsbDevice &usb; // Endpoints descriptors table __attribute__((__aligned__(4))) UsbDeviceDescriptor EP[USB_EPT_NUM]; +#endif }; /* diff --git a/cores/arduino/USB/USBCore.cpp b/cores/arduino/USB/USBCore.cpp index 83a3e5d32..f95df0d4a 100644 --- a/cores/arduino/USB/USBCore.cpp +++ b/cores/arduino/USB/USBCore.cpp @@ -54,6 +54,7 @@ static char isRemoteWakeUpEnabled = 0; static char isEndpointHalt = 0; extern void (*gpf_isr)(void); +extern "C" void USB_SetHandler(void (*new_usb_isr)(void)); // USB_Handler ISR extern "C" void UDD_Handler(void) { @@ -246,7 +247,7 @@ bool USBDeviceClass::sendDescriptor(USBSetup &setup) } else if (setup.wValueL == ISERIAL) { #ifdef PLUGGABLE_USB_ENABLED -#ifdef __SAMD51__ + #if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) #define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x008061FC) #define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x00806010) #define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x00806014) @@ -333,7 +334,7 @@ void USBDeviceClass::init() #endif /* Enable USB clock */ -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB; MCLK->AHBMASK.reg |= MCLK_AHBMASK_USB; @@ -347,6 +348,23 @@ void USBDeviceClass::init() GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); +#elif defined(ARDUINO_SAME53_E54) + MCLK_REGS->MCLK_APBBMASK |= MCLK_APBBMASK_USB_Msk; + MCLK_REGS->MCLK_AHBMASK |= MCLK_AHBMASK_USB_Msk; + + PORT_REGS->GROUP[0].PORT_PINCFG[PIN_PA24H_USB_DM] |= PORT_PINCFG_PMUXEN_Msk; + PORT_REGS->GROUP[0].PORT_PMUX[PIN_PA24H_USB_DM / 2] = + (PORT_REGS->GROUP[0].PORT_PMUX[PIN_PA24H_USB_DM / 2] & + ~(0xFu << (4u * (PIN_PA24H_USB_DM & 0x01u)))) | + (MUX_PA24H_USB_DM << (4u * (PIN_PA24H_USB_DM & 0x01u))); + PORT_REGS->GROUP[0].PORT_PINCFG[PIN_PA25H_USB_DP] |= PORT_PINCFG_PMUXEN_Msk; + PORT_REGS->GROUP[0].PORT_PMUX[PIN_PA25H_USB_DP / 2] = + (PORT_REGS->GROUP[0].PORT_PMUX[PIN_PA25H_USB_DP / 2] & + ~(0xFu << (4u * (PIN_PA25H_USB_DP & 0x01u)))) | + (MUX_PA25H_USB_DP << (4u * (PIN_PA25H_USB_DP & 0x01u))); + + GCLK_REGS->GCLK_PCHCTRL[USB_GCLK_ID] = GCLK_PCHCTRL_GEN_GCLK1 | + GCLK_PCHCTRL_CHEN_Msk; #else PM->APBBMASK.reg |= PM_APBBMASK_USB; @@ -377,7 +395,7 @@ void USBDeviceClass::init() usbd.setFullSpeed(); // Configure interrupts -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) /* Attach to the USB host */ NVIC_SetPriority(USB_0_IRQn, 0UL); NVIC_SetPriority(USB_1_IRQn, 0UL); @@ -387,6 +405,15 @@ void USBDeviceClass::init() NVIC_EnableIRQ(USB_1_IRQn); NVIC_EnableIRQ(USB_2_IRQn); NVIC_EnableIRQ(USB_3_IRQn); +#elif defined(ARDUINO_SAME53_E54) + NVIC_SetPriority(USB_OTHER_IRQn, 0UL); + NVIC_SetPriority(USB_SOF_HSOF_IRQn, 0UL); + NVIC_SetPriority(USB_TRCPT0_IRQn, 0UL); + NVIC_SetPriority(USB_TRCPT1_IRQn, 0UL); + NVIC_EnableIRQ(USB_OTHER_IRQn); + NVIC_EnableIRQ(USB_SOF_HSOF_IRQn); + NVIC_EnableIRQ(USB_TRCPT0_IRQn); + NVIC_EnableIRQ(USB_TRCPT1_IRQn); #else NVIC_SetPriority((IRQn_Type) USB_IRQn, 0UL); NVIC_EnableIRQ((IRQn_Type) USB_IRQn); @@ -422,9 +449,7 @@ void USBDeviceClass::setAddress(uint32_t addr) // Wait for transfer to complete while (!usbd.epBank1IsTransferComplete(0)) {} - // Set USB address to addr - USB->DEVICE.DADD.bit.DADD = addr; // Address - USB->DEVICE.DADD.bit.ADDEN = 1; // Enable + usbd.setAddress(addr); } bool USBDeviceClass::detach() @@ -568,19 +593,15 @@ void USBDeviceClass::flush(uint32_t ep) void USBDeviceClass::stall(uint32_t ep) { - // TODO: test - // TODO: use .bit. notation - - // Stall endpoint - USB->DEVICE.DeviceEndpoint[ep].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ(2); + usbd.epBank1SetStallReq(ep); } bool USBDeviceClass::connected() { // Count frame numbers - uint8_t f = USB->DEVICE.FNUM.bit.FNUM; + uint16_t f = usbd.frameNumber(); //delay(3); - return f != USB->DEVICE.FNUM.bit.FNUM; + return f != usbd.frameNumber(); } diff --git a/cores/arduino/USB/samd21_host.c b/cores/arduino/USB/samd21_host.c index 7d59f9c45..2627c5bfe 100644 --- a/cores/arduino/USB/samd21_host.c +++ b/cores/arduino/USB/samd21_host.c @@ -17,6 +17,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "../sam.h" + #ifndef USE_TINYUSB #include @@ -27,7 +29,6 @@ #include "variant.h" #include "USB_host.h" #include "samd21_host.h" -#include "sam.h" #include "wiring_digital.h" #include "wiring_private.h" @@ -40,10 +41,198 @@ // Handle UOTGHS Host driver state static uhd_vbus_state_t uhd_state = UHD_STATE_NO_VBUS; +#if defined(ARDUINO_SAME53_E54) +__attribute__((__aligned__(4))) volatile usb_descriptor_host_registers_t usb_pipe_table[USB_EPT_NUM]; +#else __attribute__((__aligned__(4))) volatile UsbHostDescriptor usb_pipe_table[USB_EPT_NUM]; +#endif extern void (*gpf_isr)(void); +#if defined(ARDUINO_SAME53_E54) + +void UHD_Init(void) +{ + const uint32_t calibration = *(const uint32_t *)(SW0_ADDR + 4u); + uint32_t pad_transn = (calibration & FUSES_SW0_WORD_1_USB_TRANSN_Msk) >> FUSES_SW0_WORD_1_USB_TRANSN_Pos; + uint32_t pad_transp = (calibration & FUSES_SW0_WORD_1_USB_TRANSP_Msk) >> FUSES_SW0_WORD_1_USB_TRANSP_Pos; + uint32_t pad_trim = (calibration & FUSES_SW0_WORD_1_USB_TRIM_Msk) >> FUSES_SW0_WORD_1_USB_TRIM_Pos; + + USB_SetHandler(&UHD_Handler); + MCLK_REGS->MCLK_APBBMASK |= MCLK_APBBMASK_USB_Msk; + pinPeripheral(PIN_USB_DM, PIO_COM); + pinPeripheral(PIN_USB_DP, PIO_COM); + GCLK_REGS->GCLK_PCHCTRL[USB_GCLK_ID] = GCLK_PCHCTRL_GEN_GCLK1 | GCLK_PCHCTRL_CHEN_Msk; + + USB_REGS->HOST.USB_CTRLA = USB_CTRLA_SWRST_Msk; + while (USB_REGS->HOST.USB_SYNCBUSY & USB_SYNCBUSY_SWRST_Msk); + USB_REGS->HOST.USB_CTRLA = USB_CTRLA_MODE_Msk | USB_CTRLA_RUNSTDBY_Msk; + + if (pad_transn == 0x1f) pad_transn = 5; + if (pad_transp == 0x1f) pad_transp = 29; + if (pad_trim == 0x7) pad_trim = 3; + USB_REGS->HOST.USB_PADCAL = USB_PADCAL_TRANSN(pad_transn) | + USB_PADCAL_TRANSP(pad_transp) | + USB_PADCAL_TRIM(pad_trim); + USB_REGS->HOST.USB_DESCADD = (uint32_t)&usb_pipe_table[0]; + USB_REGS->HOST.USB_CTRLB &= ~USB_HOST_CTRLB_SPDCONF_Msk; + memset((void *)usb_pipe_table, 0, sizeof(usb_pipe_table)); + uhd_state = UHD_STATE_NO_VBUS; + USB_REGS->HOST.USB_CTRLA |= USB_CTRLA_ENABLE_Msk; + while (USB_REGS->HOST.USB_SYNCBUSY & USB_SYNCBUSY_ENABLE_Msk); + + pinMode(PIN_USB_HOST_ENABLE, OUTPUT); + digitalWrite(PIN_USB_HOST_ENABLE, HIGH); + USB_REGS->HOST.USB_INTENSET = USB_HOST_INTENSET_DCONN_Msk | + USB_HOST_INTENSET_WAKEUP_Msk | + USB_HOST_INTENSET_DDISC_Msk; + USB_REGS->HOST.USB_CTRLB |= USB_HOST_CTRLB_VBUSOK_Msk; + + NVIC_SetPriority(USB_OTHER_IRQn, 0UL); + NVIC_SetPriority(USB_SOF_HSOF_IRQn, 0UL); + NVIC_SetPriority(USB_TRCPT0_IRQn, 0UL); + NVIC_SetPriority(USB_TRCPT1_IRQn, 0UL); + NVIC_EnableIRQ(USB_OTHER_IRQn); + NVIC_EnableIRQ(USB_SOF_HSOF_IRQn); + NVIC_EnableIRQ(USB_TRCPT0_IRQn); + NVIC_EnableIRQ(USB_TRCPT1_IRQn); +} + +void UHD_Handler(void) +{ + if ((USB_REGS->HOST.USB_CTRLA & USB_CTRLA_MODE_Msk) == 0) return; + + uint16_t flags = USB_REGS->HOST.USB_INTFLAG; + if (flags & USB_HOST_INTFLAG_HSOF_Msk) { + USB_REGS->HOST.USB_INTFLAG = USB_HOST_INTFLAG_HSOF_Msk; + uhd_state = UHD_STATE_CONNECTED; + } else if (flags & USB_HOST_INTFLAG_RST_Msk) { + USB_REGS->HOST.USB_INTFLAG = USB_HOST_INTFLAG_RST_Msk; + uhd_state = UHD_STATE_DISCONNECTED; + } else if (flags & USB_HOST_INTFLAG_UPRSM_Msk) { + USB_REGS->HOST.USB_INTFLAG = USB_HOST_INTFLAG_UPRSM_Msk; + uhd_state = UHD_STATE_DISCONNECTED; + } else if (flags & USB_HOST_INTFLAG_DNRSM_Msk) { + USB_REGS->HOST.USB_INTFLAG = USB_HOST_INTFLAG_DNRSM_Msk; + uhd_state = UHD_STATE_DISCONNECTED; + } else if (flags & USB_HOST_INTFLAG_WAKEUP_Msk) { + USB_REGS->HOST.USB_INTFLAG = USB_HOST_INTFLAG_WAKEUP_Msk; + uhd_state = UHD_STATE_CONNECTED; + } else if (flags & USB_HOST_INTFLAG_RAMACER_Msk) { + USB_REGS->HOST.USB_INTFLAG = USB_HOST_INTFLAG_RAMACER_Msk; + uhd_state = UHD_STATE_DISCONNECTED; + } else if (flags & USB_HOST_INTFLAG_DCONN_Msk) { + USB_REGS->HOST.USB_INTFLAG = USB_HOST_INTFLAG_DCONN_Msk | USB_HOST_INTFLAG_DDISC_Msk; + USB_REGS->HOST.USB_INTENCLR = USB_HOST_INTENCLR_DCONN_Msk; + USB_REGS->HOST.USB_INTENSET = USB_HOST_INTENSET_DDISC_Msk; + uhd_state = UHD_STATE_CONNECTED; + } else if (flags & USB_HOST_INTFLAG_DDISC_Msk) { + USB_REGS->HOST.USB_INTFLAG = USB_HOST_INTFLAG_DDISC_Msk | USB_HOST_INTFLAG_DCONN_Msk; + USB_REGS->HOST.USB_INTENCLR = USB_HOST_INTENCLR_DDISC_Msk; + USB_REGS->HOST.USB_INTENSET = USB_HOST_INTENSET_DCONN_Msk; + uhd_state = UHD_STATE_DISCONNECTED; + } +} + +uhd_vbus_state_t UHD_GetVBUSState(void) +{ + return uhd_state; +} + +uint32_t UHD_Pipe0_Alloc(uint32_t ul_add, uint32_t ul_ep_size) +{ + (void)ul_add; + (void)ul_ep_size; + uint32_t size = (USB_REGS->HOST.USB_STATUS & USB_HOST_STATUS_SPEED_Msk) ? + USB_PCKSIZE_SIZE_8_BYTES : USB_PCKSIZE_SIZE_64_BYTES; + USB_REGS->HOST.HOST_PIPE[0].USB_PCFG = USB_HOST_PCFG_PTYPE(1); + usb_pipe_table[0].HOST_DESC_BANK[0].USB_CTRL_PIPE = USB_HOST_CTRL_PIPE_PEPNUM(0); + usb_pipe_table[0].HOST_DESC_BANK[0].USB_PCKSIZE = USB_HOST_PCKSIZE_SIZE(size); + return 0; +} + +uint32_t UHD_Pipe_Alloc(uint32_t ul_dev_addr, uint32_t ul_dev_ep, uint32_t ul_type, + uint32_t ul_dir, uint32_t ul_maxsize, uint32_t ul_interval, uint32_t ul_nb_bank) +{ + usb_host_pipe_registers_t *pipe = &USB_REGS->HOST.HOST_PIPE[ul_dev_ep]; + pipe->USB_PCFG = USB_HOST_PCFG_BK(ul_nb_bank) | ul_type | + USB_HOST_PCFG_PTOKEN((ul_dir & USB_EP_DIR_IN) ? 1 : 2); + pipe->USB_BINTERVAL = ul_interval; + if (ul_dir & USB_EP_DIR_IN) pipe->USB_PSTATUSSET = USB_HOST_PSTATUSSET_BK0RDY_Msk; + else pipe->USB_PSTATUSCLR = USB_HOST_PSTATUSCLR_BK0RDY_Msk; + + ul_maxsize = (USB_REGS->HOST.USB_STATUS & USB_HOST_STATUS_SPEED_Msk) ? + USB_PCKSIZE_SIZE_8_BYTES : USB_PCKSIZE_SIZE_64_BYTES; + memset((void *)&usb_pipe_table[ul_dev_ep], 0, sizeof(usb_pipe_table[ul_dev_ep])); + usb_pipe_table[ul_dev_ep].HOST_DESC_BANK[0].USB_CTRL_PIPE = + USB_HOST_CTRL_PIPE_PDADDR(ul_dev_addr) | USB_HOST_CTRL_PIPE_PEPNUM(ul_dev_ep); + usb_pipe_table[ul_dev_ep].HOST_DESC_BANK[0].USB_PCKSIZE = USB_HOST_PCKSIZE_SIZE(ul_maxsize); + return 1; +} + +void UHD_Pipe_CountZero(uint32_t ul_pipe) +{ + usb_pipe_table[ul_pipe].HOST_DESC_BANK[0].USB_PCKSIZE &= ~USB_HOST_PCKSIZE_BYTE_COUNT_Msk; +} + +void UHD_Pipe_Free(uint32_t ul_pipe) +{ + USB_REGS->HOST.HOST_PIPE[ul_pipe].USB_PSTATUSSET = USB_HOST_PSTATUSSET_PFREEZE_Msk; +} + +uint32_t UHD_Pipe_Read(uint32_t pipe_num, uint32_t buf_size, uint8_t *buf) +{ + usb_host_pipe_registers_t *pipe = &USB_REGS->HOST.HOST_PIPE[pipe_num]; + if ((pipe->USB_PCFG & USB_HOST_PCFG_PTYPE_Msk) == USB_HOST_PTYPE_DIS) return 0; + volatile usb_host_desc_bank_registers_t *bank = &usb_pipe_table[pipe_num].HOST_DESC_BANK[0]; + bank->USB_ADDR = (uint32_t)buf; + bank->USB_PCKSIZE = (bank->USB_PCKSIZE & ~(USB_HOST_PCKSIZE_BYTE_COUNT_Msk | + USB_HOST_PCKSIZE_MULTI_PACKET_SIZE_Msk)) | + USB_HOST_PCKSIZE_MULTI_PACKET_SIZE(buf_size); + pipe->USB_PCFG = (pipe->USB_PCFG & ~USB_HOST_PCFG_PTOKEN_Msk) | USB_HOST_PCFG_PTOKEN(1); + pipe->USB_PSTATUSCLR = USB_HOST_PSTATUSCLR_BK0RDY_Msk | USB_HOST_PSTATUSCLR_PFREEZE_Msk; + return buf_size; +} + +void UHD_Pipe_Write(uint32_t ul_pipe, uint32_t ul_size, uint8_t *buf) +{ + volatile usb_host_desc_bank_registers_t *bank = &usb_pipe_table[ul_pipe].HOST_DESC_BANK[0]; + bank->USB_ADDR = (uint32_t)buf; + bank->USB_PCKSIZE = (bank->USB_PCKSIZE & ~(USB_HOST_PCKSIZE_BYTE_COUNT_Msk | + USB_HOST_PCKSIZE_MULTI_PACKET_SIZE_Msk)) | + USB_HOST_PCKSIZE_BYTE_COUNT(ul_size); +} + +void UHD_Pipe_Send(uint32_t ul_pipe, uint32_t ul_token_type) +{ + usb_host_pipe_registers_t *pipe = &USB_REGS->HOST.HOST_PIPE[ul_pipe]; + pipe->USB_PCFG = (pipe->USB_PCFG & ~USB_HOST_PCFG_PTOKEN_Msk) | + USB_HOST_PCFG_PTOKEN(ul_token_type); + if (ul_token_type == USB_HOST_PCFG_PTOKEN_SETUP) { + pipe->USB_PINTFLAG = USB_HOST_PINTFLAG_TXSTP_Msk; + pipe->USB_PSTATUSSET = USB_HOST_PSTATUSSET_BK0RDY_Msk; + } else if (ul_token_type == USB_HOST_PCFG_PTOKEN_IN) { + pipe->USB_PSTATUSCLR = USB_HOST_PSTATUSCLR_BK0RDY_Msk; + } else { + pipe->USB_PINTFLAG = USB_HOST_PINTFLAG_TRCPT1_Msk; + pipe->USB_PSTATUSSET = USB_HOST_PSTATUSSET_BK0RDY_Msk; + } + pipe->USB_PSTATUSCLR = USB_HOST_PSTATUSCLR_PFREEZE_Msk; +} + +uint32_t UHD_Pipe_Is_Transfer_Complete(uint32_t ul_pipe, uint32_t ul_token_type) +{ + usb_host_pipe_registers_t *pipe = &USB_REGS->HOST.HOST_PIPE[ul_pipe]; + uint8_t completion = (ul_token_type == USB_HOST_PCFG_PTOKEN_SETUP) ? + USB_HOST_PINTFLAG_TXSTP_Msk : USB_HOST_PINTFLAG_TRCPT1_Msk; + if ((pipe->USB_PINTFLAG & completion) == 0) return 0; + pipe->USB_PINTFLAG = completion; + pipe->USB_PSTATUSSET = USB_HOST_PSTATUSSET_PFREEZE_Msk; + return 1; +} + +#else + // NVM Software Calibration Area Mapping // USB TRANSN calibration value. Should be written to the USB PADCAL register. @@ -68,7 +257,7 @@ void UHD_Init(void) USB_SetHandler(&UHD_Handler); /* Enable USB clock */ -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB; #else PM->APBBMASK.reg |= PM_APBBMASK_USB; @@ -89,7 +278,7 @@ void UHD_Init(void) * Put Generic Clock Generator 0 as source for Generic Clock Multiplexer 6 (USB reference) */ -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); #else GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(6) | // Generic Clock Multiplexer 6 @@ -116,7 +305,7 @@ void UHD_Init(void) /* Load Pad Calibration */ -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) pad_transn = (*((uint32_t *)(NVMCTRL_SW0) // Non-Volatile Memory Controller #else pad_transn = (*((uint32_t *)(NVMCTRL_OTP4) // Non-Volatile Memory Controller @@ -133,7 +322,7 @@ void UHD_Init(void) USB->HOST.PADCAL.bit.TRANSN = pad_transn; -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) pad_transp = (*((uint32_t *)(NVMCTRL_SW0) #else pad_transp = (*((uint32_t *)(NVMCTRL_OTP4) @@ -149,7 +338,7 @@ void UHD_Init(void) USB->HOST.PADCAL.bit.TRANSP = pad_transp; -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) pad_trim = (*((uint32_t *)(NVMCTRL_SW0) #else pad_trim = (*((uint32_t *)(NVMCTRL_OTP4) @@ -189,7 +378,7 @@ void UHD_Init(void) USB->HOST.CTRLB.bit.VBUSOK = 1; // Configure interrupts -#if defined(__SAMD51__) +#if defined(ARDUINO_SAMD51_E51) NVIC_SetPriority((IRQn_Type)USB_0_IRQn, 0UL); NVIC_SetPriority((IRQn_Type)USB_1_IRQn, 0UL); NVIC_SetPriority((IRQn_Type)USB_2_IRQn, 0UL); @@ -545,6 +734,8 @@ uint32_t UHD_Pipe_Is_Transfer_Complete(uint32_t ul_pipe, uint32_t ul_token_type) return 0; } +#endif + @@ -555,4 +746,4 @@ uint32_t UHD_Pipe_Is_Transfer_Complete(uint32_t ul_pipe, uint32_t ul_token_type) #endif // HOST_DEFINED -#endif // USE_TINYUSB +#endif // !USE_TINYUSB diff --git a/cores/arduino/USB/samd21_host.h b/cores/arduino/USB/samd21_host.h index d2d9c9e09..1264eae3e 100644 --- a/cores/arduino/USB/samd21_host.h +++ b/cores/arduino/USB/samd21_host.h @@ -23,7 +23,11 @@ extern "C" { #endif +#if defined(ARDUINO_SAME53_E54) +extern __attribute__((__aligned__(4))) volatile usb_descriptor_host_registers_t usb_pipe_table[USB_EPT_NUM]; +#else extern __attribute__((__aligned__(4))) volatile UsbHostDescriptor usb_pipe_table[USB_EPT_NUM]; +#endif #define USB_EP_DIR_IN 0x80 // USB_SETUP_DEVICE_TO_HOST #define USB_EP_DIR_OUT 0x00 // USB_SETUP_HOST_TO_DEVICE @@ -41,9 +45,15 @@ extern __attribute__((__aligned__(4))) volatile UsbHostDescriptor usb_pipe_table #define USB_HOST_PCFG_PTOKEN_IN USB_HOST_PCFG_PTOKEN(0x1) #define USB_HOST_PCFG_PTOKEN_OUT USB_HOST_PCFG_PTOKEN(0x2) +#if defined(ARDUINO_SAME53_E54) +#define USB_ERRORFLOW USB_HOST_STATUS_BK_ERRORFLOW_Msk +#define USB_ERRORTIMEOUT USB_HOST_STATUS_PIPE_TOUTER_Msk +#define USB_ERROR_DATATOGGLE USB_HOST_STATUS_PIPE_DTGLER_Msk +#else #define USB_ERRORFLOW USB_HOST_STATUS_BK_ERRORFLOW #define USB_ERRORTIMEOUT USB_HOST_STATUS_PIPE_TOUTER #define USB_ERROR_DATATOGGLE USB_HOST_STATUS_PIPE_DTGLER +#endif #define USB_PCKSIZE_SIZE_8_BYTES 0 #define USB_PCKSIZE_SIZE_16_BYTES 1 @@ -55,6 +65,9 @@ extern __attribute__((__aligned__(4))) volatile UsbHostDescriptor usb_pipe_table #define USB_PCKSIZE_SIZE_1023_BYTES_FS 7 #define USB_PCKSIZE_SIZE_1024_BYTES_HS 7 +#if defined(ARDUINO_SAME53_E54) +// SAME53/E54 host operations use the native register structures directly in samd21_host.c. +#else #define USB_HOST_DTGL(p) (USB->HOST.HostPipe[p].PSTATUS.reg & USB_HOST_PSTATUS_DTGL)>>USB_HOST_PSTATUS_DTGL_Pos // USB host connection/disconnection monitoring @@ -121,6 +134,7 @@ extern __attribute__((__aligned__(4))) volatile UsbHostDescriptor usb_pipe_table // Force full speed mode #define uhd_force_full_speed() USB->HOST.CTRLB.reg &= ~USB_HOST_CTRLB_SPDCONF_Msk +#endif #ifdef __cplusplus } From 58f6e68a326de33741f99128de4cc3da85f44d98 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sat, 18 Jul 2026 17:49:46 -0400 Subject: [PATCH 51/79] fix(pendsv): coalesce TinyUSB deferred service --- cores/arduino/PendSV.cpp | 45 ++++++++++++++++++++++++++++++++++++++++ cores/arduino/PendSV.h | 2 ++ 2 files changed, 47 insertions(+) diff --git a/cores/arduino/PendSV.cpp b/cores/arduino/PendSV.cpp index bb5f32c7b..743fa866e 100644 --- a/cores/arduino/PendSV.cpp +++ b/cores/arduino/PendSV.cpp @@ -3,14 +3,46 @@ #include #include +#if defined(USE_TINYUSB) +extern "C" void TinyUSB_Device_Task(void); +#endif + namespace { PendSV pendSv; + +#if defined(USE_TINYUSB) +void tinyUsbDeviceTaskService(uint8_t serviceId, void *context) { + (void)serviceId; + (void)context; + // One invocation drains TinyUSB's queued controller events under OPT_OS_NONE. + TinyUSB_Device_Task(); +} +#endif } PendSV &PendSV::instance() { return pendSv; } +#if defined(USE_TINYUSB) +extern "C" void tud_event_hook_cb(uint8_t rhport, uint32_t eventId, bool inIsr) { + (void)rhport; + (void)eventId; + (void)inIsr; + // TinyUSB owns the event queue. PendSV is only its deferred wake signal, so + // multiple controller events before dispatch require only one service call. + PendSV::instance().setPendingOnce(PendSVChannels::Usb); +} +#endif + +bool PendSV::initializeCoreServices() { +#if defined(USE_TINYUSB) + return instance().registerService(PendSVChannels::Usb, tinyUsbDeviceTaskService, nullptr); +#else + return true; +#endif +} + uint32_t PendSV::enterCritical() { const uint32_t primask = __get_PRIMASK(); __disable_irq(); @@ -62,6 +94,19 @@ void PendSV::setPending(uint8_t serviceId) { SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; } +void PendSV::setPendingOnce(uint8_t serviceId) { + if (serviceId >= kMaxServices) + return; + + const uint32_t primask = enterCritical(); + pendingCount_[serviceId] = 1; + pendingMask_ |= (1u << serviceId); + exitCritical(primask); + + __DMB(); + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; +} + void PendSV::dispatchPending() { // Bound one PendSV entry so high-rate producers do not monopolize return to // thread mode. Remaining work re-pends PendSV below. diff --git a/cores/arduino/PendSV.h b/cores/arduino/PendSV.h index 6200c8de1..68090a252 100644 --- a/cores/arduino/PendSV.h +++ b/cores/arduino/PendSV.h @@ -104,6 +104,7 @@ class PendSV { "PendSV channel allocation exceeds dispatcher capacity"); static PendSV &instance(); + static bool initializeCoreServices(); bool registerService(uint8_t serviceId, ServiceFn fn, void *context = nullptr); // Cancels queued work that has not started dispatching. If a callback was @@ -111,6 +112,7 @@ class PendSV { void clearService(uint8_t serviceId); void dispatchPending(); void setPending(uint8_t serviceId); + void setPendingOnce(uint8_t serviceId); private: static uint32_t enterCritical(); From 8ea86e579e0b4ea63c84fd128d369fa92d311c88 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sun, 19 Jul 2026 08:16:07 -0400 Subject: [PATCH 52/79] fix(core): expand SAME5x crystal DFLL support --- cores/arduino/startup.c | 51 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/cores/arduino/startup.c b/cores/arduino/startup.c index e0df923e5..c91e3c406 100644 --- a/cores/arduino/startup.c +++ b/cores/arduino/startup.c @@ -121,14 +121,32 @@ void SystemInit( void ) /* Wait for synchronization */ } - /* DFLL Configuration in Open Loop mode */ + /* DFLL Configuration */ OSCCTRL->DFLLCTRLA.reg = 0; - //GCLK->PCHCTRL[OSCCTRL_GCLK_ID_DFLL48].reg = (1 << GCLK_PCHCTRL_CHEN_Pos) | GCLK_PCHCTRL_GEN(GCLK_PCHCTRL_GEN_GCLK3_Val); + +#ifndef CRYSTALLESS + /* + * D5x/E5x USB clock: XOSC32K -> GCLK3 -> DFLL48M closed loop. + * 32768 Hz * 1465 = 48.00512 MHz (+106.7 ppm), within the USB + * full-speed device requirement of 48 MHz +/-0.25%. + */ + GCLK->PCHCTRL[OSCCTRL_GCLK_ID_DFLL48].reg = + (1 << GCLK_PCHCTRL_CHEN_Pos) | + GCLK_PCHCTRL_GEN(GCLK_PCHCTRL_GEN_GCLK3_Val); + while ((GCLK->PCHCTRL[OSCCTRL_GCLK_ID_DFLL48].reg & + GCLK_PCHCTRL_CHEN) == 0) { + /* Wait for the DFLL reference channel to become active. */ + } +#endif // !CRYSTALLESS OSCCTRL->DFLLMUL.reg = OSCCTRL_DFLLMUL_CSTEP( 0x1 ) | OSCCTRL_DFLLMUL_FSTEP( 0x1 ) | +#ifndef CRYSTALLESS + OSCCTRL_DFLLMUL_MUL( 1465 ); +#else OSCCTRL_DFLLMUL_MUL( 0 ); +#endif // !CRYSTALLESS while ( OSCCTRL->DFLLSYNC.reg & OSCCTRL_DFLLSYNC_DFLLMUL ) { @@ -150,8 +168,13 @@ void SystemInit( void ) OSCCTRL->DFLLVAL.reg = OSCCTRL->DFLLVAL.reg; while( OSCCTRL->DFLLSYNC.bit.DFLLVAL ); +#ifndef CRYSTALLESS + OSCCTRL->DFLLCTRLB.reg = OSCCTRL_DFLLCTRLB_WAITLOCK | + OSCCTRL_DFLLCTRLB_CCDIS | OSCCTRL_DFLLCTRLB_MODE; +#else OSCCTRL->DFLLCTRLB.reg = OSCCTRL_DFLLCTRLB_WAITLOCK | - OSCCTRL_DFLLCTRLB_CCDIS | OSCCTRL_DFLLCTRLB_USBCRM ; + OSCCTRL_DFLLCTRLB_CCDIS | OSCCTRL_DFLLCTRLB_USBCRM; +#endif // !CRYSTALLESS while ( !OSCCTRL->STATUS.bit.DFLLRDY ) { @@ -344,9 +367,25 @@ void SystemInit( void ) while (GCLK_REGS->GCLK_SYNCBUSY & GCLK_SYNCBUSY_GENCTRL_Msk); OSCCTRL_REGS->OSCCTRL_DFLLCTRLA = 0; +#ifndef CRYSTALLESS + /* + * D5x/E5x USB clock: XOSC32K -> GCLK3 -> DFLL48M closed loop. + * 32768 Hz * 1465 = 48.00512 MHz (+106.7 ppm), within the USB + * full-speed device requirement of 48 MHz +/-0.25%. + */ + GCLK_REGS->GCLK_PCHCTRL[OSCCTRL_GCLK_ID_DFLL48] = + GCLK_PCHCTRL_GEN_GCLK3 | GCLK_PCHCTRL_CHEN_Msk; + while ((GCLK_REGS->GCLK_PCHCTRL[OSCCTRL_GCLK_ID_DFLL48] & + GCLK_PCHCTRL_CHEN_Msk) == 0); +#endif // !CRYSTALLESS + OSCCTRL_REGS->OSCCTRL_DFLLMUL = OSCCTRL_DFLLMUL_CSTEP(1) | OSCCTRL_DFLLMUL_FSTEP(1) | +#ifndef CRYSTALLESS + OSCCTRL_DFLLMUL_MUL(1465); +#else OSCCTRL_DFLLMUL_MUL(0); +#endif // !CRYSTALLESS while (OSCCTRL_REGS->OSCCTRL_DFLLSYNC & OSCCTRL_DFLLSYNC_DFLLMUL_Msk); OSCCTRL_REGS->OSCCTRL_DFLLCTRLB = 0; @@ -358,9 +397,15 @@ void SystemInit( void ) OSCCTRL_REGS->OSCCTRL_DFLLVAL = OSCCTRL_REGS->OSCCTRL_DFLLVAL; while (OSCCTRL_REGS->OSCCTRL_DFLLSYNC & OSCCTRL_DFLLSYNC_DFLLVAL_Msk); +#ifndef CRYSTALLESS + OSCCTRL_REGS->OSCCTRL_DFLLCTRLB = OSCCTRL_DFLLCTRLB_WAITLOCK_Msk | + OSCCTRL_DFLLCTRLB_CCDIS_Msk | + OSCCTRL_DFLLCTRLB_MODE_Msk; +#else OSCCTRL_REGS->OSCCTRL_DFLLCTRLB = OSCCTRL_DFLLCTRLB_WAITLOCK_Msk | OSCCTRL_DFLLCTRLB_CCDIS_Msk | OSCCTRL_DFLLCTRLB_USBCRM_Msk; +#endif // !CRYSTALLESS while ((OSCCTRL_REGS->OSCCTRL_STATUS & OSCCTRL_STATUS_DFLLRDY_Msk) == 0); GCLK_REGS->GCLK_GENCTRL[GENERIC_CLOCK_GENERATOR_1M] = From 73e669d84cd2937e9610bed719c54a91ff2c2044 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 24 Jul 2026 15:04:54 -0400 Subject: [PATCH 53/79] fix(core): use CMSIS SAME5x family headers --- cores/arduino/Arduino.h | 4 ++-- cores/arduino/WVariant.h | 16 ++++++++-------- cores/arduino/sam.h | 33 --------------------------------- cores/arduino/wiring_private.h | 2 +- 4 files changed, 11 insertions(+), 44 deletions(-) delete mode 100644 cores/arduino/sam.h diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index ebe2fe4a8..9a67e1f46 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -45,7 +45,7 @@ extern "C"{ #endif // __cplusplus // Include Atmel headers -#include "sam.h" +#include #include "wiring_constants.h" extern uint32_t SystemCoreClock; @@ -158,7 +158,7 @@ void loop( void ) ; #include "USB/USBDesc.h" #include "USB/USBCore.h" #include "USB/USBAPI.h" - #if !defined(ARDUINO_SAME53_E54) + #if !defined(__SAME53__) && !defined(__SAME54__) #include "USB/USB_host.h" #endif #endif diff --git a/cores/arduino/WVariant.h b/cores/arduino/WVariant.h index 47155957e..6ad36040d 100644 --- a/cores/arduino/WVariant.h +++ b/cores/arduino/WVariant.h @@ -20,15 +20,15 @@ #pragma once #include -#include "sam.h" +#include #include -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) enum { ARDUINO_TCC_INSTANCE_COUNT = TCC_INST_NUM, ARDUINO_TC_INSTANCE_COUNT = TC_INST_NUM, }; -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) enum { ARDUINO_TCC_INSTANCE_COUNT = 5, ARDUINO_TC_INSTANCE_COUNT = 8, @@ -56,7 +56,7 @@ typedef enum _EAnalogChannel ADC_Channel5=5, ADC_Channel6=6, ADC_Channel7=7, -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) ADC_Channel8=8, ADC_Channel9=9, #else @@ -67,7 +67,7 @@ typedef enum _EAnalogChannel #endif ADC_Channel10=10, ADC_Channel11=11, -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) ADC_Channel12=12, ADC_Channel13=13, ADC_Channel14=14, @@ -90,7 +90,7 @@ typedef enum _EAnalogChannel ADC_Channel_PTAT=0x1C, } EAnalogChannel ; -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) #if defined(__SAMD51G19A__) || defined(__SAME51G19A__) @@ -336,7 +336,7 @@ typedef enum _EPioType PIO_SERCOM_ALT, /* The pin is controlled by the associated signal of peripheral D. */ PIO_TIMER, /* The pin is controlled by the associated signal of peripheral E. */ PIO_TIMER_ALT, /* The pin is controlled by the associated signal of peripheral F. */ -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) PIO_TCC_PDEC, /* The pin is controlled by the associated signal of peripheral G. */ PIO_COM, /* The pin is controlled by the associated signal of peripheral H. */ PIO_SDHC, /* The pin is controlled by the associated signal of peripheral I. */ @@ -370,7 +370,7 @@ typedef enum _EPioType #define PIN_ATTR_EXTINT (1UL<<6) #define PIN_ATTR_ANALOG_ALT (1UL<<7) -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) // these correspond to the mux table #define PIN_ATTR_PWM_E (1UL<<3) #define PIN_ATTR_PWM_F (1UL<<8) diff --git a/cores/arduino/sam.h b/cores/arduino/sam.h deleted file mode 100644 index e63661196..000000000 --- a/cores/arduino/sam.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -// The board's build configuration selects the exact CMSIS device. Keep that -// selection authoritative, then expose only the family distinctions needed by -// the Arduino core. -#if defined(__SAME53J18A__) || defined(__ATSAME53J18A__) -#include_next -#elif defined(__SAME53J19A__) || defined(__ATSAME53J19A__) -#include_next -#elif defined(__SAME53J20A__) || defined(__ATSAME53J20A__) -#include_next -#elif defined(__SAME53N19A__) || defined(__ATSAME53N19A__) -#include_next -#elif defined(__SAME53N20A__) || defined(__ATSAME53N20A__) -#include_next -#elif defined(__SAME54N19A__) || defined(__ATSAME54N19A__) -#include_next -#elif defined(__SAME54N20A__) || defined(__ATSAME54N20A__) -#include_next -#elif defined(__SAME54P19A__) || defined(__ATSAME54P19A__) -#include_next -#elif defined(__SAME54P20A__) || defined(__ATSAME54P20A__) -#include_next -#else -#include_next -#endif // SAME53/E54 device selection - -#if defined(__SAMD51__) || defined(__SAME51__) -#define ARDUINO_SAMD51_E51 -#elif defined(__SAME53__) || defined(__SAME54__) -#define ARDUINO_SAME53_E54 -typedef dmac_descriptor_registers_t DmacDescriptor; -#endif diff --git a/cores/arduino/wiring_private.h b/cores/arduino/wiring_private.h index ce64e2def..639de7b89 100644 --- a/cores/arduino/wiring_private.h +++ b/cores/arduino/wiring_private.h @@ -27,7 +27,7 @@ extern "C" { #endif // Includes Atmel CMSIS -#include "sam.h" +#include #include "wiring_constants.h" From 409cdd01ca6c625df71e17aedb7c80a763fd792b Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 24 Jul 2026 15:05:25 -0400 Subject: [PATCH 54/79] fix(core): complete SAME5x pin and interrupt guards --- cores/arduino/SERCOM_PinMux.h | 2 +- cores/arduino/SercomPinMux.inc | 12 +++++----- cores/arduino/WInterrupts.c | 42 +++++++++++++++++----------------- cores/arduino/wiring_digital.c | 2 +- cores/arduino/wiring_private.c | 6 ++--- 5 files changed, 32 insertions(+), 32 deletions(-) diff --git a/cores/arduino/SERCOM_PinMux.h b/cores/arduino/SERCOM_PinMux.h index 7802e218a..8105757e9 100644 --- a/cores/arduino/SERCOM_PinMux.h +++ b/cores/arduino/SERCOM_PinMux.h @@ -12,7 +12,7 @@ * - libraries/Wire/Wire.cpp */ -#include "sam.h" +#include #include /** @brief Shared helpers for validating SERCOM pin routing. */ diff --git a/cores/arduino/SercomPinMux.inc b/cores/arduino/SercomPinMux.inc index 873501af6..98a7daa24 100644 --- a/cores/arduino/SercomPinMux.inc +++ b/cores/arduino/SercomPinMux.inc @@ -57,21 +57,21 @@ #define SAME54_120_SERIES (defined(__ATSAME54P19A__) || defined(__ATSAME54P20A__) || defined(__SAME54P19A__) || defined(__SAME54P20A__)) #endif // SAME54_120_SERIES -#if !defined(ARDUINO_SAMD51_E51) && !defined(ARDUINO_SAME53_E54) +#if !defined(__SAMD51__) && !defined(__SAME51__) && !defined(__SAME53__) && !defined(__SAME54__) #if !(SAMD21E_SERIES || SAMD21G_SERIES || SAMD21J_SERIES || SAMDA1_SERIES) #define SAMD21E_SERIES 1 #endif // !(SAMD21E_SERIES || SAMD21G_SERIES || SAMD21J_SERIES || SAMDA1_SERIES) -#endif // !ARDUINO_SAMD51_E51 && !ARDUINO_SAME53_E54 -#if defined(ARDUINO_SAMD51_E51) +#endif // !__SAMD51__ / __SAME51__ && !__SAME53__ / __SAME54__ +#if defined(__SAMD51__) || defined(__SAME51__) #if !(SAMD51_SERIES || SAMD51_120_SERIES || SAME51_SERIES) #define SAMD51_SERIES 1 #endif // !(SAMD51_SERIES || SAMD51_120_SERIES || SAME51_SERIES) -#endif // ARDUINO_SAMD51_E51 -#if defined(ARDUINO_SAME53_E54) +#endif // __SAMD51__ / __SAME51__ +#if defined(__SAME53__) || defined(__SAME54__) #if !(SAME53_SERIES || SAME54_SERIES || SAME54_120_SERIES) #define SAME54_SERIES 1 #endif // !(SAME53_SERIES || SAME54_SERIES || SAME54_120_SERIES) -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ #if defined(SERCOM_PINMUX_EMIT_I2C) #if SAMD21E_SERIES || SAMDA1_SERIES diff --git a/cores/arduino/WInterrupts.c b/cores/arduino/WInterrupts.c index 40fb96aed..500551a18 100644 --- a/cores/arduino/WInterrupts.c +++ b/cores/arduino/WInterrupts.c @@ -34,7 +34,7 @@ static void __initialize() memset(ISRcallback, 0, sizeof(ISRcallback)); nints = 0; -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) ///EIC MCLK is enabled by default for (uint32_t i = 0; i <= 15; i++) // EIC_0_IRQn = 12 ... EIC_15_IRQn = 27 { @@ -46,7 +46,7 @@ static void __initialize() } GCLK->PCHCTRL[EIC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK2_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) // EIC MCLK is enabled by default. for (uint32_t i = 0; i <= 15; i++) { @@ -76,10 +76,10 @@ static void __initialize() */ // Enable EIC -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) EIC->CTRLA.bit.ENABLE = 1; while (EIC->SYNCBUSY.bit.ENABLE == 1) { } -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) EIC_REGS->EIC_CTRLA |= EIC_CTRLA_ENABLE_Msk; while (EIC_REGS->EIC_SYNCBUSY & EIC_SYNCBUSY_ENABLE_Msk) { } #else @@ -111,7 +111,7 @@ void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode) } uint32_t inMask = (1UL << in); // Enable wakeup capability on pin in case being used during sleep - #if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) + #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) //I believe this is done automatically #else EIC->WAKEUP.reg |= (1 << in); @@ -124,7 +124,7 @@ void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode) { if (in == EXTERNAL_INT_NMI) { uint8_t sense; -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) EIC_REGS->EIC_NMIFLAG = EIC_NMIFLAG_NMI_Msk; #else EIC->NMIFLAG.bit.NMI = 1; // Clear flag @@ -154,7 +154,7 @@ void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode) return; } -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) EIC_REGS->EIC_NMICTRL = (EIC_REGS->EIC_NMICTRL & ~EIC_NMICTRL_NMISENSE_Msk) | EIC_NMICTRL_NMISENSE(sense); @@ -207,15 +207,15 @@ void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode) default: return; } - #if defined(ARDUINO_SAMD51_E51) + #if defined(__SAMD51__) || defined(__SAME51__) EIC->CTRLA.bit.ENABLE = 0; while (EIC->SYNCBUSY.bit.ENABLE == 1) { } - #elif defined(ARDUINO_SAME53_E54) + #elif defined(__SAME53__) || defined(__SAME54__) EIC_REGS->EIC_CTRLA &= ~EIC_CTRLA_ENABLE_Msk; while (EIC_REGS->EIC_SYNCBUSY & EIC_SYNCBUSY_ENABLE_Msk) { } #endif -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) EIC_REGS->EIC_CONFIG[config] = (EIC_REGS->EIC_CONFIG[config] & ~(EIC_CONFIG_SENSE0_Msk << pos)) | ((uint32_t)sense << pos); @@ -226,17 +226,17 @@ void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode) #endif } // Enable the interrupt -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) EIC_REGS->EIC_INTENSET = EIC_INTENSET_EXTINT(1 << in); #else EIC->INTENSET.reg = EIC_INTENSET_EXTINT(1 << in); #endif } - #if defined(ARDUINO_SAMD51_E51) + #if defined(__SAMD51__) || defined(__SAME51__) EIC->CTRLA.bit.ENABLE = 1; while (EIC->SYNCBUSY.bit.ENABLE == 1) { } - #elif defined(ARDUINO_SAME53_E54) + #elif defined(__SAME53__) || defined(__SAME54__) EIC_REGS->EIC_CTRLA |= EIC_CTRLA_ENABLE_Msk; while (EIC_REGS->EIC_SYNCBUSY & EIC_SYNCBUSY_ENABLE_Msk) { } #endif @@ -255,20 +255,20 @@ void detachInterrupt(uint32_t pin) if (in == NOT_AN_INTERRUPT) return; if(in == EXTERNAL_INT_NMI) { -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) EIC_REGS->EIC_NMICTRL &= ~EIC_NMICTRL_NMISENSE_Msk; #else EIC->NMICTRL.bit.NMISENSE = 0; // Turn off detection #endif } else { -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) EIC_REGS->EIC_INTENCLR = EIC_INTENCLR_EXTINT(1 << in); #else EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT(1 << in); #endif // Disable wakeup capability on pin during sleep -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) //I believe this is done automatically #else // Disable wakeup capability on pin during sleep @@ -296,7 +296,7 @@ void detachInterrupt(uint32_t pin) /* * External Interrupt Controller NVIC Interrupt Handler */ -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) void InterruptHandler(uint32_t unused_i) { (void)unused_i; @@ -306,7 +306,7 @@ void InterruptHandler(uint32_t unused_i) // Loop over all enabled interrupts in the list for (uint32_t i=0; iEIC_INTFLAG & ISRlist[i]) != 0) #else if ((EIC->INTFLAG.reg & ISRlist[i]) != 0) @@ -315,7 +315,7 @@ void InterruptHandler(uint32_t unused_i) // Call the callback function ISRcallback[i](); // Clear the interrupt - #if defined(ARDUINO_SAME53_E54) + #if defined(__SAME53__) || defined(__SAME54__) EIC_REGS->EIC_INTFLAG = ISRlist[i]; #else EIC->INTFLAG.reg = ISRlist[i]; @@ -324,7 +324,7 @@ void InterruptHandler(uint32_t unused_i) } } -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) void EIC_0_Handler(void) { InterruptHandler(EXTERNAL_INT_0); @@ -404,7 +404,7 @@ void EIC_15_Handler(void) { InterruptHandler(EXTERNAL_INT_15); } -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) void EIC_EXTINT_0_Handler(void) { InterruptHandler(EXTERNAL_INT_0); } void EIC_EXTINT_1_Handler(void) { InterruptHandler(EXTERNAL_INT_1); } void EIC_EXTINT_2_Handler(void) { InterruptHandler(EXTERNAL_INT_2); } diff --git a/cores/arduino/wiring_digital.c b/cores/arduino/wiring_digital.c index d030ae974..cad4df32d 100644 --- a/cores/arduino/wiring_digital.c +++ b/cores/arduino/wiring_digital.c @@ -22,7 +22,7 @@ extern "C" { #endif -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) void pinMode(uint32_t ulPin, uint32_t ulMode) { if (g_APinDescription[ulPin].ulPinType == PIO_NOT_A_PIN) return; diff --git a/cores/arduino/wiring_private.c b/cores/arduino/wiring_private.c index 18bb643c3..f9708fe11 100644 --- a/cores/arduino/wiring_private.c +++ b/cores/arduino/wiring_private.c @@ -68,7 +68,7 @@ int pinPeripheral( uint32_t ulPin, EPioType ulPeripheral ) case PIO_TIMER: case PIO_TIMER_ALT: case PIO_EXTINT: -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) case PIO_TCC_PDEC: case PIO_COM: case PIO_SDHC: @@ -99,7 +99,7 @@ int pinPeripheral( uint32_t ulPin, EPioType ulPeripheral ) PORT_WRCONFIG_PINMASK( g_APinDescription[ulPin].ulPin - 16 ) ; } #else -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) port_group_registers_t *group = &PORT_REGS->GROUP[g_APinDescription[ulPin].ulPort]; uint32_t pin = g_APinDescription[ulPin].ulPin; uint32_t pmux = group->PORT_PMUX[pin >> 1]; @@ -110,7 +110,7 @@ int pinPeripheral( uint32_t ulPin, EPioType ulPeripheral ) pmux = (pmux & PORT_PMUX_PMUXO_Msk) | PORT_PMUX_PMUXE(ulPeripheral); } group->PORT_PMUX[pin >> 1] = pmux; - group->PORT_PINCFG[pin] |= PORT_PINCFG_PMUXEN_Msk | PORT_PINCFG_DRVSTR_Msk; + group->PORT_PINCFG[pin] |= PORT_PINCFG_PMUXEN_Msk | PORT_PINCFG_INEN_Msk | PORT_PINCFG_DRVSTR_Msk; #else if ( g_APinDescription[ulPin].ulPin & 1 ) // is pin odd? { From e371884fc64bdf117ff038e51b924b5e1e30e275 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 24 Jul 2026 15:05:55 -0400 Subject: [PATCH 55/79] fix(core): complete SAME5x startup vectors --- cores/arduino/cortex_handlers.c | 110 +++++++++++++++++++------------- cores/arduino/startup.c | 10 +-- 2 files changed, 71 insertions(+), 49 deletions(-) diff --git a/cores/arduino/cortex_handlers.c b/cores/arduino/cortex_handlers.c index 0ca844134..2f3c670b8 100644 --- a/cores/arduino/cortex_handlers.c +++ b/cores/arduino/cortex_handlers.c @@ -35,7 +35,7 @@ void Dummy_Handler(void) for (;;) { } } -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) /* Cortex-M4 processor handlers */ void Reset_Handler ( void ); @@ -62,7 +62,7 @@ void SUPC_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Han void SUPC_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void WDT_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void RTC_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) void EIC_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void EIC_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void EIC_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); @@ -79,7 +79,7 @@ void EIC_12_Handler ( void ) __attribute__ ((weak, alias("Dummy_Han void EIC_13_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void EIC_14_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void EIC_15_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) void EIC_EXTINT_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void EIC_EXTINT_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void EIC_EXTINT_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); @@ -105,7 +105,11 @@ void DMAC_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Han void DMAC_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void DMAC_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void DMAC_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#if defined(__SAME53__) || defined(__SAME54__) +void DMAC_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#else void DMAC_4_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#endif void EVSYS_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void EVSYS_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void EVSYS_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); @@ -118,83 +122,83 @@ void RAMECC_Handler ( void ) __attribute__ ((weak, alias("Dummy_Han void SERCOM0_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM0_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM0_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) void SERCOM0_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) void SERCOM0_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); #else #endif void SERCOM1_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM1_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM1_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) void SERCOM1_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) void SERCOM1_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); #else #endif void SERCOM2_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM2_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM2_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) void SERCOM2_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) void SERCOM2_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); #else #endif void SERCOM3_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM3_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM3_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) void SERCOM3_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) void SERCOM3_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); #else #endif void SERCOM4_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM4_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM4_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) void SERCOM4_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) void SERCOM4_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); #else #endif void SERCOM5_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM5_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM5_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) void SERCOM5_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) void SERCOM5_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); #else #endif void SERCOM6_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM6_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM6_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) void SERCOM6_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) void SERCOM6_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); #else #endif void SERCOM7_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM7_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void SERCOM7_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) void SERCOM7_3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) void SERCOM7_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); #else #endif void CAN0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void CAN1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) void USB_0_Handler ( void ) __attribute__ ((weak)); void USB_1_Handler ( void ) __attribute__ ((weak)); void USB_2_Handler ( void ) __attribute__ ((weak)); void USB_3_Handler ( void ) __attribute__ ((weak)); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) void USB_OTHER_Handler ( void ) __attribute__ ((weak)); void USB_SOF_HSOF_Handler ( void ) __attribute__ ((weak)); void USB_TRCPT0_Handler ( void ) __attribute__ ((weak)); @@ -235,10 +239,17 @@ void TC7_Handler ( void ) __attribute__ ((weak, alias("Dummy_Han void PDEC_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void PDEC_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void PDEC_2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#if defined(__SAME53__) || defined(__SAME54__) +void ADC0_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void ADC0_RESRDY_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void ADC1_OTHER_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +void ADC1_RESRDY_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#else void ADC0_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void ADC0_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void ADC1_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void ADC1_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); +#endif void AC_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void DAC_0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); void DAC_1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler"))); @@ -300,7 +311,7 @@ __attribute__ ((section(".isr_vector"))) const DeviceVectors exception_table = (void*) SUPC_1_Handler, /* 9 Supply Controller IRQ 1 */ (void*) WDT_Handler, /* 10 Watchdog Timer */ (void*) RTC_Handler, /* 11 Real-Time Counter */ -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) (void*) EIC_0_Handler, /* 12 External Interrupt Controller IRQ 0 */ (void*) EIC_1_Handler, /* 13 External Interrupt Controller IRQ 1 */ (void*) EIC_2_Handler, /* 14 External Interrupt Controller IRQ 2 */ @@ -317,7 +328,7 @@ __attribute__ ((section(".isr_vector"))) const DeviceVectors exception_table = (void*) EIC_13_Handler, /* 25 External Interrupt Controller IRQ 13 */ (void*) EIC_14_Handler, /* 26 External Interrupt Controller IRQ 14 */ (void*) EIC_15_Handler, /* 27 External Interrupt Controller IRQ 15 */ -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) (void*) EIC_EXTINT_0_Handler, /* 12 External Interrupt Controller IRQ 0 */ (void*) EIC_EXTINT_1_Handler, /* 13 External Interrupt Controller IRQ 1 */ (void*) EIC_EXTINT_2_Handler, /* 14 External Interrupt Controller IRQ 2 */ @@ -343,7 +354,11 @@ __attribute__ ((section(".isr_vector"))) const DeviceVectors exception_table = (void*) DMAC_1_Handler, /* 32 Direct Memory Access Controller IRQ 1 */ (void*) DMAC_2_Handler, /* 33 Direct Memory Access Controller IRQ 2 */ (void*) DMAC_3_Handler, /* 34 Direct Memory Access Controller IRQ 3 */ +#if defined(__SAME53__) || defined(__SAME54__) + (void*) DMAC_OTHER_Handler, /* 35 Direct Memory Access Controller other IRQ */ +#else (void*) DMAC_4_Handler, /* 35 Direct Memory Access Controller IRQ 4 */ +#endif (void*) EVSYS_0_Handler, /* 36 Event System Interface IRQ 0 */ (void*) EVSYS_1_Handler, /* 37 Event System Interface IRQ 1 */ (void*) EVSYS_2_Handler, /* 38 Event System Interface IRQ 2 */ @@ -357,83 +372,83 @@ __attribute__ ((section(".isr_vector"))) const DeviceVectors exception_table = (void*) SERCOM0_0_Handler, /* 46 Serial Communication Interface 0 IRQ 0 */ (void*) SERCOM0_1_Handler, /* 47 Serial Communication Interface 0 IRQ 1 */ (void*) SERCOM0_2_Handler, /* 48 Serial Communication Interface 0 IRQ 2 */ -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) (void*) SERCOM0_3_Handler, /* 49 Serial Communication Interface 0 IRQ 3 */ -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) (void*) SERCOM0_OTHER_Handler, /* Serial Communication Interface 0 other IRQ */ #else #endif (void*) SERCOM1_0_Handler, /* 50 Serial Communication Interface 1 IRQ 0 */ (void*) SERCOM1_1_Handler, /* 51 Serial Communication Interface 1 IRQ 1 */ (void*) SERCOM1_2_Handler, /* 52 Serial Communication Interface 1 IRQ 2 */ -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) (void*) SERCOM1_3_Handler, /* 53 Serial Communication Interface 1 IRQ 3 */ -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) (void*) SERCOM1_OTHER_Handler, /* Serial Communication Interface 1 other IRQ */ #else #endif (void*) SERCOM2_0_Handler, /* 54 Serial Communication Interface 2 IRQ 0 */ (void*) SERCOM2_1_Handler, /* 55 Serial Communication Interface 2 IRQ 1 */ (void*) SERCOM2_2_Handler, /* 56 Serial Communication Interface 2 IRQ 2 */ -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) (void*) SERCOM2_3_Handler, /* 57 Serial Communication Interface 2 IRQ 3 */ -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) (void*) SERCOM2_OTHER_Handler, /* Serial Communication Interface 2 other IRQ */ #else #endif (void*) SERCOM3_0_Handler, /* 58 Serial Communication Interface 3 IRQ 0 */ (void*) SERCOM3_1_Handler, /* 59 Serial Communication Interface 3 IRQ 1 */ (void*) SERCOM3_2_Handler, /* 60 Serial Communication Interface 3 IRQ 2 */ -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) (void*) SERCOM3_3_Handler, /* 61 Serial Communication Interface 3 IRQ 3 */ -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) (void*) SERCOM3_OTHER_Handler, /* Serial Communication Interface 3 other IRQ */ #else #endif (void*) SERCOM4_0_Handler, /* 62 Serial Communication Interface 4 IRQ 0 */ (void*) SERCOM4_1_Handler, /* 63 Serial Communication Interface 4 IRQ 1 */ (void*) SERCOM4_2_Handler, /* 64 Serial Communication Interface 4 IRQ 2 */ -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) (void*) SERCOM4_3_Handler, /* 65 Serial Communication Interface 4 IRQ 3 */ -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) (void*) SERCOM4_OTHER_Handler, /* Serial Communication Interface 4 other IRQ */ #else #endif (void*) SERCOM5_0_Handler, /* 66 Serial Communication Interface 5 IRQ 0 */ (void*) SERCOM5_1_Handler, /* 67 Serial Communication Interface 5 IRQ 1 */ (void*) SERCOM5_2_Handler, /* 68 Serial Communication Interface 5 IRQ 2 */ -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) (void*) SERCOM5_3_Handler, /* 69 Serial Communication Interface 5 IRQ 3 */ -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) (void*) SERCOM5_OTHER_Handler, /* Serial Communication Interface 5 other IRQ */ #else #endif (void*) SERCOM6_0_Handler, /* 70 Serial Communication Interface 6 IRQ 0 */ (void*) SERCOM6_1_Handler, /* 71 Serial Communication Interface 6 IRQ 1 */ (void*) SERCOM6_2_Handler, /* 72 Serial Communication Interface 6 IRQ 2 */ -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) (void*) SERCOM6_3_Handler, /* 73 Serial Communication Interface 6 IRQ 3 */ -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) (void*) SERCOM6_OTHER_Handler, /* Serial Communication Interface 6 other IRQ */ #else #endif (void*) SERCOM7_0_Handler, /* 74 Serial Communication Interface 7 IRQ 0 */ (void*) SERCOM7_1_Handler, /* 75 Serial Communication Interface 7 IRQ 1 */ (void*) SERCOM7_2_Handler, /* 76 Serial Communication Interface 7 IRQ 2 */ -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) (void*) SERCOM7_3_Handler, /* 77 Serial Communication Interface 7 IRQ 3 */ -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) (void*) SERCOM7_OTHER_Handler, /* Serial Communication Interface 7 other IRQ */ #else #endif (void*) CAN0_Handler, /* 78 Control Area Network 0 (SAM E5x) */ (void*) CAN1_Handler, /* 79 Control Area Network 0 (SAM E5x) */ -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) (void*) USB_0_Handler, /* 80 Universal Serial Bus IRQ 0 */ (void*) USB_1_Handler, /* 81 Universal Serial Bus IRQ 1 */ (void*) USB_2_Handler, /* 82 Universal Serial Bus IRQ 2 */ (void*) USB_3_Handler, /* 83 Universal Serial Bus IRQ 3 */ -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) (void*) USB_OTHER_Handler, /* 80 Universal Serial Bus other IRQ */ (void*) USB_SOF_HSOF_Handler, /* 81 Universal Serial Bus SOF/HSOF IRQ */ (void*) USB_TRCPT0_Handler, /* 82 Universal Serial Bus transfer complete 0 IRQ */ @@ -474,10 +489,17 @@ __attribute__ ((section(".isr_vector"))) const DeviceVectors exception_table = (void*) PDEC_0_Handler, /* 115 Quadrature Decodeur IRQ 0 */ (void*) PDEC_1_Handler, /* 116 Quadrature Decodeur IRQ 1 */ (void*) PDEC_2_Handler, /* 117 Quadrature Decodeur IRQ 2 */ +#if defined(__SAME53__) || defined(__SAME54__) + (void*) ADC0_OTHER_Handler, /* 118 Analog Digital Converter 0 other IRQ */ + (void*) ADC0_RESRDY_Handler, /* 119 Analog Digital Converter 0 result-ready IRQ */ + (void*) ADC1_OTHER_Handler, /* 120 Analog Digital Converter 1 other IRQ */ + (void*) ADC1_RESRDY_Handler, /* 121 Analog Digital Converter 1 result-ready IRQ */ +#else (void*) ADC0_0_Handler, /* 118 Analog Digital Converter 0 IRQ 0 */ (void*) ADC0_1_Handler, /* 119 Analog Digital Converter 0 IRQ 1 */ (void*) ADC1_0_Handler, /* 120 Analog Digital Converter 1 IRQ 0 */ (void*) ADC1_1_Handler, /* 121 Analog Digital Converter 1 IRQ 1 */ +#endif (void*) AC_Handler, /* 122 Analog Comparators */ (void*) DAC_0_Handler, /* 123 Digital-to-Analog Converter IRQ 0 */ (void*) DAC_1_Handler, /* 124 Digital-to-Analog Converter IRQ 1 */ @@ -624,7 +646,7 @@ void Reset_Handler(void) } #if defined(__FPU_USED) && \ - (defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54)) + (defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__)) /* Enable FPU */ SCB->CPACR |= (0xFu << 20); __DSB(); @@ -657,12 +679,12 @@ static void dispatchUSBInterrupt(void) usb_isr(); } -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) void USB_0_Handler(void) { dispatchUSBInterrupt(); } void USB_1_Handler(void) { dispatchUSBInterrupt(); } void USB_2_Handler(void) { dispatchUSBInterrupt(); } void USB_3_Handler(void) { dispatchUSBInterrupt(); } -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) void USB_OTHER_Handler(void) { dispatchUSBInterrupt(); } void USB_SOF_HSOF_Handler(void) { dispatchUSBInterrupt(); } void USB_TRCPT0_Handler(void) { dispatchUSBInterrupt(); } diff --git a/cores/arduino/startup.c b/cores/arduino/startup.c index c91e3c406..a0f1caf88 100644 --- a/cores/arduino/startup.c +++ b/cores/arduino/startup.c @@ -17,7 +17,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "sam.h" +#include #include "variant.h" #include @@ -27,7 +27,7 @@ extern uint32_t SystemCoreClock; // Constants for Clock generators #define GENERIC_CLOCK_GENERATOR_MAIN (0u) -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) #define GENERIC_CLOCK_GENERATOR_XOSC32K (3u) #define GENERIC_CLOCK_GENERATOR_48M (1u) #define GENERIC_CLOCK_GENERATOR_48M_SYNC GCLK_SYNCBUSY_GENCTRL1 @@ -42,7 +42,7 @@ extern uint32_t SystemCoreClock; #define GENERIC_CLOCK_GENERATOR_1M (5u) //#define CRYSTALLESS -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define GENERIC_CLOCK_GENERATOR_XOSC32K (3u) #define GENERIC_CLOCK_GENERATOR_48M (1u) #define GENERIC_CLOCK_GENERATOR_48M_SYNC GCLK_SYNCBUSY_GENCTRL_Msk @@ -67,7 +67,7 @@ void SystemInit( void ) { //***************** SAMD51 ************************// -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) NVMCTRL->CTRLA.reg |= NVMCTRL_CTRLA_RWS(0); #ifndef CRYSTALLESS @@ -339,7 +339,7 @@ void SystemInit( void ) //*************** END SAMD51 *************************// -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) NVMCTRL_REGS->NVMCTRL_CTRLA |= NVMCTRL_CTRLA_RWS(0); #ifndef CRYSTALLESS From 2b6458a76847e0f03c6462cfe6bfd6f8d9d3eb22 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 24 Jul 2026 15:05:55 -0400 Subject: [PATCH 56/79] fix(core): complete SAME5x timing and reset guards --- cores/arduino/Reset.cpp | 8 ++++---- cores/arduino/Tone.cpp | 26 +++++++++++++------------- cores/arduino/pulse.c | 4 ++-- cores/arduino/wiring.c | 12 ++++++------ 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/cores/arduino/Reset.cpp b/cores/arduino/Reset.cpp index 159c4eeac..9f93ebc43 100644 --- a/cores/arduino/Reset.cpp +++ b/cores/arduino/Reset.cpp @@ -33,7 +33,7 @@ extern const uint32_t __text_start__; #else -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) #define APP_START 0x00004004 #else #define APP_START 0x00002004 @@ -42,9 +42,9 @@ extern const uint32_t __text_start__; #endif static inline bool nvmReady(void) { -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) return NVMCTRL->STATUS.reg & NVMCTRL_STATUS_READY; -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) return NVMCTRL_REGS->NVMCTRL_STATUS & NVMCTRL_STATUS_READY_Msk; #else return NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY; @@ -56,7 +56,7 @@ static void banzai() { // Disable all interrupts __disable_irq(); -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) //THESE MUST MATCH THE BOOTLOADER #define DOUBLE_TAP_MAGIC 0xf01669efUL #define BOOT_DOUBLE_TAP_ADDRESS (HSRAM_ADDR + HSRAM_SIZE - 4) diff --git a/cores/arduino/Tone.cpp b/cores/arduino/Tone.cpp index 6744edc7b..9e2278e51 100644 --- a/cores/arduino/Tone.cpp +++ b/cores/arduino/Tone.cpp @@ -30,14 +30,14 @@ volatile int64_t toggleCount; volatile bool toneIsActive = false; volatile bool firstTimeRunning = false; -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) #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); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define TONE_TC TC0_REGS #define TONE_TC_IRQn TC0_IRQn #define TONE_TC_GCLK_ID TC0_GCLK_ID @@ -55,7 +55,7 @@ volatile bool firstTimeRunning = false; #define TONE_TC_TOP 0xFFFF #define TONE_TC_CHANNEL 0 -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) static inline void resetTC(tc_registers_t *TCx) { TCx->COUNT16.TC_CTRLA &= ~TC_CTRLA_ENABLE_Msk; @@ -104,9 +104,9 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration) NVIC_SetPriority(TONE_TC_IRQn, 5); -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) GCLK->PCHCTRL[TONE_TC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) GCLK_REGS->GCLK_PCHCTRL[TONE_TC_GCLK_ID] = GCLK_PCHCTRL_GEN_GCLK0 | GCLK_PCHCTRL_CHEN_Msk; #else @@ -171,29 +171,29 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration) uint16_t tmpReg = 0; tmpReg |= TC_CTRLA_MODE_COUNT16; // Set Timer counter Mode to 16 bits -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) TONE_TC->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ; // Set TONE_TC mode as match frequency -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) TONE_TC->COUNT16.TC_WAVE = TC_WAVE_WAVEGEN_MFRQ; #else tmpReg |= TC_CTRLA_WAVEGEN_MFRQ; // Set TONE_TC mode as match frequency #endif tmpReg |= prescalerConfigBits; -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) TONE_TC->COUNT16.TC_CTRLA |= tmpReg; #else TONE_TC->COUNT16.CTRLA.reg |= tmpReg; #endif WAIT_TC16_REGS_SYNC(TONE_TC) -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) TONE_TC->COUNT16.TC_CC[TONE_TC_CHANNEL] = (uint16_t)ccValue; #else TONE_TC->COUNT16.CC[TONE_TC_CHANNEL].reg = (uint16_t) ccValue; #endif WAIT_TC16_REGS_SYNC(TONE_TC) -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) portToggleRegister = &PORT_REGS->GROUP[g_APinDescription[outputPin].ulPort].PORT_OUTTGL; portClearRegister = &PORT_REGS->GROUP[g_APinDescription[outputPin].ulPort].PORT_OUTCLR; #else @@ -203,7 +203,7 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration) portBitMask = (1ul << g_APinDescription[outputPin].ulPin); // Enable the TONE_TC interrupt request -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) TONE_TC->COUNT16.TC_INTENSET = TC_INTENSET_MC0_Msk; #else TONE_TC->COUNT16.INTENSET.bit.MC0 = 1; @@ -218,7 +218,7 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration) } // Enable TONE_TC -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) TONE_TC->COUNT16.TC_CTRLA |= TC_CTRLA_ENABLE_Msk; #else TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; @@ -263,7 +263,7 @@ void Tone_Handler (void) --toggleCount; // Clear the interrupt -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) TONE_TC->COUNT16.TC_INTFLAG = TC_INTFLAG_MC0_Msk; #else TONE_TC->COUNT16.INTFLAG.bit.MC0 = 1; diff --git a/cores/arduino/pulse.c b/cores/arduino/pulse.c index 20a6c687d..194ae3028 100644 --- a/cores/arduino/pulse.c +++ b/cores/arduino/pulse.c @@ -34,14 +34,14 @@ uint32_t pulseIn(uint32_t pin, uint32_t state, uint32_t timeout) uint32_t bit = 1 << p.ulPin; uint32_t stateMask = state ? bit : 0; -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) /* * The SAMD51 is fast enough to use really obvious code (similar to * what was used to produce pulse_asm.S, but using micros() for timing. * No assembly required, no conversion of loop counts to times (which is * worrisome in the presence of cache.) */ -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) const volatile uint32_t *port = &PORT_REGS->GROUP[p.ulPort].PORT_IN; #else const volatile uint32_t *port = &PORT->Group[p.ulPort].IN.reg; diff --git a/cores/arduino/wiring.c b/cores/arduino/wiring.c index 0f1d533c0..5635c81d3 100644 --- a/cores/arduino/wiring.c +++ b/cores/arduino/wiring.c @@ -24,7 +24,7 @@ extern "C" { #endif -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) uint32_t SystemCoreClock=F_CPU; #else /* @@ -77,7 +77,7 @@ void init( void ) // // Clock EIC for I/O interrupts // PM->APBAMASK.reg |= PM_APBAMASK_EIC ; -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) MCLK->APBAMASK.reg |= MCLK_APBAMASK_SERCOM0 | MCLK_APBAMASK_SERCOM1 | MCLK_APBAMASK_TC0 | MCLK_APBAMASK_TC1; MCLK->APBBMASK.reg |= MCLK_APBBMASK_SERCOM2 | MCLK_APBBMASK_SERCOM3 | MCLK_APBBMASK_TCC0 | MCLK_APBBMASK_TCC1 | MCLK_APBBMASK_TC3 | MCLK_APBBMASK_TC2; @@ -87,7 +87,7 @@ void init( void ) MCLK->APBDMASK.reg |= MCLK_APBDMASK_DAC | MCLK_APBDMASK_SERCOM4 | MCLK_APBDMASK_SERCOM5 | MCLK_APBDMASK_ADC0 | MCLK_APBDMASK_ADC1 | MCLK_APBDMASK_TCC4 | MCLK_APBDMASK_TC6 | MCLK_APBDMASK_TC7 | MCLK_APBDMASK_SERCOM6 | MCLK_APBDMASK_SERCOM7; -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) MCLK_REGS->MCLK_APBAMASK |= MCLK_APBAMASK_SERCOM0_Msk | MCLK_APBAMASK_SERCOM1_Msk | MCLK_APBAMASK_TC0_Msk | MCLK_APBAMASK_TC1_Msk; MCLK_REGS->MCLK_APBBMASK |= MCLK_APBBMASK_SERCOM2_Msk | MCLK_APBBMASK_SERCOM3_Msk | @@ -128,7 +128,7 @@ void init( void ) // Initialize Analog Controller // Setting clock -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) //set to 1/(1/(48000000/32) * 6) = 250000 SPS GCLK->PCHCTRL[ADC0_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); //use clock generator 1 (48Mhz) GCLK->PCHCTRL[ADC1_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); //use clock generator 1 (48Mhz) @@ -170,7 +170,7 @@ void init( void ) DAC->DACCTRL[0].bit.REFRESH = 2; DAC->DACCTRL[1].bit.REFRESH = 2; -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) GCLK_REGS->GCLK_PCHCTRL[ADC0_GCLK_ID] = GCLK_PCHCTRL_GEN_GCLK1 | GCLK_PCHCTRL_CHEN_Msk; GCLK_REGS->GCLK_PCHCTRL[ADC1_GCLK_ID] = GCLK_PCHCTRL_GEN_GCLK1 | @@ -251,7 +251,7 @@ void init( void ) #endif -#endif //SAMD51 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ / SAMD21 } #ifdef __cplusplus From dd6260423db6cf33fac12fd32a4881f72bb542c0 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 24 Jul 2026 15:06:12 -0400 Subject: [PATCH 57/79] fix(usb): complete SAME5x device and host guards --- cores/arduino/USB/SAMD21_USBDevice.cpp | 6 ++-- cores/arduino/USB/SAMD21_USBDevice.h | 10 +++---- cores/arduino/USB/USBCore.cpp | 10 +++---- cores/arduino/USB/samd21_host.c | 38 +++++++++++++------------- cores/arduino/USB/samd21_host.h | 6 ++-- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/cores/arduino/USB/SAMD21_USBDevice.cpp b/cores/arduino/USB/SAMD21_USBDevice.cpp index da0e7b4d4..3f976599a 100644 --- a/cores/arduino/USB/SAMD21_USBDevice.cpp +++ b/cores/arduino/USB/SAMD21_USBDevice.cpp @@ -10,7 +10,7 @@ #include "SAMD21_USBDevice.h" void USBDevice_SAMD21G18x::reset() { -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) usb.USB_CTRLA |= USB_CTRLA_SWRST_Msk; memset(EP, 0, sizeof(EP)); while (usb.USB_SYNCBUSY & (USB_SYNCBUSY_SWRST_Msk | USB_SYNCBUSY_ENABLE_Msk)) {} @@ -25,7 +25,7 @@ void USBDevice_SAMD21G18x::reset() { void USBDevice_SAMD21G18x::calibrate() { // Load Pad Calibration data from non-volatile memory -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) const uint32_t calibration = *(const uint32_t *)(SW0_ADDR + 4u); uint32_t pad_transn = (calibration & FUSES_SW0_WORD_1_USB_TRANSN_Msk) >> FUSES_SW0_WORD_1_USB_TRANSN_Pos; @@ -50,7 +50,7 @@ void USBDevice_SAMD21G18x::calibrate() { if (pad_trim == 0x7) // maximum value (7) pad_trim = 3; -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) usb.USB_PADCAL = USB_PADCAL_TRANSN(pad_transn) | USB_PADCAL_TRANSP(pad_transp) | USB_PADCAL_TRIM(pad_trim); diff --git a/cores/arduino/USB/SAMD21_USBDevice.h b/cores/arduino/USB/SAMD21_USBDevice.h index 825166711..489b29151 100644 --- a/cores/arduino/USB/SAMD21_USBDevice.h +++ b/cores/arduino/USB/SAMD21_USBDevice.h @@ -29,7 +29,7 @@ typedef uint8_t ep_t; class USBDevice_SAMD21G18x { public: -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) USBDevice_SAMD21G18x() : usb(USB_REGS->DEVICE) { // Empty } @@ -100,13 +100,13 @@ class USBDevice_SAMD21G18x { // Enable inline void enable() { usb.CTRLA.bit.ENABLE = 1; -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) while (usb.SYNCBUSY.reg & USB_SYNCBUSY_ENABLE); // Wait for sync #endif } inline void disable() { usb.CTRLA.bit.ENABLE = 0; -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) while (usb.SYNCBUSY.reg & USB_SYNCBUSY_ENABLE); // Wait for sync #endif } @@ -154,7 +154,7 @@ class USBDevice_SAMD21G18x { // USB Device Endpoints function mapping // ------------------------------------- -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) // Config inline void epBank0SetType(ep_t ep, uint8_t type) { usb.DEVICE_ENDPOINT[ep].USB_EPCFG = @@ -301,7 +301,7 @@ class USBDevice_SAMD21G18x { inline void epBank1EnableAutoZLP(ep_t ep) { EP[ep].DeviceDescBank[1].PCKSIZE.bit.AUTO_ZLP = 1; } #endif private: -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) inline void setSpeedConfiguration(uint8_t speed) { usb.USB_CTRLB = (usb.USB_CTRLB & ~USB_DEVICE_CTRLB_SPDCONF_Msk) | USB_DEVICE_CTRLB_SPDCONF(speed); diff --git a/cores/arduino/USB/USBCore.cpp b/cores/arduino/USB/USBCore.cpp index f95df0d4a..8de67cbf2 100644 --- a/cores/arduino/USB/USBCore.cpp +++ b/cores/arduino/USB/USBCore.cpp @@ -247,7 +247,7 @@ bool USBDeviceClass::sendDescriptor(USBSetup &setup) } else if (setup.wValueL == ISERIAL) { #ifdef PLUGGABLE_USB_ENABLED - #if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) + #if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) #define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x008061FC) #define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x00806010) #define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x00806014) @@ -334,7 +334,7 @@ void USBDeviceClass::init() #endif /* Enable USB clock */ -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB; MCLK->AHBMASK.reg |= MCLK_AHBMASK_USB; @@ -348,7 +348,7 @@ void USBDeviceClass::init() GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) MCLK_REGS->MCLK_APBBMASK |= MCLK_APBBMASK_USB_Msk; MCLK_REGS->MCLK_AHBMASK |= MCLK_AHBMASK_USB_Msk; @@ -395,7 +395,7 @@ void USBDeviceClass::init() usbd.setFullSpeed(); // Configure interrupts -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) /* Attach to the USB host */ NVIC_SetPriority(USB_0_IRQn, 0UL); NVIC_SetPriority(USB_1_IRQn, 0UL); @@ -405,7 +405,7 @@ void USBDeviceClass::init() NVIC_EnableIRQ(USB_1_IRQn); NVIC_EnableIRQ(USB_2_IRQn); NVIC_EnableIRQ(USB_3_IRQn); -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) NVIC_SetPriority(USB_OTHER_IRQn, 0UL); NVIC_SetPriority(USB_SOF_HSOF_IRQn, 0UL); NVIC_SetPriority(USB_TRCPT0_IRQn, 0UL); diff --git a/cores/arduino/USB/samd21_host.c b/cores/arduino/USB/samd21_host.c index 2627c5bfe..8a7c54355 100644 --- a/cores/arduino/USB/samd21_host.c +++ b/cores/arduino/USB/samd21_host.c @@ -17,7 +17,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "../sam.h" +#include #ifndef USE_TINYUSB @@ -41,15 +41,15 @@ // Handle UOTGHS Host driver state static uhd_vbus_state_t uhd_state = UHD_STATE_NO_VBUS; -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) __attribute__((__aligned__(4))) volatile usb_descriptor_host_registers_t usb_pipe_table[USB_EPT_NUM]; -#else +#else // __SAME53__ / __SAME54__ __attribute__((__aligned__(4))) volatile UsbHostDescriptor usb_pipe_table[USB_EPT_NUM]; -#endif +#endif // __SAME53__ / __SAME54__ extern void (*gpf_isr)(void); -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) void UHD_Init(void) { @@ -231,7 +231,7 @@ uint32_t UHD_Pipe_Is_Transfer_Complete(uint32_t ul_pipe, uint32_t ul_token_type) return 1; } -#else +#else // __SAME53__ / __SAME54__ // NVM Software Calibration Area Mapping @@ -257,11 +257,11 @@ void UHD_Init(void) USB_SetHandler(&UHD_Handler); /* Enable USB clock */ -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB; #else PM->APBBMASK.reg |= PM_APBBMASK_USB; -#endif +#endif // __SAMD51__ / __SAME51__ /* Set up the USB DP/DM pins */ pinPeripheral( PIN_USB_DM, PIO_COM ); @@ -278,7 +278,7 @@ void UHD_Init(void) * Put Generic Clock Generator 0 as source for Generic Clock Multiplexer 6 (USB reference) */ -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); #else GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(6) | // Generic Clock Multiplexer 6 @@ -289,7 +289,7 @@ void UHD_Init(void) { /* Wait for synchronization */ } -#endif +#endif // __SAMD51__ / __SAME51__ /* Reset */ USB->HOST.CTRLA.bit.SWRST = 1; @@ -305,11 +305,11 @@ void UHD_Init(void) /* Load Pad Calibration */ -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) pad_transn = (*((uint32_t *)(NVMCTRL_SW0) // Non-Volatile Memory Controller #else pad_transn = (*((uint32_t *)(NVMCTRL_OTP4) // Non-Volatile Memory Controller -#endif +#endif // __SAMD51__ / __SAME51__ + (NVM_USB_PAD_TRANSN_POS / 32)) >> (NVM_USB_PAD_TRANSN_POS % 32)) @@ -322,11 +322,11 @@ void UHD_Init(void) USB->HOST.PADCAL.bit.TRANSN = pad_transn; -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) pad_transp = (*((uint32_t *)(NVMCTRL_SW0) #else pad_transp = (*((uint32_t *)(NVMCTRL_OTP4) -#endif +#endif // __SAMD51__ / __SAME51__ + (NVM_USB_PAD_TRANSP_POS / 32)) >> (NVM_USB_PAD_TRANSP_POS % 32)) & ((1 << NVM_USB_PAD_TRANSP_SIZE) - 1); @@ -338,11 +338,11 @@ void UHD_Init(void) USB->HOST.PADCAL.bit.TRANSP = pad_transp; -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) pad_trim = (*((uint32_t *)(NVMCTRL_SW0) #else pad_trim = (*((uint32_t *)(NVMCTRL_OTP4) -#endif +#endif // __SAMD51__ / __SAME51__ + (NVM_USB_PAD_TRIM_POS / 32)) >> (NVM_USB_PAD_TRIM_POS % 32)) & ((1 << NVM_USB_PAD_TRIM_SIZE) - 1); @@ -378,7 +378,7 @@ void UHD_Init(void) USB->HOST.CTRLB.bit.VBUSOK = 1; // Configure interrupts -#if defined(ARDUINO_SAMD51_E51) +#if defined(__SAMD51__) || defined(__SAME51__) NVIC_SetPriority((IRQn_Type)USB_0_IRQn, 0UL); NVIC_SetPriority((IRQn_Type)USB_1_IRQn, 0UL); NVIC_SetPriority((IRQn_Type)USB_2_IRQn, 0UL); @@ -391,7 +391,7 @@ void UHD_Init(void) #else NVIC_SetPriority((IRQn_Type)USB_IRQn, 0UL); NVIC_EnableIRQ((IRQn_Type)USB_IRQn); -#endif +#endif // __SAMD51__ / __SAME51__ } @@ -734,7 +734,7 @@ uint32_t UHD_Pipe_Is_Transfer_Complete(uint32_t ul_pipe, uint32_t ul_token_type) return 0; } -#endif +#endif // __SAME53__ / __SAME54__ diff --git a/cores/arduino/USB/samd21_host.h b/cores/arduino/USB/samd21_host.h index 1264eae3e..804a17716 100644 --- a/cores/arduino/USB/samd21_host.h +++ b/cores/arduino/USB/samd21_host.h @@ -23,7 +23,7 @@ extern "C" { #endif -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) extern __attribute__((__aligned__(4))) volatile usb_descriptor_host_registers_t usb_pipe_table[USB_EPT_NUM]; #else extern __attribute__((__aligned__(4))) volatile UsbHostDescriptor usb_pipe_table[USB_EPT_NUM]; @@ -45,7 +45,7 @@ extern __attribute__((__aligned__(4))) volatile UsbHostDescriptor usb_pipe_table #define USB_HOST_PCFG_PTOKEN_IN USB_HOST_PCFG_PTOKEN(0x1) #define USB_HOST_PCFG_PTOKEN_OUT USB_HOST_PCFG_PTOKEN(0x2) -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) #define USB_ERRORFLOW USB_HOST_STATUS_BK_ERRORFLOW_Msk #define USB_ERRORTIMEOUT USB_HOST_STATUS_PIPE_TOUTER_Msk #define USB_ERROR_DATATOGGLE USB_HOST_STATUS_PIPE_DTGLER_Msk @@ -65,7 +65,7 @@ extern __attribute__((__aligned__(4))) volatile UsbHostDescriptor usb_pipe_table #define USB_PCKSIZE_SIZE_1023_BYTES_FS 7 #define USB_PCKSIZE_SIZE_1024_BYTES_HS 7 -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) // SAME53/E54 host operations use the native register structures directly in samd21_host.c. #else #define USB_HOST_DTGL(p) (USB->HOST.HostPipe[p].PSTATUS.reg & USB_HOST_PSTATUS_DTGL)>>USB_HOST_PSTATUS_DTGL_Pos From aef45aafd724ce654d67193b01a774f4c006908d Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 24 Jul 2026 15:06:28 -0400 Subject: [PATCH 58/79] fix(uart): complete SAME5x family guards --- cores/arduino/Uart.cpp | 52 +++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/cores/arduino/Uart.cpp b/cores/arduino/Uart.cpp index 6aba44e2b..8724c49ee 100644 --- a/cores/arduino/Uart.cpp +++ b/cores/arduino/Uart.cpp @@ -91,13 +91,13 @@ void handler2(void) { instance.IrqHandler(); } \ void handlerOther(void) { instance.IrqHandler(); } -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define UART_DEFINE_SERCOM_HANDLERS(prefix, instance) \ UART_DEFINE_SAMD51_E51_HANDLERS(prefix##_IT_HANDLER_0, \ prefix##_IT_HANDLER_1, \ prefix##_IT_HANDLER_2, \ prefix##_IT_HANDLER_3, instance) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define UART_DEFINE_SERCOM_HANDLERS(prefix, instance) \ UART_DEFINE_SAME53_E54_HANDLERS(prefix##_IT_HANDLER_0, \ prefix##_IT_HANDLER_1, \ @@ -106,7 +106,7 @@ #else #define UART_DEFINE_SERCOM_HANDLERS(prefix, instance) \ UART_DEFINE_SINGLE_HANDLER(prefix##_IT_HANDLER, instance) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ Uart::Uart(SERCOM *_s, uint8_t _pinRX, uint8_t _pinTX, SercomRXPad _padRX, SercomUartTXPad _padTX) : Uart(_s, _pinRX, _pinTX, _padRX, _padTX, NO_RTS_PIN, NO_CTS_PIN) @@ -145,13 +145,13 @@ void Uart::begin(unsigned long baudrate, uint16_t config) pinMode(uc_pinRTS, OUTPUT); EPortType rtsPort = g_APinDescription[uc_pinRTS].ulPort; -#if defined(ARDUINO_SAME53_E54) +#if defined(__SAME53__) || defined(__SAME54__) pul_outsetRTS = &PORT_REGS->GROUP[rtsPort].PORT_OUTSET; pul_outclrRTS = &PORT_REGS->GROUP[rtsPort].PORT_OUTCLR; #else pul_outsetRTS = &PORT->Group[rtsPort].OUTSET.reg; pul_outclrRTS = &PORT->Group[rtsPort].OUTCLR.reg; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ ul_pinMaskRTS = (1ul << g_APinDescription[uc_pinRTS].ulPin); *pul_outclrRTS = ul_pinMaskRTS; @@ -471,11 +471,11 @@ Uart Serial(&PERIPH_SERIAL, PIN_SERIAL_RX, PIN_SERIAL_TX, PAD_SERIAL_RX, #define SERIAL_IT_HANDLER_0 UART_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SERIAL) #define SERIAL_IT_HANDLER_1 UART_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SERIAL) #define SERIAL_IT_HANDLER_2 UART_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SERIAL) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SERIAL_IT_HANDLER_3 UART_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SERIAL) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SERIAL_IT_HANDLER_OTHER UART_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SERIAL) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SERIAL_IT_HANDLER_0 UART_DEFINE_SERCOM_HANDLERS(SERIAL, Serial) #endif // PERIPH_SERIAL && !UART_VARIANT_OWNS_SERIAL @@ -490,11 +490,11 @@ Uart SerialUART(&PERIPH_SERIAL_UART, PIN_SERIAL_UART_RX, PIN_SERIAL_UART_TX, #define SERIAL_UART_IT_HANDLER_0 UART_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SERIAL_UART) #define SERIAL_UART_IT_HANDLER_1 UART_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SERIAL_UART) #define SERIAL_UART_IT_HANDLER_2 UART_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SERIAL_UART) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SERIAL_UART_IT_HANDLER_3 UART_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SERIAL_UART) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SERIAL_UART_IT_HANDLER_OTHER UART_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SERIAL_UART) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SERIAL_UART_IT_HANDLER_0 UART_DEFINE_SERCOM_HANDLERS(SERIAL_UART, SerialUART) #endif // PERIPH_SERIAL_UART && !UART_VARIANT_OWNS_SERIAL_UART @@ -515,11 +515,11 @@ Uart Serial1(&PERIPH_SERIAL1, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, #define SERIAL1_IT_HANDLER_0 UART_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SERIAL1) #define SERIAL1_IT_HANDLER_1 UART_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SERIAL1) #define SERIAL1_IT_HANDLER_2 UART_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SERIAL1) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SERIAL1_IT_HANDLER_3 UART_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SERIAL1) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SERIAL1_IT_HANDLER_OTHER UART_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SERIAL1) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SERIAL1_IT_HANDLER_0 UART_DEFINE_SERCOM_HANDLERS(SERIAL1, Serial1) #endif // PERIPH_SERIAL1 && !UART_VARIANT_OWNS_SERIAL1 @@ -540,11 +540,11 @@ Uart Serial2(&PERIPH_SERIAL2, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, #define SERIAL2_IT_HANDLER_0 UART_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SERIAL2) #define SERIAL2_IT_HANDLER_1 UART_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SERIAL2) #define SERIAL2_IT_HANDLER_2 UART_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SERIAL2) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SERIAL2_IT_HANDLER_3 UART_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SERIAL2) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SERIAL2_IT_HANDLER_OTHER UART_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SERIAL2) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SERIAL2_IT_HANDLER_0 UART_DEFINE_SERCOM_HANDLERS(SERIAL2, Serial2) #endif // PERIPH_SERIAL2 && !UART_VARIANT_OWNS_SERIAL2 @@ -559,11 +559,11 @@ Uart Serial3(&PERIPH_SERIAL3, PIN_SERIAL3_RX, PIN_SERIAL3_TX, PAD_SERIAL3_RX, #define SERIAL3_IT_HANDLER_0 UART_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SERIAL3) #define SERIAL3_IT_HANDLER_1 UART_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SERIAL3) #define SERIAL3_IT_HANDLER_2 UART_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SERIAL3) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SERIAL3_IT_HANDLER_3 UART_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SERIAL3) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SERIAL3_IT_HANDLER_OTHER UART_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SERIAL3) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SERIAL3_IT_HANDLER_0 UART_DEFINE_SERCOM_HANDLERS(SERIAL3, Serial3) #endif // PERIPH_SERIAL3 && !UART_VARIANT_OWNS_SERIAL3 @@ -578,11 +578,11 @@ Uart Serial4(&PERIPH_SERIAL4, PIN_SERIAL4_RX, PIN_SERIAL4_TX, PAD_SERIAL4_RX, #define SERIAL4_IT_HANDLER_0 UART_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SERIAL4) #define SERIAL4_IT_HANDLER_1 UART_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SERIAL4) #define SERIAL4_IT_HANDLER_2 UART_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SERIAL4) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SERIAL4_IT_HANDLER_3 UART_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SERIAL4) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SERIAL4_IT_HANDLER_OTHER UART_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SERIAL4) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SERIAL4_IT_HANDLER_0 UART_DEFINE_SERCOM_HANDLERS(SERIAL4, Serial4) #endif // PERIPH_SERIAL4 && !UART_VARIANT_OWNS_SERIAL4 @@ -597,11 +597,11 @@ Uart Serial5(&PERIPH_SERIAL5, PIN_SERIAL_RX, PIN_SERIAL_TX, PAD_SERIAL_RX, #define SERIAL5_IT_HANDLER_0 UART_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SERIAL5) #define SERIAL5_IT_HANDLER_1 UART_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SERIAL5) #define SERIAL5_IT_HANDLER_2 UART_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SERIAL5) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SERIAL5_IT_HANDLER_3 UART_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SERIAL5) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SERIAL5_IT_HANDLER_OTHER UART_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SERIAL5) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SERIAL5_IT_HANDLER_0 UART_DEFINE_SERCOM_HANDLERS(SERIAL5, Serial5) #endif // PERIPH_SERIAL5 && !UART_VARIANT_OWNS_SERIAL5 From 2fc453a6ff6fd1e968d4211b2686cf652aca775d Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 24 Jul 2026 15:06:46 -0400 Subject: [PATCH 59/79] fix(spi): complete SAME5x family guards --- libraries/SPI/SPI.cpp | 66 +++++++++++++++++++++---------------------- libraries/SPI/SPI.h | 12 +++----- 2 files changed, 37 insertions(+), 41 deletions(-) diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 2badab80a..9a58be417 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -106,13 +106,13 @@ void handler2(void) { instance.onService(); } \ void handlerOther(void) { instance.onService(); } -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SPI_DEFINE_SERCOM_HANDLERS(prefix, instance, periph) \ SPI_DEFINE_SAMD51_E51_HANDLERS(prefix##_IT_HANDLER_0, \ prefix##_IT_HANDLER_1, \ prefix##_IT_HANDLER_2, \ prefix##_IT_HANDLER_3, instance) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SPI_DEFINE_SERCOM_HANDLERS(prefix, instance, periph) \ SPI_DEFINE_SAME53_E54_HANDLERS(prefix##_IT_HANDLER_0, \ prefix##_IT_HANDLER_1, \ @@ -121,7 +121,7 @@ #else #define SPI_DEFINE_SERCOM_HANDLERS(prefix, instance, periph) \ SPI_DEFINE_SINGLE_HANDLER(prefix##_IT_HANDLER, instance) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ const SPISettings DEFAULT_SPI_SETTINGS = SPISettings(); @@ -289,11 +289,11 @@ void SPIClass::beginTransaction(SPISettings settings) noInterrupts(); } else if (interruptMode & SPI_IMODE_EXTINT) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) EIC_REGS->EIC_INTENCLR = EIC_INTENCLR_EXTINT(interruptMask); #else EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT(interruptMask); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } } @@ -310,11 +310,11 @@ void SPIClass::endTransaction(void) interrupts(); } else if (interruptMode & SPI_IMODE_EXTINT) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) EIC_REGS->EIC_INTENSET = EIC_INTENSET_EXTINT(interruptMask); #else EIC->INTENSET.reg = EIC_INTENSET_EXTINT(interruptMask); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } } } @@ -430,7 +430,7 @@ void SPIClass::onService(void) uint8_t flags = _p_sercom->getINTFLAG(); -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) const bool isError = flags & SERCOM_SPIM_INTFLAG_ERROR_Msk; const bool isRxc = flags & SERCOM_SPIM_INTFLAG_RXC_Msk; const bool isDre = flags & SERCOM_SPIM_INTFLAG_DRE_Msk; @@ -446,7 +446,7 @@ void SPIClass::onService(void) SERCOM_SPI_INTENCLR_RXC | SERCOM_SPI_INTENCLR_ERROR; const uint8_t disableDreMask = SERCOM_SPI_INTENCLR_DRE; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ if (isError) { @@ -537,11 +537,11 @@ SPIClass SPI(&PERIPH_SPI, PIN_SPI_MISO, PIN_SPI_SCK, PIN_SPI_MOSI, PAD_SPI_TX, #define SPI_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI) #define SPI_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI) #define SPI_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SPI_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SPI_IT_HANDLER_OTHER SPI_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SPI) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SPI_IT_HANDLER_0 SPI_DEFINE_SERCOM_HANDLERS(SPI, SPI, PERIPH_SPI) #endif // SPI_INTERFACES_COUNT > 0 @@ -556,11 +556,11 @@ SPIClass SPI1(&PERIPH_SPI1, PIN_SPI1_MISO, PIN_SPI1_SCK, PIN_SPI1_MOSI, #define SPI1_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI1) #define SPI1_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI1) #define SPI1_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI1) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SPI1_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI1) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SPI1_IT_HANDLER_OTHER SPI_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SPI1) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SPI1_IT_HANDLER_0 SPI_DEFINE_SERCOM_HANDLERS(SPI1, SPI1, PERIPH_SPI1) #endif // SPI_INTERFACES_COUNT > 1 @@ -575,11 +575,11 @@ SPIClass SPI2(&PERIPH_SPI2, PIN_SPI2_MISO, PIN_SPI2_SCK, PIN_SPI2_MOSI, #define SPI2_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI2) #define SPI2_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI2) #define SPI2_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI2) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SPI2_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI2) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SPI2_IT_HANDLER_OTHER SPI_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SPI2) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SPI2_IT_HANDLER_0 SPI_DEFINE_SERCOM_HANDLERS(SPI2, SPI2, PERIPH_SPI2) #endif // SPI_INTERFACES_COUNT > 2 @@ -594,11 +594,11 @@ SPIClass SPI3(&PERIPH_SPI3, PIN_SPI3_MISO, PIN_SPI3_SCK, PIN_SPI3_MOSI, #define SPI3_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI3) #define SPI3_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI3) #define SPI3_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI3) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SPI3_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI3) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SPI3_IT_HANDLER_OTHER SPI_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SPI3) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SPI3_IT_HANDLER_0 SPI_DEFINE_SERCOM_HANDLERS(SPI3, SPI3, PERIPH_SPI3) #endif // SPI_INTERFACES_COUNT > 3 @@ -613,11 +613,11 @@ SPIClass SPI4(&PERIPH_SPI4, PIN_SPI4_MISO, PIN_SPI4_SCK, PIN_SPI4_MOSI, #define SPI4_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI4) #define SPI4_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI4) #define SPI4_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI4) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SPI4_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI4) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SPI4_IT_HANDLER_OTHER SPI_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SPI4) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SPI4_IT_HANDLER_0 SPI_DEFINE_SERCOM_HANDLERS(SPI4, SPI4, PERIPH_SPI4) #endif // SPI_INTERFACES_COUNT > 4 @@ -632,11 +632,11 @@ SPIClass SPI5(&PERIPH_SPI5, PIN_SPI5_MISO, PIN_SPI5_SCK, PIN_SPI5_MOSI, #define SPI5_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI5) #define SPI5_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI5) #define SPI5_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI5) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SPI5_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI5) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SPI5_IT_HANDLER_OTHER SPI_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SPI5) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SPI5_IT_HANDLER_0 SPI_DEFINE_SERCOM_HANDLERS(SPI5, SPI5, PERIPH_SPI5) #endif // SPI_INTERFACES_COUNT > 5 @@ -651,11 +651,11 @@ SPIClass SPI6(&PERIPH_SPI6, PIN_SPI6_MISO, PIN_SPI6_SCK, PIN_SPI6_MOSI, #define SPI6_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI6) #define SPI6_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI6) #define SPI6_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI6) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SPI6_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI6) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SPI6_IT_HANDLER_OTHER SPI_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SPI6) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SPI6_IT_HANDLER_0 SPI_DEFINE_SERCOM_HANDLERS(SPI6, SPI6, PERIPH_SPI6) #endif // SPI_INTERFACES_COUNT > 6 @@ -670,11 +670,11 @@ SPIClass SPI7(&PERIPH_SPI7, PIN_SPI7_MISO, PIN_SPI7_SCK, PIN_SPI7_MOSI, #define SPI7_IT_HANDLER_0 SPI_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_SPI7) #define SPI7_IT_HANDLER_1 SPI_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_SPI7) #define SPI7_IT_HANDLER_2 SPI_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_SPI7) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define SPI7_IT_HANDLER_3 SPI_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_SPI7) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define SPI7_IT_HANDLER_OTHER SPI_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_SPI7) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !SPI7_IT_HANDLER_0 SPI_DEFINE_SERCOM_HANDLERS(SPI7, SPI7, PERIPH_SPI7) #endif // SPI_INTERFACES_COUNT > 7 diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index 9941b61c4..1f1390b73 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -23,10 +23,6 @@ #include #include "SERCOM.h" -#if defined(SERCOM_INST_NUM) && (SPI_INTERFACES_COUNT > SERCOM_INST_NUM) -#error "SPI_INTERFACES_COUNT exceeds available SERCOM instances for this MCU" -#endif // SERCOM_INST_NUM && SPI_INTERFACES_COUNT > SERCOM_INST_NUM - // SPI_HAS_TRANSACTION means SPI has // - beginTransaction() // - endTransaction() @@ -42,7 +38,7 @@ #define SPI_MODE2 0x03 #define SPI_MODE3 0x01 -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) // SAMD51 has configurable MAX_SPI, else use peripheral clock default. // Update: changing MAX_SPI via compiler flags is DEPRECATED, because // this affects ALL SPI peripherals including some that should NOT be @@ -63,7 +59,7 @@ #define MAX_SPI 24000000 #endif // !MAX_SPI #define SPI_MIN_CLOCK_DIVIDER (uint8_t)(1 + ((F_CPU - 1) / MAX_SPI)) -#endif // ARDUINO_SAMD51_E51 || ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ || __SAME53__ / __SAME54__ class SPISettings { public: @@ -84,11 +80,11 @@ class SPISettings { } void init_AlwaysInline(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) __attribute__((__always_inline__)) { -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) this->clockFreq = clock; // Clipping handled in SERCOM.cpp #else this->clockFreq = clock >= MAX_SPI ? MAX_SPI : clock; -#endif // ARDUINO_SAMD51_E51 || ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ || __SAME53__ / __SAME54__ this->bitOrder = (bitOrder == MSBFIRST ? MSB_FIRST : LSB_FIRST); From dc59abb8da1f7c18de2a40f54b66e35374d30a9a Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 24 Jul 2026 15:11:13 -0400 Subject: [PATCH 60/79] refactor(pendsv): map services across SAME5x peripherals --- cores/arduino/PendSV.cpp | 1 + cores/arduino/PendSV.h | 212 +++++++++++++++++++++++++-------------- 2 files changed, 137 insertions(+), 76 deletions(-) diff --git a/cores/arduino/PendSV.cpp b/cores/arduino/PendSV.cpp index 743fa866e..6eb84af90 100644 --- a/cores/arduino/PendSV.cpp +++ b/cores/arduino/PendSV.cpp @@ -36,6 +36,7 @@ extern "C" void tud_event_hook_cb(uint8_t rhport, uint32_t eventId, bool inIsr) #endif bool PendSV::initializeCoreServices() { + NVIC_SetPriority(PendSV_IRQn, (1 << __NVIC_PRIO_BITS) - 1); #if defined(USE_TINYUSB) return instance().registerService(PendSVChannels::Usb, tinyUsbDeviceTaskService, nullptr); #else diff --git a/cores/arduino/PendSV.h b/cores/arduino/PendSV.h index 68090a252..98222b2c7 100644 --- a/cores/arduino/PendSV.h +++ b/cores/arduino/PendSV.h @@ -1,129 +1,189 @@ #pragma once -#include "PendSVChannelMap.h" -#include "sam.h" +#include #include #include #if defined(SERCOM0) || defined(SERCOM0_REGS) -#define PENDSV_HAS_SERCOM0 true +#define SIMIO_PENDSV_HAS_SERCOM0 true #else -#define PENDSV_HAS_SERCOM0 false +#define SIMIO_PENDSV_HAS_SERCOM0 false #endif #if defined(SERCOM1) || defined(SERCOM1_REGS) -#define PENDSV_HAS_SERCOM1 true +#define SIMIO_PENDSV_HAS_SERCOM1 true #else -#define PENDSV_HAS_SERCOM1 false +#define SIMIO_PENDSV_HAS_SERCOM1 false #endif #if defined(SERCOM2) || defined(SERCOM2_REGS) -#define PENDSV_HAS_SERCOM2 true +#define SIMIO_PENDSV_HAS_SERCOM2 true #else -#define PENDSV_HAS_SERCOM2 false +#define SIMIO_PENDSV_HAS_SERCOM2 false #endif #if defined(SERCOM3) || defined(SERCOM3_REGS) -#define PENDSV_HAS_SERCOM3 true +#define SIMIO_PENDSV_HAS_SERCOM3 true #else -#define PENDSV_HAS_SERCOM3 false +#define SIMIO_PENDSV_HAS_SERCOM3 false #endif #if defined(SERCOM4) || defined(SERCOM4_REGS) -#define PENDSV_HAS_SERCOM4 true +#define SIMIO_PENDSV_HAS_SERCOM4 true #else -#define PENDSV_HAS_SERCOM4 false +#define SIMIO_PENDSV_HAS_SERCOM4 false #endif #if defined(SERCOM5) || defined(SERCOM5_REGS) -#define PENDSV_HAS_SERCOM5 true +#define SIMIO_PENDSV_HAS_SERCOM5 true #else -#define PENDSV_HAS_SERCOM5 false +#define SIMIO_PENDSV_HAS_SERCOM5 false #endif #if defined(SERCOM6) || defined(SERCOM6_REGS) -#define PENDSV_HAS_SERCOM6 true +#define SIMIO_PENDSV_HAS_SERCOM6 true #else -#define PENDSV_HAS_SERCOM6 false +#define SIMIO_PENDSV_HAS_SERCOM6 false #endif #if defined(SERCOM7) || defined(SERCOM7_REGS) -#define PENDSV_HAS_SERCOM7 true +#define SIMIO_PENDSV_HAS_SERCOM7 true #else -#define PENDSV_HAS_SERCOM7 false +#define SIMIO_PENDSV_HAS_SERCOM7 false #endif - -#if defined(USB) || defined(USB_REGS) -#define PENDSV_HAS_USB true +#if defined(DMAC) || defined(DMAC_REGS) +#define SIMIO_PENDSV_HAS_DMAC true #else -#define PENDSV_HAS_USB false +#define SIMIO_PENDSV_HAS_DMAC false #endif - -#if defined(GMAC) || defined(GMAC_REGS) -#define PENDSV_HAS_GMAC true -#define PENDSV_HAS_PHY true -#else -#define PENDSV_HAS_GMAC false -#define PENDSV_HAS_PHY false -#endif -#if defined(ADC) || defined(ADC_REGS) || defined(ADC0) || defined(ADC0_REGS) -#define PENDSV_HAS_ADC true +#if defined(AES) || defined(AES_REGS) +#define SIMIO_PENDSV_HAS_AES true #else -#define PENDSV_HAS_ADC false +#define SIMIO_PENDSV_HAS_AES false #endif -#if defined(AES) || defined(AES_REGS) -#define PENDSV_HAS_AES true +#if defined(ICM) || defined(ICM_REGS) +#define SIMIO_PENDSV_HAS_ICM true #else -#define PENDSV_HAS_AES false +#define SIMIO_PENDSV_HAS_ICM false #endif -#if defined(PUKCC) || defined(PUKCC_REGS) || defined(ID_PUKCC) || \ - defined(PUKCC_INSTANCE_ID) || defined(PUKCC_IRQn) -#define PENDSV_HAS_PUKCC true +#if defined(PUKCC) || defined(PUKCC_REGS) || defined(ID_PUKCC) || defined(PUKCC_INSTANCE_ID) || \ + defined(PUKCC_IRQn) +#define SIMIO_PENDSV_HAS_PUKCC true #else -#define PENDSV_HAS_PUKCC false +#define SIMIO_PENDSV_HAS_PUKCC false #endif #if defined(TRNG) || defined(TRNG_REGS) -#define PENDSV_HAS_TRNG true +#define SIMIO_PENDSV_HAS_TRNG true #else -#define PENDSV_HAS_TRNG false +#define SIMIO_PENDSV_HAS_TRNG false #endif -#if defined(RTC) || defined(RTC_REGS) -#define PENDSV_HAS_RTC true +#if defined(GMAC) || defined(GMAC_REGS) +#define SIMIO_PENDSV_HAS_GMAC true +#define SIMIO_PENDSV_HAS_PHY true #else -#define PENDSV_HAS_RTC false +#define SIMIO_PENDSV_HAS_GMAC false +#define SIMIO_PENDSV_HAS_PHY false #endif +template +struct PendSVChannelMap { + static constexpr uint8_t kUnavailable = 0xFF; + +private: + static constexpr uint8_t assign(bool present, uint8_t next) { + return present ? next : kUnavailable; + } + static constexpr uint8_t advance(bool present, uint8_t next) { + return present ? static_cast(next + 1) : next; + } + + static constexpr uint8_t kBase = 0; + static constexpr uint8_t kAfterSercom0 = advance(HasSercom0, kBase); + static constexpr uint8_t kAfterSercom1 = advance(HasSercom1, kAfterSercom0); + static constexpr uint8_t kAfterSercom2 = advance(HasSercom2, kAfterSercom1); + static constexpr uint8_t kAfterSercom3 = advance(HasSercom3, kAfterSercom2); + static constexpr uint8_t kAfterSercom4 = advance(HasSercom4, kAfterSercom3); + static constexpr uint8_t kAfterSercom5 = advance(HasSercom5, kAfterSercom4); + static constexpr uint8_t kAfterSercom6 = advance(HasSercom6, kAfterSercom5); + static constexpr uint8_t kAfterSercom7 = advance(HasSercom7, kAfterSercom6); + static constexpr uint8_t kAfterRtc = advance(true, kAfterSercom7); + static constexpr uint8_t kAfterUsb = advance(true, kAfterRtc); + static constexpr uint8_t kAfterAdc = advance(true, kAfterUsb); + static constexpr uint8_t kAfterDmac = advance(HasDmac, kAfterAdc); + static constexpr uint8_t kAfterAes = advance(HasAes, kAfterDmac); + static constexpr uint8_t kAfterIcm = advance(HasIcm, kAfterAes); + static constexpr uint8_t kAfterPukcc = advance(HasPukcc, kAfterIcm); + static constexpr uint8_t kAfterTrng = advance(HasTrng, kAfterPukcc); + static constexpr uint8_t kAfterGmac = advance(HasGmac, kAfterTrng); + static constexpr uint8_t kAfterPhy = advance(HasPhy, kAfterGmac); + static constexpr uint8_t kAfterUsbProtocol = advance(true, kAfterPhy); + static constexpr uint8_t kAfterDiscreteInput = advance(true, kAfterUsbProtocol); + +public: + static constexpr uint8_t Sercom0 = assign(HasSercom0, kBase); + static constexpr uint8_t Sercom1 = assign(HasSercom1, kAfterSercom0); + static constexpr uint8_t Sercom2 = assign(HasSercom2, kAfterSercom1); + static constexpr uint8_t Sercom3 = assign(HasSercom3, kAfterSercom2); + static constexpr uint8_t Sercom4 = assign(HasSercom4, kAfterSercom3); + static constexpr uint8_t Sercom5 = assign(HasSercom5, kAfterSercom4); + static constexpr uint8_t Sercom6 = assign(HasSercom6, kAfterSercom5); + static constexpr uint8_t Sercom7 = assign(HasSercom7, kAfterSercom6); + static constexpr uint8_t Rtc = kAfterSercom7; + static constexpr uint8_t Usb = kAfterRtc; + static constexpr uint8_t Adc = kAfterUsb; + static constexpr uint8_t Dmac = assign(HasDmac, kAfterAdc); + static constexpr uint8_t Aes = assign(HasAes, kAfterDmac); + static constexpr uint8_t Icm = assign(HasIcm, kAfterAes); + static constexpr uint8_t Pukcc = assign(HasPukcc, kAfterIcm); + static constexpr uint8_t Trng = assign(HasTrng, kAfterPukcc); + static constexpr uint8_t Gmac = assign(HasGmac, kAfterTrng); + static constexpr uint8_t Phy = assign(HasPhy, kAfterGmac); + static constexpr uint8_t UsbProtocol = kAfterPhy; + static constexpr uint8_t DiscreteInput = kAfterUsbProtocol; + static constexpr uint8_t Count = kAfterDiscreteInput; + + static constexpr bool isAvailable(uint8_t channel) { return channel != kUnavailable; } + static constexpr uint8_t sercom(uint8_t index) { + return index == 0 ? Sercom0 : index == 1 ? Sercom1 : index == 2 ? Sercom2 + : index == 3 ? Sercom3 : index == 4 ? Sercom4 : index == 5 ? Sercom5 + : index == 6 ? Sercom6 : index == 7 ? Sercom7 : kUnavailable; + } + static constexpr uint8_t sercomIndex(uint8_t channel) { + return channel == Sercom0 ? 0 : channel == Sercom1 ? 1 : channel == Sercom2 ? 2 + : channel == Sercom3 ? 3 : channel == Sercom4 ? 4 : channel == Sercom5 ? 5 + : channel == Sercom6 ? 6 : channel == Sercom7 ? 7 : kUnavailable; + } +}; + using PendSVChannels = PendSVChannelMap< - PENDSV_HAS_SERCOM0, PENDSV_HAS_SERCOM1, PENDSV_HAS_SERCOM2, - PENDSV_HAS_SERCOM3, PENDSV_HAS_SERCOM4, PENDSV_HAS_SERCOM5, - PENDSV_HAS_SERCOM6, PENDSV_HAS_SERCOM7, PENDSV_HAS_USB, PENDSV_HAS_GMAC, - PENDSV_HAS_PHY, PENDSV_HAS_ADC, PENDSV_HAS_AES, PENDSV_HAS_PUKCC, - PENDSV_HAS_TRNG, PENDSV_HAS_RTC>; + SIMIO_PENDSV_HAS_SERCOM0, SIMIO_PENDSV_HAS_SERCOM1, SIMIO_PENDSV_HAS_SERCOM2, + SIMIO_PENDSV_HAS_SERCOM3, SIMIO_PENDSV_HAS_SERCOM4, SIMIO_PENDSV_HAS_SERCOM5, + SIMIO_PENDSV_HAS_SERCOM6, SIMIO_PENDSV_HAS_SERCOM7, SIMIO_PENDSV_HAS_DMAC, + SIMIO_PENDSV_HAS_AES, SIMIO_PENDSV_HAS_ICM, SIMIO_PENDSV_HAS_PUKCC, + SIMIO_PENDSV_HAS_TRNG, SIMIO_PENDSV_HAS_GMAC, SIMIO_PENDSV_HAS_PHY>; class PendSV { - public: - using ServiceFn = void (*)(uint8_t serviceId, void *context); - static constexpr uint8_t kMaxServices = PendSVChannels::kMaxServices; - static constexpr uint8_t kDispatchBudget = 16; - static_assert(kMaxServices <= 32, "PendSV pending mask supports at most 32 services"); - static_assert(PendSVChannels::Count <= kMaxServices, - "PendSV channel allocation exceeds dispatcher capacity"); +public: + using ServiceFn = void (*)(uint8_t serviceId, void *context); + static constexpr uint8_t kMaxServices = PendSVChannels::Count; + static constexpr uint8_t kDispatchBudget = 16; + static_assert(kMaxServices <= 32, "PendSV pending mask supports at most 32 services"); - static PendSV &instance(); - static bool initializeCoreServices(); + static PendSV &instance(); + static bool initializeCoreServices(); - bool registerService(uint8_t serviceId, ServiceFn fn, void *context = nullptr); - // Cancels queued work that has not started dispatching. If a callback was - // already copied for dispatch, its context must remain valid until it returns. - void clearService(uint8_t serviceId); - void dispatchPending(); - void setPending(uint8_t serviceId); - void setPendingOnce(uint8_t serviceId); + bool registerService(uint8_t serviceId, ServiceFn fn, void *context = nullptr); + void clearService(uint8_t serviceId); + void dispatchPending(); + void setPending(uint8_t serviceId); + void setPendingOnce(uint8_t serviceId); - private: - static uint32_t enterCritical(); - static void exitCritical(uint32_t primask); +private: + static uint32_t enterCritical(); + static void exitCritical(uint32_t primask); - struct ServiceEntry { - ServiceFn fn = nullptr; - void *context = nullptr; - }; + struct ServiceEntry { + ServiceFn fn = nullptr; + void *context = nullptr; + }; - std::array services_{}; - std::array pendingCount_{}; - volatile uint32_t pendingMask_ = 0; + std::array services_{}; + std::array pendingCount_{}; + volatile uint32_t pendingMask_ = 0; }; From 9f1d78fcced017a4fc822d4942021ecacd9bc793 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 24 Jul 2026 15:08:54 -0400 Subject: [PATCH 61/79] feat(wire): add deferred multimaster slave DMA --- cores/arduino/SERCOM.cpp | 808 +++++++++++++++++++++++----------- cores/arduino/SERCOM.h | 88 ++-- cores/arduino/SERCOM_Txn.h | 3 +- cores/arduino/SERCOM_inline.h | 157 ++++--- libraries/Wire/Wire.cpp | 221 +++++++--- libraries/Wire/Wire.h | 245 ++++++----- 6 files changed, 1002 insertions(+), 520 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 746b5f3d6..8e47cc728 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -18,9 +18,9 @@ */ #include "SERCOM.h" -#include "variant.h" #include "Arduino.h" #include "PendSV.h" +#include "variant.h" #ifdef USE_ZERODMA #include @@ -32,18 +32,19 @@ #define WIRE_RISE_TIME_NANOSECONDS 125 #endif // !WIRE_RISE_TIME_NANOSECONDS -#ifdef ARDUINO_SAME53_E54 -SERCOM::SERCOM(sercom_registers_t* s) +#if defined(__SAME53__) || defined(__SAME54__) +SERCOM::SERCOM(sercom_registers_t *s) #else -SERCOM::SERCOM(Sercom* s) -#endif // ARDUINO_SAME53_E54 +SERCOM::SERCOM(Sercom *s) +#endif // __SAME53__ / __SAME54__ { sercom = s; int8_t idx = getSercomIndex(); if (idx >= 0 && idx < (int8_t)kSercomCount) s_instances[idx] = this; -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || \ + defined(__SAME54__) // A briefly-available but now deprecated feature had the SPI clock source // set via a compile-time setting (MAX_SPI)...problem was this affected // ALL SERCOMs, whereas some (anything read/write, e.g. SD cards) should @@ -59,27 +60,27 @@ SERCOM::SERCOM(Sercom* s) #elif SERCOM_SPI_FREQ_REF == 100000000 // 100 MHz clock = GCLK2 clockSource = SERCOM_CLOCK_SOURCE_100M; #endif // SERCOM_SPI_FREQ_REF == F_CPU -#endif // ARDUINO_SAMD51_E51 || ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ || __SAME53__ / __SAME54__ } void SERCOM::resetSERCOM() { // UART, SPI, I2CS, and I2CM use the same SWRST and DBGCTRL bits, so this works for all modes -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->USART_INT.SERCOM_CTRLA = SERCOM_USART_INT_CTRLA_SWRST_Msk; #else sercom->USART.CTRLA.bit.SWRST = 1; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ waitSyncBusySwrst(); -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->USART_INT.SERCOM_DBGCTRL &= ~SERCOM_USART_INT_DBGCTRL_DBGSTOP_Msk; #else // DBGCTRL is not affected by SWRST, so explicitly clear it here to ensure debug behavior is // consistent after reset sercom->USART.DBGCTRL.bit.DBGSTOP = 0; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } /* ========================= @@ -112,7 +113,7 @@ void SERCOM::initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint baudTimes8 = (freqRef * 8) / (sampleRateValue * baudrate); } -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->USART_INT.SERCOM_CTRLA = SERCOM_USART_INT_CTRLA_MODE(mode) | SERCOM_USART_INT_CTRLA_SAMPR(sampleRate); sercom->USART_INT.SERCOM_INTENSET = SERCOM_USART_INT_INTENSET_RXC_Msk | @@ -136,13 +137,13 @@ void SERCOM::initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint sercom->USART.BAUD.FRAC.FP = (baudTimes8 % 8); sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8); } -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } void SERCOM::initFrame(SercomUartCharSize charSize, SercomDataOrder dataOrder, SercomParityMode parityMode, SercomNumberStopBit nbStopBits) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->USART_INT.SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_FORM(parityMode == SERCOM_NO_PARITY ? 0 : 1) | SERCOM_USART_INT_CTRLA_DORD(dataOrder); @@ -161,12 +162,12 @@ void SERCOM::initFrame(SercomUartCharSize charSize, SercomDataOrder dataOrder, SERCOM_USART_CTRLB_CHSIZE(charSize) | (nbStopBits << SERCOM_USART_CTRLB_SBMODE_Pos) | ((parityMode == SERCOM_NO_PARITY ? 0 : parityMode) << SERCOM_USART_CTRLB_PMODE_Pos); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } void SERCOM::initPads(SercomUartTXPad txPad, SercomRXPad rxPad) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->USART_INT.SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_TXPO(txPad) | SERCOM_USART_INT_CTRLA_RXPO(rxPad); sercom->USART_INT.SERCOM_CTRLB |= SERCOM_USART_INT_CTRLB_TXEN_Msk | @@ -178,7 +179,7 @@ void SERCOM::initPads(SercomUartTXPad txPad, SercomRXPad rxPad) // Enable Transceiver and Receiver sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } void SERCOM::resetUART() @@ -209,126 +210,126 @@ void SERCOM::flushUART() return; // Wait for transmission to complete -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) while ((sercom->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_TXC_Msk) == 0) ; #else while (!sercom->USART.INTFLAG.bit.TXC) ; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } void SERCOM::clearStatusUART() { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->USART_INT.SERCOM_STATUS = SERCOM_USART_INT_STATUS_RESETVALUE; #else // Reset (with 0) the STATUS register sercom->USART.STATUS.reg = SERCOM_USART_STATUS_RESETVALUE; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } bool SERCOM::availableDataUART() { // RXC: Receive Complete -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) return (sercom->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_RXC_Msk) != 0; #else return sercom->USART.INTFLAG.bit.RXC; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } bool SERCOM::isUARTError() { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) return (sercom->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_ERROR_Msk) != 0; #else return sercom->USART.INTFLAG.bit.ERROR; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } void SERCOM::acknowledgeUARTError() { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->USART_INT.SERCOM_INTFLAG = SERCOM_USART_INT_INTFLAG_ERROR_Msk; #else sercom->USART.INTFLAG.bit.ERROR = 1; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } void SERCOM::enableReceiveCompleteInterruptUART() { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->USART_INT.SERCOM_INTENSET = SERCOM_USART_INT_INTENSET_RXC_Msk; #else sercom->USART.INTENSET.reg = SERCOM_USART_INTENSET_RXC; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } void SERCOM::disableReceiveCompleteInterruptUART() { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->USART_INT.SERCOM_INTENCLR = SERCOM_USART_INT_INTENCLR_RXC_Msk; #else sercom->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_RXC; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } bool SERCOM::isBufferOverflowErrorUART() { // BUFOVF: Buffer Overflow -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) return (sercom->USART_INT.SERCOM_STATUS & SERCOM_USART_INT_STATUS_BUFOVF_Msk) != 0; #else return sercom->USART.STATUS.bit.BUFOVF; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } bool SERCOM::isFrameErrorUART() { // FERR: Frame Error -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) return (sercom->USART_INT.SERCOM_STATUS & SERCOM_USART_INT_STATUS_FERR_Msk) != 0; #else return sercom->USART.STATUS.bit.FERR; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } void SERCOM::clearFrameErrorUART() { // Clear FERR bit writing 1 status bit -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->USART_INT.SERCOM_STATUS = SERCOM_USART_INT_STATUS_FERR_Msk; #else sercom->USART.STATUS.bit.FERR = 1; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } bool SERCOM::isParityErrorUART() { // PERR: Parity Error -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) return (sercom->USART_INT.SERCOM_STATUS & SERCOM_USART_INT_STATUS_PERR_Msk) != 0; #else return sercom->USART.STATUS.bit.PERR; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } bool SERCOM::isDataRegisterEmptyUART() { // DRE: Data Register Empty -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) return (sercom->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_DRE_Msk) != 0; #else return sercom->USART.INTFLAG.bit.DRE; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } uint8_t SERCOM::readDataUART() { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) return (uint8_t)sercom->USART_INT.SERCOM_DATA; #else return sercom->USART.DATA.bit.DATA; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } int SERCOM::writeDataUART(uint8_t data) @@ -337,30 +338,30 @@ int SERCOM::writeDataUART(uint8_t data) while (!isDataRegisterEmptyUART()); // Put data into DATA register -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->USART_INT.SERCOM_DATA = data; #else sercom->USART.DATA.reg = (uint16_t)data; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ return 1; } void SERCOM::enableDataRegisterEmptyInterruptUART() { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->USART_INT.SERCOM_INTENSET = SERCOM_USART_INT_INTENSET_DRE_Msk; #else sercom->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } void SERCOM::disableDataRegisterEmptyInterruptUART() { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->USART_INT.SERCOM_INTENCLR = SERCOM_USART_INT_INTENCLR_DRE_Msk; #else sercom->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_DRE; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } bool SERCOM::startTransmissionUART(void) @@ -390,11 +391,11 @@ bool SERCOM::startTransmissionUART(void) } #ifdef USE_ZERODMA -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) void* dataReg = (void*)&sercom->USART_INT.SERCOM_DATA; #else void* dataReg = (void*)&sercom->USART.DATA.reg; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ _uart.dmaNeedTx = (txn->txPtr != nullptr); _uart.dmaNeedRx = (txn->rxPtr != nullptr); _uart.dmaTxDone = !_uart.dmaNeedTx; @@ -507,7 +508,7 @@ void SERCOM::initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize ch registerService(getSercomIndex(), &SERCOM::stopTransmissionSPI); -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->SPIM.SERCOM_CTRLA = SERCOM_SPIM_CTRLA_MODE_SPI_MASTER | SERCOM_SPIM_CTRLA_DOPO(mosi) | SERCOM_SPIM_CTRLA_DIPO(miso) | @@ -522,7 +523,7 @@ void SERCOM::initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize ch (dataOrder << SERCOM_SPI_CTRLA_DORD_Pos); sercom->SPI.CTRLB.reg = SERCOM_SPI_CTRLB_CHSIZE(charSize) | SERCOM_SPI_CTRLB_RXEN; // Active the SPI receiver. -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ waitSyncBusyCtrlB(); } @@ -541,11 +542,11 @@ bool SERCOM::startTransmissionSPI(void) _spi.useDma = _dmaConfigured; if (_spi.useDma) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) void* dataReg = (void*)&sercom->SPIM.SERCOM_DATA; #else void* dataReg = (void*)&sercom->SPI.DATA.reg; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ _spi.dmaNeedTx = (txn->txPtr != nullptr); _spi.dmaNeedRx = (txn->rxPtr != nullptr); _spi.dmaTxDone = !_spi.dmaNeedTx; @@ -570,7 +571,7 @@ bool SERCOM::startTransmissionSPI(void) _spi.useDma = false; #endif // USE_ZERODMA -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->SPIM.SERCOM_INTENSET = SERCOM_SPIM_INTENSET_DRE_Msk | SERCOM_SPIM_INTENSET_RXC_Msk | SERCOM_SPIM_INTENSET_ERROR_Msk; @@ -578,7 +579,7 @@ bool SERCOM::startTransmissionSPI(void) sercom->SPI.INTENSET.reg = SERCOM_SPI_INTENSET_DRE | SERCOM_SPI_INTENSET_RXC | SERCOM_SPI_INTENSET_ERROR; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ return true; } @@ -655,7 +656,7 @@ void SERCOM::initSPIClock(SercomSpiClockMode clockMode, uint32_t baudrate) cpol = 1; // Setting the CTRLA register -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->SPIM.SERCOM_CTRLA |= SERCOM_SPIM_CTRLA_CPHA(cpha) | SERCOM_SPIM_CTRLA_CPOL(cpol); sercom->SPIM.SERCOM_BAUD = SERCOM_SPIM_BAUD_BAUD(calculateBaudrateSynchronous(baudrate)); @@ -665,7 +666,7 @@ void SERCOM::initSPIClock(SercomSpiClockMode clockMode, uint32_t baudrate) // Synchronous arithmetic sercom->SPI.BAUD.reg = calculateBaudrateSynchronous(baudrate); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } void SERCOM::resetSPI() @@ -678,34 +679,34 @@ void SERCOM::setDataOrderSPI(SercomDataOrder dataOrder) // Register enable-protected disableSPI(); -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->SPIM.SERCOM_CTRLA = (sercom->SPIM.SERCOM_CTRLA & ~SERCOM_SPIM_CTRLA_DORD_Msk) | SERCOM_SPIM_CTRLA_DORD(dataOrder); #else sercom->SPI.CTRLA.bit.DORD = dataOrder; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ enableSPI(); } SercomDataOrder SERCOM::getDataOrderSPI() { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) return (sercom->SPIM.SERCOM_CTRLA & SERCOM_SPIM_CTRLA_DORD_Msk) ? LSB_FIRST : MSB_FIRST; #else return (sercom->SPI.CTRLA.bit.DORD ? LSB_FIRST : MSB_FIRST); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } void SERCOM::setBaudrateSPI(uint8_t divider) { disableSPI(); // Register is enable-protected -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->SPIM.SERCOM_BAUD = SERCOM_SPIM_BAUD_BAUD(calculateBaudrateSynchronous(freqRef / divider)); #else sercom->SPI.BAUD.reg = calculateBaudrateSynchronous(freqRef / divider); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ enableSPI(); } @@ -725,21 +726,21 @@ void SERCOM::setClockModeSPI(SercomSpiClockMode clockMode) // Register enable-protected disableSPI(); -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->SPIM.SERCOM_CTRLA = (sercom->SPIM.SERCOM_CTRLA & ~(SERCOM_SPIM_CTRLA_CPOL_Msk | SERCOM_SPIM_CTRLA_CPHA_Msk)) | SERCOM_SPIM_CTRLA_CPOL(cpol) | SERCOM_SPIM_CTRLA_CPHA(cpha); #else sercom->SPI.CTRLA.bit.CPOL = cpol; sercom->SPI.CTRLA.bit.CPHA = cpha; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ enableSPI(); } uint8_t SERCOM::transferDataSPI(uint8_t data) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->SPIM.SERCOM_DATA = data; while ((sercom->SPIM.SERCOM_INTFLAG & SERCOM_SPIM_INTFLAG_RXC_Msk) == 0); return (uint8_t)sercom->SPIM.SERCOM_DATA; @@ -749,26 +750,26 @@ uint8_t SERCOM::transferDataSPI(uint8_t data) while (sercom->SPI.INTFLAG.bit.RXC == 0); // Waiting Complete Reception return sercom->SPI.DATA.bit.DATA; // Reading data -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } bool SERCOM::isBufferOverflowErrorSPI() { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) return sercom->SPIM.SERCOM_STATUS & SERCOM_SPIM_STATUS_BUFOVF_Msk; #else return sercom->SPI.STATUS.bit.BUFOVF; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } bool SERCOM::isDataRegisterEmptySPI() { // DRE: Data Register Empty -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) return sercom->SPIM.SERCOM_INTFLAG & SERCOM_SPIM_INTFLAG_DRE_Msk; #else return sercom->SPI.INTFLAG.bit.DRE; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } //bool SERCOM::isTransmitCompleteSPI() @@ -821,12 +822,18 @@ void SERCOM::clearQueueWIRE(void) _wire.txnLength = 0; _wire.returnValue = SercomWireError::SUCCESS; _wire.retryCount = 0; + _wire.releasePending = false; + _wire.abortPending = false; +#ifdef USE_ZERODMA + _wireRxSuspendPending = false; +#endif // Clear deferred callbacks (from slave/receive operations) - _wireDeferredCb = nullptr; - _wireDeferredUser = nullptr; - _wireDeferredLength = 0; - _wireDeferredPending = false; + _wireCallbackUser = nullptr; + _wireReceiveCb = nullptr; + _wireRequestCb = nullptr; + _wireReceivePending = false; + _wireRequestPending = false; } void SERCOM::initWIRE(void) @@ -839,9 +846,13 @@ void SERCOM::initWIRE(void) registerService(idx, static_cast(&SERCOM::stopTransmissionWIRE)); #ifdef USE_ZERODMA - dmaSetCallbacks(SERCOM::dmaTxCallbackWIRE, SERCOM::dmaRxCallbackWIRE); + dmaSetCallbacks(SERCOM::dmaTxCallbackWIRE, SERCOM::dmaRxCallbackWIRE, + SERCOM::dmaErrorCallbackWIRE); if (idx >= 0) dmaInit(idx); + if (_dmaRx) + _dmaRx->setCallback(SERCOM::dmaRxSuspendCallbackWIRE, + DMA_CALLBACK_CHANNEL_SUSPEND); #endif // USE_ZERODMA _wire.inited = true; // Mark as initialized last @@ -859,13 +870,13 @@ void SERCOM::initSlaveWIRE( uint16_t ucAddress, bool enableGeneralCall, uint8_t uint16_t mask = enable10Bit ? 0x03FFul : 0x007Ful; uint32_t generalCall = 0; uint32_t tenBit = 0; -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) generalCall = enableGeneralCall ? SERCOM_I2CS_ADDR_GENCEN(1) : 0u; tenBit = enable10Bit ? SERCOM_I2CS_ADDR_TENBITEN(1) : 0u; #else generalCall = enableGeneralCall ? SERCOM_I2CS_ADDR_GENCEN : 0u; tenBit = enable10Bit ? SERCOM_I2CS_ADDR_TENBITEN : 0u; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ _wire.slaveSpeed = speed; _wire.addr = SERCOM_I2CS_ADDR_ADDR(ucAddress & mask) | @@ -876,10 +887,14 @@ void SERCOM::initSlaveWIRE( uint16_t ucAddress, bool enableGeneralCall, uint8_t setSlaveWIRE(); } -void SERCOM::registerReceiveWIRE(void (*cb)(void* user, int length), void* user) -{ - _wireDeferredCb = cb; - _wireDeferredUser = user; +void SERCOM::registerReceiveWIRE(WireReceiveCallback callback, void *user) { + _wireReceiveCb = callback; + _wireCallbackUser = user; +} + +void SERCOM::registerRequestWIRE(WireRequestCallback callback, void *user) { + _wireRequestCb = callback; + _wireCallbackUser = user; } void SERCOM::initMasterWIRE( uint32_t baudrate ) @@ -891,20 +906,86 @@ void SERCOM::initMasterWIRE( uint32_t baudrate ) setMasterWIRE(); } -void SERCOM::deferReceiveWIRE(int length) -{ - _wireDeferredLength = length; - _wireDeferredPending = true; +void SERCOM::deferReceiveWIRE(void) { + _wireReceivePending = true; + _wire.returnValue = SercomWireError::SUCCESS; setPending((uint8_t)getSercomIndex()); } +void SERCOM::deferRequestWIRE(void) { + _wireRequestPending = true; + _wire.returnValue = SercomWireError::SUCCESS; +#ifdef USE_ZERODMA + // A repeated START may request a slave response while the preceding RX DMA + // descriptor is still being suspended. Its suspend callback owns the next + // PendSV dispatch so the receive length is published before onRequest can + // replace the active transaction. + if (_wireRxSuspendPending) + return; +#endif // USE_ZERODMA + setPending((uint8_t)getSercomIndex()); +} + +void SERCOM::deferReceiveCompleteWIRE(void) { +#ifdef USE_ZERODMA + // BTCNT for the active channel lives in the DMAC's internal descriptor. + // Suspend first so hardware commits that counter to WRBADDR; the suspend + // callback snapshots it, aborts the channel, and defers normal retirement. + if (_dmaRxActive) { + if (!_wireRxSuspendPending && _dmaRx) { + _wireRxSuspendPending = true; + _dmaRx->suspend(); + } + return; + } +#endif // USE_ZERODMA + deferStopWIRE(SercomWireError::SUCCESS); +} + +#ifdef USE_ZERODMA +void SERCOM::dmaRxSuspendCallbackWIRE(Adafruit_ZeroDMA *dma) { + SERCOM *inst = findDmaOwner(dma, false); + if (!inst || !inst->_wireRxSuspendPending) + return; + + inst->_wireRxSuspendPending = false; + size_t transferred = 0; + uint16_t remaining = 0; + const uint8_t channel = dma->getChannel(); + if (channel < DMAC_CH_NUM) { +#if defined(__SAME53__) || defined(__SAME54__) + const DmacDescriptor *writeback = + reinterpret_cast(DMAC_REGS->DMAC_WRBADDR); + remaining = writeback[channel].DMAC_BTCNT; +#else + const DmacDescriptor *writeback = + reinterpret_cast(DMAC->WRBADDR.reg); + remaining = writeback[channel].BTCNT.reg; +#endif // __SAME53__ / __SAME54__ + if (inst->_wire.txnLength >= remaining) + transferred = inst->_wire.txnLength - remaining; + } + inst->_wire.txnIndex = transferred; + inst->dmaAbortRx(); + inst->deferStopWIRE(SercomWireError::SUCCESS); +} +#endif // USE_ZERODMA + +SercomTxn *SERCOM::retireSlaveTransactionWIRE(bool reserveForFollowup) { + SercomTxn *txn = _wire.currentTxn; + _wire.retryCount = 0; + _wire.active = reserveForFollowup; + _wire.currentTxn = nullptr; + return txn; +} + void SERCOM::setMasterWIRE(void) { // Errata: do not enable QCEN when SCLSM=1 (bus error). Hs-mode requires SCLSM=1, // so master Hs-mode must be DMA-only and STOP-only (no repeated starts). disableWIRE(); bool sclsm = (_wire.masterSpeed == 0x2); -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->I2CM.SERCOM_CTRLA = _wire.ctrla | SERCOM_I2CM_CTRLA_MODE(I2C_MASTER_OPERATION) | SERCOM_I2CM_CTRLA_SPEED(_wire.masterSpeed) | @@ -931,19 +1012,22 @@ void SERCOM::setMasterWIRE(void) SERCOM_I2CS_INTENSET_AMATCH | SERCOM_I2CS_INTENSET_DRDY | SERCOM_I2CS_INTENSET_PREC; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } void SERCOM::setSlaveWIRE(void) { disableWIRE(); bool sclsm = (_wire.slaveSpeed == 0x2); -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) + uint32_t slaveCtrlb = _wire.ctrlb; + if (slaveCtrlb & SERCOM_I2CS_CTRLB_SMEN_Msk) + slaveCtrlb &= ~SERCOM_I2CS_CTRLB_AACKEN_Msk; sercom->I2CS.SERCOM_CTRLA = _wire.ctrla | SERCOM_I2CS_CTRLA_MODE(I2C_SLAVE_OPERATION) | SERCOM_I2CS_CTRLA_SPEED(_wire.slaveSpeed) | (sclsm ? SERCOM_I2CS_CTRLA_SCLSM(1) : 0); - sercom->I2CS.SERCOM_CTRLB = _wire.ctrlb | SERCOM_I2CS_CTRLB_AACKEN(1); + sercom->I2CS.SERCOM_CTRLB = slaveCtrlb; sercom->I2CS.SERCOM_ADDR = _wire.addr; enableWIRE(); sercom->I2CS.SERCOM_INTENSET = SERCOM_I2CS_INTENSET_ERROR_Msk | @@ -951,10 +1035,13 @@ void SERCOM::setSlaveWIRE(void) SERCOM_I2CS_INTENSET_AMATCH_Msk | SERCOM_I2CS_INTENSET_DRDY_Msk; #else + uint32_t slaveCtrlb = _wire.ctrlb; + if (slaveCtrlb & SERCOM_I2CS_CTRLB_SMEN) + slaveCtrlb &= ~SERCOM_I2CS_CTRLB_AACKEN; sercom->I2CS.CTRLA.reg = SERCOM_I2CS_CTRLA_MODE(I2C_SLAVE_OPERATION) | SERCOM_I2CS_CTRLA_SPEED(_wire.slaveSpeed) | (sclsm ? SERCOM_I2CS_CTRLA_SCLSM : 0 ); - sercom->I2CS.CTRLB.reg = _wire.ctrlb | SERCOM_I2CS_CTRLB_AACKEN; + sercom->I2CS.CTRLB.reg = slaveCtrlb; sercom->I2CS.ADDR.reg = _wire.addr; enableWIRE(); // Enable slave interrupts: address match, data ready, stop/restart. @@ -962,7 +1049,7 @@ void SERCOM::setSlaveWIRE(void) SERCOM_I2CS_INTENSET_PREC | // Stop SERCOM_I2CS_INTENSET_AMATCH | // Address Match SERCOM_I2CS_INTENSET_DRDY; // Data Ready -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } void SERCOM::setBaudrateWIRE(uint32_t baudrate) @@ -1000,13 +1087,66 @@ SercomTxn* SERCOM::startTransmissionWIRE( void ) { // Writing ADDR.ADDR drives different behavior based on BUSSTATE: // UNKNOWN: MB and BUSERR assert and the transfer aborts. - // BUSY: The host waits until the bus is IDLE. - // IDLE: A START is generated, the address is sent, and on ACK the host holds SCL low with CLKHOLD - // set and MB asserted. - // OWNER: A repeated START is generated; if the prior transaction was a read, the ACK/NACK for the - // read is sent before the repeated START. The repeated START ADDR write must occur while MB or SB - // is set. - // Writing ADDR also clears BUSERR, ARBLOST, MB, and SB. + // BUSY: The master waits until the bus is IDLE. + // IDLE: A START is generated, the address is sent, and on ACK the master + // holds SCL low with CLKHOLD set and MB asserted. OWNER: A repeated START is + // generated; if the prior transaction was a read, the ACK/NACK for the read + // is sent before the repeated START. The repeated START ADDR write must occur + // while MB or SB is set. Writing ADDR also clears BUSERR, ARBLOST, MB, and + // SB. + + if (isSlaveWIRE()) { + SercomTxn *txn = _wire.currentTxn; + if (txn == nullptr || txn->length == 0) + return nullptr; + const bool slaveTransmit = (txn->config & I2C_CFG_READ) != 0; + const auto clearAmatch = [this]() { +#if defined(__SAME53__) || defined(__SAME54__) + clearINTFLAG(SERCOM_I2CS_INTFLAG_AMATCH_Msk); +#else + clearINTFLAG(SERCOM_I2CS_INTFLAG_AMATCH); +#endif // __SAME53__ / __SAME54__ + }; + +#ifdef USE_ZERODMA + // Mirror the master engine selection: eligible transactions use DMA; + // larger transactions remain on the interrupt-driven DATA path. + if (isDmaWIRE()) { +#if defined(__SAME53__) || defined(__SAME54__) + disableInterrupts(SERCOM_I2CS_INTENCLR_DRDY_Msk); +#else + disableInterrupts(SERCOM_I2CS_INTENCLR_DRDY); +#endif // __SAME53__ / __SAME54__ + if (!_dmaConfigured) + dmaInit(getSercomIndex()); + if (!_dmaConfigured || !_dmaTx || !_dmaRx) + return nullptr; + + // Slave RX must release AMATCH before enabling DATA-triggered DMA. + // Slave TX must have DATA DMA armed before AMATCH releases SCL. + if (!slaveTransmit) + clearAmatch(); + const bool started = slaveTransmit ? sendDataWIRE() : readDataWIRE(); + if (!started) + return nullptr; + if (slaveTransmit) { + clearAmatch(); + } + } else { +#if defined(__SAME53__) || defined(__SAME54__) + enableInterrupts(SERCOM_I2CS_INTENSET_DRDY_Msk); +#else + enableInterrupts(SERCOM_I2CS_INTENSET_DRDY); +#endif // __SAME53__ / __SAME54__ + clearAmatch(); + } +#else + // The no-DMA build advances this transaction from slave DRDY interrupts. + setDmaWIRE(false); + clearAmatch(); +#endif // USE_ZERODMA + return txn; + } if (isBusUnknownWIRE()) { stopTransmissionWIRE(SercomWireError::BUS_STATE_UNKNOWN); @@ -1042,38 +1182,34 @@ SercomTxn* SERCOM::startTransmissionWIRE( void ) } bool hsMode = (_wire.masterSpeed == 0x2); -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) + bool sclsm = (sercom->I2CM.SERCOM_CTRLA & SERCOM_I2CM_CTRLA_SCLSM_Msk) != 0; uint32_t addrReg = SERCOM_I2CM_ADDR_ADDR(addr) | ((txn->config & I2C_CFG_10BIT) ? SERCOM_I2CM_ADDR_TENBITEN_Msk : 0) | (hsMode ? SERCOM_I2CM_ADDR_HS(1) : 0); #else + bool sclsm = sercom->I2CM.CTRLA.bit.SCLSM; uint32_t addrReg = SERCOM_I2CM_ADDR_ADDR(addr) | ((txn->config & I2C_CFG_10BIT) ? SERCOM_I2CM_ADDR_TENBITEN : 0) | (hsMode ? SERCOM_I2CM_ADDR_HS : 0); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ - if (hsMode || -#ifdef ARDUINO_SAME53_E54 - (sercom->I2CM.SERCOM_CTRLA & SERCOM_I2CM_CTRLA_SCLSM_Msk) -#else - sercom->I2CM.CTRLA.bit.SCLSM -#endif // ARDUINO_SAME53_E54 - ) { + if (hsMode || sclsm) { #ifndef USE_ZERODMA stopTransmissionWIRE(SercomWireError::OTHER); return nullptr; #endif // !USE_ZERODMA - if (txn->length >255) { + if (txn->length > 255) { stopTransmissionWIRE(SercomWireError::DATA_TOO_LONG); return nullptr; } if ( -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->I2CM.SERCOM_CTRLB & SERCOM_I2CM_CTRLB_QCEN_Msk #else sercom->I2CM.CTRLB.bit.QCEN -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ ) { stopTransmissionWIRE(SercomWireError::OTHER); return nullptr; @@ -1099,17 +1235,17 @@ SercomTxn* SERCOM::startTransmissionWIRE( void ) return nullptr; } -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) addrReg |= SERCOM_I2CM_ADDR_LENEN_Msk | SERCOM_I2CM_ADDR_LEN((uint8_t)txn->length); #else addrReg |= SERCOM_I2CM_ADDR_LENEN | SERCOM_I2CM_ADDR_LEN((uint8_t)txn->length); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } #endif // USE_ZERODMA // Send address (non-blocking; ISR handles ERROR/MB/SB) _wire.active = true; -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->I2CM.SERCOM_INTENSET = SERCOM_I2CM_INTENSET_ERROR_Msk | SERCOM_I2CM_INTENSET_SB_Msk | SERCOM_I2CM_INTENSET_MB_Msk; @@ -1117,8 +1253,7 @@ SercomTxn* SERCOM::startTransmissionWIRE( void ) #else sercom->I2CM.INTENSET.reg = SERCOM_I2CM_INTENSET_ERROR | SERCOM_I2CM_INTENSET_SB | SERCOM_I2CM_INTENSET_MB; sercom->I2CM.ADDR.reg = addrReg; // ADDR is write synchronized so just wait for the MB/SB to know when synced -#endif // ARDUINO_SAME53_E54 - +#endif // __SAME53__ / __SAME54__ return txn; } @@ -1127,6 +1262,7 @@ bool SERCOM::enqueueWIRE(SercomTxn* txn) if (txn == nullptr) return false; + if (_txnQueue.isFull()) return false; // Queue full; caller must retry at runtime @@ -1134,8 +1270,8 @@ bool SERCOM::enqueueWIRE(SercomTxn* txn) return false; if (!_wire.active) { - // dI2C uses a single SERCOM as both an addressed client and a host. A - // queued host operation is the role-transition point; callers must not + // dI2C uses a single SERCOM in both slave and master roles. A queued + // master operation is the role-transition point; callers must not // tear down and re-begin Wire around every transaction. if (!isMasterWIRE()) setMasterWIRE(); @@ -1146,6 +1282,86 @@ bool SERCOM::enqueueWIRE(SercomTxn* txn) return true; } +bool SERCOM::startNextQueuedWIRE(void) { + if (_wire.active || _wire.currentTxn != nullptr) + return false; + + SercomTxn *next = nullptr; + if (!_txnQueue.peek(next)) + return false; + + if (!isMasterWIRE()) + setMasterWIRE(); + startTransmissionWIRE(); + return true; +} + +bool SERCOM::abortWIRE(SercomWireError error) { + if (!_wire.active || _wire.currentTxn == nullptr) + return false; + + _wire.abortPending = true; + _wire.abortError = error; + setPending((uint8_t)getSercomIndex()); + return true; +} + +bool SERCOM::serviceAbortWIRE(void) { + if (!_wire.abortPending) + return false; + +#ifdef USE_ZERODMA + dmaAbortTx(); + dmaAbortRx(); + // Reject a completion callback already pending for the aborted phase. + setDmaWIRE(false); +#endif // USE_ZERODMA + + bool commandReady = false; + if (isMasterWIRE()) { +#if defined(__SAME53__) || defined(__SAME54__) + const uint32_t flags = sercom->I2CM.SERCOM_INTFLAG; + commandReady = + (flags & (SERCOM_I2CM_INTFLAG_MB_Msk | SERCOM_I2CM_INTFLAG_SB_Msk)) != + 0u; +#else + const uint8_t flags = sercom->I2CM.INTFLAG.reg; + commandReady = + (flags & (SERCOM_I2CM_INTFLAG_MB | SERCOM_I2CM_INTFLAG_SB)) != 0u; +#endif // __SAME53__ / __SAME54__ + } + + const bool busOwner = isBusOwnerWIRE(); + if (busOwner && commandReady) + prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); + else if (busOwner) { + // A timed-out owner with no MB/SB flag has no legal command point. Reapply + // the cached master configuration to release the wedged transfer before + // publishing completion. This remains inside the SERCOM event chain. + setMasterWIRE(); + _wire.releasePending = false; + } + + const SercomWireError error = _wire.abortError; + _wire.abortPending = false; + + if (isMasterWIRE()) { + +#if defined(__SAME53__) || defined(__SAME54__) + sercom->I2CM.SERCOM_INTENCLR = SERCOM_I2CM_INTENCLR_ERROR_Msk | + SERCOM_I2CM_INTENCLR_MB_Msk | + SERCOM_I2CM_INTENCLR_SB_Msk; +#else + sercom->I2CM.INTENCLR.reg = SERCOM_I2CM_INTENCLR_ERROR | + SERCOM_I2CM_INTENCLR_MB | + SERCOM_I2CM_INTENCLR_SB; +#endif // __SAME53__ / __SAME54__ + } + + deferStopWIRE(error); + return true; +} + SercomTxn* SERCOM::stopTransmissionWIRE( void ) { return stopTransmissionWIRE( _wire.returnValue ); @@ -1158,22 +1374,76 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) // Retry/backoff policy is intentionally deferred; a future change may add // a retry budget or tick-based delay if needed. - SercomTxn* txn = _wire.currentTxn; - SercomTxn* next = nullptr; + SercomTxn *txn = _wire.currentTxn; + + + // Explicit timeout cancellation is a newer entrance into the shared + // PendSV retirement service. Keep it outside the tested normal master + // completion sequence below. + if (_wire.abortPending) { + serviceAbortWIRE(); + return txn; + } + + bool isMaster = isMasterWIRE(); + + if (!isMaster) { + const bool receivePending = _wireReceivePending; + const bool requestPending = _wireRequestPending; + const bool followupPending = receivePending || requestPending; + SercomWireError completionError = error; + if (txn && error == SercomWireError::NACK_ON_DATA) { + prepareSlaveCommandBitsWIRE(WIRE_SLAVE_ACT_COMPLETE); + // A master NACK is the expected physical end of a slave response. Keep + // NACK_ON_DATA as the stop/release reason, but publish successful request + // completion after the slave has released the bus. + completionError = SercomWireError::SUCCESS; + } + + if (txn) { +#ifdef USE_ZERODMA + dmaAbortTx(); + dmaAbortRx(); +#endif // USE_ZERODMA + retireSlaveTransactionWIRE(followupPending); + if (txn->onComplete) + txn->onComplete(txn->user, static_cast(completionError)); + } + + if (receivePending) { + _wireReceivePending = false; + if (_wireReceiveCb) + _wireReceiveCb(_wireCallbackUser); + } + + if (requestPending) { + _wireRequestPending = false; + if (_wireRequestCb) + _wireRequestCb(_wireCallbackUser); + } + + if (_wire.currentTxn == nullptr) + retireSlaveTransactionWIRE(false); + + startNextQueuedWIRE(); + return _wire.currentTxn; + } + // Everything below is master-only: retry, bus-state recovery, STOP wait, + // queue retirement, transaction chaining, and return to slave mode. constexpr uint8_t kMaxWireRetries = 3; if (error == SercomWireError::BUS_STATE_UNKNOWN) { if (_wire.retryCount < kMaxWireRetries) { ++_wire.retryCount; -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->I2CM.SERCOM_STATUS = (sercom->I2CM.SERCOM_STATUS & ~SERCOM_I2CM_STATUS_BUSSTATE_Msk) | SERCOM_I2CM_STATUS_BUSSTATE(WIRE_IDLE_STATE); #else sercom->I2CM.STATUS.bit.BUSSTATE = 1; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ waitSyncBusySysOp(); startTransmissionWIRE(); @@ -1181,31 +1451,10 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) } } - if (error == SercomWireError::ARBITRATION_LOST || error == SercomWireError::BUS_ERROR) { - if (_wire.retryCount < kMaxWireRetries) { - ++_wire.retryCount; - -#ifdef ARDUINO_SAME53_E54 - sercom->I2CM.SERCOM_STATUS = SERCOM_I2CM_STATUS_ARBLOST_Msk; - sercom->I2CM.SERCOM_INTFLAG = SERCOM_I2CM_INTFLAG_ERROR_Msk; -#else - sercom->I2CM.STATUS.bit.ARBLOST = 1; // Clear arbitration lost flag - sercom->I2CM.INTFLAG.reg = SERCOM_I2CM_INTFLAG_ERROR; -#endif // ARDUINO_SAME53_E54 - startTransmissionWIRE(); - - return txn; - } - } - - if (error == SercomWireError::BUS_STATE_UNKNOWN || - error == SercomWireError::ARBITRATION_LOST || - error == SercomWireError::BUS_ERROR) { + if (error == SercomWireError::BUS_STATE_UNKNOWN) _wire.retryCount = 0; - } - if(isMasterWIRE()) - waitSyncBusySysOp(); + waitSyncBusySysOp(); // Undocumented HW limitation: DMA transfers must terminate with STOP and bus release. // After a DMA write, the host holds the bus ~7.33 us before the next transfer (Sr window). @@ -1215,50 +1464,51 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) // At the tested 48 MHz, this busy-wait is ~350 cycles corresponding to a 3.5 us delay // at 100 MHz. This wait must occur BEFORE the callback to ensure the bus is stable // before user code can enqueue the next transaction. - if (isMasterWIRE() && txn && (txn->config & I2C_CFG_STOP)) { -#ifdef ARDUINO_SAME53_E54 - while (((sercom->I2CM.SERCOM_STATUS & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> - SERCOM_I2CM_STATUS_BUSSTATE_Pos) > WIRE_IDLE_STATE) ; + if (txn && (txn->config & I2C_CFG_STOP) && + error == SercomWireError::SUCCESS) { + const uint32_t stopSettleStart = micros(); + const auto busIsIdle = [this]() { +#if defined(__SAME53__) || defined(__SAME54__) + return ((sercom->I2CM.SERCOM_STATUS & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> + SERCOM_I2CM_STATUS_BUSSTATE_Pos) <= WIRE_IDLE_STATE; #else - while (sercom->I2CM.STATUS.bit.BUSSTATE > 0x1) ; -#endif // ARDUINO_SAME53_E54 + return sercom->I2CM.STATUS.bit.BUSSTATE <= WIRE_IDLE_STATE; +#endif // __SAME53__ / __SAME54__ + }; + + while (!busIsIdle() && + static_cast(micros() - stopSettleStart) < 4u) + ; + + if (!busIsIdle()) + prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); } - // Callbacks are expected to run in non-ISR context (main loop/PendSV). + // Preserve the tested async-DMA master retirement order: publish the + // callback before inspecting chainNext, dequeuing, or restoring slave mode. if (txn && txn->onComplete) txn->onComplete(txn->user, static_cast(error)); // Allow multi-phase I2C transactions to chain without dequeuing. - if (isMasterWIRE() && txn && txn->chainNext) { + if (txn && txn->chainNext) { txn->chainNext = false; // reset for next iteration startTransmissionWIRE(); return txn; } - bool dequeued = false; SercomTxn* headTxn = nullptr; if (_txnQueue.peek(headTxn) && headTxn == txn) - dequeued = _txnQueue.read(txn); // remove completed master txn when it is queue head - - if (!dequeued) { - // Deliver deferred WIRE callback outside the SERCOM ISR (from PendSV). - // This avoids running user code in the hardware interrupt context. - if (_wireDeferredPending && _wireDeferredCb) { - _wireDeferredPending = false; - _wireDeferredCb(_wireDeferredUser, _wireDeferredLength); - } - } + _txnQueue.read(txn); // remove completed master queue head _wire.retryCount = 0; _wire.active = false; _wire.currentTxn = nullptr; - bool isMaster = isMasterWIRE(); - - if (_txnQueue.peek(next) && isMaster) + SercomTxn *next = nullptr; + if (_txnQueue.peek(next)) startTransmissionWIRE(); - else if (isMaster) { -#ifdef ARDUINO_SAME53_E54 + else { +#if defined(__SAME53__) || defined(__SAME54__) sercom->I2CM.SERCOM_INTENCLR = SERCOM_I2CM_INTENCLR_ERROR_Msk | SERCOM_I2CM_INTENCLR_MB_Msk | SERCOM_I2CM_INTENCLR_SB_Msk; @@ -1266,7 +1516,8 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) sercom->I2CM.INTENCLR.reg = SERCOM_I2CM_INTENCLR_ERROR | SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ + if (_wire.slaveConfigured) setSlaveWIRE(); } @@ -1276,102 +1527,110 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) // Hardware metadata structure for SERCOM peripherals - private to this file struct SercomData { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom_registers_t *sercomPtr; #else Sercom *sercomPtr; -#endif // ARDUINO_SAME53_E54 -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#endif // __SAME53__ / __SAME54__ +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || \ + defined(__SAME54__) uint8_t id_core; uint8_t id_slow; IRQn_Type irq[4]; #else uint8_t clock; IRQn_Type irqn; -#endif // ARDUINO_SAMD51_E51 || ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ || __SAME53__ / __SAME54__ uint8_t dmaTxTrigger; uint8_t dmaRxTrigger; void *dataReg; // Pointer to DATA register }; static const SercomData sercomData[] = { -#ifdef ARDUINO_SAMD51_E51 - { SERCOM0, SERCOM0_GCLK_ID_CORE, SERCOM0_GCLK_ID_SLOW, - SERCOM0_0_IRQn, SERCOM0_1_IRQn, SERCOM0_2_IRQn, SERCOM0_3_IRQn, - SERCOM0_DMAC_ID_TX, SERCOM0_DMAC_ID_RX, (void*)&SERCOM0->I2CM.DATA.reg }, - { SERCOM1, SERCOM1_GCLK_ID_CORE, SERCOM1_GCLK_ID_SLOW, - SERCOM1_0_IRQn, SERCOM1_1_IRQn, SERCOM1_2_IRQn, SERCOM1_3_IRQn, - SERCOM1_DMAC_ID_TX, SERCOM1_DMAC_ID_RX, (void*)&SERCOM1->I2CM.DATA.reg }, - { SERCOM2, SERCOM2_GCLK_ID_CORE, SERCOM2_GCLK_ID_SLOW, - SERCOM2_0_IRQn, SERCOM2_1_IRQn, SERCOM2_2_IRQn, SERCOM2_3_IRQn, - SERCOM2_DMAC_ID_TX, SERCOM2_DMAC_ID_RX, (void*)&SERCOM2->I2CM.DATA.reg }, - { SERCOM3, SERCOM3_GCLK_ID_CORE, SERCOM3_GCLK_ID_SLOW, - SERCOM3_0_IRQn, SERCOM3_1_IRQn, SERCOM3_2_IRQn, SERCOM3_3_IRQn, - SERCOM3_DMAC_ID_TX, SERCOM3_DMAC_ID_RX, (void*)&SERCOM3->I2CM.DATA.reg }, - { SERCOM4, SERCOM4_GCLK_ID_CORE, SERCOM4_GCLK_ID_SLOW, - SERCOM4_0_IRQn, SERCOM4_1_IRQn, SERCOM4_2_IRQn, SERCOM4_3_IRQn, - SERCOM4_DMAC_ID_TX, SERCOM4_DMAC_ID_RX, (void*)&SERCOM4->I2CM.DATA.reg }, - { SERCOM5, SERCOM5_GCLK_ID_CORE, SERCOM5_GCLK_ID_SLOW, - SERCOM5_0_IRQn, SERCOM5_1_IRQn, SERCOM5_2_IRQn, SERCOM5_3_IRQn, - SERCOM5_DMAC_ID_TX, SERCOM5_DMAC_ID_RX, (void*)&SERCOM5->I2CM.DATA.reg }, +#if defined(__SAMD51__) || defined(__SAME51__) + {SERCOM0, SERCOM0_GCLK_ID_CORE, SERCOM0_GCLK_ID_SLOW, SERCOM0_0_IRQn, + SERCOM0_1_IRQn, SERCOM0_2_IRQn, SERCOM0_3_IRQn, SERCOM0_DMAC_ID_TX, + SERCOM0_DMAC_ID_RX, (void *)&SERCOM0->I2CM.DATA.reg}, + {SERCOM1, SERCOM1_GCLK_ID_CORE, SERCOM1_GCLK_ID_SLOW, SERCOM1_0_IRQn, + SERCOM1_1_IRQn, SERCOM1_2_IRQn, SERCOM1_3_IRQn, SERCOM1_DMAC_ID_TX, + SERCOM1_DMAC_ID_RX, (void *)&SERCOM1->I2CM.DATA.reg}, + {SERCOM2, SERCOM2_GCLK_ID_CORE, SERCOM2_GCLK_ID_SLOW, SERCOM2_0_IRQn, + SERCOM2_1_IRQn, SERCOM2_2_IRQn, SERCOM2_3_IRQn, SERCOM2_DMAC_ID_TX, + SERCOM2_DMAC_ID_RX, (void *)&SERCOM2->I2CM.DATA.reg}, + {SERCOM3, SERCOM3_GCLK_ID_CORE, SERCOM3_GCLK_ID_SLOW, SERCOM3_0_IRQn, + SERCOM3_1_IRQn, SERCOM3_2_IRQn, SERCOM3_3_IRQn, SERCOM3_DMAC_ID_TX, + SERCOM3_DMAC_ID_RX, (void *)&SERCOM3->I2CM.DATA.reg}, + {SERCOM4, SERCOM4_GCLK_ID_CORE, SERCOM4_GCLK_ID_SLOW, SERCOM4_0_IRQn, + SERCOM4_1_IRQn, SERCOM4_2_IRQn, SERCOM4_3_IRQn, SERCOM4_DMAC_ID_TX, + SERCOM4_DMAC_ID_RX, (void *)&SERCOM4->I2CM.DATA.reg}, + {SERCOM5, SERCOM5_GCLK_ID_CORE, SERCOM5_GCLK_ID_SLOW, SERCOM5_0_IRQn, + SERCOM5_1_IRQn, SERCOM5_2_IRQn, SERCOM5_3_IRQn, SERCOM5_DMAC_ID_TX, + SERCOM5_DMAC_ID_RX, (void *)&SERCOM5->I2CM.DATA.reg}, #if defined(SERCOM6) - { SERCOM6, SERCOM6_GCLK_ID_CORE, SERCOM6_GCLK_ID_SLOW, - SERCOM6_0_IRQn, SERCOM6_1_IRQn, SERCOM6_2_IRQn, SERCOM6_3_IRQn, - SERCOM6_DMAC_ID_TX, SERCOM6_DMAC_ID_RX, (void*)&SERCOM6->I2CM.DATA.reg }, + {SERCOM6, SERCOM6_GCLK_ID_CORE, SERCOM6_GCLK_ID_SLOW, SERCOM6_0_IRQn, + SERCOM6_1_IRQn, SERCOM6_2_IRQn, SERCOM6_3_IRQn, SERCOM6_DMAC_ID_TX, + SERCOM6_DMAC_ID_RX, (void *)&SERCOM6->I2CM.DATA.reg}, #endif // SERCOM6 #if defined(SERCOM7) - { SERCOM7, SERCOM7_GCLK_ID_CORE, SERCOM7_GCLK_ID_SLOW, - SERCOM7_0_IRQn, SERCOM7_1_IRQn, SERCOM7_2_IRQn, SERCOM7_3_IRQn, - SERCOM7_DMAC_ID_TX, SERCOM7_DMAC_ID_RX, (void*)&SERCOM7->I2CM.DATA.reg }, + {SERCOM7, SERCOM7_GCLK_ID_CORE, SERCOM7_GCLK_ID_SLOW, SERCOM7_0_IRQn, + SERCOM7_1_IRQn, SERCOM7_2_IRQn, SERCOM7_3_IRQn, SERCOM7_DMAC_ID_TX, + SERCOM7_DMAC_ID_RX, (void *)&SERCOM7->I2CM.DATA.reg}, #endif // SERCOM7 -#elif defined(ARDUINO_SAME53_E54) - { SERCOM0_REGS, SERCOM0_GCLK_ID_CORE, SERCOM0_GCLK_ID_SLOW, - SERCOM0_0_IRQn, SERCOM0_1_IRQn, SERCOM0_2_IRQn, SERCOM0_OTHER_IRQn, - SERCOM0_DMAC_ID_TX, SERCOM0_DMAC_ID_RX, (void*)&SERCOM0_REGS->I2CM.SERCOM_DATA }, - { SERCOM1_REGS, SERCOM1_GCLK_ID_CORE, SERCOM1_GCLK_ID_SLOW, - SERCOM1_0_IRQn, SERCOM1_1_IRQn, SERCOM1_2_IRQn, SERCOM1_OTHER_IRQn, - SERCOM1_DMAC_ID_TX, SERCOM1_DMAC_ID_RX, (void*)&SERCOM1_REGS->I2CM.SERCOM_DATA }, - { SERCOM2_REGS, SERCOM2_GCLK_ID_CORE, SERCOM2_GCLK_ID_SLOW, - SERCOM2_0_IRQn, SERCOM2_1_IRQn, SERCOM2_2_IRQn, SERCOM2_OTHER_IRQn, - SERCOM2_DMAC_ID_TX, SERCOM2_DMAC_ID_RX, (void*)&SERCOM2_REGS->I2CM.SERCOM_DATA }, - { SERCOM3_REGS, SERCOM3_GCLK_ID_CORE, SERCOM3_GCLK_ID_SLOW, - SERCOM3_0_IRQn, SERCOM3_1_IRQn, SERCOM3_2_IRQn, SERCOM3_OTHER_IRQn, - SERCOM3_DMAC_ID_TX, SERCOM3_DMAC_ID_RX, (void*)&SERCOM3_REGS->I2CM.SERCOM_DATA }, - { SERCOM4_REGS, SERCOM4_GCLK_ID_CORE, SERCOM4_GCLK_ID_SLOW, - SERCOM4_0_IRQn, SERCOM4_1_IRQn, SERCOM4_2_IRQn, SERCOM4_OTHER_IRQn, - SERCOM4_DMAC_ID_TX, SERCOM4_DMAC_ID_RX, (void*)&SERCOM4_REGS->I2CM.SERCOM_DATA }, - { SERCOM5_REGS, SERCOM5_GCLK_ID_CORE, SERCOM5_GCLK_ID_SLOW, - SERCOM5_0_IRQn, SERCOM5_1_IRQn, SERCOM5_2_IRQn, SERCOM5_OTHER_IRQn, - SERCOM5_DMAC_ID_TX, SERCOM5_DMAC_ID_RX, (void*)&SERCOM5_REGS->I2CM.SERCOM_DATA }, +#elif defined(__SAME53__) || defined(__SAME54__) + {SERCOM0_REGS, SERCOM0_GCLK_ID_CORE, SERCOM0_GCLK_ID_SLOW, SERCOM0_0_IRQn, + SERCOM0_1_IRQn, SERCOM0_2_IRQn, SERCOM0_OTHER_IRQn, SERCOM0_DMAC_ID_TX, + SERCOM0_DMAC_ID_RX, (void *)&SERCOM0_REGS->I2CM.SERCOM_DATA}, + {SERCOM1_REGS, SERCOM1_GCLK_ID_CORE, SERCOM1_GCLK_ID_SLOW, SERCOM1_0_IRQn, + SERCOM1_1_IRQn, SERCOM1_2_IRQn, SERCOM1_OTHER_IRQn, SERCOM1_DMAC_ID_TX, + SERCOM1_DMAC_ID_RX, (void *)&SERCOM1_REGS->I2CM.SERCOM_DATA}, + {SERCOM2_REGS, SERCOM2_GCLK_ID_CORE, SERCOM2_GCLK_ID_SLOW, SERCOM2_0_IRQn, + SERCOM2_1_IRQn, SERCOM2_2_IRQn, SERCOM2_OTHER_IRQn, SERCOM2_DMAC_ID_TX, + SERCOM2_DMAC_ID_RX, (void *)&SERCOM2_REGS->I2CM.SERCOM_DATA}, + {SERCOM3_REGS, SERCOM3_GCLK_ID_CORE, SERCOM3_GCLK_ID_SLOW, SERCOM3_0_IRQn, + SERCOM3_1_IRQn, SERCOM3_2_IRQn, SERCOM3_OTHER_IRQn, SERCOM3_DMAC_ID_TX, + SERCOM3_DMAC_ID_RX, (void *)&SERCOM3_REGS->I2CM.SERCOM_DATA}, + {SERCOM4_REGS, SERCOM4_GCLK_ID_CORE, SERCOM4_GCLK_ID_SLOW, SERCOM4_0_IRQn, + SERCOM4_1_IRQn, SERCOM4_2_IRQn, SERCOM4_OTHER_IRQn, SERCOM4_DMAC_ID_TX, + SERCOM4_DMAC_ID_RX, (void *)&SERCOM4_REGS->I2CM.SERCOM_DATA}, + {SERCOM5_REGS, SERCOM5_GCLK_ID_CORE, SERCOM5_GCLK_ID_SLOW, SERCOM5_0_IRQn, + SERCOM5_1_IRQn, SERCOM5_2_IRQn, SERCOM5_OTHER_IRQn, SERCOM5_DMAC_ID_TX, + SERCOM5_DMAC_ID_RX, (void *)&SERCOM5_REGS->I2CM.SERCOM_DATA}, #if defined(SERCOM6_REGS) - { SERCOM6_REGS, SERCOM6_GCLK_ID_CORE, SERCOM6_GCLK_ID_SLOW, - SERCOM6_0_IRQn, SERCOM6_1_IRQn, SERCOM6_2_IRQn, SERCOM6_OTHER_IRQn, - SERCOM6_DMAC_ID_TX, SERCOM6_DMAC_ID_RX, (void*)&SERCOM6_REGS->I2CM.SERCOM_DATA }, + {SERCOM6_REGS, SERCOM6_GCLK_ID_CORE, SERCOM6_GCLK_ID_SLOW, SERCOM6_0_IRQn, + SERCOM6_1_IRQn, SERCOM6_2_IRQn, SERCOM6_OTHER_IRQn, SERCOM6_DMAC_ID_TX, + SERCOM6_DMAC_ID_RX, (void *)&SERCOM6_REGS->I2CM.SERCOM_DATA}, #endif // SERCOM6_REGS #if defined(SERCOM7_REGS) - { SERCOM7_REGS, SERCOM7_GCLK_ID_CORE, SERCOM7_GCLK_ID_SLOW, - SERCOM7_0_IRQn, SERCOM7_1_IRQn, SERCOM7_2_IRQn, SERCOM7_OTHER_IRQn, - SERCOM7_DMAC_ID_TX, SERCOM7_DMAC_ID_RX, (void*)&SERCOM7_REGS->I2CM.SERCOM_DATA }, + {SERCOM7_REGS, SERCOM7_GCLK_ID_CORE, SERCOM7_GCLK_ID_SLOW, SERCOM7_0_IRQn, + SERCOM7_1_IRQn, SERCOM7_2_IRQn, SERCOM7_OTHER_IRQn, SERCOM7_DMAC_ID_TX, + SERCOM7_DMAC_ID_RX, (void *)&SERCOM7_REGS->I2CM.SERCOM_DATA}, #endif // SERCOM7_REGS -#else // SAMD21 -// SAMD21 has unified clock and single interrupt - { SERCOM0, GCM_SERCOM0_CORE, SERCOM0_IRQn, SERCOM0_DMAC_ID_TX, SERCOM0_DMAC_ID_RX, (void*)&SERCOM0->I2CM.DATA.reg }, - { SERCOM1, GCM_SERCOM1_CORE, SERCOM1_IRQn, SERCOM1_DMAC_ID_TX, SERCOM1_DMAC_ID_RX, (void*)&SERCOM1->I2CM.DATA.reg }, - { SERCOM2, GCM_SERCOM2_CORE, SERCOM2_IRQn, SERCOM2_DMAC_ID_TX, SERCOM2_DMAC_ID_RX, (void*)&SERCOM2->I2CM.DATA.reg }, - { SERCOM3, GCM_SERCOM3_CORE, SERCOM3_IRQn, SERCOM3_DMAC_ID_TX, SERCOM3_DMAC_ID_RX, (void*)&SERCOM3->I2CM.DATA.reg }, +#else // SAMD21 + // SAMD21 has unified clock and single interrupt + {SERCOM0, GCM_SERCOM0_CORE, SERCOM0_IRQn, SERCOM0_DMAC_ID_TX, + SERCOM0_DMAC_ID_RX, (void *)&SERCOM0->I2CM.DATA.reg}, + {SERCOM1, GCM_SERCOM1_CORE, SERCOM1_IRQn, SERCOM1_DMAC_ID_TX, + SERCOM1_DMAC_ID_RX, (void *)&SERCOM1->I2CM.DATA.reg}, + {SERCOM2, GCM_SERCOM2_CORE, SERCOM2_IRQn, SERCOM2_DMAC_ID_TX, + SERCOM2_DMAC_ID_RX, (void *)&SERCOM2->I2CM.DATA.reg}, + {SERCOM3, GCM_SERCOM3_CORE, SERCOM3_IRQn, SERCOM3_DMAC_ID_TX, + SERCOM3_DMAC_ID_RX, (void *)&SERCOM3->I2CM.DATA.reg}, #if defined(SERCOM4) - { SERCOM4, GCM_SERCOM4_CORE, SERCOM4_IRQn, SERCOM4_DMAC_ID_TX, SERCOM4_DMAC_ID_RX, (void*)&SERCOM4->I2CM.DATA.reg }, + {SERCOM4, GCM_SERCOM4_CORE, SERCOM4_IRQn, SERCOM4_DMAC_ID_TX, + SERCOM4_DMAC_ID_RX, (void *)&SERCOM4->I2CM.DATA.reg}, #endif // SERCOM4 #if defined(SERCOM5) - { SERCOM5, GCM_SERCOM5_CORE, SERCOM5_IRQn, SERCOM5_DMAC_ID_TX, SERCOM5_DMAC_ID_RX, (void*)&SERCOM5->I2CM.DATA.reg }, + {SERCOM5, GCM_SERCOM5_CORE, SERCOM5_IRQn, SERCOM5_DMAC_ID_TX, + SERCOM5_DMAC_ID_RX, (void *)&SERCOM5->I2CM.DATA.reg}, #endif // SERCOM5 -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 / SAMD21 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ / SAMD21 }; std::array SERCOM::s_states = {}; std::array SERCOM::s_instances = {}; volatile uint32_t SERCOM::s_pendingMask = 0; + bool SERCOM::claim(uint8_t sercomId, Role role) { if (sercomId >= kSercomCount) @@ -1393,7 +1652,7 @@ void SERCOM::release(uint8_t sercomId) SercomState &state = s_states[sercomId]; state.role = Role::None; state.service = nullptr; - PendSV::instance().clearService(sercomId); + PendSV::instance().clearService(PendSVChannels::sercom(sercomId)); #ifdef SERCOM_STRICT_PADS clearPads(sercomId); #endif // SERCOM_STRICT_PADS @@ -1405,7 +1664,8 @@ bool SERCOM::registerService(uint8_t sercomId, ServiceFn fn) return false; s_states[sercomId].service = fn; - if (!PendSV::instance().registerService(sercomId, &SERCOM::dispatchService, nullptr)) + if (!PendSV::instance().registerService(PendSVChannels::sercom(sercomId), + &SERCOM::dispatchService, nullptr)) { s_states[sercomId].service = nullptr; return false; @@ -1441,11 +1701,11 @@ SERCOM::DmaStatus SERCOM::dmaInit(int8_t sercomId, uint8_t beatSize) // DATA register is at the same offset (0x28) for all protocols (I2C, SPI, UART). // Access via any union member is transparent—just use I2CM as the canonical reference. -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) void* dataReg = (void*)&sercom->I2CM.SERCOM_DATA; #else void* dataReg = (void*)&sercom->I2CM.DATA.reg; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ if (!_dmaTx) _dmaTx = new Adafruit_ZeroDMA(); @@ -1480,6 +1740,10 @@ SERCOM::DmaStatus SERCOM::dmaInit(int8_t sercomId, uint8_t beatSize) _dmaTx->setCallback(_dmaTxCb); if (_dmaRxCb) _dmaRx->setCallback(_dmaRxCb); + if (_dmaErrorCb) { + _dmaTx->setCallback(_dmaErrorCb, DMA_CALLBACK_TRANSFER_ERROR); + _dmaRx->setCallback(_dmaErrorCb, DMA_CALLBACK_TRANSFER_ERROR); + } if (_dmaTxDesc == nullptr) _dmaTxDesc = _dmaTx->addDescriptor(&_dmaDummy, dataReg, 0, (dma_beat_size)beatSize, true, false); @@ -1497,10 +1761,11 @@ SERCOM::DmaStatus SERCOM::dmaInit(int8_t sercomId, uint8_t beatSize) return _dmaLastError; } -void SERCOM::dmaSetCallbacks(DmaCallback txCb, DmaCallback rxCb) -{ +void SERCOM::dmaSetCallbacks(DmaCallback txCb, DmaCallback rxCb, + DmaCallback errorCb) { _dmaTxCb = txCb; _dmaRxCb = rxCb; + _dmaErrorCb = errorCb; if (_dmaConfigured) { @@ -1508,6 +1773,8 @@ void SERCOM::dmaSetCallbacks(DmaCallback txCb, DmaCallback rxCb) _dmaTx->setCallback(_dmaTxCb); if (_dmaRxCb) _dmaRx->setCallback(_dmaRxCb); + _dmaTx->setCallback(_dmaErrorCb, DMA_CALLBACK_TRANSFER_ERROR); + _dmaRx->setCallback(_dmaErrorCb, DMA_CALLBACK_TRANSFER_ERROR); } } @@ -1733,20 +2000,23 @@ void SERCOM::setPending(uint8_t sercomId) if (sercomId >= kSercomCount) return; - PendSV::instance().setPending(sercomId); + PendSV::instance().setPending(PendSVChannels::sercom(sercomId)); } void SERCOM::dispatchService(uint8_t sercomId, void *context) { (void)context; + sercomId = PendSVChannels::sercomIndex(sercomId); + if (sercomId >= kSercomCount) return; ServiceFn fn = s_states[sercomId].service; SERCOM* inst = s_instances[sercomId]; - if (fn && inst) + if (fn && inst) { (inst->*fn)(); + } } void SERCOM::dispatchPending(void) @@ -1764,19 +2034,20 @@ int8_t SERCOM::getSercomIndex(void) { uint32_t SERCOM::getSercomFreqRef(void) { -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || \ + defined(__SAME54__) int8_t idx = getSercomIndex(); uint8_t gen = 1; // default to GCLK1 (48 MHz) if we can't resolve if (idx >= 0) { uint8_t pch = sercomData[idx].id_core; -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) gen = (GCLK_REGS->GCLK_PCHCTRL[pch] & GCLK_PCHCTRL_GEN_Msk) >> GCLK_PCHCTRL_GEN_Pos; #else gen = GCLK->PCHCTRL[pch].bit.GEN; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } switch (gen) @@ -1802,12 +2073,13 @@ uint32_t SERCOM::getSercomFreqRef(void) } #else freqRef = SystemCoreClock; -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ return freqRef; } -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || \ + defined(__SAME54__) // This is currently for overriding an SPI SERCOM's clock source only -- // NOT for UART or WIRE SERCOMs, where it will have unintended consequences. // It does not check. @@ -1820,13 +2092,13 @@ void SERCOM::setClockSource(int8_t idx, SercomClockSource src, bool core) { uint8_t clk_id = core ? sercomData[idx].id_core : sercomData[idx].id_slow; -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) GCLK_REGS->GCLK_PCHCTRL[clk_id] &= ~GCLK_PCHCTRL_CHEN_Msk; while (GCLK_REGS->GCLK_PCHCTRL[clk_id] & GCLK_PCHCTRL_CHEN_Msk); #else GCLK->PCHCTRL[clk_id].bit.CHEN = 0; // Disable timer while(GCLK->PCHCTRL[clk_id].bit.CHEN); // Wait for disable -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ if(core) clockSource = src; // Save SercomClockSource value @@ -1837,69 +2109,70 @@ void SERCOM::setClockSource(int8_t idx, SercomClockSource src, bool core) { // GCLK3 = XOSC32K // GCLK4 = 12 MHz if(src == SERCOM_CLOCK_SOURCE_FCPU) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) GCLK_REGS->GCLK_PCHCTRL[clk_id] = GCLK_PCHCTRL_GEN_GCLK2 | GCLK_PCHCTRL_CHEN_Msk; #else GCLK->PCHCTRL[clk_id].reg = GCLK_PCHCTRL_GEN_GCLK2_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); // Guard Sercom from exceeding 100 MHz maximum -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ if (core) freqRef = 100000000; // Save clock frequency value } else if (src == SERCOM_CLOCK_SOURCE_48M) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) GCLK_REGS->GCLK_PCHCTRL[clk_id] = GCLK_PCHCTRL_GEN_GCLK1 | GCLK_PCHCTRL_CHEN_Msk; #else GCLK->PCHCTRL[clk_id].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ if(core) freqRef = 48000000; } else if(src == SERCOM_CLOCK_SOURCE_100M) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) GCLK_REGS->GCLK_PCHCTRL[clk_id] = GCLK_PCHCTRL_GEN_GCLK2 | GCLK_PCHCTRL_CHEN_Msk; #else GCLK->PCHCTRL[clk_id].reg = GCLK_PCHCTRL_GEN_GCLK2_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ if(core) freqRef = 100000000; } else if(src == SERCOM_CLOCK_SOURCE_32K) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) GCLK_REGS->GCLK_PCHCTRL[clk_id] = GCLK_PCHCTRL_GEN_GCLK3 | GCLK_PCHCTRL_CHEN_Msk; #else GCLK->PCHCTRL[clk_id].reg = GCLK_PCHCTRL_GEN_GCLK3_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ if(core) freqRef = 32768; } else if(src == SERCOM_CLOCK_SOURCE_12M) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) GCLK_REGS->GCLK_PCHCTRL[clk_id] = GCLK_PCHCTRL_GEN_GCLK4 | GCLK_PCHCTRL_CHEN_Msk; #else GCLK->PCHCTRL[clk_id].reg = GCLK_PCHCTRL_GEN_GCLK4_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ if(core) freqRef = 12000000; } -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) while ((GCLK_REGS->GCLK_PCHCTRL[clk_id] & GCLK_PCHCTRL_CHEN_Msk) == 0); #else while(!GCLK->PCHCTRL[clk_id].bit.CHEN); // Wait for clock enable -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ void SERCOM::initClockNVIC( void ) { int8_t idx = getSercomIndex(); if(idx < 0) return; // We got a problem here -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || \ + defined(__SAME54__) for(uint8_t i=0; i<4; i++) { NVIC_ClearPendingIRQ(sercomData[idx].irq[i]); @@ -1907,9 +2180,10 @@ void SERCOM::initClockNVIC( void ) NVIC_EnableIRQ(sercomData[idx].irq[i]); } - setClockSource(idx, clockSource, true); // true = core clock + setClockSource(idx, clockSource, true); // true = core clock + setClockSource(idx, SERCOM_CLOCK_SOURCE_32K, false); // false = slow clock -#else // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#else // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ uint8_t clockId = sercomData[idx].clock; IRQn_Type IdNvic = sercomData[idx].irqn; @@ -1927,7 +2201,7 @@ void SERCOM::initClockNVIC( void ) while(GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); // Wait for synchronization -#endif // ARDUINO_SAMD51_E51 || ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ || __SAME53__ / __SAME54__ getSercomFreqRef(); } diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index bf751830f..ba137c901 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -19,13 +19,16 @@ #ifndef _SERCOM_CLASS_ #define _SERCOM_CLASS_ -#include "sam.h" +#include #include "SERCOM_PinMux.h" #include "SERCOM_Txn.h" #include "RingBuffer.h" #include #ifdef USE_ZERODMA +#if defined(__SAME53__) || defined(__SAME54__) +typedef dmac_descriptor_registers_t DmacDescriptor; +#endif // __SAME53__ / __SAME54__ class Adafruit_ZeroDMA; #endif // USE_ZERODMA @@ -158,6 +161,12 @@ typedef enum WIRE_MASTER_ACT_STOP } SercomMasterCommandWire; +typedef enum +{ + WIRE_SLAVE_ACT_NO_ACTION = 0, + WIRE_SLAVE_ACT_COMPLETE = 2 +} SercomSlaveCommandWire; + typedef enum { WIRE_MASTER_ACK_ACTION = 0, @@ -180,11 +189,11 @@ typedef enum { class SERCOM { public: -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) SERCOM(sercom_registers_t* s) ; #else SERCOM(Sercom* s) ; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ void resetSERCOM( void ) ; inline void enableSERCOM( void ) ; inline void disableSERCOM( void ) ; @@ -192,19 +201,19 @@ class SERCOM inline void waitSyncBusySwrst( void ) ; inline void waitSyncBusySysOp( void ) ; inline void waitSyncBusyCtrlB( void ) ; -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) inline void disableInterrupts(uint8_t mask) { sercom->I2CM.SERCOM_INTENCLR = mask; } inline void enableInterrupts(uint8_t mask) { sercom->I2CM.SERCOM_INTENSET = mask; } inline uint8_t getINTFLAG( void ) const { return sercom->I2CM.SERCOM_INTFLAG; } inline uint16_t getSTATUS( void ) const { return sercom->I2CM.SERCOM_STATUS; } - inline void clearINTFLAG( void ) { sercom->I2CM.SERCOM_INTFLAG = 0xFF; } + inline void clearINTFLAG(uint8_t mask = 0xFF) { sercom->I2CM.SERCOM_INTFLAG = mask; } #else inline void disableInterrupts(uint8_t mask) { sercom->I2CM.INTENCLR.reg = mask; } inline void enableInterrupts(uint8_t mask) { sercom->I2CM.INTENSET.reg = mask; } inline uint8_t getINTFLAG( void ) const { return sercom->I2CM.INTFLAG.reg; } inline uint16_t getSTATUS( void ) const { return sercom->I2CM.STATUS.reg; } - inline void clearINTFLAG( void ) { sercom->I2CM.INTFLAG.reg = 0xFF; } -#endif // ARDUINO_SAME53_E54 + inline void clearINTFLAG(uint8_t mask = 0xFF) { sercom->I2CM.INTFLAG.reg = mask; } +#endif // __SAME53__ / __SAME54__ /* ========== UART ========== */ void initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint32_t baudrate=0) ; @@ -273,8 +282,15 @@ class SERCOM inline void setTxnWIRE(SercomTxn* txn); inline void setDmaWIRE(bool useDma) { _wire.useDma = useDma; } inline bool isDmaWIRE(void) const { return _wire.useDma; } - void registerReceiveWIRE(void (*cb)(void* user, int length), void* user); - void deferReceiveWIRE(int length); + inline void markBusReleasePendingWIRE(void) { _wire.releasePending = true; } + using WireReceiveCallback = void (*)(void* user); + using WireRequestCallback = void (*)(void* user); + void registerReceiveWIRE(WireReceiveCallback callback, void* user); + void registerRequestWIRE(WireRequestCallback callback, void* user); + void deferReceiveWIRE(void); + void deferRequestWIRE(void); + void deferReceiveCompleteWIRE(void); + SercomTxn* retireSlaveTransactionWIRE(bool reserveForFollowup); void setSlaveWIRE( void ) ; void setMasterWIRE( void ) ; @@ -286,11 +302,16 @@ class SERCOM inline void prepareNackBitWIRE( void ) ; inline void prepareAckBitWIRE( void ) ; inline void prepareCommandBitsWIRE(uint8_t cmd) ; + inline void prepareSlaveCommandBitsWIRE(uint8_t cmd); SercomTxn* startTransmissionWIRE( void ) ; bool startTransmissionWIRE( uint8_t address, SercomWireReadWriteFlag flag ) = delete ; SercomTxn* stopTransmissionWIRE( void ) ; SercomTxn* stopTransmissionWIRE( SercomWireError error ) ; bool enqueueWIRE(SercomTxn* txn); + inline bool canEnqueueWIRE(void) { return !_txnQueue.isFull(); } + bool abortWIRE(SercomWireError error = SercomWireError::MASTER_TIMEOUT); + inline bool isAbortPendingWIRE(void) const { return _wire.abortPending; } + bool serviceAbortWIRE(void); void deferStopWIRE(SercomWireError error); inline bool sendDataWIRE( void ) ; @@ -317,15 +338,15 @@ class SERCOM inline bool isDBGSTOP( void ) const; inline void setDBGSTOP( bool stop ); -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) inline sercom_registers_t* getSercom() const { return sercom; } #else inline Sercom* getSercom() const { return sercom; } -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ int8_t getSercomIndex(void) ; uint32_t getSercomFreqRef(void) ; -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) // SERCOM clock source override is only available on // SAMD51 (not 21) ... but these functions are declared // regardless so user code doesn't need ifdefs or lengthy @@ -351,6 +372,7 @@ class SERCOM static void dispatchService(uint8_t sercomId, void *context); static void dispatchPending(void); + #ifdef USE_ZERODMA using DmaCallback = void (*)(Adafruit_ZeroDMA*); enum class DmaStatus : uint8_t { @@ -364,7 +386,8 @@ class SERCOM }; DmaStatus dmaInit(int8_t sercomId, uint8_t beatSize = 0); // beatSize: 0=byte (default), 1=halfword, 2=word - void dmaSetCallbacks(DmaCallback txCb, DmaCallback rxCb); + void dmaSetCallbacks(DmaCallback txCb, DmaCallback rxCb, + DmaCallback errorCb = nullptr); DmaStatus dmaStartTx(const void* src, volatile void* dstReg, size_t len); DmaStatus dmaStartRx(void* dst, volatile void* srcReg, size_t len); DmaStatus dmaStartDuplex(const void* txSrc, void* rxDst, volatile void* txReg, volatile void* rxReg, size_t len, @@ -382,6 +405,8 @@ class SERCOM // --- WIRE DMA callbacks (ISR-safe, PendSV-only completion) --- static inline void dmaTxCallbackWIRE(Adafruit_ZeroDMA* dma); static inline void dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma); + static void dmaRxSuspendCallbackWIRE(Adafruit_ZeroDMA* dma); + static inline void dmaErrorCallbackWIRE(Adafruit_ZeroDMA* dma); // --- SPI DMA callbacks (protocol-owned) --- static inline void dmaTxCallbackSPI(Adafruit_ZeroDMA* dma); static inline void dmaRxCallbackSPI(Adafruit_ZeroDMA* dma); @@ -408,26 +433,27 @@ class SERCOM #endif // SERCOM_STRICT_PADS private: -#ifdef ARDUINO_SAME53_E54 + bool startNextQueuedWIRE(void); +#if defined(__SAME53__) || defined(__SAME54__) sercom_registers_t *sercom; #else Sercom *sercom; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ uint32_t freqRef = 48000000ul; // Frequency corresponding to clockSource -#if defined(ARDUINO_SAMD51_E51) || defined(ARDUINO_SAME53_E54) +#if defined(__SAMD51__) || defined(__SAME51__) || defined(__SAME53__) || defined(__SAME54__) SercomClockSource clockSource; -#endif // ARDUINO_SAMD51_E51 || ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ || __SAME53__ / __SAME54__ #if defined(SERCOM_INST_NUM) && (SERCOM_INST_NUM > 0) static constexpr size_t kSercomCount = SERCOM_INST_NUM; -#elif defined(ARDUINO_SAME53_E54) && defined(SERCOM7_REGS) +#elif (defined(__SAME53__) || defined(__SAME54__)) && defined(SERCOM7_REGS) static constexpr size_t kSercomCount = 8; -#elif defined(ARDUINO_SAME53_E54) && defined(SERCOM5_REGS) +#elif (defined(__SAME53__) || defined(__SAME54__)) && defined(SERCOM5_REGS) static constexpr size_t kSercomCount = 6; #else #pragma message("SERCOM_INST_NUM not defined; SERCOM support disabled.") static constexpr size_t kSercomCount = 0; -#endif // SERCOM_INST_NUM / ARDUINO_SAME53_E54 +#endif // SERCOM_INST_NUM / __SAME53__ / __SAME54__ struct SercomState { Role role = Role::None; @@ -453,19 +479,22 @@ class SERCOM // for (hs) mode and DMA. struct WireConfig { uint32_t ctrla = 0x00000000; // default CTRLA value: auto ENABLE -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) uint32_t ctrlb = SERCOM_I2CM_CTRLB_SMEN_Msk; // default CTRLB value: SMEN #else uint32_t ctrlb = SERCOM_I2CM_CTRLB_SMEN; // default CTRLB value: SMEN -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ uint32_t baud = 0x000000FF; // default to lowest supported speed uint32_t addr = 0x00000000; // default address no GCEN, no ADDRMASK, 7-bit address only uint8_t masterSpeed = 0x0; // default to lowest speed uint8_t slaveSpeed = 0x0; // default to lowest speed bool inited = false; // whether initMaster/SlaveWIRE has been called - bool slaveConfigured = false; // return to client mode after queued host work - bool useDma = false; // per transaction DMA use flag for Host/Client modes + bool slaveConfigured = false; // return to slave mode after queued master work + bool useDma = false; // per-transaction DMA use for master/slave modes bool active = false; // active transaction in progress + bool releasePending = false; // STOP requested; callback waits until OWNER clears + bool abortPending = false; // wait for MB/SB before issuing timeout STOP + SercomWireError abortError = SercomWireError::MASTER_TIMEOUT; uint8_t retryCount = 0; // retry count for recoverable bus errors SercomWireError returnValue = SercomWireError::SUCCESS; SercomTxn* currentTxn = nullptr; @@ -500,10 +529,11 @@ class SERCOM } _uart; RingBufferN _txnQueue; - void (*_wireDeferredCb)(void* user, int length) = nullptr; - void* _wireDeferredUser = nullptr; - int _wireDeferredLength = 0; - bool _wireDeferredPending = false; + void* _wireCallbackUser = nullptr; + WireReceiveCallback _wireReceiveCb = nullptr; + WireRequestCallback _wireRequestCb = nullptr; + bool _wireReceivePending = false; + bool _wireRequestPending = false; #ifdef USE_ZERODMA Adafruit_ZeroDMA* _dmaTx = nullptr; @@ -512,12 +542,14 @@ class SERCOM DmacDescriptor* _dmaRxDesc = nullptr; DmaCallback _dmaTxCb = nullptr; DmaCallback _dmaRxCb = nullptr; + DmaCallback _dmaErrorCb = nullptr; uint8_t _dmaDummy = 0; uint8_t _dmaTxTrigger = 0; uint8_t _dmaRxTrigger = 0; bool _dmaConfigured = false; bool _dmaTxActive = false; bool _dmaRxActive = false; + bool _wireRxSuspendPending = false; DmaStatus _dmaLastError = DmaStatus::Ok; #endif // USE_ZERODMA }; diff --git a/cores/arduino/SERCOM_Txn.h b/cores/arduino/SERCOM_Txn.h index d3334e0e4..05e9ccafb 100644 --- a/cores/arduino/SERCOM_Txn.h +++ b/cores/arduino/SERCOM_Txn.h @@ -53,7 +53,8 @@ enum class SercomWireError : uint8_t MASTER_TIMEOUT = 10, // Master timeout (STATUS.MEXTTOUT) SLAVE_TIMEOUT = 11, // Slave timeout (STATUS.SEXTTOUT) LENGTH_ERROR = 12, // LENERR when LEN/LENEN mismatch (STATUS.LENERR) - UNKNOWN_ERROR = 13 // Error flag set but no specific bit matched + UNKNOWN_ERROR = 13, // Error flag set but no specific bit matched + DMA_ERROR = 14 // DMAC transfer/descriptor bus error }; // SPI error reporting (async callbacks) diff --git a/cores/arduino/SERCOM_inline.h b/cores/arduino/SERCOM_inline.h index e05602bf0..553a3e27a 100644 --- a/cores/arduino/SERCOM_inline.h +++ b/cores/arduino/SERCOM_inline.h @@ -5,60 +5,60 @@ inline void SERCOM::waitSyncBusyEnable( void ) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) while (sercom->I2CM.SERCOM_SYNCBUSY & SERCOM_I2CM_SYNCBUSY_ENABLE_Msk) ; #else while (sercom->I2CM.SYNCBUSY.bit.ENABLE != 0) ; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } inline void SERCOM::waitSyncBusySwrst( void ) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) while ((sercom->I2CM.SERCOM_CTRLA & SERCOM_I2CM_CTRLA_SWRST_Msk) || (sercom->I2CM.SERCOM_SYNCBUSY & SERCOM_I2CM_SYNCBUSY_SWRST_Msk)) ; #else while (sercom->I2CM.CTRLA.bit.SWRST || sercom->I2CM.SYNCBUSY.bit.SWRST) ; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } inline void SERCOM::waitSyncBusySysOp( void ) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) while (sercom->I2CM.SERCOM_SYNCBUSY & SERCOM_I2CM_SYNCBUSY_SYSOP_Msk) ; #else while (sercom->I2CM.SYNCBUSY.bit.SYSOP != 0) ; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } inline void SERCOM::waitSyncBusyCtrlB( void ) { -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) while (sercom->SPIM.SERCOM_SYNCBUSY & SERCOM_SPIM_SYNCBUSY_CTRLB_Msk) ; #else while (sercom->SPI.SYNCBUSY.bit.CTRLB != 0) ; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } inline void SERCOM::enableSERCOM( void ) { // UART, SPI, I2CS, and I2CM use the same enable bit -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->I2CM.SERCOM_CTRLA |= SERCOM_I2CM_CTRLA_ENABLE_Msk; #else sercom->I2CM.CTRLA.bit.ENABLE = 1; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ waitSyncBusyEnable(); } inline void SERCOM::disableSERCOM( void ) { // UART, SPI, I2CS, and I2CM use the same enable bit -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->I2CM.SERCOM_CTRLA &= ~SERCOM_I2CM_CTRLA_ENABLE_Msk; #else sercom->I2CM.CTRLA.bit.ENABLE = 0; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ waitSyncBusyEnable(); } @@ -67,13 +67,13 @@ inline void SERCOM::enableWIRE( void ) enableSERCOM(); // Setting bus idle mode -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->I2CM.SERCOM_STATUS = (sercom->I2CM.SERCOM_STATUS & ~SERCOM_I2CM_STATUS_BUSSTATE_Msk) | SERCOM_I2CM_STATUS_BUSSTATE(WIRE_IDLE_STATE); #else sercom->I2CM.STATUS.bit.BUSSTATE = 1; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ waitSyncBusySysOp(); } @@ -92,21 +92,21 @@ inline bool SERCOM::sendDataWIRE( void ) if (isDmaWIRE()) { DmaStatus value = DmaStatus::StartFailed; if (!_dmaTxActive && !_dmaRxActive) -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) value = dmaStartTx(txn->txPtr, &sercom->I2CM.SERCOM_DATA, _wire.txnLength); #else value = dmaStartTx(txn->txPtr, &sercom->I2CM.DATA.reg, _wire.txnLength); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ return value == DmaStatus::Ok; } #endif // USE_ZERODMA // Wait for DATA to sync out of the ISR and clear MB -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->I2CM.SERCOM_DATA = txn->txPtr[_wire.txnIndex++]; #else sercom->I2CM.DATA.reg = txn->txPtr[_wire.txnIndex++]; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ // Return false when the last byte has been consumed so the caller can // issue STOP / complete the transaction without waiting for another SB. @@ -115,33 +115,46 @@ inline bool SERCOM::sendDataWIRE( void ) inline void SERCOM::prepareCommandBitsWIRE(uint8_t cmd) { -#ifdef ARDUINO_SAME53_E54 + if (isMasterWIRE() && cmd == WIRE_MASTER_ACT_STOP) + markBusReleasePendingWIRE(); +#if defined(__SAME53__) || defined(__SAME54__) sercom->I2CM.SERCOM_CTRLB = (sercom->I2CM.SERCOM_CTRLB & ~SERCOM_I2CM_CTRLB_CMD_Msk) | SERCOM_I2CM_CTRLB_CMD(cmd); #else sercom->I2CM.CTRLB.bit.CMD = cmd; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ if (isMasterWIRE()) waitSyncBusySysOp(); } +inline void SERCOM::prepareSlaveCommandBitsWIRE(uint8_t cmd) +{ +#if defined(__SAME53__) || defined(__SAME54__) + sercom->I2CS.SERCOM_CTRLB = + (sercom->I2CS.SERCOM_CTRLB & ~SERCOM_I2CS_CTRLB_CMD_Msk) | + SERCOM_I2CS_CTRLB_CMD(cmd); +#else + sercom->I2CS.CTRLB.bit.CMD = cmd; +#endif // __SAME53__ / __SAME54__ +} + inline bool SERCOM::readDataWIRE( void ) { SercomTxn* txn = _wire.currentTxn; if (txn == nullptr || txn->rxPtr == nullptr) return false; #ifdef USE_ZERODMA - if (isDmaWIRE()) { - DmaStatus value = DmaStatus::StartFailed; - if (!_dmaRxActive && !_dmaTxActive) -#ifdef ARDUINO_SAME53_E54 - value = dmaStartRx(txn->rxPtr, &sercom->I2CM.SERCOM_DATA, _wire.txnLength); + if (isDmaWIRE()) { + DmaStatus value = DmaStatus::StartFailed; + if (!_dmaRxActive && !_dmaTxActive) +#if defined(__SAME53__) || defined(__SAME54__) + value = dmaStartRx(txn->rxPtr, &sercom->I2CM.SERCOM_DATA, _wire.txnLength); #else - value = dmaStartRx(txn->rxPtr, &sercom->I2CM.DATA.reg, _wire.txnLength); -#endif // ARDUINO_SAME53_E54 - return value == DmaStatus::Ok; - } + value = dmaStartRx(txn->rxPtr, &sercom->I2CM.DATA.reg, _wire.txnLength); +#endif // __SAME53__ / __SAME54__ + return value == DmaStatus::Ok; + } #endif // USE_ZERODMA bool isMaster = isMasterWIRE(); @@ -149,14 +162,16 @@ inline bool SERCOM::readDataWIRE( void ) if (isMaster) { if (_wire.txnIndex == (_wire.txnLength - 1)) { uint8_t cmd = txn->config & I2C_CFG_STOP ? WIRE_MASTER_ACT_STOP : WIRE_MASTER_ACT_NO_ACTION; -#ifdef ARDUINO_SAME53_E54 + if (cmd == WIRE_MASTER_ACT_STOP) + markBusReleasePendingWIRE(); +#if defined(__SAME53__) || defined(__SAME54__) sercom->I2CM.SERCOM_CTRLB = sercom->I2CM.SERCOM_CTRLB | SERCOM_I2CM_CTRLB_ACKACT_Msk | SERCOM_I2CM_CTRLB_CMD(cmd); #else sercom->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | SERCOM_I2CM_CTRLB_CMD(cmd); // NACK the last byte and send STOP if requested -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ } else prepareAckBitWIRE(); // ACK bytes otherwise for non-SCLSM mode @@ -170,11 +185,11 @@ inline bool SERCOM::readDataWIRE( void ) } // Read DATA register (accesses auto-trigger bus operation based on ACKACT/SMEN) -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) txn->rxPtr[_wire.txnIndex++] = sercom->I2CM.SERCOM_DATA; #else txn->rxPtr[_wire.txnIndex++] = sercom->I2CM.DATA.reg; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ return (_wire.txnIndex < _wire.txnLength); } @@ -188,21 +203,21 @@ inline bool SERCOM::sendDataSPI(void) if (_spi.useDma) { DmaStatus value = DmaStatus::StartFailed; if (!_dmaTxActive && !_dmaRxActive) -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) value = dmaStartTx(txn->txPtr, &sercom->SPIM.SERCOM_DATA, _spi.length); #else value = dmaStartTx(txn->txPtr, &sercom->SPI.DATA.reg, _spi.length); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ return value == DmaStatus::Ok; } #endif // USE_ZERODMA // Byte-by-byte: Write DATA register -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) sercom->SPIM.SERCOM_DATA = txn->txPtr[_spi.index++]; #else sercom->SPI.DATA.bit.DATA = txn->txPtr[_spi.index++]; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ // Return false when last byte consumed so caller can complete transaction return (_spi.index < _spi.length); @@ -217,21 +232,21 @@ inline bool SERCOM::readDataSPI(void) if (_spi.useDma) { DmaStatus value = DmaStatus::StartFailed; if (!_dmaRxActive && !_dmaTxActive) -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) value = dmaStartRx(txn->rxPtr, &sercom->SPIM.SERCOM_DATA, _spi.length); #else value = dmaStartRx(txn->rxPtr, &sercom->SPI.DATA.reg, _spi.length); -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ return value == DmaStatus::Ok; } #endif // USE_ZERODMA // Byte-by-byte: Read DATA register -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) txn->rxPtr[_spi.index - 1] = sercom->SPIM.SERCOM_DATA; #else txn->rxPtr[_spi.index - 1] = sercom->SPI.DATA.bit.DATA; -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ // Return false when all bytes consumed return (_spi.index < _spi.length); @@ -241,37 +256,42 @@ inline void SERCOM::setTxnWIRE(SercomTxn* txn) { _wire.currentTxn = txn; _wire.txnIndex = 0; + _wire.returnValue = SercomWireError::SUCCESS; if (txn) _wire.txnLength = txn->length; #ifdef USE_ZERODMA -#ifdef ARDUINO_SAME53_E54 - setDmaWIRE((txn && txn->length > 0 && txn->length < 256) || - (sercom->I2CM.SERCOM_CTRLA & SERCOM_I2CM_CTRLA_SCLSM_Msk)); +#if defined(__SAME53__) || defined(__SAME54__) + const bool sclsm = + (sercom->I2CM.SERCOM_CTRLA & SERCOM_I2CM_CTRLA_SCLSM_Msk) != 0; #else - setDmaWIRE((txn && txn->length > 0 && txn->length < 256) || sercom->I2CM.CTRLA.bit.SCLSM); -#endif // ARDUINO_SAME53_E54 + const bool sclsm = sercom->I2CM.CTRLA.bit.SCLSM; +#endif // __SAME53__ / __SAME54__ + const size_t length = txn ? txn->length : 0u; + const bool dmaEligible = length > 0u && length <= 255u; + setDmaWIRE(dmaEligible); + if (sclsm && !dmaEligible) + _wire.returnValue = SercomWireError::DATA_TOO_LONG; if (isDmaWIRE()) _wire.txnLength = (_wire.txnLength < 255u) ? _wire.txnLength : 255u; #else _wire.useDma = false; -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) if (sercom->I2CM.SERCOM_CTRLA & SERCOM_I2CM_CTRLA_SCLSM_Msk) #else if (sercom->I2CM.CTRLA.bit.SCLSM) -#endif // ARDUINO_SAME53_E54 - _wire.currentTxn = nullptr; // SCLSM requires DMA for proper operation (true for master; I think for slave) +#endif // __SAME53__ / __SAME54__ + _wire.returnValue = SercomWireError::DATA_TOO_LONG; #endif // USE_ZERODMA if (!_wire.active) _wire.retryCount = 0; - _wire.returnValue = SercomWireError::SUCCESS; _wire.active = true; } -#ifdef ARDUINO_SAME53_E54 +#if defined(__SAME53__) || defined(__SAME54__) inline void SERCOM::prepareNackBitWIRE( void ) { sercom->I2CM.SERCOM_CTRLB |= SERCOM_I2CM_CTRLB_ACKACT_Msk; } inline void SERCOM::prepareAckBitWIRE( void ) { sercom->I2CM.SERCOM_CTRLB &= ~SERCOM_I2CM_CTRLB_ACKACT_Msk; } @@ -313,7 +333,7 @@ inline int SERCOM::availableWIRE( void ) { return isMasterWIRE() ? sercom->I2CM. inline bool SERCOM::isDBGSTOP( void ) const { return sercom->I2CM.DBGCTRL.bit.DBGSTOP; } inline void SERCOM::setDBGSTOP( bool stop ) { sercom->I2CM.DBGCTRL.bit.DBGSTOP = stop; } -#endif // ARDUINO_SAME53_E54 +#endif // __SAME53__ / __SAME54__ #ifdef USE_ZERODMA inline SERCOM* SERCOM::findDmaOwner(Adafruit_ZeroDMA* dma, bool tx) @@ -344,22 +364,28 @@ inline void SERCOM::dmaTxCallbackWIRE(Adafruit_ZeroDMA* dma) { SERCOM* inst = findDmaOwner(dma, true); if (!inst) return; + if (!inst->_wire.active || inst->_wire.currentTxn == nullptr || + !inst->_wire.useDma) return; // When using ADDR.LENEN mode, the hardware automatically generates STOP // after ADDR.LEN bytes are transferred (datasheet §28.6.4.1.2). - // If a NACK TOPis received by the client for a host write transaction before - // ADDR.LEN bytes, a STOP will be automatically generated and the length error - // (STATUS.LENERR) will be raised along with the INTFLAG.ERROR interrupt.re + // If the slave NACKs a master-write transaction before ADDR.LEN bytes, STOP + // is generated automatically and STATUS.LENERR is raised with INTFLAG.ERROR. inst->_wire.txnIndex = inst->_wire.txnLength; inst->_dmaTxActive = false; - inst->deferStopWIRE(SercomWireError::SUCCESS); + if (inst->isSlaveWIRE()) + inst->deferStopWIRE(SercomWireError::SUCCESS); + else + inst->deferStopWIRE(SercomWireError::SUCCESS); } inline void SERCOM::dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma) { SERCOM* inst = findDmaOwner(dma, false); if (!inst) return; + if (!inst->_wire.active || inst->_wire.currentTxn == nullptr || + !inst->_wire.useDma) return; // When using ADDR.LENEN mode, the hardware automatically generates NACK+STOP // after ADDR.LEN bytes are transferred (datasheet §28.6.4.1.2). @@ -368,7 +394,24 @@ inline void SERCOM::dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma) inst->_wire.txnIndex = inst->_wire.txnLength; inst->_dmaRxActive = false; - inst->deferStopWIRE(SercomWireError::SUCCESS); + if (inst->isSlaveWIRE()) + inst->deferStopWIRE(SercomWireError::SUCCESS); + else + inst->deferStopWIRE(SercomWireError::SUCCESS); +} + +inline void SERCOM::dmaErrorCallbackWIRE(Adafruit_ZeroDMA* dma) +{ + SERCOM* inst = findDmaOwner(dma, true); + if (!inst) + inst = findDmaOwner(dma, false); + if (!inst) return; + if (!inst->_wire.active || inst->_wire.currentTxn == nullptr || + !inst->_wire.useDma) return; + + inst->dmaAbortTx(); + inst->dmaAbortRx(); + inst->abortWIRE(SercomWireError::DMA_ERROR); } inline void SERCOM::dmaTxCallbackSPI(Adafruit_ZeroDMA* dma) diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index 55142ed82..107d3c20a 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -81,30 +81,48 @@ extern "C" { #define WIRE_SERCOM_HANDLER_OTHER_FROM_TOKEN(token) \ WIRE_SERCOM_HANDLER_OTHER_FROM_INDEX(WIRE_SERCOM_INDEX(token)) + #define WIRE_DEFINE_SINGLE_HANDLER(handler, instance) \ - void handler(void) { instance.onService(); } + void handler(void) { \ + instance.onService(); \ + } +#if defined(__SAMD51__) || defined(__SAME51__) #define WIRE_DEFINE_SAMD51_E51_HANDLERS(handler0, handler1, handler2, \ handler3, instance) \ - void handler0(void) { instance.onService(); } \ - void handler1(void) { instance.onService(); } \ - void handler2(void) { instance.onService(); } \ - void handler3(void) { instance.onService(); } + void handler0(void) { \ + instance.onService(); \ + } \ + void handler1(void) { \ + instance.onService(); \ + } \ + void handler2(void) { \ + instance.onService(); \ + } \ + void handler3(void) { \ + instance.onService(); \ + } +#define WIRE_DEFINE_SERCOM_HANDLERS(prefix, instance) \ + WIRE_DEFINE_SAMD51_E51_HANDLERS( \ + prefix##_IT_HANDLER_0, prefix##_IT_HANDLER_1, prefix##_IT_HANDLER_2, \ + prefix##_IT_HANDLER_3, instance) +#elif defined(__SAME53__) || defined(__SAME54__) #define WIRE_DEFINE_SAME53_E54_HANDLERS(handler0, handler1, handler2, \ handlerOther, instance) \ - void handler0(void) { instance.onService(); } \ - void handler1(void) { instance.onService(); } \ - void handler2(void) { instance.onService(); } \ - void handlerOther(void) { instance.onService(); } + void handler0(void) { \ + instance.onService(); \ + } \ + void handler1(void) { \ + instance.onService(); \ + } \ + void handler2(void) { \ + instance.onService(); \ + } \ + void handlerOther(void) { \ + instance.onService(); \ + } -#ifdef ARDUINO_SAMD51_E51 -#define WIRE_DEFINE_SERCOM_HANDLERS(prefix, instance) \ - WIRE_DEFINE_SAMD51_E51_HANDLERS(prefix##_IT_HANDLER_0, \ - prefix##_IT_HANDLER_1, \ - prefix##_IT_HANDLER_2, \ - prefix##_IT_HANDLER_3, instance) -#elif defined(ARDUINO_SAME53_E54) #define WIRE_DEFINE_SERCOM_HANDLERS(prefix, instance) \ WIRE_DEFINE_SAME53_E54_HANDLERS(prefix##_IT_HANDLER_0, \ prefix##_IT_HANDLER_1, \ @@ -113,7 +131,7 @@ extern "C" { #else #define WIRE_DEFINE_SERCOM_HANDLERS(prefix, instance) \ WIRE_DEFINE_SINGLE_HANDLER(prefix##_IT_HANDLER, instance) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ namespace sercomPinMux { bool wirePinValidForSercom(uint8_t arduinoPin, uint8_t sercomIndex) { @@ -153,8 +171,11 @@ TwoWire::TwoWire(SERCOM * s, uint8_t pinSDA, uint8_t pinSCL) rxBufferPtr = rxBuffer; rxBufferCapacity = WIRE_BUFFER_LENGTH; txBufferCapacity = WIRE_BUFFER_LENGTH; - pendingReceive = false; - pendingReceiveLength = 0; + onRequestCallback = nullptr; + onReceiveCallback = nullptr; + onReceiveResultCallback = nullptr; + requestCompletionCallback = nullptr; + requestCompletionUser = nullptr; txnPoolHead = 0; } @@ -187,6 +208,7 @@ bool TwoWire::begin(uint16_t address, bool enableGeneralCall, uint8_t speed, sercom->initSlaveWIRE(address, enableGeneralCall, speed, enable10Bit); sercom->enableWIRE(); sercom->registerReceiveWIRE(&TwoWire::onDeferredReceive, this); + sercom->registerRequestWIRE(&TwoWire::onDeferredRequest, this); return true; } @@ -202,11 +224,17 @@ void TwoWire::end() { sercom->resetWIRE(); // SWRST: resets hardware + clears state + drains queue } +bool TwoWire::abortTransmission(int status) { + return sercom->abortWIRE(static_cast(status)); +} + uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit, uint8_t* rxBuffer, void (*onComplete)(void* user, int status), void* user) { if(quantity == 0) return 0; + if (!sercom->canEnqueueWIRE()) + return 0; loader = SercomTxn{}; @@ -288,10 +316,17 @@ void TwoWire::beginTransmission(uint8_t address) { // 2 : NACK on transmit of address // 3 : NACK on transmit of data // 4 : Other error -uint8_t TwoWire::endTransmission(bool stopBit, void (*onComplete)(void* user, int status), void* user) -{ +uint8_t TwoWire::endTransmission(bool stopBit, + void (*onComplete)(void *user, int status), + void *user) { transmissionBegun = false ; + if (!sercom->canEnqueueWIRE()) { + if (onComplete) + return static_cast(SercomWireError::QUEUE_FULL); + return 4; + } + // Allocate a fresh transaction from the pool and copy staged data from loader SercomTxn* txn = allocateTxn(); *txn = loader; // Copy staged transaction data @@ -445,11 +480,23 @@ void TwoWire::flush(void) void TwoWire::onReceive(void(*function)(int)) { onReceiveCallback = function; + onReceive(static_cast(nullptr)); +} + +void TwoWire::onReceive(void (*function)(SercomWireError, int)) { + onReceiveResultCallback = function; } void TwoWire::onRequest(void(*function)(void)) { + onRequest(function, nullptr, nullptr); +} + +void TwoWire::onRequest(void (*function)(void), RequestCompletion completion, + void *user) { onRequestCallback = function; + requestCompletionCallback = completion; + requestCompletionUser = user; } void TwoWire::setRxBuffer(uint8_t* buffer, size_t length) @@ -511,19 +558,72 @@ void TwoWire::onTxnComplete(void* user, int status) self->txnDone = true; } -void TwoWire::onDeferredReceive(void* user, int length) -{ +void TwoWire::onDeferredReceive(void *user) { if (!user) return; - TwoWire* self = static_cast(user); - if (!self->pendingReceive) - return; - if (self->onReceiveCallback) - self->onReceiveCallback(length); + TwoWire *self = static_cast(user); self->rxLength = 0; self->rxIndex = 0; - self->pendingReceive = false; - self->pendingReceiveLength = 0; + self->slaveTxn = SercomTxn{}; + self->slaveTxn.rxPtr = self->rxBufferPtr; + self->slaveTxn.length = self->rxBufferCapacity; + self->slaveTxn.config = 0; + self->slaveTxn.onComplete = &TwoWire::onSlaveReceiveComplete; + self->slaveTxn.user = self; + self->sercom->setTxnWIRE(&self->slaveTxn); + if (!self->sercom->startTransmissionWIRE()) { + self->sercom->deferStopWIRE(SercomWireError::DMA_ERROR); + return; + } +} + +void TwoWire::onDeferredRequest(void *user) { + if (!user) + return; + TwoWire *self = static_cast(user); + self->loader = SercomTxn{}; + self->transmissionBegun = true; + if (self->onRequestCallback) + self->onRequestCallback(); + self->transmissionBegun = false; + if (self->loader.txPtr == nullptr || self->loader.length == 0) { + if (self->requestCompletionCallback) + self->requestCompletionCallback(self->requestCompletionUser, + SercomWireError::SUCCESS); + return; + } + self->slaveTxn = self->loader; + self->slaveTxn.config |= I2C_CFG_READ; + self->slaveTxn.onComplete = &TwoWire::onSlaveRequestComplete; + self->slaveTxn.user = self; + self->sercom->setTxnWIRE(&self->slaveTxn); + if (!self->sercom->startTransmissionWIRE()) { + self->sercom->deferStopWIRE(SercomWireError::DMA_ERROR); + return; + } +} + +void TwoWire::onSlaveReceiveComplete(void *user, int status) { + if (!user) + return; + TwoWire *self = static_cast(user); + const SercomWireError error = static_cast(status); + const int received = static_cast(self->sercom->getTxnIndexWIRE()); + self->rxLength = received; + self->rxIndex = 0; + if (error == SercomWireError::SUCCESS && self->onReceiveCallback) + self->onReceiveCallback(received); + if (self->onReceiveResultCallback) + self->onReceiveResultCallback(error, received); +} + +void TwoWire::onSlaveRequestComplete(void *user, int status) { + if (!user) + return; + TwoWire *self = static_cast(user); + if (self->requestCompletionCallback) + self->requestCompletionCallback(self->requestCompletionUser, + static_cast(status)); } #if WIRE_INTERFACES_COUNT > 0 @@ -544,11 +644,11 @@ TwoWire Wire(&PERIPH_WIRE, PIN_WIRE_SDA, PIN_WIRE_SCL); #define WIRE_IT_HANDLER_0 WIRE_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_WIRE) #define WIRE_IT_HANDLER_1 WIRE_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_WIRE) #define WIRE_IT_HANDLER_2 WIRE_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_WIRE) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define WIRE_IT_HANDLER_3 WIRE_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_WIRE) -#elif defined(ARDUINO_SAME53_E54) +#elif defined(__SAME53__) || defined(__SAME54__) #define WIRE_IT_HANDLER_OTHER WIRE_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_WIRE) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !WIRE_IT_HANDLER_0 WIRE_DEFINE_SERCOM_HANDLERS(WIRE, Wire) #endif // WIRE_INTERFACES_COUNT > 0 @@ -562,13 +662,14 @@ WIRE_DEFINE_SERCOM_HANDLERS(WIRE, Wire) #define WIRE1_IT_HANDLER_0 WIRE_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_WIRE1) #define WIRE1_IT_HANDLER_1 WIRE_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_WIRE1) #define WIRE1_IT_HANDLER_2 WIRE_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_WIRE1) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define WIRE1_IT_HANDLER_3 WIRE_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_WIRE1) -#elif defined(ARDUINO_SAME53_E54) -#define WIRE1_IT_HANDLER_OTHER WIRE_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_WIRE1) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#elif defined(__SAME53__) || defined(__SAME54__) +#define WIRE1_IT_HANDLER_OTHER \ + WIRE_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_WIRE1) +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !WIRE1_IT_HANDLER_0 -WIRE_DEFINE_SERCOM_HANDLERS(WIRE1, Wire1) + WIRE_DEFINE_SERCOM_HANDLERS(WIRE1, Wire1) #endif // WIRE_INTERFACES_COUNT > 1 #if WIRE_INTERFACES_COUNT > 2 @@ -580,13 +681,14 @@ WIRE_DEFINE_SERCOM_HANDLERS(WIRE1, Wire1) #define WIRE2_IT_HANDLER_0 WIRE_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_WIRE2) #define WIRE2_IT_HANDLER_1 WIRE_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_WIRE2) #define WIRE2_IT_HANDLER_2 WIRE_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_WIRE2) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define WIRE2_IT_HANDLER_3 WIRE_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_WIRE2) -#elif defined(ARDUINO_SAME53_E54) -#define WIRE2_IT_HANDLER_OTHER WIRE_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_WIRE2) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#elif defined(__SAME53__) || defined(__SAME54__) +#define WIRE2_IT_HANDLER_OTHER \ + WIRE_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_WIRE2) +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !WIRE2_IT_HANDLER_0 -WIRE_DEFINE_SERCOM_HANDLERS(WIRE2, Wire2) + WIRE_DEFINE_SERCOM_HANDLERS(WIRE2, Wire2) #endif // WIRE_INTERFACES_COUNT > 2 #if WIRE_INTERFACES_COUNT > 3 @@ -598,13 +700,14 @@ WIRE_DEFINE_SERCOM_HANDLERS(WIRE2, Wire2) #define WIRE3_IT_HANDLER_0 WIRE_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_WIRE3) #define WIRE3_IT_HANDLER_1 WIRE_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_WIRE3) #define WIRE3_IT_HANDLER_2 WIRE_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_WIRE3) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define WIRE3_IT_HANDLER_3 WIRE_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_WIRE3) -#elif defined(ARDUINO_SAME53_E54) -#define WIRE3_IT_HANDLER_OTHER WIRE_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_WIRE3) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#elif defined(__SAME53__) || defined(__SAME54__) +#define WIRE3_IT_HANDLER_OTHER \ + WIRE_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_WIRE3) +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !WIRE3_IT_HANDLER_0 -WIRE_DEFINE_SERCOM_HANDLERS(WIRE3, Wire3) + WIRE_DEFINE_SERCOM_HANDLERS(WIRE3, Wire3) #endif // WIRE_INTERFACES_COUNT > 3 #if WIRE_INTERFACES_COUNT > 4 @@ -616,13 +719,14 @@ WIRE_DEFINE_SERCOM_HANDLERS(WIRE3, Wire3) #define WIRE4_IT_HANDLER_0 WIRE_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_WIRE4) #define WIRE4_IT_HANDLER_1 WIRE_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_WIRE4) #define WIRE4_IT_HANDLER_2 WIRE_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_WIRE4) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define WIRE4_IT_HANDLER_3 WIRE_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_WIRE4) -#elif defined(ARDUINO_SAME53_E54) -#define WIRE4_IT_HANDLER_OTHER WIRE_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_WIRE4) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#elif defined(__SAME53__) || defined(__SAME54__) +#define WIRE4_IT_HANDLER_OTHER \ + WIRE_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_WIRE4) +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !WIRE4_IT_HANDLER_0 -WIRE_DEFINE_SERCOM_HANDLERS(WIRE4, Wire4) + WIRE_DEFINE_SERCOM_HANDLERS(WIRE4, Wire4) #endif // WIRE_INTERFACES_COUNT > 4 #if WIRE_INTERFACES_COUNT > 5 @@ -634,11 +738,12 @@ WIRE_DEFINE_SERCOM_HANDLERS(WIRE4, Wire4) #define WIRE5_IT_HANDLER_0 WIRE_SERCOM_HANDLER0_FROM_TOKEN(PERIPH_WIRE5) #define WIRE5_IT_HANDLER_1 WIRE_SERCOM_HANDLER1_FROM_TOKEN(PERIPH_WIRE5) #define WIRE5_IT_HANDLER_2 WIRE_SERCOM_HANDLER2_FROM_TOKEN(PERIPH_WIRE5) -#ifdef ARDUINO_SAMD51_E51 +#if defined(__SAMD51__) || defined(__SAME51__) #define WIRE5_IT_HANDLER_3 WIRE_SERCOM_HANDLER3_FROM_TOKEN(PERIPH_WIRE5) -#elif defined(ARDUINO_SAME53_E54) -#define WIRE5_IT_HANDLER_OTHER WIRE_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_WIRE5) -#endif // ARDUINO_SAMD51_E51 / ARDUINO_SAME53_E54 +#elif defined(__SAME53__) || defined(__SAME54__) +#define WIRE5_IT_HANDLER_OTHER \ + WIRE_SERCOM_HANDLER_OTHER_FROM_TOKEN(PERIPH_WIRE5) +#endif // __SAMD51__ / __SAME51__ / __SAME53__ / __SAME54__ #endif // !WIRE5_IT_HANDLER_0 -WIRE_DEFINE_SERCOM_HANDLERS(WIRE5, Wire5) + WIRE_DEFINE_SERCOM_HANDLERS(WIRE5, Wire5) #endif // WIRE_INTERFACES_COUNT > 5 diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 81efc69a6..42b2ce1c7 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -23,8 +23,8 @@ #include "Stream.h" #include "variant.h" -#include "SERCOM.h" #include "RingBuffer.h" +#include "SERCOM.h" #include // WIRE_HAS_END means Wire has end() @@ -39,6 +39,7 @@ class TwoWire : public Stream { public: + using RequestCompletion = void (*)(void *user, SercomWireError result); TwoWire(SERCOM *s, uint8_t pinSDA, uint8_t pinSCL); bool begin(); bool begin(uint8_t, bool enableGeneralCall = false); @@ -53,6 +54,8 @@ class TwoWire : public Stream uint8_t endTransmission(bool stopBit = true, void (*onComplete)(void *user, int status) = nullptr, void *user = nullptr); + bool abortTransmission( + int status = static_cast(SercomWireError::MASTER_TIMEOUT)); // If onComplete is nullptr, this blocks for legacy sync behavior. // If onComplete is non-null, this enqueues and returns immediately (async). @@ -75,7 +78,10 @@ class TwoWire : public Stream virtual int peek(void); virtual void flush(void); void onReceive(void (*)(int)); + void onReceive(void (*)(SercomWireError, int)); void onRequest(void (*)(void)); + void onRequest(void (*)(void), RequestCompletion completion, + void *user = nullptr); void setRxBuffer(uint8_t *buffer, size_t length); void setTxBuffer(uint8_t *buffer, size_t length); void clearRxBuffer(void); @@ -113,8 +119,6 @@ class TwoWire : public Stream bool awaitingAddressAck; volatile bool txnDone; volatile int txnStatus; - bool pendingReceive; - int pendingReceiveLength; SercomTxn slaveTxn; SercomTxn loader; // Staging area for building transactions @@ -129,9 +133,15 @@ class TwoWire : public Stream // Callback user functions void (*onRequestCallback)(void); void (*onReceiveCallback)(int); + void (*onReceiveResultCallback)(SercomWireError, int); + RequestCompletion requestCompletionCallback; + void *requestCompletionUser; static void onTxnComplete(void *user, int status); - static void onDeferredReceive(void *user, int length); + static void onDeferredReceive(void *user); + static void onDeferredRequest(void *user); + static void onSlaveReceiveComplete(void *user, int status); + static void onSlaveRequestComplete(void *user, int status); // TWI clock frequency static const uint32_t TWI_CLOCK = 100000; @@ -170,44 +180,86 @@ inline void TwoWire::onService(void) if (flags == 0) return; - if (status & SERCOM_I2CM_STATUS_RXNACK) { - sercom->prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); - SercomWireError err = awaitingAddressAck ? SercomWireError::NACK_ON_ADDRESS +#if defined(__SAME53__) || defined(__SAME54__) + const bool rxNack = status & SERCOM_I2CM_STATUS_RXNACK_Msk; + const bool wireError = flags & SERCOM_I2CM_INTFLAG_ERROR_Msk; + const bool slaveDrdy = flags & SERCOM_I2CS_INTFLAG_DRDY_Msk; +#else + const bool rxNack = status & SERCOM_I2CM_STATUS_RXNACK; + const bool wireError = flags & SERCOM_I2CM_INTFLAG_ERROR; + const bool slaveDrdy = flags & SERCOM_I2CS_INTFLAG_DRDY; +#endif // __SAME53__ / __SAME54__ + + SercomTxn *activeTxn = sercom->getCurrentTxnWIRE(); + const bool effectiveSlaveRead = + activeTxn && (activeTxn->config & I2C_CFG_READ) != 0; + const bool actionableRxNack = + isMaster ? rxNack : slaveDrdy && effectiveSlaveRead && rxNack; + + if (actionableRxNack || wireError) { +#if defined(__SAME53__) || defined(__SAME54__) + const bool arbitrationLost = status & SERCOM_I2CM_STATUS_ARBLOST_Msk; + const bool busError = status & SERCOM_I2CM_STATUS_BUSERR_Msk; + const bool lowTimeout = status & SERCOM_I2CM_STATUS_LOWTOUT_Msk; + const bool slaveExtendTimeout = status & SERCOM_I2CM_STATUS_SEXTTOUT_Msk; + const bool masterExtendTimeout = + isMaster && (status & SERCOM_I2CM_STATUS_MEXTTOUT_Msk); + const bool lengthError = + isMaster && (status & SERCOM_I2CM_STATUS_LENERR_Msk); +#else + const bool arbitrationLost = status & SERCOM_I2CM_STATUS_ARBLOST; + const bool busError = status & SERCOM_I2CM_STATUS_BUSERR; + const bool lowTimeout = status & SERCOM_I2CM_STATUS_LOWTOUT; + const bool slaveExtendTimeout = status & SERCOM_I2CM_STATUS_SEXTTOUT; + const bool masterExtendTimeout = + isMaster && (status & SERCOM_I2CM_STATUS_MEXTTOUT); + const bool lengthError = isMaster && (status & SERCOM_I2CM_STATUS_LENERR); +#endif // __SAME53__ / __SAME54__ + const bool masterTimeout = isMaster && (masterExtendTimeout || lowTimeout); + const bool slaveTimeout = slaveExtendTimeout || (!isMaster && lowTimeout); + + SercomWireError error = SercomWireError::UNKNOWN_ERROR; + + if (actionableRxNack) { + error = isMaster && awaitingAddressAck ? SercomWireError::NACK_ON_ADDRESS : SercomWireError::NACK_ON_DATA; - sercom->deferStopWIRE(err); + } else if (arbitrationLost) { + error = SercomWireError::ARBITRATION_LOST; + } else if (busError) { + error = SercomWireError::BUS_ERROR; + } else if (masterTimeout) { + error = SercomWireError::MASTER_TIMEOUT; + } else if (slaveTimeout) { + error = SercomWireError::SLAVE_TIMEOUT; + } else if (lengthError) { + error = SercomWireError::LENGTH_ERROR; + } else if (isMaster) { +#if defined(__SAME53__) || defined(__SAME54__) + const uint8_t busState = (status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> + SERCOM_I2CM_STATUS_BUSSTATE_Pos; +#else + const uint8_t busState = (status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> + SERCOM_I2CM_STATUS_BUSSTATE_Pos; +#endif // __SAME53__ / __SAME54__ + if (busState == WIRE_UNKNOWN_STATE) + error = SercomWireError::BUS_STATE_UNKNOWN; + } + + if (isMaster && !arbitrationLost) + sercom->prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); + if (isMaster && wireError) + sercom->clearINTFLAG(); + sercom->deferStopWIRE(error); return; } if (isMaster) { - SercomTxn *txn = sercom->getCurrentTxnWIRE(); + SercomTxn *txn = activeTxn; if (!txn) { sercom->clearINTFLAG(); return; } - if (flags & SERCOM_I2CM_INTFLAG_ERROR) { - sercom->prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); - uint8_t busState = (status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> SERCOM_I2CM_STATUS_BUSSTATE_Pos; - SercomWireError err = SercomWireError::UNKNOWN_ERROR; - - if (status & SERCOM_I2CM_STATUS_ARBLOST) - err = SercomWireError::ARBITRATION_LOST; - if (status & SERCOM_I2CM_STATUS_BUSERR) - err = SercomWireError::BUS_ERROR; - if (status & SERCOM_I2CM_STATUS_MEXTTOUT) - err = SercomWireError::MASTER_TIMEOUT; - if (status & SERCOM_I2CM_STATUS_SEXTTOUT) - err = SercomWireError::SLAVE_TIMEOUT; - if (status & SERCOM_I2CM_STATUS_LENERR) - err = SercomWireError::LENGTH_ERROR; - if (busState == 0x0) - err = SercomWireError::BUS_STATE_UNKNOWN; - - sercom->clearINTFLAG(); - sercom->deferStopWIRE(err); - return; - } - bool isRead = (txn->config & I2C_CFG_READ); if (sercom->getTxnIndexWIRE() < sercom->getTxnLengthWIRE()) { @@ -225,91 +277,66 @@ inline void TwoWire::onService(void) awaitingAddressAck = true; sercom->deferStopWIRE(SercomWireError::SUCCESS); return; - } - else { - if (flags & SERCOM_I2CS_INTFLAG_ERROR) { - SercomWireError err = SercomWireError::UNKNOWN_ERROR; - - if (status & SERCOM_I2CS_STATUS_BUSERR) - err = SercomWireError::BUS_ERROR; - if (status & SERCOM_I2CS_STATUS_COLL) - err = SercomWireError::ARBITRATION_LOST; - if (status & SERCOM_I2CS_STATUS_SEXTTOUT) - err = SercomWireError::SLAVE_TIMEOUT; - if (status & SERCOM_I2CS_STATUS_LOWTOUT) - err = SercomWireError::SLAVE_TIMEOUT; - - sercom->clearINTFLAG(); - sercom->deferStopWIRE(err); - return; - } - - // To avoid unnecessary clock cycles for register reads, avoid using inline getters - bool isMasterRead = (status & SERCOM_I2CS_STATUS_DIR); // Master Read / Slave Transmit - bool sr = (status & SERCOM_I2CS_STATUS_SR); // Repeated Start detected + } else { +#if defined(__SAME53__) || defined(__SAME54__) + bool isMasterRead = (status & SERCOM_I2CS_STATUS_DIR_Msk); + bool prec = (flags & SERCOM_I2CS_INTFLAG_PREC_Msk); + bool amatch = (flags & SERCOM_I2CS_INTFLAG_AMATCH_Msk); +#else + bool isMasterRead = + (status & SERCOM_I2CS_STATUS_DIR); // Master Read / Slave Transmit bool prec = (flags & SERCOM_I2CS_INTFLAG_PREC); // Stop detected bool amatch = (flags & SERCOM_I2CS_INTFLAG_AMATCH); // Address Match detected - bool drdy = (flags & SERCOM_I2CS_INTFLAG_DRDY); // Data Ready detected - - // Stop or Restart detected - defer receive callback - if (prec || (amatch && sr && !isMasterRead)) - { - pendingReceive = true; - pendingReceiveLength = available(); - sercom->deferReceiveWIRE(pendingReceiveLength); - return; +#endif // __SAME53__ / __SAME54__ + + // A new address match terminates an active slave receive even when the + // master uses a repeated START instead of STOP. Queue receive completion + // before the follow-up setup so a combined write/read transaction publishes + // its request bytes before onRequest prepares the response. + if (amatch) { + if (activeTxn && (activeTxn->config & I2C_CFG_READ) == 0) + sercom->deferReceiveCompleteWIRE(); + if (isMasterRead) + sercom->deferRequestWIRE(); + else + sercom->deferReceiveWIRE(); } - // Address Match - setup transaction - // AACKEN enabled: address ACK is automatic, no manual ACK/clear needed - else if (amatch) { - if (isMasterRead) // Master Read / Slave TX - { - // onRequestCallback runs in ISR context here. Deferring to PendSV - // would require stalling DRDY or returning 0xFF until the buffer is filled. - // onRequestCallback is what will set TwoWire::slaveTxn for the transaction. - if (onRequestCallback) - onRequestCallback(); - - // Ensure callback actually set slaveTxn.length; if not, stall with 0-length txn - if (slaveTxn.length == 0) - return; - - if (!(slaveTxn.config & I2C_CFG_READ)) - slaveTxn.config |= I2C_CFG_READ; - } - else // Master Write / Slave RX - { - // rxLength needs to be set in the rxCallback so the user has runtime control - // setting rxLength to 0 will default to the internal buffer - rxIndex = 0; - slaveTxn.txPtr = nullptr; - slaveTxn.rxPtr = rxLength ? rxBufferPtr : rxBuffer; - slaveTxn.length = rxLength ? rxLength : WIRE_BUFFER_LENGTH; - slaveTxn.config = 0; + // Preserve the interrupt-driven slave byte engine both when DMA is not + // compiled and when this transaction is not DMA-eligible. + if (slaveDrdy && !amatch && !sercom->isDmaWIRE()) { + SercomTxn *slaveTxn = activeTxn; + if (!slaveTxn) { + sercom->clearINTFLAG(); + return; } - sercom->setTxnWIRE(&slaveTxn); - - // SCLSM=0 (Smart Mode disabled): AMATCH and DRDY never fire together - // → return now, DRDY will fire in next interrupt - // SCLSM=1 (Smart Mode enabled) + Master Read: AMATCH+DRDY fire together - // → fall through to handle data immediately - // SCLSM=1 + Master Write: DRDY not set yet - // → return now, DRDY fires later - if (!drdy) - return; - // else: DRDY is set (SCLSM=1 Master Read case), fall through + if (slaveTxn->config & I2C_CFG_READ) { + if (sercom->getTxnIndexWIRE() < sercom->getTxnLengthWIRE()) + sercom->sendDataWIRE(); + else { + sercom->prepareSlaveCommandBitsWIRE(WIRE_SLAVE_ACT_COMPLETE); + sercom->deferStopWIRE(SercomWireError::SUCCESS); + } + } else if (sercom->getTxnIndexWIRE() < sercom->getTxnLengthWIRE()) { + sercom->readDataWIRE(); + } else { + sercom->prepareNackBitWIRE(); + } + return; } - // Data Ready - handle byte transfer - if (drdy) - { - isMasterRead ? sercom->sendDataWIRE() : sercom->readDataWIRE(); - - if (!isMasterRead) - rxLength = sercom->getTxnIndexWIRE(); - } + // A short slave receive ends at STOP before its capacity-sized descriptor + // reaches zero. PREC finalizes the actual DMA byte count; it is not an + // error and does not enter stopTransmissionWIRE(). + SercomTxn *slaveTxn = activeTxn; + const bool activeSlaveReceive = + prec && !amatch && slaveTxn && + (slaveTxn->config & I2C_CFG_READ) == 0; + if (activeSlaveReceive) + sercom->deferReceiveCompleteWIRE(); + else if (prec) + sercom->clearINTFLAG(); } } From 068321cc6a4d8f06074ca8216ef1588d90d4b5a3 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 24 Jul 2026 15:09:17 -0400 Subject: [PATCH 62/79] fix(wire): service final slave NACK after DMA --- cores/arduino/SERCOM_inline.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/cores/arduino/SERCOM_inline.h b/cores/arduino/SERCOM_inline.h index 553a3e27a..c1ed99da2 100644 --- a/cores/arduino/SERCOM_inline.h +++ b/cores/arduino/SERCOM_inline.h @@ -374,10 +374,18 @@ inline void SERCOM::dmaTxCallbackWIRE(Adafruit_ZeroDMA* dma) inst->_wire.txnIndex = inst->_wire.txnLength; inst->_dmaTxActive = false; - if (inst->isSlaveWIRE()) - inst->deferStopWIRE(SercomWireError::SUCCESS); - else + if (inst->isSlaveWIRE()) { + // DMA has written the final DATA byte, but the master's ACK/NACK is + // reported at the following DRDY boundary. Restore that one interrupt + // so onService() can release the slave transaction physically. +#if defined(__SAME53__) || defined(__SAME54__) + inst->enableInterrupts(SERCOM_I2CS_INTENSET_DRDY_Msk); +#else + inst->enableInterrupts(SERCOM_I2CS_INTENSET_DRDY); +#endif + } else { inst->deferStopWIRE(SercomWireError::SUCCESS); + } } inline void SERCOM::dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma) From 1581f366dd223bb2b5a049e410d61e646f1eb042 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 24 Jul 2026 15:14:11 -0400 Subject: [PATCH 63/79] build(core): update SAME5x USB and DMA dependencies --- libraries/Adafruit_TinyUSB_Arduino | 2 +- libraries/Adafruit_ZeroDMA | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/Adafruit_TinyUSB_Arduino b/libraries/Adafruit_TinyUSB_Arduino index 3e5369cd4..2c72b2c1f 160000 --- a/libraries/Adafruit_TinyUSB_Arduino +++ b/libraries/Adafruit_TinyUSB_Arduino @@ -1 +1 @@ -Subproject commit 3e5369cd4720c63d9caf31db6ca4c18ce7bcd0b7 +Subproject commit 2c72b2c1f9d774cdd5e5407c16fe12bb7a455c30 diff --git a/libraries/Adafruit_ZeroDMA b/libraries/Adafruit_ZeroDMA index 5b48867c1..27e47be8b 160000 --- a/libraries/Adafruit_ZeroDMA +++ b/libraries/Adafruit_ZeroDMA @@ -1 +1 @@ -Subproject commit 5b48867c1d3015ec63cd8f31def45f5a2d8e49bd +Subproject commit 27e47be8beb686785cdbffd5d2171b845cb0232e From 5299f1227771e0e1ac2aac209d44d06814f483a5 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 24 Jul 2026 15:52:58 -0400 Subject: [PATCH 64/79] fix(sercom): generate pinmux guards from chip families --- tools/generate_pinmux_inc.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/generate_pinmux_inc.py b/tools/generate_pinmux_inc.py index d237b1c3c..6a657d02b 100644 --- a/tools/generate_pinmux_inc.py +++ b/tools/generate_pinmux_inc.py @@ -509,21 +509,21 @@ def emit_series_macros( lines.extend(format_series_macro(series_name, unique_checks)) lines.append("") - lines.append("#if !defined(ARDUINO_SAMD51_E51) && !defined(ARDUINO_SAME53_E54)") + lines.append("#if !defined(__SAMD51__) && !defined(__SAME51__) && !defined(__SAME53__) && !defined(__SAME54__)") lines.append("#if !(SAMD21E_SERIES || SAMD21G_SERIES || SAMD21J_SERIES || SAMDA1_SERIES)") lines.append("#define SAMD21E_SERIES 1") lines.append("#endif // !(SAMD21E_SERIES || SAMD21G_SERIES || SAMD21J_SERIES || SAMDA1_SERIES)") - lines.append("#endif // !ARDUINO_SAMD51_E51 && !ARDUINO_SAME53_E54") - lines.append("#if defined(ARDUINO_SAMD51_E51)") + lines.append("#endif // !__SAMD51__ / __SAME51__ && !__SAME53__ / __SAME54__") + lines.append("#if defined(__SAMD51__) || defined(__SAME51__)") lines.append("#if !(SAMD51_SERIES || SAMD51_120_SERIES || SAME51_SERIES)") lines.append("#define SAMD51_SERIES 1") lines.append("#endif // !(SAMD51_SERIES || SAMD51_120_SERIES || SAME51_SERIES)") - lines.append("#endif // ARDUINO_SAMD51_E51") - lines.append("#if defined(ARDUINO_SAME53_E54)") + lines.append("#endif // __SAMD51__ / __SAME51__") + lines.append("#if defined(__SAME53__) || defined(__SAME54__)") lines.append("#if !(SAME53_SERIES || SAME54_SERIES || SAME54_120_SERIES)") lines.append("#define SAME54_SERIES 1") lines.append("#endif // !(SAME53_SERIES || SAME54_SERIES || SAME54_120_SERIES)") - lines.append("#endif // ARDUINO_SAME53_E54") + lines.append("#endif // __SAME53__ / __SAME54__") lines.append("") return lines From 0d3d40cdc3b5ffa17498dbabae179ece34135d1f Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 24 Jul 2026 16:10:19 -0400 Subject: [PATCH 65/79] build(sercom): update USB and DMA dependencies --- libraries/Adafruit_TinyUSB_Arduino | 2 +- libraries/Adafruit_ZeroDMA | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/Adafruit_TinyUSB_Arduino b/libraries/Adafruit_TinyUSB_Arduino index 2c72b2c1f..08e7e9cd2 160000 --- a/libraries/Adafruit_TinyUSB_Arduino +++ b/libraries/Adafruit_TinyUSB_Arduino @@ -1 +1 @@ -Subproject commit 2c72b2c1f9d774cdd5e5407c16fe12bb7a455c30 +Subproject commit 08e7e9cd22ad5dcd5465536caa6f0b200eb12486 diff --git a/libraries/Adafruit_ZeroDMA b/libraries/Adafruit_ZeroDMA index 27e47be8b..5a8ec1581 160000 --- a/libraries/Adafruit_ZeroDMA +++ b/libraries/Adafruit_ZeroDMA @@ -1 +1 @@ -Subproject commit 27e47be8beb686785cdbffd5d2171b845cb0232e +Subproject commit 5a8ec158176c7c3be7313169c9c13d3c9eecb99f From 7daaf0c974f61a9af096b894d3402808c90da9b3 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sat, 25 Jul 2026 21:43:14 -0400 Subject: [PATCH 66/79] fix(wire): recover DMA stop and slave role transitions --- cores/arduino/SERCOM.cpp | 66 +++++++++++++++++++++++------------ cores/arduino/SERCOM_Txn.h | 3 +- cores/arduino/SERCOM_inline.h | 11 +----- libraries/Wire/Wire.h | 16 ++++----- 4 files changed, 55 insertions(+), 41 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 8e47cc728..8ad08f6c3 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -1030,10 +1030,8 @@ void SERCOM::setSlaveWIRE(void) sercom->I2CS.SERCOM_CTRLB = slaveCtrlb; sercom->I2CS.SERCOM_ADDR = _wire.addr; enableWIRE(); - sercom->I2CS.SERCOM_INTENSET = SERCOM_I2CS_INTENSET_ERROR_Msk | - SERCOM_I2CS_INTENSET_PREC_Msk | - SERCOM_I2CS_INTENSET_AMATCH_Msk | - SERCOM_I2CS_INTENSET_DRDY_Msk; + sercom->I2CS.SERCOM_INTENSET = + SERCOM_I2CS_INTENSET_ERROR_Msk | SERCOM_I2CS_INTENSET_AMATCH_Msk; #else uint32_t slaveCtrlb = _wire.ctrlb; if (slaveCtrlb & SERCOM_I2CS_CTRLB_SMEN) @@ -1044,11 +1042,10 @@ void SERCOM::setSlaveWIRE(void) sercom->I2CS.CTRLB.reg = slaveCtrlb; sercom->I2CS.ADDR.reg = _wire.addr; enableWIRE(); - // Enable slave interrupts: address match, data ready, stop/restart. - sercom->I2CS.INTENSET.reg = SERCOM_I2CS_INTENSET_ERROR | // Error - SERCOM_I2CS_INTENSET_PREC | // Stop - SERCOM_I2CS_INTENSET_AMATCH | // Address Match - SERCOM_I2CS_INTENSET_DRDY; // Data Ready + // ERROR and AMATCH remain armed while the slave is idle. DRDY and PREC are + // owned by an active transaction and are enabled in startTransmissionWIRE(). + sercom->I2CS.INTENSET.reg = + SERCOM_I2CS_INTENSET_ERROR | SERCOM_I2CS_INTENSET_AMATCH; #endif // __SAME53__ / __SAME54__ } @@ -1112,11 +1109,6 @@ SercomTxn* SERCOM::startTransmissionWIRE( void ) // Mirror the master engine selection: eligible transactions use DMA; // larger transactions remain on the interrupt-driven DATA path. if (isDmaWIRE()) { -#if defined(__SAME53__) || defined(__SAME54__) - disableInterrupts(SERCOM_I2CS_INTENCLR_DRDY_Msk); -#else - disableInterrupts(SERCOM_I2CS_INTENCLR_DRDY); -#endif // __SAME53__ / __SAME54__ if (!_dmaConfigured) dmaInit(getSercomIndex()); if (!_dmaConfigured || !_dmaTx || !_dmaRx) @@ -1129,20 +1121,33 @@ SercomTxn* SERCOM::startTransmissionWIRE( void ) const bool started = slaveTransmit ? sendDataWIRE() : readDataWIRE(); if (!started) return nullptr; +#if defined(__SAME53__) || defined(__SAME54__) + enableInterrupts(SERCOM_I2CS_INTENSET_DRDY_Msk | + SERCOM_I2CS_INTENSET_PREC_Msk); +#else + enableInterrupts(SERCOM_I2CS_INTENSET_DRDY | SERCOM_I2CS_INTENSET_PREC); +#endif // __SAME53__ / __SAME54__ if (slaveTransmit) { clearAmatch(); } } else { #if defined(__SAME53__) || defined(__SAME54__) - enableInterrupts(SERCOM_I2CS_INTENSET_DRDY_Msk); + enableInterrupts(SERCOM_I2CS_INTENSET_DRDY_Msk | + SERCOM_I2CS_INTENSET_PREC_Msk); #else - enableInterrupts(SERCOM_I2CS_INTENSET_DRDY); + enableInterrupts(SERCOM_I2CS_INTENSET_DRDY | SERCOM_I2CS_INTENSET_PREC); #endif // __SAME53__ / __SAME54__ clearAmatch(); } #else // The no-DMA build advances this transaction from slave DRDY interrupts. setDmaWIRE(false); +#if defined(__SAME53__) || defined(__SAME54__) + enableInterrupts(SERCOM_I2CS_INTENSET_DRDY_Msk | + SERCOM_I2CS_INTENSET_PREC_Msk); +#else + enableInterrupts(SERCOM_I2CS_INTENSET_DRDY | SERCOM_I2CS_INTENSET_PREC); +#endif // __SAME53__ / __SAME54__ clearAmatch(); #endif // USE_ZERODMA return txn; @@ -1369,6 +1374,7 @@ SercomTxn* SERCOM::stopTransmissionWIRE( void ) SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) { + constexpr uint32_t kWireStopSettleTimeoutUs = 100u; // Policy: only auto-retry recoverable bus-state errors here. All other // errors are surfaced to the transaction callback for protocol handling. // Retry/backoff policy is intentionally deferred; a future change may add @@ -1388,6 +1394,12 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) bool isMaster = isMasterWIRE(); if (!isMaster) { +#if defined(__SAME53__) || defined(__SAME54__) + disableInterrupts(SERCOM_I2CS_INTENCLR_DRDY_Msk | + SERCOM_I2CS_INTENCLR_PREC_Msk); +#else + disableInterrupts(SERCOM_I2CS_INTENCLR_DRDY | SERCOM_I2CS_INTENCLR_PREC); +#endif // __SAME53__ / __SAME54__ const bool receivePending = _wireReceivePending; const bool requestPending = _wireRequestPending; const bool followupPending = receivePending || requestPending; @@ -1456,6 +1468,8 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) waitSyncBusySysOp(); + SercomWireError completionError = error; + // Undocumented HW limitation: DMA transfers must terminate with STOP and bus release. // After a DMA write, the host holds the bus ~7.33 us before the next transfer (Sr window). // Writing ADDR during that window leaves the bus in an undefined state and breaks @@ -1476,25 +1490,33 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) #endif // __SAME53__ / __SAME54__ }; - while (!busIsIdle() && - static_cast(micros() - stopSettleStart) < 4u) + while (!busIsIdle() && static_cast(micros() - stopSettleStart) < + kWireStopSettleTimeoutUs) ; - if (!busIsIdle()) - prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); + if (!busIsIdle()) { + completionError = SercomWireError::BUS_RELEASE_TIMEOUT; + // The device datasheet identifies CTRLA.SWRST as recovery for an I2C + // protocol hang. Preserve the cached Wire configuration and transaction + // queue, reset only the peripheral, then restore a known master state. + resetSERCOM(); + setMasterWIRE(); + } } // Preserve the tested async-DMA master retirement order: publish the // callback before inspecting chainNext, dequeuing, or restoring slave mode. if (txn && txn->onComplete) - txn->onComplete(txn->user, static_cast(error)); + txn->onComplete(txn->user, static_cast(completionError)); // Allow multi-phase I2C transactions to chain without dequeuing. - if (txn && txn->chainNext) { + if (txn && completionError == SercomWireError::SUCCESS && txn->chainNext) { txn->chainNext = false; // reset for next iteration startTransmissionWIRE(); return txn; } + if (txn) + txn->chainNext = false; SercomTxn* headTxn = nullptr; if (_txnQueue.peek(headTxn) && headTxn == txn) diff --git a/cores/arduino/SERCOM_Txn.h b/cores/arduino/SERCOM_Txn.h index 05e9ccafb..17fb7dde6 100644 --- a/cores/arduino/SERCOM_Txn.h +++ b/cores/arduino/SERCOM_Txn.h @@ -54,7 +54,8 @@ enum class SercomWireError : uint8_t SLAVE_TIMEOUT = 11, // Slave timeout (STATUS.SEXTTOUT) LENGTH_ERROR = 12, // LENERR when LEN/LENEN mismatch (STATUS.LENERR) UNKNOWN_ERROR = 13, // Error flag set but no specific bit matched - DMA_ERROR = 14 // DMAC transfer/descriptor bus error + DMA_ERROR = 14, // DMAC transfer/descriptor bus error + BUS_RELEASE_TIMEOUT = 15 // STOP issued but BUSSTATE did not return to IDLE }; // SPI error reporting (async callbacks) diff --git a/cores/arduino/SERCOM_inline.h b/cores/arduino/SERCOM_inline.h index c1ed99da2..681bad113 100644 --- a/cores/arduino/SERCOM_inline.h +++ b/cores/arduino/SERCOM_inline.h @@ -374,16 +374,7 @@ inline void SERCOM::dmaTxCallbackWIRE(Adafruit_ZeroDMA* dma) inst->_wire.txnIndex = inst->_wire.txnLength; inst->_dmaTxActive = false; - if (inst->isSlaveWIRE()) { - // DMA has written the final DATA byte, but the master's ACK/NACK is - // reported at the following DRDY boundary. Restore that one interrupt - // so onService() can release the slave transaction physically. -#if defined(__SAME53__) || defined(__SAME54__) - inst->enableInterrupts(SERCOM_I2CS_INTENSET_DRDY_Msk); -#else - inst->enableInterrupts(SERCOM_I2CS_INTENSET_DRDY); -#endif - } else { + if (!inst->isSlaveWIRE()) { inst->deferStopWIRE(SercomWireError::SUCCESS); } } diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 42b2ce1c7..a6357ab2f 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -183,20 +183,20 @@ inline void TwoWire::onService(void) #if defined(__SAME53__) || defined(__SAME54__) const bool rxNack = status & SERCOM_I2CM_STATUS_RXNACK_Msk; const bool wireError = flags & SERCOM_I2CM_INTFLAG_ERROR_Msk; - const bool slaveDrdy = flags & SERCOM_I2CS_INTFLAG_DRDY_Msk; + const bool slaveDrdy = + !isMaster && (flags & SERCOM_I2CS_INTFLAG_DRDY_Msk); #else const bool rxNack = status & SERCOM_I2CM_STATUS_RXNACK; const bool wireError = flags & SERCOM_I2CM_INTFLAG_ERROR; - const bool slaveDrdy = flags & SERCOM_I2CS_INTFLAG_DRDY; + const bool slaveDrdy = !isMaster && (flags & SERCOM_I2CS_INTFLAG_DRDY); #endif // __SAME53__ / __SAME54__ SercomTxn *activeTxn = sercom->getCurrentTxnWIRE(); - const bool effectiveSlaveRead = - activeTxn && (activeTxn->config & I2C_CFG_READ) != 0; - const bool actionableRxNack = - isMaster ? rxNack : slaveDrdy && effectiveSlaveRead && rxNack; + const bool rxSlaveNack = + rxNack && activeTxn && (activeTxn->config & I2C_CFG_READ) != 0; + const bool isRxNack = isMaster ? rxNack : rxSlaveNack; - if (actionableRxNack || wireError) { + if (isRxNack || wireError) { #if defined(__SAME53__) || defined(__SAME54__) const bool arbitrationLost = status & SERCOM_I2CM_STATUS_ARBLOST_Msk; const bool busError = status & SERCOM_I2CM_STATUS_BUSERR_Msk; @@ -220,7 +220,7 @@ inline void TwoWire::onService(void) SercomWireError error = SercomWireError::UNKNOWN_ERROR; - if (actionableRxNack) { + if (isRxNack) { error = isMaster && awaitingAddressAck ? SercomWireError::NACK_ON_ADDRESS : SercomWireError::NACK_ON_DATA; } else if (arbitrationLost) { From bb1a49a3bc1e0c065f35cbbc5e0e35dca4a54665 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 27 Jul 2026 06:26:57 -0400 Subject: [PATCH 67/79] feat(wire): stream slave DMA transfers --- cores/arduino/SERCOM.cpp | 6 ++-- cores/arduino/SERCOM.h | 1 + cores/arduino/SERCOM_inline.h | 63 +++++++++++++++++++++++------------ libraries/Adafruit_ZeroDMA | 2 +- 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 8ad08f6c3..c87102c11 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -962,10 +962,10 @@ void SERCOM::dmaRxSuspendCallbackWIRE(Adafruit_ZeroDMA *dma) { reinterpret_cast(DMAC->WRBADDR.reg); remaining = writeback[channel].BTCNT.reg; #endif // __SAME53__ / __SAME54__ - if (inst->_wire.txnLength >= remaining) - transferred = inst->_wire.txnLength - remaining; + if (inst->_wire.dmaBlockLength >= remaining) + transferred = inst->_wire.dmaBlockLength - remaining; } - inst->_wire.txnIndex = transferred; + inst->_wire.txnIndex += transferred; inst->dmaAbortRx(); inst->deferStopWIRE(SercomWireError::SUCCESS); } diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index ba137c901..42a6b695b 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -500,6 +500,7 @@ class SERCOM SercomTxn* currentTxn = nullptr; size_t txnIndex = 0; size_t txnLength = 0; + size_t dmaBlockLength = 0; } _wire; struct SpiConfig { diff --git a/cores/arduino/SERCOM_inline.h b/cores/arduino/SERCOM_inline.h index 681bad113..a14656b11 100644 --- a/cores/arduino/SERCOM_inline.h +++ b/cores/arduino/SERCOM_inline.h @@ -91,12 +91,20 @@ inline bool SERCOM::sendDataWIRE( void ) #ifdef USE_ZERODMA if (isDmaWIRE()) { DmaStatus value = DmaStatus::StartFailed; - if (!_dmaTxActive && !_dmaRxActive) + if (!_dmaTxActive && !_dmaRxActive) { + const size_t remaining = _wire.txnLength - _wire.txnIndex; + _wire.dmaBlockLength = + isSlaveWIRE() && remaining > 255u ? 255u : remaining; #if defined(__SAME53__) || defined(__SAME54__) - value = dmaStartTx(txn->txPtr, &sercom->I2CM.SERCOM_DATA, _wire.txnLength); + value = dmaStartTx(txn->txPtr + _wire.txnIndex, + &sercom->I2CM.SERCOM_DATA, + _wire.dmaBlockLength); #else - value = dmaStartTx(txn->txPtr, &sercom->I2CM.DATA.reg, _wire.txnLength); + value = dmaStartTx(txn->txPtr + _wire.txnIndex, + &sercom->I2CM.DATA.reg, + _wire.dmaBlockLength); #endif // __SAME53__ / __SAME54__ + } return value == DmaStatus::Ok; } #endif // USE_ZERODMA @@ -145,16 +153,24 @@ inline bool SERCOM::readDataWIRE( void ) if (txn == nullptr || txn->rxPtr == nullptr) return false; #ifdef USE_ZERODMA - if (isDmaWIRE()) { - DmaStatus value = DmaStatus::StartFailed; - if (!_dmaRxActive && !_dmaTxActive) + if (isDmaWIRE()) { + DmaStatus value = DmaStatus::StartFailed; + if (!_dmaRxActive && !_dmaTxActive) { + const size_t remaining = _wire.txnLength - _wire.txnIndex; + _wire.dmaBlockLength = + isSlaveWIRE() && remaining > 255u ? 255u : remaining; #if defined(__SAME53__) || defined(__SAME54__) - value = dmaStartRx(txn->rxPtr, &sercom->I2CM.SERCOM_DATA, _wire.txnLength); + value = dmaStartRx(txn->rxPtr + _wire.txnIndex, + &sercom->I2CM.SERCOM_DATA, + _wire.dmaBlockLength); #else - value = dmaStartRx(txn->rxPtr, &sercom->I2CM.DATA.reg, _wire.txnLength); + value = dmaStartRx(txn->rxPtr + _wire.txnIndex, + &sercom->I2CM.DATA.reg, + _wire.dmaBlockLength); #endif // __SAME53__ / __SAME54__ - return value == DmaStatus::Ok; } + return value == DmaStatus::Ok; + } #endif // USE_ZERODMA bool isMaster = isMasterWIRE(); @@ -256,6 +272,7 @@ inline void SERCOM::setTxnWIRE(SercomTxn* txn) { _wire.currentTxn = txn; _wire.txnIndex = 0; + _wire.dmaBlockLength = 0; _wire.returnValue = SercomWireError::SUCCESS; if (txn) @@ -268,13 +285,9 @@ inline void SERCOM::setTxnWIRE(SercomTxn* txn) const bool sclsm = sercom->I2CM.CTRLA.bit.SCLSM; #endif // __SAME53__ / __SAME54__ const size_t length = txn ? txn->length : 0u; - const bool dmaEligible = length > 0u && length <= 255u; + const bool dmaEligible = length > 0u; setDmaWIRE(dmaEligible); - if (sclsm && !dmaEligible) - _wire.returnValue = SercomWireError::DATA_TOO_LONG; - - if (isDmaWIRE()) - _wire.txnLength = (_wire.txnLength < 255u) ? _wire.txnLength : 255u; + (void)sclsm; #else _wire.useDma = false; @@ -372,9 +385,14 @@ inline void SERCOM::dmaTxCallbackWIRE(Adafruit_ZeroDMA* dma) // If the slave NACKs a master-write transaction before ADDR.LEN bytes, STOP // is generated automatically and STATUS.LENERR is raised with INTFLAG.ERROR. - inst->_wire.txnIndex = inst->_wire.txnLength; inst->_dmaTxActive = false; - if (!inst->isSlaveWIRE()) { + if (inst->isSlaveWIRE()) { + inst->_wire.txnIndex += inst->_wire.dmaBlockLength; + if (inst->_wire.txnIndex < inst->_wire.txnLength && + !inst->sendDataWIRE()) + inst->abortWIRE(SercomWireError::DMA_ERROR); + } else { + inst->_wire.txnIndex = inst->_wire.txnLength; inst->deferStopWIRE(SercomWireError::SUCCESS); } } @@ -391,12 +409,15 @@ inline void SERCOM::dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma) // Do NOT issue a manual STOP command - it conflicts with the automatic sequence. // The STOP is generated by hardware, not by software CMD write. - inst->_wire.txnIndex = inst->_wire.txnLength; inst->_dmaRxActive = false; - if (inst->isSlaveWIRE()) - inst->deferStopWIRE(SercomWireError::SUCCESS); - else + inst->_wire.txnIndex += inst->_wire.dmaBlockLength; + if (inst->isSlaveWIRE() && + inst->_wire.txnIndex < inst->_wire.txnLength) { + if (!inst->readDataWIRE()) + inst->abortWIRE(SercomWireError::DMA_ERROR); + } else { inst->deferStopWIRE(SercomWireError::SUCCESS); + } } inline void SERCOM::dmaErrorCallbackWIRE(Adafruit_ZeroDMA* dma) diff --git a/libraries/Adafruit_ZeroDMA b/libraries/Adafruit_ZeroDMA index 5a8ec1581..ae4842e49 160000 --- a/libraries/Adafruit_ZeroDMA +++ b/libraries/Adafruit_ZeroDMA @@ -1 +1 @@ -Subproject commit 5a8ec158176c7c3be7313169c9c13d3c9eecb99f +Subproject commit ae4842e4921c50d85bd9ae74f281e91506a545ff From b127b185e9b36d525062dfa1c8b625ec9d29741c Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 27 Jul 2026 07:09:08 -0400 Subject: [PATCH 68/79] fix(wire): clear shared error interrupt in slave mode --- libraries/Wire/Wire.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index a6357ab2f..d01184b09 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -247,7 +247,7 @@ inline void TwoWire::onService(void) if (isMaster && !arbitrationLost) sercom->prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); - if (isMaster && wireError) + if (wireError) sercom->clearINTFLAG(); sercom->deferStopWIRE(error); return; From 359700c47f854c755e9903e2742e2698ecafe82a Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 27 Jul 2026 07:52:10 -0400 Subject: [PATCH 69/79] feat(wire): expose semantic completion state --- cores/arduino/SERCOM.cpp | 65 ++++++++++++++++++++++++++++++++++++++ cores/arduino/SERCOM.h | 4 +++ cores/arduino/SERCOM_Txn.h | 30 ++++++++++++++++++ libraries/Wire/Wire.h | 3 ++ 4 files changed, 102 insertions(+) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index c87102c11..a4e2e76ce 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -1382,6 +1382,69 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) SercomTxn *txn = _wire.currentTxn; + SercomWireCompletionReport &report = _wire.lastCompletionReport; + report = {}; + report.error = error; + report.address = txn ? txn->address : 0u; + report.requestedLength = txn ? static_cast(txn->length) : 0u; + report.transferredLength = static_cast(_wire.txnIndex); + report.role = + isMasterWIRE() ? SercomWireRole::Master : SercomWireRole::Slave; + report.read = txn && (txn->config & I2C_CFG_READ); + + const auto refreshCompletionReport = [this, &report]() { + report.transferredLength = static_cast(_wire.txnIndex); + + if (report.role == SercomWireRole::Slave) { + report.bus = report.error == SercomWireError::SUCCESS + ? SercomWireBusDisposition::Released + : SercomWireBusDisposition::Unknown; + } else { + const uint16_t status = getSTATUS(); +#if defined(__SAME53__) || defined(__SAME54__) + const uint8_t busState = + static_cast((status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> + SERCOM_I2CM_STATUS_BUSSTATE_Pos); + const bool clockHeld = + (status & SERCOM_I2CM_STATUS_CLKHOLD_Msk) != 0u; +#else + const uint8_t busState = + static_cast((status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> + SERCOM_I2CM_STATUS_BUSSTATE_Pos); + const bool clockHeld = (status & SERCOM_I2CM_STATUS_CLKHOLD) != 0u; +#endif // __SAME53__ / __SAME54__ + + if (isBusOwnerWIRE()) + report.bus = SercomWireBusDisposition::LocalOwner; + else if (clockHeld) + report.bus = SercomWireBusDisposition::Unknown; + else if (busState == WIRE_IDLE_STATE) + report.bus = SercomWireBusDisposition::Released; + else if (busState == WIRE_BUSY_STATE) + report.bus = SercomWireBusDisposition::OtherOwner; + else + report.bus = SercomWireBusDisposition::Unknown; + } + + if (report.error == SercomWireError::SUCCESS) { + report.delivery = SercomWireDelivery::Complete; + } else if (report.error == SercomWireError::NACK_ON_ADDRESS || + report.error == SercomWireError::QUEUE_FULL || + report.error == SercomWireError::BUS_CONFLICT || + report.error == SercomWireError::BUS_STATE_UNKNOWN) { + report.delivery = report.transferredLength == 0u + ? SercomWireDelivery::None + : SercomWireDelivery::Ambiguous; + } else if (report.error == SercomWireError::NACK_ON_DATA) { + report.delivery = report.transferredLength == 0u + ? SercomWireDelivery::None + : SercomWireDelivery::Partial; + } else if (report.read && report.transferredLength != 0u) { + report.delivery = SercomWireDelivery::Partial; + } else { + report.delivery = SercomWireDelivery::Ambiguous; + } + }; // Explicit timeout cancellation is a newer entrance into the shared // PendSV retirement service. Keep it outside the tested normal master @@ -1418,6 +1481,7 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) dmaAbortRx(); #endif // USE_ZERODMA retireSlaveTransactionWIRE(followupPending); + refreshCompletionReport(); if (txn->onComplete) txn->onComplete(txn->user, static_cast(completionError)); } @@ -1504,6 +1568,7 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) } } + refreshCompletionReport(); // Preserve the tested async-DMA master retirement order: publish the // callback before inspecting chainNext, dequeuing, or restoring slave mode. if (txn && txn->onComplete) diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index 42a6b695b..10d84763e 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -335,6 +335,9 @@ class SERCOM inline size_t getTxnIndexWIRE(void) const { return _wire.txnIndex; } inline size_t getTxnLengthWIRE(void) const { return _wire.txnLength; } inline bool isActiveWIRE(void) const { return _wire.active; } + inline const SercomWireCompletionReport& lastCompletionReportWIRE(void) const { + return _wire.lastCompletionReport; + } inline bool isDBGSTOP( void ) const; inline void setDBGSTOP( bool stop ); @@ -501,6 +504,7 @@ class SERCOM size_t txnIndex = 0; size_t txnLength = 0; size_t dmaBlockLength = 0; + SercomWireCompletionReport lastCompletionReport = {}; } _wire; struct SpiConfig { diff --git a/cores/arduino/SERCOM_Txn.h b/cores/arduino/SERCOM_Txn.h index 17fb7dde6..dba332310 100644 --- a/cores/arduino/SERCOM_Txn.h +++ b/cores/arduino/SERCOM_Txn.h @@ -58,6 +58,36 @@ enum class SercomWireError : uint8_t BUS_RELEASE_TIMEOUT = 15 // STOP issued but BUSSTATE did not return to IDLE }; +enum class SercomWireRole : uint8_t { + Master, + Slave, +}; + +enum class SercomWireBusDisposition : uint8_t { + Released, + LocalOwner, + OtherOwner, + Unknown, +}; + +enum class SercomWireDelivery : uint8_t { + None, + Partial, + Complete, + Ambiguous, +}; + +struct SercomWireCompletionReport { + SercomWireError error = SercomWireError::SUCCESS; + uint16_t address = 0; + uint16_t requestedLength = 0; + uint16_t transferredLength = 0; + SercomWireRole role = SercomWireRole::Master; + SercomWireBusDisposition bus = SercomWireBusDisposition::Unknown; + SercomWireDelivery delivery = SercomWireDelivery::Ambiguous; + bool read = false; +}; + // SPI error reporting (async callbacks) enum class SercomSpiError : uint8_t { diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index d01184b09..ff6e0e729 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -56,6 +56,9 @@ class TwoWire : public Stream void *user = nullptr); bool abortTransmission( int status = static_cast(SercomWireError::MASTER_TIMEOUT)); + inline const SercomWireCompletionReport &lastCompletionReport(void) const { + return sercom->lastCompletionReportWIRE(); + } // If onComplete is nullptr, this blocks for legacy sync behavior. // If onComplete is non-null, this enqueues and returns immediately (async). From 10a7c1f76bf88a8bb1e8e03dc15e9c0bad85a8e4 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 27 Jul 2026 08:11:44 -0400 Subject: [PATCH 70/79] feat(wire): restart queued transfer after arbitration loss --- cores/arduino/SERCOM.cpp | 4 ---- libraries/Wire/Wire.h | 17 +++++++++++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index a4e2e76ce..35a67115c 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -1176,10 +1176,6 @@ SercomTxn* SERCOM::startTransmissionWIRE( void ) addr = (uint16_t)((addr << 1) | (read ? 1u : 0u)); if (!isBusOwnerWIRE()) { - if (isArbLostWIRE() && !isBusIdleWIRE()) { - stopTransmissionWIRE(SercomWireError::ARBITRATION_LOST); - return nullptr; - } if (isBusUnknownWIRE()) { stopTransmissionWIRE(SercomWireError::BUS_STATE_UNKNOWN); return nullptr; diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index ff6e0e729..59c03fdbf 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -221,13 +221,26 @@ inline void TwoWire::onService(void) const bool masterTimeout = isMaster && (masterExtendTimeout || lowTimeout); const bool slaveTimeout = slaveExtendTimeout || (!isMaster && lowTimeout); + if (isMaster && arbitrationLost) { +#ifdef USE_ZERODMA + sercom->dmaAbortTx(); + sercom->dmaAbortRx(); + sercom->setDmaWIRE(false); +#endif + // Arbitration loss releases SDA and SCL. Re-arm the same queue head by + // writing ADDR again. ADDR clears ARBLOST; when another master still + // owns the bus, SERCOM remains in BUSY and waits for IDLE before START. + sercom->clearINTFLAG(); + awaitingAddressAck = true; + sercom->startTransmissionWIRE(); + return; + } + SercomWireError error = SercomWireError::UNKNOWN_ERROR; if (isRxNack) { error = isMaster && awaitingAddressAck ? SercomWireError::NACK_ON_ADDRESS : SercomWireError::NACK_ON_DATA; - } else if (arbitrationLost) { - error = SercomWireError::ARBITRATION_LOST; } else if (busError) { error = SercomWireError::BUS_ERROR; } else if (masterTimeout) { From 40fadb8bd8f9884ad5c31470c5c89009d4054bdf Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 27 Jul 2026 08:17:49 -0400 Subject: [PATCH 71/79] chore(variant): correct Xplained debug script label --- variants/xplained_m4/debug_scripts/variant.gdb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/xplained_m4/debug_scripts/variant.gdb b/variants/xplained_m4/debug_scripts/variant.gdb index 17cecc0de..78a89f013 100644 --- a/variants/xplained_m4/debug_scripts/variant.gdb +++ b/variants/xplained_m4/debug_scripts/variant.gdb @@ -1,5 +1,5 @@ # -# SimIO Device M4 OpenOCD/GDB script. +# SAME54 Xplained Pro OpenOCD/GDB script. # # Copyright (c) 2014-2015 Arduino LLC. All right reserved. # From 4cf798bacd846297046ee37d48717a977c423ac2 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Mon, 10 Aug 2026 21:40:51 -0400 Subject: [PATCH 72/79] feat(wire): harden async DMA multimaster transactions --- cores/arduino/SERCOM.cpp | 168 ++++++++++++-- cores/arduino/SERCOM.h | 29 +++ cores/arduino/SERCOM_Txn.h | 1 + cores/arduino/SERCOM_WireBusErrorPolicy.h | 76 +++++++ cores/arduino/SERCOM_inline.h | 18 +- libraries/Wire/Wire.cpp | 41 +++- libraries/Wire/Wire.h | 265 +++++++++++++++++----- 7 files changed, 506 insertions(+), 92 deletions(-) create mode 100644 cores/arduino/SERCOM_WireBusErrorPolicy.h diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 35a67115c..bd611e6d9 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -18,6 +18,7 @@ */ #include "SERCOM.h" +#include "SERCOM_WireBusErrorPolicy.h" #include "Arduino.h" #include "PendSV.h" #include "variant.h" @@ -817,13 +818,21 @@ void SERCOM::clearQueueWIRE(void) // Ensure wire state is completely clean _wire.active = false; + _wire.slaveTransactionActive = false; _wire.currentTxn = nullptr; + _wire.slaveTxn = nullptr; _wire.txnIndex = 0; _wire.txnLength = 0; _wire.returnValue = SercomWireError::SUCCESS; _wire.retryCount = 0; _wire.releasePending = false; _wire.abortPending = false; + _wire.busErrorRecoveryPending = false; + _wire.busErrorRecoveryStartedUs = 0; + _wire.busErrorRecoveryDeadlineActive = false; + _wire.busErrorRecoveryDeadlineStartedUs = 0; + _wire.busErrorRecoveryArbitrationLost = false; + _wire.busErrorRecoveryCommandReady = false; #ifdef USE_ZERODMA _wireRxSuspendPending = false; #endif @@ -909,6 +918,12 @@ void SERCOM::initMasterWIRE( uint32_t baudrate ) void SERCOM::deferReceiveWIRE(void) { _wireReceivePending = true; _wire.returnValue = SercomWireError::SUCCESS; +#ifdef USE_ZERODMA + // The RX suspend callback publishes the completed byte count and owns the + // retirement dispatch. Do not let PendSV abort the channel first. + if (_dmaRxActive) + return; +#endif // USE_ZERODMA setPending((uint8_t)getSercomIndex()); } @@ -920,7 +935,7 @@ void SERCOM::deferRequestWIRE(void) { // descriptor is still being suspended. Its suspend callback owns the next // PendSV dispatch so the receive length is published before onRequest can // replace the active transaction. - if (_wireRxSuspendPending) + if (_wireRxSuspendPending || _dmaRxActive) return; #endif // USE_ZERODMA setPending((uint8_t)getSercomIndex()); @@ -975,6 +990,7 @@ SercomTxn *SERCOM::retireSlaveTransactionWIRE(bool reserveForFollowup) { SercomTxn *txn = _wire.currentTxn; _wire.retryCount = 0; _wire.active = reserveForFollowup; + _wire.slaveTransactionActive = false; _wire.currentTxn = nullptr; return txn; } @@ -1114,10 +1130,9 @@ SercomTxn* SERCOM::startTransmissionWIRE( void ) if (!_dmaConfigured || !_dmaTx || !_dmaRx) return nullptr; - // Slave RX must release AMATCH before enabling DATA-triggered DMA. - // Slave TX must have DATA DMA armed before AMATCH releases SCL. - if (!slaveTransmit) - clearAmatch(); + // AMATCH holds SCL low while software prepares the transfer. Arm the + // DATA-triggered DMA channel before releasing AMATCH so the first DRDY + // request cannot arrive before DMAC is ready. const bool started = slaveTransmit ? sendDataWIRE() : readDataWIRE(); if (!started) return nullptr; @@ -1127,9 +1142,7 @@ SercomTxn* SERCOM::startTransmissionWIRE( void ) #else enableInterrupts(SERCOM_I2CS_INTENSET_DRDY | SERCOM_I2CS_INTENSET_PREC); #endif // __SAME53__ / __SAME54__ - if (slaveTransmit) { - clearAmatch(); - } + clearAmatch(); } else { #if defined(__SAME53__) || defined(__SAME54__) enableInterrupts(SERCOM_I2CS_INTENSET_DRDY_Msk | @@ -1150,6 +1163,11 @@ SercomTxn* SERCOM::startTransmissionWIRE( void ) #endif // __SAME53__ / __SAME54__ clearAmatch(); #endif // USE_ZERODMA +#if defined(__SAME53__) || defined(__SAME54__) + enableInterrupts(SERCOM_I2CS_INTENSET_AMATCH_Msk); +#else + enableInterrupts(SERCOM_I2CS_INTENSET_AMATCH); +#endif // __SAME53__ / __SAME54__ return txn; } @@ -1163,8 +1181,10 @@ SercomTxn* SERCOM::startTransmissionWIRE( void ) if (!_txnQueue.peek(txn)) return nullptr; - if (txn != _wire.currentTxn) + if (txn != _wire.currentTxn) { _wire.retryCount = 0; + _wire.busErrorRecoveryDeadlineActive = false; + } _wire.currentTxn = txn; _wire.txnIndex = 0; @@ -1270,6 +1290,14 @@ bool SERCOM::enqueueWIRE(SercomTxn* txn) if (!_txnQueue.store(txn)) return false; + if (isSlaveWIRE() && _wire.currentTxn == _wire.slaveTxn && + !_wire.slaveTransactionActive) { + _wire.active = false; + _wire.currentTxn = nullptr; + setMasterWIRE(); + return startTransmissionWIRE() != nullptr; + } + if (!_wire.active) { // dI2C uses a single SERCOM in both slave and master roles. A queued // master operation is the role-transition point; callers must not @@ -1363,6 +1391,92 @@ bool SERCOM::serviceAbortWIRE(void) { return true; } +void SERCOM::deferBusErrorRecoveryWIRE(bool arbitrationLost, + bool commandReady) { +#ifdef USE_ZERODMA + dmaAbortTx(); + dmaAbortRx(); + setDmaWIRE(false); +#endif + _wire.busErrorRecoveryPending = true; + _wire.busErrorRecoveryStartedUs = micros(); + if (!_wire.busErrorRecoveryDeadlineActive) { + _wire.busErrorRecoveryDeadlineActive = true; + _wire.busErrorRecoveryDeadlineStartedUs = _wire.busErrorRecoveryStartedUs; + } + _wire.busErrorRecoveryArbitrationLost = arbitrationLost; + _wire.busErrorRecoveryCommandReady = commandReady; + setPending((uint8_t)getSercomIndex()); +} + +SercomTxn *SERCOM::serviceBusErrorRecoveryWIRE(void) { + if (!_wire.busErrorRecoveryPending) + return _wire.currentTxn; + + if (_wire.abortPending) { + _wire.busErrorRecoveryPending = false; + return serviceAbortWIRE() ? _wire.currentTxn : nullptr; + } + + SercomTxn *txn = _wire.currentTxn; + if (!txn) { + _wire.busErrorRecoveryPending = false; + return nullptr; + } + + if (simio::wire::busErrorDeadlineExpired( + _wire.busErrorRecoveryDeadlineStartedUs, micros(), + _wire.busErrorRecoveryDeadlineActive)) { + _wire.busErrorRecoveryPending = false; + _wire.busErrorRecoveryDeadlineActive = false; + return stopTransmissionWIRE(SercomWireError::BUS_ERROR); + } + + simio::wire::BusState busState = simio::wire::BusState::Unknown; + if (isBusIdleWIRE()) + busState = simio::wire::BusState::Idle; + else if (isBusOwnerWIRE()) + busState = simio::wire::BusState::Owner; + else if (isBusBusyWIRE()) + busState = simio::wire::BusState::Busy; + + const auto action = simio::wire::decideBusErrorAction( + {true, busState, _wire.busErrorRecoveryArbitrationLost, + _wire.busErrorRecoveryCommandReady, false, true, true}); + if (action == simio::wire::BusErrorAction::RestartQueueHead) { + _wire.busErrorRecoveryPending = false; + return startTransmissionWIRE(); + } + + if (action == simio::wire::BusErrorAction::EstablishIdle) { +#if defined(__SAME53__) || defined(__SAME54__) + sercom->I2CM.SERCOM_STATUS = + (sercom->I2CM.SERCOM_STATUS & ~SERCOM_I2CM_STATUS_BUSSTATE_Msk) | + SERCOM_I2CM_STATUS_BUSSTATE(WIRE_IDLE_STATE); +#else + sercom->I2CM.STATUS.bit.BUSSTATE = WIRE_IDLE_STATE; +#endif // __SAME53__ / __SAME54__ + waitSyncBusySysOp(); + _wire.busErrorRecoveryPending = false; + return startTransmissionWIRE(); + } + + constexpr uint32_t kBusErrorRecoveryTimeoutUs = 100u; + const bool timedOut = + static_cast(micros() - _wire.busErrorRecoveryStartedUs) >= + kBusErrorRecoveryTimeoutUs; + if (simio::wire::decideBusErrorWaitAction(busState, timedOut) == + simio::wire::BusErrorAction::WaitForBusState) { + setPending((uint8_t)getSercomIndex()); + return txn; + } + + resetSERCOM(); + setMasterWIRE(); + _wire.busErrorRecoveryPending = false; + return startTransmissionWIRE(); +} + SercomTxn* SERCOM::stopTransmissionWIRE( void ) { return stopTransmissionWIRE( _wire.returnValue ); @@ -1371,11 +1485,12 @@ SercomTxn* SERCOM::stopTransmissionWIRE( void ) SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) { constexpr uint32_t kWireStopSettleTimeoutUs = 100u; + if (_wire.busErrorRecoveryPending) + return serviceBusErrorRecoveryWIRE(); // Policy: only auto-retry recoverable bus-state errors here. All other // errors are surfaced to the transaction callback for protocol handling. // Retry/backoff policy is intentionally deferred; a future change may add // a retry budget or tick-based delay if needed. - SercomTxn *txn = _wire.currentTxn; SercomWireCompletionReport &report = _wire.lastCompletionReport; @@ -1453,6 +1568,7 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) bool isMaster = isMasterWIRE(); if (!isMaster) { + const bool stopDetected = isStopDetectedWIRE(); #if defined(__SAME53__) || defined(__SAME54__) disableInterrupts(SERCOM_I2CS_INTENCLR_DRDY_Msk | SERCOM_I2CS_INTENCLR_PREC_Msk); @@ -1463,14 +1579,29 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) const bool requestPending = _wireRequestPending; const bool followupPending = receivePending || requestPending; SercomWireError completionError = error; - if (txn && error == SercomWireError::NACK_ON_DATA) { - prepareSlaveCommandBitsWIRE(WIRE_SLAVE_ACT_COMPLETE); + const bool slaveTransmit = + txn && (txn->config & I2C_CFG_READ) != 0; + if (slaveTransmit && + (error == SercomWireError::SUCCESS || + error == SercomWireError::NACK_ON_DATA)) { + if (!stopDetected) + prepareSlaveCommandBitsWIRE(WIRE_SLAVE_ACT_COMPLETE); // A master NACK is the expected physical end of a slave response. Keep // NACK_ON_DATA as the stop/release reason, but publish successful request // completion after the slave has released the bus. - completionError = SercomWireError::SUCCESS; + if (error == SercomWireError::NACK_ON_DATA) + completionError = SercomWireError::SUCCESS; } + if (stopDetected) { +#if defined(__SAME53__) || defined(__SAME54__) + clearINTFLAG(SERCOM_I2CS_INTFLAG_PREC_Msk); +#else + clearINTFLAG(SERCOM_I2CS_INTFLAG_PREC); +#endif // __SAME53__ / __SAME54__ + } + _wire.releasePending = false; + if (txn) { #ifdef USE_ZERODMA dmaAbortTx(); @@ -1494,8 +1625,8 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) _wireRequestCb(_wireCallbackUser); } - if (_wire.currentTxn == nullptr) - retireSlaveTransactionWIRE(false); + if (_wire.currentTxn == nullptr && _wire.slaveTxn != nullptr) + setTxnWIRE(_wire.slaveTxn); startNextQueuedWIRE(); return _wire.currentTxn; @@ -1563,7 +1694,6 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) setMasterWIRE(); } } - refreshCompletionReport(); // Preserve the tested async-DMA master retirement order: publish the // callback before inspecting chainNext, dequeuing, or restoring slave mode. @@ -1586,6 +1716,8 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) _wire.retryCount = 0; _wire.active = false; _wire.currentTxn = nullptr; + _wire.busErrorRecoveryPending = false; + _wire.busErrorRecoveryDeadlineActive = false; SercomTxn *next = nullptr; if (_txnQueue.peek(next)) @@ -1601,8 +1733,10 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) SERCOM_I2CM_INTENCLR_SB; #endif // __SAME53__ / __SAME54__ - if (_wire.slaveConfigured) + if (_wire.slaveConfigured) { setSlaveWIRE(); + setTxnWIRE(_wire.slaveTxn); + } } return txn; diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index 10d84763e..39fa92aa0 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -207,12 +207,14 @@ class SERCOM inline uint8_t getINTFLAG( void ) const { return sercom->I2CM.SERCOM_INTFLAG; } inline uint16_t getSTATUS( void ) const { return sercom->I2CM.SERCOM_STATUS; } inline void clearINTFLAG(uint8_t mask = 0xFF) { sercom->I2CM.SERCOM_INTFLAG = mask; } + inline void clearStatusWIRE(uint16_t mask) { sercom->I2CM.SERCOM_STATUS = mask; } #else inline void disableInterrupts(uint8_t mask) { sercom->I2CM.INTENCLR.reg = mask; } inline void enableInterrupts(uint8_t mask) { sercom->I2CM.INTENSET.reg = mask; } inline uint8_t getINTFLAG( void ) const { return sercom->I2CM.INTFLAG.reg; } inline uint16_t getSTATUS( void ) const { return sercom->I2CM.STATUS.reg; } inline void clearINTFLAG(uint8_t mask = 0xFF) { sercom->I2CM.INTFLAG.reg = mask; } + inline void clearStatusWIRE(uint16_t mask) { sercom->I2CM.STATUS.reg = mask; } #endif // __SAME53__ / __SAME54__ /* ========== UART ========== */ @@ -280,6 +282,22 @@ class SERCOM void initSlaveWIRE(uint16_t address, bool enableGeneralCall = false, uint8_t speed = 0x0, bool enable10Bit = false) ; void initMasterWIRE(uint32_t baudrate) ; inline void setTxnWIRE(SercomTxn* txn); + inline void setSlaveTxnWIRE(SercomTxn* txn); + inline bool isSlaveTransactionActiveWIRE(void) const { + return _wire.slaveTransactionActive; + } + inline void markSlaveTransactionActiveWIRE(void) { + _wire.slaveTransactionActive = true; + } + inline void discardIdleSlaveTransactionWIRE(void) { + if (!_wire.slaveTransactionActive && + _wire.currentTxn == _wire.slaveTxn) { + _wire.active = false; + _wire.currentTxn = nullptr; + _wire.txnIndex = 0; + _wire.txnLength = 0; + } + } inline void setDmaWIRE(bool useDma) { _wire.useDma = useDma; } inline bool isDmaWIRE(void) const { return _wire.useDma; } inline void markBusReleasePendingWIRE(void) { _wire.releasePending = true; } @@ -313,6 +331,9 @@ class SERCOM inline bool isAbortPendingWIRE(void) const { return _wire.abortPending; } bool serviceAbortWIRE(void); void deferStopWIRE(SercomWireError error); + void deferBusErrorRecoveryWIRE(bool arbitrationLost, + bool commandReady); + SercomTxn* serviceBusErrorRecoveryWIRE(void); inline bool sendDataWIRE( void ) ; inline bool isMasterWIRE( void ); @@ -495,12 +516,20 @@ class SERCOM bool slaveConfigured = false; // return to slave mode after queued master work bool useDma = false; // per-transaction DMA use for master/slave modes bool active = false; // active transaction in progress + bool slaveTransactionActive = false; // AMATCH observed for cached slave descriptor bool releasePending = false; // STOP requested; callback waits until OWNER clears bool abortPending = false; // wait for MB/SB before issuing timeout STOP + bool busErrorRecoveryPending = false; + uint32_t busErrorRecoveryStartedUs = 0; + bool busErrorRecoveryDeadlineActive = false; + uint32_t busErrorRecoveryDeadlineStartedUs = 0; + bool busErrorRecoveryArbitrationLost = false; + bool busErrorRecoveryCommandReady = false; SercomWireError abortError = SercomWireError::MASTER_TIMEOUT; uint8_t retryCount = 0; // retry count for recoverable bus errors SercomWireError returnValue = SercomWireError::SUCCESS; SercomTxn* currentTxn = nullptr; + SercomTxn* slaveTxn = nullptr; size_t txnIndex = 0; size_t txnLength = 0; size_t dmaBlockLength = 0; diff --git a/cores/arduino/SERCOM_Txn.h b/cores/arduino/SERCOM_Txn.h index dba332310..aec16dc66 100644 --- a/cores/arduino/SERCOM_Txn.h +++ b/cores/arduino/SERCOM_Txn.h @@ -110,6 +110,7 @@ enum : uint16_t { I2C_CFG_CRC = 1u << 2, I2C_CFG_10BIT = 1u << 3, I2C_CFG_NODMA = 1u << 4, + I2C_CFG_REPLAY_SAFE = 1u << 5, }; // I2C STATUS register error-clear masks (write to I2CM.STATUS or I2CS.STATUS) diff --git a/cores/arduino/SERCOM_WireBusErrorPolicy.h b/cores/arduino/SERCOM_WireBusErrorPolicy.h new file mode 100644 index 000000000..ae465a40a --- /dev/null +++ b/cores/arduino/SERCOM_WireBusErrorPolicy.h @@ -0,0 +1,76 @@ +#ifndef SERCOM_WIRE_BUS_ERROR_POLICY_H +#define SERCOM_WIRE_BUS_ERROR_POLICY_H + +#include + +namespace simio { +namespace wire { + +enum class BusState : uint8_t { + Unknown = 0, + Idle = 1, + Owner = 2, + Busy = 3, +}; + +enum class BusErrorAction : uint8_t { + ClearSlaveWarning, + RestartQueueHead, + WaitForBusState, + WaitForArbitrationRelease, + WaitAtCommandPoint, + EstablishIdle, + ResetPeripheralAndRestart, + RetireAmbiguous, + RetireTerminal, +}; + +struct BusErrorContext { + bool master; + BusState busState; + bool arbitrationLost; + bool commandReady; + bool terminalCondition; + bool replayAllowed; + bool transactionActive; +}; + +constexpr BusErrorAction decideBusErrorAction(BusErrorContext context) { + return !context.master + ? BusErrorAction::ClearSlaveWarning + : !context.transactionActive || !context.replayAllowed + ? BusErrorAction::RetireAmbiguous + : context.terminalCondition + ? BusErrorAction::RetireTerminal + : context.busState == BusState::Busy || + context.busState == BusState::Idle + ? BusErrorAction::RestartQueueHead + : context.busState == BusState::Unknown + ? BusErrorAction::EstablishIdle + : context.arbitrationLost + ? BusErrorAction::WaitForArbitrationRelease + : context.commandReady ? BusErrorAction::WaitAtCommandPoint + : BusErrorAction::WaitForBusState; +} + +constexpr uint32_t kBusErrorRecoveryDeadlineUs = 100000u; + +constexpr bool busErrorDeadlineExpired(uint32_t startedUs, uint32_t nowUs, + bool active) { + return active && + static_cast(nowUs - startedUs) >= + kBusErrorRecoveryDeadlineUs; +} + +constexpr BusErrorAction decideBusErrorWaitAction(BusState busState, + bool timedOut) { + return busState == BusState::Busy || busState == BusState::Idle + ? BusErrorAction::RestartQueueHead + : timedOut ? BusErrorAction::ResetPeripheralAndRestart + : BusErrorAction::WaitForBusState; +} + +} // namespace wire +} // namespace simio + +#endif diff --git a/cores/arduino/SERCOM_inline.h b/cores/arduino/SERCOM_inline.h index a14656b11..fcc6f3199 100644 --- a/cores/arduino/SERCOM_inline.h +++ b/cores/arduino/SERCOM_inline.h @@ -304,6 +304,13 @@ inline void SERCOM::setTxnWIRE(SercomTxn* txn) _wire.active = true; } +inline void SERCOM::setSlaveTxnWIRE(SercomTxn* txn) +{ + _wire.slaveTxn = txn; + if (isSlaveWIRE() && !_wire.active) + setTxnWIRE(txn); +} + #if defined(__SAME53__) || defined(__SAME54__) inline void SERCOM::prepareNackBitWIRE( void ) { sercom->I2CM.SERCOM_CTRLB |= SERCOM_I2CM_CTRLB_ACKACT_Msk; } @@ -388,9 +395,10 @@ inline void SERCOM::dmaTxCallbackWIRE(Adafruit_ZeroDMA* dma) inst->_dmaTxActive = false; if (inst->isSlaveWIRE()) { inst->_wire.txnIndex += inst->_wire.dmaBlockLength; - if (inst->_wire.txnIndex < inst->_wire.txnLength && - !inst->sendDataWIRE()) - inst->abortWIRE(SercomWireError::DMA_ERROR); + if (inst->_wire.txnIndex < inst->_wire.txnLength) { + if (!inst->sendDataWIRE()) + inst->abortWIRE(SercomWireError::DMA_ERROR); + } } else { inst->_wire.txnIndex = inst->_wire.txnLength; inst->deferStopWIRE(SercomWireError::SUCCESS); @@ -410,6 +418,10 @@ inline void SERCOM::dmaRxCallbackWIRE(Adafruit_ZeroDMA* dma) // The STOP is generated by hardware, not by software CMD write. inst->_dmaRxActive = false; + // Transfer-complete and suspend can become pending together at an exact + // descriptor boundary. Completion owns retirement in that case; do not let + // the abandoned suspend intent suppress suspension of the next RX phase. + inst->_wireRxSuspendPending = false; inst->_wire.txnIndex += inst->_wire.dmaBlockLength; if (inst->isSlaveWIRE() && inst->_wire.txnIndex < inst->_wire.txnLength) { diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index 107d3c20a..b69c223a1 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -209,6 +209,7 @@ bool TwoWire::begin(uint16_t address, bool enableGeneralCall, uint8_t speed, sercom->enableWIRE(); sercom->registerReceiveWIRE(&TwoWire::onDeferredReceive, this); sercom->registerRequestWIRE(&TwoWire::onDeferredRequest, this); + prepareSlaveReceive(); return true; } @@ -229,7 +230,8 @@ bool TwoWire::abortTransmission(int status) { } uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit, uint8_t* rxBuffer, - void (*onComplete)(void* user, int status), void* user) + void (*onComplete)(void* user, int status), void* user, + bool replaySafe) { if(quantity == 0) return 0; @@ -246,7 +248,8 @@ uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit, uin loader.length = ( quantity > WIRE_BUFFER_LENGTH) ? WIRE_BUFFER_LENGTH : quantity; } - loader.config = I2C_CFG_READ | (stopBit ? I2C_CFG_STOP : 0); + loader.config = I2C_CFG_READ | (stopBit ? I2C_CFG_STOP : 0) | + (replaySafe ? I2C_CFG_REPLAY_SAFE : 0); loader.address = address; // Allocate fresh transaction from pool and copy loader data @@ -310,6 +313,13 @@ void TwoWire::beginTransmission(uint8_t address) { transmissionBegun = true; } +void TwoWire::setTransactionReplaySafe(bool replaySafe) { + if (replaySafe) + loader.config |= I2C_CFG_REPLAY_SAFE; + else + loader.config &= ~I2C_CFG_REPLAY_SAFE; +} + // Errors: // 0 : Success // 1 : Data too long @@ -333,7 +343,8 @@ uint8_t TwoWire::endTransmission(bool stopBit, txn->chainNext = false; // Set parameters that weren't known during beginTransmission/write - txn->config = stopBit ? I2C_CFG_STOP : 0; + txn->config = (loader.config & I2C_CFG_REPLAY_SAFE) | + (stopBit ? I2C_CFG_STOP : 0); if (onComplete) { txn->onComplete = onComplete; txn->user = (user == nullptr) ? txn : user; @@ -558,19 +569,25 @@ void TwoWire::onTxnComplete(void* user, int status) self->txnDone = true; } +void TwoWire::prepareSlaveReceive() { + rxLength = 0; + rxIndex = 0; + slaveTxn = SercomTxn{}; + slaveTxn.rxPtr = rxBufferPtr; + slaveTxn.length = rxBufferCapacity; + slaveTxn.config = 0; + slaveTxn.onComplete = &TwoWire::onSlaveReceiveComplete; + slaveTxn.user = this; + sercom->setSlaveTxnWIRE(&slaveTxn); +} + void TwoWire::onDeferredReceive(void *user) { if (!user) return; TwoWire *self = static_cast(user); - self->rxLength = 0; - self->rxIndex = 0; - self->slaveTxn = SercomTxn{}; - self->slaveTxn.rxPtr = self->rxBufferPtr; - self->slaveTxn.length = self->rxBufferCapacity; - self->slaveTxn.config = 0; - self->slaveTxn.onComplete = &TwoWire::onSlaveReceiveComplete; - self->slaveTxn.user = self; + self->prepareSlaveReceive(); self->sercom->setTxnWIRE(&self->slaveTxn); + self->sercom->markSlaveTransactionActiveWIRE(); if (!self->sercom->startTransmissionWIRE()) { self->sercom->deferStopWIRE(SercomWireError::DMA_ERROR); return; @@ -597,6 +614,7 @@ void TwoWire::onDeferredRequest(void *user) { self->slaveTxn.onComplete = &TwoWire::onSlaveRequestComplete; self->slaveTxn.user = self; self->sercom->setTxnWIRE(&self->slaveTxn); + self->sercom->markSlaveTransactionActiveWIRE(); if (!self->sercom->startTransmissionWIRE()) { self->sercom->deferStopWIRE(SercomWireError::DMA_ERROR); return; @@ -624,6 +642,7 @@ void TwoWire::onSlaveRequestComplete(void *user, int status) { if (self->requestCompletionCallback) self->requestCompletionCallback(self->requestCompletionUser, static_cast(status)); + self->prepareSlaveReceive(); } #if WIRE_INTERFACES_COUNT > 0 diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 59c03fdbf..7a7faf870 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -25,6 +25,7 @@ #include "RingBuffer.h" #include "SERCOM.h" +#include "SERCOM_WireBusErrorPolicy.h" #include // WIRE_HAS_END means Wire has end() @@ -49,6 +50,7 @@ class TwoWire : public Stream void setClock(uint32_t); void beginTransmission(uint8_t); + void setTransactionReplaySafe(bool replaySafe = true); // If onComplete is nullptr, this blocks for legacy sync behavior. // If onComplete is non-null, this enqueues and returns immediately (async). uint8_t endTransmission(bool stopBit = true, @@ -67,7 +69,7 @@ class TwoWire : public Stream uint8_t requestFrom(uint8_t address, size_t quantity, bool stopBit = true, uint8_t *rxBuffer = nullptr, void (*onComplete)(void *user, int status) = nullptr, - void *user = nullptr); + void *user = nullptr, bool replaySafe = false); size_t write(uint8_t data); // 3-arg write: when setExternal=true, data is used directly (zero-copy) and @@ -141,6 +143,7 @@ class TwoWire : public Stream void *requestCompletionUser; static void onTxnComplete(void *user, int status); + void prepareSlaveReceive(); static void onDeferredReceive(void *user); static void onDeferredRequest(void *user); static void onSlaveReceiveComplete(void *user, int status); @@ -188,10 +191,18 @@ inline void TwoWire::onService(void) const bool wireError = flags & SERCOM_I2CM_INTFLAG_ERROR_Msk; const bool slaveDrdy = !isMaster && (flags & SERCOM_I2CS_INTFLAG_DRDY_Msk); + const bool slavePrec = + !isMaster && (flags & SERCOM_I2CS_INTFLAG_PREC_Msk); + const bool slaveAmatch = + !isMaster && (flags & SERCOM_I2CS_INTFLAG_AMATCH_Msk); #else const bool rxNack = status & SERCOM_I2CM_STATUS_RXNACK; const bool wireError = flags & SERCOM_I2CM_INTFLAG_ERROR; const bool slaveDrdy = !isMaster && (flags & SERCOM_I2CS_INTFLAG_DRDY); + const bool slavePrec = + !isMaster && (flags & SERCOM_I2CS_INTFLAG_PREC); + const bool slaveAmatch = + !isMaster && (flags & SERCOM_I2CS_INTFLAG_AMATCH); #endif // __SAME53__ / __SAME54__ SercomTxn *activeTxn = sercom->getCurrentTxnWIRE(); @@ -199,10 +210,54 @@ inline void TwoWire::onService(void) rxNack && activeTxn && (activeTxn->config & I2C_CFG_READ) != 0; const bool isRxNack = isMaster ? rxNack : rxSlaveNack; - if (isRxNack || wireError) { +#if defined(__SAME53__) || defined(__SAME54__) + const bool busError = status & SERCOM_I2CM_STATUS_BUSERR_Msk; +#else + const bool busError = status & SERCOM_I2CM_STATUS_BUSERR; +#endif // __SAME53__ / __SAME54__ + const bool slaveBusError = !isMaster && wireError && busError; + if (slaveBusError) { +#if defined(__SAME53__) || defined(__SAME54__) + sercom->clearStatusWIRE(SERCOM_I2CS_STATUS_BUSERR_Msk); + sercom->clearINTFLAG(SERCOM_I2CS_INTFLAG_ERROR_Msk); +#else + sercom->clearStatusWIRE(SERCOM_I2CS_STATUS_BUSERR); + sercom->clearINTFLAG(SERCOM_I2CS_INTFLAG_ERROR); +#endif // __SAME53__ / __SAME54__ + } + + if (slavePrec && !slaveAmatch) { + if (!sercom->isSlaveTransactionActiveWIRE()) { + sercom->clearINTFLAG(); + return; + } + const bool slaveTransmit = + activeTxn && (activeTxn->config & I2C_CFG_READ) != 0; +#ifdef USE_ZERODMA + if (slaveTransmit && sercom->dmaTxBusy()) + sercom->dmaAbortTx(); +#endif // USE_ZERODMA + if (slaveTransmit) + sercom->deferStopWIRE(SercomWireError::SUCCESS); + else if (activeTxn) + sercom->deferReceiveCompleteWIRE(); + else + sercom->clearINTFLAG(); + return; + } + + bool continueOwnedArbitration = false; + if (isRxNack || (wireError && !slaveBusError)) { + if (!isMaster && isRxNack) { +#ifdef USE_ZERODMA + if (sercom->dmaTxBusy()) + sercom->dmaAbortTx(); +#endif // USE_ZERODMA + sercom->deferStopWIRE(SercomWireError::NACK_ON_DATA); + return; + } #if defined(__SAME53__) || defined(__SAME54__) const bool arbitrationLost = status & SERCOM_I2CM_STATUS_ARBLOST_Msk; - const bool busError = status & SERCOM_I2CM_STATUS_BUSERR_Msk; const bool lowTimeout = status & SERCOM_I2CM_STATUS_LOWTOUT_Msk; const bool slaveExtendTimeout = status & SERCOM_I2CM_STATUS_SEXTTOUT_Msk; const bool masterExtendTimeout = @@ -211,7 +266,6 @@ inline void TwoWire::onService(void) isMaster && (status & SERCOM_I2CM_STATUS_LENERR_Msk); #else const bool arbitrationLost = status & SERCOM_I2CM_STATUS_ARBLOST; - const bool busError = status & SERCOM_I2CM_STATUS_BUSERR; const bool lowTimeout = status & SERCOM_I2CM_STATUS_LOWTOUT; const bool slaveExtendTimeout = status & SERCOM_I2CM_STATUS_SEXTTOUT; const bool masterExtendTimeout = @@ -220,53 +274,114 @@ inline void TwoWire::onService(void) #endif // __SAME53__ / __SAME54__ const bool masterTimeout = isMaster && (masterExtendTimeout || lowTimeout); const bool slaveTimeout = slaveExtendTimeout || (!isMaster && lowTimeout); + const bool terminalCondition = + isRxNack || masterTimeout || slaveTimeout || lengthError; + const bool arbitrationOnly = !isRxNack && !busError && !masterTimeout && + !slaveTimeout && !lengthError; - if (isMaster && arbitrationLost) { + bool commandReady = false; +#if defined(__SAME53__) || defined(__SAME54__) + commandReady = + (flags & (SERCOM_I2CM_INTFLAG_MB_Msk | SERCOM_I2CM_INTFLAG_SB_Msk)) != + 0u; +#else + commandReady = + (flags & (SERCOM_I2CM_INTFLAG_MB | SERCOM_I2CM_INTFLAG_SB)) != 0u; +#endif // __SAME53__ / __SAME54__ + + if (isMaster && busError) { + simio::wire::BusState busState = simio::wire::BusState::Unknown; + if (sercom->isBusIdleWIRE()) + busState = simio::wire::BusState::Idle; + else if (sercom->isBusOwnerWIRE()) + busState = simio::wire::BusState::Owner; + else if (sercom->isBusBusyWIRE()) + busState = simio::wire::BusState::Busy; + const bool replayAllowed = + activeTxn && (activeTxn->config & I2C_CFG_REPLAY_SAFE); + const auto busErrorAction = simio::wire::decideBusErrorAction( + {true, busState, arbitrationLost, commandReady, terminalCondition, + replayAllowed, activeTxn != nullptr}); + if (busErrorAction != simio::wire::BusErrorAction::RetireAmbiguous && + busErrorAction != simio::wire::BusErrorAction::RetireTerminal) { +#if defined(__SAME53__) || defined(__SAME54__) + sercom->clearStatusWIRE(SERCOM_I2CM_STATUS_BUSERR_Msk | + SERCOM_I2CM_STATUS_ARBLOST_Msk); + sercom->clearINTFLAG(SERCOM_I2CM_INTFLAG_ERROR_Msk); +#else + sercom->clearStatusWIRE(SERCOM_I2CM_STATUS_BUSERR | + SERCOM_I2CM_STATUS_ARBLOST); + sercom->clearINTFLAG(SERCOM_I2CM_INTFLAG_ERROR); +#endif // __SAME53__ / __SAME54__ + sercom->deferBusErrorRecoveryWIRE(arbitrationLost, commandReady); + return; + } + } + + if (isMaster && arbitrationLost && !busError) { + if (sercom->isBusOwnerWIRE() && arbitrationOnly) { + // ARBLOST normally transitions OWNER to BUSY. If OWNER remains set, + // rewriting ADDR would request a repeated START and restart a + // transaction that the hardware still owns. Clear only the error + // indications and preserve MB/SB so normal master service continues. +#if defined(__SAME53__) || defined(__SAME54__) + sercom->clearStatusWIRE(SERCOM_I2CM_STATUS_ARBLOST_Msk); + sercom->clearINTFLAG(SERCOM_I2CM_INTFLAG_ERROR_Msk); +#else + sercom->clearStatusWIRE(SERCOM_I2CM_STATUS_ARBLOST); + sercom->clearINTFLAG(SERCOM_I2CM_INTFLAG_ERROR); +#endif // __SAME53__ / __SAME54__ + continueOwnedArbitration = true; + } else { #ifdef USE_ZERODMA - sercom->dmaAbortTx(); - sercom->dmaAbortRx(); - sercom->setDmaWIRE(false); + sercom->dmaAbortTx(); + sercom->dmaAbortRx(); + sercom->setDmaWIRE(false); #endif - // Arbitration loss releases SDA and SCL. Re-arm the same queue head by - // writing ADDR again. ADDR clears ARBLOST; when another master still - // owns the bus, SERCOM remains in BUSY and waits for IDLE before START. - sercom->clearINTFLAG(); - awaitingAddressAck = true; - sercom->startTransmissionWIRE(); - return; + // Arbitration loss releases SDA and SCL. Re-arm the same queue head by + // writing ADDR again. ADDR clears ARBLOST; when another master still + // owns the bus, SERCOM remains in BUSY and waits for IDLE before START. + sercom->clearINTFLAG(); + awaitingAddressAck = true; + sercom->startTransmissionWIRE(); + return; + } } - SercomWireError error = SercomWireError::UNKNOWN_ERROR; - - if (isRxNack) { - error = isMaster && awaitingAddressAck ? SercomWireError::NACK_ON_ADDRESS - : SercomWireError::NACK_ON_DATA; - } else if (busError) { - error = SercomWireError::BUS_ERROR; - } else if (masterTimeout) { - error = SercomWireError::MASTER_TIMEOUT; - } else if (slaveTimeout) { - error = SercomWireError::SLAVE_TIMEOUT; - } else if (lengthError) { - error = SercomWireError::LENGTH_ERROR; - } else if (isMaster) { + if (!continueOwnedArbitration) { + SercomWireError error = SercomWireError::UNKNOWN_ERROR; + + if (isRxNack) { + error = isMaster && awaitingAddressAck + ? SercomWireError::NACK_ON_ADDRESS + : SercomWireError::NACK_ON_DATA; + } else if (masterTimeout) { + error = SercomWireError::MASTER_TIMEOUT; + } else if (slaveTimeout) { + error = SercomWireError::SLAVE_TIMEOUT; + } else if (lengthError) { + error = SercomWireError::LENGTH_ERROR; + } else if (busError) { + error = SercomWireError::BUS_ERROR; + } else if (isMaster) { #if defined(__SAME53__) || defined(__SAME54__) - const uint8_t busState = (status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> - SERCOM_I2CM_STATUS_BUSSTATE_Pos; + const uint8_t busState = (status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> + SERCOM_I2CM_STATUS_BUSSTATE_Pos; #else - const uint8_t busState = (status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> - SERCOM_I2CM_STATUS_BUSSTATE_Pos; + const uint8_t busState = (status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> + SERCOM_I2CM_STATUS_BUSSTATE_Pos; #endif // __SAME53__ / __SAME54__ - if (busState == WIRE_UNKNOWN_STATE) - error = SercomWireError::BUS_STATE_UNKNOWN; - } + if (busState == WIRE_UNKNOWN_STATE) + error = SercomWireError::BUS_STATE_UNKNOWN; + } - if (isMaster && !arbitrationLost) - sercom->prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); - if (wireError) - sercom->clearINTFLAG(); - sercom->deferStopWIRE(error); - return; + if (isMaster && !arbitrationLost) + sercom->prepareCommandBitsWIRE(WIRE_MASTER_ACT_STOP); + if (wireError) + sercom->clearINTFLAG(); + sercom->deferStopWIRE(error); + return; + } } if (isMaster) { @@ -296,13 +411,13 @@ inline void TwoWire::onService(void) } else { #if defined(__SAME53__) || defined(__SAME54__) bool isMasterRead = (status & SERCOM_I2CS_STATUS_DIR_Msk); - bool prec = (flags & SERCOM_I2CS_INTFLAG_PREC_Msk); - bool amatch = (flags & SERCOM_I2CS_INTFLAG_AMATCH_Msk); + bool prec = slavePrec; + bool amatch = slaveAmatch; #else bool isMasterRead = (status & SERCOM_I2CS_STATUS_DIR); // Master Read / Slave Transmit - bool prec = (flags & SERCOM_I2CS_INTFLAG_PREC); // Stop detected - bool amatch = (flags & SERCOM_I2CS_INTFLAG_AMATCH); // Address Match detected + bool prec = slavePrec; // Stop detected + bool amatch = slaveAmatch; // Address Match detected #endif // __SAME53__ / __SAME54__ // A new address match terminates an active slave receive even when the @@ -310,12 +425,51 @@ inline void TwoWire::onService(void) // before the follow-up setup so a combined write/read transaction publishes // its request bytes before onRequest prepares the response. if (amatch) { - if (activeTxn && (activeTxn->config & I2C_CFG_READ) == 0) + // PREC can arrive with the next AMATCH when the preceding transaction's + // STOP is recognized just before a new address. It belongs to the + // completed transaction and must not retire the transaction started by + // this address match. + if (prec) { +#if defined(__SAME53__) || defined(__SAME54__) + sercom->clearINTFLAG(SERCOM_I2CS_INTFLAG_PREC_Msk); +#else + sercom->clearINTFLAG(SERCOM_I2CS_INTFLAG_PREC); +#endif // __SAME53__ / __SAME54__ + } + if (!sercom->isSlaveTransactionActiveWIRE()) { + sercom->discardIdleSlaveTransactionWIRE(); + activeTxn = sercom->getCurrentTxnWIRE(); + } + const bool activeReceive = + sercom->isSlaveTransactionActiveWIRE() && activeTxn && + (activeTxn->config & I2C_CFG_READ) == 0; + if (activeReceive) { +#if defined(__SAME53__) || defined(__SAME54__) + sercom->disableInterrupts(SERCOM_I2CS_INTENCLR_AMATCH_Msk); +#else + sercom->disableInterrupts(SERCOM_I2CS_INTENCLR_AMATCH); +#endif // __SAME53__ / __SAME54__ + if (isMasterRead) + sercom->deferRequestWIRE(); + else + sercom->deferReceiveWIRE(); sercom->deferReceiveCompleteWIRE(); - if (isMasterRead) + return; + } + sercom->markSlaveTransactionActiveWIRE(); + if (isMasterRead) { +#if defined(__SAME53__) || defined(__SAME54__) + sercom->disableInterrupts(SERCOM_I2CS_INTENCLR_AMATCH_Msk); +#else + sercom->disableInterrupts(SERCOM_I2CS_INTENCLR_AMATCH); +#endif // __SAME53__ / __SAME54__ sercom->deferRequestWIRE(); - else - sercom->deferReceiveWIRE(); + } else { + prepareSlaveReceive(); + sercom->setTxnWIRE(&slaveTxn); + sercom->startTransmissionWIRE(); + } + return; } // Preserve the interrupt-driven slave byte engine both when DMA is not @@ -342,17 +496,6 @@ inline void TwoWire::onService(void) return; } - // A short slave receive ends at STOP before its capacity-sized descriptor - // reaches zero. PREC finalizes the actual DMA byte count; it is not an - // error and does not enter stopTransmissionWIRE(). - SercomTxn *slaveTxn = activeTxn; - const bool activeSlaveReceive = - prec && !amatch && slaveTxn && - (slaveTxn->config & I2C_CFG_READ) == 0; - if (activeSlaveReceive) - sercom->deferReceiveCompleteWIRE(); - else if (prec) - sercom->clearINTFLAG(); } } From 9d655dd96b111319d549f0549633f1130c309d41 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 21 Aug 2026 15:06:44 -0400 Subject: [PATCH 73/79] fix(wire): start queued master after slave retirement --- cores/arduino/SERCOM.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index bd611e6d9..4eeadba6e 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -1625,10 +1625,20 @@ SercomTxn* SERCOM::stopTransmissionWIRE( SercomWireError error ) _wireRequestCb(_wireCallbackUser); } + // An enqueue that arrives during an active slave transaction must wait + // until that transaction retires. Give that already-admitted master work + // the idle handoff before restoring the cached slave descriptor, whose + // active/current state would otherwise make startNextQueuedWIRE() refuse + // to run it. + if (_wire.currentTxn == nullptr) + startNextQueuedWIRE(); + if (_wire.currentTxn == nullptr && _wire.slaveTxn != nullptr) setTxnWIRE(_wire.slaveTxn); - - startNextQueuedWIRE(); +#if defined(SERCOM_WIRE_TEST_POINTS) + recordTestPointWIRE(SercomWireTestEvent::StopSlaveComplete, + static_cast(completionError)); +#endif return _wire.currentTxn; } From d7523f3446b46e94580979a647cb3aafadba6c58 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 21 Aug 2026 19:49:36 -0400 Subject: [PATCH 74/79] refactor(wire): reuse ISR status snapshot --- libraries/Wire/Wire.h | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 7a7faf870..aae01ed41 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -215,6 +215,9 @@ inline void TwoWire::onService(void) #else const bool busError = status & SERCOM_I2CM_STATUS_BUSERR; #endif // __SAME53__ / __SAME54__ + const uint8_t busState = + (status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> + SERCOM_I2CM_STATUS_BUSSTATE_Pos; const bool slaveBusError = !isMaster && wireError && busError; if (slaveBusError) { #if defined(__SAME53__) || defined(__SAME54__) @@ -290,17 +293,13 @@ inline void TwoWire::onService(void) #endif // __SAME53__ / __SAME54__ if (isMaster && busError) { - simio::wire::BusState busState = simio::wire::BusState::Unknown; - if (sercom->isBusIdleWIRE()) - busState = simio::wire::BusState::Idle; - else if (sercom->isBusOwnerWIRE()) - busState = simio::wire::BusState::Owner; - else if (sercom->isBusBusyWIRE()) - busState = simio::wire::BusState::Busy; + const auto capturedBusState = + static_cast(busState); const bool replayAllowed = activeTxn && (activeTxn->config & I2C_CFG_REPLAY_SAFE); const auto busErrorAction = simio::wire::decideBusErrorAction( - {true, busState, arbitrationLost, commandReady, terminalCondition, + {true, capturedBusState, arbitrationLost, commandReady, + terminalCondition, replayAllowed, activeTxn != nullptr}); if (busErrorAction != simio::wire::BusErrorAction::RetireAmbiguous && busErrorAction != simio::wire::BusErrorAction::RetireTerminal) { @@ -319,7 +318,7 @@ inline void TwoWire::onService(void) } if (isMaster && arbitrationLost && !busError) { - if (sercom->isBusOwnerWIRE() && arbitrationOnly) { + if (busState == WIRE_OWNER_STATE && arbitrationOnly) { // ARBLOST normally transitions OWNER to BUSY. If OWNER remains set, // rewriting ADDR would request a repeated START and restart a // transaction that the hardware still owns. Clear only the error @@ -364,13 +363,6 @@ inline void TwoWire::onService(void) } else if (busError) { error = SercomWireError::BUS_ERROR; } else if (isMaster) { -#if defined(__SAME53__) || defined(__SAME54__) - const uint8_t busState = (status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> - SERCOM_I2CM_STATUS_BUSSTATE_Pos; -#else - const uint8_t busState = (status & SERCOM_I2CM_STATUS_BUSSTATE_Msk) >> - SERCOM_I2CM_STATUS_BUSSTATE_Pos; -#endif // __SAME53__ / __SAME54__ if (busState == WIRE_UNKNOWN_STATE) error = SercomWireError::BUS_STATE_UNKNOWN; } From 02d425f8612a4adb24183b8357473c7bf5b5a9b5 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 21 Aug 2026 20:34:12 -0400 Subject: [PATCH 75/79] build(dma): pin shared descriptor implementation --- libraries/Adafruit_ZeroDMA | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Adafruit_ZeroDMA b/libraries/Adafruit_ZeroDMA index ae4842e49..25e92a081 160000 --- a/libraries/Adafruit_ZeroDMA +++ b/libraries/Adafruit_ZeroDMA @@ -1 +1 @@ -Subproject commit ae4842e4921c50d85bd9ae74f281e91506a545ff +Subproject commit 25e92a0811b364a068989c43788727c18803fc77 From 6e3e0b7823fcdf0c814369e53417995ccfbfed2a Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 21 Aug 2026 22:49:15 -0400 Subject: [PATCH 76/79] fix(analog): classify SAMD51 and SAME5x families --- cores/arduino/wiring_analog.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cores/arduino/wiring_analog.c b/cores/arduino/wiring_analog.c index f5562d9dc..7b051718a 100644 --- a/cores/arduino/wiring_analog.c +++ b/cores/arduino/wiring_analog.c @@ -24,6 +24,14 @@ extern "C" { #endif +#if (defined(__SAMD51__) || defined(__SAME51__)) && !defined(ARDUINO_SAMD51_E51) +#define ARDUINO_SAMD51_E51 +#endif + +#if (defined(__SAME53__) || defined(__SAME54__)) && !defined(ARDUINO_SAME53_E54) +#define ARDUINO_SAME53_E54 +#endif + static int _readResolution = 10; static int _ADCResolution = 10; From 712b9fad8fa1171ba94ee08ec6a5fa8639174452 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 21 Aug 2026 22:58:10 -0400 Subject: [PATCH 77/79] build(usb): pin Metro M0-aware TinyUSB revision --- libraries/Adafruit_TinyUSB_Arduino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Adafruit_TinyUSB_Arduino b/libraries/Adafruit_TinyUSB_Arduino index 08e7e9cd2..1c8d3964c 160000 --- a/libraries/Adafruit_TinyUSB_Arduino +++ b/libraries/Adafruit_TinyUSB_Arduino @@ -1 +1 @@ -Subproject commit 08e7e9cd22ad5dcd5465536caa6f0b200eb12486 +Subproject commit 1c8d3964c685d039d78ca6ec38bf18e283de7f21 From 0bd5c7760056c9e7328610571d5cd706e742c431 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 21 Aug 2026 22:59:59 -0400 Subject: [PATCH 78/79] build(usb): pin formatted TinyUSB revision --- libraries/Adafruit_TinyUSB_Arduino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Adafruit_TinyUSB_Arduino b/libraries/Adafruit_TinyUSB_Arduino index 1c8d3964c..057f673cb 160000 --- a/libraries/Adafruit_TinyUSB_Arduino +++ b/libraries/Adafruit_TinyUSB_Arduino @@ -1 +1 @@ -Subproject commit 1c8d3964c685d039d78ca6ec38bf18e283de7f21 +Subproject commit 057f673cb08ab31ea76434148899f47b01d05ca0 From 0b814e165ad586b68f62efdfc71e0daa3ec8f738 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 21 Aug 2026 23:01:27 -0400 Subject: [PATCH 79/79] fix(usb): correct TinyUSB revision pin --- libraries/Adafruit_TinyUSB_Arduino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Adafruit_TinyUSB_Arduino b/libraries/Adafruit_TinyUSB_Arduino index 057f673cb..057f6734f 160000 --- a/libraries/Adafruit_TinyUSB_Arduino +++ b/libraries/Adafruit_TinyUSB_Arduino @@ -1 +1 @@ -Subproject commit 057f673cb08ab31ea76434148899f47b01d05ca0 +Subproject commit 057f6734f589680072de3a467b3b4051e57f3a9e