From ffce7315b66cf0a7f3fec253f3113fb02642851f Mon Sep 17 00:00:00 2001 From: Phillip Burgess Date: Mon, 15 Jul 2013 13:51:42 -0700 Subject: [PATCH] readPixel() working, more robust readID() --- Adafruit_TFTLCD.cpp | 82 ++++++++++++-------- Adafruit_TFTLCD.h | 8 +- pin_magic.h | 178 ++++++++++++++++++++++++++------------------ 3 files changed, 157 insertions(+), 111 deletions(-) diff --git a/Adafruit_TFTLCD.cpp b/Adafruit_TFTLCD.cpp index 9cd6088..1453113 100644 --- a/Adafruit_TFTLCD.cpp +++ b/Adafruit_TFTLCD.cpp @@ -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 diff --git a/Adafruit_TFTLCD.h b/Adafruit_TFTLCD.h index 2d9ad9f..a001c03 100644 --- a/Adafruit_TFTLCD.h +++ b/Adafruit_TFTLCD.h @@ -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 , diff --git a/pin_magic.h b/pin_magic.h index 275dc3c..b6c3cf1 100644 --- a/pin_magic.h +++ b/pin_magic.h @@ -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,