Crusty61 / lpc1343codebase

Automatically exported from code.google.com/p/lpc1343codebase
Other
0 stars 0 forks source link

Inefficient lcdGetPixel in ILI9325.c #4

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Hi! There's an extra ili9325SetCursor and ili9325WriteCmd in the lcdGetPixel 
method. 

ILI9325.c line 394:
> // Eeek ... why does this need to be done twice for a proper value?!?

It's becuse the first read clocks the data from the GRAM into the register. 
You'll get the proper value when you read the register again (the second read 
clocks the data from the register to the bus, at the first read you get an 
old/dummy value). But you don't need to do writes in the middle, 2 reads is 
enough.

-PP.

PS. Has anyone been able to read multiple pixels (like when you write pixels) 
without updating the pixel address between pixels? It's dreadfully slow to read 
pixels one by one (I'm trying to implement both horizontal and vertical scroll 
on the ILI9325 screen)

Original issue reported on code.google.com by ptechdes...@gmail.com on 2 Oct 2011 at 9:29

GoogleCodeExporter commented 9 years ago
I haven't had any success with reading pixels from GRAM.  All I can read from 
GRAM is 0x001F (equivalent to a blue pixel given RGB565 color coding) even 
though there is no blue pixel on screen.

I am able to read other register values properly, however.

Has 'lcdGetPixel' worked for anyone else?

Original comment by alec.gre...@gmail.com on 23 Nov 2012 at 4:56

GoogleCodeExporter commented 9 years ago
Hmmh, it was ages ago, but here's some code I wrote years ago that worked for 
me back then, although it was too slow for me. It's not clean or anything, but 
have fun deciphering it. By the way, don't trust the comments in the code, they 
might not be up to date. 

This interface level function reads a rectangle from the screen:

/// LcdReadRectange should not be used. Reading from the display memory is 
unsupported in many displays and very slow in others.
u_int16 LcdReadRectangle (u_int16 x1, u_int16 y1, u_int16 x2, u_int16 y2, 
u_int16 *texture) {
    u_int16 buff[2];
    u_int32 n = (((u_int32)x2-x1)+1);
    u_int16 x, y;

    n *= (((u_int32)y2-y1)+1);
    n *= 2; //pixels to bytes

    if (!texture) {
        return S_ERROR; //Caller must supply pointer to texture
    }
    for (y=y1; y<=y2; y++) {
        TFTWriteRegister(DRAM_Horizontal_Address_Set,y);
        for (x=x1; x<=x2; x++) {
            TFTWriteRegister(DRAM_Vertical_Address_Set,(__LCD_LOGICAL_WIDTH-1)-x);
            *texture++ = TFTReadRegister(Read_Data_from_DRAM);
        }
    }       
}

It uses TFTWriteRegister and TFTReadRegister, which are written as:

// This function seems to work at at least 80 MHz.
void TFTWriteRegister(u_int16 reg, u_int16 data) {
    PERIP(GPIO0_MODE) &= ~0x07ff; //Set GPIO0 pins [10:0] as GPIO pins
    PERIP(GPIO0_DDR) |= 0x06ff; //Set GPIO0 pins [10:9,7:0] as outputs
    WR_SET_HIGH();
    RD_SET_HIGH();
    A0_SET_LOW();
    CS_SET_LOW();
    A0_SET_LOW();
    WRITE_BYTE(0);
    WRITE_BYTE(reg);
    A0_SET_HIGH();
    WRITE_BYTE(data >> 8);
    WRITE_BYTE(data);
    CS_SET_HIGH();
    PERIP(GPIO0_MODE) |= 0x07ff; //Set GPIO0 pins [10:0] as peripherals
}

/// This function is optimized for 60 MHz speed. It reads the register twice to 
be able to read from display memory.
u_int16 TFTReadRegister(u_int16 reg) {
    u_int16 result,result2; 
    PERIP(GPIO0_MODE) &= ~0x07ff; //Set GPIO0 pins [10:0] as GPIO pins
    PERIP(GPIO0_DDR) |= 0x06ff; //Set GPIO0 pins [10:9,7:0] as outputs
    WR_SET_HIGH();
    A0_SET_LOW();
    CS_SET_LOW();   
    A0_SET_LOW();
    WRITE_BYTE(0);
    WRITE_BYTE(reg);
    A0_SET_HIGH();
    PERIP(GPIO0_DDR) &= ~0x00ff; //Set GPIO0 pins [7:0] as inputs
    READ_BYTE(result2);
    READ_BYTE(result2);
    READ_BYTE(result2);
    READ_BYTE(result);
    result = result2<<8 | result;
    CS_SET_HIGH();
    PERIP(GPIO0_DDR) |= 0x06ff; //Set GPIO0 pins [10:9,7:0] as outputs
    PERIP(GPIO0_MODE) |= 0x07ff; //Set GPIO0 pins [10:0] as peripherals
    return result;
}

Those finally use some hardware macros to manipulate GPIO pins. Note the 
reduplication of some instructions just to introduce small delays into the 
waveform.

// Hard macros to manipulate the interface pins. 
#define A0_SET_HIGH(){PERIP(GPIO0_SET_MASK) = (1 << 0xe);}
#define A0_SET_LOW(){PERIP(GPIO0_CLEAR_MASK) = (1 << 0xe);}
#define CS_SET_HIGH(){PERIP(GPIO1_SET_MASK) = (1 << 0x0);}
#define CS_SET_LOW(){PERIP(GPIO1_CLEAR_MASK) = (1 << 0x0);}
#define RD_SET_HIGH(){PERIP(GPIO0_SET_MASK) = (1 << 0x9);}
#define RD_SET_LOW(){PERIP(GPIO0_CLEAR_MASK) = (1 << 0x9);}
#define WR_SET_HIGH(){PERIP(GPIO0_SET_MASK) = (1 << 0xa);}
#define WR_SET_LOW(){PERIP(GPIO0_CLEAR_MASK) = (1 << 0xa);}
#define WRITE_BYTE(a){PERIP(GPIO0_CLEAR_MASK)=0x00ff; 
PERIP(GPIO0_SET_MASK)=((a)&0xff); PERIP(GPIO0_CLEAR_MASK)=0x0400; 
WR_SET_LOW();WR_SET_LOW();PERIP(GPIO0_SET_MASK)=0x0600;}
#define 
READ_BYTE(a){RD_SET_LOW();RD_SET_LOW();RD_SET_LOW();RD_SET_LOW();RD_SET_LOW();RD
_SET_LOW();RD_SET_LOW();RD_SET_LOW();a=PERIP(GPIO0_IDATA)&0xff;RD_SET_HIGH();}

Have loads of fun...!
-PP.

Original comment by ptechdes...@gmail.com on 23 Nov 2012 at 8:22

GoogleCodeExporter commented 9 years ago
Oh, btw that last code snippet from me is not for arm, and maybe not even 
working, but it may help you to get some ideas for you to tweak your code.

Original comment by ptechdes...@gmail.com on 23 Nov 2012 at 8:45

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
Okay, thanks for the comments on the code.  It appears that in essence you are 
still writing the GRAM reg value (0x0022), and then reading as indicated in the 
attached diagram.

Datasheet says minimum low and high hold times for read operation are 150ns 
each.  I am performing both for 170ns each (perhaps not long enough?), with 
oscilloscope readings exactly as depicted in the attached diagram.  The strange 
thing is that read works fine for non-GRAM registers.

I'll try with longer read hold times, and let you know.

*Also, the attached diagram makes it look like you can perform sequential pixel 
reads (with auto-address increment) just like you can with writes.  You asked 
if it was possible in your initial post; did you ever succeed with sequential 
reads?

Original comment by alec.gre...@gmail.com on 24 Nov 2012 at 5:39

Attachments:

GoogleCodeExporter commented 9 years ago
Hi!

No, I never got the autoincrement to work so it was just pixel-by-pixel 
reading. But it was just one LCD module from China and who knows what the 
controller in that module /really/ was..., I never tried it again with other 
modules.

BTW, A0 is another traditional name for RS (Register Select), inherited from 
naming conventions in old code and schematics...

> The strange thing is that read works fine for non-GRAM registers.
Hmmh, it's not strange at all, since the mechanism for reading the GRAM 
registers is different: it takes 1-2 clock cycles for the RAM fetch, which 
needs to trickle its way down to the register. Other registers don't need this 
step, they are read directly, so they don't need the dummy cycles. How many 
times do you clock in the register value? Try adding a few..

Original comment by ptechdes...@gmail.com on 24 Nov 2012 at 5:47

GoogleCodeExporter commented 9 years ago
Well, I had some luck in reading pixels (doesn't seem to auto-increment, 
though).

However, weirdly enough there seem to be certain pixel values that I can't read 
correctly.  I can read green (0x07E0), magenta (0xF81F), cyan (0x07FF), and 
others.  However, when I try to read red (0xF800), I get blue (0x001F) AND vice 
versa (read red when really blue pixel).  I'm also having issues with other 
colors (eg read 0xD7E6 when I really write 0x37FA).

I'm using the same read/write routines for all of it, just changing out the 
pixel values with straight hex numbers, so wonder if it could be hardware issue?

Original comment by alec.gre...@gmail.com on 25 Nov 2012 at 4:53

GoogleCodeExporter commented 9 years ago
If you get blue when you read red and other such problems, then you have a RGB 
vs BGR bit order problem. There's a bit in the register set which chooses which 
bits are the leftmost, red (RGB) or blue (BGR). 

It may also help to know that the display memory is always 18 bits wide. When 
you write a pixel value, it's translated into a 18-bit value using some 
colorscheme, such as 18-bit (RGB666), 16 bit (RGB565), 15-bit (RGB555), 8 bit 
(BGR233, not supported in ILI9325) or some other translation. For each 
colorscheme, there may be different translations for example to support 
different orderings of the plastic RGB color filter on top of the LCD glass.

When you read back the pixel value, the 18-bit value in the display memory is 
again translated into a 8-bit, 15-bit, 16-bit (or 18-bit) value. So if the 
display's configuration changes between write and read, at least then you will 
get different hex values than the ones you write in.

Original comment by ptechdes...@gmail.com on 25 Nov 2012 at 4:55

GoogleCodeExporter commented 9 years ago
This explains it; thank you so much.  I assumed from the attached diagram that 
I should be writing and reading in RGB.

My initialization of the 'entry mode register' (0x0003) was setting the 'BGR' 
bit, which causes the RGB565 input pixels to be swapped (color inversion).

I was thrown off because some of the pixels I was reading were color inversions 
of each other (eg YELLOW = 0xFFE0 and CYAN = 0x07FF) and some were palindromes 
(eg MAGENTA = 0xF81F), thus making it seem that some pixels were being read 
correctly while others were not.  Very insidious!

However, for my display the 'BGR' bit apparently needs to be set in order to 
map pixels written in RGB correctly.  In other words, since I can't find 
another register value that will color-invert the pixel upon read-out, I will 
either have to write BGR (BGR bit = 0) and/or read BGR (BGR bit = 1), and 
cannot both write and read in RGB.  Is this what you experienced with the 
display?

Original comment by alec.gre...@gmail.com on 26 Nov 2012 at 2:05

Attachments:

GoogleCodeExporter commented 9 years ago
Sounds right, and now that you mention it, the way it behaves on read does feel 
familiar. I think my unit might have behaved just like yours. The conversion 
might not be symmetrical or perhaps not all conversions are available for 
reading. But as I said, I felt it was too slow and abandoned reading the very 
same day I got it working, so I don't have much memories accumulated from that.

But hey, great to hear that it works!

Original comment by ptechdes...@gmail.com on 6 Dec 2012 at 6:15