Thanks to Matrix Orbital for supplying sample LCD Displays for me to experiment with. I'll go through how to talk to the LK202-25 20 x 2, LCD 2041 20 x 4, and GLK12232 Graphics LCD displays.
This section will grow as time goes by I'll be approaching these devices in two steps...
1. Testing the Displays work via use of my Mac and/or Virtual PC.
2. Interfacing them to a Stamp.
The Test Program I have written has been tested OK with all the above mentioned modules under OS9 and Win98. The program was written in Realbasic and compiled for Mac OS and Win98.
You can down load the test program here for Mac OS. (screen shot here). A Windows version is here (note it is not 100% as functional as the Mac version yet).
If you are on a Macintosh, you will need to wire up a RS422 to RS232 cable, follow this table:
|
|
|
| HSKout, 1 | DTR, 4 |
| HSKin, 2 | RTS, 7 |
| RX-, 5 | RX, 2 |
| TX-, 3 | TX, 3 |
| GND, 4 | GND, 5 |
NOTE: Ground RX+, leave TX+ floating.
About the code:
I basically followed the matrix Orbital manual - translating their words in to RealBasic code.
It's really very simple. For example, to tell the display you want to send a command to it, you need to send a code to tell the display you are sending a command, then you need to send that command, followed by any parameters.
As an example, to clear the display, send 254 dec (FE Hex), then 88 dec. No parameters are required.
serial1.write
chr(254)
serial1.write chr(88)
To draw a line from 0,0 to 10,10:
'set the draw
colour (black)
serial1.write
chr(254) 'I
want to command the display
serial1.write chr(99) 'this
is the command
serial1.write chr(255) 'this is the parameter of that command
'Draw a line
serial1.write chr(254) 'I want to command the display
serial1.write chr(108) 'this is the command
serial1.write chr(val(editfield5.text)) 'this is a parameter of that command
(x1)
serial1.write chr(val(editfield6.text)) 'this is a parameter of that command
(y1)
serial1.write chr(val(editfield7.text))'this is a parameter of that command
(x2)
serial1.write chr(val(editfield8.text))'this is a parameter of that command
(y2)
To send a text message to the display, just don't precede it by a command code. So, to Display "Hello":
serial1.write "Hello"
LCD 2041 specifics.
The above will work OK as a quick test to check the LCD 2041 is working. However, this display does behave a little differently than the 20 x 2 display. If you have ever played with an Hitachi HD44780 based parallel interface 20 x 4 display, you will know what to expect (as this display seems to be HD44780 compatible with a serial converter on board).
Most notable point, is that the display displays the next line of data on line 3 after line 1. ie - instead of going 1,2,3,4, it goes 1,3,2,4.
As with my previous experiments, the aim of this experiment is to display "Lennard Electronics www.lennard.net.nz" on the display.
Suffice to say that it's very easy with a Matrix Orbital Display! I think I'll be retiring my HD44780 based parallel display for good. I used the LK202-25 20 x 2 for this experiment.
There are two ways to talk to the LCDs. Serial (RS232 or TTL levels), or I2C.
Serial Control:
The displays are easily modified to use TTL or RS232 levels. However, as the Stamp has a built in RS232 level serial port (Pins 1 to 4), it was just as easy to use the default RS232 levels. Note that you will have to set your Display's RS232 port to 9600 baud. Easy to do, just consult the Matrix Orbital manual. Later on, I tried the displays on other ports on the Stamp. But, again, rather than modifying them to TTL level, I used them in standard RS232 mode. I did this by quickly whipping up a MAX232 based circuit. I did this rather than modify the displays, as I knew I'd want to use them in standard RS232 levels later on for other prototyping. Once you have proven your code works, you can then modify the display that will be used in your final product, and remove the MAX232 from your Stamp circuit.
To use with your Stamp, on it's built in Serial Port, connect Sout to Rx on the display, and Sin to TX on the display. Make sure both Display and Stamp share the same ground.
The Code:
You'll notice the use of "debug" throughout the example code. Debug "blah de blah" is another way of writing "Serout 16,84,("blah de blah")". In other words, debug defaults to Sout on the Stamp, at 9600 baud.
|
'{$STAMP BS2} 'Write to a Matrix Orbital LCD via RS232 on a BS2 Main: |
I2C Control:
Remember, that I'm using a BS2, which does not have I2C support built in - so you have to "bit bash" a solution. You can use the I2C subroutines for other I2C based projects as well. If you are using a BS2p, then your I2C based code will basically be as simple as the serial example above.
The Code:
|
'{$STAMP BS2} 'I2C test program.
27/4/2003 Ben Lennard 'STAMP EEPROM
DATA 'CONSTANTS 'VARIABLES DIRS=%0011111111111111 Main: 'Stop Procedure.
Set stop condition on bus: SDA Lo-Hi while SCL Hi 'Write Address/Data
Procedure |
Talking to the GLK12232 with a stamp:
You may have noticed if you looked at the screen shot of the Macintosh Application, that one of the options is to draw a Sine wave on the screen.
Drawing lines, and rectangles is very easy, but how do you draw a sine wave using a Stamp??
Of course, the Stamp doesn't do floating point Maths, so how can it calculate the points to plot on the screen I hear you say. The Stamp uses "Binary Radians" (or BRAD). BRADs range from 0 to 255 (which equates to 0 to 359 degrees, or 2pi radians). One BRAD is approximately 1.4 degrees. So calculating the points to plot is relatively easy.
Here is the code from the Mac application to plot a sine wave, we'll basically modify it to PBASIC for the Stamp.
|
dim i as integer if sinescale(0).value
= true then 'show
quarter cycle serial1.write
chr(254) serial1.write
chr(254) for i = 0 to
90 |
The way it works is to simply plot an x and y coordinate , where x is 0 to 90, and y = sin x.
The code that determines y is:
y = 16 - (15 * (sin((scale*i*3.14159)/180)))
We want to center the sine wave in the middle of the display, and fill the display from top to bottom. So, 16 is the zero cross over point, and 15 * the rest, expands the wave size to fit the display.
To convert the degrees to radians (which is what computers work in), we multiply the x value by pi and divide it by 180. The scale variable determines whether a quarter, half, one, or two cycle wave is drawn.
Now as the Basic Stamp works in BRADs, all that is required is to change the main calculation line slightly to:
y =15 - ((SIN (4*i*128/180))/8) '4 IS THE SCALER - to display one cycle
Then, just change the syntax a little as we are now using PBasic instead of RealBasic.
Here's the code: Serial version first, then I2C. There seems to be very little difference in drawing speed, from a visual perspective.
Serial version:
|
'{$STAMP BS2} 'Draw a sine wave on a Matrix Orbital LCD via RS232 on a BS2 i VAR BYTE
'just a
for loop variable Main: |
I2C version:
|
'{$STAMP BS2} 'I2C test program.
15/6/2002 Ben Lennard 'CONSTANTS 'VARIABLES Main: PAUSE 1000 'Give the MO Display time to initalise it's self - 1 second GOSUB StartI2C I2C_data = %01010000 'MO Display address for WRITING to GOSUB Write_data I2C_data = 254 '254 = command GOSUB Write_data I2C_data = 88 '88 = clear display GOSUB Write_data I2C_data = 254 '254 = command GOSUB Write_data I2C_data = 99 'Drawing colour GOSUB Write_data I2C_data = 255 'Black GOSUB Write_data for i = 0 to 90 'this for loop plots the sine wave I2C_data =254 GOSUB Write_data I2C_data =112 'draw a pixel at x,y GOSUB Write_data I2C_data = 15 + i 'x coordinate - offset by 15 to draw the wave in the middle GOSUB Write_data I2C_data = 15 -((sin (4*i*128/180)/8)) 'Convert to brads, do SIN.) 'y coordinate GOSUB Write_data next GOSUB StopI2C end '*********************** I2C SUBROUTINES ********************************** 'Start Procedure. Set start condition on bus: SDA Hi-Lo while SCL Hi StartI2C: HIGH SDA HIGH SCL LOW SDA LOW SCL RETURN 'Stop Procedure.
Set stop condition on bus: SDA Lo-Hi while SCL Hi 'Write Address/Data
Procedure |
The Circuit:
You need 4 resistors. 2 x 220ohm, 2 x 10k. Connect the 220ohm resistors to ports 0 and 1, and connect the Display's i2C ports to these resistors. Next connect the 10k resistors from the I2C ports of the display to Vdd. For further information on I2C, please visit my I2C page.
A simple ADC with Graphic display: (in progress...)
This idea will lead on to a simple Oscillioscope project eventually, but the basic idea of this example is to show how simple it is to display graphical data on a Matrix Orbital Display using a Stamp.
The idea of this project, is to simply display the discharge curve of a capacitor over time. I'll basically be grabbing code form another example on this web site, and modifying it so that the data is displayed on a Matrix Orbital display. Part of that example is repeated below. The original circuit saved the data to an EEPROM, and then downloaded that data to a Mac or PC, and using Excel, that data was then displayed as a graph.
This is a chart created in Excel. A .csv file is imported to Excel with the data as read from the Datalogger's memory, then in Excel, a simple calculation is performed to get the actual voltage that the data represents. It is exactly as expected, showing the discharge of a capacitor.

We can display the above graph easily on a Matrix Orbital display. We'll be using a 122x32 display here.
Simply treat the display as a 122 byte device. That is: x = 0 to 121, and y = (the value of the byte divided by 8) at location x (since y is limited to 0 to 32 pixels on the display, ie: 256/8).
In other words, take a sample (x), the value to display is the pixel location (x = sample number, y = value of sample/8).
The Circuit:
This is basically the ADC circuit from my ADC page.

The Code:
|
'{$STAMP BS2sx} 'Plot data from an ADC on a Matrix Orbital Graphics LCD via RS232 on a BS2 i VAR BYTE
'just a
for loop variable |
Here's a shot of the display showing the capacitor discharging.

Next step: Let's get a little more advanced now. The same circuit can be used for other data as well. For example, you could display the temperature over time using an LM35. All you'd need to do, is change the sample rate by changing the pause statement, and maybe scale the y axis as well.
Or, try this: Modify the circuit slightly by adding an LM35, as well as my RTC circuit, as shown below.
<circuit here>
Now, download this code to the BS2. This code displays the temperature and plots it on a graph at the same time. It also displays the date and time - using the RTC from my I2C page - allowing you to adjust the date and time, and uses the RTC to get a sample a second. Take care to check the pin allocations for the ADC and RTC, as well as the RS232 level shifter.
The display will look something like this:
On fonts: Now, bare in mind, that I have used three fonts with this display (only two used with this code). The 3rd font is a 3x5 font. It is easy to create fonts and download them to your MO display. Consult the manual on how to do this, or send me an email via our contacts page.
The next step after this? Try to store the data in memory, along with a time stamp, and have the option to download that data to a PC or Mac, or display results stored in memory on the screen graphically, showing the time that each sample was made. Also, add an option to change the sample rate, using the RTC as the time base for your samples.
This is a very simple example of using I2C to talk to a Matrix Orbital Display, using a C8051F005. The code is written in Dunefield Micro C.
Connect the SDA and SCL lines up as you usually would for I2C (you know how to do that by now, right? :-) ). Only differrence, is the 220R resistors are not needed.
The main code:
|
// 11/4/04
First test run of I2C bus. // Memory model = 3, use medium.lib, SDA = P0.0, SCL = P0.1 #include "config.h"
//This
library configures the I/O ports, interrupts, timers, etc. main() while (!tstbit(SMB0CN,3))
{} //interrupt
occured? while (!tstbit(SMB0CN,3))
{} while (!tstbit(SMB0CN,3))
{} while (!tstbit(SMB0CN,3))
{} while (!tstbit(SMB0CN,3))
{} while (!tstbit(SMB0CN,3))
{} while (!tstbit(SMB0CN,3))
{} while (!tstbit(SMB0CN,3))
{} while (tstbit(SMB0CN,7))
{} //wait
until the bus is free } |
An interrupt is raised everytime an event occurs on the bus. Eg: A Start condition, an acknowledge. The only time to put data on the bus, is when an Interrupt is raised.