DMD Noise?

The Dot Matrix Display (DMD) is a 32x16 array of high-brightness LEDs for visually striking effects. [Product Page]
Binkles
Posts:10
Joined:Mon Jul 14, 2014 1:09 pm
DMD Noise?

Post by Binkles » Mon Jul 14, 2014 1:16 pm

Hiya Everyone!

I'm building a lap timer for my slot car set and have two display types - I have the 16x2 LCD and also a 32x16 DMD (Well I have two of each, but still testing).

Under the track I have two reed switches connected to a 1k resistor through ground and to an input, and 5v on the other side.

With the 16x2 I get no false laps reading, but with the DMD I tend to get quite a few, the code is identical, the only thing different is the displays (and also the reed switch pins, but that's been ruled out of the equation, as it doesn't matter which pins I try the result is the same.

I have tried the DMD with just the internal 5v, as well as an external 5v as well, and the result is the same.


Has anyone heard of this kind of thing?

I've posted the code for both scripts, just in case someone can spot something I can't.

LCD

Code: Select all

/*
  LiquidCrystal Library - display() and noDisplay()

 Demonstrates the use a 16x2 LCD display.  The LiquidCrystal
 library works with all LCD displays that are compatible with the
 Hitachi HD44780 driver. There are many of them out there, and you
 can usually tell them by the 16-pin interface.

 This sketch prints "Hello World!" to the LCD and uses the
 display() and noDisplay() functions to turn on and off
 the display.

 The circuit:
 * LCD RS pin to digital pin 12
 * LCD Enable pin to digital pin 11
 * LCD D4 pin to digital pin 5
 * LCD D5 pin to digital pin 4
 * LCD D6 pin to digital pin 3
 * LCD D7 pin to digital pin 2
 * LCD R/W pin to ground
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)

 Library originally added 18 Apr 2008
 by David A. Mellis
 library modified 5 Jul 2009
 by Limor Fried (http://www.ladyada.net)
 example added 9 Jul 2009
 by Tom Igoe
 modified 22 Nov 2010
 by Tom Igoe

 This example code is in the public domain.

 http://arduino.cc/en/Tutorial/LiquidCrystalDisplay

 */

// include the library code:
 #include <LiquidCrystal.h>  // LCD
#include <stdlib.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);  // LCD
int Laps[4] = {0,0,0,0};
unsigned long LastLap[4] = {0,0,0,0};
float BestLap[4] = {9999,9999,9999,9999};
float CurrentLap[4] = {0,0,0,0};
long RaceMode = 0;  // 0 = Practise  1 = Timed  2 = Laps
long RaceState = 0;  // Setup



// Key Setup
int inByte = 0;
int MinimumBounce = 500;
unsigned long time[] = {
  0, 0, 0, 0};
int inState[] = {
  0, 0, 0, 0};
int inPins[] = {
  6,7}; // For LCD

int pinCount = 2;
unsigned long currentTime;

const int ledPin =  13;      // the number of the LED pin
int buttonPin = 14;
int buttonState = 0; 
String OutputString = "";

void LapTime(int lane)
{
  CurrentLap[lane] = millis() - LastLap[lane];
  LastLap[lane] = millis();
  Laps[lane]++;
  if (BestLap[lane] == 9999) BestLap[lane] = CurrentLap[lane];
  if (CurrentLap[lane] < BestLap[lane]) BestLap[lane] = CurrentLap[lane];
  UpdateDisplay();

}

void UpdateDisplay()
{
    lcd.clear();
    float DisplayLap = CurrentLap[0];
    lcd.setCursor(0,0);
    lcd.print(CurrentLap[0] / 1000,3);
    lcd.setCursor(0,1);
    lcd.print(CurrentLap[1] / 1000,3);
  
    lcd.setCursor(11,0);
    if (BestLap[0] < 9999) lcd.print(BestLap[0] / 1000,3);
    lcd.setCursor(11,1);
    if (BestLap[1] < 9999) lcd.print(BestLap[1] / 1000,3);
    
    lcd.setCursor(7,0);
    lcd.print(Laps[0]);
    lcd.setCursor(7,1);
    lcd.print(Laps[1]);


}

void setup() {
  // set up the LCD's number of columns and rows:

   lcd.begin(16, 2);  
  // Print a message to the LCD.
  
    Serial.begin(115200);
    for (int index = 0; index < pinCount; index++) {
      //Set each defined pin as an input and enable the internal pullup resistor
      //The pullup resistor is a 20K resistor that is tied to 5v so we do not have
      //floating inputs.  
      pinMode(inPins[index], INPUT_PULLUP);
      pinMode(13,OUTPUT);

    }
}

void loop() {
  // Turn off the display:

  for (int index = 0; index < pinCount; index++) {

    //Read the digital input and store it into a variable
    inByte = digitalRead(inPins[index]);
    //Check to see if the state of the pin changed
    if (inByte != inState[index]){
      //Since the pin state changed we check to see how long it was since the last detection
      //If it has not been at least 100 milliseconds since the last detection we ignore it
      //This prevents flooding the serial port with tons of data
      //If it has been greater than 100 ms then we transmit 1 byte of info representing the pin number
      //This will show up as a hex value and not printable ascii
      if (millis() - time[index] > MinimumBounce) {
        if (RaceMode == 0)  LapTime(index);  //  Practise
      }
      //Store the current state of the pin so we can detect future changes
      inState[index] = inByte;
      time[index] = millis();
    }
  }
}

DMD

Code: Select all

/*
  LiquidCrystal Library - display() and noDisplay()

 Demonstrates the use a 16x2 LCD display.  The LiquidCrystal
 library works with all LCD displays that are compatible with the
 Hitachi HD44780 driver. There are many of them out there, and you
 can usually tell them by the 16-pin interface.

 This sketch prints "Hello World!" to the LCD and uses the
 display() and noDisplay() functions to turn on and off
 the display.

 The circuit:
 * LCD RS pin to digital pin 12
 * LCD Enable pin to digital pin 11
 * LCD D4 pin to digital pin 5
 * LCD D5 pin to digital pin 4
 * LCD D6 pin to digital pin 3
 * LCD D7 pin to digital pin 2
 * LCD R/W pin to ground
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)

 Library originally added 18 Apr 2008
 by David A. Mellis
 library modified 5 Jul 2009
 by Limor Fried (http://www.ladyada.net)
 example added 9 Jul 2009
 by Tom Igoe
 modified 22 Nov 2010
 by Tom Igoe

 This example code is in the public domain.

 http://arduino.cc/en/Tutorial/LiquidCrystalDisplay

 */

// include the library code:
// #include <LiquidCrystal.h>  // LCD
#include <stdlib.h>
// DMD
#include <SPI.h>        //SPI.h must be included as DMD is written by SPI (the IDE complains otherwise)
#include <DMD.h>        //
#include <TimerOne.h>   //
#include "SystemFont5x7.h"
#include "Font3x5.h"

// initialize the library with the numbers of the interface pins
// LiquidCrystal lcd(12, 11, 5, 4, 3, 2);  // LCD
char DisplayString [7];
int Laps[4] = {0,0,0,0};
unsigned long LastLap[4] = {0,0,0,0};
float BestLap[4] = {9999,9999,9999,9999};
float CurrentLap[4] = {0,0,0,0};
long RaceMode = 0;  // 0 = Practise  1 = Timed  2 = Laps
long RaceState = 0;  // Setup

int OutputDevice = 0;  // 0 = LCD  1 = Dot Matrix


// Key Setup
int inByte = 0;
int MinimumBounce = 500;
unsigned long time[] = {
  0, 0, 0, 0};
int inState[] = {
  0, 0, 0, 0};
int inPins[] = {
  2,3}; // For LCD

/*
int inPins[] = {
  6,7}; // For DMD
*/  
int pinCount = 2;
unsigned long currentTime;

const int ledPin =  13;      // the number of the LED pin
int buttonPin = 14;
int buttonState = 0; 
String OutputString = "";

//Fire up the DMD library as dmd
#define DISPLAYS_ACROSS 1
#define DISPLAYS_DOWN 1
#define DISPLAYS_BPP 1
#define WHITE 0xFF
#define BLACK 0

DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN, DISPLAYS_BPP);

/*--------------------------------------------------------------------------------------
  Interrupt handler for Timer1 (TimerOne) driven DMD refresh scanning, this gets
  called at the period set in Timer1.initialize();
--------------------------------------------------------------------------------------*/
void ScanDMD()
{ 
  dmd.scanDisplayBySPI();
}



void LapTime(int lane)
{
  CurrentLap[lane] = millis() - LastLap[lane];
  LastLap[lane] = millis();
  Laps[lane]++;
  if (BestLap[lane] == 9999) BestLap[lane] = CurrentLap[lane];
  if (CurrentLap[lane] < BestLap[lane]) BestLap[lane] = CurrentLap[lane];
  UpdateDisplay();

}

void UpdateDisplay()
{
/*  LCD
    lcd.clear();
    float DisplayLap = CurrentLap[0];
    lcd.setCursor(0,0);
    lcd.print(CurrentLap[0] / 1000,3);
    lcd.setCursor(0,1);
    lcd.print(CurrentLap[1] / 1000,3);
  
    lcd.setCursor(11,0);
    if (BestLap[0] < 9999) lcd.print(BestLap[0] / 1000,3);
    lcd.setCursor(11,1);
    if (BestLap[1] < 9999) lcd.print(BestLap[1] / 1000,3);
    
    lcd.setCursor(7,0);
    lcd.print(Laps[0]);
    lcd.setCursor(7,1);
    lcd.print(Laps[1]);
*/


// DMD

   dmd.clearScreen( BLACK );
   dmd.selectFont(Font3x5);
   for (byte x=0;x<DISPLAYS_ACROSS;x++) {
     for (byte y=0;y<DISPLAYS_DOWN;y++) {
       dtostrf(CurrentLap[0] / 1000, 6, 3, DisplayString);
       Serial.println(String(DisplayString));
       dmd.drawString(  2+(32*x),  1+(16*y), DisplayString, 6, WHITE,BLACK );
       dtostrf(CurrentLap[1] / 1000, 6, 3, DisplayString);
       dmd.drawString(  2+(32*x),  9+(16*y), DisplayString, 6, WHITE,BLACK );
       Serial.println(String(DisplayString));

     }
   }
}

void setup() {
  // set up the LCD's number of columns and rows:

//   lcd.begin(16, 2);  LCD
  // Print a message to the LCD.

// DMD  
     //initialize TimerOne's interrupt/CPU usage used to scan and refresh the display
   Timer1.initialize( 5000/DISPLAYS_BPP );           //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()

   //clear/init the DMD pixels held in RAM
   dmd.clearScreen( BLACK );   //true is normal (all pixels off), false is negative (all pixels on)

  
    Serial.begin(115200);
    for (int index = 0; index < pinCount; index++) {
      //Set each defined pin as an input and enable the internal pullup resistor
      //The pullup resistor is a 20K resistor that is tied to 5v so we do not have
      //floating inputs.  
      pinMode(inPins[index], INPUT_PULLUP);
      pinMode(13,OUTPUT);

    }
}

void loop() {
  // Turn off the display:
  for (int index = 0; index < pinCount; index++) {

    //Read the digital input and store it into a variable
    inByte = digitalRead(inPins[index]);
    //Check to see if the state of the pin changed
    if (inByte != inState[index]){
      //Since the pin state changed we check to see how long it was since the last detection
      //If it has not been at least 100 milliseconds since the last detection we ignore it
      //This prevents flooding the serial port with tons of data
      //If it has been greater than 100 ms then we transmit 1 byte of info representing the pin number
      //This will show up as a hex value and not printable ascii
      if (millis() - time[index] > MinimumBounce) {
        if (RaceMode == 0)  LapTime(index);  //  Practise
      }
      //Store the current state of the pin so we can detect future changes
      inState[index] = inByte;
      time[index] = millis();
    }
  }
}

I thank you for your assistance in working this out. I can go with the LCD, but the effect with the DMD is pretty damn awesome, and easier to read in the heat of racing.

Brissieboy
Posts:209
Joined:Fri Sep 20, 2013 7:25 am

Re: DMD Noise?

Post by Brissieboy » Tue Jul 15, 2014 1:18 am

Blinkles,

Without looking at your code, I think you may have a couple of hardware issues here.
1. You will definitely get lots of electrical noise from your DMD as they are rapidly switching significant amounts of current.
2. I am assuming that you are directly connecting the reed switch inputs to the Arduino without any input circuitry except for the pullup/down resistor (your message is a little confusing on this point). Because this input is likely to be some distance from the processor, you should be providing some input protection and filtering or buffering. These digital lines are best kept to inches in length unless you take special precautions. Unprotected long lines often work, but it is definitely risky and susceptible to problems. (Google will give you heaps of info on input protection).
3. Using shielded cable for you reed switch inputs may also help (but you should still use protection and filtering).
4. Make sure you are using connections direct from the Arduino (not ones common to the DMD) for your reed switches - i.e do not use common ground connection leads.
5. Definitely use an external power supply for your DMD(s).

I hope that is some help.

angusgr
Freetronics Staff
Freetronics Staff
Posts:853
Joined:Tue Apr 09, 2013 11:19 pm
Location:Melbourne, Australia
Contact:

Re: DMD Noise?

Post by angusgr » Tue Jul 15, 2014 1:58 am

Brissieboy's hardware advice is good.

The other thing to consider is software input debouncing. This is commonly used with switches, but you likely have the same problem with the reed relays.

There's a page on debouncing here:
http://arduino.cc/en/Tutorial/Debounce

Why would this be a problem with the DMD and not the LCD? With the DMD, most of the work updating the display happens in the timer interrupt not in the main loop. This means that after reading the input once, it loops and reads the input again after only a short delay. With the LCD, the updating happens inside the loop() after an input is read - so there's naturally a short delay before the next time around.

To test if this might be the cause, you can try putting a delay(5) or similar at the bottom of the loop before it re-runs. The problem may go away but you may start missing some laps depending on how long the delay is, in which case you'll need to do proper debouncing as mentioned above.


Angus

Binkles
Posts:10
Joined:Mon Jul 14, 2014 1:09 pm

Re: DMD Noise?

Post by Binkles » Tue Jul 15, 2014 11:34 am

Hi, and thanks for the information,

The run between the reeds and the arduino are no more than 20cm, and the arduino to the DMD is the standard cable that comes with the kit, where as the LCD cable I've made is 50cm.

Length of cabling etc.
Image

Yellow cable to 5v
Green and Blue (on right) to Ground (blue was unplugged in the top picture)
Two Orange going to inputs.

Image

angusgr
Freetronics Staff
Freetronics Staff
Posts:853
Joined:Tue Apr 09, 2013 11:19 pm
Location:Melbourne, Australia
Contact:

Re: DMD Noise?

Post by angusgr » Wed Jul 16, 2014 4:52 am

Hi Binkles,

I'd focus on software debouncing for now, then.


Angus

Binkles
Posts:10
Joined:Mon Jul 14, 2014 1:09 pm

Re: DMD Noise?

Post by Binkles » Wed Jul 16, 2014 10:20 am

Problem is the noise isn't just affecting the one switch.

I can have one car going around, yet at random times, the other lane will still register laps, so I can't see the debouncing working - especially not with a delay, as once I get the four lanes going delaying for a second for each input would not be feasible.

Again, it's also not an issue at all when using the LCD's. I ran nearly 500 laps the other night, and not a single issue with the lcd.

Within 10 laps of the dmd, I was getting random laps for both lanes even when only one lane was in use.

I'm beginning to think that I can't use the DMD's for this project, and will have to find another use for them, which is a shame, as they are easier to read, and were the main reason I bought them.

angusgr
Freetronics Staff
Freetronics Staff
Posts:853
Joined:Tue Apr 09, 2013 11:19 pm
Location:Melbourne, Australia
Contact:

Re: DMD Noise?

Post by angusgr » Wed Jul 16, 2014 10:59 pm

Binkles wrote:Problem is the noise isn't just affecting the one switch.

I can have one car going around, yet at random times, the other lane will still register laps,
Thanks for clarifying, that makes it seem like noise is more likely. Also rereading your initial code you've debounced input effectively with your 100ms per pin check.
Binkles wrote:so I can't see the debouncing working - especially not with a delay, as once I get the four lanes going delaying for a second for each input would not be feasible.

Again, it's also not an issue at all when using the LCD's. I ran nearly 500 laps the other night, and not a single issue with the lcd.
The software timing for the LCD library is quite different, so I still wouldn't rule out that software differences (polling less often) may be causing the difference.

You can still use a debouncing technique to check for transient glitches and differentiate them from legitimate pulses, which should last longer. Perhaps try something like this:

At the top of your sketch:

Code: Select all

long OnAt[4] = {-1,-1,-1,-1};
const int MIN_SIGNAL = 20; // Minimum time in milliseconds that a pulse has to last for
const int LAP_EDGE = HIGH;  // Signal is high or low when a car passes over?
Then in your main loop, something like this:

Code: Select all

// Turn off the display:
  for (int index = 0; index < pinCount; index++) {

    //Read the digital input and store it into a variable
    inByte = digitalRead(inPins[index]);
    //Check to see if the state of the pin changed
    if (inByte != inState[index]) {
      inState[index] = inByte;
      long now = millis();

      if(inByte == LAP_EDGE) {
        // Start of a pulse from the reed relay
        OnAt[index] = now;
      } else if(OnAt[index] - now < MIN_SIGNAL) { 
        // Was a short glitch not a legitimate pulse
        OnAt[index] = -1;
      } else if(OnAt[index] != -1) { // Legitimate pulse
         //Since the pin state changed we check to see how long it was since the last detection
        //If it has not been at least 100 milliseconds since the last detection we ignore it
        //This prevents flooding the serial port with tons of data
        //If it has been greater than 100 ms then we transmit 1 byte of info representing the pin number
        //This will show up as a hex value and not printable ascii
        if (millis() - time[index] > MinimumBounce) {
          if (RaceMode == 0)  LapTime(index);  //  Practise
        }
        //Store the current state of the pin so we can detect future changes
        time[index] = millis();
      }
    }
  }
}
I haven't compiled the above, but it should give you something to work from at least.

You'll need to change the LAP_EDGE value to reflect which direction the reed relay changes to when the car is over it, and possibly also tune MIN_SIGNAL to make sure you always capture legitimate passes but reject glitches.

Hopefully something like this will work and you don't have to abandon the DMD. :)

Angus

Brissieboy
Posts:209
Joined:Fri Sep 20, 2013 7:25 am

Re: DMD Noise?

Post by Brissieboy » Thu Jul 17, 2014 4:46 am

It's really looking like a hardware issue to me. You may be able to hide it with software, but it would be preferable to determine exactly what is causing the problem and fix that. You may need access to a CRO to help sort this one out. It is very difficult when you can't see what is happening.

If it is escaping a debounce of 100mSec, it points to something a little more serious than simple noise.

I would be looking at a couple of things:
- The state of the various ground points with respect to that at the Arduino. As you are using pull-down resistors, a rise of the ground potential at the reed will look like a high signal.
- The state of Vcc at the Arduino with respect to ground at the Arduino. Is it a stable 5.0V? Does it fluctuate at all?
- And of course what does the input lead look like with respect to ground at the Arduino.
Are these different when driving only the LCD?

Make sure that there is no common path for either the 5v connections or the ground connections.
Are you using separate power supplies? If so where are you commoning the grounds? Have you tried a separate battery supply for the Arduino? A bit extra power supply filtering?

And don't forget that your slot cars will be generating a heap of electrical noise (but obviously the same for both displays).

angusgr
Freetronics Staff
Freetronics Staff
Posts:853
Joined:Tue Apr 09, 2013 11:19 pm
Location:Melbourne, Australia
Contact:

Re: DMD Noise?

Post by angusgr » Fri Jul 18, 2014 4:10 am

Brissieboy wrote: If it is escaping a debounce of 100mSec, it points to something a little more serious than simple noise.
The code Binkles is running at the moment ignores pulses 100ms after the "lap" pulse, but it doesn't ignore very short pulses that happen at other times - so if noise is causing very short edges to appear on the digital pins at other times, they'll be counted as laps.

I agree with you about looking for the hardware solution in the long run, though.

Brissieboy
Posts:209
Joined:Fri Sep 20, 2013 7:25 am

Re: DMD Noise?

Post by Brissieboy » Sat Jul 19, 2014 2:14 am

Something to think about and discuss??

We generally don't handle de-bouncing very well. It is usually done by various means within our main code loop, often using delays, and mostly requiring only 2 consecutive stable reads to indicate a valid state change. This often works fairly well, but is it really good enough?

I believe a better approach is to do the scanning of inputs at regular intervals independent of the main code by utilising interrupts. It also offers a better immunity to noise and false triggering. We tend to shy away from interrupts, but they are a great feature.

When using a DMD, an interrupt is setup using the TimerOne library by the line
Timer1.initialize( 5000 );
which initiates a 5mSec interrupt which in turn calls the service routine by the line
Timer1.attachInterrupt( ScanDMD );
This period of 5mSec is also quite reasonable to use as the de-bounce interval. We can add functionality to the service routine simply by adding to the ScanDMD() routine. There is no need to delve into the intricacies of timers or interrupts.
This new functionality should read and process the raw input data and subsequently set flags which will be processed in the main code loop. The signal should only be considered valid after a series of several (3 minimum?) consecutive stable reads.
There are numerous good and very efficient code examples around for this.
You need to keep in mind that this code will execute every 5mSec so it does need to be efficient. And you will need to use 'volatile' variables.
There is a good article on de-bouncing here.

Post Reply