aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/lib/protomatter/src/arch/samd21.h
blob: 25deef33d02b312bc6c20fa78b3e1014c776f3de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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