Wednesday, July 22, 2009

Serial LED4

I bought this LED display from Sparkfun a while back and it was pretty easy to get setup and running with it. But I wanted to create a little class to control it in a standard way (class = "library" in Arduino speak). This is how I did it.

First things first, I looked at the specification from the web site. The device can be controlled through a single wire serial interface so I wired that up on the breadboard with the power and ground all setup and the single serial wire attached to the digital pin 4.

I also saw the NewSoftSerial library which had everything I wanted - I didn't need to worry about how to actually send serial data down pin 4 - I just needed to encapsulated the protocol defined in the data sheet.

So - the first step was to get the class ready with the types of function I wanted (which pretty much emulates what the protocol in the data sheet says). The device is simple - it gives you the ability to put some data on (in chunks of 4 digits) and a couple of control codes for the decimal points and brightness.

Here was my first stab at that:

#ifndef SERIAL_LED_4  
#define SERIAL_LED_4  
  
#include "NewSoftSerial.h"  
  
#define COLON_DISPLAY 0x30  
  
class SerialLED4 : public NewSoftSerial  
{  
public:  
 SerialLED4(uint8_t sendPin);  
 ~SerialLED4();  
  
 void setup();  
 void displayValue(int val);  
 void brightness(uint8_t level);  
 void decimalPoint(uint8_t flags);  
  
private:  
};  
  
  
#endif  

In this case we construct our class (which derives from NewSoftSerial) with the send pin and we have functions to setup the display (if needed) and to display values and adjust the brightness. As for the implementation?

SerialLED4::SerialLED4(uint8_t sendPin) :
 NewSoftSerial(sendPin, sendPin)
{
}

SerialLED4::~SerialLED4()
{
 
}

void SerialLED4::brightness(uint8_t level)
{
print(0x7A, BYTE);
print(level, BYTE);
}

void SerialLED4::decimalPoint(uint8_t flags)
{
print(0x77, BYTE);
print(flags, BYTE);
}

void SerialLED4::displayValue(int val)
{
if (val < 1000)
{
print(0);
}
if (val < 100)
{
print(0);
}
if (val < 10)
{
print(0);
}
print(val);
flush();
}

In this case the NewSoftSerial has a send and receive mode, we simply use the same pin for both (it doesn't matter). And then looking at the protocol the act of displaying and controlling the display is achieved by simply "printing" the value (print is a method in NewSoftSerial). For the display of the value we make sure we send 4 bytes at a time.

And that kind of worked, except that the display sometimes got out of sync (i.e. the 4 digits were offset or rolled around). Not good. So instead of wiring the power pin of the device direct to Vcc I actually wired it to another digital out pin (which seems to provide enough power to the device when HIGH). That way I can add some new methods to powerOn and powerOff the device and to control how it starts up. This is how I did that:

class SerialLED4 : public NewSoftSerial
{
 public:
  SerialLED4( uint8_t sendPin, uint8_t powerPin);
  ~SerialLED4();
  
  void setup();
  void displayValue(int val);
  void powerOn();
  void powerOff();
  
  void brightness(uint8_t level);
  void decimalPoint(uint8_t flags);

 private:
  uint8_t _powerPin;
};

I added a powerPin to the constructor and some new methods to power it on and off. The changes to the implementation are reproduced below:

In the constructor, store the power pin and set it to be an output pin:

SerialLED4::SerialLED4(uint8_t sendPin, uint8_t powerPin) :
 NewSoftSerial(sendPin, sendPin)
{
 _powerPin = powerPin;
 pinMode(_powerPin, OUTPUT);
}

Then in the setup function, call powerOn:

void SerialLED4::setup()
{
 powerOn();
}

Power off is easy:

void SerialLED4::powerOff()
{
 digitalWrite(_powerPin, LOW); 
}

Power on introduces a delay to make sure the device is ready and sets the brightness to be the maximum.

void SerialLED4::powerOn()
{
begin(9600);
flush();
digitalWrite(_powerPin, HIGH);
delay(100);
brightness(0);
}

This now works just fine - here's an example use of the software in my data logger application:

class DLApp
{
private:
 SerialLED4 _ledDisplay;

// blah blah blah

};

DLApp::DLApp(uint8_t ledSend,
    uint8_t ledPower)
: _ledDisplay(ledSend, ledPower)
{

}

void DLApp::setup()
{
 _ledDisplay.setup();
}


// And then somewhere in my application:

// Display current temperature on display
_ledDisplay.displayValue(_temperature);

I'll post a link to the source code for my libraries soon once I've cleaned them up a bit. Then you can use them verbatim.

No comments:

Post a Comment