aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/lib/protomatter/src/arch/teensy4.h
blob: 75cd1f4433f679b42e2189af828c9e3143b06def (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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*!
 * @file teensy4.h
 *
 * Part of Adafruit's Protomatter library for HUB75-style RGB LED matrices.
 * This file contains i.MX 1062 (Teensy 4.x) 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(__IMXRT1062__)

// i.MX only allows full 32-bit aligned writes to GPIO.
#define _PM_STRICT_32BIT_IO ///< Change core.c behavior for long accesses only

#if defined(ARDUINO) // COMPILING FOR ARDUINO ------------------------------

static const struct {
  volatile uint32_t *base; ///< GPIO base address for pin
  uint8_t bit;             ///< GPIO bit number for pin (0-31)
} _PM_teensyPins[] = {
    {&CORE_PIN0_PORTREG, CORE_PIN0_BIT},
    {&CORE_PIN1_PORTREG, CORE_PIN1_BIT},
    {&CORE_PIN2_PORTREG, CORE_PIN2_BIT},
    {&CORE_PIN3_PORTREG, CORE_PIN3_BIT},
    {&CORE_PIN4_PORTREG, CORE_PIN4_BIT},
    {&CORE_PIN5_PORTREG, CORE_PIN5_BIT},
    {&CORE_PIN6_PORTREG, CORE_PIN6_BIT},
    {&CORE_PIN7_PORTREG, CORE_PIN7_BIT},
    {&CORE_PIN8_PORTREG, CORE_PIN8_BIT},
    {&CORE_PIN9_PORTREG, CORE_PIN9_BIT},
    {&CORE_PIN10_PORTREG, CORE_PIN10_BIT},
    {&CORE_PIN11_PORTREG, CORE_PIN11_BIT},
    {&CORE_PIN12_PORTREG, CORE_PIN12_BIT},
    {&CORE_PIN13_PORTREG, CORE_PIN13_BIT},
    {&CORE_PIN14_PORTREG, CORE_PIN14_BIT},
    {&CORE_PIN15_PORTREG, CORE_PIN15_BIT},
    {&CORE_PIN16_PORTREG, CORE_PIN16_BIT},
    {&CORE_PIN17_PORTREG, CORE_PIN17_BIT},
    {&CORE_PIN18_PORTREG, CORE_PIN18_BIT},
    {&CORE_PIN19_PORTREG, CORE_PIN19_BIT},
    {&CORE_PIN20_PORTREG, CORE_PIN20_BIT},
    {&CORE_PIN21_PORTREG, CORE_PIN21_BIT},
    {&CORE_PIN22_PORTREG, CORE_PIN22_BIT},
    {&CORE_PIN23_PORTREG, CORE_PIN23_BIT},
    {&CORE_PIN24_PORTREG, CORE_PIN24_BIT},
    {&CORE_PIN25_PORTREG, CORE_PIN25_BIT},
    {&CORE_PIN26_PORTREG, CORE_PIN26_BIT},
    {&CORE_PIN27_PORTREG, CORE_PIN27_BIT},
    {&CORE_PIN28_PORTREG, CORE_PIN28_BIT},
    {&CORE_PIN29_PORTREG, CORE_PIN29_BIT},
    {&CORE_PIN30_PORTREG, CORE_PIN30_BIT},
    {&CORE_PIN31_PORTREG, CORE_PIN31_BIT},
    {&CORE_PIN32_PORTREG, CORE_PIN32_BIT},
    {&CORE_PIN33_PORTREG, CORE_PIN33_BIT},
    {&CORE_PIN34_PORTREG, CORE_PIN34_BIT},
    {&CORE_PIN35_PORTREG, CORE_PIN35_BIT},
    {&CORE_PIN36_PORTREG, CORE_PIN36_BIT},
    {&CORE_PIN37_PORTREG, CORE_PIN37_BIT},
    {&CORE_PIN38_PORTREG, CORE_PIN38_BIT},
    {&CORE_PIN39_PORTREG, CORE_PIN39_BIT},
};

#define _PM_SET_OFFSET 33    ///< 0x84 byte offset = 33 longs
#define _PM_CLEAR_OFFSET 34  ///< 0x88 byte offset = 34 longs
#define _PM_TOGGLE_OFFSET 35 ///< 0x8C byte offset = 35 longs

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define _PM_byteOffset(pin) (_PM_teensyPins[pin].bit / 8)
#define _PM_wordOffset(pin) (_PM_teensyPins[pin].bit / 16)
#else
#define _PM_byteOffset(pin) (3 - (_PM_teensyPins[pin].bit / 8))
#define _PM_wordOffset(pin) (1 - (_PM_teensyPins[pin].bit / 16))
#endif

#define _PM_portOutRegister(pin) (void *)_PM_teensyPins[pin].base

#define _PM_portSetRegister(pin)                                               \
  ((volatile uint32_t *)_PM_teensyPins[pin].base + _PM_SET_OFFSET)

#define _PM_portClearRegister(pin)                                             \
  ((volatile uint32_t *)_PM_teensyPins[pin].base + _PM_CLEAR_OFFSET)

#define _PM_portToggleRegister(pin)                                            \
  ((volatile uint32_t *)_PM_teensyPins[pin].base + _PM_TOGGLE_OFFSET)

// As written, because it's tied to a specific timer right now, the
// Arduino lib only permits one instance of the Protomatter_core struct,
// which it sets up when calling begin().
void *_PM_protoPtr = NULL;

// Code as written works with the Periodic Interrupt Timer directly,
// rather than using the Teensy IntervalTimer library, reason being we
// need to be able to poll the current timer value in _PM_timerGetCount(),
// but that's not available from IntervalTimer, and the timer base address
// it keeps is a private member (possible alternative is to do dirty pool
// and access the pointer directly, knowing it's the first element in the
// IntervalTimer object, but this is fraught with peril).

#define _PM_timerFreq 24000000 // 24 MHz
#define _PM_timerNum 0         // PIT timer #0 (can be 0-3)
#define _PM_TIMER_DEFAULT (IMXRT_PIT_CHANNELS + _PM_timerNum) // PIT channel *

// Interrupt service routine for Periodic Interrupt Timer
static void _PM_timerISR(void) {
  IMXRT_PIT_CHANNEL_t *timer = _PM_TIMER_DEFAULT;
  _PM_row_handler(_PM_protoPtr); // In core.c
  timer->TFLG = 1;               // Clear timer interrupt
}

// Initialize, but do not start, timer.
void _PM_timerInit(void *tptr) {
  IMXRT_PIT_CHANNEL_t *timer = (IMXRT_PIT_CHANNEL_t *)tptr;
  CCM_CCGR1 |= CCM_CCGR1_PIT(CCM_CCGR_ON); // Enable clock signal to PIT
  PIT_MCR = 1;                             // Enable PIT
  timer->TCTRL = 0;                        // Disable timer and interrupt
  timer->LDVAL = 100000;                   // Timer initial load value
  // Interrupt is attached but not enabled yet
  attachInterruptVector(IRQ_PIT, &_PM_timerISR);
  NVIC_ENABLE_IRQ(IRQ_PIT);
}

// Set timer period, initialize count value to zero, enable timer.
inline void _PM_timerStart(void *tptr, uint32_t period) {
  IMXRT_PIT_CHANNEL_t *timer = (IMXRT_PIT_CHANNEL_t *)tptr;
  timer->TCTRL = 0;      // Disable timer and interrupt
  timer->LDVAL = period; // Set load value
  // timer->CVAL = period; // And current value (just in case?)
  timer->TFLG = 1;  // Clear timer interrupt
  timer->TCTRL = 3; // Enable timer and interrupt
}

// Return current count value (timer enabled or not).
// Timer must be previously initialized.
inline uint32_t _PM_timerGetCount(void *tptr) {
  IMXRT_PIT_CHANNEL_t *timer = (IMXRT_PIT_CHANNEL_t *)tptr;
  return (timer->LDVAL - timer->CVAL);
}

// Disable timer and return current count value.
// Timer must be previously initialized.
uint32_t _PM_timerStop(void *tptr) {
  IMXRT_PIT_CHANNEL_t *timer = (IMXRT_PIT_CHANNEL_t *)tptr;
  timer->TCTRL = 0; // Disable timer and interrupt
  return _PM_timerGetCount(tptr);
}

#define _PM_clockHoldHigh                                                      \
  asm("nop; nop; nop; nop; nop; nop; nop;");                                   \
  asm("nop; nop; nop; nop; nop; nop; nop;");
#define _PM_clockHoldLow                                                       \
  asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;");                    \
  asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;");

#define _PM_chunkSize 1 ///< DON'T unroll loop, Teensy 4 is SO FAST

#elif defined(CIRCUITPY) // COMPILING FOR CIRCUITPYTHON --------------------

// Teensy 4 CircuitPython magic goes here.

#endif // END CIRCUITPYTHON ------------------------------------------------

#endif // END __IMXRT1062__ (Teensy 4)