Setting Pixels for ST7920 based 128x64 LCD

Started by Kevin Townsend November 24, 2008
I've been working on a driver for a ST7920 LCD I received today,
since most of the drivers I found on the web tend to be for
KS0107/0108 LCDs. I'd like to post the driver here when I'm finished
so that other people don't have to pull their own hair out, but I was
wondering if anyone would be able to offer some help on setting
individual pixels.

I've got everything working with the basic command set, and can
display text, initialise, etc. I can't figure out how to get the
extended command set working, though, specifically the ability to set
pixels individually.

I've noticed that you need to change the GDRam address to a position
in an 8x4 grid (see: st7920_setGDRamAddress), and then you can set
pixels in a 16x16 block, but how to set those 16x16 pixels is where
I'm stuck.

Does anyone here have any experience with this LCD controller that
could perhaps fill in the "st7920_setPixel(U8 x, U8 y)" method
appropriately?

http://www.sitronix.com.tw/sitronix/product.nsf/Doc/ST7920

I'll post the code in the another message

Kevin.
ST7920.H
=============================================================
#ifndef ST7920_H
#define ST7920_H

#include "sysdefs.h"

/* Common Aliases */
#ifndef PIN
#define PIN(n) (U32)(1 << (n))
#endif

/* Pin Registers */
#define ST7920_BUSPIN0 16 // Parallel Bus
(P0.16..23)
#define ST7920_DATA ((U32)0xff< #define ST7920_BUSALL 0x00FF0000 // Parallel Bus Mask
(P0.16..23)
#define ST7920_PSB PIN(28) // Parallel Select (0 Serial Mode, 1 = 8/4-Bit Parallel Bus Mode) (P0.28)
#define ST7920_RW PIN(29) // RW (P0.29)
#define ST7920_DI PIN(6) // RS (P0.6)
#define ST7920_E PIN(30) // Enable (P0.30)
#define ST7920_RST PIN(25) // Reset (P0.25)

/* General Definitions */
#define ST7920_BUSIN 0 // Parallel Bus as Input
#define ST7920_BUSOUT 1 // Parallel Bus as Output
#define ST7920_BUSY 0x80 // Busy Flag

/* Basic ST7920 Commands */
#define ST7920_CMD_CLEAR 0x01 // Clear Display
#define ST7920_CMD_HOME 0x02 // Move Cursor Home
#define ST7920_CMD_EM 0x04 // Entry Mode Base
#define ST7920_CMD_EM_INCRR 0x02 // Increment Cursor Right
#define ST7920_CMD_EM_INCRL 0x00 // Increment Cursor Left
#define ST7920_CMD_EM_SHFTR 0x03 // Shift Display Right
#define ST7920_CMD_EM_SHFTL 0x01 // Shift Display Left
#define ST7920_CMD_DC 0x08 // Display Control
#define ST7920_CMD_DC_DISPON 0x04 // Display On
#define ST7920_CMD_DC_CURON 0x02 // Cursor On
#define ST7920_CMD_DC_BLNKON 0x01 // Blink On
#define ST7920_CMD_FNC 0x20 // Function Set
#define ST7920_CMD_FNC_DL8 0x10 // 8-Bit Interface
#define ST7920_CMD_FNC_DL4 0x00 // 4-Bit Interface
#define ST7920_CMD_FNC_EXTINS 0x04 // Extended Instruction Set
#define ST7920_CMD_FNC_BASINS 0x00 // Basic Instruction Set
#define ST7920_CMD_CGRAM_ADDR 0x40 // Set CGRAM Address
#define ST7920_CMD_DDRAM_ADDR 0x80 // Set DDRAM Address

/* Extended ST7920 Commands */
#define ST7920_ECMD_GFXDISPON 0x36 // Ext. Display Control (8-
bit, Extended Instructions, GFX Display On)

// Method Prototypes
void st7920_init(void);
void st7920_clear(void);
void st7920_printf(U8 *text);
void st7920_setPixel(U8 x, U8 y);

#endif

ST7920.C
=============================================================
#include "lpc214x.h"
#include "st7920.h"
#include "delay.h"

void st7920_strobeEnable(void)
{
GPIO0_IOSET = ST7920_E;
DelayMS(15);
GPIO0_IOCLR = ST7920_E;
}

void st7920_setBusDir(U8 dir)
{
if (dir==ST7920_BUSOUT)
{
GPIO0_IODIR |= (ST7920_BUSALL);
}
else
{
GPIO0_IODIR &= ~(ST7920_BUSALL);
}
}

void st7920_waitWhileBusy()
{
U8 rdata;

st7920_setBusDir(ST7920_BUSIN);

// Read busy flag
GPIO0_IOCLR = ST7920_DI; /* Clear RS */
GPIO0_IOSET = ST7920_RW; /* Set Read Mode */

st7920_strobeEnable();

// Loop until no longer busy
while ((rdata & 0x7F) == ST7920_BUSY)
{
rdata = (unsigned char)(GPIO0_IOPIN >> ST7920_BUSPIN0);
}

st7920_setBusDir(ST7920_BUSOUT);
GPIO0_IOCLR = (U32)(ST7920_BUSALL); /* Set all pins low */
GPIO0_IOCLR = ST7920_DI;
GPIO0_IOCLR = ST7920_RW; /* Read mode */
}

void st7920_data(U8 data)
{
st7920_setBusDir(ST7920_BUSOUT);

GPIO0_IOSET = ST7920_DI;
GPIO0_IOCLR = ST7920_RW;

GPIO0_IOSET = ((U32)data< st7920_strobeEnable();

GPIO0_IOSET = ST7920_RW;
GPIO0_IOCLR = (U32)(ST7920_BUSALL);
GPIO0_IOCLR = ST7920_DI;

DelayMS(2);
}

void st7920_command(U8 command)
{
st7920_setBusDir(ST7920_BUSOUT);

st7920_waitWhileBusy();
GPIO0_IOCLR = ST7920_DI;
GPIO0_IOCLR = ST7920_RW;
GPIO0_IOSET = ((U32)command< st7920_strobeEnable();

GPIO0_IOSET = ST7920_RW;
GPIO0_IOCLR = (U32)(ST7920_BUSALL);
GPIO0_IOCLR = ST7920_DI;

DelayMS(2);
}

// 0x80 0x81 0x82 0x83 0x84 0x85 0x86 0x87
// 0x90 0x91 0x92 0x93 0x94 0x95 0x96 0x97
// 0x88 0x89 0x8a 0x8b 0x8c 0x8d 0x8e 0x8f
// 0x98 0x99 0x9a 0x9b 0x9c 0x9d 0x9e 0x9f
void st7920_setGDRamAddress(U8 addr)
{
st7920_command(addr);
}

/* Public Methods
********************************************************** */
void st7920_init(void)
{
// Set pin to default state
U32 mask = (U32)(ST7920_RST | ST7920_PSB | ST7920_RW | ST7920_DI |
ST7920_E);
GPIO0_IOCLR = mask; // Clear all pins
GPIO0_IODIR |= mask; // Set all pins for output
GPIO0_IOSET = ST7920_PSB; // Set PSB High (0 = Serial Mode,
1 = 8/4-Bit Parallel Bus Mode)

// Wait until the LCD is not busy
st7920_waitWhileBusy();

// Toggle Reset
GPIO0_IOSET = ST7920_RST;
DelayMS(21);
GPIO0_IOCLR = ST7920_RST;
DelayMS(20);
GPIO0_IOSET = ST7920_RST;
DelayMS(20);

// Instantiate intialisation commands (Assigned here for debug
purposes. Remove variables in a production environment.)
U8 functionSetBas = (ST7920_CMD_FNC | ST7920_CMD_FNC_DL8 |
ST7920_CMD_FNC_BASINS); // (8-bit, Basic Instruction Set)
U8 functionSetExt = (ST7920_CMD_FNC | ST7920_CMD_FNC_DL8 |
ST7920_CMD_FNC_EXTINS); // (8-bit, Extended Instruction Set)
U8 displayCmd = (ST7920_CMD_DC | ST7920_CMD_DC_DISPON |
ST7920_CMD_DC_BLNKON); // (Display On, Blink On, Cursor OFF)
U8 entryMode = (ST7920_CMD_EM |
ST7920_CMD_EM_INCRR); // (Increment cursor
right, no shift)

// Send initialisation command sequence
st7920_command(functionSetBas); // Basic Function Set
st7920_command(functionSetBas); // Repeat Function Set
st7920_command(displayCmd); // Display
st7920_command(ST7920_CMD_CLEAR); // Clear Display
st7920_command(entryMode); // Set Entry Mode
st7920_command(functionSetExt); // Extended Function Set
st7920_command(ST7920_ECMD_GFXDISPON); // Graphic Display On

st7920_setPixel(1,1);
}

void st7920_clear(void)
{
st7920_command(ST7920_CMD_CLEAR); // Clear Display
}

void st7920_printf(U8 *text)
{
while(*text != 0)
{
st7920_data(*text);
text++;
}
}

void st7920_setPixel(U8 x, U8 y)
{
st7920_setGDRamAddress(0x80);

// ToDo: How to set individual pixels?
}

l... napisa(a):
ST7920.H

[...]
// ToDo: How to set individual pixels?
}

Try:
void st7920_setPixel(U8 x, U8 y)
{
U16 val;

val = 0x8000 >> (x & 0xF);
st7920_command(ST7920_CMD_DDRAM_ADDR + y);
st7920_command(ST7920_CMD_DDRAM_ADDR + (x >> 4));
st7920_command(val >> 8);
st7920_command(val & 0xFF);
}
Albert

Albert:

Thanks for the reply. I tried the supplied code, but don't seem to have any luck with it.
Whenever I send 'val >> 8' the screen clears as if I was in basic (text-only) mode, even
though I'm in extended mode. I've come up with the following code to at least put move
to the correct 16x16 block by means of the GDRAM Address ... I'm just missing what
command I need to send to set, say, pixel '8, 3' in the current 16x16 block. The code can
be heavily optimised later ... for the moment I just want to get it working, and understand
how it works:

void st7920_drawPixel(U8 x, U8 y, U8 color)
{
U8 xBlock, yBlock;
U8 xPixel, yPixel;

// Set horizontal GDRam position (0-7)
if (x > 15) { xBlock = x / 16; }
else { xBlock = 0; }

// Set vertical GDRam position (0-3)
if (y > 15) { yBlock = y / 16; }
else { yBlock = 0; }

// Move to appropriate GDRam block
st7920_setGDRamAddress(xBlock, yBlock);

// Determine 16x16 block address of the requested pixel
if (x < 16) { xPixel = x; }
else { xPixel = x - (xBlock * 16); }
if (y < 16) { yPixel = y; }
else { yPixel = y - (yBlock * 16); }

// ToDo: How to set individual pixels using xPixel and yPixel 16x16 position?
}

Dnia 2008-11-25, wto o godzinie 13:43 +0000, Kevin Townsend pisze:
> Albert:
>
> Thanks for the reply. I tried the supplied code, but don't seem to
> have any luck with it.
> Whenever I send 'val >> 8' the screen clears as if I was in basic
Yes, my mistake.
Correct is:
...
st7920_data(val >> 8);
st7920_data(val & 0xFF);
...

See also:
http://www.crystalfontz.com/forum/showthread.php?tV05

Albert

Albert:

That's a lot closer to what I was hoping for, thanks. However, whenever I set one 'column'
of pixels, it seems to clear all the previous ones in the same 16x16 block. For example, I
would expect the code in 'main()' below to product a solid black block 128x8 pixels in size
along the top of the screen. Instead, I have 15 blank pixels, 1 black pixel, 15 blank, 1
black, repeated for each 16x16 block, 8 pixels high:

................................................ (etc.)
................................................ (etc.)
................................................ (etc.)
................................................ (etc.)
................................................ (etc.)
................................................ (etc.)
................................................ (etc.)
................................................ (etc.)

I see that the pixels are all set as the program executes, but they mysteriously get cleared
as pixels on the following column are set. Any idea why this might be?

int main(void)
{
st7920_init();
for (int x = -1; x < 127; x++)
{
for (int y = -1; y < 7; y++)
{
st7920_setPixel(x,y);
}
}

while (1);
}

--------------

void st7920_drawPixel(U8 x, U8 y)
{
U16 val;

val = 0x8000 >> (x & 0xF);
st7920_command(ST7920_CMD_DDRAM_ADDR + y);
st7920_command(ST7920_CMD_DDRAM_ADDR + (x >> 4));
st7920_data(val >> 8);
st7920_data(val & 0xFF);
}

In any case, thanks for your help. It's already a big lead in the right direction. I have really
been scratching my head with the datasheet trying to figure out what I was missing.

Kevin.

Am Mittwoch, 26. November 2008 schrieb Kevin Townsend:
> Albert:
>
> That's a lot closer to what I was hoping for, thanks. However, whenever I
> set one 'column' of pixels, it seems to clear all the previous ones in the
> same 16x16 block. For example, I would expect the code in 'main()' below to
> product a solid black block 128x8 pixels in size along the top of the
> screen. Instead, I have 15 blank pixels, 1 black pixel, 15 blank, 1 black,
> repeated for each 16x16 block, 8 pixels high:
>
> ................................................ (etc.)
> ................................................ (etc.)
> ................................................ (etc.)
> ................................................ (etc.)
> ................................................ (etc.)
> ................................................ (etc.)
> ................................................ (etc.)
> ................................................ (etc.)
>
> I see that the pixels are all set as the program executes, but they
> mysteriously get cleared as pixels on the following column are set. Any
> idea why this might be?
>

Hello Kevin,

you access the display's memory directly, so it does know of any "setpixel" or
whatever commands. Therefore, it does not know how to combine the data in the
memory already, with the data you just send. So it just overwrites the data.

To set an individual pixel anywhere, without affecting already set pixels, you
need to read the display ram first. So, set the address, read the old content
from the display, combine with the new data, write back to the display. Many
displays offer a special mode where a read does not advance the display's
address-counter, but a write will do. That would save you an "set address"
command inbetween.

Lets assume a stripe (pixel row) is byte-wide, and the display is 128x64
pixels. A simple function to read-modify-write the display could look like
this "pseudo-code":

#define WIDTH 128
#define HEIGHT 64

setPixel(pos_x, pos_y, colour)
{
// row_data holds a stripe, pix_pos is the position of the bit in the
stripe.
unsigned char row_data, pix_pos;

setDisplayAddress( (x >> 3) + (y * (WIDTH >> 3)) ); // >>3 equals /8

row_data = readDisplayRam(); // read stripe from the display

pix_pos = x % 8; //calculate the new pixel position inside a stripe

if(colour) // if != 0, draw pixel
{
row_data |= (0x80 >> pix_pos); // combine old data and new pixel using OR
}
else
{
row_data &= ~(0x80 >> pix_pos); // combine using AND with the inverted
new-pixel-stripe
}

setDisplayAddress( (x >> 3) + (y * (WIDTH >> 3)) ); // set address again

writeDisplayData(row_data); // write out the new stripe
}
Here i assume that inside a stripe pixel 0 is on the left. If it is different,
just do a "0x01 << pix_pos" instead. If you want to invert a pixel on the
display, combine using XOR "row_data ^= (0x80 >> pix_pos);", etc, etc...

The pseudo-code is not meant to be functional, just an example to show how it
should be done. It sets the display address twice, that could be avoided
depending on the display.

An alternative method would be to have a display-buffer in local RAM and work
on that, if you can afford it. Then just send the modified bytes from the
internal buffer to the display. That could allow you a flicker-free update of
the whole display, at the expense using more RAM resources on the chip. In
any case, the method would be the same when working on an internal buffer:
read-modify-write.

Greetings,

Chris

Kevin Townsend wrote:
> st7920_data(val >> 8);
> st7920_data(val & 0xFF);

This doesn't only set pixels on, it *writes* them 16 pixels at once. So
when value of val is 0x0001, then you get 15 leftmost pixels cleared and
one rightmost pixel set on.

When your code writes 16 times into the same address, the effect of the
last write remains. And that happens to be always 0x0001 which you see
on your display.

I glanced over the datasheet and didn't see any method to touch only one
pixel at a time.

--

Timo

Chris:

Thanks for the lengthy reply. After a bit of thought, that's what I assummed was happening,
but it's nice to have a clear explanation of how to resolve it. I'll adjust the code accordingly.
Hopefully, once I can add some support for fonts, etc., I can optimise the code a bit and
upload it here so other's can avoid some of the same problems.

Kevin.
Am Mittwoch, 26. November 2008 schrieb Kevin Townsend:
> Chris:
>
> Thanks for the lengthy reply. After a bit of thought, that's what I
> assummed was happening, but it's nice to have a clear explanation of how to
> resolve it. I'll adjust the code accordingly. Hopefully, once I can add
> some support for fonts, etc., I can optimise the code a bit and upload it
> here so other's can avoid some of the same problems.
>
> Kevin.

Hello Kevin,

quite some while ago i did some code for an LPC2220 based remote-control. It
also contains LCD routines for drawing, and the display also need's the
read-copy-modify method. If you want you can take a look at it. You can find
the code is svn, at svn://svn.mamalala.org/boop id you use a svn client, or
by web-interface at http://svn.mamalala.org/ and then the "boop" project.
There you will find a trunk/display/ folder which contains the code.

It has functions to draw lines, circles, rectangles, fonts, ....

It's far from perfect, but works for me. Maybe that will give you some ideas
for your stuff. Font-Handling is a bit uncommon in that code. If you need
help with that stuff, drop me a note. i'm not that good at documenting code,
as you can see ;)

Greetings,

Chris

> I found the problem with the compiler ... I just needed to change a
> declaration.
>
> I've now got the fonts working (the 3x6 font anyway ... I haven't tried
> the others, yet). The only disadvantage I can see to the way you've
> defined your font data is that you can't naturally have fonts more than
> 8 pixels high because you are using 8 bit values for each 'column'.

If you look at 18 dot font in T6963 code, the font width is 3 byte (24
pixel) and height is 18 bytes (or pixel). We are using these fonts in our
products with LPC2136.
It is worth while to mention that credit for these fonts goes to Michael J.
Karas, I only implemented it on K107/108 and T6963.
Warm Regards,

Mukund Deshmukh,
Beta Computronics Pvt Ltd.
10/1 IT Park, Parsodi,
Nagpur -440022 India.
Web site - http://betacomp.com

Meet us at our Stall No M91 Hall No 18 MZ ,
PLASTINDIA 2009 , Feb 4 -9, 2009,
Pragati Maidan, New Delhi, INDIA

> Mukund:
>
> I was looking at it, and it looks interesting but I'm having a hard
> time compiling it in Crossworks (using the GCC compiler I believe).
>
> I get errors on all of the following lines in fonts.c:

The code works well on GCC 4.1.1.
Try T6963 code, it has bigger fonts and better.
Warm Regards,

Mukund Deshmukh,
Beta Computronics Pvt Ltd.
10/1 IT Park, Parsodi,
Nagpur -440022 India.
Web site - http://betacomp.com

Meet us at our Stall No M91 Hall No 18 MZ ,
PLASTINDIA 2009 , Feb 4 -9, 2009,
Pragati Maidan, New Delhi, INDIA

Mukund:

I found the problem with the compiler ... I just needed to change a
declaration.

I've now got the fonts working (the 3x6 font anyway ... I haven't tried
the others, yet). The only disadvantage I can see to the way you've
defined your font data is that you can't naturally have fonts more than
8 pixels high because you are using 8 bit values for each 'column'.
Obviously moving up to 16-bit values (16 pixels high) will double the
memory requirements, but I'll probably modify the code to use U16
instead of U8 for added flexbility.

In any case, thanks for the code to serve as a good starting point!

Kevin.

Mukund:

I was looking at it, and it looks interesting but I'm having a hard
time compiling it in Crossworks (using the GCC compiler I believe).

I get errors on all of the following lines in fonts.c:

const struct FONT_DEF Font_System3x6 = {3, 6, au8FontSystem3x6};
const struct FONT_DEF Font_System5x8 = {5, 8, au8FontSystem5x8};
const struct FONT_DEF Font_System7x8 = {7, 8, au8FontSystem7x8};
const struct FONT_DEF Font_Courrier8x12 = {8, 12,
au8FontCourrier8x12};
const struct FONT_DEF Font_8x8thk = {8, 8, au8Font8x8thk};
const struct FONT_DEF Font_8x8thn = {8, 8, au8Font8x8thn};
const struct FONT_DEF Font_8x8ord = {8, 8, au8Font8x8ord};

The error is: "variable 'Font_System3x6' has initializer but
incomplete type" (etc.)

They definitions in fonts.h are as follows:

/* Extern definitions */

typedef struct
{
U8 u8Width; /* Character width for storage */
U8 u8Height; /* Character height for storage */
U8 *au8FontTable; /* Font table start address in memory */
} FONT_DEF ;

extern const struct FONT_DEF Font_System3x6;
extern const struct FONT_DEF Font_System5x8;
extern const struct FONT_DEF Font_System7x8;
extern const struct FONT_DEF Font_Courrier8x12;
extern const struct FONT_DEF Font_8x8thk;
extern const struct FONT_DEF Font_8x8thn;
extern const struct FONT_DEF Font_8x8ord;

extern const U8 au8FontSystem3x6[];
extern const U8 au8FontSystem5x8[];
extern const U8 au8FontSystem7x8[];
extern const U8 au8FontCourrier8x12[];
extern const U8 au8Font8x8thk[];
extern const U8 au8Font8x8thn[];
extern const U8 au8Font8x8ord[];

I don't really see what the problem is, but I'm fairly new to
embedded and C development. Any idea yourself?

Kevin.
> I will probably update it later if I add font support, etc., but for
> now I wanted to at least post something. For reference sake, I have
> made no effort to support the LCD's 'basic' (text only) mode, since I
> will only use the display in graphic (pixel) mode.
>

In file section I have uploaded LCD driver for T6369 and K107/108. You may
take some clue about fonts.
Warm Regards,

Mukund Deshmukh,
Beta Computronics Pvt Ltd.
10/1 IT Park, Parsodi,
Nagpur -440022 India.
Web site - http://betacomp.com

Meet us at our Stall No M91 Hall No 18 MZ ,
PLASTINDIA 2009 , Feb 4 -9, 2009,
Pragati Maidan, New Delhi, INDIA
I have uploaded a beta version of this driver in the files section
named 'ST7920-Graphic-LCD-Driver.zip'. I'm sure there are still bugs,
and the performance could probably be significantly improved, but it
should at least serve as a decent starting point for further
development by other people.

I will probably update it later if I add font support, etc., but for
now I wanted to at least post something. For reference sake, I have
made no effort to support the LCD's 'basic' (text only) mode, since I
will only use the display in graphic (pixel) mode.

All pixels are stored in an internal 8x64 16-bit memory map, and when
you modify a pixel via the appropriate method you have the option to
update the LCD entirely, partially, or not at all (depending on your
performance requirements). Please see the main.c file for examples of
all three ways to update the screen. I made the decision to use the
memory map since the lpc2148 has lots of memory, and it saves me from
having to read the LCD contents every time a pixel is modified to make
sure I'm not overwriting other existing data. Feel free to change the
code if you're requirements are different.

Thanks again for all the help/advice I received on this (Christian,
Albert, etc.)! I would have a lot more gray hairs if this forum wasn't
as helpful and informative as it typically is. :-)

Kevin.
> Hi Kevin,
>
> hmm, from what i see the single lcd controller is capable for up to 256x64
> pixel, so i think you see a driver chip there instead of a second controller,
> unless you display is bigger than that.

You're right ... there is only one controller, but two 'drivers'. The diagram in the datasheet
shows one ST7920 connected to two ST7921s which are connected to the LCD. The key
may be that the display is shown as 256x32, when the screen is actually 128x64. As I
mentionned, I'm sure it is simply a problem of finding the right address.

> Have you tried to fill the whole possible memory range of the lcd controller?
> There is a slight chance that the memory mapping isnt linear for the two
> screen-half's, altough mostly such memory would be linear.

I'll give it a shot.

> In your writeRow() function, at the very first you do a "value = value >>
> (column & 0xF);" Do you really want that, since "value" seems to be the
> pixel-data you actually want to send.

I'm not sure ... that's the method I had the hardest time to wrap my head around, and that
code was supplied by someone earlier in this thread. As I mentionned, I'm sure the
problem is here ... but I don't particularly understand the logic of how the addressing
works relative to actual pixel locations.

> Finally, _if_ there is a second controller on the lcd, it probably would have
> its own enable line, while sharing the other lines.
>
> What exact type/brand is the display that you use? Any link at hand?

http://cgi.ebay.fr/ws/eBayISAPI.dll?
ViewItem&ssPageName=STRK:MEWNX:IT&item&0312979724#ht_2331wt_0

http://www.pic16.net/down/12864液晶中文资料.pdf

Kevin

I think I've figure it out. The screen is expecting pixel addresses of 256x32, even though
the screen is 128x64. To set pixel 0,32 (the first pixel on the bottom half of the screen),
you actually need to pass the address 128, 0 (and then 129, 0, etc.).

Thanks again for the quick help,
Kevin

// Renders the contents of the memory map to the LCD screen (16 pixels at a time)
void st7920_refreshScreen(void)
{
U8 pixelX;

for (U8 x = 0; x < ST7920_MEMMAP_WIDTH; x++) // Scroll through all columns (16
pixels wide)
{
for (U8 y = 0; y < ST7920_MEMMAP_HEIGHT; y++) // Scroll through all rows (1 pixel
high)
{
// Top half of screen
if (y < 32)
{
pixelX = x * 16; // pixelX = current column (x) * 16
st7920_writeRow(pixelX, y, _memoryMap[x][y]); // Send the current column/row to
LCD display
}
// Bottom half of screen
else
{
pixelX = (x * 16) + 128; // pixelX = current column (x) * 16
st7920_writeRow(pixelX, y - 32, _memoryMap[x][y]); // Send the current column/row
to LCD display
}
}
}
}

Am Donnerstag, 27. November 2008 schrieb Kevin Townsend:
> Christian (or anyone else):
>
> One last question (hopefully) ... I've noticed that whatever I do, I am
> only able to fill one half of the screen. I assume this is because there
> are actually two controllers, each driving half of the screen, but I don't
> see anything in the datasheet to indicate how to switch between the
> controllers to set any pixels on row 33-64 (I have rows 1-32 working as I
> expected).
>
> I've reworked the code to work with a 16-bit 8x64 memory map to avoid
> having to send read commands (the 2148 has more than enough memory for
> this), but when I update the screen from the map, I can't seem to find a
> way to force controller for the bottom of the screen to update it's memory.
> I'm sure I'll figure it out eventually, but perhaps you know the answer
> off the top of your head? I assume I just need to modify an address in the
> 'st7920_writeRow' method below???

Hi Kevin,

hmm, from what i see the single lcd controller is capable for up to 256x64
pixel, so i think you see a driver chip there instead of a second controller,
unless you display is bigger than that.

Have you tried to fill the whole possible memory range of the lcd controller?
There is a slight chance that the memory mapping isnt linear for the two
screen-half's, altough mostly such memory would be linear.

In your writeRow() function, at the very first you do a "value = value >>
(column & 0xF);" Do you really want that, since "value" seems to be the
pixel-data you actually want to send.

Finally, _if_ there is a second controller on the lcd, it probably would have
its own enable line, while sharing the other lines.

What exact type/brand is the display that you use? Any link at hand?

Greetings,

Chris

Christian (or anyone else):

One last question (hopefully) ... I've noticed that whatever I do, I am only able to fill one
half of the screen. I assume this is because there are actually two controllers, each driving
half of the screen, but I don't see anything in the datasheet to indicate how to switch
between the controllers to set any pixels on row 33-64 (I have rows 1-32 working as I
expected).

I've reworked the code to work with a 16-bit 8x64 memory map to avoid having to send
read commands (the 2148 has more than enough memory for this), but when I update the
screen from the map, I can't seem to find a way to force controller for the bottom of the
screen to update it's memory. I'm sure I'll figure it out eventually, but perhaps you know
the answer off the top of your head? I assume I just need to modify an address in the
'st7920_writeRow' method below???

// Fills the entire screen with the supplied 16-pixel wide pattern.
// '0xFFFF' will fill the screen with black pixels, '0X0000' will clear
// the entire screen, and '0xAAAA' will fill the screen with alternating
// pixels, for example.
void st7920_fill(U16 pattern)
{
st7920_fillMemoryMap(pattern);
st7920_refreshScreen();
}

// Renders the contents of the memory map to the LCD screen (16x1 pixels at a time)
void st7920_refreshScreen(void)
{
U8 pixelX;

for (U8 x = 0; x < ST7920_MEMMAP_WIDTH; x++) // Scroll through all columns (16
pixels wide)
{
for (U8 y = 0; y < ST7920_MEMMAP_HEIGHT; y++) // Scroll through all rows (1 pixel
high)
{
pixelX = x * 16; // pixelX = current column (x) * 16
st7920_writeRow(pixelX, y, _memoryMap[x][y]); // Send the current column/row to
LCD display
}
}
}

// Writes data to a 16 pixel row corresponding to the supplied X and Y co-ordinates
void st7920_writeRow(U8 column, U8 row, U16 value)
{
value = value >> (column & 0xF);
st7920_sendCommand(ST7920_CMD_DDRAM_ADDR + row);
st7920_sendCommand(ST7920_CMD_DDRAM_ADDR + (column >> 4));
st7920_sendData(value >> 8);
st7920_sendData(value & 0xFF);
}