freetronics / DMD2

Beta release of a new Dot Matrix Display Arduino library
http://forum.freetronics.com/viewtopic.php?f=26&t=5893
GNU General Public License v3.0
81 stars 71 forks source link

DMD2 library does not work with I2C (RTC DS3231) #67

Open rtek1000 opened 2 years ago

rtek1000 commented 2 years ago

Library that does not support I2C: https://github.com/freetronics/DMD2

Library that accepts I2C: https://github.com/freetronics/DMD

Ref.: https://github.com/adafruit/RTClib/issues/261

rtek1000 commented 2 years ago

(P.S.: It didn't work, the brightness was different when using manual scanning. See more details below)

I managed to get it to work by disabling automatic scanning and performing manual scanning using Timer1.

To operate the RTC on the I2C port it was necessary to stop Timer1, and after the RTC was operated, Timer1 was resumed.

  Timer1.stop();
  RtcDateTime now = Rtc.GetDateTime();
  Timer1.resume();

Complete example code (AllDrawingOperations.ino):

/*
  Quick demo of major drawing operations on a single DMD
*/
#include <avr/wdt.h>
#include <TimerOne.h>   //
#include <SPI.h>
#include "DMD2.h"
#include "fonts/SystemFont5x7.h"

SoftDMD dmd(1, 1); // DMD controls the entire display

#include <Wire.h> // must be included here so that Arduino library object file references work
#include <RtcDS3231.h>
RtcDS3231<TwoWire> Rtc(Wire);

void ScanDMD()
{
  dmd.scanDisplay();
}

// the setup routine runs once when you press reset:
void setup() {
  wdt_reset(); //  reset watchdog

  wdt_enable(WDTO_4S);

  dmd.setBrightness(10);
  dmd.selectFont(SystemFont5x7);
  dmd.beginNoTimer();

  // Circle with a line at a tangent to it
  dmd.drawCircle(24, 8, 5);
  dmd.drawLine(14, 9, 28, 15);

  // Outline box containing a filled box
  dmd.drawBox(6, 10, 11, 15);
  dmd.drawFilledBox(8, 12, 9, 13);

  Serial.begin(115200);

  Serial.println("Start");

  Rtc.Begin();

  //initialize TimerOne's interrupt/CPU usage used to scan and refresh the display
  Timer1.initialize( 5000 );           //period in microseconds to call ScanDMD. Anything longer than 5000 (5ms) and you can see flicker.
  Timer1.attachInterrupt( ScanDMD );   //attach the Timer1 interrupt to ScanDMD which goes to dmd.scanDisplayBySPI()
}

int n = 123;

// the loop routine runs over and over again forever:
void loop() {
  dmd.drawString(0, 0, String(n));
  n = n + 1;
  delay(1000);

  if (n % 2 == 0) {
    dmd.drawFilledBox(0, 11, 4, 15, GRAPHICS_OFF);
    dmd.drawBox(0, 11, 4, 15);
  } else {
    dmd.drawFilledBox(0, 11, 4, 15);
  }

  Timer1.stop();
  RtcDateTime now = Rtc.GetDateTime();
  Timer1.resume();

  printDateTime(now);
  Serial.println();

  Timer1.stop();
  RtcTemperature temp = Rtc.GetTemperature();
  Timer1.resume();

  temp.Print(Serial);
  // you may also get the temperature as a float and print it
  // Serial.print(temp.AsFloatDegC());
  Serial.println("C");

  wdt_reset(); //  reset watchdog
}

#define countof(a) (sizeof(a) / sizeof(a[0]))

void printDateTime(const RtcDateTime& dt)
{
  char datestring[20];

  snprintf_P(datestring,
             countof(datestring),
             PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
             dt.Month(),
             dt.Day(),
             dt.Year(),
             dt.Hour(),
             dt.Minute(),
             dt.Second() );
  Serial.print(datestring);
}

DMD2_Timer.cpp:

#define NO_TIMERS

Ref.: https://github.com/Makuna/Rtc/blob/master/src/RtcDS3231.h

rtek1000 commented 2 years ago

It didn't work, the brightness was different when using manual scanning.

But based on the TimerOne library, Stop and Resume functions have been added:

DMD2_Timer.cpp:

ISR(TIMER1_OVF_vect)
{
  static uint8_t skip_isrs = 0;
  skip_isrs = (skip_isrs + 1) % 2;
  if (skip_isrs)
    return;
  scan_running_dmds();
}

void BaseDMD::stop() {
  TCCR1B = 0;
}

void BaseDMD::resume() {
  TCCR1B = 3;
}

void BaseDMD::begin()
{
  beginNoTimer(); // Do any generic setup

  char oldSREG = SREG;
  cli();
  register_running_dmd(this);
  TIMSK1 = _BV(TOIE1); // set overflow interrupt
  SREG = oldSREG;
}

DMD2.h:

  void end();
  void stop();
  void resume();

  /* Start display, but use manual scanning */
  virtual void beginNoTimer();

Usage mode:

SoftDMD dmd(1, 1); // DMD controls the entire display

void setup () {
...
}

void Loop() {
  ...
  dmd.stop();        // <---- Stop the scan timer
  DateTime now = rtc.now(); // <---- Access routine on the I2C port (RTClib Library etc)
  dmd.resume();  // <---- Resume scan timer
}

P.S.: The timer resume value was wrong here (it was modified), in normal operation the TCCR1B register has a value of 3, or Bit 2:0 in state 1 (CS12:0: Clock Select)

Ref.: https://github.com/PaulStoffregen/TimerOne/blob/master/TimerOne.h