readPixel() working, more robust readID()

This commit is contained in:
Phillip Burgess
2013-07-15 13:51:42 -07:00
parent 2926bacdbc
commit ffce7315b6
3 changed files with 157 additions and 111 deletions
+49 -33
View File
@@ -301,7 +301,6 @@ void Adafruit_TFTLCD::begin(uint16_t id) {
void Adafruit_TFTLCD::reset(void) {
CS_IDLE;
CD_DATA;
WR_IDLE;
RD_IDLE;
@@ -317,12 +316,12 @@ void Adafruit_TFTLCD::reset(void) {
}
#endif
// Data transfer sync
CS_ACTIVE;
CD_DATA;
CD_COMMAND;
write8(0x00);
for(uint8_t i=0; i<7; i++) WR_STROBE;
for(uint8_t i=0; i<3; i++) WR_STROBE; // Three extra 0x00s
CS_IDLE;
delay(100);
}
// Sets the LCD address window (and address counter, on 932X).
@@ -679,19 +678,22 @@ void Adafruit_TFTLCD::setRotation(uint8_t x) {
}
}
#ifdef read8isFunctionalized
#define read8(x) x=read8fn()
#endif
// Because this function is used infrequently, it configures the ports for
// the read operation, reads the data, then restores the ports to the write
// configuration. Write operations happen a LOT, so it's advantageous to
// leave the ports in that state as a default.
uint16_t Adafruit_TFTLCD::readPixel(int16_t x, int16_t y) {
uint16_t c;
if((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return 0;
CS_ACTIVE;
if(driver == ID_932X) {
uint8_t hi, lo;
int16_t t;
switch(rotation) {
case 1:
@@ -711,45 +713,59 @@ uint16_t Adafruit_TFTLCD::readPixel(int16_t x, int16_t y) {
}
writeRegister16(0x0020, x);
writeRegister16(0x0021, y);
CD_COMMAND; write8(0x00); write8(0x22); // Read data from GRAM
// Inexplicable thing: sometimes pixel read has high/low bytes
// reversed. A second read fixes this. Unsure of reason. Have
// tried adjusting timing in read8() etc. to no avail.
for(uint8_t pass=0; pass<2; pass++) {
CD_COMMAND; write8(0x00); write8(0x22); // Read data from GRAM
CD_DATA;
setReadDir(); // Set up LCD data port(s) for READ operations
read8(hi); // First 2 bytes back are a dummy read
read8(hi);
read8(hi); // Bytes 3, 4 are actual pixel value
read8(lo);
setWriteDir(); // Restore LCD data port(s) to WRITE configuration
}
CS_IDLE;
return ((uint16_t)hi << 8) | lo;
} else if(driver == ID_7575) {
uint8_t r, g, b;
writeRegisterPair(HX8347G_COLADDRSTART_HI, HX8347G_COLADDRSTART_LO, x);
writeRegisterPair(HX8347G_ROWADDRSTART_HI, HX8347G_ROWADDRSTART_LO, y);
CD_COMMAND; write8(0x22); // Read data from GRAM
}
setReadDir(); // Set up LCD data port(s) for READ operations
CD_DATA;
c = read8(); // Do not merge or otherwise simplify
c <<= 8; // these lines. It's an unfortunate
delayMicroseconds(1); // artifact of the macro substitution
c |= read8(); // shenanigans that are going on.
setWriteDir(); // Restore LCD data port(s) to WRITE configuration
CS_IDLE;
return c;
setReadDir(); // Set up LCD data port(s) for READ operations
CD_DATA;
read8(r); // First byte back is a dummy read
read8(r);
read8(g);
read8(b);
setWriteDir(); // Restore LCD data port(s) to WRITE configuration
CS_IDLE;
return (((uint16_t)r & B11111000) << 8) |
(((uint16_t)g & B11111100) << 3) |
( b >> 3);
} else return 0;
}
// Ditto with the read/write port directions, as above.
uint16_t Adafruit_TFTLCD::readID(void) {
uint16_t id;
uint8_t hi, lo;
CS_ACTIVE;
CD_COMMAND;
write8(0x00);
write8(0x00);
WR_STROBE; // Repeat prior byte (0x00)
setReadDir(); // Set up LCD data port(s) for READ operations
CD_DATA;
delayMicroseconds(10);
id = read8(); // Do not merge or otherwise simplify
id <<= 8; // these lines. It's an unfortunate
delayMicroseconds(10); // artifact of the macro substitution
id |= read8(); // shenanigans that are going on.
CS_IDLE;
read8(hi);
read8(lo);
setWriteDir(); // Restore LCD data port(s) to WRITE configuration
CS_IDLE;
return id;
return (hi << 8) | lo;
}
// Pass 8-bit (each) R,G,B, get back 16-bit packed color
@@ -766,11 +782,11 @@ void Adafruit_TFTLCD::write8(uint8_t value) {
}
#endif
#ifndef read8
uint8_t Adafruit_TFTLCD::read8(void) {
// Do not merge or simplify -- macro shenanigans going on!
uint8_t d = read8inline();
return d;
#ifdef read8isFunctionalized
uint8_t Adafruit_TFTLCD::read8fn(void) {
uint8_t result;
read8inline(result);
return result;
}
#endif
+4 -4
View File
@@ -38,10 +38,6 @@ class Adafruit_TFTLCD : public Adafruit_GFX {
setAddrWindow(int x1, int y1, int x2, int y2),
pushColors(uint16_t *data, uint8_t len, boolean first);
#ifndef read8
uint8_t read8(void); // See notes below re: macros
#endif
uint16_t color565(uint8_t r, uint8_t g, uint8_t b),
readPixel(int16_t x, int16_t y),
readID(void);
@@ -72,6 +68,10 @@ class Adafruit_TFTLCD : public Adafruit_GFX {
setLR(void),
flood(uint16_t color, uint32_t len);
uint8_t driver;
#ifndef read8
uint8_t read8fn(void);
#define read8isFunctionalized
#endif
#ifndef USE_ADAFRUIT_SHIELD_PINOUT
volatile uint8_t *csPort , *cdPort , *wrPort , *rdPort;
uint8_t csPinSet , cdPinSet , wrPinSet , rdPinSet ,
+104 -74
View File
@@ -15,7 +15,7 @@
// writes that all refer to x, so it needs to be a constant or fixed
// variable and not something like *ptr++ (which, after macro
// expansion, may increment the pointer repeatedly and run off into
// la-la land). Macros also give us fune-grained control over which
// la-la land). Macros also give us fine-grained control over which
// operations are inlined on which boards (balancing speed against
// available program space).
@@ -46,6 +46,19 @@
// Leo dig. pin : 7 6 5 4 3 2 9 8
// Leo port/pin : PE6 PD7 PC6 PD4 PD0 PD1 PB5 PB4
// Pixel read operations require a minimum 400 nS delay from RD_ACTIVE
// to polling the input pins. At 16 MHz, one machine cycle is 62.5 nS.
// This code burns 7 cycles (437.5 nS) doing nothing; the RJMPs are
// equivalent to two NOPs each, final NOP burns the 7th cycle, and the
// last line is a radioactive mutant emoticon.
#define DELAY7 \
asm volatile( \
"rjmp .+0" "\n\t" \
"rjmp .+0" "\n\t" \
"rjmp .+0" "\n\t" \
"nop" "\n" \
::);
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined (__AVR_ATmega328__) || defined(__AVR_ATmega8__)
// Arduino Uno, Duemilanove, etc.
@@ -66,13 +79,19 @@
// These are macros for I/O operations...
// Write 8-bit value to LCD data lines
#define write8inline(d) { \
PORTD = (PORTD & B00101111) | ((d) & B11010000); \
PORTB = (PORTB & B11010000) | ((d) & B00101111); \
WR_STROBE; } // STROBEs are defined later
#define write8inline(d) { \
PORTD = (PORTD & B00101111) | ((d) & B11010000); \
PORTB = (PORTB & B11010000) | ((d) & B00101111); \
WR_STROBE; } // STROBEs are defined later
// Read 8-bit value from LCD data lines
#define read8inline() (RD_STROBE, (PIND & B11010000) | (PINB & B00101111))
// Read 8-bit value from LCD data lines. The signle argument
// is a destination variable; this isn't a function and doesn't
// return a value in the conventional sense.
#define read8inline(result) { \
RD_ACTIVE; \
DELAY7; \
result = (PIND & B11010000) | (PINB & B00101111); \
RD_IDLE; }
// These set the PORT directions as required before the write and read
// operations. Because write operations are much more common than reads,
@@ -85,13 +104,17 @@
#else // Uno w/Breakout board
#define write8inline(d) { \
PORTD = (PORTD & B00000011) | ((d) & B11111100); \
PORTB = (PORTB & B11111100) | ((d) & B00000011); \
WR_STROBE; }
#define read8inline() (RD_STROBE, (PIND& B11111100)|(PINB& B00000011))
#define setWriteDirInline() { DDRD|= B11111100; DDRB|= B00000011; }
#define setReadDirInline() { DDRD&=~B11111100; DDRB&=~B00000011; }
#define write8inline(d) { \
PORTD = (PORTD & B00000011) | ((d) & B11111100); \
PORTB = (PORTB & B11111100) | ((d) & B00000011); \
WR_STROBE; }
#define read8inline(result) { \
RD_ACTIVE; \
DELAY7; \
result = (PIND & B11111100) | (PINB & B00000011); \
RD_IDLE; }
#define setWriteDirInline() { DDRD |= B11111100; DDRB |= B00000011; }
#define setReadDirInline() { DDRD &= ~B11111100; DDRB &= ~B00000011; }
#endif
@@ -99,7 +122,8 @@
// of these are left undefined, an equivalent function version (non-inline)
// is declared later. The Uno has a moderate amount of program space, so
// only write8() is inlined -- that one provides the most performance
// benefit, but also generates the most bloat.
// benefit, but unfortunately also generates the most bloat. This is
// why only certain cases are inlined for each board.
#define write8 write8inline
#elif defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__)
@@ -117,29 +141,32 @@
#define CD_MASK B00000100
#define CS_MASK B00001000
#define write8inline(d) { \
PORTH = (PORTH & B10000111)|(((d) & B11000000)>>3)|(((d) & B00000011)<<5); \
PORTB = (PORTB & B01001111)|(((d) & B00101100)<<2); \
PORTG = (PORTG & B11011111)|(((d) & B00010000)<<1); \
WR_STROBE; }
#define read8inline() (RD_STROBE, \
((PINH & B00011000) << 3) | ((PINB & B10110000) >> 2) | \
((PING & B00100000) >> 1) | ((PINH & B01100000) >> 5))
#define setWriteDirInline() { \
DDRH |= B01111000; DDRB |= B10110000; DDRG |= B00100000; }
#define setReadDirInline() { \
DDRH &= ~B01111000; DDRB &= ~B10110000; DDRG &= ~B00100000; }
// Strobe is wonky on Mega w/shield. Haven't worked out the underlying
// reason, but an interim kludge is just to use inverted levels. ???
#define RD_STROBE RD_IDLE, RD_ACTIVE
#define write8inline(d) { \
PORTH = (PORTH&B10000111)|(((d)&B11000000)>>3)|(((d)&B00000011)<<5); \
PORTB = (PORTB&B01001111)|(((d)&B00101100)<<2); \
PORTG = (PORTG&B11011111)|(((d)&B00010000)<<1); \
WR_STROBE; }
#define read8inline(result) { \
RD_ACTIVE; \
DELAY7; \
result = ((PINH & B00011000) << 3) | ((PINB & B10110000) >> 2) | \
((PING & B00100000) >> 1) | ((PINH & B01100000) >> 5); \
RD_IDLE; }
#define setWriteDirInline() { \
DDRH |= B01111000; DDRB |= B10110000; DDRG |= B00100000; }
#define setReadDirInline() { \
DDRH &= ~B01111000; DDRB &= ~B10110000; DDRG &= ~B00100000; }
#else // Mega w/Breakout board
#define write8inline(d) { PORTA = (d); WR_STROBE; }
#define read8inline() (RD_STROBE, PINA)
#define setWriteDirInline() DDRA = 0xff
#define setReadDirInline() DDRA = 0
#define write8inline(d) { PORTA = (d); WR_STROBE; }
#define read8inline(result) { \
RD_ACTIVE; \
DELAY7; \
result = PINA; \
RD_IDLE; }
#define setWriteDirInline() DDRA = 0xff
#define setReadDirInline() DDRA = 0
#endif
@@ -172,44 +199,50 @@
#define CD_MASK B00100000
#define CS_MASK B00010000
#define write8inline(d) { \
PORTE = (PORTE & B10111111) | (((d) & B10000000)>>1); \
PORTD = (PORTD & B01101111) | (((d) & B01000000)<<1) | ((d) & B00010000); \
PORTC = (PORTC & B01111111) | (((d) & B00100000)<<2); \
PORTB = (PORTB & B00001111) | (((d) & B00001111)<<4); \
WR_STROBE; }
#define read8inline() (RD_STROBE, \
(((PINE & B01000000) << 1) | ((PIND & B10000000) >> 1) | \
((PINC & B10000000) >> 2) | ((PINB & B11110000) >> 4) | \
(PIND & B00010000)))
#define setWriteDirInline() { \
DDRE |= B01000000; DDRD |= B10010000; \
DDRC |= B10000000; DDRB |= B11110000; }
#define setReadDirInline() { \
DDRE &= ~B01000000; DDRD &= ~B10010000; \
DDRC &= ~B10000000; DDRB &= ~B11110000; }
#define write8inline(d) { \
PORTE = (PORTE & B10111111) | (((d) & B10000000)>>1); \
PORTD = (PORTD & B01101111) | (((d) & B01000000)<<1) | ((d) & B00010000); \
PORTC = (PORTC & B01111111) | (((d) & B00100000)<<2); \
PORTB = (PORTB & B00001111) | (((d) & B00001111)<<4); \
WR_STROBE; }
#define read8inline(result) { \
RD_ACTIVE; \
DELAY7; \
result = ((PINE & B01000000) << 1) | ((PIND & B10000000) >> 1) | \
((PINC & B10000000) >> 2) | ((PINB & B11110000) >> 4) | \
(PIND & B00010000); \
RD_IDLE; }
#define setWriteDirInline() { \
DDRE |= B01000000; DDRD |= B10010000; \
DDRC |= B10000000; DDRB |= B11110000; }
#define setReadDirInline() { \
DDRE &= ~B01000000; DDRD &= ~B10010000; \
DDRC &= ~B10000000; DDRB &= ~B11110000; }
#else // Leonardo w/Breakout board
#define write8inline(d) { \
uint8_t dr1 = (d) >> 1, dl1 = (d) << 1; \
PORTE = (PORTE & B10111111) | (dr1 & B01000000); \
PORTD = (PORTD & B01101100) | (dl1 & B10000000) | (((d) & B00001000)>>3) | \
(dr1 & B00000010) | ((d) & B00010000); \
PORTC = (PORTC & B10111111) | (dl1 & B01000000); \
PORTB = (PORTB & B11001111) |(((d) & B00000011)<<4); \
WR_STROBE; }
#define read8inline() (RD_STROBE, \
(((PINE & B01000000) | (PIND & B00000010)) << 1) | \
(((PINC & B01000000) | (PIND & B10000000)) >> 1) | \
((PIND & B00000001)<<3) | ((PINB & B00110000)>>4) | (PIND & B00010000))
#define setWriteDirInline() { \
DDRE |= B01000000; DDRD |= B10010011; \
DDRC |= B01000000; DDRB |= B00110000; }
#define setReadDirInline() { \
DDRE &= ~B01000000; DDRD &= ~B10010011; \
DDRC &= ~B01000000; DDRB &= ~B00110000; }
#define write8inline(d) { \
uint8_t dr1 = (d) >> 1, dl1 = (d) << 1; \
PORTE = (PORTE & B10111111) | (dr1 & B01000000); \
PORTD = (PORTD & B01101100) | (dl1 & B10000000) | (((d) & B00001000)>>3) |\
(dr1 & B00000010) | ((d) & B00010000); \
PORTC = (PORTC & B10111111) | (dl1 & B01000000); \
PORTB = (PORTB & B11001111) |(((d) & B00000011)<<4); \
WR_STROBE; }
#define read8inline(result) { \
RD_ACTIVE; \
DELAY7; \
result = (((PINE & B01000000) | (PIND & B00000010)) << 1) | \
(((PINC & B01000000) | (PIND & B10000000)) >> 1) | \
((PIND & B00000001) << 3) | ((PINB & B00110000) >> 4) | \
(PIND & B00010000); \
RD_IDLE; }
#define setWriteDirInline() { \
DDRE |= B01000000; DDRD |= B10010011; \
DDRC |= B01000000; DDRB |= B00110000; }
#define setReadDirInline() { \
DDRE &= ~B01000000; DDRD &= ~B10010011; \
DDRC &= ~B01000000; DDRB &= ~B00110000; }
#endif
@@ -255,10 +288,7 @@
#endif
// Data read and write strobes, ~2 instructions and always inline
#ifndef RD_STROBE
#define RD_STROBE RD_ACTIVE, RD_IDLE
#endif
// Data write strobe, ~2 instructions and always inline
#define WR_STROBE { WR_ACTIVE; WR_IDLE; }
// These higher-level operations are usually functionalized,