diff --git a/doc/dmd-running-figure.dox b/doc/dmd-running-figure.dox index b4da2d78..9207d519 100644 --- a/doc/dmd-running-figure.dox +++ b/doc/dmd-running-figure.dox @@ -70,7 +70,7 @@ always 16). We must also call DMD::loop() repeatedly from the application's main loop() function to ensure that the display is kept refreshed. Sometimes it can be inconvenient to arrange for DMD::loop() to be called -regularly. An alternative is to use Timer1 and +regularly. An alternative is to use Timer1 or Timer2 and \ref dmd_interrupts "interrupt-driven display refresh": \dontinclude DMD/examples/RunningFigureISR/RunningFigureISR.pde @@ -78,6 +78,9 @@ regularly. An alternative is to use Timer1 and \until loop() \until } +In the case of Timer2, \c TIMER2_OVF_vect and \ref DMD::enableTimer2() "enableTimer2()" +would be used in place of \c TIMER1_OVF_vect and \ref DMD::enableTimer1() "enableTimer1()". + The full source code for the example follows: \include DMD/examples/RunningFigure/RunningFigure.pde diff --git a/libraries/DMD/DMD.cpp b/libraries/DMD/DMD.cpp index bd493b52..547c48d7 100644 --- a/libraries/DMD/DMD.cpp +++ b/libraries/DMD/DMD.cpp @@ -95,6 +95,24 @@ * } * \endcode * + * If Timer1 is already in use by some other part of your application, + * then Timer2 can be used as an alternative interrupt source: + * + * \code + * #include + * + * DMD display; + * + * ISR(TIMER2_OVF_vect) + * { + * display.refresh(); + * } + * + * void setup() { + * display.enableTimer2(); + * } + * \endcode + * * DMD can also be used with third-party timer libraries such as * TimerOne: * @@ -540,7 +558,7 @@ void DMD::refresh() * If timer interrupts are being used to update the display, then it is * unnecessary to call loop(). * - * \sa refresh(), disableTimer1(), setDoubleBuffer() + * \sa refresh(), disableTimer1(), enableTimer2(), setDoubleBuffer() */ void DMD::enableTimer1() { @@ -599,6 +617,66 @@ void DMD::disableTimer1() TIMSK1 &= ~_BV(TOIE1); } +/** + * \brief Enables Timer2 overflow interrupts for updating this display. + * + * The application must also provide an interrupt service routine for + * Timer2 that calls refresh(): + * + * \code + * #include + * + * DMD display; + * + * ISR(TIMER2_OVF_vect) + * { + * display.refresh(); + * } + * + * void setup() { + * display.enableTimer2(); + * } + * \endcode + * + * If timer interrupts are being used to update the display, then it is + * unnecessary to call loop(). + * + * \sa refresh(), disableTimer2(), enableTimer1(), setDoubleBuffer() + */ +void DMD::enableTimer2() +{ + // Configure Timer2 for the period we want. With the prescaler set + // to 128, then 256 increments of Timer2 gives roughly 4 ms between + // overflows on a system with a 16 MHz clock. We adjust the prescaler + // accordingly for other clock frequencies. + TCCR2A = 0; + if (F_CPU >= 32000000) + TCCR2B = _BV(CS22) | _BV(CS21); // Prescaler = 256 + else if (F_CPU >= 16000000) + TCCR2B = _BV(CS22) | _BV(CS20); // Prescaler = 128 + else if (F_CPU >= 8000000) + TCCR2B = _BV(CS22); // Prescaler = 64 + else + TCCR2B = _BV(CS21) | _BV(CS20); // Prescaler = 32 + + // Reset Timer2 to kick off the process. + TCNT2 = 0; + + // Turn on the Timer2 overflow interrupt (also turn off OCIE2A and OCIE2B). + TIMSK2 = _BV(TOIE2); +} + +/** + * \brief Disables Timer2 overflow interrupts. + * + * \sa enableTimer2() + */ +void DMD::disableTimer2() +{ + // Turn off the Timer2 overflow interrupt. + TIMSK2 &= ~_BV(TOIE2); +} + /** * \brief Converts an RGB value into a pixel color value. * diff --git a/libraries/DMD/DMD.h b/libraries/DMD/DMD.h index 1bc46570..e07a61c4 100644 --- a/libraries/DMD/DMD.h +++ b/libraries/DMD/DMD.h @@ -42,6 +42,9 @@ public: void enableTimer1(); void disableTimer1(); + void enableTimer2(); + void disableTimer2(); + static Color fromRGB(uint8_t r, uint8_t g, uint8_t b); private: