aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/lib/protomatter/src/arch/samd21.h
diff options
context:
space:
mode:
Diffstat (limited to 'circuitpython/lib/protomatter/src/arch/samd21.h')
-rw-r--r--circuitpython/lib/protomatter/src/arch/samd21.h150
1 files changed, 150 insertions, 0 deletions
diff --git a/circuitpython/lib/protomatter/src/arch/samd21.h b/circuitpython/lib/protomatter/src/arch/samd21.h
new file mode 100644
index 0000000..25deef3
--- /dev/null
+++ b/circuitpython/lib/protomatter/src/arch/samd21.h
@@ -0,0 +1,150 @@
+/*!
+ * @file samd21.h
+ *
+ * Part of Adafruit's Protomatter library for HUB75-style RGB LED matrices.
+ * This file contains SAMD21-SPECIFIC CODE.
+ *
+ * Adafruit invests time and resources providing this open source code,
+ * please support Adafruit and open-source hardware by purchasing
+ * products from Adafruit!
+ *
+ * Written by Phil "Paint Your Dragon" Burgess and Jeff Epler for
+ * Adafruit Industries, with contributions from the open source community.
+ *
+ * BSD license, all text here must be included in any redistribution.
+ *
+ */
+
+#pragma once
+
+#if defined(_SAMD21_) || defined(SAMD21) // Arduino, Circuitpy SAMD21 defs
+
+#if defined(ARDUINO) // COMPILING FOR ARDUINO ------------------------------
+
+// g_APinDescription[] table and pin indices are Arduino specific:
+#define _PM_portOutRegister(pin) \
+ &PORT_IOBUS->Group[g_APinDescription[pin].ulPort].OUT.reg
+
+#define _PM_portSetRegister(pin) \
+ &PORT_IOBUS->Group[g_APinDescription[pin].ulPort].OUTSET.reg
+
+#define _PM_portClearRegister(pin) \
+ &PORT_IOBUS->Group[g_APinDescription[pin].ulPort].OUTCLR.reg
+
+#define _PM_portToggleRegister(pin) \
+ &PORT_IOBUS->Group[g_APinDescription[pin].ulPort].OUTTGL.reg
+
+#else // END ARDUINO -------------------------------------------------------
+
+// Non-Arduino port register lookups go here, if not already declared
+// in samd-common.h.
+
+#endif
+
+// CODE COMMON TO ALL ENVIRONMENTS -----------------------------------------
+
+// Initialize, but do not start, timer
+void _PM_timerInit(void *tptr) {
+ static const struct {
+ Tc *tc; // -> Timer/counter peripheral base address
+ IRQn_Type IRQn; // Interrupt number
+ uint8_t GCM_ID; // GCLK selection ID
+ } timer[] = {
+#if defined(TC0)
+ {TC0, TC0_IRQn, GCM_TCC0_TCC1},
+#endif
+#if defined(TC1)
+ {TC1, TC1_IRQn, GCM_TCC0_TCC1},
+#endif
+#if defined(TC2)
+ {TC2, TC2_IRQn, GCM_TCC2_TC3},
+#endif
+#if defined(TC3)
+ {TC3, TC3_IRQn, GCM_TCC2_TC3},
+#endif
+#if defined(TC4)
+ {TC4, TC4_IRQn, GCM_TC4_TC5},
+#endif
+ };
+#define NUM_TIMERS (sizeof timer / sizeof timer[0])
+
+ Tc *tc = (Tc *)tptr; // Cast peripheral address passed in
+
+ uint8_t timerNum = 0;
+ while ((timerNum < NUM_TIMERS) && (timer[timerNum].tc != tc)) {
+ timerNum++;
+ }
+ if (timerNum >= NUM_TIMERS)
+ return;
+
+ // Enable GCLK for timer/counter
+ GCLK->CLKCTRL.reg = (uint16_t)(GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 |
+ GCLK_CLKCTRL_ID(timer[timerNum].GCM_ID));
+ while (GCLK->STATUS.bit.SYNCBUSY == 1)
+ ;
+
+ // Counter must first be disabled to configure it
+ tc->COUNT16.CTRLA.bit.ENABLE = 0;
+ while (tc->COUNT16.STATUS.bit.SYNCBUSY)
+ ;
+
+ tc->COUNT16.CTRLA.reg = // Configure timer counter
+ TC_CTRLA_PRESCALER_DIV1 | // 1:1 Prescale
+ TC_CTRLA_WAVEGEN_MFRQ | // Match frequency generation mode (MFRQ)
+ TC_CTRLA_MODE_COUNT16; // 16-bit counter mode
+ while (tc->COUNT16.STATUS.bit.SYNCBUSY)
+ ;
+
+ tc->COUNT16.CTRLBCLR.reg = TCC_CTRLBCLR_DIR; // Count up
+ while (tc->COUNT16.STATUS.bit.SYNCBUSY)
+ ;
+
+ // Overflow interrupt
+ tc->COUNT16.INTENSET.reg = TC_INTENSET_OVF;
+
+ NVIC_DisableIRQ(timer[timerNum].IRQn);
+ NVIC_ClearPendingIRQ(timer[timerNum].IRQn);
+ NVIC_SetPriority(timer[timerNum].IRQn, 0); // Top priority
+ NVIC_EnableIRQ(timer[timerNum].IRQn);
+
+ // Timer is configured but NOT enabled by default
+}
+
+// Set timer period, initialize count value to zero, enable timer.
+// Timer must be initialized to 16-bit mode using the init function
+// above, but must be inactive before calling this.
+inline void _PM_timerStart(void *tptr, uint32_t period) {
+ Tc *tc = (Tc *)tptr; // Cast peripheral address passed in
+ tc->COUNT16.COUNT.reg = 0;
+ while (tc->COUNT16.STATUS.bit.SYNCBUSY)
+ ;
+ tc->COUNT16.CC[0].reg = period;
+ while (tc->COUNT16.STATUS.bit.SYNCBUSY)
+ ;
+ tc->COUNT16.CTRLA.bit.ENABLE = 1;
+ while (tc->COUNT16.STATUS.bit.SYNCBUSY)
+ ;
+}
+
+// Return current count value (timer enabled or not).
+// Timer must be previously initialized.
+inline uint32_t _PM_timerGetCount(void *tptr) {
+ Tc *tc = (Tc *)tptr; // Cast peripheral address passed in
+ tc->COUNT16.READREQ.reg = TC_READREQ_RCONT | TC_READREQ_ADDR(0x10);
+ while (tc->COUNT16.STATUS.bit.SYNCBUSY)
+ ;
+ return tc->COUNT16.COUNT.reg;
+}
+
+// Disable timer and return current count value.
+// Timer must be previously initialized.
+inline uint32_t _PM_timerStop(void *tptr) {
+ Tc *tc = (Tc *)tptr; // Cast peripheral address passed in
+ uint32_t count = _PM_timerGetCount(tptr);
+ tc->COUNT16.CTRLA.bit.ENABLE = 0;
+ while (tc->COUNT16.STATUS.bit.SYNCBUSY)
+ ;
+ return count;
+}
+
+#endif // END _SAMD21_ || SAMD21