Siemens A60 LCD
Here's a bit of info and an application for the display from
a Siemens A60 (also shared with A65, M55, C60, MC60 and S55
models). This is a nice self-contained module with built in
backlight, and connection is made via a row of nice, large
easy-to-solder-to
gold pads, like the Nokia
3510i. The image resolution is 101 columns by 80 lines.
Having the backlight built in eliminates any problems with
uneven illumination which will inevitably happen when trying to
backlight transflective LCDs like the 3510's, unless you're very
patient to get the backlight diffusion just right. This display
uses two white LEDs which are connected via the pads.
As with the 3510i display, there is a driver chip mounted on
the back of the LCD. This includes the LCD bias supply, the
scanning logic and enough SRAM for a frame buffer. All that's
required to make it work is a power supply and a source of data.
Image data is sent to the display via a special 4-wire synchronous
serial interface.
This display works on a 2.9V supply, which by happy
coincidence is almost exactly the same as the forward voltage of
each of the backlight LEDs. This means that one of the LEDs can
be used as a simple shunt regulator if you don't happen to have
a 2.9V rail.
The pinout is (from the pad nearest the edge of the plastic
frame): -
- Chip Select (pull low to enable)
- Reset (pull low to reset)
- Register Select (low = command, hi = data)
- Serial Clock (serial data is latched on the rising edge)
- Serial Data
- Logic Supply (+2.9V as measured in the donor A60 cell phone)
- Logic Ground
- Backlight Anode 1
- Backlight Cathodes
- Backlight Anode 2
Driving from a PC under Linux
The display is low powered enough to mean that it can be bus
powered from a PC parallel port. The code listed below will
enable raw image data to be streamed to the display via the
parallel port. This might be useful to make a handy self-contained
status indicator for a Linux box - e.g. a headless MythTV backend.
Connect the LCD data lines to the parallel port via resistors
to limit the current in the IC's clamp diodes. I used 12K
resistors, but this might be a bit too high for reliable
operation. 4K7 might be a better choice, depending on the specific
parallel port.
Connect the anodes of the LCD backlight LEDs to the parallel
port via lower value resistors. I used 620ohms for a reasonable
brightness. You may need to adjust this value.
The +2.9V logic supply is connected to one of the LED anodes.
Logic ground and the LED common cathode are connected together
and to the PC ground in the parallel port.
To use it, compile then execute, streaming in your raw image
data. The program expects exactly 24240 bytes of data: triplets of
unsigned bytes representing red, green and blue samples (in that
order), 101 triplets per line, 80 lines in the image. This stream
can be trivially effected under Linux with the ImageMagick tools,
e.g. something like this: -
convert MyImage.JPG -resize 101x80\> -size 101x80 xc:black +swap \
-gravity center -composite rgb:'-' | stream -size 101x80 rgb:'-' \
-storage-type char -map rgb '-' | ./a60-lcd
Two arguments are recognized: ./a60-lcd 0 (number zero)
switches off the LCD, rendering it safe to disconnect and
./a60-lcd s skips the reset/initialisation sequence,
possibly useful for slideshows or live status updates
Just copy the following into a text editor and save as
a60-lcd.c then build by typing make a60-lcd from a
terminal:
#include <stdio.h>
#include <stdlib.h>
#include <linux/ioctl.h>
#include <linux/parport.h>
#include <linux/ppdev.h>
#include <fcntl.h>
#include <time.h>
int fd;
int data = 0x7f;
/*
* Linux tester for Siemens A60 LCD (A65, M55, C60, MC60, S55, etc)
*
* Connect signals to parallel port via ~10K resistors thus: -
*
* Parallel Port A60-LCD
* 2 D0 <--10K-> 1 CS
* 3 D1 <--10K-> 2 Reset
* 4 D2 <--10K-> 3 RS
* 5 D3 <--10K-> 4 SCL
* 6 D4 <--10K-> 5 SDA
* 7 D5 <-620R-> 6 +2.9V *and* 8 A1
* 8 D6 <-620R-> 10 A2
* 25 GND <------> 7 GND *and* 9 K
*
* LCD pad 1 is nearest the edge of the frame, pad 10 is nearest the
* middle of the assembly.
*
* By coincidence, Vf of the backlight LEDs is approx 2.9V, thus
* one can be used as a simple shunt regulator for the LCD supply.
*
* Remember, the series resistor limits the current in the shunt.
* The series resistors in the signal lines limit the current in
* the LCD's clamp diodes. Adjust values as necessary to suit the
* specific parallel port.
*
* Make sure you have your distro's ppdev package installed.
*/
void Send (X)
{
int mask;
/* clock a byte out serially to the LCD */
for (mask = 0x80; mask; mask >>= 1)
//for (mask = 0x01; mask & 0x100; mask <<= 1)
{
if (mask & X)
data |= 0x10;
else
data &= ~0x10;
data &= ~0x08;
ioctl (fd, PPWDATA, &data);
//ioctl (fd, PPWDATA, &data);
data |= 0x08;
ioctl (fd, PPWDATA, &data);
//ioctl (fd, PPWDATA, &data);
}
}
void Initialise (void)
{
int i, flag;
int Sequence [] =
{
0xf4, 0x90, 0xb3, 0xa0, 0xd0, -1,
0xf0, 0xe2, 0xd4, 0x70, 0x66, 0xb2, 0xba, 0xa1, 0xa3, 0xab,
0x94, 0x95, 0x95, 0x95, -1,
0xf5, 0x90, -1,
0xf1, 0x00, 0x10, 0x22, 0x30, 0x45, 0x50, 0x68, 0x70, 0x8a,
0x90, 0xac, 0xb0, 0xce, 0xd0, -1,
0xf2, 0x0f, 0x10, 0x20, 0x30, 0x43, 0x50, 0x66, 0x70, 0x89,
0x90, 0xab, 0xb0, 0xcd, 0xd0, -1,
0xf3, 0x0e, 0x10, 0x2f, 0x30, 0x40, 0x50, 0x64, 0x70, 0x87,
0x90, 0xaa, 0xb0, 0xcb, 0xd0, -1,
0xf4, 0x0d, 0x10, 0x2e, 0x30, 0x4f, 0x50, -1,
0xf5, 0x91, -1,
0xf1, 0x01, 0x11, 0x22, 0x31, 0x43, 0x51, 0x64, 0x71, 0x86,
0x91, 0xa8, 0xb1, 0xcb, 0xd1, -1,
0xf2, 0x0f, 0x11, 0x21, 0x31, 0x42, 0x51, 0x63, 0x71, 0x85,
0x91, 0xa6, 0xb1, 0xc8, 0xd1, -1,
0xf3, 0x0b, 0x11, 0x2f, 0x31, 0x41, 0x51, 0x62, 0x71, 0x83,
0x91, 0xa4, 0xb1, 0xc6, 0xd1, -1,
0xf4, 0x08, 0x11, 0x2b, 0x31, 0x4f, 0x51, -1,
0x80, 0x94, -1,
0xf5, 0xa2, -1,
0xf4, 0x60, -1,
0xf0, 0x40, 0x50, 0xc0, -1,
0xf4, 0x70, -1, -1,
0xf0, 0x81, -1,
0xf4, 0xb3, 0xa0, -1,
0xf0, 0x00 | 0x6, 0x10, 0x20, 0x30, -1,
0xf5, 0x00 | 0xf, 0x10 | 0xc, 0x20 | 0xf, 0x30 | 0x4, -1,
-1, -1
};
/* power off */
data = 0;
ioctl (fd, PPWDATA, &data);
usleep (500000);
/* power on, reset */
data = 0x7d;
ioctl (fd, PPWDATA, &data);
usleep (10000);
/* reset off */
data |= 0x02;
ioctl (fd, PPWDATA, &data);
usleep (10000);
/* send commands */
i = 0;
data &= ~0x01;
data |= 0x04;
ioctl (fd, PPWDATA, &data);
flag = 1;
do
{
Send (Sequence [i++]);
if (Sequence [i] == -1)
{
data |= 0x01;
ioctl (fd, PPWDATA, &data);
usleep (1);
data &= ~0x01;
ioctl (fd, PPWDATA, &data);
i++;
}
if (Sequence [i] == -1)
{
usleep (20000);
i++;
}
} while (Sequence [i] != -1);
/* select data register */
data &= ~0x04;
ioctl (fd, PPWDATA, &data);
/* good to go! */
}
void PowerOff (void)
{
data = 0;
ioctl (fd, PPWDATA, &data);
}
void SetWindow (int x, int y, int sx, int sy)
{
/* bounds checking */
sx += x;
if (x < 0) x = 0;
if (x > 100) x = 100;
if (sx <= x) sx = x + 1;
if (sx > 101) sx = 101;
sy += y;
if (y < 0) y = 0;
if (y > 79) y = 79;
if (sy <= y) sy = y + 1;
if (sy > 80) sy = 80;
/* send command */
x = 2 * (3 + x);
sx = 2 * (3 + sx) + 1;
data |= 0x04;
data &= ~0x01;
ioctl (fd, PPWDATA, &data);
Send (0xf0);
Send (0x00 | (x & 0x0f));
Send (0x10 | (x >> 4));
Send (0x20 | (y & 0x0f));
Send (0x30 | (y >> 4));
Send (0xf5);
Send (0x00 | (sx & 0x0f));
Send (0x10 | (sx >> 4));
Send (0x20 | (sy & 0x0f));
Send (0x30 | (sy >> 4));
data &= ~0x04;
data |= 0x01;
ioctl (fd, PPWDATA, &data);
}
int main (int argc, char *argv[])
{
int i, j, k, demo;
/* open the parallel port driver */
fd = open ("/dev/parport0", O_RDWR);
if (fd == -1)
{
perror ("open");
exit (1);
}
if (ioctl (fd, PPCLAIM))
{
perror ("PPCLAIM");
close (fd);
exit (1);
}
/* mode = IEEE1284_MODE_COMPAT;
if (ioctl (fd, PPNEGOT, &mode))
{
perror ("PPNEGOT");
close (fd);
exit (1);
} */
if (argc > 1 && argv[1][0] == '0')
{
PowerOff ();
close (fd);
exit (0);
}
if (argc < 2 || argv[1][0] != 's')
{
Initialise ();
}
demo = (argc > 1 && argv[1][0] == 'd');
k = 0;
do
{
for (i = 0; i < 80; i++)
{
SetWindow (0, i, 101, 1);
data &= ~0x01;
ioctl (fd, PPWDATA, &data);
for (j = 0; j < 101; j++)
{
if (demo)
{
int x, c;
x = ((i + k) ^ (j - k)) & 0xFF;
c = ((x >> 4) % 7) + 1;
x &= 0x0F;
Send ((c & 0x01)? x: 0);
Send (((c & 0x02)? x << 4: 0) |
((c & 0x04)? x: 0));
}
else
{
int r, g, b;
r = getchar ();
g = getchar ();
b = getchar ();
Send ((r >> 4) & 0x0F);
Send (((b >> 4) & 0x0F) |
(g & 0xF0));
}
}
data |= 0x01;
ioctl (fd, PPWDATA, &data);
}
k++;
usleep (100000);
} while (demo);
close (fd);
return 0;
}