@@ -132,6 +132,21 @@ ICACHE_RAM_ATTR unsigned int BaseLEDMatrix::multiplier5microseconds( size_t fram
132132 return 1 ;
133133}
134134
135+ /*
136+ * Interrupt Handlers
137+ *
138+ * Here, different interrupt handlers are implemented for each kind of micro-controller.
139+ * Pull requests for more microcontroller types are encouraged!
140+ *
141+ * The basic goal of the handlers is to fire the next interrupt N*5 microseconds after
142+ * the last one interrupt ended, where N is multiple determine by the scan count. This
143+ * requires stopping then starting the interrupts within the handler and ensure what
144+ * happens while interrupts are off takes a consistent number of clock cycles, otherwise
145+ * the LEDs will have uneven brightness.
146+ *
147+ */
148+
149+ #pragma mark Teensy Handlers
135150#if (defined(__arm__) && defined(TEENSYDUINO))
136151//
137152// On the Teensy ARM boards, use the TimerThree library to drive scan timing
@@ -168,7 +183,7 @@ unsigned int BaseLEDMatrix::nextTimerInterval(void) const {
168183 return 5 *this ->multiplier5microseconds ( _scanPass );
169184}
170185
171-
186+ # pragma mark ESP8266 Handlers
172187#elif defined ( ESP8266 )
173188
174189//
@@ -213,9 +228,136 @@ ICACHE_RAM_ATTR unsigned int BaseLEDMatrix::nextTimerInterval(void) const {
213228 return 5 *this ->multiplier5microseconds ( _scanPass );
214229}
215230
216- #elif defined(ARDUINO_SAMD_ZERO)||defined(_SAM3XA_)
217- // no timer code for the Due or Zero yet
231+ #pragma mark Arduino Due Handlers
232+ #elif defined(_SAM3XA_) // Arduino Due
233+
234+ void BaseLEDMatrix::startScanning (void ) {
235+ this ->setup ();
236+
237+ /* turn on the timer clock in the power management controller */
238+ pmc_set_writeprotect (false ); // disable write protection for pmc registers
239+ pmc_enable_periph_clk (ID_TC7); // enable peripheral clock TC7
240+
241+ /* we want wavesel 01 with RC */
242+ TC_Configure (/* clock */ TC2,/* channel */ 1 , TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK2);
243+ TC_SetRC (TC2, 1 , 1000 );
244+ TC_Start (TC2, 1 );
245+
246+ // enable timer interrupts on the timer
247+ TC2->TC_CHANNEL [1 ].TC_IER =TC_IER_CPCS; // IER = interrupt enable register
248+ TC2->TC_CHANNEL [1 ].TC_IDR =~TC_IER_CPCS; // IDR = interrupt disable register
249+
250+ /* Enable the interrupt in the nested vector interrupt controller */
251+ /* TC4_IRQn where 4 is the timer number * timer channels (3) + the channel number (=(1*3)+1) for timer1 channel1 */
252+ NVIC_EnableIRQ (TC7_IRQn);
253+
254+ }
255+
256+ unsigned int BaseLEDMatrix::nextTimerInterval (void ) const {
257+ // Calculates the microseconds for each scan
258+ // The base interval is set to 55, which for the
259+ // 10.5MHz CLOCK2 yields a ~5 micro second interval.
260+ return 55 *this ->multiplier5microseconds ( _scanPass );
261+ }
262+
263+ void BaseLEDMatrix::stopScanning (void ) {
264+ NVIC_DisableIRQ (TC7_IRQn);
265+ TC_Stop (TC2, 1 );
266+ }
267+
268+ void TC7_Handler () {
269+
270+ TC_GetStatus (TC2, 1 );
271+ NVIC_DisableIRQ (TC7_IRQn);
272+ TC_Stop (TC2, 1 );
273+
274+ gSingleton ->shiftOutCurrentRow ();
275+
276+ TC_SetRC (TC2, 1 , gSingleton ->nextTimerInterval ());
277+
278+ NVIC_ClearPendingIRQ (TC7_IRQn);
279+ NVIC_EnableIRQ (TC7_IRQn);
280+ TC_Start (TC2, 1 );
281+
282+ gSingleton ->incrementScanRow ();
283+ }
284+
285+ #pragma mark Arduino Zero Handlers
286+ #elif defined(ARDUINO_SAMD_ZERO) // Arduino Zero
287+
288+ void BaseLEDMatrix::startScanning (void ) {
289+ this ->setup ();
290+ REG_GCLK_CLKCTRL = (uint16_t ) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TCC2_TC3) ;
291+ while ( GCLK->STATUS .bit .SYNCBUSY == 1 ); // wait for sync
292+
293+ // The type cast must fit with the selected timer mode
294+ TcCount16* TC = (TcCount16*) TC3; // get timer struct
295+
296+ TC->CTRLA .reg &= ~TC_CTRLA_ENABLE; // Disable TC
297+ while (TC->STATUS .bit .SYNCBUSY == 1 ); // wait for sync
298+
299+ TC->CTRLA .reg |= TC_CTRLA_MODE_COUNT16; // Set Timer counter Mode to 16 bits
300+ while (TC->STATUS .bit .SYNCBUSY == 1 ); // wait for sync
301+ TC->CTRLA .reg |= TC_CTRLA_WAVEGEN_NFRQ; // Set TC as match mode
302+ while (TC->STATUS .bit .SYNCBUSY == 1 ); // wait for sync
303+
304+ TC->CTRLA .reg |= TC_CTRLA_PRESCALER_DIV4; // Set perscaler
305+ while (TC->STATUS .bit .SYNCBUSY == 1 ); // wait for sync
306+
307+ TC->CC [0 ].reg = 0xFFFF ;
308+ while (TC->STATUS .bit .SYNCBUSY == 1 ); // wait for sync
309+
310+ // Interrupts
311+ TC->INTENSET .reg = 0 ; // disable all interrupts
312+ TC->INTENSET .bit .MC0 = 1 ; // enable compare match to CC0
313+
314+ // Enable InterruptVector
315+ NVIC_EnableIRQ (TC3_IRQn);
316+
317+ // Enable TC
318+ TC->CTRLA .reg |= TC_CTRLA_ENABLE;
319+ while (TC->STATUS .bit .SYNCBUSY == 1 ); // wait for sync
320+ }
321+
322+ unsigned int BaseLEDMatrix::nextTimerInterval (void ) const {
323+ // Calculates the microseconds for each scan
324+ // The base interval is set to 59, which with a /4
325+ // prescaler should yield a 5 ms interval.
326+ return 59 *this ->multiplier5microseconds ( _scanPass );
327+ }
328+
329+ void BaseLEDMatrix::stopScanning (void ) {
330+ TcCount16* TC = (TcCount16*) TC3; // get timer struct
331+ TC->CTRLA .reg &= ~TC_CTRLA_ENABLE; // Disable TC
332+ while (TC->STATUS .bit .SYNCBUSY == 1 ); // wait for sync
333+ }
334+
335+ void TC3_Handler () // Interrupt Service Routine (ISR) for timer TC4
336+ {
337+ // Serial.println("ARDUINO_SAMD_ZERO TC3_Handler()");
338+
339+ TcCount16* TC = (TcCount16*) TC3; // get timer struct
340+
341+ TC->CTRLA .reg &= ~TC_CTRLA_ENABLE; // Disable TC
342+ while (TC->STATUS .bit .SYNCBUSY == 1 ); // wait for sync
343+
344+ // shift out next row
345+ gSingleton ->shiftOutCurrentRow ();
346+ // reload the timer
347+ TC->CC [0 ].reg = gSingleton ->nextTimerInterval ();
348+ while (TC->STATUS .bit .SYNCBUSY == 1 ); // wait for sync
349+
350+ TC->INTFLAG .bit .MC0 = 1 ;
351+
352+ TC->CTRLA .reg |= TC_CTRLA_ENABLE;
353+ while (TC->STATUS .bit .SYNCBUSY == 1 ); // wait for sync
354+
355+ // update scan row. Done outside of interrupt stoppage since execution time can
356+ // be inconsistent, which would lead to vary brightness in rows.
357+ gSingleton ->incrementScanRow ();
358+ }
218359
360+ #pragma mark ATmega 8-bit Handlers
219361#else
220362//
221363// On normal Arduino board (Uno, Nano, etc), use the timer interrupts to drive the
0 commit comments