aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/lib/protomatter/src/arch/nrf52.h
blob: c46cf1e910db6b03926318ce57f2d1ccb6667128 (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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*!
 * @file nrf52.h
 *
 * Part of Adafruit's Protomatter library for HUB75-style RGB LED matrices.
 * This file contains NRF52-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(NRF52_SERIES)

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

// digitalPinToPort, g_ADigitalPinMap[] are Arduino specific:

void *_PM_portOutRegister(uint32_t pin) {
  NRF_GPIO_Type *port = digitalPinToPort(pin);
  return &port->OUT;
}

void *_PM_portSetRegister(uint32_t pin) {
  NRF_GPIO_Type *port = digitalPinToPort(pin);
  return &port->OUTSET;
}

void *_PM_portClearRegister(uint32_t pin) {
  NRF_GPIO_Type *port = digitalPinToPort(pin);
  return &port->OUTCLR;
}

// Leave _PM_portToggleRegister(pin) undefined on nRF!

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define _PM_byteOffset(pin) ((g_ADigitalPinMap[pin] & 0x1F) / 8)
#define _PM_wordOffset(pin) ((g_ADigitalPinMap[pin] & 0x1F) / 16)
#else
#define _PM_byteOffset(pin) (3 - ((g_ADigitalPinMap[pin] & 0x1F) / 8))
#define _PM_wordOffset(pin) (1 - ((g_ADigitalPinMap[pin] & 0x1F) / 16))
#endif

// Because it's tied to a specific timer right now, there can be only
// one instance of the Protomatter_core struct. The Arduino library
// sets up this pointer when calling begin().
void *_PM_protoPtr = NULL;

// Arduino implementation is tied to a specific timer/counter,
// Partly because IRQs must be declared at compile-time.
#define _PM_IRQ_HANDLER TIMER4_IRQHandler
#define _PM_timerFreq 16000000
#define _PM_TIMER_DEFAULT NRF_TIMER4

#ifdef __cplusplus
extern "C" {
#endif

// Timer interrupt service routine
void _PM_IRQ_HANDLER(void) {
  if (_PM_TIMER_DEFAULT->EVENTS_COMPARE[0]) {
    _PM_TIMER_DEFAULT->EVENTS_COMPARE[0] = 0;
  }
  _PM_row_handler(_PM_protoPtr); // In core.c
}

#ifdef __cplusplus
}
#endif

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

#include "nrf_gpio.h"

volatile uint32_t *_PM_portOutRegister(uint32_t pin) {
  NRF_GPIO_Type *port = nrf_gpio_pin_port_decode(&pin);
  return &port->OUT;
}

volatile uint32_t *_PM_portSetRegister(uint32_t pin) {
  NRF_GPIO_Type *port = nrf_gpio_pin_port_decode(&pin);
  return &port->OUTSET;
}

volatile uint32_t *_PM_portClearRegister(uint32_t pin) {
  NRF_GPIO_Type *port = nrf_gpio_pin_port_decode(&pin);
  return &port->OUTCLR;
}
#define _PM_pinOutput(pin)                                                     \
  nrf_gpio_cfg(pin, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT,    \
               NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE)
#define _PM_pinInput(pin) nrf_gpio_cfg_input(pin)
#define _PM_pinHigh(pin) nrf_gpio_pin_set(pin)
#define _PM_pinLow(pin) nrf_gpio_pin_clear(pin)
#define _PM_portBitMask(pin) (1u << ((pin)&31))

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define _PM_byteOffset(pin) ((pin & 31) / 8)
#define _PM_wordOffset(pin) ((pin & 31) / 16)
#else
#define _PM_byteOffset(pin) (3 - ((pin & 31) / 8))
#define _PM_wordOffset(pin) (1 - ((pin & 31) / 16))
#endif

// CircuitPython implementation is tied to a specific freq (but the counter
// is dynamically allocated):
#define _PM_timerFreq 16000000

// Because it's tied to a specific timer right now, there can be only
// one instance of the Protomatter_core struct. The Arduino library
// sets up this pointer when calling begin().
void *_PM_protoPtr = NULL;

// Timer interrupt service routine
void _PM_IRQ_HANDLER(void) {
  NRF_TIMER_Type *timer = (((Protomatter_core *)_PM_protoPtr)->timer);
  if (timer->EVENTS_COMPARE[0]) {
    timer->EVENTS_COMPARE[0] = 0;
  }

  _PM_row_handler(_PM_protoPtr); // In core.c
}

#else // END CIRCUITPYTHON -------------------------------------------------

// Byte offset macros, timer and ISR work for other environments go here.

#endif

// CODE COMMON TO ALL ENVIRONMENTS -----------------------------------------

void _PM_timerInit(void *tptr) {
  static const struct {
    NRF_TIMER_Type *tc; // -> Timer peripheral base address
    IRQn_Type IRQn;     // Interrupt number
  } timer[] = {
#if defined(NRF_TIMER0)
    {NRF_TIMER0, TIMER0_IRQn},
#endif
#if defined(NRF_TIMER1)
    {NRF_TIMER1, TIMER1_IRQn},
#endif
#if defined(NRF_TIMER2)
    {NRF_TIMER2, TIMER2_IRQn},
#endif
#if defined(NRF_TIMER3)
    {NRF_TIMER3, TIMER3_IRQn},
#endif
#if defined(NRF_TIMER4)
    {NRF_TIMER4, TIMER4_IRQn},
#endif
  };
#define NUM_TIMERS (sizeof timer / sizeof timer[0])

  // Determine IRQn from timer address
  uint8_t timerNum = 0;
  while ((timerNum < NUM_TIMERS) && (timer[timerNum].tc != tptr)) {
    timerNum++;
  }
  if (timerNum >= NUM_TIMERS)
    return;

  NRF_TIMER_Type *tc = timer[timerNum].tc;

  tc->TASKS_STOP = 1;               // Stop timer
  tc->MODE = TIMER_MODE_MODE_Timer; // Timer (not counter) mode
  tc->TASKS_CLEAR = 1;
  tc->BITMODE = TIMER_BITMODE_BITMODE_16Bit
                << TIMER_BITMODE_BITMODE_Pos; // 16-bit timer res
  tc->PRESCALER = 0;                          // 1:1 prescale (16 MHz)
  tc->INTENSET = TIMER_INTENSET_COMPARE0_Enabled
                 << TIMER_INTENSET_COMPARE0_Pos; // Event 0 interrupt
  // NVIC_DisableIRQ(timer[timerNum].IRQn);
  // NVIC_ClearPendingIRQ(timer[timerNum].IRQn);
  // NVIC_SetPriority(timer[timerNum].IRQn, 0); // Top priority
  NVIC_EnableIRQ(timer[timerNum].IRQn);
}

inline void _PM_timerStart(void *tptr, uint32_t period) {
  volatile NRF_TIMER_Type *tc = (volatile NRF_TIMER_Type *)tptr;
  tc->TASKS_STOP = 1;  // Stop timer
  tc->TASKS_CLEAR = 1; // Reset to 0
  tc->CC[0] = period;
  tc->TASKS_START = 1; // Start timer
}

inline uint32_t _PM_timerGetCount(void *tptr) {
  volatile NRF_TIMER_Type *tc = (volatile NRF_TIMER_Type *)tptr;
  tc->TASKS_CAPTURE[0] = 1; // Capture timer to CC[n] register
  return tc->CC[0];
}

uint32_t _PM_timerStop(void *tptr) {
  volatile NRF_TIMER_Type *tc = (volatile NRF_TIMER_Type *)tptr;
  tc->TASKS_STOP = 1; // Stop timer
  __attribute__((unused)) uint32_t count = _PM_timerGetCount(tptr);
  // NOTE TO FUTURE SELF: I don't know why the GetCount code isn't
  // working. It does the expected thing in a small test program but
  // not here. I need to get on with testing on an actual matrix, so
  // this is just a nonsense fudge value for now:
  return 100;
  // return count;
}

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

#define _PM_minMinPeriod 100

#endif // END NRF52_SERIES