t r o n i x s t u f f

fun and learning with electronics

Tutorial: Arduino and the MSGEQ7 Spectrum Analyzer

This is a tutorial on using the MSGEQ7 Spectrum Analyser with Arduino, and chapter forty-eight of a series originally titled “Getting Started/Moving Forward with Arduino!” by John Boxall – A tutorial on the Arduino universe. The first chapter is here, the complete series is detailed here.

Updated 30/01/2013

In this article we’re going to explain how to make simple spectrum analysers with an Arduino-style board. (Analyser? Analyzer? Take your pick).

First of all, what is a spectrum analyser? Good question. Do you remember what  this is?

It’s a mixed graphic equaliser/spectrum analyser deck for a hi-fi system. The display in the middle is the spectrum analyser, and roughly-speaking it shows the strength of  different frequencies in the music being listened to – and looked pretty awesome doing it. We can recreate displays similar to this for entertainment and also as a base for creative lighting effects. By working through this tutorial you’ll have the base knowledge to recreate these yourself.

We’ll be using the MSGEQ7 “seven band graphic equaliser IC” from Mixed Signal Integration. Here’s the MSGEQ7 data sheet (.pdf).  This little IC can accept a single audio source, analyse seven frequency bands of the audio, and output a DC representation of each frequency band. This isn’t super-accurate or calibrated in any way, but it works. You can get the IC separately, for example:


and then build your own circuit around it… or like most things in the Arduino world – get a shield. In this case, a derivative of the original Bliptronics shield by Sparkfun. It’s designed to pass through stereo audio via 3.5mm audio sockets and contains two MSGEQ7s, so we can do a stereo analyser:

As usual Sparkfun have saved a few cents by not including the stackable header sockets, so you’ll need to buy and solder those in yourself. There is also space for three header pins for direct audio input (left, right and common), which are useful – so if you can add those as well.

So now you have a shield that’s ready for use. Before moving forward let’s examine how the MSGEQ7 works for us. As mentioned earlier, it analyses seven frequency bands. These are illustrated in the following graph from the data sheet:

It will return the strengths of the audio at seven points – 63 Hz, 160 Hz, 400 Hz, 1 kHz, 2.5 kHz, 6.25 kHz and 16 kHz – and as you can see there is some overlap between the bands. The strength is returned as a DC voltage – which we can then simply measure with the Arduino’s analogue input and create a display of some sort. At this point audio purists, Sheldonites and RF people might get a little cranky, so once again – this is more for visual indication than any sort of calibration device.

However as an 8-pin IC a different approach is required to get the different levels. The IC will sequentially give out the levels for each band on pin 3- e.g. 63 Hz then 160 Hz then 400 Hz then 1 kHz then 2.5 kHz then 6.25 kHz  then 16 kHz then back to 63 Hz and so on. To start this sequence we first reset the IC by pulsing the RESET pin HIGH then low. This tells the IC to start at the first band. Next, we set the STROBE pin to LOW, take the DC reading from pin 3 with analogue input, store the value in a variable (an array), then set the STROBE pin HIGH. We repeat the strobe-measure sequence six more times to get the rest of the data, then RESET the IC and start all over again. For the visual learners consider the diagram below from the data sheet:

To demonstrate this process, consider the function

readMSGEQ7()

in the following example sketch (download):

// Example 48.1 - tronixstuff.com/tutorials > chapter 48 - 30 Jan 2013 
// MSGEQ7 spectrum analyser shield - basic demonstration
int strobe = 4; // strobe pins on digital 4
int res = 5; // reset pins on digital 5
int left[7]; // store band values in these arrays
int right[7];
int band;
void setup()
{
 Serial.begin(115200);
 pinMode(res, OUTPUT); // reset
 pinMode(strobe, OUTPUT); // strobe
 digitalWrite(res,LOW); // reset low
 digitalWrite(strobe,HIGH); //pin 5 is RESET on the shield
}
void readMSGEQ7()
// Function to read 7 band equalizers
{
 digitalWrite(res, HIGH);
 digitalWrite(res, LOW);
 for(band=0; band <7; band++)
 {
 digitalWrite(strobe,LOW); // strobe pin on the shield - kicks the IC up to the next band 
 delayMicroseconds(30); // 
 left[band] = analogRead(0); // store left band reading
 right[band] = analogRead(1); // ... and the right
 digitalWrite(strobe,HIGH); 
 }
}
void loop()
{
 readMSGEQ7();
 // display values of left channel on serial monitor
 for (band = 0; band < 7; band++)
 {
 Serial.print(left[band]);
 Serial.print(" ");
 }
 Serial.println();
// display values of right channel on serial monitor
 for (band = 0; band < 7; band++)
 {
 Serial.print(right[band]);
 Serial.print(" ");
 }
 Serial.println();
}

If you follow through the sketch, you can see that it reads both left- and right-channel values from the two MSGEQ7s on the shield, then stores each value in the arrays left[] and right[]. These values are then sent to the serial monitor for display – for example:

If you have a function generator, connect the output to one of the channels and GND – then adjust the frequency and amplitude to see how the values change. The following video clip is a short demonstration of this – we set the generator to 1 kHz and adjust the amplitude of the signal. To make things easier to read we only measure and display the left channel:


Keep an eye on the fourth column of data – this is the analogRead() value returned by the Arduino when reading the 1khz frequency band. You can also see the affect on the other bands around 1 kHz as we increase and decrease the frequency. However that wasn’t really visually appealing – so now we’ll create a small and large graphical version.

First we’ll use an inexpensive LCD, the I2C model from akafugu reviewed previously. To save repeating myself, also review how to create custom LCD characters from here.

With the LCD with have two rows of sixteen characters. The plan is to use the top row for the levels, the left-channel’s on … the left, and the right on the right. Each character will be a little bar graph for the level. The bottom row can be for a label. We don’t have too many pixels to work with, but it’s a compact example:

We have eight rows for each character, and the results from an analogueRead() fall between 0 and 1023. So that’s 1024 possible values spread over eight sections. Thus each row of pixels in each character will represent 128 “units of analogue read” or around 0.63 V if the Arduino is running from true 5 V (remember your AREF notes?). The sketch will again read the values from the MSGEQ7, feed them into two arrays – then display the required character in each band space  on the LCD.

Here’s the resulting sketch (download):

// Example 48.2 - tronixstuff.com/tutorials > chapter 48 - 30 Jan 2013 
// MSGEQ7 spectrum analyser shield and I2C LCD from akafugu
// for akafugu I2C LCD
#include 
#include "TWILiquidCrystal.h"
LiquidCrystal lcd(50);
// create custom characters for LCD
byte level0[8] = { 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111};
byte level1[8] = { 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111};
byte level2[8] = { 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111};
byte level3[8] = { 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111};
byte level4[8] = { 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
byte level5[8] = { 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
byte level6[8] = { 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
byte level7[8] = { 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
int strobe = 4; // strobe pins on digital 4
int res = 5; // reset pins on digital 5
int left[7]; // store band values in these arrays
int right[7];
int band;
void setup()
{
 Serial.begin(9600);
 // setup LCD and custom characters
 lcd.begin(16, 2);
 lcd.setContrast(24);
 lcd.clear();
lcd.createChar(0,level0);
 lcd.createChar(1,level1);
 lcd.createChar(2,level2);
 lcd.createChar(3,level3);
 lcd.createChar(4,level4);
 lcd.createChar(5,level5);
 lcd.createChar(6,level6);
 lcd.createChar(7,level7);
 lcd.setCursor(0,1);
 lcd.print("Left");
 lcd.setCursor(11,1);
 lcd.print("Right");
 pinMode(res, OUTPUT); // reset
 pinMode(strobe, OUTPUT); // strobe
 digitalWrite(res,LOW); // reset low
 digitalWrite(strobe,HIGH); //pin 5 is RESET on the shield
}
void readMSGEQ7()
// Function to read 7 band equalizers
{
 digitalWrite(res, HIGH);
 digitalWrite(res, LOW);
 for( band = 0; band < 7; band++ )
 {
 digitalWrite(strobe,LOW); // strobe pin on the shield - kicks the IC up to the next band 
 delayMicroseconds(30); // 
 left[band] = analogRead(0); // store left band reading
 right[band] = analogRead(1); // ... and the right
 digitalWrite(strobe,HIGH); 
 }
}
void loop()
{
 readMSGEQ7();
// display values of left channel on LCD
 for( band = 0; band < 7; band++ )
 {
 lcd.setCursor(band,0);
 if (left[band]>=895) { lcd.write(7); } else
 if (left[band]>=767) { lcd.write(6); } else
 if (left[band]>=639) { lcd.write(5); } else
 if (left[band]>=511) { lcd.write(4); } else
 if (left[band]>=383) { lcd.write(3); } else
 if (left[band]>=255) { lcd.write(2); } else
 if (left[band]>=127) { lcd.write(1); } else
 if (left[band]>=0) { lcd.write(0); }
 }
 // display values of right channel on LCD
 for( band = 0; band < 7; band++ )
 {
 lcd.setCursor(band+9,0);
 if (right[band]>=895) { lcd.write(7); } else
 if (right[band]>=767) { lcd.write(6); } else
 if (right[band]>=639) { lcd.write(5); } else
 if (right[band]>=511) { lcd.write(4); } else
 if (right[band]>=383) { lcd.write(3); } else
 if (right[band]>=255) { lcd.write(2); } else
 if (right[band]>=127) { lcd.write(1); } else
 if (right[band]>=0) { lcd.write(0); }
 }
}

If you’ve been reading through my tutorials there isn’t anything new to worry about. And now for the demo, with sound -

That would look great on the side of a Walkman, however it’s a bit small. Let’s scale it up by using a Freetronics Dot Matrix Display - you may recall these from Clock One. For some background knowledge check the review here.  Don’t forget to use a suitable power supply for the DMD – 5 V at 4 A will do nicely. The DMD contains 16 rows of 32 LEDs. This gives us twice the “resolution” to display each band level if desired. The display style is subjective, so for this example we’ll use a single column of LEDs for each frequency band, with a blank column between each one.

We use a lot of line-drawing statements to display the levels, and clear the DMD after each display. With this and the previous sketches, there could be room for efficiency – however I write these with the beginner in mind. Here’s the sketch (download):

// Example 48.3 - tronixstuff.com/tutorials > chapter 48 - 30 Jan 2013 
// MSGEQ7 spectrum analyser shield with a Freetronics DMD
// for DMD
#include  // for DMD
#include  // SPI.h must be included as DMD is written by SPI (the IDE complains otherwise)
#include  
#include "SystemFont5x7.h" // keep next two lines if you want to add some text
#include "Arial_black_16.h"
DMD dmd(1, 1); // creates instance of DMD to refer to in sketch
void ScanDMD() // necessary interrupt handler for refresh scanning of DMD
{ 
 dmd.scanDisplayBySPI();
}
int strobe = 4; // strobe pins on digital 4
int res = 5; // reset pins on digital 5
int left[7]; // store band values in these arrays
int right[7];
int band;
void setup()
{
 // for DMD
 //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() 
 dmd.clearScreen( true ); //true is normal (all pixels off), false is negative (all pixels on)

 // for MSGEQ7
 pinMode(res, OUTPUT); // reset
 pinMode(strobe, OUTPUT); // strobe
 digitalWrite(res,LOW); // reset low
 digitalWrite(strobe,HIGH); //pin 5 is RESET on the shield
}
void readMSGEQ7()
// Function to read 7 band equalizers
{
 digitalWrite(res, HIGH);
 digitalWrite(res, LOW);
 for( band = 0; band < 7; band++ )
 {
 digitalWrite(strobe,LOW); // strobe pin on the shield - kicks the IC up to the next band 
 delayMicroseconds(30); // 
 left[band] = analogRead(0); // store left band reading
 right[band] = analogRead(1); // ... and the right
 digitalWrite(strobe,HIGH); 
 }
}
void loop()
{
 int xpos;
 readMSGEQ7();
 dmd.clearScreen( true ); 
 // display values of left channel on DMD
 for( band = 0; band < 7; band++ )
 {
 xpos = (band*2)+1;
 if (left[band]>=895) { dmd.drawLine( xpos, 15, xpos, 1, GRAPHICS_NORMAL ); } else
 if (left[band]>=767) { dmd.drawLine( xpos, 15, xpos, 3, GRAPHICS_NORMAL ); } else
 if (left[band]>=639) { dmd.drawLine( xpos, 15, xpos, 5, GRAPHICS_NORMAL ); } else
 if (left[band]>=511) { dmd.drawLine( xpos, 15, xpos, 7, GRAPHICS_NORMAL ); } else
 if (left[band]>=383) { dmd.drawLine( xpos, 15, xpos, 9, GRAPHICS_NORMAL ); } else
 if (left[band]>=255) { dmd.drawLine( xpos, 15, xpos, 11, GRAPHICS_NORMAL ); } else
 if (left[band]>=127) { dmd.drawLine( xpos, 15, xpos, 13, GRAPHICS_NORMAL ); } else
 if (left[band]>=0) { dmd.drawLine( xpos, 15, xpos, 15, GRAPHICS_NORMAL ); }
 }

 // display values of right channel on DMD
 for( band = 0; band < 7; band++ )
 {
 xpos = (band*2)+18;
 if (right[band]>=895) { dmd.drawLine( xpos, 15, xpos, 1, GRAPHICS_NORMAL ); } else
 if (right[band]>=767) { dmd.drawLine( xpos, 15, xpos, 3, GRAPHICS_NORMAL ); } else
 if (right[band]>=639) { dmd.drawLine( xpos, 15, xpos, 5, GRAPHICS_NORMAL ); } else
 if (right[band]>=511) { dmd.drawLine( xpos, 15, xpos, 7, GRAPHICS_NORMAL ); } else
 if (right[band]>=383) { dmd.drawLine( xpos, 15, xpos, 9, GRAPHICS_NORMAL ); } else
 if (right[band]>=255) { dmd.drawLine( xpos, 15, xpos, 11, GRAPHICS_NORMAL ); } else
 if (right[band]>=127) { dmd.drawLine( xpos, 15, xpos, 13, GRAPHICS_NORMAL ); } else
 if (right[band]>=0) { dmd.drawLine( xpos, 15, xpos, 15, GRAPHICS_NORMAL ); }
 }
}

… and here it is in action:

Conclusion

At this point you have the knowledge to use the MSGEQ7 ICs to create some interesting spectrum analysers for entertainment and visual appeal – now you just choose the type of display enjoy the results.

Have fun and keep checking into tronixstuff.com. Why not follow things on twitterGoogle+, subscribe  for email updates or RSS using the links on the right-hand column, or join our Google Group – dedicated to the projects and related items on this website. Sign up – it’s free, helpful to each other –  and we can all learn something.

January 31, 2013 Posted by | analyser, analyzer, arduino, BLIPTRONICS, com-10468, dev-10306, education, graphic, lesson, MSGEQ7, sparkfun, spectrum, tutorial | , , , , , , , , , , , , , , , , , , , , , , | 7 Comments

Tutorial: Arduino and TFT LCD

Learn how to use the 4D Systems 1.44″ TFT serial interface LCD with Arduino in chapter twenty-nine of a series originally titled “Getting Started/Moving Forward with Arduino!” by John Boxall – A tutorial on the Arduino universe. The first chapter is here, the complete series is detailed here.

If you’re looking for an inexpensive and simple TFT LCD to use with Arduino, visit the new tutorial.

Update 20/04/2013 

The Arduino library for this module hasn’t been updated to work with Arduino v1.0.1+ – so you need to use Arduino IDE v22 or v23. And the module itself has been discontinued. For the time being I’m leaving the tutorial here until a more suitable item can be used. We can’t help you with the 4D module

Nevertheless – if you have one – here’s the subject of the tutorial- the 4D Systems 1.44″ TFT serial interface LCD:

The LCD is an LED-backlit thin-film transistor type, resolution is 128 x 128 pixels, with an RGB colour range of 65536.

As an aside, this is a very powerful piece of hardware. The module not only contains a 1.44″ square TFT LCD, there is also a dedicated graphics processor and a microSD card interface. One can program the display processor in the same manner as another microcontroller platform for incredibly interesting results. For more information, please visit here.

However in the spirit of keeping things simple, this article will focus on driving the LCD directly using our Arduino or compatible boards. There are two firmware versions of this module – the GFX and the SGC. We need to have the SGC firmware, as this allows control via the serial TX/RX pins from our Arduno board. If you have purchased the SGC module, you’re ready to go. Scroll down until you see “And we’re back…”. However if you have the GFX version, please read the following instructions on how to change your LCD’s firmware from GFX to SGC…

Changing the firmware from GFX to SGC

  • At the moment this process only seems available to users of Microsoft Windows. All complaints to 4D Systems.
  • Unfortunately this process may not work with an Arduino Mega board.
  • First of all, remove the ATmega328 from your Arduino board. Please be careful, use a chip puller if possible. We are going to use the board as a simple serial-USB converter;
  • Insert your LCD module into a solderless breadboard;
  • Connect Arduino pin 0 (RX) to display pin 7 (RX); connect Arduino pin 1 (TX) to display pin 8 (TX). [Yes - TX>TX, RX>RX];
  • Connect Arduino 5V to display pin 9; connect Arduino GND to display pin 6; your LCD should display the following:

  • Visit here, download and open the PmmC Loader application; visit here and download the .pmmc file to your local drive;
  • Connect your Arduino board via USB to the computer; then run the PmmC loader application;
  • Select the appropriate COM: port, load in the .pmmc file, then click Load. The firmware update should take less than sixty seconds;
  • When finished, you will be presented with the following on the computer:

… and the following on your LCD:

  • At this point unplug the USB lead from your Arduino board and all leads into the Arduino board;
  • Re-insert the ATmega328 back into your Arduino board;
  • Reconnect the wires from the LCD module to the Arduino, but this time connect Arduino TX to LCD RX; and LCD TX to Arduino RX.
  • Now you have  the serial-interface SGC firmware model LCD.

And we’re back…

To control this LCD, it requires commands to be sent via Serial.write(), and such commands are in the form of hexadecimal numbers. (You see something new every day). You can download the reference book with all the commands: SGC Commands.pdf and bypass the library by directly writing the hexadecimal numbers directly to the module.

However, to get up to speed as fast as possible we can use a library with more of the popular functions included. Kudos and thanks to Oscar Gonzalez for writing a very useful library. Download the library from here and install into your ../Arduino-002x/libraries folder, then re-start the Arduino IDE if you had it running. You may be wondering why the library is named displayshield4d - the LCD manufacturer sells this LCD on an Arduino shield. Although that would be great for experimenting, one would need to purchase another standalone LCD if their project moved forward – myself included. So that’s why we’re using the bare LCD board.

To connect the LCD to our Arduino is very simple:

  • LCD pin 5 to Arduino RST;
  • LCD pin 6 to Arduino GND;
  • LCD pin 7 to Arduino D1;
  • LCD pin 8 to Arduino D0;
  • LCD pin 9 to Arduino 5V.

In the following examples we will demonstrate the various functions available in the library. As this is chapter 29, I will no longer explain the more basic functions or ideas that you should know by now, instead relying on comments within the sketch if it feels necessary. It can take a short moment for the LCD controller to process, so always put a short delay between functions.

When uploading a sketch to your Arduino you may need to disconnect the LCD from Arduino D0/D1 as it can interfere with the serial process.

Firstly we will demonstrate text display. Initialising the display requires a few functions:

#include <displayshield4d.h> // include the LCD library
DisplayShield4d  lcd;

The second line creates an instance of lcd to be used with the relevant functions. Next, within void setup():

Serial.begin(115200);  // LCD speed is very high
lcd.Init(); // wake up LCD
lcd.Clear(); // clear the LCD, set background to black

To write text to the LCD, the following function is required:

lcd.setfontmode(OLED_FONT_TRANSPARENT);  // set font background type

This line sets the font transparency. If we use the parameter OLED_FONT_TRANSPARENT the unused pixels in the character area will be transparent and continue to show what they were set to before the text was over-written with. You can also use OLED_FONT_OPAQUE, which blocks the item displayed “behind” the text.

Whenever a function requires a colour parameter, we use:

lcd.RGB(x,y,z);

where x, y and z are numerical values (between 0 and 255) for the red, green and blue components of the required colour. If you need an RGB numerical reference, download this handy chart. Finally, to display some text we use the following:

lcd.drawstringblock(a, b, c, lcd.RGB(255, 255, 255), d, e, "Hello, world");

The parameters required are:

  • a – the x-position of the first character. E.g. if this was a zero, the top-left pixel of the first character would be on the left-most pixel column of the LCD;
  • b – the y-position of the first character. e.g. if both a and b were zero, the text would start from the top-left of the LCD;
  • c – numerical code for the font to use: 1 is for 5×7 pixel characters, 2 for 8×8 and 3 for 8×12;
  • the three values within the lcd.RGB() function determine the colour of the text;
  • d – x-axis resolution multiplier. E.g. if you double this and use the 5×7 font, the characters will be double-width;
  • e – y-axis resolution multiplier.

Now let’s see this in action with the following sketch:

Example 29.1

/*  Example 29.1 - uLCD-144 text demonstration
http://tronixstuff.com/tutorials > chapter 29  CC by-sa-nc  */
#include <displayshield4d.h> // necessary library
DisplayShield4d  lcd; // create an instance of the LCD
void setup()
{
Serial.begin(115200);  // LCD speed is very high
lcd.Init(); // wake up LCD
delay(20);
}
void loop()
{
lcd.Clear(); // clear LCD
delay(20);
lcd.setfontmode(OLED_FONT_TRANSPARENT);  // set font background type
delay(20);
for (int a=0; a<128; a+=8)
{
lcd.drawstringblock(0, a, 2 , lcd.RGB(255, 0, 0), 1, 1, "visit me");
delay(20);
}
delay(2000);
lcd.Clear();
for (int a=0; a<128; a+=16)
{
lcd.drawstringblock(0, a, 0, lcd.RGB(0, 255, 0), 2, 2, "0123456789");
delay(20);
}
delay(2000);
lcd.Clear();
delay(20);
for (int a=0; a<128; a+=32)
{
lcd.drawstringblock(0, a, 0, lcd.RGB(0, 0, 255), 4, 4, "Great");
delay(20);
}
delay(2000);
}

And a short video clip of the example in action:

As you can see the display update speed is much better than the LCD from the previous chapter. Although this example was short, don’t be afraid to try out your own parameters in the example sketch.

Next we will demonstrate the various graphics functions in the library. Creating graphics isn’t rocket science, it just takes some imagination (something I admit to lacking) and following the parameters for each function. Our first is

lcd.putpixel(x,y,lcd.RGB(r,g,b));

which places a pixel on the screen at location x,y of colour described using lcd.RGB(). Next we have

lcd.line(x1,y1,x2,y2,lcd.RGB(r,g,b));

which draws a line from x1, y1 to x2, y2 of colour rgb. One can also create rectangles and so on using

lcd.rectangle(x,y,l,h,z,lcd.RGB(r,g,b));

This will create a rectangle with the top-left point at x,y; width is l pixels, height is h pixels, and a new parameter z. If z is 0, the function will draw a solid shape, if z is 1, it will display only a wire-frame rectangle with a pixel width of one. Circles are created using

lcd.circle(x,y,r,z,lcd.RGB(r,g,b);

where x and y are the coordinates for the centre of the circle, r is the radius, and z is the solid/wireframe parameter. And finally – triangles:

lcd.triangle(x1,y1,x2,y2,x3,y3,z,lcd.RGB(r,g,b));

This will draw a triangle with the corners at the coordinate parameters; z again is the solid/wireframe parameter. However you need to order the corners in an anti-clockwise order. This will become evident in the example sketch below.

Example 29.2

In this example we run through the graphical functions described above. By following through the sketch you should gain an idea of how the graphical functions are used, in order to create your own displays.

/*  Example 29.2 - uLCD-144 graphic library demonstration
http://tronixstuff.com/tutorials > chapter 29  CC by-sa-nc  */
int a,b,c,d,e=0;
#include <displayshield4d.h> // necessary library
DisplayShield4d  lcd; // create an instance of the LCDvoid
setup()
{
randomSeed(analogRead(0));
Serial.begin(115200);  // LCD speed is very high
lcd.Init(); // wake up LCD
delay(50);
}
void loop()
{
lcd.Clear();
delay(50);
for (int z=0; z<2500; z++)
{
a=random(127);
b=random(127);
c=random(255);
d=random(255);
e=random(255);
lcd.putpixel(a,b,lcd.RGB(c,d,e));
delay(50);
}
delay(1000);
lcd.Clear();
delay(50);
for (int z=0; z<64; z++)
{
lcd.line(0,0,127,z*2,lcd.RGB(0,255,0));
delay(50);
}
for (int z=0; z<64; z++)
{
lcd.line(0,0,z*2,127,lcd.RGB(0,0,255));
delay(50);
}
delay(1000);
lcd.Clear();
delay(50);
for (int z=0; z<15; z++)
{
lcd.rectangle(z*10, z*10, 20, 20, 1, lcd.RGB(255,0,0));
delay(250);
lcd.rectangle(z*10, z*10, 20, 20, 0, lcd.RGB(0,0,255));
delay(250);
lcd.rectangle(z*10, z*10, 20, 20, 0, lcd.RGB(0,0,0));
delay(250);
}
delay(1000);
lcd.Clear();
delay(50);
for (int z=0; z<14; z++)
{
lcd.circle(63,63,z*3, 1, lcd.RGB(0,0,255));
delay(250);
lcd.circle(63,63,z*3, 0, lcd.RGB(0,255,0));
delay(250);
lcd.circle(63,63,z*3, 0, lcd.RGB(0,0,0));
delay(250);
}
delay(1000);
lcd.Clear();
delay(50);
for (int z=10; z>-0; --z)
{
lcd.triangle(127,127,64,z*10,0,127,1,lcd.RGB(0,255,0));
delay(250);
lcd.triangle(127,127,64,z*10,0,127,0,lcd.RGB(0,0,255));
delay(250);
lcd.triangle(127,127,64,z*10,0,127,0,lcd.RGB(0,0,0));
delay(250);
}
}

And here is the video of example 29.2 in action … brought to you by Mr Blurrycam:

So there you have it, another useful part and a very nice colour LCD to make use of.

Have fun and keep checking into tronixstuff.com. Why not follow things on twitterGoogle+, subscribe  for email updates or RSS using the links on the right-hand column, or join our Google Group – dedicated to the projects and related items on this website. Sign up – it’s free, helpful to each other –  and we can all learn something.

February 18, 2011 Posted by | arduino, education, LCD, learning electronics, microcontrollers | , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , | Leave a Comment

Tutorial: Arduino and monochrome LCDs

Please note that the tutorials are not currently compatible with Arduino IDE v1.0. Please continue to use v22 or v23 until further notice. 

This is chapter twenty-four of a series originally titled “Getting Started/Moving Forward with Arduino!” by John Boxall – A tutorial on the Arduino universe.

The first chapter is here, the complete series is detailed here. Please note from November 1, 2010 files from tutorials will be found here.

Welcome back fellow arduidans!

The purpose of this article is to summarise a range of affordable monochrome liquid-crystal display units that are available to work with our Arduino; and to replace the section about LCDs in chapter two of this series. We will first examine some fixed-character and then graphical LCD units in this article. So let’s go!

Fixed-character LCD modules

When shopping around for LCD modules, these will usually be the the most common found in retail outlets. Their size is normally measured by the number of columns and rows of characters in the display. For example, the three LCDs below are 8×2, 16×2 and 20×4 characters in size:

Currently, most LCDs should have a backlight of some sort, however you may come across some heavily-discounted models on (for example) eBay that are not. Character, background and backlight colours can vary, for example:

Interfacing these screens with our Arduino boards is very easy, and there are several ways to do so. These interface types can include four- and eight-bit parallel, three-wire,  serial, I2C and SPI interfaces; and the LCD price is usually inversely proportional to the ease of interface (that is, parallel are usually the cheapest).

Four-bit parallel interface

This is the cheapest method of interface, and our first example for this article. Your LCD will need a certain type of controller IC called a Hitachi HD44780 or compatible such as the KS0066. From a hardware perspective, there are sixteen pins on the LCD. These are usually in one row:

… or two rows of eight:

The pin labels for our example are the following:

  1. GND
  2. 5V (careful! Some LCDs use 3.3 volts – adjust according to LCD data sheet from supplier)
  3. Contrast
  4. RS
  5. RW
  6. Enable
  7. DB0 (pins DB0~DB7 are the data lines)
  8. DB1
  9. DB2
  10. DB3
  11. DB4
  12. DB5
  13. DB6
  14. DB7
  15. backlight + (unused on non-backlit LCDs) – again, check your LCD data sheet as backlight voltages can vary.
  16. backlight GND (unused on non-backlit LCDs)

As always, check your LCD’s data sheet before wiring it up.

Some LCDs may also have the pinout details on their PCB if you are lucky, however it can be hard to decipher:

Now let’s connect our example 16×2 screen to our Arduino using the following diagram.

Our LCD runs from 5V and also has a 5V backlight – yours may differ, so check the datasheet:

(Circuit layout created using Fritzing)

Notice how we have used six digital output pins on the Arduino, plus ground and 5V. The 10k ohm potentiometer connected between LCD pins 2, 3 and 5 is used to adjust the display contrast. You can use any digital out pins on your Arduino, just remember to take note of which ones are connected to the LCD as you will need to alter a function in your sketch. If your backlight is 3.3V, you can use the 3.3V pin on the Arduino.

From a software perspective, we need to use the LiquidCrystal() library. This library should be pre-installed with the Arduino IDE. So in the start of your sketch, add the following line:

#include <LiquidCrystal.h>

Next, you need to create a variable for our LCD module, and tell the sketch which pins are connected to which digital output pins. This is done with the following function:

LiquidCrystal lcd(4,5,6,7,8,9);

The parameters in the brackets define which digital output pins connect to (in order) LCD pins: RS, enable, D4, D5, D6, and D7.

Finally, in your void setup(), add the line:

lcd.begin(16,2);

This tells the sketch the dimensions in characters (columns, rows) of our LCD module defined as the variable lcd.

Example 24.1

In this example we will get started with out LCD by using the basic setup and functions. To save space the explanation of each function will be in the sketch itself. Please note that you do not have to use an Arduino Mega – it is used in this article as my usual Arduino boards are occupied elsewhere.

/*
Example 24.1 - LCD demonstration
http://tronixstuff.com/tutorials > Chapter 24
CC by-sa-nc liquidCrystal library originally added 18 Apr 2008 by David A. Mellis
library modified 5 Jul 2009   by Limor Fried (http://www.ladyada.net, http://www.adafruit.com)
*/
#include <LiquidCrystal.h> // we need this library for the LCD commands
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(4,5,6,7,8,9); // define our LCD and which pins to user
int a = 63;
int d = 3000; // used for display delayfloat
float b = 3.1415926;
void setup()
{
lcd.begin(16, 2); // need to specify how many columns and rows are in the LCD unit
lcd.clear();      // this clears the LCD. You can use this at any time
}
void loop()
{
lcd.clear();
lcd.setCursor(0,0);
// positions starting point on LCD, column 0, row 0 (that is, the top left of our LCD)
lcd.print("Hello!");
// prints "Hello" at the LCD's cursor position defined above
lcd.setCursor(0,1);
// positions starting point on LCD, column 0, row 1 (that is, the bottom left of our LCD)
lcd.println("This is fun     ");
// note the rest of the line is padded out with spaces
delay(d);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("a = ");
lcd.print(a); // this will immediately follow "a = "
lcd.setCursor(0,1);
lcd.print("pi = ");
lcd.print(b,7);
// the 7 means 7 decimal places. You can also replace this with DEC, HEX, BIN as with
// other *.print functions, as such:
delay(d);
lcd.clear();
lcd.home(); // sets cursor back to position 0,0 - same as lcd.setCursor(0,0);
lcd.print("a (HEX) = ");
lcd.print(a, HEX); // this will immediately follow "a = "
lcd.setCursor(0,1);
lcd.print("a (BIN) = ");
lcd.print(a,BIN);
delay(d);
lcd.noDisplay(); // turns off the display, leaving currently-displayed data as is
delay(d);        // however this does not control the backlight
lcd.display();   // resumes display
delay(d);
}

And here is a quick video of the example 24.1 sketch in action:

There are also a some special effects that we can take advantage of with out display units – in that we can actually define our own characters (up to eight per sketch). That is, control the individual dots (or pixels) that make up each character. With the our character displays, each character is made up of five columns of eight rows of pixels, as illustrated in the close-up below:

In order to create our characters, we need to define which pixels are on and which are off. This is easily done with the use of an array (array? see chapter four). For example, to create a solid block character as shown in the image above, our array would look like:

byte a[8] = {
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111}

Notice how we have eight elements, each representing a row (from top to bottom), and each element has five bits – representing the pixel column for each row. The next step is to reference the custom character’s array to a reference number (0~7) using the following function within void setup():

lcd.createChar(0,a);

Now when you want to display the custom character, use the following function:

lcd.write(0);

where 0 is the memory position of the character to display.

To help make things easier, there is a small website that does the array element creation for you.

Example 24.2

Now let’s display a couple of custom characters to get a feel for how they work. In the following sketch there are three defined characters:

/* Example 24.2 - LCD custom character demonstration
 http://tronixstuff.com/tutorials > Chapter 24 CC by-sa-nc
 liquidCrystal library originally added 18 Apr 2008 by David A. Mellis
 library modified 5 Jul 2009  by Limor Fried (http://www.ladyada.net, http://www.adafruit.com)
*/
#include <LiquidCrystal.h> // we need this library for the LCD commands
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(4,5,6,7,8,9); // define our LCD and which pins to use
int d = 3000; // used for display delay
byte a[8] = {  B00000,  B00000,  B00000,  B00100,  B00100,    B00000,  B00000,  B00000};
byte b[8] = {  B00000,  B00000,  B10001,  B10001,  B10001,  B10001,  B00000,  B00000};
byte c[8] = {  B11111,  B10001,  B10001,  B10001,  B10001,  B10001,  B10001,  B11111};
void setup()
{
lcd.createChar(0,a); // define our characters into the sketch as variables
lcd.createChar(1,b);
lcd.createChar(2,c);
lcd.begin(16, 2); // need to specify how many columns and rows are in the LCD unit
lcd.clear();      // this clears the LCD. You can use this at any time
}
void loop()
{
for (int z=0; z<16; z++)
{
lcd.setCursor(z,0);
lcd.write(0);  // write the first character
delay(250);
lcd.setCursor(z,0);
lcd.write(1);  // write the second character
delay(250);
lcd.setCursor(z,0);
lcd.write(2);  // write the third character
delay(250);
lcd.setCursor(z,0);
lcd.write(1); // write the second character
delay(250);
lcd.setCursor(z,0);
lcd.write(0);  // write the first character
delay(250);
}
delay(1000);
lcd.clear();
}

And here is a quick video of the example 24.2 sketch in action:

So there you have it – a summary of the standard parallel method of connecting an LCD to your Arduino. Now let’s look at the next type:

Three-wire LCD interface

If you cannot spare many digital output pins on your Arduino, only need basic text display and don’t want to pay for a serial or I2C LCD, this could be an option for you. A 4094 shift register IC allows use of the example HD44780 LCD with only three digital output pins from your Arduino. The hardware is connected as such:


And in real life:

From a software perspective, we need to use the LCD3Wire library, which you can download from here. To install the library, copy the folder within the .zip file to your system’s \Arduino-2x\hardware\libraries folder and restart the Arduino IDE. Then, in the start of your sketch, add the following line:

#include <LCD3Wire.h>

Next, you need to create a variable for our LCD module, and tell the sketch which of the 4094′s pins are connected to which digital output pins as well as define how many physical lines are in the LCD module. This is done with the following function:

LCD3Wire lcd = LCD3Wire(2, 11, 12, 10); The parameters in the brackets are the number of lines in the LCD and the digital output pins connected to 4094 pin numbers 2, 1 and 3.

Finally, in your void setup(), add the line:

lcd.init();

The number of available LCD functions in the LCD3wire library are few – that is the current trade-off with using this method of LCD connection … you lose LCD functions but gain Arduino output pins. In the following example, we will demonstrate all of the available functions within the LCD3Wire library.

Example 24.3

/*
Example 24.3 - 3-wire LCD demonstration
http://tronixstuff.com/tutorials > chapter 24
Contains copyleft code from http://www.arduino.cc/playground/Code/LCD3wires
and LCD3Wire library  */
#include  // we need this library for the LCD commands
LCD3Wire lcd = LCD3Wire(2, 11, 12, 10); //create object to control an LCD.
void setup()  {
lcd.init();
}
void loop()
{
lcd.printIn("LCD3Wire library"); // displays text on the LCD
lcd.cursorTo(2,0);  // rows are 1... and columns are 0...
lcd.printIn("-tronixstuff.com");
delay(1000);
lcd.leftScroll(16,500);
// scrolls the entire display 16 chars to left, 100ms per character-shift
delay(1000);
lcd.clear(); // clears the display
}  

And as always, let’s see it in action. The LCD update speed is somewhat slower than using the parallel interface, this is due to the extra handling of the data by the 4094 IC:

Now for some real fun with:

Graphic LCD modules

(Un)fortunately there are many graphic LCD modules on the market. To keep things relatively simple, we will examine two – one with a parallel data interface and one with a serial data interface.

Parallel interface

Our example in this case is a 128 by 64 pixel unit with a KS0108B parallel interface:

For the more technically-minded here is the data sheet. From a hardware perspective there are twenty interface pins, and we’re going to use all of them. For breadboard use, solder in a row of header pins to save your sanity!

This particular unit runs from 5V and also has a 5V backlight. Yours may vary, so check and reduce backlight voltage if different.

You will again need a 10k ohm potentiometer to adjust the display contrast. Looking at the image above, the pin numbering runs from left to right. For our examples, please connect the LCD pins to the following Arduino Uno/Duemilanove sockets:

  1. 5V
  2. GND
  3. centre pin of 10k ohm potentiometer
  4. D8
  5. D9
  6. D10
  7. D11
  8. D4
  9. D5
  10. D6
  11. D7
  12. A0
  13. A1
  14. RST
  15. A2
  16. A3
  17. A4
  18. outer leg of potentiometer; connect other leg to GND
  19. 5V
  20. GND

A quick measurement of current shows my TwentyTen board and LCD uses 20mA with the backlight off and 160mA with it on. The display is certainly readable with the backlight off, but it looks a lot better with it on.

From a software perspective we have another library to install. By now you should be able to install a library, so download this KS0108 library and install it as usual. Once again, there are several functions that need to be called in order to activate our LCD. The first of these being:

GLCD.Init(NON_INVERTED);

which is placed within void setup(); The parameter sets the default pixel status. That is, with NON_INVERTED, the default display is as you would expect, pixels off unless activated; whereas INVERTED causes all pixels to be on by default, and turned off when activated. Unlike the character LCDs we don’t have to create an instance of the LCD in software, nor tell the sketch which pins to use – this is already done automatically. Also please remember that whenever coordinates are involved with the display, the X-axis is 0~127 and the Y-axis is 0~63.

There are many functions available to use with the KS0108 library, so let’s try a few of them out in this first example. Once again, we will leave the explanation in the sketch, or refer to the library’s page in the Arduino website. My creative levels are not that high, so the goal is to show you how to use the functions, then you can be creative on your own time :)

Example 24.4

This example demonstrate a simpler variety of graphic display functions.

/*  Example 24.4 - KS0108 Graphic LCD demonstration
http://tronixstuff.com/tutorials > chapter 24  CC by-sa-nc
*/
#include <ks0108.h>  // library header
int xc, yc = 0;
int d=1000; // for delay use
void setup()
{
GLCD.Init(NON_INVERTED);   // initialise the library with pixel default as off
GLCD.ClearScreen();        // clear the LCD
randomSeed(analogRead(5));
}
void loop()
{
GLCD.DrawRect(0, 0, 127, 63, BLACK);
// draw an open rectangle that spans the extremties of the LCD
GLCD.DrawRect(10, 10, 117, 53, BLACK);
GLCD.DrawRect(20, 20, 107, 43, BLACK);
GLCD.DrawRect(30, 30, 97, 33, BLACK);
delay(d);
GLCD.ClearScreen();  // clear the LCD
for (int a=1; a<20; a++)
{
GLCD.DrawCircle(63,31,a,BLACK);
// draws a circle with centre at 61,31; radius of a, with black pixels
delay(d-800);
GLCD.DrawCircle(63,31,a,WHITE); // draws the same circle with the pixels off
}
delay(d);
GLCD.ClearScreen();  // clear the LCD
for (int a=0; a<128; a++)
{
GLCD.DrawVertLine(a, 0, 63, BLACK);
// draws a vertical line from xy position a, 0 of length 63
delay(d-950);
}
delay(d-800);
for (int a=0; a<128; a++)
{
GLCD.DrawVertLine(a, 0, 63, WHITE);
delay(d-950);
}
GLCD.ClearScreen();  // clear the LCD
for (int a=0; a<64; a++)
{
GLCD.DrawHoriLine(0, a, 127, BLACK);
// draws a horizontal line from xy position 0, a of length 127
delay(d-950);
}
for (int a=0; a<64; a++)
{
GLCD.DrawHoriLine(0, a, 127, WHITE);
delay(d-950);
}
GLCD.ClearScreen();  // clear the LCD
GLCD.DrawRoundRect(30, 30, 20,20, 5,BLACK);
// draw a rectangle with rounded edges: x, y, width, height, radius of rounded edge, colour
GLCD.DrawRoundRect(60, 30, 20,20, 5,BLACK);
delay(d);
GLCD.ClearScreen();  // clear the LCD
delay(d);
GLCD.FillRect(30, 30, 30, 10, BLACK);
// draws a filled rectangle: x, y, width, height, colour
delay(d);
GLCD.ClearScreen();  // clear the LCD
for (int a=0; a<1000; a++)
{
xc=random(0,127);
yc=random(0, 63);
GLCD.SetDot(xc, yc, BLACK);
// turn on a pixel at xc, yc);
delay(2);
}
GLCD.ClearScreen();
// clear the LCD
}

Now let’s see all of that in action:

You can also send normal characters to your KS0108 LCD. Doing so allows you to display much more information in a smaller physical size than using a character  LCD. Furthermore you can mix graphical functions with character text functions – with some careful display planning you can create quite professional installations. With a standard 5×7 pixel font, you can have eight rows of twenty-one characters each. Doing so is quite easy, we need to use another two #include statements which are detailed in the following example. You don’t need to install any more library files to use this example. Once again, function descriptions are in the sketch.

Example 24.5

/*  Example 24.5 - KS0108 Graphic LCD demonstration #2
http://tronixstuff.com/tutorials > chapter 24  CC by-sa-nc
*/
#include <ks0108.h> //  library for LCD
#include "SystemFont5x7.h"
// we need this for character display, included with ks0108.h download
void setup()
{
GLCD.Init(NON_INVERTED);   // load the GLCD library
GLCD.ClearScreen();
GLCD.SelectFont(System5x7);
// choose font to use (note this needs to match the #include above
}
int j = 24;
void loop()
{
GLCD.ClearScreen();
GLCD.DrawRect(0, 0, 127, 63,BLACK);
GLCD.CursorTo(1, 1);
// set cursor to top left of LCD (uses character coordinates
// not pixel coordinates
GLCD.Puts("Hello, world."); // sends strings to LCD. Does not wrap to next line!
GLCD.CursorTo(1, 2);
GLCD.Puts("I hope you are ");
GLCD.CursorTo(1, 3);
GLCD.Puts("enjoying this");
GLCD.CursorTo(1, 4);
GLCD.Puts("series of lessons. ");
GLCD.CursorTo(1, 5);
GLCD.Puts("This is from ");
GLCD.CursorTo(1, 6);
GLCD.Puts("chapter ");
GLCD.PrintNumber(j); // sends an integer to the LCD. Does not wrap to next line either
GLCD.Puts(".");
delay(3000);
GLCD.ClearScreen();
for (int xx=0; xx<21; xx++)
{
for (int yy=0; yy<8; yy++)
{
GLCD.CursorTo(xx, yy);  // position the text cursor
GLCD.Puts("#");
delay(50);
}
}
delay(1000);
GLCD.ClearScreen();
}

Again,  let’s see all of that in action:

If you’re looking for a very simple way of using character LCD modules, check this out.

Have fun and keep checking into tronixstuff.com. Why not follow things on twitterGoogle+, subscribe  for email updates or RSS using the links on the right-hand column, or join our Google Group – dedicated to the projects and related items on this website. Sign up – it’s free, helpful to each other –  and we can all learn something.

January 8, 2011 Posted by | arduino, education, LCD, LCD-00710, learning electronics, microcontrollers, tutorial | , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , | 53 Comments

   

Follow

Get every new post delivered to your Inbox.

Join 3,843 other followers

%d bloggers like this: